• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *   Copyright (C) Bull S.A. 1996
3  *   Copyright (c) International Business Machines  Corp., 2001
4  *
5  *   This program is free software;  you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13  *   the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program;  if not, write to the Free Software
17  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 /*---------------------------------------------------------------------+
20 |                           semaphore_test_03                          |
21 | ==================================================================== |
22 |                                                                      |
23 | Description:  Verify semop () command options                        |
24 |                                                                      |
25 | Algorithm:    o  Spawn N child processes                             |
26 |                                                                      |
27 |               o  Obtain N semaphores with semget (IPC_PRIVATE)       |
28 |                                                                      |
29 |               o  Call semop () with variations of the following      |
30 |                  parameters:                                         |
31 |                                                                      |
32 |                     sem_op:  negative, 0, positive                   |
33 |                     sem_flg: IPC_NOWAIT, SEM_UNDO                    |
34 |                                                                      |
35 | System calls: The following system calls are made                    |
36 |                                                                      |
37 |               semget () - Gets a set of semaphores                   |
38 |               semctl () - Controls semaphore operations              |
39 |               semop () - Performs semaphore operations               |
40 |                                                                      |
41 | Usage:        semaphore_test_03 [-p nprocs] [-s nsems]               |
42 |                                                                      |
43 |               where:  nprocs - number of child processes to spawn    |
44 |                       nsems  - number of semaphores (per process)    |
45 |                                                                      |
46 | To compile:   cc -o semaphore_test_03 semaphore_test_03.c            |
47 |                                                                      |
48 | Last update:   Ver. 1.6, 7/21/94 13:37:28                            |
49 |                                                                      |
50 | Change Activity                                                      |
51 |                                                                      |
52 |   Version  Date    Name  Reason                                      |
53 |    0.1     050689  CTU   Initial version                             |
54 |    0.2     111993  DJK   Modify for AIX version 4.1                  |
55 |    1.2     021494  DJK   Moved to "prod" directory                   |
56 |    1.3     Jan-28-02 	Manoj Iyer, IBM Austin, TX.manjo@austin.ibm.com|
57 |			Modified - Ported to work on PPC64.            |
58 |                                                                      |
59 +---------------------------------------------------------------------*/
60 
61 #include <errno.h>
62 #include <stdio.h>
63 #include <sys/ipc.h>
64 #include <sys/param.h>
65 #include <sys/sem.h>
66 #include <sys/shm.h>
67 #include <sys/stat.h>
68 #include <sys/signal.h>
69 #include <sys/types.h>
70 #include <sys/wait.h>
71 #include <stdlib.h>
72 #include <unistd.h>
73 #include <string.h>
74 #include "lapi/semun.h"
75 
76 /*
77  * Defines
78  *
79  * MAX_SEMAPHORES: maximum number of semaphores per id (limited by
80  * maximum number of semaphore operations per call by semop () function).
81  *
82  * MAX_CHILDREN:   maximum number of child processes to spawn
83  *
84  * DEFAULT_NUM_SEMAPHORES: default number of semaphores to create unless
85  * specified with (-s nsems) command line option
86  *
87  * DEFAULT_NUM_CHILDREN: default number of child processes to spawn unless
88  * specified with (-p nprocs) command line option
89  *
90  * USAGE: usage statement macro
91  *
92  * SEMOP_TABLE: macro for printing attempted semop command combinations
93  */
94 #define MAX_SEMAPHORES	        32
95 #define MAX_CHILDREN		200
96 #define DEFAULT_NUM_SEMAPHORES	16
97 #define DEFAULT_NUM_CHILDREN	0
98 
99 #define USAGE	"\nUsage: %s [-s nsems] [-p nproc]\n\n" \
100 		"\t-s nsems  number of semaphores (per process)\n\n"	\
101 		"\t-p nproc  number of child processes to spawn\n\n"
102 #define SEMOP_TABLE(p1,p2,p3,p4)	\
103 	if (proc_pid == parent_pid)	\
104 		printf ("\t   %3d    %3d    %-10s %-20s\n", p1, p2, p3, p4)
105 
106 #define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
107 
108 /*
109  * Function prototypes
110  *
111  * setup_signal_handler (): Sets up signal handler for SIGUSR1
112  * test_commands (): Tests semget () and semctl () commands
113  * handler (): Signal handler
114  * sys_error (): System error message function
115  * error (): Error message function
116  * parse_args (): Parse command line arguments
117  * catch: Signal catching function for SIGUSR1 signal
118  */
119 static void setup_signal_handler();
120 static void test_commands();
121 static void sys_error(const char *, int);
122 static void error(const char *, int);
123 static void parse_args(int, char **);
124 static void catch(int);
125 
126 /*
127  * Structures and Global variables:
128  *
129  * nsems: number of semaphores to create (per process)
130  * nprocs: number of child processes to spawn
131  * childpid: array containing process id's of the child processes
132  * parent_pid: process id of parent process
133  */
134 int nsems = DEFAULT_NUM_SEMAPHORES;
135 int nprocs = DEFAULT_NUM_CHILDREN;
136 pid_t childpid[MAX_CHILDREN];
137 pid_t parent_pid;
138 pid_t errpid;
139 
140 union semun arg;
141 
142 /*---------------------------------------------------------------------+
143 |                               main                                   |
144 | ==================================================================== |
145 |                                                                      |
146 | Function:  Main program  (see prolog for more details)               |
147 |                                                                      |
148 | Returns:   (0)  Successful completion                                |
149 |            (-1) Error occurred                                       |
150 |                                                                      |
151 +---------------------------------------------------------------------*/
main(int argc,char ** argv)152 int main(int argc, char **argv)
153 {
154 	pid_t pid;		/* Child's process id */
155 	int proc;		/* Fork loop index */
156 	int status;		/* Child's exit status */
157 
158 	/*
159 	 * Parse command line arguments, print out program header, setup
160 	 * signal handler (for SIGUSR1) and save parent process id.
161 	 */
162 	parse_args(argc, argv);
163 	printf("%s: IPC Semaphore TestSuite program\n", *argv);
164 	fflush(stdout);
165 	setup_signal_handler();
166 	errpid = parent_pid = getpid();
167 
168 	if (nsems < 8)
169 		nsems = 8;
170 
171 	/*
172 	 * Fork off the additional processes.
173 	 */
174 	if (nprocs > 0) {
175 		printf("\n\tParent: spawning %d child processes\n", nprocs);
176 		fflush(stdout);
177 	}
178 	for (proc = 1; proc < nprocs; proc++) {
179 		/*
180 		 * Child leaves loop, parent continues to fork.
181 		 */
182 		if ((pid = fork()) < 0)
183 			sys_error("fork failed", __LINE__);
184 		else if (pid == (pid_t) 0) {
185 			errpid = pid;
186 			break;
187 		} else
188 			childpid[proc] = pid;
189 	}
190 	pid = getpid();
191 
192 	/*
193 	 * Test the semget () and semctl () commands
194 	 */
195 	test_commands(pid);
196 
197 	/*
198 	 * Finished testing commands, only parent process needs to continue
199 	 */
200 	if (pid != parent_pid)
201 		exit(0);
202 
203 	/*
204 	 * Wait for all of the child processes to complete & check their
205 	 * exit status.
206 	 *
207 	 * Upon completion of the child proccesses, exit program with success.
208 	 */
209 	for (proc = 1; proc < nprocs; proc++) {
210 		waitpid(childpid[proc], &status, 0);
211 
212 		if (WEXITSTATUS(status))
213 			sys_error("child process terminated abnormally",
214 				  __LINE__);
215 	}
216 	if (nprocs > 0)
217 		printf
218 		    ("\n\tAll child processes verified commands successfully\n");
219 	printf("\nsuccessful!\n");
220 	return (0);
221 }
222 
223 /*---------------------------------------------------------------------+
224 |                             test_commands ()                         |
225 | ==================================================================== |
226 |                                                                      |
227 | Function:  Verifies options for semop () system function call.       |
228 |                                                                      |
229 +---------------------------------------------------------------------*/
test_commands(pid_t proc_pid)230 static void test_commands(pid_t proc_pid)
231 {
232 	int i;			/* Misc loop index */
233 	int val;		/* Value (semctl parameter) */
234 	int semid;		/* Unique semaphore id */
235 	int status;		/* Child's exit status */
236 	int expected_value;	/* Expected semaphore value */
237 	pid_t pid;		/* Misc process id */
238 	gid_t gid = getgid();	/* Misc group id */
239 	uid_t uid = getuid();	/* Misc user id */
240 	mode_t mode = 0666;	/* Misc mode bits */
241 	// ushort       array [MAX_SEMAPHORES]; /* Misc array of semaphore values */
242 	struct sembuf semoparray[MAX_SEMAPHORES];
243 
244 	/*
245 	 * Create the semaphores...
246 	 */
247 	if (proc_pid == parent_pid)
248 		printf("\n\tCreating %d semaphores ...\n", nsems);
249 	if ((semid = semget(IPC_PRIVATE, nsems, IPC_CREAT | mode)) < 0)
250 		sys_error("semget (IPC_PRIVATE) failed", __LINE__);
251 
252 	/*
253 	 * Set the semaphore uid, gid and mode
254 	 */
255 	if (proc_pid == parent_pid)
256 		printf
257 		    ("\n\tSetting semaphore uid, gid and mode ... semid = %d\n",
258 		     semid);
259 	arg.buf = (struct semid_ds *)calloc(1, sizeof(struct semid_ds));
260 	if (!arg.buf)
261 		error("calloc failed", __LINE__);
262 	arg.buf->sem_perm.uid = uid;
263 	arg.buf->sem_perm.gid = gid;
264 	arg.buf->sem_perm.mode = mode;
265 	if (semctl(semid, 0, IPC_SET, arg) < 0)
266 		sys_error("semctl failed", __LINE__);
267 
268 	/*
269 	 * Verify that semaphore uid, gid and mode were set correctly
270 	 */
271 	if (proc_pid == parent_pid)
272 		printf("\n\tVerifying semaphore info ...\n");
273 	if (semctl(semid, 0, IPC_STAT, arg) < 0)
274 		sys_error("semctl (IPC_STAT) failed", __LINE__);
275 	if (arg.buf->sem_perm.uid != uid)
276 		error("semctl: uid was not set", __LINE__);
277 	if (arg.buf->sem_perm.gid != gid)
278 		error("semctl: gid was not set", __LINE__);
279 	if ((arg.buf->sem_perm.mode & 0777) != mode)
280 		error("semctl: mode was not set", __LINE__);
281 	if (arg.buf->sem_nsems != nsems)
282 		error("semctl: nsems (number of semaphores) was not set",
283 		      __LINE__);
284 	SAFE_FREE(arg.buf);
285 
286 	/*
287 	 * Set the value of each semaphore in the set to 2.
288 	 */
289 	arg.array = malloc(sizeof(int) * nsems);
290 	if (!arg.array)
291 		error("malloc failed", __LINE__);
292 	for (i = 0; i < nsems; i++)
293 		arg.array[i] = 2;
294 	if (semctl(semid, 0, SETALL, arg) < 0)
295 		sys_error("semctl (SETALL) failed", __LINE__);
296 	SAFE_FREE(arg.array);
297 
298 	/* ------------------------------------------------------------------ */
299 	/*  possibilities for sem_flg are:                                    */
300 	/*               0                                                    */
301 	/*               SEM_UN                                               */
302 	/*               IPC_NOWAIT                                           */
303 	/*               Return Immediately                                   */
304 	/* ------------------------------------------------------------------ */
305 	if (proc_pid == parent_pid) {
306 		printf
307 		    ("\n\tTesting semop() with all Semaphore values, options and flags\n");
308 		printf("\n\t   Semval Semop  Semflag    Description\n");
309 	}
310 
311 	/* ------------------------------------------------------------------ */
312 	/* TEST # 1  --- semval = 2, sem_op = -1, sem_flg = 0                 */
313 	/*           --- semval > |sem_op| THEN (semval - |sem_op|) = 1       */
314 	/*           THE FOLLOWING SHOULD SHOW semval = 1.                    */
315 	/* ------------------------------------------------------------------ */
316 	SEMOP_TABLE(2, -1, "0", "Obtain resource");
317 	for (i = 0; i < nsems; i++) {
318 		semoparray[i].sem_num = i;
319 		semoparray[i].sem_op = -1;
320 		semoparray[i].sem_flg = 0;
321 	}
322 	if (semop(semid, semoparray, nsems) < 0)
323 		sys_error("semop failed", __LINE__);
324 
325 	expected_value = 1;
326 	for (i = 0; i < nsems; i++) {
327 		arg.val = 0;
328 		if ((val = semctl(semid, i, GETVAL, arg)) < 0)
329 			sys_error("semctl (GETVAL) failed", __LINE__);
330 		if (val != expected_value)
331 			error("incorrect semaphore value", __LINE__);
332 	}
333 
334 	/* ------------------------------------------------------------------ */
335 	/* TEST # 2  --- semval = 1, sem_op = -1, sem_flg = 0                 */
336 	/*           --- semval = |sem_op| THEN (semval - |sem_op|) = 0       */
337 	/*           THE FOLLOWING SHOULD SHOW semval = 0                     */
338 	/* ------------------------------------------------------------------ */
339 	SEMOP_TABLE(1, -1, "0", "Obtain resource");
340 	for (i = 0; i < nsems; i++) {
341 		semoparray[i].sem_num = i;
342 		semoparray[i].sem_op = -1;
343 		semoparray[i].sem_flg = 0;
344 	}
345 	if (semop(semid, semoparray, nsems) < 0)
346 		sys_error("semop failed", __LINE__);
347 
348 	expected_value = 0;
349 	for (i = 0; i < nsems; i++) {
350 		arg.val = 0;
351 		if ((val = semctl(semid, i, GETVAL, arg)) < 0)
352 			sys_error("semctl (GETVAL) failed", __LINE__);
353 		if (val != expected_value)
354 			error("incorrect semaphore value", __LINE__);
355 	}
356 
357 	/* ------------------------------------------------------------------ */
358 	/* TEST # 3 --- semval = 0, sem_op = 0, sem_flg = 0                   */
359 	/*          --- semop =  0 AND semval = 0 returns immediately.        */
360 	/*           THE FOLLOWING SHOULD SHOW semval = 0                     */
361 	/* ------------------------------------------------------------------ */
362 	SEMOP_TABLE(0, 0, "0", "Semop function returns immediately");
363 	for (i = 0; i < nsems; i++) {
364 		semoparray[i].sem_num = i;
365 		semoparray[i].sem_op = 0;
366 		semoparray[i].sem_flg = 0;
367 	}
368 	if (semop(semid, semoparray, nsems) < 0)
369 		sys_error("semop failed", __LINE__);
370 
371 	expected_value = 0;
372 	for (i = 0; i < nsems; i++) {
373 		arg.val = 0;
374 		if ((val = semctl(semid, i, GETVAL, arg)) < 0)
375 			sys_error("semctl (GETVAL) failed", __LINE__);
376 		if (val != expected_value)
377 			error("incorrect semaphore value", __LINE__);
378 	}
379 
380 	/* ------------------------------------------------------------------ */
381 	/* TEST # 4 --- semval = 5, sem_op = 1, sem_flg = 0                   */
382 	/*          --- semop > 0 THEN (semval + sem_op) = 6                  */
383 	/*           THE FOLLOWING SHOULD SHOW semval = 6                     */
384 	/* ------------------------------------------------------------------ */
385 	SEMOP_TABLE(5, 1, "0", "Return resource");
386 	arg.array = malloc(sizeof(int) * nsems);
387 	if (!arg.array)
388 		error("malloc failed", __LINE__);
389 	for (i = 0; i < nsems; i++) {
390 		arg.array[i] = 5;
391 	}
392 	if (semctl(semid, 0, SETALL, arg) < 0)
393 		sys_error("semctl (SETALL) failed", __LINE__);
394 	SAFE_FREE(arg.array);
395 
396 	for (i = 0; i < nsems; i++) {
397 		semoparray[i].sem_num = i;
398 		semoparray[i].sem_op = 1;
399 		semoparray[i].sem_flg = 0;
400 	}
401 	if (semop(semid, semoparray, nsems) < 0)
402 		sys_error("semop failed", __LINE__);
403 
404 	expected_value = 6;
405 	for (i = 0; i < nsems; i++) {
406 		arg.val = 0;
407 		if ((val = semctl(semid, i, GETVAL, arg)) < 0)
408 			sys_error("semctl (GETVAL) failed", __LINE__);
409 		if (val != expected_value)
410 			error("incorrect semaphore value", __LINE__);
411 	}
412 
413 	/* ------------------------------------------------------------------ */
414 	/* TEST # 5 --- semval = 6, sem_op = -7, sem_flg = IPC_NOWAIT         */
415 	/*          --- semval < |sem_op| && IPC_NOWAIT, THEN return immed.   */
416 	/*         THE FOLLOWING SHOULD SHOW semval = 6.                      */
417 	/* ------------------------------------------------------------------ */
418 	SEMOP_TABLE(6, -7, "IPC_NOWAIT", "Semop function returns immediately");
419 	for (i = 0; i < nsems; i++) {
420 		semoparray[i].sem_num = i;
421 		semoparray[i].sem_op = -7;
422 		semoparray[i].sem_flg = IPC_NOWAIT;
423 	}
424 	if (semop(semid, semoparray, nsems) >= 0)
425 		error("semop did not return EAGAIN", __LINE__);
426 	else if (errno != EAGAIN)
427 		sys_error("semop failed", __LINE__);
428 
429 	expected_value = 6;
430 	for (i = 0; i < nsems; i++) {
431 		arg.val = 0;
432 		if ((val = semctl(semid, i, GETVAL, arg)) < 0)
433 			sys_error("semctl (GETVAL) failed", __LINE__);
434 		if (val != expected_value)
435 			error("incorrect semaphore value", __LINE__);
436 	}
437 
438 	/* ------------------------------------------------------------------ */
439 	/* TEST # 6 --- semval = 6, sem_op = 0, sem_flg = IPC_NOWAIT          */
440 	/*          --- semop = 0 AND semval != 0 AND IPC_NOWAIT,          */
441 	/*          ---              THEN  return immediately.                */
442 	/*         THE FOLLOWING SHOULD SHOW semval = 6.                      */
443 	/* ------------------------------------------------------------------ */
444 	SEMOP_TABLE(6, 0, "IPC_NOWAIT", "Semop function returns immediately");
445 	for (i = 0; i < nsems; i++) {
446 		semoparray[i].sem_num = i;
447 		semoparray[i].sem_op = 0;
448 		semoparray[i].sem_flg = IPC_NOWAIT;
449 	}
450 	if (semop(semid, semoparray, nsems) >= 0)
451 		error("semop did not return EAGAIN", __LINE__);
452 	else if (errno != EAGAIN)
453 		sys_error("semop failed", __LINE__);
454 
455 	expected_value = 6;
456 	for (i = 0; i < nsems; i++) {
457 		arg.val = 0;
458 		if ((val = semctl(semid, i, GETVAL, arg)) < 0)
459 			sys_error("semctl (GETVAL) failed", __LINE__);
460 		if (val != expected_value)
461 			error("incorrect semaphore value", __LINE__);
462 	}
463 
464 	/* ------------------------------------------------------------------ */
465 	/* TEST # 7  --- semval = 6, sem_op = 1, sem_flg = 0                  */
466 	/*          --- semop > 0 THEN (semval + sem_op) = 7               */
467 	/*         THE FOLLOWING SHOULD SHOW semval = 7.                      */
468 	/* ------------------------------------------------------------------ */
469 	SEMOP_TABLE(6, 1, "0", "Return resource");
470 	for (i = 0; i < nsems; i++) {
471 		semoparray[i].sem_num = i;
472 		semoparray[i].sem_op = 1;
473 		semoparray[i].sem_flg = 0;
474 	}
475 	if (semop(semid, semoparray, nsems) < 0)
476 		sys_error("semop failed", __LINE__);
477 
478 	expected_value = 7;
479 	for (i = 0; i < nsems; i++) {
480 		arg.val = 0;
481 		if ((val = semctl(semid, i, GETVAL, arg)) < 0)
482 			sys_error("semctl (GETVAL) failed", __LINE__);
483 		if (val != expected_value)
484 			error("incorrect semaphore value", __LINE__);
485 	}
486 
487 	/* ------------------------------------------------------------------ */
488 	/* TEST # 8 --- semval = 7, sem_op[0] = -8, sem_flg = 0               */
489 	/*          --- semval < |semop| && ! IPC_NOWAIT, caller sleeps       */
490 	/* call #1  --- semval = 7, sem_op[0] =  2, sem_flg = 0               */
491 	/*          --- semop > 0 THEN (semval + sem_op) = 9                  */
492 	/*           --- "child" is awaken via call #1.                       */
493 	/*         THE FOLLOWING SHOULD SHOW semval = 1                       */
494 	/* ------------------------------------------------------------------ */
495 	SEMOP_TABLE(7, -8, "0", "Sleep (until resource becomes available)");
496 	/*
497 	 * Child process
498 	 */
499 	if ((pid = fork()) == (pid_t) 0) {
500 		semoparray[0].sem_num = 0;
501 		semoparray[0].sem_op = -8;
502 		semoparray[0].sem_flg = 0;
503 		if (semop(semid, semoparray, 1) < 0)
504 			sys_error("semop failed", __LINE__);
505 		exit(0);
506 	} else if (pid < 0) {
507 		sys_error("fork failed", __LINE__);
508 	}
509 	semoparray[0].sem_num = 0;
510 	semoparray[0].sem_op = 2;
511 	semoparray[0].sem_flg = 0;
512 
513 	/*
514 	 * Wait for child process's semaphore request before proceeding...
515 	 */
516 	while (!semctl(semid, 0, GETNCNT, arg))
517 		sleep(1);
518 
519 	if (semop(semid, semoparray, 1) < 0)
520 		sys_error("semop failed", __LINE__);
521 
522 	waitpid(pid, &status, 0);	/* Wait for child to complete */
523 	if (WEXITSTATUS(status))
524 		sys_error("child process terminated abnormally", __LINE__);
525 
526 	expected_value = 1;
527 	arg.val = 0;
528 	if ((val = semctl(semid, 0, GETVAL, arg)) < 0)
529 		sys_error("semctl (GETVAL) failed", __LINE__);
530 	if (val != expected_value)
531 		error("incorrect semaphore value", __LINE__);
532 
533 	/* ------------------------------------------------------------------ */
534 	/* TEST # 9  --- semval = 7, sem_op[0] = -8, sem_flg = 0              */
535 	/*           --- semval < |semop| && ! IPC_NOWAIT, caller sleeps      */
536 	/*           --- "child" is awaken via a signal.  AFTER AWAKENING,    */
537 	/*           --- semval > |sem_op| THEN (semval - |sem_op|) = 1       */
538 	/*         THE FOLLOWING SHOULD SHOW semval = 7                       */
539 	/* ------------------------------------------------------------------ */
540 	SEMOP_TABLE(7, -8, "0", "Sleep (until signaled)");
541 	/*
542 	 * Child process
543 	 */
544 	if ((pid = fork()) == (pid_t) 0) {
545 		semoparray[0].sem_num = 1;
546 		semoparray[0].sem_op = -8;
547 		semoparray[0].sem_flg = 0;
548 
549 		if (semop(semid, semoparray, 1) >= 0)
550 			error("semop did not return EINTR", __LINE__);
551 		else if (errno != EINTR) {
552 			printf("semop returned: %d\n", errno);
553 			sys_error("semop failed", __LINE__);
554 		}
555 		exit(0);
556 	} else if (pid < (pid_t) 0) {
557 		sys_error("fork failed", __LINE__);
558 	}
559 
560 	/*
561 	 * Wait for child process's semaphore request before proceeding...
562 	 */
563 	while (!semctl(semid, 1, GETNCNT, arg))
564 		sleep(1);
565 
566 	kill(pid, SIGUSR1);
567 
568 	waitpid(pid, &status, 0);	/* Wait for child to complete */
569 	if (WEXITSTATUS(status))
570 		sys_error("child process terminated abnormally", __LINE__);
571 
572 	expected_value = 7;
573 	arg.val = 0;
574 	if ((val = semctl(semid, 1, GETVAL, arg)) < 0)
575 		sys_error("semctl (GETVAL) failed", __LINE__);
576 	if (val != expected_value)
577 		error("incorrect semaphore value", __LINE__);
578 
579 	/* ------------------------------------------------------------------ */
580 	/* TEST # 10 --- semval = 1, sem_op[3] = -3, sem_flg = 0              */
581 	/*           --- semval < |semop| && ! IPC_NOWAIT, caller sleeps      */
582 	/* call #1   --- semval = 1, sem_op[3] =  5, sem_flg = 0              */
583 	/*           --- sem_op > 0 THEN (semval + sem_op) = 6                */
584 	/* call #2   --- semval = 6, sem_op[3] =  5, sem_flg = SEM_UN         */
585 	/*           --- sem_op > 0 && SEM_UN, THEN                           */
586 	/*           ---               THEN (semval + sem_op) = 11            */
587 	/*           --- "child" is awaken via call #2.                       */
588 	/*           --- semval < |semop (-3)| THEN semval = 8                */
589 	/*         THE FOLLOWING SHOULD SHOW semval = 8                       */
590 	/* ------------------------------------------------------------------ */
591 	SEMOP_TABLE(1, 5, "SEM_UNDO",
592 		    "Sleep (until resource becomes available)");
593 	/*
594 	 * Child process
595 	 */
596 	if ((pid = fork()) == (pid_t) 0) {
597 		semoparray[0].sem_num = 0;
598 		semoparray[0].sem_op = -3;
599 		semoparray[0].sem_flg = 0;
600 
601 		if (semop(semid, semoparray, 1) < 0)
602 			sys_error("semop failed", __LINE__);
603 		exit(0);
604 	} else if (pid < (pid_t) 0) {
605 		sys_error("fork failed", __LINE__);
606 	}
607 
608 	/*
609 	 * Wait for child process's semaphore request before proceeding...
610 	 */
611 	while (!semctl(semid, 0, GETNCNT, arg))
612 		sleep(1);
613 
614 	semoparray[0].sem_num = 0;
615 	semoparray[0].sem_op = 5;
616 	semoparray[0].sem_flg = 0;
617 	if (semop(semid, semoparray, 1) < 0)
618 		sys_error("semop failed", __LINE__);
619 
620 	semoparray[0].sem_num = 0;
621 	semoparray[0].sem_op = 5;
622 	semoparray[0].sem_flg = SEM_UNDO;
623 	if (semop(semid, semoparray, 1) < 0)
624 		sys_error("semop failed", __LINE__);
625 
626 	waitpid(pid, &status, 0);	/* Wait for child to complete */
627 	if (WEXITSTATUS(status))
628 		sys_error("child process terminated abnormally", __LINE__);
629 
630 	expected_value = 8;
631 	arg.val = 0;
632 	if ((val = semctl(semid, 0, GETVAL, arg)) < 0)
633 		sys_error("semctl (GETVAL) failed", __LINE__);
634 	if (val != expected_value)
635 		error("incorrect semaphore value", __LINE__);
636 
637 	/* ------------------------------------------------------------------ */
638 	/* TEST # 11 --- semval = 7, sem_op[3] = -8, sem_flg = 0              */
639 	/*           --- semval < |semop| && ! IPC_NOWAIT, caller sleeps      */
640 	/*           --- "child" is awaken via removal of semaphores          */
641 	/*         THE FOLLOWING SHOULD SHOW now be destroyed                 */
642 	/* ------------------------------------------------------------------ */
643 	SEMOP_TABLE(7, -8, "0", "Sleep (until semaphores are removed)");
644 	/*
645 	 * Child process
646 	 */
647 	if ((pid = fork()) == (pid_t) 0) {
648 		semoparray[0].sem_num = 2;
649 		semoparray[0].sem_op = -8;
650 		semoparray[0].sem_flg = 0;
651 
652 		if (semop(semid, semoparray, 1) >= 0)
653 			error("semop did not return ERMID", __LINE__);
654 		else if (errno != EIDRM) {
655 			printf("semop returned: %d\n", errno);
656 			sys_error("semop failed", __LINE__);
657 		}
658 		exit(0);
659 	} else if (pid < (pid_t) 0) {
660 		sys_error("fork failed", __LINE__);
661 	}
662 
663 	/*
664 	 * Wait for child process's semaphore request before deleting the
665 	 * semaphores...
666 	 */
667 	while (!semctl(semid, 2, GETNCNT, arg))
668 		sleep(1);
669 
670 	arg.val = 0;
671 	if (semctl(semid, 0, IPC_RMID, arg) < 0)
672 		sys_error("semctl (IPC_RMID) failed", __LINE__);
673 
674 	waitpid(pid, &status, 0);	/* Wait for child to complete */
675 	if (WEXITSTATUS(status))
676 		sys_error("child process terminated abnormally", __LINE__);
677 
678 	/* ------------------------------------------------------------------ */
679 	/*        IPC_RMID DESTROYED THE SEMAPHORE STRUCTURES.                */
680 	/*        THEREFORE:  REBUILD A SEMAPHORE STRUCTURE SET.              */
681 	/* ------------------------------------------------------------------ */
682 	/*
683 	 * Create the semaphores...
684 	 */
685 	if ((semid = semget(IPC_PRIVATE, nsems, IPC_CREAT | mode)) < 0)
686 		sys_error("semget (IPC_PRIVATE) failed", __LINE__);
687 
688 	/*
689 	 * Set the semaphore uid, gid and mode
690 	 */
691 	arg.buf = (struct semid_ds *)calloc(1, sizeof(struct semid_ds));
692 	if (!arg.buf)
693 		error("calloc failed", __LINE__);
694 	arg.buf->sem_perm.uid = uid;
695 	arg.buf->sem_perm.gid = gid;
696 	arg.buf->sem_perm.mode = mode;
697 	if (semctl(semid, 0, IPC_SET, arg) < 0)
698 		sys_error("semctl failed", __LINE__);
699 
700 	/*
701 	 * Verify that semaphore uid, gid and mode were set correctly
702 	 */
703 	if (semctl(semid, 0, IPC_STAT, arg) < 0)
704 		sys_error("semctl (IPC_STAT) failed", __LINE__);
705 	if (arg.buf->sem_perm.uid != uid)
706 		error("semctl: uid was not set", __LINE__);
707 	if (arg.buf->sem_perm.gid != gid)
708 		error("semctl: gid was not set", __LINE__);
709 	if ((arg.buf->sem_perm.mode & 0777) != mode)
710 		error("semctl: mode was not set", __LINE__);
711 	if (arg.buf->sem_nsems != nsems)
712 		error("semctl: nsems (number of semaphores) was not set",
713 		      __LINE__);
714 	SAFE_FREE(arg.buf);
715 
716 	arg.array = malloc(sizeof(int) * nsems);
717 	if (!arg.array)
718 		error("malloc failed", __LINE__);
719 	for (i = 0; i < nsems; i++)
720 		arg.array[i] = 9;
721 	if (semctl(semid, 0, SETALL, arg) < 0)
722 		sys_error("semctl (SETALL) failed", __LINE__);
723 	SAFE_FREE(arg.array);
724 
725 	/* ------------------------------------------------------------------ */
726 	/* TEST # 12 --- semval = 9, sem_op = -1, sem_flg = SEM_UN          */
727 	/*           --- semval > |sem_op| THEN (semval - |sem_op|) = 8       */
728 	/*           ---                   ALSO (semadj = semadj + sem_op)    */
729 	/*         THE FOLLOWING SHOULD SHOW semval = 8                       */
730 	/* ------------------------------------------------------------------ */
731 	SEMOP_TABLE(9, -1, "SEM_UNDO", "Obtain resource");
732 	for (i = 0; i < nsems; i++) {
733 		semoparray[i].sem_num = i;
734 		semoparray[i].sem_op = -1;
735 		semoparray[i].sem_flg = SEM_UNDO;
736 	}
737 	if (semop(semid, semoparray, nsems) < 0)
738 		sys_error("semop failed", __LINE__);
739 
740 	expected_value = 8;
741 	for (i = 0; i < nsems; i++) {
742 		arg.val = 0;
743 		if ((val = semctl(semid, 0, GETVAL, arg)) < 0)
744 			sys_error("semctl (GETVAL) failed", __LINE__);
745 		if (val != expected_value)
746 			error("incorrect semaphore value", __LINE__);
747 	}
748 
749 	/* ------------------------------------------------------------------ */
750 	/* TEST # 13 --- semval = 8, sem_op = -8, sem_flg = SEM_UN          */
751 	/*           --- semval = |sem_op| THEN (semval - |sem_op|) = 0       */
752 	/*           ---                   ALSO (semadj = semadj + sem_op)    */
753 	/* ------------------------------------------------------------------ */
754 	SEMOP_TABLE(8, -8, "SEM_UNDO", "Obtain resource");
755 	for (i = 0; i < nsems; i++) {
756 		semoparray[i].sem_num = i;
757 		semoparray[i].sem_op = -8;
758 		semoparray[i].sem_flg = SEM_UNDO;
759 	}
760 	if (semop(semid, semoparray, nsems) < 0)
761 		sys_error("semop failed", __LINE__);
762 
763 	expected_value = 0;
764 	for (i = 0; i < nsems; i++) {
765 		arg.val = 0;
766 		if ((val = semctl(semid, 0, GETVAL, arg)) < 0)
767 			sys_error("semctl (GETVAL) failed", __LINE__);
768 		if (val != expected_value)
769 			error("incorrect semaphore value", __LINE__);
770 	}
771 
772 	arg.array = malloc(sizeof(int) * nsems);
773 	if (!arg.array)
774 		error("malloc failed", __LINE__);
775 	for (i = 0; i < nsems; i++)
776 		arg.array[i] = 9;
777 	if (semctl(semid, 0, SETALL, arg) < 0)
778 		sys_error("semctl (SETALL) failed", __LINE__);
779 	SAFE_FREE(arg.array);
780 
781 	/* ------------------------------------------------------------------ */
782 	/* TEST # 14 --- semval = 9, sem_op[4] = 0, sem_flg = 0               */
783 	/*           --- semval != 0 && ! IPC_NOWAIT     caller sleeps        */
784 	/*           ---                   ALSO  ++semzcnt                    */
785 	/*           --- "child" is awaken via a signal.                      */
786 	/* ------------------------------------------------------------------ */
787 	SEMOP_TABLE(9, 0, "0", "Sleep (until signaled)");
788 	/*
789 	 * Child process
790 	 */
791 	if ((pid = fork()) == 0) {
792 		semoparray[0].sem_num = 0;
793 		semoparray[0].sem_op = 0;
794 		semoparray[0].sem_flg = 0;
795 
796 		if (semop(semid, semoparray, 1) >= 0)
797 			error("semop did not return EINTR", __LINE__);
798 		else if (errno != EINTR) {
799 			printf("semop returned: %d\n", errno);
800 			sys_error("semop failed", __LINE__);
801 		}
802 		exit(0);
803 	} else if (pid < 0) {
804 		sys_error("fork failed", __LINE__);
805 	}
806 
807 	/*
808 	 * Wait for child process's semaphore request before proceeding...
809 	 */
810 	while (!semctl(semid, 0, GETZCNT, arg))
811 		sleep(1);
812 
813 	kill(pid, SIGUSR1);
814 
815 	waitpid(pid, &status, 0);	/* Wait for child to complete */
816 	if (WEXITSTATUS(status))
817 		sys_error("child process terminated abnormally", __LINE__);
818 
819 	expected_value = 9;
820 	arg.val = 0;
821 	if ((val = semctl(semid, 0, GETVAL, arg)) < 0)
822 		sys_error("semctl (GETVAL) failed", __LINE__);
823 	if (val != expected_value)
824 		error("incorrect semaphore value", __LINE__);
825 
826 	/* ------------------------------------------------------------------ */
827 	/* TEST # 15 --- semval = 9, sem_op[0] = 0, sem_flg = 0               */
828 	/*           --- semval != 0 && ! IPC_NOWAIT     caller sleeps     */
829 	/*           ---                   ALSO  ++semzcnt                    */
830 	/* call #1   --- semval = 9, sem_op[0] = -9, sem_flg = 0              */
831 	/*           ---    THEN (semval - |sem_op|) = 0 and  --semzcnt       */
832 	/*           --- "child" is awaken via call #1.                       */
833 	/* ------------------------------------------------------------------ */
834 	SEMOP_TABLE(9, 0, "0", "Sleep (until resource becomes available)");
835 	/*
836 	 * Child process
837 	 */
838 	if ((pid = fork()) == (pid_t) 0) {
839 		semoparray[0].sem_num = 0;
840 		semoparray[0].sem_op = 0;
841 		semoparray[0].sem_flg = 0;
842 		if (semop(semid, semoparray, 1) < 0)
843 			sys_error("semop failed", __LINE__);
844 		exit(0);
845 	} else if (pid < (pid_t) 0) {
846 		sys_error("fork failed", __LINE__);
847 	}
848 
849 	/*
850 	 * Wait for child process's semaphore request before proceeding...
851 	 */
852 	while (!semctl(semid, 0, GETZCNT, arg))
853 		sleep(1);
854 
855 	semoparray[0].sem_num = 0;
856 	semoparray[0].sem_op = -9;
857 	semoparray[0].sem_flg = 0;
858 
859 	if (semop(semid, semoparray, 1) < 0)
860 		sys_error("semop failed", __LINE__);
861 
862 	waitpid(pid, &status, 0);	/* Wait for child to complete */
863 	if (WEXITSTATUS(status))
864 		sys_error("child process terminated abnormally", __LINE__);
865 
866 	expected_value = 0;
867 	arg.val = 0;
868 	if ((val = semctl(semid, 0, GETVAL, arg)) < 0)
869 		sys_error("semctl (GETVAL) failed", __LINE__);
870 	if (val != expected_value)
871 		error("incorrect semaphore value", __LINE__);
872 
873 	/* ------------------------------------------------------------------ */
874 	/* TEST # 16 --- semval = 4, sem_op[4] = 0, sem_flg = 0               */
875 	/*           --- semval != 0 && ! IPC_NOWAIT     caller sleeps     */
876 	/*           ---                   ALSO  ++semzcnt                    */
877 	/*           --- "child" is awaken via removal of semaphores          */
878 	/* ------------------------------------------------------------------ */
879 	SEMOP_TABLE(4, 0, "0", "Sleep (until semaphores are removed)");
880 	/*
881 	 * Child process
882 	 */
883 	arg.val = 4;
884 	if (semctl(semid, 4, SETVAL, arg) < 0)
885 		sys_error("semctl (SETALL) failed", __LINE__);
886 
887 	if ((pid = fork()) == (pid_t) 0) {
888 		semoparray[0].sem_num = 4;
889 		semoparray[0].sem_op = -8;
890 		semoparray[0].sem_flg = 0;
891 
892 		if (semop(semid, semoparray, 1) >= 0)
893 			error("semop did not return ERMID", __LINE__);
894 		else if (errno != EIDRM) {
895 			printf("semop returned: %d\n", errno);
896 			sys_error("semop failed", __LINE__);
897 		}
898 		exit(0);
899 	} else if (pid < 0) {
900 		sys_error("fork failed", __LINE__);
901 	}
902 
903 	/*
904 	 * Wait for child process's semaphore request before proceeding...
905 	 */
906 	while (!semctl(semid, 4, GETNCNT, arg))
907 		sleep(1);
908 
909 	arg.val = 0;
910 	if (semctl(semid, 0, IPC_RMID, arg) < 0)
911 		sys_error("semctl (IPC_RMDI) failed", __LINE__);
912 
913 	waitpid(pid, &status, 0);	/* Wait for child to complete */
914 	if (WEXITSTATUS(status))
915 		sys_error("child process terminated abnormally", __LINE__);
916 }
917 
918 /*---------------------------------------------------------------------+
919 |                             parse_args ()                            |
920 | ==================================================================== |
921 |                                                                      |
922 | Function:  Parse the command line arguments & initialize global      |
923 |            variables.                                                |
924 |                                                                      |
925 | Updates:   (command line options)                                    |
926 |                                                                      |
927 |            [-p] nproc: number of child processes                     |
928 |                                                                      |
929 +---------------------------------------------------------------------*/
parse_args(int argc,char ** argv)930 void parse_args(int argc, char **argv)
931 {
932 	int opt;
933 	int errflag = 0;
934 	char *program_name = *argv;
935 	extern char *optarg;	/* Command line option */
936 
937 	while ((opt = getopt(argc, argv, "s:p:")) != EOF) {
938 		switch (opt) {
939 		case 's':
940 			nsems = atoi(optarg);
941 			break;
942 		case 'p':
943 			nprocs = atoi(optarg);
944 			break;
945 		default:
946 			errflag++;
947 			break;
948 		}
949 	}
950 	if (nsems >= MAX_SEMAPHORES) {
951 		errflag++;
952 		fprintf(stderr, "ERROR: nsems must be less than %d\n",
953 			MAX_SEMAPHORES);
954 	}
955 	if (nprocs >= MAX_CHILDREN) {
956 		errflag++;
957 		fprintf(stderr, "ERROR: nproc must be less than %d\n",
958 			MAX_CHILDREN);
959 	}
960 
961 	if (errflag) {
962 		fprintf(stderr, USAGE, program_name);
963 		exit(2);
964 	}
965 }
966 
967 /*---------------------------------------------------------------------+
968 |                          setup_signal_handler ()                     |
969 | ==================================================================== |
970 |                                                                      |
971 | Function:  Sets up signal handler for SIGUSR1 signal                 |
972 |                                                                      |
973 +---------------------------------------------------------------------*/
setup_signal_handler()974 static void setup_signal_handler()
975 {
976 	struct sigaction sigact;
977 
978 	sigact.sa_flags = 0;
979 	sigfillset(&sigact.sa_mask);
980 
981 	/*
982 	 * Establish the signal handler for SIGUSR1
983 	 */
984 	sigact.sa_handler = (void (*)(int))catch;
985 	if (sigaction(SIGUSR1, &sigact, NULL) < 0)
986 		sys_error("sigaction failed", __LINE__);
987 }
988 
989 /*---------------------------------------------------------------------+
990 |                             catch ()                                 |
991 | ==================================================================== |
992 |                                                                      |
993 | Function:  Signal catching function for SIGUSR1                      |
994 |                                                                      |
995 +---------------------------------------------------------------------*/
catch(int sig)996 static void catch(int sig)
997 {
998 	char err_msg[256];
999 	pid_t pid = getpid();
1000 
1001 	if (sig == SIGUSR1) {
1002 		if (pid == parent_pid)
1003 			printf
1004 			    ("\t\t\t\t    <<< caught signal (SIGUSR1, %d) >>>\n",
1005 			     sig);
1006 	} else {
1007 		sprintf(err_msg, "caught unexpected signal (%d)", sig);
1008 		error(err_msg, __LINE__);
1009 	}
1010 }
1011 
1012 /*---------------------------------------------------------------------+
1013 |                             sys_error ()                             |
1014 | ==================================================================== |
1015 |                                                                      |
1016 | Function:  Creates system error message and calls error ()           |
1017 |                                                                      |
1018 +---------------------------------------------------------------------*/
sys_error(const char * msg,int line)1019 static void sys_error(const char *msg, int line)
1020 {
1021 	char syserr_msg[256];
1022 
1023 	sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
1024 	error(syserr_msg, line);
1025 }
1026 
1027 /*---------------------------------------------------------------------+
1028 |                               error ()                               |
1029 | ==================================================================== |
1030 |                                                                      |
1031 | Function:  Prints out message and exits...                           |
1032 |                                                                      |
1033 +---------------------------------------------------------------------*/
error(const char * msg,int line)1034 static void error(const char *msg, int line)
1035 {
1036 	fprintf(stderr, "ERROR pid %d [line: %d] %s\n", errpid, line, msg);
1037 	exit(-1);
1038 }
1039