| /* |
| * Copyright (C) Bull S.A. 1996 |
| * Copyright (c) International Business Machines Corp., 2001 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| /*---------------------------------------------------------------------+ |
| | semaphore_test_03 | |
| | ==================================================================== | |
| | | |
| | Description: Verify semop () command options | |
| | | |
| | Algorithm: o Spawn N child processes | |
| | | |
| | o Obtain N semaphores with semget (IPC_PRIVATE) | |
| | | |
| | o Call semop () with variations of the following | |
| | parameters: | |
| | | |
| | sem_op: negative, 0, positive | |
| | sem_flg: IPC_NOWAIT, SEM_UNDO | |
| | | |
| | System calls: The following system calls are made | |
| | | |
| | semget () - Gets a set of semaphores | |
| | semctl () - Controls semaphore operations | |
| | semop () - Performs semaphore operations | |
| | | |
| | Usage: semaphore_test_03 [-p nprocs] [-s nsems] | |
| | | |
| | where: nprocs - number of child processes to spawn | |
| | nsems - number of semaphores (per process) | |
| | | |
| | To compile: cc -o semaphore_test_03 semaphore_test_03.c | |
| | | |
| | Last update: Ver. 1.6, 7/21/94 13:37:28 | |
| | | |
| | Change Activity | |
| | | |
| | Version Date Name Reason | |
| | 0.1 050689 CTU Initial version | |
| | 0.2 111993 DJK Modify for AIX version 4.1 | |
| | 1.2 021494 DJK Moved to "prod" directory | |
| | 1.3 Jan-28-02 Manoj Iyer, IBM Austin, TX.manjo@austin.ibm.com| |
| | Modified - Ported to work on PPC64. | |
| | | |
| +---------------------------------------------------------------------*/ |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <sys/ipc.h> |
| #include <sys/param.h> |
| #include <sys/sem.h> |
| #include <sys/shm.h> |
| #include <sys/stat.h> |
| #include <sys/signal.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| |
| /* |
| * Defines |
| * |
| * MAX_SEMAPHORES: maximum number of semaphores per id (limited by |
| * maximum number of semaphore operations per call by semop () function). |
| * |
| * MAX_CHILDREN: maximum number of child processes to spawn |
| * |
| * DEFAULT_NUM_SEMAPHORES: default number of semaphores to create unless |
| * specified with (-s nsems) command line option |
| * |
| * DEFAULT_NUM_CHILDREN: default number of child processes to spawn unless |
| * specified with (-p nprocs) command line option |
| * |
| * USAGE: usage statement macro |
| * |
| * SEMOP_TABLE: macro for printing attempted semop command combinations |
| */ |
| # define MAX_SEMAPHORES 32 |
| # define MAX_CHILDREN 200 |
| # define DEFAULT_NUM_SEMAPHORES 16 |
| # define DEFAULT_NUM_CHILDREN 0 |
| |
| #define USAGE "\nUsage: %s [-s nsems] [-p nproc]\n\n" \ |
| "\t-s nsems number of semaphores (per process)\n\n" \ |
| "\t-p nproc number of child processes to spawn\n\n" |
| #define SEMOP_TABLE(p1,p2,p3,p4) \ |
| if (proc_pid == parent_pid) \ |
| printf ("\t %3d %3d %-10s %-20s\n", p1, p2, p3, p4) |
| |
| #define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } } |
| |
| /* |
| * Function prototypes |
| * |
| * setup_signal_handler (): Sets up signal handler for SIGUSR1 |
| * test_commands (): Tests semget () and semctl () commands |
| * handler (): Signal handler |
| * sys_error (): System error message function |
| * error (): Error message function |
| * parse_args (): Parse command line arguments |
| * catch: Signal catching function for SIGUSR1 signal |
| */ |
| static void setup_signal_handler (); |
| static void test_commands (); |
| static void sys_error (const char *, int); |
| static void error (const char *, int); |
| static void parse_args (int, char **); |
| static void catch (int); |
| |
| /* |
| * Structures and Global variables: |
| * |
| * nsems: number of semaphores to create (per process) |
| * nprocs: number of child processes to spawn |
| * childpid: array containing process id's of the child processes |
| * parent_pid: process id of parent process |
| */ |
| int nsems = DEFAULT_NUM_SEMAPHORES; |
| int nprocs = DEFAULT_NUM_CHILDREN; |
| pid_t childpid [MAX_CHILDREN]; |
| pid_t parent_pid; |
| pid_t errpid; |
| |
| union semun { |
| int val; |
| struct semid_ds *buf; |
| unsigned short *array; |
| } arg; |
| |
| /*---------------------------------------------------------------------+ |
| | main | |
| | ==================================================================== | |
| | | |
| | Function: Main program (see prolog for more details) | |
| | | |
| | Returns: (0) Successful completion | |
| | (-1) Error occurred | |
| | | |
| +---------------------------------------------------------------------*/ |
| int main (int argc, char **argv) |
| { |
| pid_t pid; /* Child's process id */ |
| int proc; /* Fork loop index */ |
| int status; /* Child's exit status */ |
| |
| /* |
| * Parse command line arguments, print out program header, setup |
| * signal handler (for SIGUSR1) and save parent process id. |
| */ |
| parse_args (argc, argv); |
| printf ("%s: IPC Semaphore TestSuite program\n", *argv); |
| fflush (stdout); |
| setup_signal_handler (); |
| errpid = parent_pid = getpid (); |
| |
| if (nsems < 8) nsems = 8; |
| |
| /* |
| * Fork off the additional processes. |
| */ |
| if (nprocs > 0) { |
| printf ("\n\tParent: spawning %d child processes\n", nprocs); |
| fflush (stdout); |
| } |
| for (proc = 1; proc < nprocs; proc++) { |
| /* |
| * Child leaves loop, parent continues to fork. |
| */ |
| if ((pid = fork ()) < 0) |
| sys_error ("fork failed", __LINE__); |
| else if (pid == (pid_t)0) |
| { |
| errpid = pid; |
| break; |
| } |
| else |
| childpid [proc] = pid; |
| } |
| pid = getpid(); |
| |
| /* |
| * Test the semget () and semctl () commands |
| */ |
| test_commands (pid); |
| |
| /* |
| * Finished testing commands, only parent process needs to continue |
| */ |
| if (pid != parent_pid) exit (0); |
| |
| /* |
| * Wait for all of the child processes to complete & check their |
| * exit status. |
| * |
| * Upon completion of the child proccesses, exit program with success. |
| */ |
| for (proc = 1; proc < nprocs; proc++) { |
| waitpid (childpid [proc], &status, 0); |
| |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", |
| __LINE__); |
| } |
| if (nprocs > 0) |
| printf ("\n\tAll child processes verified commands successfully\n"); |
| printf ("\nsuccessful!\n"); |
| return (0); |
| } |
| |
| /*---------------------------------------------------------------------+ |
| | test_commands () | |
| | ==================================================================== | |
| | | |
| | Function: Verifies options for semop () system function call. | |
| | | |
| +---------------------------------------------------------------------*/ |
| static void test_commands (pid_t proc_pid) |
| { |
| int i; /* Misc loop index */ |
| int val; /* Value (semctl parameter) */ |
| int semid; /* Unique semaphore id */ |
| int status; /* Child's exit status */ |
| int expected_value; /* Expected semaphore value */ |
| pid_t pid; /* Misc process id */ |
| gid_t gid = getgid (); /* Misc group id */ |
| uid_t uid = getuid (); /* Misc user id */ |
| mode_t mode = 0666; /* Misc mode bits */ |
| // ushort array [MAX_SEMAPHORES]; /* Misc array of semaphore values */ |
| struct sembuf semoparray [MAX_SEMAPHORES]; |
| |
| /* |
| * Create the semaphores... |
| */ |
| if (proc_pid == parent_pid) |
| printf ("\n\tCreating %d semaphores ...\n", nsems); |
| if ((semid = semget (IPC_PRIVATE, nsems, IPC_CREAT|mode)) < 0) |
| sys_error ("semget (IPC_PRIVATE) failed", __LINE__); |
| |
| /* |
| * Set the semaphore uid, gid and mode |
| */ |
| if (proc_pid == parent_pid) |
| printf ("\n\tSetting semaphore uid, gid and mode ... semid = %d\n", semid); |
| arg.buf = (struct semid_ds *) calloc (1, sizeof (struct semid_ds)); |
| if (!arg.buf) |
| error("calloc failed", __LINE__); |
| arg.buf->sem_perm.uid = uid; |
| arg.buf->sem_perm.gid = gid; |
| arg.buf->sem_perm.mode = mode; |
| if (semctl (semid, 0, IPC_SET, arg) < 0) |
| sys_error ("semctl failed", __LINE__); |
| |
| /* |
| * Verify that semaphore uid, gid and mode were set correctly |
| */ |
| if (proc_pid == parent_pid) |
| printf ("\n\tVerifying semaphore info ...\n"); |
| if (semctl (semid, 0, IPC_STAT, arg) < 0) |
| sys_error ("semctl (IPC_STAT) failed", __LINE__); |
| if (arg.buf->sem_perm.uid != uid) |
| error ("semctl: uid was not set", __LINE__); |
| if (arg.buf->sem_perm.gid != gid) |
| error ("semctl: gid was not set", __LINE__); |
| if ((arg.buf->sem_perm.mode & 0777) != mode) |
| error ("semctl: mode was not set", __LINE__); |
| if (arg.buf->sem_nsems != nsems) |
| error ("semctl: nsems (number of semaphores) was not set", |
| __LINE__); |
| SAFE_FREE(arg.buf); |
| |
| /* |
| * Set the value of each semaphore in the set to 2. |
| */ |
| arg.array = malloc(sizeof(int) * nsems); |
| if (!arg.array) |
| error("malloc failed", __LINE__); |
| for (i = 0; i < nsems; i++) |
| arg.array [i] = 2; |
| if (semctl (semid, 0, SETALL, arg) < 0) |
| sys_error ("semctl (SETALL) failed", __LINE__); |
| SAFE_FREE(arg.array); |
| |
| /* ------------------------------------------------------------------ */ |
| /* possibilities for sem_flg are: */ |
| /* 0 */ |
| /* SEM_UN */ |
| /* IPC_NOWAIT */ |
| /* Return Immediately */ |
| /* ------------------------------------------------------------------ */ |
| if (proc_pid == parent_pid) { |
| printf ("\n\tTesting semop() with all Semaphore values, options and flags\n"); |
| printf ("\n\t Semval Semop Semflag Description\n"); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 1 --- semval = 2, sem_op = -1, sem_flg = 0 */ |
| /* --- semval > |sem_op| THEN (semval - |sem_op|) = 1 */ |
| /* THE FOLLOWING SHOULD SHOW semval = 1. */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(2, -1, "0", "Obtain resource"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = -1; |
| semoparray [i].sem_flg = 0; |
| } |
| if (semop (semid, semoparray, nsems) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 1; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, i, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 2 --- semval = 1, sem_op = -1, sem_flg = 0 */ |
| /* --- semval = |sem_op| THEN (semval - |sem_op|) = 0 */ |
| /* THE FOLLOWING SHOULD SHOW semval = 0 */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(1, -1, "0", "Obtain resource"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = -1; |
| semoparray [i].sem_flg = 0; |
| } |
| if (semop (semid, semoparray, nsems) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 0; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, i, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 3 --- semval = 0, sem_op = 0, sem_flg = 0 */ |
| /* --- semop = 0 AND semval = 0 returns immediately. */ |
| /* THE FOLLOWING SHOULD SHOW semval = 0 */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(0, 0, "0", "Semop function returns immediately"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = 0; |
| semoparray [i].sem_flg = 0; |
| } |
| if (semop (semid, semoparray, nsems) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 0; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, i, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 4 --- semval = 5, sem_op = 1, sem_flg = 0 */ |
| /* --- semop > 0 THEN (semval + sem_op) = 6 */ |
| /* THE FOLLOWING SHOULD SHOW semval = 6 */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(5, 1, "0", "Return resource"); |
| arg.array = malloc(sizeof(int) * nsems); |
| if (!arg.array) |
| error("malloc failed", __LINE__); |
| for (i = 0; i < nsems; i++) { |
| arg.array [i] = 5; |
| } |
| if (semctl (semid, 0, SETALL, arg) < 0) |
| sys_error ("semctl (SETALL) failed", __LINE__); |
| SAFE_FREE(arg.array); |
| |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = 1; |
| semoparray [i].sem_flg = 0; |
| } |
| if (semop (semid, semoparray, nsems) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 6; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, i, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 5 --- semval = 6, sem_op = -7, sem_flg = IPC_NOWAIT */ |
| /* --- semval < |sem_op| && IPC_NOWAIT, THEN return immed. */ |
| /* THE FOLLOWING SHOULD SHOW semval = 6. */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(6, -7, "IPC_NOWAIT", "Semop function returns immediately"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = -7; |
| semoparray [i].sem_flg = IPC_NOWAIT; |
| } |
| if (semop (semid, semoparray, nsems) >= 0) |
| error ("semop did not return EAGAIN", __LINE__); |
| else |
| if (errno != EAGAIN) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 6; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, i, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 6 --- semval = 6, sem_op = 0, sem_flg = IPC_NOWAIT */ |
| /* --- semop = 0 AND semval != 0 AND IPC_NOWAIT, */ |
| /* --- THEN return immediately. */ |
| /* THE FOLLOWING SHOULD SHOW semval = 6. */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(6, 0, "IPC_NOWAIT", "Semop function returns immediately"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = 0; |
| semoparray [i].sem_flg = IPC_NOWAIT; |
| } |
| if (semop (semid, semoparray, nsems) >= 0) |
| error ("semop did not return EAGAIN", __LINE__); |
| else |
| if (errno != EAGAIN) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 6; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, i, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 7 --- semval = 6, sem_op = 1, sem_flg = 0 */ |
| /* --- semop > 0 THEN (semval + sem_op) = 7 */ |
| /* THE FOLLOWING SHOULD SHOW semval = 7. */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(6, 1, "0", "Return resource"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = 1; |
| semoparray [i].sem_flg = 0; |
| } |
| if (semop (semid, semoparray, nsems) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 7; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, i, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 8 --- semval = 7, sem_op[0] = -8, sem_flg = 0 */ |
| /* --- semval < |semop| && ! IPC_NOWAIT, caller sleeps */ |
| /* call #1 --- semval = 7, sem_op[0] = 2, sem_flg = 0 */ |
| /* --- semop > 0 THEN (semval + sem_op) = 9 */ |
| /* --- "child" is awaken via call #1. */ |
| /* THE FOLLOWING SHOULD SHOW semval = 1 */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(7, -8, "0", "Sleep (until resource becomes available)"); |
| /* |
| * Child process |
| */ |
| if ((pid = fork()) == (pid_t)0) { |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = -8; |
| semoparray [0].sem_flg = 0; |
| if (semop (semid, semoparray, 1) < 0) |
| sys_error ("semop failed", __LINE__); |
| exit(0); |
| } else if (pid < 0) { |
| sys_error ("fork failed", __LINE__); |
| } |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = 2; |
| semoparray [0].sem_flg = 0; |
| |
| /* |
| * Wait for child process's semaphore request before proceeding... |
| */ |
| while (!semctl (semid, 0, GETNCNT, arg)) |
| sleep (1); |
| |
| if (semop (semid, semoparray, 1) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| waitpid (pid, &status, 0); /* Wait for child to complete */ |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", __LINE__); |
| |
| expected_value = 1; |
| arg.val = 0; |
| if ((val = semctl (semid, 0, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 9 --- semval = 7, sem_op[0] = -8, sem_flg = 0 */ |
| /* --- semval < |semop| && ! IPC_NOWAIT, caller sleeps */ |
| /* --- "child" is awaken via a signal. AFTER AWAKENING, */ |
| /* --- semval > |sem_op| THEN (semval - |sem_op|) = 1 */ |
| /* THE FOLLOWING SHOULD SHOW semval = 7 */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(7, -8, "0", "Sleep (until signaled)"); |
| /* |
| * Child process |
| */ |
| if ((pid = fork()) == (pid_t)0) { |
| semoparray [0].sem_num = 1; |
| semoparray [0].sem_op = -8; |
| semoparray [0].sem_flg = 0; |
| |
| if (semop (semid, semoparray, 1) >= 0) |
| error ("semop did not return EINTR", __LINE__); |
| else |
| if (errno != EINTR) { |
| printf ("semop returned: %d\n", errno); |
| sys_error ("semop failed", __LINE__); |
| } |
| exit (0); |
| } else if (pid < (pid_t)0) { |
| sys_error ("fork failed", __LINE__); |
| } |
| |
| /* |
| * Wait for child process's semaphore request before proceeding... |
| */ |
| while (!semctl (semid, 1, GETNCNT, arg)) |
| sleep (1); |
| |
| kill (pid, SIGUSR1); |
| |
| waitpid (pid, &status, 0); /* Wait for child to complete */ |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", __LINE__); |
| |
| expected_value = 7; |
| arg.val = 0; |
| if ((val = semctl (semid, 1, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 10 --- semval = 1, sem_op[3] = -3, sem_flg = 0 */ |
| /* --- semval < |semop| && ! IPC_NOWAIT, caller sleeps */ |
| /* call #1 --- semval = 1, sem_op[3] = 5, sem_flg = 0 */ |
| /* --- sem_op > 0 THEN (semval + sem_op) = 6 */ |
| /* call #2 --- semval = 6, sem_op[3] = 5, sem_flg = SEM_UN */ |
| /* --- sem_op > 0 && SEM_UN, THEN */ |
| /* --- THEN (semval + sem_op) = 11 */ |
| /* --- "child" is awaken via call #2. */ |
| /* --- semval < |semop (-3)| THEN semval = 8 */ |
| /* THE FOLLOWING SHOULD SHOW semval = 8 */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(1, 5, "SEM_UNDO", "Sleep (until resource becomes available)"); |
| /* |
| * Child process |
| */ |
| if ((pid = fork()) == (pid_t)0) { |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = -3; |
| semoparray [0].sem_flg = 0; |
| |
| if (semop (semid, semoparray, 1) < 0) |
| sys_error ("semop failed", __LINE__); |
| exit (0); |
| } else if (pid < (pid_t)0) { |
| sys_error ("fork failed", __LINE__); |
| } |
| |
| /* |
| * Wait for child process's semaphore request before proceeding... |
| */ |
| while (!semctl (semid, 0, GETNCNT, arg)) |
| sleep (1); |
| |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = 5; |
| semoparray [0].sem_flg = 0; |
| if (semop (semid, semoparray, 1) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = 5; |
| semoparray [0].sem_flg = SEM_UNDO; |
| if (semop (semid, semoparray, 1) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| waitpid (pid, &status, 0); /* Wait for child to complete */ |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", __LINE__); |
| |
| expected_value = 8; |
| arg.val = 0; |
| if ((val = semctl (semid, 0, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 11 --- semval = 7, sem_op[3] = -8, sem_flg = 0 */ |
| /* --- semval < |semop| && ! IPC_NOWAIT, caller sleeps */ |
| /* --- "child" is awaken via removal of semaphores */ |
| /* THE FOLLOWING SHOULD SHOW now be destroyed */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(7, -8, "0", "Sleep (until semaphores are removed)"); |
| /* |
| * Child process |
| */ |
| if ((pid = fork()) == (pid_t)0) { |
| semoparray [0].sem_num = 2; |
| semoparray [0].sem_op = -8; |
| semoparray [0].sem_flg = 0; |
| |
| if (semop (semid, semoparray, 1) >= 0) |
| error ("semop did not return ERMID", __LINE__); |
| else |
| if (errno != EIDRM) { |
| printf ("semop returned: %d\n", errno); |
| sys_error ("semop failed", __LINE__); |
| } |
| exit (0); |
| } else if (pid < (pid_t)0) { |
| sys_error ("fork failed", __LINE__); |
| } |
| |
| /* |
| * Wait for child process's semaphore request before deleting the |
| * semaphores... |
| */ |
| while (!semctl (semid, 2, GETNCNT, arg)) |
| sleep (1); |
| |
| arg.val = 0; |
| if (semctl (semid, 0, IPC_RMID, arg) < 0) |
| sys_error ("semctl (IPC_RMID) failed", __LINE__); |
| |
| waitpid (pid, &status, 0); /* Wait for child to complete */ |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", __LINE__); |
| |
| /* ------------------------------------------------------------------ */ |
| /* IPC_RMID DESTROYED THE SEMAPHORE STRUCTURES. */ |
| /* THEREFORE: REBUILD A SEMAPHORE STRUCTURE SET. */ |
| /* ------------------------------------------------------------------ */ |
| /* |
| * Create the semaphores... |
| */ |
| if ((semid = semget (IPC_PRIVATE, nsems, IPC_CREAT|mode)) < 0) |
| sys_error ("semget (IPC_PRIVATE) failed", __LINE__); |
| |
| /* |
| * Set the semaphore uid, gid and mode |
| */ |
| arg.buf = (struct semid_ds *) calloc (1, sizeof (struct semid_ds)); |
| if (!arg.buf) |
| error("calloc failed", __LINE__); |
| arg.buf->sem_perm.uid = uid; |
| arg.buf->sem_perm.gid = gid; |
| arg.buf->sem_perm.mode = mode; |
| if (semctl (semid, 0, IPC_SET, arg) < 0) |
| sys_error ("semctl failed", __LINE__); |
| |
| /* |
| * Verify that semaphore uid, gid and mode were set correctly |
| */ |
| if (semctl (semid, 0, IPC_STAT, arg) < 0) |
| sys_error ("semctl (IPC_STAT) failed", __LINE__); |
| if (arg.buf->sem_perm.uid != uid) |
| error ("semctl: uid was not set", __LINE__); |
| if (arg.buf->sem_perm.gid != gid) |
| error ("semctl: gid was not set", __LINE__); |
| if ((arg.buf->sem_perm.mode & 0777) != mode) |
| error ("semctl: mode was not set", __LINE__); |
| if (arg.buf->sem_nsems != nsems) |
| error ("semctl: nsems (number of semaphores) was not set", |
| __LINE__); |
| SAFE_FREE(arg.buf); |
| |
| arg.array = malloc(sizeof(int) * nsems); |
| if (!arg.array) |
| error("malloc failed", __LINE__); |
| for (i = 0; i < nsems; i++) |
| arg.array [i] = 9; |
| if (semctl (semid, 0, SETALL, arg) < 0) |
| sys_error ("semctl (SETALL) failed", __LINE__); |
| SAFE_FREE(arg.array); |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 12 --- semval = 9, sem_op = -1, sem_flg = SEM_UN */ |
| /* --- semval > |sem_op| THEN (semval - |sem_op|) = 8 */ |
| /* --- ALSO (semadj = semadj + sem_op) */ |
| /* THE FOLLOWING SHOULD SHOW semval = 8 */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(9, -1, "SEM_UNDO", "Obtain resource"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = -1; |
| semoparray [i].sem_flg = SEM_UNDO; |
| } |
| if (semop (semid, semoparray, nsems) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 8; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, 0, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 13 --- semval = 8, sem_op = -8, sem_flg = SEM_UN */ |
| /* --- semval = |sem_op| THEN (semval - |sem_op|) = 0 */ |
| /* --- ALSO (semadj = semadj + sem_op) */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(8, -8, "SEM_UNDO", "Obtain resource"); |
| for (i = 0; i < nsems; i++) { |
| semoparray [i].sem_num = i; |
| semoparray [i].sem_op = -8; |
| semoparray [i].sem_flg = SEM_UNDO; |
| } |
| if (semop (semid, semoparray, nsems) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| expected_value = 0; |
| for (i = 0; i < nsems; i++) { |
| arg.val = 0; |
| if ((val = semctl (semid, 0, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| } |
| |
| arg.array = malloc(sizeof(int) * nsems); |
| if (!arg.array) |
| error("malloc failed", __LINE__); |
| for (i = 0; i < nsems; i++) |
| arg.array [i] = 9; |
| if (semctl (semid, 0, SETALL, arg) < 0) |
| sys_error ("semctl (SETALL) failed", __LINE__); |
| SAFE_FREE(arg.array); |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 14 --- semval = 9, sem_op[4] = 0, sem_flg = 0 */ |
| /* --- semval != 0 && ! IPC_NOWAIT caller sleeps */ |
| /* --- ALSO ++semzcnt */ |
| /* --- "child" is awaken via a signal. */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(9, 0, "0", "Sleep (until signaled)"); |
| /* |
| * Child process |
| */ |
| if ((pid = fork()) == 0) { |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = 0; |
| semoparray [0].sem_flg = 0; |
| |
| if (semop (semid, semoparray, 1) >= 0) |
| error ("semop did not return EINTR", __LINE__); |
| else |
| if (errno != EINTR) { |
| printf ("semop returned: %d\n", errno); |
| sys_error ("semop failed", __LINE__); |
| } |
| exit (0); |
| } else if (pid < 0) { |
| sys_error ("fork failed", __LINE__); |
| } |
| |
| /* |
| * Wait for child process's semaphore request before proceeding... |
| */ |
| while (!semctl (semid, 0, GETZCNT, arg)) |
| sleep (1); |
| |
| kill (pid, SIGUSR1); |
| |
| waitpid (pid, &status, 0); /* Wait for child to complete */ |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", __LINE__); |
| |
| expected_value = 9; |
| arg.val = 0; |
| if ((val = semctl (semid, 0, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 15 --- semval = 9, sem_op[0] = 0, sem_flg = 0 */ |
| /* --- semval != 0 && ! IPC_NOWAIT caller sleeps */ |
| /* --- ALSO ++semzcnt */ |
| /* call #1 --- semval = 9, sem_op[0] = -9, sem_flg = 0 */ |
| /* --- THEN (semval - |sem_op|) = 0 and --semzcnt */ |
| /* --- "child" is awaken via call #1. */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(9, 0, "0", "Sleep (until resource becomes available)"); |
| /* |
| * Child process |
| */ |
| if ((pid = fork()) == (pid_t)0) { |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = 0; |
| semoparray [0].sem_flg = 0; |
| if (semop (semid, semoparray, 1) < 0) |
| sys_error ("semop failed", __LINE__); |
| exit(0); |
| } else if (pid < (pid_t)0) { |
| sys_error ("fork failed", __LINE__); |
| } |
| |
| /* |
| * Wait for child process's semaphore request before proceeding... |
| */ |
| while (!semctl (semid, 0, GETZCNT, arg)) |
| sleep (1); |
| |
| semoparray [0].sem_num = 0; |
| semoparray [0].sem_op = -9; |
| semoparray [0].sem_flg = 0; |
| |
| if (semop (semid, semoparray, 1) < 0) |
| sys_error ("semop failed", __LINE__); |
| |
| waitpid (pid, &status, 0); /* Wait for child to complete */ |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", __LINE__); |
| |
| expected_value = 0; |
| arg.val = 0; |
| if ((val = semctl (semid, 0, GETVAL, arg)) < 0) |
| sys_error ("semctl (GETVAL) failed", __LINE__); |
| if (val != expected_value) |
| error ("incorrect semaphore value", __LINE__); |
| |
| /* ------------------------------------------------------------------ */ |
| /* TEST # 16 --- semval = 4, sem_op[4] = 0, sem_flg = 0 */ |
| /* --- semval != 0 && ! IPC_NOWAIT caller sleeps */ |
| /* --- ALSO ++semzcnt */ |
| /* --- "child" is awaken via removal of semaphores */ |
| /* ------------------------------------------------------------------ */ |
| SEMOP_TABLE(4, 0, "0", "Sleep (until semaphores are removed)"); |
| /* |
| * Child process |
| */ |
| arg.val = 4; |
| if (semctl (semid, 4, SETVAL, arg) < 0) |
| sys_error ("semctl (SETALL) failed", __LINE__); |
| |
| if ((pid = fork()) == (pid_t)0) { |
| semoparray [0].sem_num = 4; |
| semoparray [0].sem_op = -8; |
| semoparray [0].sem_flg = 0; |
| |
| if (semop (semid, semoparray, 1) >= 0) |
| error ("semop did not return ERMID", __LINE__); |
| else |
| if (errno != EIDRM) { |
| printf ("semop returned: %d\n", errno); |
| sys_error ("semop failed", __LINE__); |
| } |
| exit (0); |
| } else if (pid < 0) { |
| sys_error ("fork failed", __LINE__); |
| } |
| |
| /* |
| * Wait for child process's semaphore request before proceeding... |
| */ |
| while (!semctl (semid, 4, GETNCNT, arg)) |
| sleep (1); |
| |
| arg.val = 0; |
| if (semctl (semid, 0, IPC_RMID, arg) < 0) |
| sys_error ("semctl (IPC_RMDI) failed", __LINE__); |
| |
| waitpid (pid, &status, 0); /* Wait for child to complete */ |
| if (WEXITSTATUS (status)) |
| sys_error ("child process terminated abnormally", __LINE__); |
| } |
| |
| /*---------------------------------------------------------------------+ |
| | parse_args () | |
| | ==================================================================== | |
| | | |
| | Function: Parse the command line arguments & initialize global | |
| | variables. | |
| | | |
| | Updates: (command line options) | |
| | | |
| | [-p] nproc: number of child processes | |
| | | |
| +---------------------------------------------------------------------*/ |
| void parse_args (int argc, char **argv) |
| { |
| int opt; |
| int errflag = 0; |
| char *program_name = *argv; |
| extern char *optarg; /* Command line option */ |
| |
| while ((opt = getopt(argc, argv, "s:p:")) != EOF) { |
| switch (opt) { |
| case 's': |
| nsems = atoi (optarg); |
| break; |
| case 'p': |
| nprocs = atoi (optarg); |
| break; |
| default: |
| errflag++; |
| break; |
| } |
| } |
| if (nsems >= MAX_SEMAPHORES) { |
| errflag++; |
| fprintf (stderr, "ERROR: nsems must be less than %d\n", |
| MAX_SEMAPHORES); |
| } |
| if (nprocs >= MAX_CHILDREN) { |
| errflag++; |
| fprintf (stderr, "ERROR: nproc must be less than %d\n", |
| MAX_CHILDREN); |
| } |
| |
| if (errflag) { |
| fprintf (stderr, USAGE, program_name); |
| exit (2); |
| } |
| } |
| |
| /*---------------------------------------------------------------------+ |
| | setup_signal_handler () | |
| | ==================================================================== | |
| | | |
| | Function: Sets up signal handler for SIGUSR1 signal | |
| | | |
| +---------------------------------------------------------------------*/ |
| static void setup_signal_handler () |
| { |
| struct sigaction sigact; |
| |
| sigact.sa_flags = 0; |
| sigfillset (&sigact.sa_mask); |
| |
| /* |
| * Establish the signal handler for SIGUSR1 |
| */ |
| sigact.sa_handler = (void (*)(int)) catch; |
| if (sigaction (SIGUSR1, &sigact, NULL) < 0) |
| sys_error ("sigaction failed", __LINE__); |
| } |
| |
| /*---------------------------------------------------------------------+ |
| | catch () | |
| | ==================================================================== | |
| | | |
| | Function: Signal catching function for SIGUSR1 | |
| | | |
| +---------------------------------------------------------------------*/ |
| static void catch (int sig) |
| { |
| char err_msg [256]; |
| pid_t pid = getpid (); |
| |
| if (sig == SIGUSR1) { |
| if (pid == parent_pid) |
| printf ("\t\t\t\t <<< caught signal (SIGUSR1, %d) >>>\n", |
| sig); |
| } else { |
| sprintf (err_msg, "caught unexpected signal (%d)", sig); |
| error (err_msg, __LINE__); |
| } |
| } |
| |
| /*---------------------------------------------------------------------+ |
| | sys_error () | |
| | ==================================================================== | |
| | | |
| | Function: Creates system error message and calls error () | |
| | | |
| +---------------------------------------------------------------------*/ |
| static void sys_error (const char *msg, int line) |
| { |
| char syserr_msg [256]; |
| |
| sprintf (syserr_msg, "%s: %s\n", msg, strerror (errno)); |
| error (syserr_msg, line); |
| } |
| |
| /*---------------------------------------------------------------------+ |
| | error () | |
| | ==================================================================== | |
| | | |
| | Function: Prints out message and exits... | |
| | | |
| +---------------------------------------------------------------------*/ |
| static void error (const char *msg, int line) |
| { |
| fprintf (stderr, "ERROR pid %d [line: %d] %s\n", errpid, line, msg); |
| exit (-1); |
| } |