semctl01.c revision 60fa8014af7534eaefa901200c8df4b74ce422e6
1/*
2 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20/*
21 * NAME
22 *	semctl01.c
23 *
24 * DESCRIPTION
25 *	semctl01 - test the 10 possible semctl() commands
26 *
27 * ALGORITHM
28 *	create a semaphore set with read and alter permissions
29 *	loop if that option was specified
30 *	  loop through the test cases
31 *	    do any setup required for the test case
32 *	    make the semctl() call using the TEST() macro
33 *	    check the return code
34 *	      if failure, issue a FAIL message.
35 *	    otherwise,
36 *	      if doing functionality testing
37 *		call the appropriate test function
38 *	  	if correct,
39 *			issue a PASS message
40 *		otherwise
41 *			issue a FAIL message
42 *	call cleanup
43 *
44 * USAGE:  <for command-line>
45 *  semctl01 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
46 *     where,  -c n : Run n copies concurrently.
47 *             -f   : Turn off functionality Testing.
48 *	       -i n : Execute test n times.
49 *	       -I x : Execute test for x seconds.
50 *	       -P x : Pause for x seconds between iterations.
51 *	       -t   : Turn on syscall timing.
52 *
53 * HISTORY
54 *	03/2001 - Written by Wayne Boyer
55 *
56 *      11/03/2008 Renaud Lottiaux (Renaud.Lottiaux@kerlabs.com)
57 *      - Fix concurrency issue. Due to the use of usleep function to
58 *        synchronize processes, synchronization issues can occur on a loaded
59 *        system. Fix this by using pipes to synchronize processes.
60 *
61 * RESTRICTIONS
62 *	none
63 */
64
65#include "ipcsem.h"
66#include "libtestsuite.h"
67
68char *TCID = "semctl01";
69int TST_TOTAL = 10;
70extern int Tst_count;
71
72int sem_id_1 = -1;		/* a semaphore set with read and alter permissions */
73
74int sync_pipes[2];
75
76/*
77 * These are the various setup and check functions for the 10 different
78 * commands that are available for the semctl() call.
79 */
80void func_stat(void);
81void set_setup(void), func_set(void);
82void func_gall(void);
83void cnt_setup(int), func_cnt(int);
84void pid_setup(void), func_pid(int);
85void gval_setup(void), func_gval(int);
86void sall_setup(void), func_sall(void);
87void func_sval(void);
88void func_rmid(void);
89void child_cnt(void);
90void child_pid(void);
91
92struct semid_ds buf;
93unsigned short array[PSEMS];
94struct sembuf sops;
95
96#define INCVAL 2		/* a semaphore increment value */
97#define NEWMODE	066
98#define NCHILD	5
99#define SEM2	2		/* semaphore to use for GETPID and GETVAL */
100#define SEM4	4		/* semaphore to use for GETNCNT and GETZCNT */
101#define ONE	1
102#ifdef _XLC_COMPILER
103#define SEMUN_CAST
104#else
105#define SEMUN_CAST (union semun)
106#endif
107
108#ifdef _XLC_COMPILER
109#define SEMUN_CAST
110#else
111#define SEMUN_CAST (union semun)
112#endif
113
114int pid_arr[NCHILD];
115
116#ifdef UCLINUX
117#define PIPE_NAME	"semctl01"
118static char *argv0;
119int sem_op;
120#endif
121
122struct test_case_t {
123	int semnum;		/* the primitive semaphore to use */
124	int cmd;		/* the command to test */
125	void (*func_test) ();	/* the test function */
126	union semun arg;
127	void (*func_setup) ();	/* the setup function if necessary */
128} TC[] = {
129	{
130	0, IPC_STAT, func_stat, SEMUN_CAST & buf, NULL}, {
131	0, IPC_SET, func_set, SEMUN_CAST & buf, set_setup}, {
132	0, GETALL, func_gall, SEMUN_CAST array, NULL}, {
133	SEM4, GETNCNT, func_cnt, SEMUN_CAST & buf, cnt_setup}, {
134	SEM2, GETPID, func_pid, SEMUN_CAST & buf, pid_setup}, {
135	SEM2, GETVAL, func_gval, SEMUN_CAST & buf, NULL}, {
136	SEM4, GETZCNT, func_cnt, SEMUN_CAST & buf, cnt_setup}, {
137	0, SETALL, func_sall, SEMUN_CAST array, sall_setup}, {
138	SEM4, SETVAL, func_sval, SEMUN_CAST INCVAL, NULL}, {
139	0, IPC_RMID, func_rmid, SEMUN_CAST & buf, NULL}
140};
141
142int main(int ac, char **av)
143{
144	int lc;			/* loop counter */
145	char *msg;		/* message returned from parse_opts */
146	int i, j;
147
148	/* parse standard options */
149	if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) {
150		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
151	}
152#ifdef UCLINUX
153	argv0 = av[0];
154	maybe_run_child(&child_pid, "nd", 1, &sem_id_1);
155	maybe_run_child(&child_cnt, "ndd", 2, &sem_id_1, &sem_op);
156#endif
157
158	setup();		/* global setup */
159
160	/* The following loop checks looping state if -i option given */
161
162	for (lc = 0; TEST_LOOPING(lc); lc++) {
163		/* reset Tst_count in case we are looping */
164		Tst_count = 0;
165
166		/* loop through the test cases */
167		for (i = 0; i < TST_TOTAL; i++) {
168
169			/*
170			 * Set up any conditions if needed
171			 */
172			if (TC[i].func_setup != NULL) {
173				/* call the setup function */
174				switch (TC[i].cmd) {
175				case GETNCNT:
176					(*TC[i].func_setup) (-ONE);
177					break;
178				case GETZCNT:
179					(*TC[i].func_setup) (0);
180					break;
181				default:
182					(*TC[i].func_setup) ();
183					break;
184				}
185			}
186
187			/*
188			 * Use TEST macro to make the call
189			 */
190
191			TEST(semctl(sem_id_1, TC[i].semnum, TC[i].cmd,
192				    TC[i].arg));
193
194			if (TEST_RETURN == -1) {
195				tst_resm(TFAIL, "%s call failed - errno = %d "
196					 ": %s", TCID, TEST_ERRNO,
197					 strerror(TEST_ERRNO));
198			} else {
199				if (STD_FUNCTIONAL_TEST) {
200					/*
201					 * call the appropriate test function
202					 * and pass the return value where it
203					 * is needed to perform certain tests.
204					 */
205					switch (TC[i].cmd) {
206					case GETNCNT:
207					 /*FALLTHROUGH*/ case GETZCNT:
208					 /*FALLTHROUGH*/ case GETPID:
209					 /*FALLTHROUGH*/ case GETVAL:
210						(*TC[i].
211						 func_test) (TEST_RETURN);
212						break;
213					default:
214						(*TC[i].func_test) ();
215						break;
216					}
217				} else {
218					tst_resm(TPASS, "call succeeded");
219				}
220			}
221
222			/*
223			 * If testing GETNCNT or GETZCNT, clean up the children.
224			 */
225			switch (TC[i].cmd) {
226			case GETNCNT:
227			 /*FALLTHROUGH*/ case GETZCNT:
228				for (j = 0; j < NCHILD; j++) {
229					if (kill(pid_arr[j], SIGKILL) == -1) {
230						tst_brkm(TBROK, cleanup,
231							 "child kill failed");
232					}
233				}
234				break;
235			}
236		}
237		/*
238		 * recreate the semaphore resource if looping
239		 */
240		if (TEST_LOOPING(lc)) {
241			if ((sem_id_1 = semget(semkey, PSEMS,
242					       IPC_CREAT | IPC_EXCL | SEM_RA))
243			    == -1) {
244				tst_brkm(TBROK, cleanup,
245					 "couldn't recreate " "semaphore");
246			}
247		}
248	}
249
250	cleanup();
251
252	 /*NOTREACHED*/ return 0;
253
254}
255
256/*
257 * func_stat() - check the functionality of the IPC_STAT command with semctl()
258 */
259void func_stat()
260{
261	/* check the number of semaphores and the ipc_perm.mode value */
262	if (buf.sem_nsems == PSEMS && buf.sem_perm.mode == (SEM_RA)) {
263		tst_resm(TPASS, "buf.sem_nsems and buf.sem_perm.mode"
264			 " are correct");
265	} else {
266		tst_resm(TFAIL, "semaphore STAT info is incorrect");
267	}
268}
269
270/*
271 * set_setup() - set up for the IPC_SET command with semctl()
272 */
273void set_setup()
274{
275	/* set up a new mode for the semaphore set */
276	buf.sem_perm.mode = SEM_RA | NEWMODE;
277}
278
279/*
280 * func_set() - check the functionality of the IPC_SET command with semctl()
281 */
282void func_set()
283{
284	/* first stat the semaphore to get the new data */
285	if (semctl(sem_id_1, 0, IPC_STAT, (union semun)&buf) == -1) {
286		tst_resm(TBROK, "stat failed in func_set()");
287		return;
288	}
289
290	/* check that the new mode is what we set */
291	if (buf.sem_perm.mode == (SEM_RA | NEWMODE)) {
292		tst_resm(TPASS, "buf.sem_perm.mode is correct");
293	} else {
294		tst_resm(TFAIL, "semaphore mode info is incorrect");
295	}
296}
297
298/*
299 * func_gall() - check the functionality of the GETALL command with semctl()
300 */
301void func_gall()
302{
303	int i;
304
305	/* the initial value of the primitive semaphores should be zero */
306	for (i = 0; i < PSEMS; i++) {
307		if (array[i] != 0) {
308			tst_resm(TFAIL, "semaphore %d has unexpected value", i);
309			return;
310		}
311	}
312	tst_resm(TPASS, "semaphores have expected values");
313}
314
315/*
316 * cnt_setup() - set up for the GETNCNT and GETZCNT commands with semctl()
317 */
318void cnt_setup(int opval)
319{
320	int pid, i;
321
322	sops.sem_num = SEM4;
323	sops.sem_flg = 0;
324
325	/*
326	 * if seting up for GETZCNT, the semaphore value needs to be positive
327	 */
328	if (opval == 0) {
329		/* initialize the semaphore value to ONE */
330		sops.sem_op = ONE;
331		if (semop(sem_id_1, &sops, 1) == -1) {
332			tst_brkm(TBROK, cleanup, "semop #1 failed - cnt_setup");
333		}
334	}
335
336	sops.sem_op = opval;	/* set the correct operation */
337	for (i = 0; i < NCHILD; i++) {
338		if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
339			tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
340
341		/* fork five children to wait */
342		if ((pid = FORK_OR_VFORK()) == -1)
343			tst_brkm(TBROK, cleanup, "fork failed in cnt_setup");
344
345		if (pid == 0) {	/* child */
346#ifdef UCLINUX
347			sem_op = sops.sem_op;
348			if (self_exec(argv0, "ndd", 2, sem_id_1, sem_op) < 0) {
349				tst_brkm(TBROK, cleanup, "self_exec failed "
350					 "in cnt_setup");
351			}
352#else
353			child_cnt();
354#endif
355		} else {	/* parent */
356			if (sync_pipe_wait(sync_pipes) == -1)
357				tst_brkm(TBROK, cleanup,
358					 "sync_pipe_wait failed");
359
360			if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
361				tst_brkm(TBROK, cleanup,
362					 "sync_pipe_close failed");
363
364			/* save the pid so we can kill it later */
365			pid_arr[i] = pid;
366		}
367	}
368	/* After last son has been created, give it a chance to execute the
369	 * semop command before we continue. Without this sleep, on SMP machine
370	 * the father semctl could be executed before the son semop.
371	 */
372	sleep(1);
373}
374
375void child_cnt()
376{
377#ifdef UCLINUX
378	sops.sem_op = (short int)sem_op;
379	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
380		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
381#endif
382
383	if (sync_pipe_notify(sync_pipes) == -1)
384		tst_brkm(TBROK, cleanup, "sync_pipe_notify failed");
385
386#ifdef UCLINUX
387	if (sync_pipe_close(sync_pipes, NULL) == -1)
388#else
389	if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
390#endif
391		tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
392
393	sops.sem_num = SEM4;
394	sops.sem_flg = 0;
395
396	/*
397	 * Do a semop that will cause the child to sleep.
398	 * The child process will be killed in the func_ncnt
399	 * routine which should cause an error to be return
400	 * by the semop() call.
401	 */
402	if (semop(sem_id_1, &sops, 1) != -1) {
403		tst_resm(TBROK, "semop succeeded - cnt_setup");
404	}
405	exit(0);
406}
407
408/*
409 * func_cnt() - check the functionality of the GETNCNT and GETZCNT commands
410 *	        with semctl()
411 */
412void func_cnt(int rval)
413{
414
415	if (rval == NCHILD) {
416		tst_resm(TPASS, "number of sleeping processes is correct");
417	} else {
418		tst_resm(TFAIL, "number of sleeping processes is not correct");
419	}
420}
421
422/*
423 * pid_setup() - set up for the GETPID command with semctl()
424 */
425void pid_setup()
426{
427	int pid;
428
429	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) {
430		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
431	}
432
433	/*
434	 * Fork a child to do a semop that will pass.
435	 */
436	if ((pid = FORK_OR_VFORK()) == -1) {
437		tst_brkm(TBROK, cleanup, "fork failed in pid_setup()");
438	}
439
440	if (pid == 0) {		/* child */
441#ifdef UCLINUX
442		if (self_exec(argv0, "nd", 1, sem_id_1) < 0) {
443			tst_brkm(TBROK, cleanup, "self_exec failed "
444				 "in pid_setup()");
445		}
446#else
447		child_pid();
448#endif
449	} else {		/* parent */
450		if (sync_pipe_wait(sync_pipes) == -1)
451			tst_brkm(TBROK, cleanup, "sync_pipe_wait failed");
452
453		if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
454			tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
455		sleep(1);
456		pid_arr[SEM2] = pid;
457	}
458}
459
460void child_pid()
461{
462#ifdef UCLINUX
463	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
464		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
465#endif
466
467	if (sync_pipe_notify(sync_pipes) == -1)
468		tst_brkm(TBROK, cleanup, "sync_pipe_notify failed");
469
470	if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
471		tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
472
473	sops.sem_num = SEM2;	/* semaphore to change */
474	sops.sem_op = ONE;	/* operation is to increment semaphore */
475	sops.sem_flg = 0;
476
477	/*
478	 * Do a semop that will increment the semaphore.
479	 */
480	if (semop(sem_id_1, &sops, 1) == -1) {
481		tst_resm(TBROK, "semop failed - pid_setup");
482	}
483	exit(0);
484}
485
486/*
487 * func_pid() - check the functionality of the GETPID command with semctl()
488 */
489void func_pid(int rval)
490{
491	/* compare the rval (pid) to the saved pid from the setup */
492	if (rval == pid_arr[SEM2]) {
493		tst_resm(TPASS, "last pid value is correct");
494	} else {
495		tst_resm(TFAIL, "last pid value is not correct");
496	}
497}
498
499/*
500 * func_gval() - check the functionality of the GETVAL command with semctl()
501 */
502void func_gval(int rval)
503{
504	/*
505	 * This is a simple test.  The semaphore value should be equal
506	 * to ONE as it was set in the last test (GETPID).
507	 */
508	if (rval == 1) {
509		tst_resm(TPASS, "semaphore value is correct");
510	} else {
511		tst_resm(TFAIL, "semaphore value is not correct");
512	}
513
514}
515
516/*
517 * all_setup() - set up for the SETALL command with semctl()
518 */
519void sall_setup()
520{
521	int i;
522
523	for (i = 0; i < PSEMS; i++) {
524		/* initialize the array values to 3 */
525		array[i] = 3;
526	}
527}
528
529/*
530 * func_sall() - check the functionality of the SETALL command with semctl()
531 */
532void func_sall()
533{
534	int i;
535	unsigned short rarray[PSEMS];
536
537	/*
538	 * do a GETALL and compare the values to those set above
539	 */
540
541	if (semctl(sem_id_1, 0, GETALL, (union semun)rarray) == -1) {
542		tst_brkm(TBROK, cleanup, "semctl failed in func_sall");
543	}
544
545	for (i = 0; i < PSEMS; i++) {
546		if (array[i] != rarray[i]) {
547			tst_resm(TFAIL, "semaphore values are not correct");
548			return;
549		}
550	}
551
552	tst_resm(TPASS, "semaphore values are correct");
553}
554
555/*
556 * func_sval() - check the functionality of the SETVAL command with semctl()
557 */
558void func_sval()
559{
560	int semv;
561	union semun arr;
562
563	/*
564	 * do a GETVAL and compare it to the value set above
565	 */
566
567	if ((semv = semctl(sem_id_1, SEM4, GETVAL, arr)) == -1) {
568		tst_brkm(TBROK, cleanup, "semctl failed in func_sval");
569	}
570
571	if (semv != INCVAL) {
572		tst_resm(TFAIL, "semaphore value is not what was set");
573	} else {
574		tst_resm(TPASS, "semaphore value is correct");
575	}
576}
577
578/*
579 * func_rmid() - check the functionality of the IPC_RMID command with semctl()
580 */
581void func_rmid()
582{
583
584	/*
585	 * do a semop() - we should get EINVAL
586	 */
587	if (semop(sem_id_1, &sops, 1) != -1) {
588		tst_resm(TFAIL, "semop succeeded on expected fail");
589	}
590
591	if (errno != EINVAL) {
592		tst_resm(TFAIL, "returned errno - %d - is not expected", errno);
593	} else {
594		tst_resm(TPASS, "semaphore appears to be removed");
595	}
596
597	sem_id_1 = -1;
598}
599
600/*
601 * setup() - performs all the ONE TIME setup for this test.
602 */
603void setup(void)
604{
605	/* capture signals */
606	tst_sig(FORK, DEF_HANDLER, cleanup);
607
608	/* Pause if that option was specified */
609	TEST_PAUSE;
610
611	/*
612	 * Create a temporary directory and cd into it.
613	 * This helps to ensure that a unique msgkey is created.
614	 * See ../lib/libipc.c for more information.
615	 */
616	tst_tmpdir();
617
618	/* get an IPC resource key */
619	semkey = getipckey();
620
621	/* create a semaphore set with read and alter permissions */
622	if ((sem_id_1 =
623	     semget(semkey, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA)) == -1) {
624		tst_brkm(TBROK, cleanup, "couldn't create semaphore in setup");
625	}
626}
627
628/*
629 * cleanup() - performs all the ONE TIME cleanup for this test at completion
630 * 	       or premature exit.
631 */
632void cleanup(void)
633{
634	/* if it exists, remove the semaphore resource */
635	rm_sema(sem_id_1);
636
637	/* Remove the temporary directory */
638	tst_rmdir();
639
640	/*
641	 * print timing stats if that option was specified.
642	 * print errno log if that option was specified.
643	 */
644	TEST_CLEANUP;
645
646	/* exit with return code appropriate for results */
647	tst_exit();
648}
649