semctl06.c revision 4bb656a129f7507823e9e6d6b98b1a02fd80ef89
1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2002
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 *	semctl06
23 *
24 * CALLS
25 *	semctl(2) semget(2) semop(2)
26 *
27 * ALGORITHM
28 *	Get and manipulate a set of semaphores.
29 *
30 * RESTRICTIONS
31 *
32 * WARNING
33 *	If this test fail, it may be necessary to use the ipcs and ipcrm
34 *	commands to remove any semaphores left in the system due to a
35 *	premature exit of this test.
36 *
37 * HISTORY
38 *      06/30/2001	Port to Linux	nsharoff@us.ibm.com
39 *      10/30/2002	Port to LTP	dbarrera@us.ibm.com
40 *      12/03/2008 Matthieu Fertré (Matthieu.Fertre@irisa.fr)
41 *      - Fix concurrency issue. The IPC keys used for this test could
42 *        conflict with keys from another task.
43 */
44
45#define DEBUG 0
46
47#ifdef UCLINUX
48#define _GNU_SOURCE /* for asprintf */
49#include <stdio.h>
50#endif
51
52#include <sys/types.h>		/* needed for test		*/
53#include <sys/ipc.h>		/* needed for test		*/
54#include <sys/sem.h>		/* needed for test		*/
55#include <unistd.h>
56#include <errno.h>
57#include <stdlib.h>
58#include <signal.h>
59#include "test.h"
60#include "usctest.h"
61#include <wait.h>
62#include "ipcsem.h"
63
64int local_flag=1;
65
66
67#define NREPS	500
68#define NPROCS	3
69#define NKIDS	5
70#define NSEMS	5
71#define HVAL	1000
72#define LVAL	100
73#define FAILED	0
74
75void setup ();
76void cleanup();
77
78static key_t		keyarray[NPROCS];
79static struct sembuf	semops[NSEMS];
80static short		maxsemvals[NSEMS];
81static int		pidarray[NPROCS];
82static int		kidarray[NKIDS];
83static int		tid;
84static int		procstat;
85static char	       *prog;
86static unsigned short		semvals[NSEMS];
87
88/*
89 *  * These globals must be defined in the test.
90 *   * */
91
92
93char *TCID="semctl06";           /* Test program identifier.    */
94int TST_TOTAL=1;                /* Total number of test cases. */
95extern int Tst_count;           /* Test Case counter for tst_* routines */
96
97int exp_enos[]={0};     /* List must end with 0 */
98
99
100static void term(int sig);
101static void dosemas(int id);
102static void dotest(key_t key);
103
104#ifdef UCLINUX
105static char *argv0;
106
107void do_child();
108static int id_uclinux;
109static char *maxsemstring;
110#endif
111
112/*--------------------------------------------------------------*/
113/*ARGSUSED*/
114int
115main(int argc, char **argv)
116{
117	register int i, pid;
118	int count, child, status, nwait;
119
120#ifdef UCLINUX
121	char *msg;
122	if ((msg = parse_opts(argc, argv, (option_t *)NULL, NULL)) != (char *)NULL){
123		tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
124	}
125
126	argv0 = argv[0];
127	maybe_run_child(&do_child, "dS", &id_uclinux, &maxsemstring);
128#endif
129
130	prog = argv[0];
131	nwait = 0;
132	setup();
133/*--------------------------------------------------------------*/
134	tid = -1;
135
136	for (i = 0; i < NPROCS; i++)
137		keyarray[i] = getipckey();
138
139	if ((signal(SIGTERM, term)) == SIG_ERR) {
140                tst_resm(TFAIL, "\tsignal failed. errno = %d", errno);
141		tst_exit();
142	}
143
144	for (i = 0; i <  NPROCS; i++) {
145		if ((pid = FORK_OR_VFORK()) < 0) {
146                        tst_resm(TFAIL, "\tFork failed (may be OK if under stress)");
147                        tst_exit();
148		}
149		if (pid == 0) {
150			procstat = 1;
151			dotest(keyarray[i]);
152			exit(0);
153		}
154		pidarray[i] = pid;
155		nwait++;
156	}
157
158	/*
159	 * Wait for children to finish.
160	 */
161
162	count = 0;
163	while((child = wait(&status)) > 0) {
164		if (status) {
165	                tst_resm(TFAIL, "%s[%d] Test failed.  exit=0x%x", prog, child, status);
166			local_flag = FAILED;
167		}
168		++count;
169	}
170
171	/*
172	 * Should have collected all children.
173	 */
174
175	if (count != nwait) {
176                tst_resm(TFAIL, "\tWrong # children waited on, count = %d", count);
177		local_flag = FAILED;
178	}
179
180	if (local_flag != FAILED)
181		tst_resm(TPASS, "semctl06 ran successfully!");
182	else tst_resm(TFAIL, "semctl06 failed");
183
184/*--------------------------------------------------------------*/
185/* Clean up any files created by test before call to anyfail.	*/
186
187	cleanup ();
188
189	return (0); /* shut lint up */
190}
191/*--------------------------------------------------------------*/
192
193
194static void
195dotest(key_t key)
196{
197	int id, pid, status;
198	int count, child, nwait;
199	short i;
200		 union semun get_arr;
201
202	nwait = 0;
203	srand(getpid());
204	if ((id = semget(key, NSEMS, IPC_CREAT|IPC_EXCL)) < 0) {
205		tst_resm(TFAIL, "\tsemget() failed errno %d", errno);
206		exit(1);
207	}
208	tid = id;
209	for (i = 0; i < NSEMS; i++) {
210		do {
211			maxsemvals[i] = /*CASTOK*/(short)(rand() % HVAL);
212		} while (maxsemvals[i] < LVAL);
213		semops[i].sem_num = i;
214		semops[i].sem_op = maxsemvals[i];
215		semops[i].sem_flg = SEM_UNDO;
216	}
217	if (semop(id, semops, NSEMS) < 0) {
218		tst_resm(TFAIL, "\tfirst semop() failed errno %d", errno);
219		exit(1);
220	}
221
222	for (i = 0; i < NKIDS; i++) {
223		if ((pid = FORK_OR_VFORK()) < 0) {
224			tst_resm(TFAIL, "\tfork failed");
225		}
226		if (pid == 0) {
227#ifdef UCLINUX
228			int j;
229			maxsemstring = "";
230			for (j = 0; j < NSEMS; j++) {
231				if (asprintf(&maxsemstring, "%s%s%d",
232					     maxsemstring, (j ? ":" : ""),
233					     maxsemvals[j]) < 0) {
234					tst_resm(TBROK, "Could not serialize "
235						 "maxsemvals");
236					tst_exit();
237				}
238			}
239			if (self_exec(argv0, "dS", id, maxsemstring) < 0) {
240				tst_resm(TFAIL, "\tself_exec failed");
241			}
242#else
243			dosemas(id);
244#endif
245		}
246		if (pid > 0) {
247			kidarray[i] = pid;
248			nwait++;
249		}
250	}
251
252
253	procstat = 2;
254	/*
255	 * Wait for children to finish.
256	 */
257
258	count = 0;
259	while((child = wait(&status)) > 0) {
260		if (status) {
261	                tst_resm(TFAIL, "\t%s:dotest[%d] exited status = 0x%x", prog, child, status);
262			local_flag = FAILED;
263		}
264		++count;
265	}
266
267	/*
268	 * Should have collected all children.
269	 */
270
271	if (count != nwait) {
272                tst_resm(TFAIL, "\tWrong # children waited on, count = %d", count);
273		local_flag = FAILED;
274	}
275
276		 get_arr.array = semvals;
277		 if (semctl(id, 0, GETALL, get_arr) < 0) {
278                tst_resm(TFAIL, "\terror on GETALL");
279		tst_resm(TFAIL, "\tsemctl() failed errno %d", errno);
280	}
281
282	if (DEBUG)
283		tst_resm(TINFO, "\tchecking maxvals");
284	for (i = 0; i < NSEMS; i++) {
285		if (semvals[i] !=  maxsemvals[i]) {
286			tst_resm(TFAIL, "\terror on i %d orig %d final %d", i, semvals[i],
287					maxsemvals[i]);
288			local_flag = FAILED;
289		}
290	}
291	if (DEBUG)
292		tst_resm(TINFO, "\tmaxvals checked");
293
294	/* 4th arg must either be missing, or must be of type 'union semun'.
295	 * CANNOT just be an int, else it crashes on ppc.
296	 */
297	get_arr.val = 0;
298	if (semctl(id, 0, IPC_RMID, get_arr) < 0) {
299		tst_resm(TFAIL, "\tsemctl(IPC_RMID) failed errno %d", errno);
300		local_flag = FAILED;
301	}
302	if (local_flag == FAILED)
303		exit(1);
304}
305
306#ifdef UCLINUX
307void
308do_child()
309{
310	int i;
311	char *tok;
312	char *endptr;
313
314	tok = strtok(maxsemstring, ":");
315	for (i = 0; i < NSEMS; i++) {
316		if (strlen(tok) == 0) {
317			tst_resm(TBROK, "Invalid argument to -C option");
318			tst_exit();
319		}
320
321		maxsemvals[i] = strtol(tok, &endptr, 10);
322		if (*endptr != '\0') {
323			tst_resm(TBROK, "Invalid argument to -C option");
324			tst_exit();
325                }
326		tok = strtok(NULL, ":");
327	}
328
329	dosemas(id_uclinux);
330}
331#endif
332
333static void
334dosemas(int id)
335{
336	int i, j;
337
338	srand(getpid());
339	for (i = 0; i < NREPS; i++) {
340		for (j = 0; j < NSEMS; j++) {
341			semops[j].sem_num = j;
342			semops[j].sem_flg = SEM_UNDO;
343
344			do {
345				semops[j].sem_op =
346					( - /*CASTOK*/(short)(rand() % (maxsemvals[j]/2)));
347			} while (semops[j].sem_op == 0);
348		}
349		if (semop(id, semops, NSEMS) < 0) {
350			tst_resm(TFAIL, "\tsemop1 failed errno %d", errno);
351			exit(1);
352		}
353		for (j = 0; j < NSEMS; j++) {
354			semops[j].sem_op = ( - semops[j].sem_op);
355		}
356		if (semop(id, semops, NSEMS) < 0) {
357			tst_resm(TFAIL, "\tsemop2 failed errno %d", errno);
358			exit(1);
359		}
360	}
361	exit(0);
362}
363
364
365/*ARGSUSED*/
366static void
367term(int sig)
368{
369	int i;
370
371	if ((signal(SIGTERM, term)) == SIG_ERR) {
372		tst_resm(TFAIL, "\tsignal failed. errno %d", errno);
373		exit(1);
374	}
375	if (procstat == 0) {
376		if (DEBUG)
377			tst_resm(TINFO, "\ttest killing kids");
378		for (i = 0; i < NPROCS; i++) {
379			if (kill(pidarray[i], SIGTERM) != 0) {
380				tst_resm(TFAIL, "Kill error pid = %d :",pidarray[1]);
381			}
382		}
383		if (DEBUG)
384			tst_resm(TINFO, "\ttest kids killed");
385		return;
386	}
387
388	if (procstat == 1) {
389		/* 4th arg must either be missing, or must be of type 'union semun'.
390		 * CANNOT just be an int, else it crashes on ppc.
391		 */
392		union semun arg;
393		arg.val = 0;
394		(void)semctl(tid, 0, IPC_RMID, arg);
395		exit(1);
396	}
397
398	if (tid == -1) {
399		exit(1);
400	}
401	for (i = 0; i < NKIDS; i++) {
402		if (kill(kidarray[i], SIGTERM) != 0) {
403			tst_resm(TFAIL, "Kill error kid id = %d :",kidarray[1]);
404		}
405	}
406}
407
408/***************************************************************
409 * setup() - performs all ONE TIME setup for this test.
410 *****************************************************************/
411void
412setup()
413{
414        /* You will want to enable some signal handling so you can capture
415	 * unexpected signals like SIGSEGV.
416	 *                   */
417        tst_sig(FORK, DEF_HANDLER, cleanup);
418
419
420        /* Pause if that option was specified */
421        /* One cavet that hasn't been fixed yet.  TEST_PAUSE contains the code to
422	 * fork the test with the -c option.  You want to make sure you do this
423	 * before you create your temporary directory.
424	 */
425        TEST_PAUSE;
426
427	/*
428	 * Create a temporary directory and cd into it.
429	 * This helps to ensure that a unique msgkey is created.
430	 * See ../lib/libipc.c for more information.
431	 */
432	tst_tmpdir();
433}
434
435
436/***************************************************************
437 * cleanup() - performs all ONE TIME cleanup for this test at
438 * completion or premature exit.
439 ****************************************************************/
440void
441cleanup()
442{
443	/* Remove the temporary directory */
444	tst_rmdir();
445
446        /*
447	 * print timing stats if that option was specified.
448	 * print errno log if that option was specified.
449	 */
450        TEST_CLEANUP;
451
452        /* exit with return code appropriate for results */
453        tst_exit();
454}
455
456