/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <errno.h>
#include <stdlib.h>
+#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "util.h"
#include "vlog.h"
-#undef NDEBUG
-#include <assert.h>
-
struct test {
const char *name;
void (*function)(void);
static const struct test tests[];
+#define CHECK(A, B) check(A, B, #A, #B, __FILE__, __LINE__)
+static void
+check(int a, int b,
+ const char *a_string, const char *b_string, const char *file, int line)
+{
+ if (a != b) {
+ fprintf(stderr, "%s:%d: expected %s == %s but %d != %d\n",
+ file, line, a_string, b_string, a, b);
+ fflush(stderr);
+ abort();
+ }
+}
+
static void
run_lock_and_unlock(void)
{
struct lockfile *lockfile;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
}
{
struct lockfile *lockfile;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
}
{
struct lockfile *lockfile;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
- assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), EDEADLK);
lockfile_unlock(lockfile);
}
{
struct lockfile *lockfile;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
- assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
- assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), EDEADLK);
lockfile_unlock(lockfile);
}
* this function that does the wait() call. */
static struct lockfile *lockfile;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
if (do_fork() == CHILD) {
lockfile_unlock(lockfile);
- assert(lockfile_lock("file", 0, &lockfile) == EAGAIN);
+ CHECK(lockfile_lock("file", &lockfile), EAGAIN);
exit(11);
}
}
{
struct lockfile *lockfile, *dummy;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
- assert(lockfile_lock("file", 0, &dummy) == EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), 0);
+ CHECK(lockfile_lock("file", &dummy), EDEADLK);
if (do_fork() == CHILD) {
- assert(lockfile_lock("file", 0, &dummy) == EAGAIN);
+ CHECK(lockfile_lock("file", &dummy), EAGAIN);
exit(11);
}
}
{
struct lockfile *lockfile;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
if (do_fork() == CHILD) {
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
exit(11);
}
}
static void
-run_lock_timeout_gets_the_lock(void)
+run_lock_multiple(void)
{
- struct lockfile *lockfile;
+ struct lockfile *a, *b, *c, *dummy;
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lockfile_lock("b", &b), 0);
+ CHECK(lockfile_lock("c", &c), 0);
- if (do_fork() == CHILD) {
- lockfile_unlock(lockfile);
- assert(lockfile_lock("file", TIME_UPDATE_INTERVAL * 3,
- &lockfile) == 0);
- exit(11);
- } else {
- long long int now = time_msec();
- while (time_msec() < now + TIME_UPDATE_INTERVAL) {
- pause();
- }
- lockfile_unlock(lockfile);
- }
+ lockfile_unlock(a);
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lockfile_lock("a", &dummy), EDEADLK);
+ lockfile_unlock(a);
+
+ lockfile_unlock(b);
+ CHECK(lockfile_lock("a", &a), 0);
+
+ lockfile_unlock(c);
+ lockfile_unlock(a);
}
+/* Checks that locking a dangling symlink works OK. (It used to hang.) */
static void
-run_lock_timeout_runs_out(void)
+run_lock_symlink(void)
{
- struct lockfile *lockfile;
+ struct lockfile *a, *b, *dummy;
+ struct stat s;
+
+ /* Create a symlink .a.~lock~ pointing to .b.~lock~. */
+ CHECK(symlink(".b.~lock~", ".a.~lock~"), 0);
+ CHECK(lstat(".a.~lock~", &s), 0);
+ CHECK(S_ISLNK(s.st_mode) != 0, 1);
+ CHECK(stat(".a.~lock~", &s), -1);
+ CHECK(errno, ENOENT);
+ CHECK(stat(".b.~lock~", &s), -1);
+ CHECK(errno, ENOENT);
+
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lockfile_lock("a", &dummy), EDEADLK);
+ CHECK(lockfile_lock("b", &dummy), EDEADLK);
+ lockfile_unlock(a);
- assert(lockfile_lock("file", 0, &lockfile) == 0);
+ CHECK(lockfile_lock("b", &b), 0);
+ CHECK(lockfile_lock("b", &dummy), EDEADLK);
+ CHECK(lockfile_lock("a", &dummy), EDEADLK);
+ lockfile_unlock(b);
- if (do_fork() == CHILD) {
- lockfile_unlock(lockfile);
- assert(lockfile_lock("file", TIME_UPDATE_INTERVAL,
- &lockfile) == ETIMEDOUT);
- exit(11);
- } else {
- long long int now = time_msec();
- while (time_msec() < now + TIME_UPDATE_INTERVAL * 3) {
- pause();
- }
- lockfile_unlock(lockfile);
- }
+ CHECK(lstat(".a.~lock~", &s), 0);
+ CHECK(S_ISLNK(s.st_mode) != 0, 1);
+ CHECK(stat(".a.~lock~", &s), 0);
+ CHECK(S_ISREG(s.st_mode) != 0, 1);
+ CHECK(stat(".b.~lock~", &s), 0);
+ CHECK(S_ISREG(s.st_mode) != 0, 1);
}
+/* Checks that locking a file that is itself a symlink yields a lockfile in the
+ * directory that the symlink points to, named for the target of the
+ * symlink.
+ *
+ * (That is, if "a" is a symlink to "dir/b", then "a"'s lockfile is named
+ * "dir/.b.~lock".) */
static void
-run_lock_multiple(void)
+run_lock_symlink_to_dir(void)
{
- struct lockfile *a, *b, *c, *dummy;
-
- assert(lockfile_lock("a", 0, &a) == 0);
- assert(lockfile_lock("b", 0, &b) == 0);
- assert(lockfile_lock("c", 0, &c) == 0);
-
- lockfile_unlock(a);
- assert(lockfile_lock("a", 0, &a) == 0);
- assert(lockfile_lock("a", 0, &dummy) == EDEADLK);
- lockfile_unlock(a);
+ struct lockfile *a, *dummy;
+ struct stat s;
+
+ /* Create a symlink "a" pointing to "dir/b". */
+ CHECK(mkdir("dir", 0700), 0);
+ CHECK(symlink("dir/b", "a"), 0);
+ CHECK(lstat("a", &s), 0);
+ CHECK(S_ISLNK(s.st_mode) != 0, 1);
+
+ /* Lock 'a'. */
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lstat("dir/.b.~lock~", &s), 0);
+ CHECK(S_ISREG(s.st_mode) != 0, 1);
+ CHECK(lstat(".a.~lock~", &s), -1);
+ CHECK(errno, ENOENT);
+ CHECK(lockfile_lock("dir/b", &dummy), EDEADLK);
- lockfile_unlock(b);
- assert(lockfile_lock("a", 0, &a) == 0);
-
- lockfile_unlock(c);
lockfile_unlock(a);
}
TEST(lock_blocks_other_process),
TEST(lock_twice_blocks_other_process),
TEST(lock_and_unlock_allows_other_process),
- TEST(lock_timeout_gets_the_lock),
- TEST(lock_timeout_runs_out),
TEST(lock_multiple),
+ TEST(lock_symlink),
+ TEST(lock_symlink_to_dir),
TEST(help),
{ NULL, NULL }
#undef TEST
int
main(int argc, char *argv[])
{
- extern struct vlog_module VLM_lockfile;
size_t i;
set_program_name(argv[0]);
- vlog_set_levels(&VLM_lockfile, VLF_ANY_FACILITY, VLL_ERR);
+ vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m");
+ vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF);
if (argc != 2) {
ovs_fatal(0, "exactly one argument required; use \"%s help\" for help",