/*------------------------------------------------------------ | This program plays with record locking (Stevens 12.3), and | also uses a signal handler for alarm() (see last lab notes). | | This program first creates a file, overwriting the file | if it previously existed. Then it puts a write lock on the | file, forks off a child, and goes to sleep for 15 seconds. | | While the parent is sleeping, the alarm() function wakes it | up every second and the associated signal handler for | SIG_ALRM causes it to output the time remaining in it's sleep. | | The child immediately tries to obtain a read_lock on the | same file, but it unable to until the parent wakes up & releases | the lock. | | Author: J. Gamble, Mar 2/2000 ------------------------------------------------------------ */ #include #include #include #include #include /* for waitpid() */ #include /* for sigaction() */ #include /* for assert() */ /* these macros are copied from Stevens p. 370 */ #define read_lock(fd, offset, whence, len) \ lock_reg (fd, F_SETLK, F_RDLCK, offset, whence, len) #define readw_lock(fd, offset, whence, len) \ lock_reg (fd, F_SETLKW, F_RDLCK, offset, whence, len) #define write_lock(fd, offset, whence, len) \ lock_reg (fd, F_SETLK, F_WRLCK, offset, whence, len) #define writew_lock(fd, offset, whence, len) \ lock_reg (fd, F_SETLKW, F_WRLCK, offset, whence, len) #define un_lock(fd, offset, whence, len) \ lock_reg (fd, F_SETLK, F_UNLCK, offset, whence, len) void myHandler(int signo); int lock_reg (int fd, int cmd, int type, off_t offset, int whence, off_t len); #define TRUE 1 #define FALSE 0 #define SLEEP_DUR 15 /* sleep duration */ int parentSleeping = TRUE; /*------------------------------------------------------------ */ int main(int argc, char *argv[]) { int pid, pid2; /* hold childrens' process-ids in parent */ struct sigaction mySignalStruct; FILE *fp = NULL; int fd = 0; /* * change stdout's buffering to be "no buffering" -- see lab 4 */ assert (setvbuf(stdout, NULL, _IONBF, NULL) == 0); /* * setup signal handler for SIGALRM */ mySignalStruct.sa_handler = myHandler; mySignalStruct.sa_flags = 0; mySignalStruct.sa_flags |= SA_RESTART; /* restart interrupted system calls */ sigemptyset( &mySignalStruct.sa_mask); assert (sigaction(SIGALRM, &mySignalStruct, NULL) == 0); /* * create a file to lock */ fp = tmpfile(); /* returns a temporary file, see Stevens 5.13 */ assert(fp != NULL); fprintf(fp, "Writing a line of text to the file\n"); fflush(fp); /* * write-lock the file */ fd = fileno(fp); assert (writew_lock(fd, 0, SEEK_SET, 0) == 0); printf("Parent has write-lock on the file\n"); /* * Now create child & put parent to sleep for SLEEP_DUR seconds * (but waking up every second to report to stdout) */ alarm(1); switch ( pid = fork() ) { case 0: /* child */ /* * note: Stevens p.192 indicates that fork() causes * the set of pending signals for the child to be * set to the empty set. So the above alarm() won't * affect the child */ printf("Child has pid [%d]\n", getpid()); printf("Child attempting to get read-lock on the file\n"); assert (readw_lock(fd, 0, SEEK_SET, 0) == 0); printf("Child has got the read-lock on the file\n"); exit(0); /* mission accomplished, quitting */ case -1: /* error: resources must be exceeded */ printf("Fork returned error code, no child\n"); exit(1); default: /* parent */ printf("Parent has pid [%d], child has pid [%d].\n", getpid(), pid); printf("Parent going to sleep..\n"); /* according to sigactions' manpage, when describing SA_RESTART, * - wait() is an automatically restarted system call * - sleep() and pause() are NOT automatically restarted * * so the following obscurity is done to create a 10-second sleep for the * parent, and still allow sigaction() to auto-restart the system call */ switch ( pid2 = fork() ) { case 0: /* child: the exit(0) wakes up the parent's waitpid() */ sleep(SLEEP_DUR); exit(0); case -1: /* error */ printf("Fork #2 error\n"); exit(1); default: /* parent: sleep for SLEEP_DUR seconds */ assert (waitpid(pid2, NULL, 0) > 0); parentSleeping = FALSE; /* parent: release write-lock on the file */ assert (un_lock(fd, 0, SEEK_SET, 0) == 0); } /* clean-up zombie */ printf("Parent waiting for childpid [%d]\n", pid); assert (waitpid(pid, NULL, 0) > 0); break; } } /*------------------------------------------------------------ * Signal handler for SIGALRM */ void myHandler(int signo) { static int timeLeft = SLEEP_DUR; if (parentSleeping) { printf("Process %d caught SIGALRM. Seconds remaining in sleep: %d\n", (int) getpid(), --timeLeft); alarm(1); /* reset alarm to go off again */ } } /*------------------------------------------------------------ * Function to lock or unlock a region of a file. * Copied from Stevens, p. 370 */ int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) { struct flock lock; lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */ lock.l_start = offset; /* byte offset, relative to l_whence */ lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ lock.l_len = len; /* #bytes (0 means to EOF) */ return ( fcntl(fd, cmd, &lock) ); }