semctl01.c revision bffa8865e3ec438fe437163f95f0686f8c17c5f3
1/*
2 * Copyright (c) International Business Machines  Corp., 2001
3 *
4 * This program is free software;  you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12 * the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program;  if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19/*
20 * NAME
21 *	semctl01.c
22 *
23 * DESCRIPTION
24 *	semctl01 - test the 13 possible semctl() commands
25 *
26 * ALGORITHM
27 *	create a semaphore set with read and alter permissions
28 *	loop if that option was specified
29 *	  loop through the test cases
30 *	    do any setup required for the test case
31 *	    make the semctl() call using the TEST() macro
32 *	    check the return code
33 *	      if failure, issue a FAIL message.
34 *	    otherwise,
35 *	      if doing functionality testing
36 *		call the appropriate test function
37 *		if correct,
38 *			issue a PASS message
39 *		otherwise
40 *			issue a FAIL message
41 *	call cleanup
42 */
43
44#include "ipcsem.h"
45#include "libtestsuite.h"
46
47char *TCID = "semctl01";
48int TST_TOTAL = 10;
49
50static int sem_id_1 = -1;
51
52static int sync_pipes[2];
53
54/*
55 * These are the various setup and check functions for the 10 different
56 * commands that are available for the semctl() call.
57 */
58static void func_stat(void);
59static void set_setup(void), func_set(void);
60static void func_gall(void);
61static void cnt_setup(int), func_cnt(int);
62static void pid_setup(void), func_pid(int);
63static void func_gval(int);
64static void sall_setup(void), func_sall(void);
65static void func_sval(void);
66static void func_rmid(void);
67static void child_cnt(void);
68static void child_pid(void);
69
70static struct semid_ds buf;
71static unsigned short array[PSEMS];
72static struct sembuf sops;
73
74#define INCVAL 2
75#define NEWMODE	066
76#define NCHILD	5
77#define SEM2	2
78#define SEM4	4
79#define ONE	1
80#ifdef _XLC_COMPILER
81#define SEMUN_CAST
82#else
83#define SEMUN_CAST (union semun)
84#endif
85
86static int pid_arr[NCHILD];
87
88#ifdef UCLINUX
89#define PIPE_NAME	"semctl01"
90static char *argv0;
91static int sem_op;
92#endif
93
94static struct test_case_t {
95	int semnum;
96	int cmd;
97	void (*func_test) ();
98	union semun arg;
99	void (*func_setup) ();
100} TC[] = {
101	{0, IPC_STAT, func_stat, SEMUN_CAST & buf, NULL},
102	{0, IPC_SET, func_set, SEMUN_CAST & buf, set_setup},
103	{0, GETALL, func_gall, SEMUN_CAST array, NULL},
104	{SEM4, GETNCNT, func_cnt, SEMUN_CAST & buf, cnt_setup},
105	{SEM2, GETPID, func_pid, SEMUN_CAST & buf, pid_setup},
106	{SEM2, GETVAL, func_gval, SEMUN_CAST & buf, NULL},
107	{SEM4, GETZCNT, func_cnt, SEMUN_CAST & buf, cnt_setup},
108	{0, SETALL, func_sall, SEMUN_CAST array, sall_setup},
109	{SEM4, SETVAL, func_sval, SEMUN_CAST INCVAL, NULL},
110	{0, IPC_RMID, func_rmid, SEMUN_CAST & buf, NULL},
111};
112
113int main(int argc, char *argv[])
114{
115	int lc;
116	char *msg;
117	int i, j;
118
119	msg = parse_opts(argc, argv, NULL, NULL);
120	if (msg != NULL)
121		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
122
123#ifdef UCLINUX
124	argv0 = argv[0];
125	maybe_run_child(&child_pid, "nd", 1, &sem_id_1);
126	maybe_run_child(&child_cnt, "ndd", 2, &sem_id_1, &sem_op);
127#endif
128
129	setup();
130
131	for (lc = 0; TEST_LOOPING(lc); lc++) {
132		tst_count = 0;
133
134		for (i = 0; i < TST_TOTAL; i++) {
135
136			/*
137			 * Set up any conditions if needed
138			 */
139			if (TC[i].func_setup != NULL) {
140				/* call the setup function */
141				switch (TC[i].cmd) {
142				case GETNCNT:
143					(*TC[i].func_setup) (-ONE);
144					break;
145				case GETZCNT:
146					(*TC[i].func_setup) (0);
147					break;
148				default:
149					(*TC[i].func_setup) ();
150					break;
151				}
152			}
153
154			TEST(semctl(sem_id_1, TC[i].semnum, TC[i].cmd,
155				    TC[i].arg));
156
157			if (TEST_RETURN == -1) {
158				tst_resm(TFAIL, "%s call failed - errno = %d "
159					 ": %s", TCID, TEST_ERRNO,
160					 strerror(TEST_ERRNO));
161			} else {
162				if (STD_FUNCTIONAL_TEST) {
163					/*
164					 * call the appropriate test function
165					 * and pass the return value where it
166					 * is needed to perform certain tests.
167					 */
168					switch (TC[i].cmd) {
169					case GETNCNT:
170					case GETZCNT:
171					case GETPID:
172					case GETVAL:
173						(*TC[i].func_test)
174						    (TEST_RETURN);
175						break;
176					default:
177						(*TC[i].func_test) ();
178						break;
179					}
180				} else {
181					tst_resm(TPASS, "call succeeded");
182				}
183			}
184
185			/*
186			 * If testing GETNCNT or GETZCNT, clean up the children.
187			 */
188			switch (TC[i].cmd) {
189			case GETNCNT:
190			case GETZCNT:
191				for (j = 0; j < NCHILD; j++) {
192					if (kill(pid_arr[j], SIGKILL) == -1)
193						tst_brkm(TBROK, cleanup,
194							 "child kill failed");
195				}
196				break;
197			}
198		}
199		/*
200		 * recreate the semaphore resource if looping
201		 */
202		if (TEST_LOOPING(lc)) {
203			sem_id_1 = semget(semkey, PSEMS,
204					  IPC_CREAT | IPC_EXCL | SEM_RA);
205			if (sem_id_1 == -1)
206				tst_brkm(TBROK, cleanup,
207					 "couldn't recreate " "semaphore");
208		}
209	}
210
211	cleanup();
212
213	tst_exit();
214}
215
216/*
217 * func_stat() - check the functionality of the IPC_STAT command with semctl()
218 */
219static void func_stat(void)
220{
221	/* check the number of semaphores and the ipc_perm.mode value */
222	if (buf.sem_nsems == PSEMS && buf.sem_perm.mode == (SEM_RA))
223		tst_resm(TPASS, "buf.sem_nsems and buf.sem_perm.mode"
224			 " are correct");
225	else
226		tst_resm(TFAIL, "semaphore STAT info is incorrect");
227}
228
229/*
230 * set_setup() - set up for the IPC_SET command with semctl()
231 */
232static void set_setup(void)
233{
234	/* set up a new mode for the semaphore set */
235	buf.sem_perm.mode = SEM_RA | NEWMODE;
236}
237
238/*
239 * func_set() - check the functionality of the IPC_SET command with semctl()
240 */
241static void func_set(void)
242{
243	/* first stat the semaphore to get the new data */
244	if (semctl(sem_id_1, 0, IPC_STAT, (union semun)&buf) == -1) {
245		tst_resm(TBROK, "stat failed in func_set()");
246		return;
247	}
248
249	/* check that the new mode is what we set */
250	if (buf.sem_perm.mode == (SEM_RA | NEWMODE))
251		tst_resm(TPASS, "buf.sem_perm.mode is correct");
252	else
253		tst_resm(TFAIL, "semaphore mode info is incorrect");
254}
255
256/*
257 * func_gall() - check the functionality of the GETALL command with semctl()
258 */
259static void func_gall(void)
260{
261	int i;
262
263	/* the initial value of the primitive semaphores should be zero */
264	for (i = 0; i < PSEMS; i++) {
265		if (array[i] != 0) {
266			tst_resm(TFAIL, "semaphore %d has unexpected value", i);
267			return;
268		}
269	}
270	tst_resm(TPASS, "semaphores have expected values");
271}
272
273/*
274 * cnt_setup() - set up for the GETNCNT and GETZCNT commands with semctl()
275 */
276static void cnt_setup(int opval)
277{
278	int pid, i;
279
280	sops.sem_num = SEM4;
281	sops.sem_flg = 0;
282
283	/*
284	 * if seting up for GETZCNT, the semaphore value needs to be positive
285	 */
286	if (opval == 0) {
287		/* initialize the semaphore value to ONE */
288		sops.sem_op = ONE;
289		if (semop(sem_id_1, &sops, 1) == -1)
290			tst_brkm(TBROK, cleanup, "semop #1 failed - cnt_setup");
291	}
292
293	/* set the correct operation */
294	sops.sem_op = opval;
295	for (i = 0; i < NCHILD; i++) {
296		if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
297			tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
298
299		/* fork five children to wait */
300		pid = FORK_OR_VFORK();
301		if (pid == -1)
302			tst_brkm(TBROK, cleanup, "fork failed in cnt_setup");
303
304		if (pid == 0) {
305#ifdef UCLINUX
306			sem_op = sops.sem_op;
307			if (self_exec(argv0, "ndd", 2, sem_id_1, sem_op) < 0)
308				tst_brkm(TBROK, cleanup, "self_exec failed "
309					 "in cnt_setup");
310#else
311			child_cnt();
312#endif
313		} else {
314			if (sync_pipe_wait(sync_pipes) == -1)
315				tst_brkm(TBROK, cleanup,
316					 "sync_pipe_wait failed");
317
318			if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
319				tst_brkm(TBROK, cleanup,
320					 "sync_pipe_close failed");
321
322			/* save the pid so we can kill it later */
323			pid_arr[i] = pid;
324		}
325	}
326	/* After last son has been created, give it a chance to execute the
327	 * semop command before we continue. Without this sleep, on SMP machine
328	 * the father semctl could be executed before the son semop.
329	 */
330	sleep(1);
331}
332
333static void child_cnt(void)
334{
335#ifdef UCLINUX
336	sops.sem_op = (short int)sem_op;
337	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
338		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
339#endif
340
341	if (sync_pipe_notify(sync_pipes) == -1)
342		tst_brkm(TBROK, cleanup, "sync_pipe_notify failed");
343
344#ifdef UCLINUX
345	if (sync_pipe_close(sync_pipes, NULL) == -1)
346#else
347	if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
348#endif
349		tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
350
351	sops.sem_num = SEM4;
352	sops.sem_flg = 0;
353
354	/*
355	 * Do a semop that will cause the child to sleep.
356	 * The child process will be killed in the func_ncnt
357	 * routine which should cause an error to be return
358	 * by the semop() call.
359	 */
360	if (semop(sem_id_1, &sops, 1) != -1)
361		tst_resm(TBROK, "semop succeeded - cnt_setup");
362
363	exit(0);
364}
365
366/*
367 * func_cnt() - check the functionality of the GETNCNT and GETZCNT commands
368 *	        with semctl()
369 */
370static void func_cnt(int rval)
371{
372
373	if (rval == NCHILD)
374		tst_resm(TPASS, "number of sleeping processes is correct");
375	else
376		tst_resm(TFAIL, "number of sleeping processes is not correct");
377}
378
379/*
380 * pid_setup() - set up for the GETPID command with semctl()
381 */
382static void pid_setup(void)
383{
384	int pid;
385
386	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
387		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
388
389	/*
390	 * Fork a child to do a semop that will pass.
391	 */
392	pid = FORK_OR_VFORK();
393	if (pid == -1)
394		tst_brkm(TBROK, cleanup, "fork failed in pid_setup()");
395
396	if (pid == 0) {		/* child */
397#ifdef UCLINUX
398		if (self_exec(argv0, "nd", 1, sem_id_1) < 0)
399			tst_brkm(TBROK, cleanup, "self_exec failed "
400				 "in pid_setup()");
401#else
402		child_pid();
403#endif
404	} else {		/* parent */
405		if (sync_pipe_wait(sync_pipes) == -1)
406			tst_brkm(TBROK, cleanup, "sync_pipe_wait failed");
407
408		if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
409			tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
410		sleep(1);
411		pid_arr[SEM2] = pid;
412	}
413}
414
415static void child_pid(void)
416{
417#ifdef UCLINUX
418	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
419		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
420#endif
421
422	if (sync_pipe_notify(sync_pipes) == -1)
423		tst_brkm(TBROK, cleanup, "sync_pipe_notify failed");
424
425	if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
426		tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
427
428	sops.sem_num = SEM2;
429	sops.sem_op = ONE;
430	sops.sem_flg = 0;
431
432	/*
433	 * Do a semop that will increment the semaphore.
434	 */
435	if (semop(sem_id_1, &sops, 1) == -1)
436		tst_resm(TBROK, "semop failed - pid_setup");
437
438	exit(0);
439}
440
441/*
442 * func_pid() - check the functionality of the GETPID command with semctl()
443 */
444static void func_pid(int rval)
445{
446	/* compare the rval (pid) to the saved pid from the setup */
447	if (rval == pid_arr[SEM2])
448		tst_resm(TPASS, "last pid value is correct");
449	else
450		tst_resm(TFAIL, "last pid value is not correct");
451}
452
453/*
454 * func_gval() - check the functionality of the GETVAL command with semctl()
455 */
456static void func_gval(int rval)
457{
458	/*
459	 * This is a simple test.  The semaphore value should be equal
460	 * to ONE as it was set in the last test (GETPID).
461	 */
462	if (rval == 1)
463		tst_resm(TPASS, "semaphore value is correct");
464	else
465		tst_resm(TFAIL, "semaphore value is not correct");
466}
467
468/*
469 * all_setup() - set up for the SETALL command with semctl()
470 */
471static void sall_setup(void)
472{
473	int i;
474
475	for (i = 0; i < PSEMS; i++) {
476		/* initialize the array values to 3 */
477		array[i] = 3;
478	}
479}
480
481/*
482 * func_sall() - check the functionality of the SETALL command with semctl()
483 */
484static void func_sall(void)
485{
486	int i;
487	unsigned short rarray[PSEMS];
488
489	/*
490	 * do a GETALL and compare the values to those set above
491	 */
492
493	if (semctl(sem_id_1, 0, GETALL, (union semun)rarray) == -1)
494		tst_brkm(TBROK, cleanup, "semctl failed in func_sall");
495
496	for (i = 0; i < PSEMS; i++) {
497		if (array[i] != rarray[i]) {
498			tst_resm(TFAIL, "semaphore values are not correct");
499			return;
500		}
501	}
502
503	tst_resm(TPASS, "semaphore values are correct");
504}
505
506/*
507 * func_sval() - check the functionality of the SETVAL command with semctl()
508 */
509static void func_sval(void)
510{
511	int semv;
512	union semun arr;
513
514	/*
515	 * do a GETVAL and compare it to the value set above
516	 */
517
518	semv = semctl(sem_id_1, SEM4, GETVAL, arr);
519	if (semv == -1)
520		tst_brkm(TBROK, cleanup, "semctl failed in func_sval");
521
522	if (semv != INCVAL)
523		tst_resm(TFAIL, "semaphore value is not what was set");
524	else
525		tst_resm(TPASS, "semaphore value is correct");
526}
527
528/*
529 * func_rmid() - check the functionality of the IPC_RMID command with semctl()
530 */
531static void func_rmid(void)
532{
533
534	/*
535	 * do a semop() - we should get EINVAL
536	 */
537	if (semop(sem_id_1, &sops, 1) != -1)
538		tst_resm(TFAIL, "semop succeeded on expected fail");
539
540	if (errno != EINVAL)
541		tst_resm(TFAIL, "returned errno - %d - is not expected", errno);
542	else
543		tst_resm(TPASS, "semaphore appears to be removed");
544
545	sem_id_1 = -1;
546}
547
548void setup(void)
549{
550
551	tst_sig(FORK, DEF_HANDLER, cleanup);
552
553	TEST_PAUSE;
554
555	tst_tmpdir();
556
557	/* get an IPC resource key */
558	semkey = getipckey();
559
560	/* create a semaphore set with read and alter permissions */
561	sem_id_1 = semget(semkey, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA);
562	if (sem_id_1 == -1)
563		tst_brkm(TBROK, cleanup, "couldn't create semaphore in setup");
564}
565
566void cleanup(void)
567{
568	/* if it exists, remove the semaphore resource */
569	rm_sema(sem_id_1);
570
571	tst_rmdir();
572
573	TEST_CLEANUP;
574}
575