/*********************************************************************** * This is a STC to show a process can get an exclusive lock on a file using * fcntl, even though another process has an exclusive lock. * * A parent process uses fcntl to get an exclusive lock. It then * uses fork/exec to spawn a child of itself, which also tries to get an * exclusive lock on the file. * * If all works correctly, the child should not be able to get the * lock. However, the child is able to get the lock. * * This test was extracted from the APR test suite. * * Compile: gcc -Wall -o stc-fcntl-forkexec stc-fcntl-forkexec.c ***********************************************************************/ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #define TESTFILE "testfile.lock" error_t lock_file (int fd, int cmd) { int rc; struct flock l = { 0 }; int fc; l.l_whence = SEEK_SET; /* lock from current point */ l.l_start = 0; /* begin lock at this offset */ l.l_len = 0; /* lock to end of file */ l.l_type = F_WRLCK; fc = cmd; /* keep trying if fcntl() gets interrupted (by a signal) */ while ((rc = fcntl(fd, fc, &l)) < 0 && errno == EINTR) continue; if (rc == -1) { /* on some Unix boxes (e.g., Tru64), we get EACCES instead * of EAGAIN; we don't want APR_STATUS_IS_EAGAIN() matching EACCES * since that breaks other things, so fix up the retcode here */ if (errno == EACCES) { return EAGAIN; } return errno; } return 0; } /* The child */ void tryread () { int fd; error_t status; fd = open (TESTFILE, O_WRONLY, 0666); if (fd < 0) { perror ("child open failed"); exit (2); } status = lock_file (fd, F_SETLK); if (status == 0) exit(0); if (status == EAGAIN) exit(1); exit(2); } int main (int argc, const char *const *argv) { int fd; const char *args[3]; pid_t pid; pid_t pstatus; int exit_int; if (argc > 1) { /* Called to run the child. */ tryread (); fprintf (stderr, "Should not get here!\n"); return 2; } /* Create the lock file. */ fd = open (TESTFILE, O_WRONLY|O_CREAT, 0666); if (fd < 0) { perror ("open failed"); return 1; } /* Lock the file. */ if (lock_file (fd, F_SETLKW) != 0) { perror ("lock"); return 1; } /* Spawn the child reader */ if ((pid = fork ()) < 0) { perror ("fork"); return 1; } else if (pid == 0) { /* child */ args[0] = program_invocation_name; args[1] = "child"; args[2] = NULL; execl (program_invocation_name, program_invocation_name, "child", NULL); fprintf (stderr, "execv failed\n"); _exit (2); } /* Wait for the child. */ do { pstatus = waitpid (pid, &exit_int, WUNTRACED); } while (pstatus < 0 && errno == EINTR); if (WIFEXITED (exit_int)) { exit_int = WEXITSTATUS (exit_int); if (exit_int == 0) printf ("FAILED: Child was able to get a lock when it shouldn't.\n"); else if (exit_int == 1) printf ("SUCCESS: Child was not able to get the lock.\n"); else fprintf (stderr, "Unexpected error from child: %d\n", exit_int); } else fprintf (stderr, "Child did not terminate normally.\n"); /* Close the file */ close (fd); /* Clean up. */ unlink (TESTFILE); return 0; }