--- /dev/null
+/*
+ * semex.c --- System V semaphore exerciser
+ *
+ * by Eric S. Raymond <esr@snark.thyrsus.com>
+ */
+#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(), *ctime();
+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) printf("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) printf(
+ "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) printf("semop in process %d failed: %s\n",
+ getpid(), errmsg(errno));
+ else
+ (void) printf(
+ "semop in process %d succeeded, returning %d\n",
+ getpid(), nops);
+ }
+}
+
+main(argc, argv)
+/* exercise the active file 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) printf( "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(cmdline, "f %d", &semid);
+ (void) printf("Semaphore id %d selected\n", semid);
+ break;
+
+ case 'i': /* set current semaphore number */
+ (void) sscanf(cmdline, "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) printf("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 */
+ {
+ union {
+ int val;
+ struct semid_ds *buf;
+ unsigned short *array;
+ } semval;
+
+ semval.val = 0;
+
+ (void) sscanf(cmdline, "s %d", &semval.val);
+
+ 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(0);
+
+ case '!': /* escape to a shell */
+ (void) system(cmdline + 1);
+ break;
+
+ case '?': /* print on-line help */
+(void)printf("c(reate) key nsems [ce] perms -- create a semaphore group\n");
+(void)printf("f(ind) semid -- selectsemaphore group by id\n");
+(void)printf("i(ndex) semnum -- select a semaphore index\n");
+(void)printf("d(o) op [un] -- do a semaphore operation\n");
+(void)printf("v(alue) semid semnum -- query a semaphore's semval\n");
+(void)printf("p(id) semid semnum -- query a semaphore's sempid\n");
+(void)printf("n(cnt) semid semnum -- query a semaphore's semncnt\n");
+(void)printf("z(cnt) semid semnum -- query a semaphore's semzcnt\n");
+(void)printf("m(mask) semid uid gid mode -- query/set semaphore's mode\n");
+(void)printf("r(emove) semid -- remove a semaphore group\n");
+(void)printf("s(et) semval -- set given semaphore's value\n");
+(void)printf("l(ist) -- run ipcs -sbopt\n");
+(void)printf("x(it) -- exit\n\n");
+
+(void)printf("! cmd -- execute a shell command\n");
+(void)printf("? -- print this help message\n\n");
+ break;
+
+ default:
+ (void)printf("Illegal command -- type ? for help\n");
+ break;
+ }
+
+ (void) sleep(1);
+ }
+ return(0);
+}
+
+/* semex.c ends here */