eric@snark.UUCP (Eric S. Raymond) (08/17/87)
[I'm testing the new version of my submitter; send me mail if this is screwed up somehow. ++bsa] /***************************************************************************** NAME semex -- interactive exerciser for System V semaphore operations SYNOPSIS semex DESCRIPTION This is an interactive exerciser for the semctl(2), semget(2) and semop(2) system calls of System V UNIX. You can use it to experiment with the semaphore features in order to understand them better. It includes on-line help. Calls that might cause semex to block (semop(2) with negative operation values) are handed to a forked copy of semex. Semex's children emit reports just before they block and when they unblock, to enable the user to track what's going on. Semex can also be used as a semaphores interface for scripts. Command prompting is suppressed if stdin isn't a tty; so are the verbose descriptions of actions performed that it normally emits, and the process-forking described above. COMMANDS All arguments of every command are optional; the code tries to give reasonable defaults to any you leave off. Anytime a `semid' or `semnum' argument is given, the default semid or semnum for future commands is set to it. c(reate) key nsems -[ce] perms -- create a semaphore group This calls semget(2). The `key' argument should be a long, the `nsems' argument an int. The third (flags) argument of the semget(2) call is created from the third and fourth arguments of this command; flags `c' and `e' stand for IPC_CREAT and IPC_EXCL respectively and `perms' should be at least three digits of octal permission mask. The `current semaphore group id' (semid) is set to the return value of this command. The default arguments are `0L 1 - 0660' (note that 0L = IPC_PRIVATE). f(ind) semid -- select a semaphore group by id This changes semex's notion of the current semaphore group id. If the argument is omitted, the current value is simply printed out. i(ndex) semnum -- select a semaphore index This changes semex's notion of the current semaphore index. If the argument is omitted, the current value is simply printed out. d(o) op -[un] -- do a semaphore operation This command does a semop(2) call. The first argument of the call will be the currently selected semaphore id. The third argument (number of operations) will be 1. The (struct sembuf *) second argument will point to a single operation structure. The sem_num field of this structure is set to the `current semaphore index' value set by the `n' command (normally 1). The sem_op field is set to the value of the `op' command argument (which should be an integer). The sem_flg field is set according to the flags in the third command argument; `u' stands for SEM_UNDO, `n' for IPC_NOWAIT. The argument defaults are `0 -'. v(alue) semid semnum -- query a semaphore's semval This command displays the return of a semctl(semid, semnum, GETVAL). If semnum is omitted it defaults to the currently selected semaphore index. If semid is omitted it defaults to the currently selected semaphore id. p(id) semid semnum -- query a semaphore's sempid This command displays the return of a semctl(semid, semnum, GETPID). If semnum is omitted it defaults to the currently selected semaphore index. If semid is omitted it defaults to the currently selected semaphore id. n(cnt) semid semnum -- query a semaphore's semncnt This command displays the return of a semctl(semid, semnum, GETNCNT). If semnum is omitted it defaults to the currently selected semaphore index. If semid is omitted it defaults to the currently selected semaphore id. z(cnt) semid semnum -- query a semaphore's semzcnt This command displays the return of a semctl(semid, semnum, GETZCNT). If semnum is omitted it defaults to the currently selected semaphore index. If semid is omitted it defaults to the currently selected semaphore id. s(et) semval -- set the value of a semaphore This command does a semop(2) call using the SETVAL command to set the value of the currently selected semaphore. The first argument of the call will be the currently selected semaphore id. The second argument of the call will be the currently selected semaphore index. The fourth (value) argument will be the semval argument of the command (which defaults to 0 if omitted). m(mask) semid uid gid mode -- query/set a semaphore's mode The m command with no arguments displays the uid, gid and mode of the currently selected semaphore group. With one argument, it displays this information for the given semaphore group. With two or more arguments it sets whatever portions of the mode and ownership data are given; uid and gid should be decimal integers and mode at least three digits of octal. r(emove) semid -- remove a semaphore group Do a semctl(2) to remove a semaphore group. If the semid argument is omitted, the currently selected semaphore group will be removed. l(ist) -- run ipcs -sbopt This is just a convenience. It displays data on active semaphores. x(it) -- exit Exit semex. Child semex processes created by 'd' commands will get SIGHUP and die gracefully. ! cmd -- execute a shell command Escape to a shell. ? -- print this help message In addition, typing a newline will simply display the value of the currently selected semaphore (newline is a synonym for the `v' command). NOTE The AT&T documentation entries for semop(2) tell lies about the type of the second argument. System V Release 1 claims the type is struct sembuf (*)[] and its lint library entry is correspondingly broken. Release 2 claims that it's struct sembuf ** but its lint library checks for the correct type, which is struct sembuf * as one can actually deduce from the description text for both entries. BUGS The 'p' and 'v' commands are not Dyskstra's P and V operations. There is no way for semex to specify more than one semaphore op at once in semop(2). There is no support for exercising the GETALL or SETALL modes of semctl(2). AUTHOR Eric S. Raymond {{ihnp4,seismo}!cbmvax,hplabs!sdcrdcf!burdvax}!snark!eric *****************************************************************************/ /* LINTLIBRARY */ #include <stdio.h> #include <signal.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> extern int errno; extern void exit(); extern char *strchr(), *strcat(); extern unsigned sleep(); /* * This will need to change if any future version changes the define values * of any UNIX error types. The highest value currently recognized is * EIDRM; the code that uses it in errmsg() will issue a generic error type * for higher values. */ static char *errtypes[] = { "?", "EPERM", /* Not super-user */ "ENOENT", /* No such file or directory */ "ESRCH", /* No such process */ "EINTR", /* interrupted system call */ "EIO", /* I/O error */ "ENXIO", /* No such device or address */ "E2BIG", /* Arg list too long */ "ENOEXEC", /* Exec format error */ "EBADF", /* Bad file number */ "ECHILD", /* No children */ "EAGAIN", /* No more processes */ "ENOMEM", /* Not enough core */ "EACCES", /* Permission denied */ "EFAULT", /* Bad address */ "ENOTBLK", /* Block device required */ "EBUSY", /* Mount device busy */ "EEXIST", /* File exists */ "EXDEV", /* Cross-device link */ "ENODEV", /* No such device */ "ENOTDIR", /* Not a directory */ "EISDIR", /* Is a directory */ "EINVAL", /* Invalid argument */ "ENFILE", /* File table overflow */ "EMFILE", /* Too many open files */ "ENOTTY", /* Not a typewriter */ "ETXTBSY", /* Text file busy */ "EFBIG", /* File too large */ "ENOSPC", /* No space left on device */ "ESPIPE", /* Illegal seek */ "EROFS", /* Read only file system */ "EMLINK", /* Too many links */ "EPIPE", /* Broken pipe */ "EDOM", /* Math arg out of domain of func */ "ERANGE", /* Math result not representable */ "ENOMSG", /* No message of desired type */ "EIDRM", /* Identifier removed */ }; static ushort parent; static int tty; static void sigabort(sig) /* log the occurrence of a signal, die gracefully if it's SIGHUP */ int sig; /* the signal number */ { (void) fprintf("Received signal %d\n", sig); if (sig == SIGHUP) exit(0); } static char *errmsg(code) /* return the UNIX error message for a given errno code */ int code; { extern int sys_nerr; extern char *sys_errlist[]; static char ebuf[BUFSIZ]; (void) sprintf(ebuf, "error %d", code); if (code < sizeof(errtypes) / sizeof(char *)) { (void) strcat(ebuf, " ("); (void) strcat(ebuf, errtypes[code]); (void) strcat(ebuf, ")"); } if (code <= sys_nerr) { (void) strcat(ebuf, ", "); (void) strcat(ebuf, sys_errlist[code]); } return(ebuf); } void shsemop(semid, sops, nops) /* perform a semaphore op, announcing the specifics of the operation */ int semid; struct sembuf *sops; int nops; { if (tty) (void) fprintf(stdout, "semop(%d, {%hd, %hd, %hd}, %d) in process %d\n", semid, sops->sem_num, sops->sem_op, sops->sem_flg, nops, getpid()); nops = semop(semid, sops, nops); if (tty) { /* if we're in the parent, give child processes time to report */ if (getpid() == parent) (void) sleep(2); if (nops == -1) (void) fprintf(stdout,"semop in process %d failed: %s\n", getpid(), errmsg(errno)); else (void) fprintf(stdout, "semop in process %d succeeded, returning %d\n", getpid(), nops); } } main(argc, argv) /* excercise the semaphore functions */ int argc; char *argv[]; { key_t key = IPC_PRIVATE; int sc, rv, nsems, perms, semflg, semid, semnum = 0; struct sembuf sop; char cmdline[BUFSIZ], strbuf[BUFSIZ]; parent = getpid(); tty = isatty(fileno(stdin)); for (rv = SIGHUP; rv <= SIGTERM; rv++) (void) signal(rv, sigabort); if (tty) { (void) puts("This is the semaphore exerciser, type ? for help"); (void) fprintf(stdout, "The exerciser process pid is %d\n", parent); } while ((!tty || fputs("> ", stdout) != EOF) && gets(cmdline)) { errno = 0; switch(cmdline[0]) { case 'c': /* create and select a new semaphore */ key = IPC_PRIVATE; nsems = 1; perms = 0660; /* read & alter by owning user & group */ semflg = 0; sc = sscanf(cmdline, "c %ld %d %s %o", &key,&nsems,strbuf,&perms); if (sc >= 3) { if (strchr(strbuf, 'c') != (char *)NULL) semflg |= IPC_CREAT; if (strchr(strbuf, 'e') != (char *)NULL) semflg |= IPC_EXCL; } semflg |= perms; semid = semget(key, nsems, semflg); if (tty) { (void) printf("semget(%ld, %d, %04o) ", key, nsems, semflg); if (semid == -1) (void) printf("failed: %s\n", errmsg(errno)); else (void) printf("succeeded, semid = %d\n", semid); } break; case 'f': /* select a given semaphore group */ (void) sscanf("f %d", &semid); (void) printf("Semaphore id %d selected\n", semid); break; case 'i': /* set current semaphore number */ (void) sscanf("i %d", &semnum); (void) printf("Semaphore index %d selected\n", semnum); break; case 'd': /* perform a semaphore operation */ sc = sscanf(cmdline, "d %hd %s", &sop.sem_op, strbuf); sop.sem_num = semnum; if (sc == 0) sop.sem_op = 0; if (sc <= 1) sop.sem_flg = 0; if (sc == 2) { if (strchr(strbuf, 'u') != (char *)NULL) sop.sem_flg |= SEM_UNDO; if (strchr(strbuf, 'n') != (char *)NULL) sop.sem_flg |= IPC_NOWAIT; } if (tty && sop.sem_op < 0) { if (rv = fork()) /* parent side */ { (void)fprintf(stdout,"Spawning child with pid = %d\n",rv); (void) sleep(1); } else /* child side */ { (void) sleep(1); shsemop(semid, &sop, 1); exit(0); } } else shsemop(semid, &sop, 1); break; case 'v': case '\0': (void) sscanf(cmdline, "v %d %d\n", &semid, &semnum); if (tty) (void) printf("semctl(%d, %d, GETVAL) returns %d\n", semid, semnum, semctl(semid, semnum, GETVAL)); break; case 'p': (void) sscanf(cmdline, "p %d %d\n", &semid, &semnum); if (tty) (void) printf("semctl(%d, %d, GETPID) returns %d\n", semid, semnum, semctl(semid, semnum, GETPID)); break; case 'n': (void) sscanf(cmdline, "n %d %d\n", &semid, &semnum); if (tty) (void) printf("semctl(%d, %d, GETNCNT) returns %d\n", semid, semnum, semctl(semid, semnum, GETNCNT)); break; case 'z': (void) sscanf(cmdline, "z %d %d\n", &semid, &semnum); if (tty) (void) printf("semctl(%d, %d, GETZCNT) returns %d\n", semid, semnum, semctl(semid, semnum, GETZCNT)); break; case 's': /* set the value of a semaphore */ { int semval = 0; (void) sscanf(cmdline, "s %d", &semval); rv = semctl(semid, semnum, SETVAL, semval); if (tty) { (void) printf("semctl(%d, %d, SETVAL, %d) ", semid, semnum, semval); if (rv == -1) (void) printf("failed: %s\n", errmsg(errno)); else (void) printf("succeeded, returning %d\n", rv); } } break; case 'm': /* query-set mode information */ { int uid, gid, mode; struct semid_ds ds; sc = sscanf(cmdline, "m %d %d %d %o", &semid,&uid,&gid,&mode); rv = semctl(semid, semnum, IPC_STAT, &ds); /* retrieve and show the existing modes */ (void) printf("semctl(%d, %d, IPC_STAT, &ds) ", semnum,semid); if (rv == -1) (void) printf("failed: %s\n", errmsg(errno)); else { (void) printf("succeeded, returning %d\n", rv); (void) printf("Here is the semaphore group data:\nds = {\n"); (void) printf(" sem_perm.uid = %d\n", ds.sem_perm.uid); (void) printf(" sem_perm.gid = %d\n", ds.sem_perm.gid); (void) printf(" sem_perm.mode = %04o\n", ds.sem_perm.mode); (void) printf(" sem_nsems = %d\n", ds.sem_nsems); (void) printf(" sem_otime = (%ld) %s", ds.sem_otime, ctime(&ds.sem_otime)); (void) printf(" sem_ctime = (%ld) %s}\n", ds.sem_ctime, ctime(&ds.sem_ctime)); } /* if two or more arguments were given, set new modes */ if (sc >= 2) { ds.sem_perm.uid = uid; if (sc >= 3) ds.sem_perm.gid = gid; if (sc >= 4) ds.sem_perm.mode = mode; rv = semctl(semid, semnum, IPC_SET, &ds); if (tty) { (void) printf("semctl(%d, %d, IPC_SET, &ds) ", semnum, semid); if (rv == -1) (void) printf("failed: %s\n", errmsg(errno)); else (void) printf("succeeded, returning %d\n", rv); } } } break; case 'r': /* delete an existing semaphore */ (void) sscanf(cmdline, "r %d", &semid); rv = semctl(semid, 0, IPC_RMID); if (tty) { (void) printf("semctl(%d, 0, IPC_RMID) ", semid); if (rv == -1) (void) printf("failed: %s\n", errmsg(errno)); else (void) printf("succeeded\n"); } break; case 'l': /* list status of semaphore groups */ (void) system("exec ipcs -sbopt"); break; case 'x': /* leave */ return; case '!': /* escape to a shell */ (void) system(cmdline + 1); break; case '?': /* print on-line help */ printf("c(reate) key nsems [ce] perms -- create a semaphore group\n"); printf("f(ind) semid -- select a semaphore group by id\n"); printf("i(ndex) semnum -- select a semaphore index\n"); printf("d(o) op [un] -- do a semaphore operation\n"); printf("v(alue) semid semnum -- query a semaphore's semval\n"); printf("p(id) semid semnum -- query a semaphore's sempid\n"); printf("n(cnt) semid semnum -- query a semaphore's semncnt\n"); printf("z(cnt) semid semnum -- query a semaphore's semzcnt\n"); printf("m(mask) semid uid gid mode -- query/set a semaphore's mode\n"); printf("r(emove) semid -- remove a semaphore group\n"); printf("s(et) semval -- set the value of a semaphore\n"); printf("l(ist) -- run ipcs -sbopt\n"); printf("x(it) -- exit\n\n"); printf("! cmd -- execute a shell command\n"); printf("? -- print this help message\n\n"); break; default: (void)printf("Illegal command -- type ? for help\n"); break; } (void) sleep(1); } } /* semex.c ends here */ -- Eric S. Raymond UUCP: {{seismo,ihnp4,rutgers}!cbmvax,sdcrdcf!burdvax}!snark!eric Post: 22 South Warren Avenue, Malvern, PA 19355 Phone: (215)-296-ed w