• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) International Business Machines  Corp., 2001
3  *
4  * This program is free software;  you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  * the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program;  if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 /*
20  * NAME
21  *	shmctl01.c
22  *
23  * DESCRIPTION
24  *	shmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as
25  *		   they are used with shmctl()
26  *
27  * ALGORITHM
28  *	loop if that option was specified
29  *	create a shared memory segment with read and write permission
30  *	set up any test case specific conditions
31  *	call shmctl() using the TEST macro
32  *	check the return code
33  *	  if failure, issue a FAIL message.
34  *	otherwise,
35  *	  if doing functionality testing
36  *		call the correct test function
37  *		if the conditions are correct,
38  *			issue a PASS message
39  *		otherwise
40  *			issue a FAIL message
41  *	  otherwise
42  *	    issue a PASS message
43  *	call cleanup
44  */
45 
46 #ifndef _GNU_SOURCE
47 #define _GNU_SOURCE
48 #endif
49 #include "ipcshm.h"
50 #include "safe_macros.h"
51 
52 char *TCID = "shmctl01";
53 
54 static int shm_id_1 = -1;
55 static int shm_index;
56 static struct shmid_ds buf;
57 static struct shminfo info;
58 static long save_time;
59 
60 #define FIRST	0
61 #define SECOND	1
62 static int stat_time;
63 
64 static void *set_shared;
65 
66 #define N_ATTACH	4
67 
68 static pid_t pid_arr[N_ATTACH];
69 
70 /* Setup, cleanup and check routines for IPC_STAT */
71 static void stat_setup(void), func_istat(int ret);
72 static void stat_cleanup(void);
73 
74 /* Setup and check routines for IPC_SET */
75 static void set_setup(void), func_set(int ret);
76 
77 /* Check routine for IPC_INFO */
78 static void func_info(int ret);
79 
80 /* Check routine for SHM_STAT */
81 static void func_sstat(int ret);
82 static void func_sstat_setup(void);
83 
84 /* Check routine for SHM_LOCK */
85 static void func_lock(int ret);
86 
87 /* Check routine for SHM_UNLOCK */
88 static void func_unlock(int ret);
89 
90 /* Check routine for IPC_RMID */
91 static void func_rmid(int ret);
92 
93 /* Child function */
94 static void do_child(void);
95 
96 static struct test_case_t {
97 	int *shmid;
98 	int cmd;
99 	struct shmid_ds *arg;
100 	void (*func_test) (int);
101 	void (*func_setup) (void);
102 } TC[] = {
103 	{&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
104 #ifndef UCLINUX
105 	    /*
106 	     * The second test is not applicable to uClinux;
107 	     * shared memory segments are detached on exec(),
108 	     * so cannot be passed to uClinux children.
109 	     */
110 	{&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
111 #endif
112 	{&shm_id_1, IPC_SET, &buf, func_set, set_setup},
113 	{&shm_id_1, IPC_INFO, (struct shmid_ds *) &info, func_info, NULL},
114 	{&shm_index, SHM_STAT, &buf, func_sstat, func_sstat_setup},
115 	{&shm_id_1, SHM_LOCK, NULL, func_lock, NULL},
116 	{&shm_id_1, SHM_UNLOCK, NULL, func_unlock, NULL},
117 	{&shm_id_1, IPC_RMID, NULL, func_rmid, NULL},
118 };
119 
120 static int TST_TOTAL = ARRAY_SIZE(TC);
121 
122 #define NEWMODE	0066
123 
124 #ifdef UCLINUX
125 #define PIPE_NAME	"shmctl01"
126 static char *argv0;
127 #endif
128 
129 static int stat_i;
130 
main(int argc,char * argv[])131 int main(int argc, char *argv[])
132 {
133 	int lc;
134 	int i;
135 
136 	tst_parse_opts(argc, argv, NULL, NULL);
137 #ifdef UCLINUX
138 	argv0 = argv[0];
139 	maybe_run_child(do_child, "ddd", &stat_i, &stat_time, &shm_id_1);
140 #endif
141 
142 	setup();
143 
144 	for (lc = 0; TEST_LOOPING(lc); lc++) {
145 		tst_count = 0;
146 
147 		stat_time = FIRST;
148 
149 		/*
150 		 * Create a shared memory segment with read and write
151 		 * permissions.  Do this here instead of in setup()
152 		 * so that looping (-i) will work correctly.
153 		 */
154 		shm_id_1 = shmget(shmkey, SHM_SIZE,
155 				  IPC_CREAT | IPC_EXCL | SHM_RW);
156 		if (shm_id_1 == -1)
157 			tst_brkm(TBROK, cleanup, "couldn't create the shared"
158 				 " memory segment");
159 
160 		for (i = 0; i < TST_TOTAL; i++) {
161 
162 			/*
163 			 * if needed, set up any required conditions by
164 			 * calling the appropriate setup function
165 			 */
166 			if (TC[i].func_setup != NULL)
167 				(*TC[i].func_setup) ();
168 
169 			TEST(shmctl(*(TC[i].shmid), TC[i].cmd, TC[i].arg));
170 
171 			if (TEST_RETURN == -1) {
172 				tst_resm(TFAIL, "%s call failed - errno "
173 					 "= %d : %s", TCID, TEST_ERRNO,
174 					 strerror(TEST_ERRNO));
175 				continue;
176 			}
177 			(*TC[i].func_test) (TEST_RETURN);
178 		}
179 	}
180 
181 	cleanup();
182 	tst_exit();
183 }
184 
185 /*
186  * set_shmat() - Attach the shared memory and return the pointer.  Use
187  *		 this seperate routine to avoid code duplication in
188  *		 stat_setup() below.
189  */
set_shmat(void)190 void *set_shmat(void)
191 {
192 	void *rval;
193 
194 	/* attach the shared memory */
195 	rval = shmat(shm_id_1, 0, 0);
196 
197 	/*
198 	 * if shmat() fails, the only thing we can do is
199 	 * print a message to that effect.
200 	 */
201 	if (rval == (void *)-1) {
202 		tst_resm(TBROK, "shmat() failed - %s", strerror(errno));
203 		cleanup();
204 	}
205 
206 	return rval;
207 }
208 
209 /*
210  * stat_setup() - Set up for the IPC_STAT command with shmctl().
211  *		  Make things interesting by forking some children
212  *		  that will either attach or inherit the shared memory.
213  */
stat_setup(void)214 void stat_setup(void)
215 {
216 	void *set_shmat();
217 	pid_t pid;
218 
219 	/*
220 	 * The first time through, let the children attach the memory.
221 	 * The second time through, attach the memory first and let
222 	 * the children inherit the memory.
223 	 */
224 
225 	if (stat_time == SECOND)
226 		/*
227 		 * use the global "set_shared" variable here so that
228 		 * it can be removed in the stat_func() routine.
229 		 */
230 		set_shared = set_shmat();
231 
232 	tst_old_flush();
233 	for (stat_i = 0; stat_i < N_ATTACH; stat_i++) {
234 		pid = FORK_OR_VFORK();
235 		if (pid == -1)
236 			tst_brkm(TBROK, cleanup, "could not fork");
237 
238 		if (pid == 0) {
239 #ifdef UCLINUX
240 			if (self_exec(argv0, "ddd", stat_i, stat_time,
241 				      shm_id_1) < 0)
242 				tst_brkm(TBROK, cleanup, "could not self_exec");
243 #else
244 			do_child();
245 #endif
246 
247 		} else {
248 			/* save the child's pid for cleanup later */
249 			pid_arr[stat_i] = pid;
250 			TST_PROCESS_STATE_WAIT(cleanup, pid, 'S');
251 		}
252 	}
253 }
254 
do_child(void)255 void do_child(void)
256 {
257 	void *test;
258 
259 	if (stat_time == FIRST)
260 		test = set_shmat();
261 	else
262 		test = set_shared;
263 
264 	memcpy(test, &stat_i, sizeof(stat_i));
265 
266 	/* pause until we get a signal from stat_cleanup() */
267 	pause();
268 
269 	/* now we're back - detach the memory and exit */
270 	if (shmdt(test) == -1)
271 		tst_resm(TBROK, "shmdt() failed - %d", errno);
272 
273 	tst_exit();
274 }
275 
276 /*
277  * func_istat() - check the functionality of the IPC_STAT command with shmctl()
278  *		 by looking at the pid of the creator, the segement size,
279  *		 the number of attaches and the mode.
280  */
func_istat(int ret)281 void func_istat(int ret)
282 {
283 	int fail = 0;
284 	pid_t pid;
285 
286 	/* check perm, pid, nattach and size */
287 
288 	pid = getpid();
289 
290 	if (buf.shm_cpid != pid) {
291 		tst_resm(TFAIL, "creator pid is incorrect");
292 		fail = 1;
293 	}
294 
295 	if (!fail && buf.shm_segsz != SHM_SIZE) {
296 		tst_resm(TFAIL, "segment size is incorrect");
297 		fail = 1;
298 	}
299 
300 	/*
301 	 * The first time through, only the children attach the memory, so
302 	 * the attaches equal N_ATTACH + stat_time (0).  The second time
303 	 * through, the parent attaches the memory and the children inherit
304 	 * that memory so the attaches equal N_ATTACH + stat_time (1).
305 	 */
306 	if (!fail && buf.shm_nattch != N_ATTACH + stat_time) {
307 		tst_resm(TFAIL, "# of attaches is incorrect - %ld",
308 			 buf.shm_nattch);
309 		fail = 1;
310 	}
311 
312 	/* use MODE_MASK to make sure we are comparing the last 9 bits */
313 	if (!fail && (buf.shm_perm.mode & MODE_MASK) !=
314 			((SHM_RW) & MODE_MASK)) {
315 		tst_resm(TFAIL, "segment mode is incorrect");
316 		fail = 1;
317 	}
318 
319 	stat_cleanup();
320 
321 	/* save the change time for use in the next test */
322 	save_time = buf.shm_ctime;
323 
324 	if (fail)
325 		return;
326 
327 	tst_resm(TPASS, "pid, size, # of attaches and mode are correct "
328 		 "- pass #%d", stat_time);
329 }
330 
331 /*
332  * stat_cleanup() - signal the children to clean up after themselves and
333  *		    have the parent make dessert, er, um, make that remove
334  *		    the shared memory that is no longer needed.
335  */
stat_cleanup(void)336 void stat_cleanup(void)
337 {
338 	int i;
339 
340 	/* wake up the childern so they can detach the memory and exit */
341 	for (i = 0; i < N_ATTACH; i++) {
342 		SAFE_KILL(cleanup, pid_arr[i], SIGUSR1);
343 	}
344 
345 	/* remove the parent's shared memory the second time through */
346 	if (stat_time == SECOND) {
347 		if (shmdt(set_shared) == -1)
348 			tst_resm(TINFO, "shmdt() failed");
349 	}
350 
351 	for (i = 0; i < N_ATTACH; i++) {
352 		SAFE_WAITPID(cleanup, pid_arr[i], NULL, 0);
353 	}
354 
355 	stat_time++;
356 }
357 
358 /*
359  * set_setup() - set up for the IPC_SET command with shmctl()
360  */
set_setup(void)361 void set_setup(void)
362 {
363 	/* set up a new mode for the shared memory segment */
364 	buf.shm_perm.mode = SHM_RW | NEWMODE;
365 
366 	/* sleep for one second to get a different shm_ctime value */
367 	sleep(1);
368 }
369 
370 /*
371  * func_set() - check the functionality of the IPC_SET command with shmctl()
372  */
func_set(int ret)373 void func_set(int ret)
374 {
375 	int fail = 0;
376 
377 	/* first stat the shared memory to get the new data */
378 	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
379 		tst_resm(TBROK, "stat failed in func_set()");
380 		return;
381 	}
382 
383 	if ((buf.shm_perm.mode & MODE_MASK) !=
384 			((SHM_RW | NEWMODE) & MODE_MASK)) {
385 		tst_resm(TFAIL, "new mode is incorrect");
386 		fail = 1;
387 	}
388 
389 	if (!fail && save_time >= buf.shm_ctime) {
390 		tst_resm(TFAIL, "change time is incorrect");
391 		fail = 1;
392 	}
393 
394 	if (fail)
395 		return;
396 
397 	tst_resm(TPASS, "new mode and change time are correct");
398 }
399 
func_info(int ret)400 static void func_info(int ret)
401 {
402 	if (info.shmmin != 1)
403 		tst_resm(TFAIL, "value of shmmin is incorrect");
404 	else
405 		tst_resm(TPASS, "get correct shared memory limits");
406 }
407 
func_sstat(int ret)408 static void func_sstat(int ret)
409 {
410 	if (ret >= 0)
411 		tst_resm(TPASS, "get correct shared memory id for index: %d",
412 			shm_index);
413 	else
414 		tst_resm(TFAIL, "shared memory id is incorrect, index: %d",
415 			shm_index);
416 }
417 
func_sstat_setup(void)418 static void func_sstat_setup(void)
419 {
420 	struct shm_info tmp;
421 	int ret;
422 
423 	ret = shmctl(shm_id_1, SHM_INFO, (void *)&tmp);
424 	if (ret < 0)
425 		tst_resm(TFAIL|TERRNO, "shmctl(SHM_INFO)");
426 	else
427 		shm_index = ret;
428 }
429 
func_lock(int ret)430 static void func_lock(int ret)
431 {
432 	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
433 		tst_resm(TBROK, "stat failed in func_lock()");
434 		return;
435 	}
436 
437 	if (buf.shm_perm.mode & SHM_LOCKED)
438 		tst_resm(TPASS, "SHM_LOCK is set");
439 	else
440 		tst_resm(TFAIL, "SHM_LOCK is cleared");
441 }
442 
func_unlock(int ret)443 static void func_unlock(int ret)
444 {
445 	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
446 		tst_resm(TBROK, "stat failed in func_unlock()");
447 		return;
448 	}
449 
450 	if (buf.shm_perm.mode & SHM_LOCKED)
451 		tst_resm(TFAIL, "SHM_LOCK is set");
452 	else
453 		tst_resm(TPASS, "SHM_LOCK is cleared");
454 }
455 
456 
457 /*
458  * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
459  */
func_rmid(int ret)460 void func_rmid(int ret)
461 {
462 	/* Do another shmctl() - we should get EINVAL */
463 	if (shmctl(shm_id_1, IPC_STAT, &buf) != -1)
464 		tst_brkm(TBROK, cleanup, "shmctl succeeded on expected fail");
465 
466 	if (errno != EINVAL)
467 		tst_resm(TFAIL, "returned unexpected errno %d", errno);
468 	else
469 		tst_resm(TPASS, "shared memory appears to be removed");
470 
471 	shm_id_1 = -1;
472 }
473 
474 /*
475  * sighandler() - handle signals, in this case SIGUSR1 is the only one expected
476  */
sighandler(int sig)477 void sighandler(int sig)
478 {
479 	if (sig != SIGUSR1)
480 		tst_resm(TBROK, "received unexpected signal %d", sig);
481 }
482 
setup(void)483 void setup(void)
484 {
485 	tst_sig(FORK, sighandler, cleanup);
486 
487 	TEST_PAUSE;
488 
489 	tst_tmpdir();
490 
491 	shmkey = getipckey();
492 }
493 
cleanup(void)494 void cleanup(void)
495 {
496 	rm_shm(shm_id_1);
497 
498 	tst_rmdir();
499 }
500