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_02 |
21 | ==================================================================== |
22 | |
23 | Description: Verify semget () and semctl () options |
24 | Also uses semop in linux (semctl(GETPID) requires semop|
25 | |
26 | Algorithm: o Spawn N child processes |
27 | |
28 | o Obtain N semaphores with semget (IPC_PRIVATE) |
29 | |
30 | o Call semctl () with following commands: |
31 | IPC_SET: set uid, gid & mode |
32 | IPC_STAT: get uid, gid, mode & verify |
33 | SETVAL: set each semaphore value individually |
34 | GETVAL: get each semaphore value & verify |
35 | #if linux then call semop
36 | GETPID: get pid of last operation & verify |
37 | GETNCNT: get semncnt & verify |
38 | GETZCNT: get semzcnt & verify |
39 | SETALL: set all the semaphores |
40 | GETALL: get all the semaphores & verify values |
41 | IPC_RMID: remove the N semaphores |
42 | |
43 | System calls: The following system calls are made |
44 | |
45 | semget () - Gets a set of semaphores |
46 | semctl () - Controls semaphore operations |
47 | |
48 | Usage: semaphore_test_02 [-p nprocs] |
49 | |
50 | To compile: cc -o semaphore_test_02 semaphore_test_02.c |
51 | |
52 | Last update: Ver. 1.2, 2/14/94 00:18:23 |
53 | |
54 | Change Activity |
55 | |
56 | Version Date Name Reason |
57 | 0.1 050689 CTU Initial version |
58 | 0.2 111993 DJK Modify for AIX version 4.1 |
59 | 1.2 021494 DJK Moved to "prod" directory |
60 | |
61 | 1.3 Jan-28-02 Manoj Iyer, IBM Austin TX. manjo@austin.ibm.com|
62 | Modified semctl() to work in linux. Also the |
63 | #ifdef _LINUX_ was removed from the code. |
64 | |
65 | |
66 +---------------------------------------------------------------------*/
67
68 #include <errno.h>
69 #include <stdio.h>
70 #include <unistd.h>
71 #include <sys/ipc.h>
72 #include <sys/param.h>
73 #include <sys/sem.h>
74 #include <sys/shm.h>
75 #include <sys/stat.h>
76 #include <sys/types.h>
77 #include <sys/wait.h>
78 #include <stdlib.h>
79 #include <string.h>
80 #include "lapi/semun.h"
81
82 /*
83 * Defines
84 *
85 * NUM_SEMAPHORES: number of semaphores to create
86 */
87 #define MAX_SEMAPHORES 250
88 #define MAX_CHILDREN 200
89 #define DEFAULT_NUM_SEMAPHORES 96
90 #define DEFAULT_NUM_CHILDREN 0
91
92 #define USAGE "\nUsage: %s [-s nsems] [-p nproc]\n\n" \
93 "\t-s nsems number of semaphores (per process)\n\n" \
94 "\t-p nproc number of child processes to spawn\n\n"
95
96 #define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
97 /*
98 * Function prototypes
99 *
100 * test_commands (): Tests semget () and semctl () commands
101 * parse_args (): Parse command line arguments
102 * sys_error (): System error message function
103 * error (): Error message function
104 */
105 static void test_commands(pid_t);
106 static void sys_error(const char *, int);
107 static void error(const char *, int);
108 static void parse_args(int, char **);
109
110 /*
111 * Structures and Global variables:
112 *
113 * nsems: number of semaphores to create (per process)
114 * nprocs: number of child processes to spawn
115 * childpid: array containing process id's of the child processes
116 * parent_pid: process id of parent process
117 */
118 int nsems = DEFAULT_NUM_SEMAPHORES;
119 int nprocs = DEFAULT_NUM_CHILDREN;
120 int childpid[MAX_CHILDREN];
121 int errors = 0;
122 pid_t parent_pid;
123
124 union semun arg;
125
126 /*---------------------------------------------------------------------+
127 | main |
128 | ==================================================================== |
129 | |
130 | Function: Main program (see prolog for more details) |
131 | |
132 | Returns: (0) Successful completion |
133 | (-1) Error occurred |
134 | |
135 +---------------------------------------------------------------------*/
main(int argc,char ** argv)136 int main(int argc, char **argv)
137 {
138 int proc; /* fork loop index */
139 pid_t pid; /* Process id */
140 int status; /* Child's exit status */
141
142 /*
143 * Parse command line arguments and print out program header
144 */
145 parse_args(argc, argv);
146 printf("%s: IPC Semaphore TestSuite program\n", *argv);
147 fflush(stdout);
148 parent_pid = getpid();
149
150 /*
151 * Fork off the additional processes.
152 */
153 if (nprocs > 0) {
154 printf("\n\tParent: spawning %d child processes\n", nprocs);
155 fflush(stdout);
156 }
157 for (proc = 1; proc < nprocs; proc++) {
158 /*
159 * Child leaves loop, parent continues to fork.
160 */
161 if ((pid = fork()) < 0)
162 error("fork failed", __LINE__);
163 else if (pid == 0)
164 break;
165 else
166 childpid[proc] = pid;
167 }
168 pid = getpid();
169
170 /*
171 * Test the semget () and semctl () commands
172 */
173 test_commands(pid);
174
175 /*
176 * Finished testing commands, only parent process needs to continue
177 */
178 if (pid != parent_pid)
179 exit(0);
180
181 /*
182 * Wait for all of the child processes to complete & check their
183 * exit status.
184 *
185 * Upon completion of the child proccesses, exit program with success.
186 */
187 for (proc = 1; proc < nprocs; proc++) {
188 waitpid(childpid[proc], &status, 0);
189
190 if (!WIFEXITED(status))
191 error("child process terminated abnormally", __LINE__);
192 }
193 if (nprocs > 0)
194 printf
195 ("\n\tAll child processes verified commands successfully\n");
196 printf("\nsuccessful!\n");
197 return (errors);
198 }
199
200 /*---------------------------------------------------------------------+
201 | test_commands (pid_t pid) |
202 | ==================================================================== |
203 | |
204 | Function: test semget() and semctl() |
205 | |
206 +---------------------------------------------------------------------*/
test_commands(pid_t pid)207 static void test_commands(pid_t pid)
208 {
209 int i; /* loop index */
210 int semid; /* Unique semaphore id */
211 int val; /* Misc value */
212 gid_t gid = getgid(); /* User's group id */
213 mode_t mode = 0666; /* User's mode value */
214 uid_t uid = getuid(); /* User's user id */
215 struct sembuf sops;
216 union semun semunptr; /* This union has struct semid_ds *buf */
217 /*
218 * Test semget () with IPC_PRIVATE command
219 *
220 * Create nsems semaphores and store the returned unique
221 * semaphore identifier as semid.
222 */
223 if (pid == parent_pid)
224 printf("\n\tTesting semctl (IPC_SET) command operation\n");
225 if ((semid = semget(IPC_PRIVATE, nsems, IPC_CREAT | 0666)) < 0)
226 error("semget failed", __LINE__);
227
228 /*
229 * Test semctl () with IPC_SET command
230 *
231 * Set the uid, gid and mode fields
232 */
233 if (pid == parent_pid)
234 printf("\n\tTesting semctl (IPC_SET) command operation\n");
235
236 semunptr.buf = (struct semid_ds *)calloc(1, sizeof(struct semid_ds));
237 if (!semunptr.buf)
238 error("calloc failed", __LINE__);
239
240 semunptr.buf->sem_perm.uid = uid;
241 semunptr.buf->sem_perm.gid = gid;
242 semunptr.buf->sem_perm.mode = mode;
243
244 if (semctl(semid, 0, IPC_SET, semunptr) < 0)
245 sys_error("semctl failed", __LINE__);
246
247 /*
248 * Test semctl () with IPC_STAT command
249 *
250 * Get the semid_ds structure and verify it's fields.
251 */
252 if (pid == parent_pid)
253 printf("\n\tTesting semctl (IPC_STAT) command operation\n");
254
255 if (semctl(semid, 0, IPC_STAT, semunptr) < 0)
256 sys_error("semctl failed", __LINE__);
257 if (semunptr.buf->sem_perm.uid != uid)
258 sys_error("semctl: uid was not set", __LINE__);
259 if (semunptr.buf->sem_perm.gid != gid)
260 sys_error("semctl: gid was not set", __LINE__);
261 if ((semunptr.buf->sem_perm.mode & 0777) != mode)
262 sys_error("semctl: mode was not set", __LINE__);
263 if (semunptr.buf->sem_nsems != nsems)
264 sys_error("semctl: nsems (number of semaphores) was not set",
265 __LINE__);
266 SAFE_FREE(semunptr.buf);
267
268 /*
269 * Test semctl () with SETVAL command
270 *
271 * Loop through all the semaphores and set the semaphore value
272 * to the loop index (i).
273 */
274 if (pid == parent_pid)
275 printf("\n\tTesting semctl (SETVAL) command operation\n");
276 for (i = 0; i < nsems; i++) {
277 arg.val = i;
278 if (semctl(semid, i, SETVAL, arg) < 0)
279 sys_error("semctl failed", __LINE__);
280 }
281
282 /*
283 * Test semctl () with GETVAL command
284 *
285 * Loop through all the semaphores and retrieve the semaphore values
286 * and compare with the expected value, the loop index (i).
287 */
288 if (pid == parent_pid)
289 printf("\n\tTesting semctl (GETVAL) command operation\n");
290 for (i = 0; i < nsems; i++) {
291 if ((val = semctl(semid, i, GETVAL, arg)) < 0)
292 sys_error("semctl failed", __LINE__);
293 if (val != i)
294 sys_error("semctl (GETVAL) failed", __LINE__);
295 }
296
297 // testing in linux. before semctl(GETPID) works, we must call semop
298 if (pid == parent_pid)
299 printf("\n\tTesting semop (signal and wait) operations\n");
300 sops.sem_flg = 0;
301 for (i = 0; i < nsems; i++) {
302 sops.sem_num = i;
303 sops.sem_op = 1;
304 if ((val = semop(semid, &sops, 1)) < 0)
305 sys_error("semop signal failed", __LINE__);
306 sops.sem_op = -1;
307 if ((val = semop(semid, &sops, 1)) < 0)
308 sys_error("semop wait failed", __LINE__);
309 }
310
311 /*
312 * Test semctl () with GETPID command
313 */
314 if (pid == parent_pid)
315 printf("\n\tTesting semctl (GETPID) command operation\n");
316 for (i = 0; i < nsems; i++) {
317 if ((val = semctl(semid, i, GETPID, arg)) < 0)
318 sys_error("semctl failed", __LINE__);
319 if (val != pid)
320 sys_error("semctl (GETPID) failed", __LINE__);
321 }
322
323 /*
324 * Test semctl () with GETNCNT command
325 *
326 * Get semncnt (the number of processes awaiting semval > currval)
327 * and insure that this value is 0...
328 *
329 * Note: A better test would include forking off a process that
330 * waits for the semaphore so that semncnt would be nonzero.
331 */
332 if (pid == parent_pid)
333 printf("\n\tTesting semctl (GETNCNT) command operation\n");
334 for (i = 0; i < nsems; i++) {
335 if ((val = semctl(semid, i, GETNCNT, arg)) < 0)
336 sys_error("semctl failed", __LINE__);
337 if (val != 0)
338 sys_error("semctl (GETNCNT) returned wrong value",
339 __LINE__);
340 }
341
342 /*
343 * Test semctl () with GETZCNT command
344 *
345 * Get semzcnt (the number of processes awaiting semval = currval)
346 * and insure that this value is 0...
347 *
348 * Note: A better test would include forking off a process that
349 * waits for the semaphore so that semzcnt would be nonzero.
350 */
351 if (pid == parent_pid)
352 printf("\n\tTesting semctl (GETZCNT) command operation\n");
353 for (i = 0; i < nsems; i++) {
354 if ((val = semctl(semid, i, GETZCNT, arg)) < 0)
355 sys_error("semctl failed", __LINE__);
356 if (val != 0)
357 sys_error("semctl (GETZCNT) returned wrong value",
358 __LINE__);
359 }
360
361 /*
362 * Test semctl () with SETALL command
363 *
364 * Set all of the semaphore values in the set.
365 */
366 arg.array = malloc(sizeof(int) * nsems);
367 if (!arg.array)
368 error("malloc failed", __LINE__);
369 if (pid == parent_pid)
370 printf("\n\tTesting semctl (SETALL) command operation\n");
371 for (i = 0; i < nsems; i++)
372 arg.array[i] = i;
373 if (semctl(semid, 0, SETALL, arg) < 0)
374 sys_error("semctl failed", __LINE__);
375
376 /*
377 * Test semctl () with GETALL command
378 *
379 * Get all of the semaphore values in the set, and verify that
380 * they are all correct.
381 */
382 if (pid == parent_pid)
383 printf("\n\tTesting semctl (GETALL) command operation\n");
384 if (semctl(semid, nsems, GETALL, arg) < 0)
385 sys_error("semctl failed", __LINE__);
386 for (i = 0; i < nsems; i++) {
387 if (arg.array[i] != i)
388 sys_error("semaphore does not match expected value",
389 __LINE__);
390 }
391
392 /*
393 * Test semctl () with IPC_RMID command
394 *
395 * Remove the semaphores
396 */
397 if (pid == parent_pid)
398 printf("\n\tTesting semctl (IPC_RMID) command operation\n");
399 if (semctl(semid, nsems, IPC_RMID, arg) < 0)
400 sys_error("semctl failed", __LINE__);
401 SAFE_FREE(arg.array);
402
403 }
404
405 /*---------------------------------------------------------------------+
406 | parse_args () |
407 | ==================================================================== |
408 | |
409 | Function: Parse the command line arguments & initialize global |
410 | variables. |
411 | |
412 | Updates: (command line options) |
413 | |
414 | [-p] nproc: number of child processes |
415 | |
416 +---------------------------------------------------------------------*/
parse_args(int argc,char ** argv)417 void parse_args(int argc, char **argv)
418 {
419 int opt;
420 int errflag = 0;
421 char *program_name = *argv;
422 extern char *optarg; /* Command line option */
423
424 while ((opt = getopt(argc, argv, "s:p:")) != EOF) {
425 switch (opt) {
426 case 's':
427 nsems = atoi(optarg);
428 break;
429 case 'p':
430 nprocs = atoi(optarg);
431 break;
432 default:
433 errflag++;
434 break;
435 }
436 }
437 if (nsems >= MAX_SEMAPHORES) {
438 errflag++;
439 fprintf(stderr, "ERROR: nsems must be less than %d\n",
440 MAX_SEMAPHORES);
441 }
442 if (nprocs >= MAX_CHILDREN) {
443 errflag++;
444 fprintf(stderr, "ERROR: nproc must be less than %d\n",
445 MAX_CHILDREN);
446 }
447
448 if (errflag) {
449 fprintf(stderr, USAGE, program_name);
450 exit(2);
451 }
452 }
453
454 /*---------------------------------------------------------------------+
455 | sys_error () |
456 | ==================================================================== |
457 | |
458 | Function: Creates system error message and increments errors |
459 | |
460 +---------------------------------------------------------------------*/
sys_error(const char * msg,int line)461 static void sys_error(const char *msg, int line)
462 {
463 char syserr_msg[256];
464
465 sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
466 fprintf(stderr, "ERROR [line: %d] %s\n", line, syserr_msg);
467 errors++;
468 /* error (syserr_msg, line); */
469 }
470
471 /*---------------------------------------------------------------------+
472 | error () |
473 | ==================================================================== |
474 | |
475 | Function: Prints out message and exits... |
476 | |
477 +---------------------------------------------------------------------*/
error(const char * msg,int line)478 static void error(const char *msg, int line)
479 {
480 fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
481 exit(-1);
482 }
483