#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include /* A temporary file used for flock. */ char tmpfilename[] = "/tmp/flocktstXXXXXX"; void test_child (int fd, const char *type, const char *fname) { int rc; /* First try to lock using fd. */ do { rc = flock (fd, LOCK_EX | LOCK_NB); } while (rc < 0 && errno == EINTR); if (rc < 0) fprintf (stderr, "flock from %s child with same descriptor: %s\n", type, strerror (errno)); int fd2 = open (fname, O_RDONLY); if (fd2 < 0) perror ("child open"); else { /* Try another descriptor. */ do { rc = flock (fd2, LOCK_EX | LOCK_NB); } while (rc < 0 && errno == EINTR); if (rc == 0) fprintf (stderr, "flock from %s child with new descriptor succeeded but shouldn't\n", type); else if (errno != EWOULDBLOCK) fprintf (stderr, "flock from %s child with new descriptor: %s\n", type, strerror (errno)); if (rc < 0) { do { rc = flock (fd2, LOCK_UN); } while (rc < 0 && errno == EINTR); if (rc == 0) { fprintf (stderr, "funlock from %s child with new descriptor succeeded but shouldn't\n", type); do { rc = flock (fd2, LOCK_EX | LOCK_NB); } while (rc < 0 && errno == EINTR); if (rc == 0) fprintf (stderr, "flock from %s child with new descriptor succeeded but shouldn't\n", type); else if (errno != EWOULDBLOCK) fprintf (stderr, "flock from %s child with new descriptor: %s\n", type, strerror (errno)); do { rc = flock (fd, LOCK_EX | LOCK_NB); } while (rc < 0 && errno == EINTR); if (rc < 0) fprintf (stderr, "flock from %s child with same descriptor: %s\n", type, strerror (errno)); } } close (fd2); } } /* Fork and use flock to lock and unlock the file repeatedly in the child. */ void make_child (int fd, pid_t * pid) { if ((*pid = fork ()) < 0) { perror ("fork failed"); exit (1); } else if (*pid == 0) { char buf[32]; test_child (fd, "forked", tmpfilename); snprintf (buf, 32, "%d", fd); execl (program_invocation_name, program_invocation_name, buf, tmpfilename, NULL); perror ("execl"); exit (1); } } /* Wait for the child to finish. */ void await_child (pid_t pid) { pid_t pstatus; int exit_int; do { pstatus = waitpid (pid, &exit_int, WUNTRACED); } while (pstatus < 0 && errno == EINTR); } int main (int argc, const char *const *argv) { pid_t child; int rc; int fd; if (argc > 1) { test_child (atoi (argv[1]), "execed", argv[2]); exit (0); } /* Create the temporary file. */ fd = mkstemp (tmpfilename); if (fd < 0) { perror ("open failed"); exit (1); } do { rc = flock (fd, LOCK_EX); } while (rc < 0 && errno == EINTR); if (rc < 0) { perror ("lock"); exit (1); } make_child (fd, &child); await_child (child); close (fd); /* Clean up. */ unlink (tmpfilename); return 0; }