home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume1 / 8708 / 16 < prev    next >
Encoding:
Internet Message Format  |  1990-07-13  |  17.0 KB

  1. From: eric@snark.UUCP (Eric S. Raymond)
  2. Newsgroups: comp.sources.misc
  3. Subject: Semaphore facilities exerciser for System V
  4. Message-ID: <4193@ncoast.UUCP>
  5. Date: 17 Aug 87 00:57:59 GMT
  6. Sender: allbery@ncoast.UUCP
  7. Lines: 546
  8. Approved: allbery@ncoast.UUCP
  9. X-Archive: comp.sources.misc/8708/16
  10.  
  11. [I'm testing the new version of my submitter; send me mail if this is screwed
  12. up somehow.  ++bsa]
  13.  
  14. /*****************************************************************************
  15.  
  16. NAME
  17.     semex -- interactive exerciser for System V semaphore operations
  18.  
  19. SYNOPSIS
  20.     semex
  21.  
  22. DESCRIPTION
  23.     This is an interactive exerciser for the semctl(2), semget(2) and
  24. semop(2) system calls of System V UNIX. You can use it to experiment with
  25. the semaphore features in order to understand them better. It includes
  26. on-line help.
  27.  
  28.     Calls that might cause semex to block (semop(2) with negative operation
  29. values) are handed to a forked copy of semex. Semex's children emit reports
  30. just before they block and when they unblock, to enable the user to track
  31. what's going on.
  32.  
  33.     Semex can also be used as a semaphores interface for scripts. Command
  34. prompting is suppressed if stdin isn't a tty; so are the verbose
  35. descriptions of actions performed that it normally emits, and the
  36. process-forking described above.
  37.  
  38. COMMANDS
  39.     All arguments of every command are optional; the code tries to give
  40. reasonable defaults to any you leave off. Anytime a `semid' or `semnum'
  41. argument is given, the default semid or semnum for future commands is set
  42. to it.
  43.  
  44.     c(reate) key nsems -[ce] perms -- create a semaphore group
  45.  
  46.     This calls semget(2). The `key' argument should be a long, the `nsems'
  47. argument an int. The third (flags) argument of the semget(2) call is
  48. created from the third and fourth arguments of this command; flags
  49. `c' and `e' stand for IPC_CREAT and IPC_EXCL respectively and `perms'
  50. should be at least three digits of octal permission mask.
  51.     The `current semaphore group id' (semid) is set to the return value
  52. of this command. 
  53.     The default arguments are `0L 1 - 0660' (note that 0L = IPC_PRIVATE).
  54.  
  55.     f(ind) semid                   -- select a semaphore group by id
  56.  
  57.     This changes semex's notion of the current semaphore group id. If the
  58. argument is omitted, the current value is simply printed out.
  59.  
  60.     i(ndex) semnum                 -- select a semaphore index
  61.  
  62.     This changes semex's notion of the current semaphore index. If the
  63. argument is omitted, the current value is simply printed out.
  64.  
  65.     d(o) op -[un]                  -- do a semaphore operation
  66.  
  67.     This command does a semop(2) call. The first argument of the call will
  68. be the currently selected semaphore id. The third argument (number of
  69. operations) will be 1. The (struct sembuf *) second argument will point
  70. to a single operation structure.
  71.     The sem_num field of this structure is set to the `current semaphore
  72. index' value set by the `n' command (normally 1). The sem_op field is set
  73. to the value of the `op' command argument (which should be an integer). The
  74. sem_flg field is set according to the flags in the third command argument;
  75. `u' stands for SEM_UNDO, `n' for IPC_NOWAIT.
  76.     The argument defaults are `0 -'.
  77.  
  78.     v(alue) semid semnum            -- query a semaphore's semval
  79.  
  80.     This command displays the return of a semctl(semid, semnum, GETVAL). If
  81. semnum is omitted it defaults to the currently selected semaphore index. If
  82. semid is omitted it defaults to the currently selected semaphore id.
  83.  
  84.     p(id) semid semnum             -- query a semaphore's sempid
  85.  
  86.     This command displays the return of a semctl(semid, semnum, GETPID). If
  87. semnum is omitted it defaults to the currently selected semaphore index. If
  88. semid is omitted it defaults to the currently selected semaphore id.
  89.  
  90.     n(cnt) semid semnum            -- query a semaphore's semncnt
  91.  
  92.     This command displays the return of a semctl(semid, semnum, GETNCNT). If
  93. semnum is omitted it defaults to the currently selected semaphore index. If
  94. semid is omitted it defaults to the currently selected semaphore id.
  95.  
  96.     z(cnt) semid semnum            -- query a semaphore's semzcnt
  97.  
  98.     This command displays the return of a semctl(semid, semnum, GETZCNT). If
  99. semnum is omitted it defaults to the currently selected semaphore index. If
  100. semid is omitted it defaults to the currently selected semaphore id.
  101.  
  102.     s(et) semval           -- set the value of a semaphore
  103.  
  104.     This command does a semop(2) call using the SETVAL command to set
  105. the value of the currently selected semaphore. The first argument of the call
  106. will be the currently selected semaphore id. The second argument of the call
  107. will be the currently selected semaphore index. The fourth (value) argument
  108. will be the semval argument of the command (which defaults to 0 if omitted).
  109.  
  110.     m(mask) semid uid gid mode    -- query/set a semaphore's mode
  111.  
  112.     The m command with no arguments displays the uid, gid and mode
  113. of the currently selected semaphore group. With one argument, it displays
  114. this information for the given semaphore group. With two or more arguments
  115. it sets whatever portions of the mode and ownership data are given; uid and
  116. gid should be decimal integers and mode at least three digits of octal.
  117.  
  118.     r(emove) semid                -- remove a semaphore group
  119.  
  120.     Do a semctl(2) to remove a semaphore group. If the semid argument is
  121. omitted, the currently selected semaphore group will be removed.
  122.  
  123.     l(ist)                        -- run ipcs -sbopt
  124.  
  125.     This is just a convenience. It displays data on active semaphores.
  126.  
  127.     x(it)                         -- exit
  128.  
  129.     Exit semex. Child semex processes created by 'd' commands will get SIGHUP
  130. and die gracefully.
  131.  
  132.     ! cmd                         -- execute a shell command
  133.  
  134.     Escape to a shell.    
  135.  
  136.     ?                      -- print this help message
  137.  
  138. In addition, typing a newline will simply display the value of the
  139. currently selected semaphore (newline is a synonym for the `v' command).
  140.  
  141. NOTE
  142.     The AT&T documentation entries for semop(2) tell lies about the type of
  143. the second argument. System V Release 1 claims the type is
  144.  
  145.     struct sembuf (*)[]
  146.  
  147. and its lint library entry is correspondingly broken. Release 2 claims
  148. that it's
  149.  
  150.     struct sembuf **
  151.  
  152. but its lint library checks for the correct type, which is
  153.  
  154.     struct sembuf *
  155.  
  156. as one can actually deduce from the description text for both entries.
  157.  
  158. BUGS
  159.    The 'p' and 'v' commands are not Dyskstra's P and V operations.
  160.    There is no way for semex to specify more than one semaphore op at once
  161. in semop(2).
  162.    There is no support for exercising the GETALL or SETALL modes of semctl(2).
  163.  
  164. AUTHOR
  165.    Eric S. Raymond {{ihnp4,seismo}!cbmvax,hplabs!sdcrdcf!burdvax}!snark!eric
  166.  
  167. *****************************************************************************/
  168. /* LINTLIBRARY */
  169.  
  170. #include <stdio.h>
  171. #include <signal.h>
  172. #include <sys/types.h>
  173. #include <sys/ipc.h>
  174. #include <sys/sem.h>
  175.  
  176. extern int errno;
  177. extern void exit();
  178. extern char *strchr(), *strcat();
  179. extern unsigned sleep();
  180.  
  181. /*
  182.  * This will need to change if any future version changes the define values
  183.  * of any UNIX error types. The highest value currently recognized is
  184.  * EIDRM; the code that uses it in errmsg() will issue a generic error type
  185.  * for higher values.
  186.  */
  187. static char *errtypes[] =
  188. {
  189.     "?",
  190.     "EPERM",        /* Not super-user            */
  191.     "ENOENT",        /* No such file or directory        */
  192.     "ESRCH",        /* No such process            */
  193.     "EINTR",        /* interrupted system call        */
  194.     "EIO",        /* I/O error                */
  195.     "ENXIO",        /* No such device or address        */
  196.     "E2BIG",        /* Arg list too long            */
  197.     "ENOEXEC",        /* Exec format error            */
  198.     "EBADF",        /* Bad file number            */
  199.     "ECHILD",        /* No children                */
  200.     "EAGAIN",        /* No more processes            */
  201.     "ENOMEM",        /* Not enough core            */
  202.     "EACCES",        /* Permission denied            */
  203.     "EFAULT",        /* Bad address                */
  204.     "ENOTBLK",        /* Block device required        */
  205.     "EBUSY",        /* Mount device busy            */
  206.     "EEXIST",        /* File exists                */
  207.     "EXDEV",        /* Cross-device link            */
  208.     "ENODEV",        /* No such device            */
  209.     "ENOTDIR",        /* Not a directory            */
  210.     "EISDIR",        /* Is a directory            */
  211.     "EINVAL",        /* Invalid argument            */
  212.     "ENFILE",        /* File table overflow            */
  213.     "EMFILE",        /* Too many open files            */
  214.     "ENOTTY",        /* Not a typewriter            */
  215.     "ETXTBSY",        /* Text file busy            */
  216.     "EFBIG",        /* File too large            */
  217.     "ENOSPC",        /* No space left on device        */
  218.     "ESPIPE",        /* Illegal seek                */
  219.     "EROFS",        /* Read only file system        */
  220.     "EMLINK",        /* Too many links            */
  221.     "EPIPE",        /* Broken pipe                */
  222.     "EDOM",        /* Math arg out of domain of func    */
  223.     "ERANGE",        /* Math result not representable    */
  224.     "ENOMSG",        /* No message of desired type        */
  225.     "EIDRM",        /* Identifier removed            */
  226. };
  227.  
  228. static ushort    parent;
  229. static int    tty;
  230.  
  231. static void sigabort(sig)
  232. /* log the occurrence of a signal, die gracefully if it's SIGHUP */
  233. int    sig;    /* the signal number */
  234. {
  235.     (void) fprintf("Received signal %d\n", sig);
  236.     if (sig == SIGHUP)
  237.     exit(0);
  238. }
  239.  
  240. static char *errmsg(code)
  241. /* return the UNIX error message for a given errno code */
  242. int code;
  243. {
  244.     extern int sys_nerr;
  245.     extern char *sys_errlist[];
  246.     static char ebuf[BUFSIZ];
  247.  
  248.     (void) sprintf(ebuf, "error %d", code);
  249.  
  250.     if (code < sizeof(errtypes) / sizeof(char *))
  251.     {
  252.     (void) strcat(ebuf, " (");
  253.     (void) strcat(ebuf, errtypes[code]);
  254.     (void) strcat(ebuf, ")");
  255.     }
  256.  
  257.     if (code <= sys_nerr)
  258.     {
  259.     (void) strcat(ebuf, ", ");
  260.     (void) strcat(ebuf, sys_errlist[code]);
  261.     }
  262.  
  263.     return(ebuf);
  264. }
  265.  
  266. void shsemop(semid, sops, nops)
  267. /* perform a semaphore op, announcing the specifics of the operation */
  268. int        semid;
  269. struct sembuf    *sops;
  270. int        nops;
  271. {
  272.     if (tty)
  273.     (void) fprintf(stdout,
  274.         "semop(%d, {%hd, %hd, %hd}, %d) in process %d\n",
  275.         semid, sops->sem_num, sops->sem_op, sops->sem_flg, 
  276.         nops, getpid());
  277.  
  278.     nops = semop(semid, sops, nops);
  279.  
  280.     if (tty)
  281.     {
  282.     /* if we're in the parent, give child processes time to report */
  283.     if (getpid() == parent)
  284.         (void) sleep(2);
  285.  
  286.     if (nops == -1)
  287.         (void) fprintf(stdout,"semop in process %d failed: %s\n",
  288.                 getpid(), errmsg(errno));
  289.     else
  290.         (void) fprintf(stdout,
  291.                 "semop in process %d succeeded, returning %d\n",
  292.                 getpid(), nops);
  293.     }
  294. }
  295.  
  296. main(argc, argv)
  297. /* excercise the semaphore functions */
  298. int    argc;
  299. char    *argv[];
  300. {
  301.     key_t        key = IPC_PRIVATE;
  302.     int            sc, rv, nsems, perms, semflg, semid, semnum = 0;
  303.     struct sembuf   sop;
  304.     char        cmdline[BUFSIZ], strbuf[BUFSIZ];
  305.  
  306.     parent = getpid();
  307.     tty = isatty(fileno(stdin));
  308.  
  309.     for (rv = SIGHUP; rv <= SIGTERM; rv++)
  310.     (void) signal(rv, sigabort);
  311.  
  312.     if (tty)
  313.     {
  314.     (void) puts("This is the semaphore exerciser, type ? for help");
  315.     (void) fprintf(stdout, "The exerciser process pid is %d\n", parent);
  316.     }
  317.     while ((!tty || fputs("> ", stdout) != EOF) && gets(cmdline))
  318.     {
  319.     errno = 0;
  320.  
  321.     switch(cmdline[0])
  322.     {
  323.     case 'c':    /* create and select a new semaphore */
  324.         key = IPC_PRIVATE;
  325.         nsems = 1;
  326.         perms = 0660;    /* read & alter by owning user & group */
  327.         semflg = 0;
  328.  
  329.         sc = sscanf(cmdline, "c %ld %d %s %o", &key,&nsems,strbuf,&perms);
  330.  
  331.         if (sc >= 3)
  332.         {
  333.         if (strchr(strbuf, 'c') != (char *)NULL)
  334.             semflg |= IPC_CREAT;
  335.  
  336.         if (strchr(strbuf, 'e') != (char *)NULL)
  337.             semflg |= IPC_EXCL;
  338.         }
  339.         semflg |= perms;
  340.  
  341.         semid = semget(key, nsems, semflg);
  342.         if (tty)
  343.         {
  344.         (void) printf("semget(%ld, %d, %04o) ",    key, nsems, semflg);
  345.         if (semid == -1)
  346.             (void) printf("failed: %s\n", errmsg(errno));
  347.         else
  348.             (void) printf("succeeded, semid = %d\n", semid);
  349.         }
  350.         break;
  351.  
  352.     case 'f':    /* select a given semaphore group */
  353.         (void) sscanf("f %d", &semid);
  354.         (void) printf("Semaphore id %d selected\n", semid);
  355.         break;
  356.  
  357.     case 'i':    /* set current semaphore number */
  358.         (void) sscanf("i %d", &semnum);
  359.         (void) printf("Semaphore index %d selected\n", semnum);
  360.         break;
  361.  
  362.     case 'd':    /* perform a semaphore operation */
  363.         sc = sscanf(cmdline, "d %hd %s", &sop.sem_op, strbuf);
  364.         sop.sem_num = semnum;
  365.  
  366.         if (sc == 0)
  367.         sop.sem_op = 0;
  368.  
  369.         if (sc <= 1)
  370.         sop.sem_flg = 0;
  371.  
  372.         if (sc == 2)
  373.         {
  374.         if (strchr(strbuf, 'u') != (char *)NULL)
  375.             sop.sem_flg |= SEM_UNDO;
  376.  
  377.         if (strchr(strbuf, 'n') != (char *)NULL)
  378.             sop.sem_flg |= IPC_NOWAIT;
  379.         }
  380.  
  381.         if (tty && sop.sem_op < 0)
  382.         {
  383.         if (rv = fork())    /* parent side */
  384.         {
  385.             (void)fprintf(stdout,"Spawning child with pid = %d\n",rv);
  386.             (void) sleep(1);
  387.         }            
  388.         else            /* child side */
  389.         {
  390.             (void) sleep(1);
  391.             shsemop(semid, &sop, 1);
  392.             exit(0);
  393.         }
  394.         }
  395.         else
  396.         shsemop(semid, &sop, 1);
  397.         break;
  398.  
  399.     case 'v':
  400.     case '\0':
  401.         (void) sscanf(cmdline, "v %d %d\n", &semid, &semnum);
  402.         if (tty)
  403.         (void) printf("semctl(%d, %d, GETVAL) returns %d\n",
  404.             semid, semnum, semctl(semid, semnum, GETVAL));
  405.         break;
  406.  
  407.     case 'p':
  408.         (void) sscanf(cmdline, "p %d %d\n", &semid, &semnum);
  409.         if (tty)
  410.         (void) printf("semctl(%d, %d, GETPID) returns %d\n",
  411.             semid, semnum, semctl(semid, semnum, GETPID));
  412.         break;
  413.  
  414.     case 'n':
  415.         (void) sscanf(cmdline, "n %d %d\n", &semid, &semnum);
  416.         if (tty)
  417.         (void) printf("semctl(%d, %d, GETNCNT) returns %d\n",
  418.             semid, semnum, semctl(semid, semnum, GETNCNT));
  419.         break;
  420.  
  421.     case 'z':
  422.         (void) sscanf(cmdline, "z %d %d\n", &semid, &semnum);
  423.         if (tty)
  424.         (void) printf("semctl(%d, %d, GETZCNT) returns %d\n",
  425.             semid, semnum, semctl(semid, semnum, GETZCNT));
  426.         break;
  427.  
  428.     case 's':    /* set the value of a semaphore */
  429.         {
  430.         int    semval = 0;
  431.  
  432.         (void) sscanf(cmdline, "s %d", &semval);
  433.  
  434.         rv = semctl(semid, semnum, SETVAL, semval);
  435.         if (tty)
  436.         {
  437.             (void) printf("semctl(%d, %d, SETVAL, %d) ",
  438.                         semid, semnum, semval);
  439.             if (rv == -1)
  440.             (void) printf("failed: %s\n", errmsg(errno));
  441.             else
  442.             (void) printf("succeeded, returning %d\n", rv);
  443.         }
  444.         }
  445.         break;
  446.  
  447.     case 'm':    /* query-set mode information */
  448.         {
  449.         int            uid, gid, mode;
  450.         struct semid_ds        ds;
  451.  
  452.         sc = sscanf(cmdline, "m %d %d %d %o", &semid,&uid,&gid,&mode);
  453.  
  454.         rv = semctl(semid, semnum, IPC_STAT, &ds);
  455.  
  456.         /* retrieve and show the existing modes */
  457.         (void) printf("semctl(%d, %d, IPC_STAT, &ds) ", semnum,semid);
  458.         if (rv == -1)
  459.             (void) printf("failed: %s\n", errmsg(errno));
  460.         else
  461.         {
  462.         (void) printf("succeeded, returning %d\n", rv);
  463.         (void) printf("Here is the semaphore group data:\nds = {\n");
  464.         (void) printf("    sem_perm.uid = %d\n", ds.sem_perm.uid);
  465.         (void) printf("    sem_perm.gid = %d\n", ds.sem_perm.gid);
  466.         (void) printf("    sem_perm.mode = %04o\n", ds.sem_perm.mode);
  467.         (void) printf("    sem_nsems = %d\n", ds.sem_nsems);
  468.         (void) printf("    sem_otime = (%ld) %s",
  469.                     ds.sem_otime, ctime(&ds.sem_otime));
  470.         (void) printf("    sem_ctime = (%ld) %s}\n",
  471.                     ds.sem_ctime, ctime(&ds.sem_ctime));
  472.         }
  473.  
  474.         /* if two or more arguments were given, set new modes */
  475.         if (sc >= 2)
  476.         {
  477.             ds.sem_perm.uid = uid;
  478.             if (sc >= 3)
  479.             ds.sem_perm.gid = gid;
  480.             if (sc >= 4)
  481.             ds.sem_perm.mode = mode;
  482.  
  483.             rv = semctl(semid, semnum, IPC_SET, &ds);
  484.             if (tty)
  485.             {
  486.             (void) printf("semctl(%d, %d, IPC_SET, &ds) ",
  487.                     semnum, semid);
  488.             if (rv == -1)
  489.                 (void) printf("failed: %s\n", errmsg(errno));
  490.             else
  491.                 (void) printf("succeeded, returning %d\n", rv);
  492.             }
  493.         }
  494.         }
  495.         break;
  496.  
  497.     case 'r':    /* delete an existing semaphore */
  498.         (void) sscanf(cmdline, "r %d", &semid);
  499.  
  500.         rv = semctl(semid, 0, IPC_RMID);
  501.  
  502.         if (tty)
  503.         {
  504.         (void) printf("semctl(%d, 0, IPC_RMID) ", semid);
  505.         if (rv == -1)
  506.             (void) printf("failed: %s\n", errmsg(errno));
  507.         else
  508.             (void) printf("succeeded\n");
  509.         }
  510.         break;
  511.  
  512.     case 'l':    /* list status of semaphore groups */
  513.         (void) system("exec ipcs -sbopt");
  514.         break;
  515.  
  516.     case 'x':    /* leave */
  517.         return;
  518.  
  519.     case '!':    /* escape to a shell */
  520.         (void) system(cmdline + 1);
  521.         break;
  522.  
  523.     case '?':    /* print on-line help */
  524. printf("c(reate) key nsems [ce] perms  -- create a semaphore group\n");
  525. printf("f(ind) semid                   -- select a semaphore group by id\n");
  526. printf("i(ndex) semnum                 -- select a semaphore index\n");
  527. printf("d(o) op [un]                   -- do a semaphore operation\n");
  528. printf("v(alue) semid semnum           -- query a semaphore's semval\n");
  529. printf("p(id) semid semnum             -- query a semaphore's sempid\n");
  530. printf("n(cnt) semid semnum            -- query a semaphore's semncnt\n");
  531. printf("z(cnt) semid semnum            -- query a semaphore's semzcnt\n");
  532. printf("m(mask) semid uid gid mode     -- query/set a semaphore's mode\n");
  533. printf("r(emove) semid                 -- remove a semaphore group\n");
  534. printf("s(et) semval                   -- set the value of a semaphore\n");
  535. printf("l(ist)                         -- run ipcs -sbopt\n");
  536. printf("x(it)                          -- exit\n\n");
  537.  
  538. printf("! cmd                          -- execute a shell command\n");
  539. printf("?                              -- print this help message\n\n");
  540.         break;
  541.  
  542.     default:
  543.         (void)printf("Illegal command -- type ? for help\n");
  544.         break;
  545.     }
  546.  
  547.     (void) sleep(1);
  548.     }
  549. }
  550.  
  551. /* semex.c ends here */
  552. -- 
  553.       Eric S. Raymond
  554.       UUCP:  {{seismo,ihnp4,rutgers}!cbmvax,sdcrdcf!burdvax}!snark!eric
  555.       Post:  22 South Warren Avenue, Malvern, PA 19355
  556.       Phone: (215)-296-5718
  557.