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 
63 int 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 
73 void setup();
74 void cleanup();
75 
76 static key_t keyarray[NPROCS];
77 static struct sembuf semops[NSEMS];
78 static short maxsemvals[NSEMS];
79 static int pidarray[NPROCS];
80 static int kidarray[NKIDS];
81 static int tid;
82 static int procstat;
83 static char *prog;
84 static unsigned short semvals[NSEMS];
85 
86 char *TCID = "semctl06";
87 int TST_TOTAL = 1;
88 
89 static void term(int sig);
90 static void dosemas(int id);
91 static void dotest(key_t key);
92 
main(int argc,char ** argv)93 int 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 
dotest(key_t key)163 static 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 
dosemas(int id)257 static 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 
term(int sig)288 static 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 
setup(void)331 void setup(void)
332 {
333 	tst_sig(FORK, DEF_HANDLER, cleanup);
334 
335 	TEST_PAUSE;
336 
337 	tst_tmpdir();
338 }
339 
cleanup(void)340 void cleanup(void)
341 {
342 	tst_rmdir();
343 }
344