semctl06.c revision 526fdf8d8ea3b43b73de7cc25eb754f12702c8d2
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
49#include <stdio.h>
50#endif
51
52#include <sys/types.h>
53#include <sys/ipc.h>
54#include <sys/sem.h>
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#define NREPS	500
67#define NPROCS	3
68#define NKIDS	5
69#define NSEMS	5
70#define HVAL	1000
71#define LVAL	100
72#define FAILED	0
73
74void setup();
75void cleanup();
76
77static key_t keyarray[NPROCS];
78static struct sembuf semops[NSEMS];
79static short maxsemvals[NSEMS];
80static int pidarray[NPROCS];
81static int kidarray[NKIDS];
82static int tid;
83static int procstat;
84static char *prog;
85static unsigned short semvals[NSEMS];
86
87char *TCID = "semctl06";
88int TST_TOTAL = 1;
89
90static void term(int sig);
91static void dosemas(int id);
92static void dotest(key_t key);
93
94#ifdef UCLINUX
95static char *argv0;
96
97void do_child();
98static int id_uclinux;
99static char *maxsemstring;
100#endif
101
102int main(int argc, char **argv)
103{
104	register int i, pid;
105	int count, child, status, nwait;
106
107#ifdef UCLINUX
108	const char *msg;
109	if ((msg = parse_opts(argc, argv, NULL, NULL)) != NULL)
110		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
111
112	argv0 = argv[0];
113	maybe_run_child(&do_child, "dS", &id_uclinux, &maxsemstring);
114#endif
115
116	prog = argv[0];
117	nwait = 0;
118	setup();
119
120	tid = -1;
121
122	for (i = 0; i < NPROCS; i++)
123		keyarray[i] = getipckey();
124
125	if ((signal(SIGTERM, term)) == SIG_ERR) {
126		tst_resm(TFAIL, "\tsignal failed. errno = %d", errno);
127
128	}
129
130	for (i = 0; i < NPROCS; i++) {
131		if ((pid = FORK_OR_VFORK()) < 0) {
132			tst_resm(TFAIL,
133				 "\tFork failed (may be OK if under stress)");
134
135		}
136		if (pid == 0) {
137			procstat = 1;
138			dotest(keyarray[i]);
139			exit(0);
140		}
141		pidarray[i] = pid;
142		nwait++;
143	}
144
145	/*
146	 * Wait for children to finish.
147	 */
148
149	count = 0;
150	while ((child = wait(&status)) > 0) {
151		if (status) {
152			tst_resm(TFAIL, "%s[%d] Test failed.  exit=0x%x", prog,
153				 child, status);
154			local_flag = FAILED;
155		}
156		++count;
157	}
158
159	/*
160	 * Should have collected all children.
161	 */
162
163	if (count != nwait) {
164		tst_resm(TFAIL, "\tWrong # children waited on, count = %d",
165			 count);
166		local_flag = FAILED;
167	}
168
169	if (local_flag != FAILED)
170		tst_resm(TPASS, "semctl06 ran successfully!");
171	else
172		tst_resm(TFAIL, "semctl06 failed");
173
174
175	cleanup();
176	tst_exit();
177}
178
179static void dotest(key_t key)
180{
181	int id, pid, status;
182	int count, child, nwait;
183	short i;
184	union semun get_arr;
185
186	nwait = 0;
187	srand(getpid());
188	if ((id = semget(key, NSEMS, IPC_CREAT | IPC_EXCL)) < 0) {
189		tst_resm(TFAIL, "\tsemget() failed errno %d", errno);
190		exit(1);
191	}
192	tid = id;
193	for (i = 0; i < NSEMS; i++) {
194		do {
195			maxsemvals[i] = (short) (rand() % HVAL);
196		} while (maxsemvals[i] < LVAL);
197		semops[i].sem_num = i;
198		semops[i].sem_op = maxsemvals[i];
199		semops[i].sem_flg = SEM_UNDO;
200	}
201	if (semop(id, semops, NSEMS) < 0) {
202		tst_resm(TFAIL, "\tfirst semop() failed errno %d", errno);
203		exit(1);
204	}
205
206	for (i = 0; i < NKIDS; i++) {
207		if ((pid = FORK_OR_VFORK()) < 0) {
208			tst_resm(TFAIL, "\tfork failed");
209		}
210		if (pid == 0) {
211#ifdef UCLINUX
212			int j;
213			maxsemstring = "";
214			for (j = 0; j < NSEMS; j++) {
215				if (asprintf(&maxsemstring, "%s%s%d",
216					     maxsemstring, (j ? ":" : ""),
217					     maxsemvals[j]) < 0) {
218					tst_brkm(TBROK, NULL, "Could not serialize "
219						 "maxsemvals");
220				}
221			}
222			if (self_exec(argv0, "dS", id, maxsemstring) < 0) {
223				tst_resm(TFAIL, "\tself_exec failed");
224			}
225#else
226			dosemas(id);
227#endif
228		}
229		if (pid > 0) {
230			kidarray[i] = pid;
231			nwait++;
232		}
233	}
234
235	procstat = 2;
236	/*
237	 * Wait for children to finish.
238	 */
239
240	count = 0;
241	while ((child = wait(&status)) > 0) {
242		if (status) {
243			tst_resm(TFAIL, "\t%s:dotest[%d] exited status = 0x%x",
244				 prog, child, status);
245			local_flag = FAILED;
246		}
247		++count;
248	}
249
250	/*
251	 * Should have collected all children.
252	 */
253
254	if (count != nwait) {
255		tst_resm(TFAIL, "\tWrong # children waited on, count = %d",
256			 count);
257		local_flag = FAILED;
258	}
259
260	get_arr.array = semvals;
261	if (semctl(id, 0, GETALL, get_arr) < 0) {
262		tst_resm(TFAIL, "\terror on GETALL");
263		tst_resm(TFAIL, "\tsemctl() failed errno %d", errno);
264	}
265
266	if (DEBUG)
267		tst_resm(TINFO, "\tchecking maxvals");
268	for (i = 0; i < NSEMS; i++) {
269		if (semvals[i] != maxsemvals[i]) {
270			tst_resm(TFAIL, "\terror on i %d orig %d final %d", i,
271				 semvals[i], maxsemvals[i]);
272			local_flag = FAILED;
273		}
274	}
275	if (DEBUG)
276		tst_resm(TINFO, "\tmaxvals checked");
277
278	/* 4th arg must either be missing, or must be of type 'union semun'.
279	 * CANNOT just be an int, else it crashes on ppc.
280	 */
281	get_arr.val = 0;
282	if (semctl(id, 0, IPC_RMID, get_arr) < 0) {
283		tst_resm(TFAIL, "\tsemctl(IPC_RMID) failed errno %d", errno);
284		local_flag = FAILED;
285	}
286	if (local_flag == FAILED)
287		exit(1);
288}
289
290#ifdef UCLINUX
291void do_child(void)
292{
293	int i;
294	char *tok;
295	char *endptr;
296
297	tok = strtok(maxsemstring, ":");
298	for (i = 0; i < NSEMS; i++) {
299		if (strlen(tok) == 0) {
300			tst_brkm(TBROK, NULL, "Invalid argument to -C option");
301		}
302
303		maxsemvals[i] = strtol(tok, &endptr, 10);
304		if (*endptr != '\0') {
305			tst_brkm(TBROK, NULL, "Invalid argument to -C option");
306		}
307		tok = strtok(NULL, ":");
308	}
309
310	dosemas(id_uclinux);
311}
312#endif
313
314static void dosemas(int id)
315{
316	int i, j;
317
318	srand(getpid());
319	for (i = 0; i < NREPS; i++) {
320		for (j = 0; j < NSEMS; j++) {
321			semops[j].sem_num = j;
322			semops[j].sem_flg = SEM_UNDO;
323
324			do {
325				semops[j].sem_op =
326				    (-(short) (rand() %
327							(maxsemvals[j] / 2)));
328			} while (semops[j].sem_op == 0);
329		}
330		if (semop(id, semops, NSEMS) < 0) {
331			tst_resm(TFAIL, "\tsemop1 failed errno %d", errno);
332			exit(1);
333		}
334		for (j = 0; j < NSEMS; j++) {
335			semops[j].sem_op = (-semops[j].sem_op);
336		}
337		if (semop(id, semops, NSEMS) < 0) {
338			tst_resm(TFAIL, "\tsemop2 failed errno %d", errno);
339			exit(1);
340		}
341	}
342	exit(0);
343}
344
345static void term(int sig)
346{
347	int i;
348
349	if ((signal(SIGTERM, term)) == SIG_ERR) {
350		tst_resm(TFAIL, "\tsignal failed. errno %d", errno);
351		exit(1);
352	}
353	if (procstat == 0) {
354		if (DEBUG)
355			tst_resm(TINFO, "\ttest killing kids");
356		for (i = 0; i < NPROCS; i++) {
357			if (kill(pidarray[i], SIGTERM) != 0) {
358				tst_resm(TFAIL, "Kill error pid = %d :",
359					 pidarray[1]);
360			}
361		}
362		if (DEBUG)
363			tst_resm(TINFO, "\ttest kids killed");
364		return;
365	}
366
367	if (procstat == 1) {
368		/* 4th arg must either be missing, or must be of type 'union semun'.
369		 * CANNOT just be an int, else it crashes on ppc.
370		 */
371		union semun arg;
372		arg.val = 0;
373		(void)semctl(tid, 0, IPC_RMID, arg);
374		exit(1);
375	}
376
377	if (tid == -1) {
378		exit(1);
379	}
380	for (i = 0; i < NKIDS; i++) {
381		if (kill(kidarray[i], SIGTERM) != 0) {
382			tst_resm(TFAIL, "Kill error kid id = %d :",
383				 kidarray[1]);
384		}
385	}
386}
387
388void setup(void)
389{
390	tst_sig(FORK, DEF_HANDLER, cleanup);
391
392	TEST_PAUSE;
393
394	tst_tmpdir();
395}
396
397void cleanup(void)
398{
399	tst_rmdir();
400	TEST_CLEANUP;
401}
402