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 <sys/wait.h>
61#include "ipcsem.h"
62
63int local_flag = 1;
64
65#define NREPS	500
66#define NPROCS	3
67#define NKIDS	5
68#define NSEMS	5
69#define HVAL	1000
70#define LVAL	100
71#define FAILED	0
72
73void setup();
74void cleanup();
75
76static key_t keyarray[NPROCS];
77static struct sembuf semops[NSEMS];
78static short maxsemvals[NSEMS];
79static int pidarray[NPROCS];
80static int kidarray[NKIDS];
81static int tid;
82static int procstat;
83static char *prog;
84static unsigned short semvals[NSEMS];
85
86char *TCID = "semctl06";
87int TST_TOTAL = 1;
88
89static void term(int sig);
90static void dosemas(int id);
91static void dotest(key_t key);
92
93int main(int argc, char **argv)
94{
95	register int i, pid;
96	int count, child, status, nwait;
97
98	tst_parse_opts(argc, argv, NULL, NULL);
99
100	prog = argv[0];
101	nwait = 0;
102	setup();
103
104	tid = -1;
105
106	for (i = 0; i < NPROCS; i++)
107		keyarray[i] = getipckey();
108
109	if ((signal(SIGTERM, term)) == SIG_ERR) {
110		tst_resm(TFAIL, "\tsignal failed. errno = %d", errno);
111
112	}
113
114	for (i = 0; i < NPROCS; i++) {
115		if ((pid = FORK_OR_VFORK()) < 0) {
116			tst_resm(TFAIL,
117				 "\tFork failed (may be OK if under stress)");
118
119		}
120		if (pid == 0) {
121			procstat = 1;
122			dotest(keyarray[i]);
123			exit(0);
124		}
125		pidarray[i] = pid;
126		nwait++;
127	}
128
129	/*
130	 * Wait for children to finish.
131	 */
132
133	count = 0;
134	while ((child = wait(&status)) > 0) {
135		if (status) {
136			tst_resm(TFAIL, "%s[%d] Test failed.  exit=0x%x", prog,
137				 child, status);
138			local_flag = FAILED;
139		}
140		++count;
141	}
142
143	/*
144	 * Should have collected all children.
145	 */
146
147	if (count != nwait) {
148		tst_resm(TFAIL, "\tWrong # children waited on, count = %d",
149			 count);
150		local_flag = FAILED;
151	}
152
153	if (local_flag != FAILED)
154		tst_resm(TPASS, "semctl06 ran successfully!");
155	else
156		tst_resm(TFAIL, "semctl06 failed");
157
158
159	cleanup();
160	tst_exit();
161}
162
163static void dotest(key_t key)
164{
165	int id, pid, status;
166	int count, child, nwait;
167	short i;
168	union semun get_arr;
169
170	nwait = 0;
171	srand(getpid());
172	if ((id = semget(key, NSEMS, IPC_CREAT | IPC_EXCL)) < 0) {
173		tst_resm(TFAIL, "\tsemget() failed errno %d", errno);
174		exit(1);
175	}
176	tid = id;
177	for (i = 0; i < NSEMS; i++) {
178		do {
179			maxsemvals[i] = (short) (rand() % HVAL);
180		} while (maxsemvals[i] < LVAL);
181		semops[i].sem_num = i;
182		semops[i].sem_op = maxsemvals[i];
183		semops[i].sem_flg = SEM_UNDO;
184	}
185	if (semop(id, semops, NSEMS) < 0) {
186		tst_resm(TFAIL, "\tfirst semop() failed errno %d", errno);
187		exit(1);
188	}
189
190	for (i = 0; i < NKIDS; i++) {
191		if ((pid = FORK_OR_VFORK()) < 0) {
192			tst_resm(TFAIL, "\tfork failed");
193		}
194		if (pid == 0)
195			dosemas(id);
196		if (pid > 0) {
197			kidarray[i] = pid;
198			nwait++;
199		}
200	}
201
202	procstat = 2;
203	/*
204	 * Wait for children to finish.
205	 */
206
207	count = 0;
208	while ((child = wait(&status)) > 0) {
209		if (status) {
210			tst_resm(TFAIL, "\t%s:dotest[%d] exited status = 0x%x",
211				 prog, child, status);
212			local_flag = FAILED;
213		}
214		++count;
215	}
216
217	/*
218	 * Should have collected all children.
219	 */
220
221	if (count != nwait) {
222		tst_resm(TFAIL, "\tWrong # children waited on, count = %d",
223			 count);
224		local_flag = FAILED;
225	}
226
227	get_arr.array = semvals;
228	if (semctl(id, 0, GETALL, get_arr) < 0) {
229		tst_resm(TFAIL, "\terror on GETALL");
230		tst_resm(TFAIL, "\tsemctl() failed errno %d", errno);
231	}
232
233	if (DEBUG)
234		tst_resm(TINFO, "\tchecking maxvals");
235	for (i = 0; i < NSEMS; i++) {
236		if (semvals[i] != maxsemvals[i]) {
237			tst_resm(TFAIL, "\terror on i %d orig %d final %d", i,
238				 semvals[i], maxsemvals[i]);
239			local_flag = FAILED;
240		}
241	}
242	if (DEBUG)
243		tst_resm(TINFO, "\tmaxvals checked");
244
245	/* 4th arg must either be missing, or must be of type 'union semun'.
246	 * CANNOT just be an int, else it crashes on ppc.
247	 */
248	get_arr.val = 0;
249	if (semctl(id, 0, IPC_RMID, get_arr) < 0) {
250		tst_resm(TFAIL, "\tsemctl(IPC_RMID) failed errno %d", errno);
251		local_flag = FAILED;
252	}
253	if (local_flag == FAILED)
254		exit(1);
255}
256
257static void dosemas(int id)
258{
259	int i, j;
260
261	srand(getpid());
262	for (i = 0; i < NREPS; i++) {
263		for (j = 0; j < NSEMS; j++) {
264			semops[j].sem_num = j;
265			semops[j].sem_flg = SEM_UNDO;
266
267			do {
268				semops[j].sem_op =
269				    (-(short) (rand() %
270							(maxsemvals[j] / 2)));
271			} while (semops[j].sem_op == 0);
272		}
273		if (semop(id, semops, NSEMS) < 0) {
274			tst_resm(TFAIL, "\tsemop1 failed errno %d", errno);
275			exit(1);
276		}
277		for (j = 0; j < NSEMS; j++) {
278			semops[j].sem_op = (-semops[j].sem_op);
279		}
280		if (semop(id, semops, NSEMS) < 0) {
281			tst_resm(TFAIL, "\tsemop2 failed errno %d", errno);
282			exit(1);
283		}
284	}
285	exit(0);
286}
287
288static void term(int sig)
289{
290	int i;
291
292	if ((signal(SIGTERM, term)) == SIG_ERR) {
293		tst_resm(TFAIL, "\tsignal failed. errno %d", errno);
294		exit(1);
295	}
296	if (procstat == 0) {
297		if (DEBUG)
298			tst_resm(TINFO, "\ttest killing kids");
299		for (i = 0; i < NPROCS; i++) {
300			if (kill(pidarray[i], SIGTERM) != 0) {
301				tst_resm(TFAIL, "Kill error pid = %d :",
302					 pidarray[1]);
303			}
304		}
305		if (DEBUG)
306			tst_resm(TINFO, "\ttest kids killed");
307		return;
308	}
309
310	if (procstat == 1) {
311		/* 4th arg must either be missing, or must be of type 'union semun'.
312		 * CANNOT just be an int, else it crashes on ppc.
313		 */
314		union semun arg;
315		arg.val = 0;
316		(void)semctl(tid, 0, IPC_RMID, arg);
317		exit(1);
318	}
319
320	if (tid == -1) {
321		exit(1);
322	}
323	for (i = 0; i < NKIDS; i++) {
324		if (kill(kidarray[i], SIGTERM) != 0) {
325			tst_resm(TFAIL, "Kill error kid id = %d :",
326				 kidarray[1]);
327		}
328	}
329}
330
331void setup(void)
332{
333	tst_sig(FORK, DEF_HANDLER, cleanup);
334
335	TEST_PAUSE;
336
337	tst_tmpdir();
338}
339
340void cleanup(void)
341{
342	tst_rmdir();
343}
344