semctl01.c revision 56207cec7732e09c216c751c0b5f88a242bacae6
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, (option_t *) NULL, NULL)) != (char *)NULL) {
150		tst_brkm(TBROK, cleanup, "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	if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
387		tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
388
389	sops.sem_num = SEM4;
390	sops.sem_flg = 0;
391
392	/*
393	 * Do a semop that will cause the child to sleep.
394	 * The child process will be killed in the func_ncnt
395	 * routine which should cause an error to be return
396	 * by the semop() call.
397	 */
398	if (semop(sem_id_1, &sops, 1) != -1) {
399		tst_resm(TBROK, "semop succeeded - cnt_setup");
400	}
401	exit(0);
402}
403
404/*
405 * func_cnt() - check the functionality of the GETNCNT and GETZCNT commands
406 *	        with semctl()
407 */
408void func_cnt(int rval)
409{
410
411	if (rval == NCHILD) {
412		tst_resm(TPASS, "number of sleeping processes is correct");
413	} else {
414		tst_resm(TFAIL, "number of sleeping processes is not correct");
415	}
416}
417
418/*
419 * pid_setup() - set up for the GETPID command with semctl()
420 */
421void pid_setup()
422{
423	int pid;
424
425	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) {
426		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
427	}
428
429	/*
430	 * Fork a child to do a semop that will pass.
431	 */
432	if ((pid = FORK_OR_VFORK()) == -1) {
433		tst_brkm(TBROK, cleanup, "fork failed in pid_setup()");
434	}
435
436	if (pid == 0) {		/* child */
437#ifdef UCLINUX
438		if (self_exec(argv0, "nd", 1, sem_id_1) < 0) {
439			tst_brkm(TBROK, cleanup, "self_exec failed "
440				 "in pid_setup()");
441		}
442#else
443		child_pid();
444#endif
445	} else {		/* parent */
446		if (sync_pipe_wait(sync_pipes) == -1)
447			tst_brkm(TBROK, cleanup, "sync_pipe_wait failed");
448
449		if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
450			tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
451		sleep(1);
452		pid_arr[SEM2] = pid;
453	}
454}
455
456void child_pid()
457{
458#ifdef UCLINUX
459	if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1)
460		tst_brkm(TBROK, cleanup, "sync_pipe_create failed");
461#endif
462
463	if (sync_pipe_notify(sync_pipes) == -1)
464		tst_brkm(TBROK, cleanup, "sync_pipe_notify failed");
465
466	if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1)
467		tst_brkm(TBROK, cleanup, "sync_pipe_close failed");
468
469	sops.sem_num = SEM2;	/* semaphore to change */
470	sops.sem_op = ONE;	/* operation is to increment semaphore */
471	sops.sem_flg = 0;
472
473	/*
474	 * Do a semop that will increment the semaphore.
475	 */
476	if (semop(sem_id_1, &sops, 1) == -1) {
477		tst_resm(TBROK, "semop failed - pid_setup");
478	}
479	exit(0);
480}
481
482/*
483 * func_pid() - check the functionality of the GETPID command with semctl()
484 */
485void func_pid(int rval)
486{
487	/* compare the rval (pid) to the saved pid from the setup */
488	if (rval == pid_arr[SEM2]) {
489		tst_resm(TPASS, "last pid value is correct");
490	} else {
491		tst_resm(TFAIL, "last pid value is not correct");
492	}
493}
494
495/*
496 * func_gval() - check the functionality of the GETVAL command with semctl()
497 */
498void func_gval(int rval)
499{
500	/*
501	 * This is a simple test.  The semaphore value should be equal
502	 * to ONE as it was set in the last test (GETPID).
503	 */
504	if (rval == 1) {
505		tst_resm(TPASS, "semaphore value is correct");
506	} else {
507		tst_resm(TFAIL, "semaphore value is not correct");
508	}
509
510}
511
512/*
513 * all_setup() - set up for the SETALL command with semctl()
514 */
515void sall_setup()
516{
517	int i;
518
519	for (i = 0; i < PSEMS; i++) {
520		/* initialize the array values to 3 */
521		array[i] = 3;
522	}
523}
524
525/*
526 * func_sall() - check the functionality of the SETALL command with semctl()
527 */
528void func_sall()
529{
530	int i;
531	unsigned short rarray[PSEMS];
532
533	/*
534	 * do a GETALL and compare the values to those set above
535	 */
536
537	if (semctl(sem_id_1, 0, GETALL, (union semun)rarray) == -1) {
538		tst_brkm(TBROK, cleanup, "semctl failed in func_sall");
539	}
540
541	for (i = 0; i < PSEMS; i++) {
542		if (array[i] != rarray[i]) {
543			tst_resm(TFAIL, "semaphore values are not correct");
544			return;
545		}
546	}
547
548	tst_resm(TPASS, "semaphore values are correct");
549}
550
551/*
552 * func_sval() - check the functionality of the SETVAL command with semctl()
553 */
554void func_sval()
555{
556	int semv;
557	union semun arr;
558
559	/*
560	 * do a GETVAL and compare it to the value set above
561	 */
562
563	if ((semv = semctl(sem_id_1, SEM4, GETVAL, arr)) == -1) {
564		tst_brkm(TBROK, cleanup, "semctl failed in func_sval");
565	}
566
567	if (semv != INCVAL) {
568		tst_resm(TFAIL, "semaphore value is not what was set");
569	} else {
570		tst_resm(TPASS, "semaphore value is correct");
571	}
572}
573
574/*
575 * func_rmid() - check the functionality of the IPC_RMID command with semctl()
576 */
577void func_rmid()
578{
579
580	/*
581	 * do a semop() - we should get EINVAL
582	 */
583	if (semop(sem_id_1, &sops, 1) != -1) {
584		tst_resm(TFAIL, "semop succeeded on expected fail");
585	}
586
587	if (errno != EINVAL) {
588		tst_resm(TFAIL, "returned errno - %d - is not expected", errno);
589	} else {
590		tst_resm(TPASS, "semaphore appears to be removed");
591	}
592
593	sem_id_1 = -1;
594}
595
596/*
597 * setup() - performs all the ONE TIME setup for this test.
598 */
599void setup(void)
600{
601	/* capture signals */
602	tst_sig(FORK, DEF_HANDLER, cleanup);
603
604	/* Pause if that option was specified */
605	TEST_PAUSE;
606
607	/*
608	 * Create a temporary directory and cd into it.
609	 * This helps to ensure that a unique msgkey is created.
610	 * See ../lib/libipc.c for more information.
611	 */
612	tst_tmpdir();
613
614	/* get an IPC resource key */
615	semkey = getipckey();
616
617	/* create a semaphore set with read and alter permissions */
618	if ((sem_id_1 =
619	     semget(semkey, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA)) == -1) {
620		tst_brkm(TBROK, cleanup, "couldn't create semaphore in setup");
621	}
622}
623
624/*
625 * cleanup() - performs all the ONE TIME cleanup for this test at completion
626 * 	       or premature exit.
627 */
628void cleanup(void)
629{
630	/* if it exists, remove the semaphore resource */
631	rm_sema(sem_id_1);
632
633	/* Remove the temporary directory */
634	tst_rmdir();
635
636	/*
637	 * print timing stats if that option was specified.
638	 * print errno log if that option was specified.
639	 */
640	TEST_CLEANUP;
641
642	/* exit with return code appropriate for results */
643	tst_exit();
644}
645