• 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 
51 char *TCID = "shmctl01";
52 
53 static int shm_id_1 = -1;
54 static int shm_index;
55 static struct shmid_ds buf;
56 static struct shminfo info;
57 static long save_time;
58 
59 #define FIRST	0
60 #define SECOND	1
61 static int stat_time;
62 
63 static void *set_shared;
64 
65 #define N_ATTACH	4
66 
67 static pid_t pid_arr[N_ATTACH];
68 
69 /* Setup, cleanup and check routines for IPC_STAT */
70 static void stat_setup(void), func_istat(int ret);
71 static void stat_cleanup(void);
72 
73 /* Setup and check routines for IPC_SET */
74 static void set_setup(void), func_set(int ret);
75 
76 /* Check routine for IPC_INFO */
77 static void func_info(int ret);
78 
79 /* Check routine for SHM_STAT */
80 static void func_sstat(int ret);
81 
82 /* Check routine for SHM_LOCK */
83 static void func_lock(int ret);
84 
85 /* Check routine for SHM_UNLOCK */
86 static void func_unlock(int ret);
87 
88 /* Check routine for IPC_RMID */
89 static void func_rmid(int ret);
90 
91 /* Child function */
92 static void do_child(void);
93 
94 static struct test_case_t {
95 	int *shmid;
96 	int cmd;
97 	struct shmid_ds *arg;
98 	void (*func_test) (int);
99 	void (*func_setup) (void);
100 } TC[] = {
101 	{&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
102 #ifndef UCLINUX
103 	    /*
104 	     * The second test is not applicable to uClinux;
105 	     * shared memory segments are detached on exec(),
106 	     * so cannot be passed to uClinux children.
107 	     */
108 	{&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
109 #endif
110 	{&shm_id_1, IPC_SET, &buf, func_set, set_setup},
111 	{&shm_id_1, IPC_INFO, (struct shmid_ds *) &info, func_info, NULL},
112 	{&shm_index, SHM_STAT, &buf, func_sstat, NULL},
113 	{&shm_id_1, SHM_LOCK, NULL, func_lock, NULL},
114 	{&shm_id_1, SHM_UNLOCK, NULL, func_unlock, NULL},
115 	{&shm_id_1, IPC_RMID, NULL, func_rmid, NULL},
116 };
117 
118 static int TST_TOTAL = ARRAY_SIZE(TC);
119 
120 #define NEWMODE	0066
121 
122 #ifdef UCLINUX
123 #define PIPE_NAME	"shmctl01"
124 static char *argv0;
125 #endif
126 
127 static int stat_i;
128 
main(int argc,char * argv[])129 int main(int argc, char *argv[])
130 {
131 	int lc;
132 	int i;
133 
134 	tst_parse_opts(argc, argv, NULL, NULL);
135 #ifdef UCLINUX
136 	argv0 = argv[0];
137 	maybe_run_child(do_child, "ddd", &stat_i, &stat_time, &shm_id_1);
138 #endif
139 
140 	setup();
141 
142 	for (lc = 0; TEST_LOOPING(lc); lc++) {
143 		tst_count = 0;
144 
145 		stat_time = FIRST;
146 
147 		/*
148 		 * Create a shared memory segment with read and write
149 		 * permissions.  Do this here instead of in setup()
150 		 * so that looping (-i) will work correctly.
151 		 */
152 		shm_id_1 = shmget(shmkey, SHM_SIZE,
153 				  IPC_CREAT | IPC_EXCL | SHM_RW);
154 		if (shm_id_1 == -1)
155 			tst_brkm(TBROK, cleanup, "couldn't create the shared"
156 				 " memory segment");
157 
158 		for (i = 0; i < TST_TOTAL; i++) {
159 
160 			/*
161 			 * if needed, set up any required conditions by
162 			 * calling the appropriate setup function
163 			 */
164 			if (TC[i].func_setup != NULL)
165 				(*TC[i].func_setup) ();
166 
167 			TEST(shmctl(*(TC[i].shmid), TC[i].cmd, TC[i].arg));
168 
169 			if (TEST_RETURN == -1) {
170 				tst_resm(TFAIL, "%s call failed - errno "
171 					 "= %d : %s", TCID, TEST_ERRNO,
172 					 strerror(TEST_ERRNO));
173 				continue;
174 			}
175 			(*TC[i].func_test) (TEST_RETURN);
176 		}
177 	}
178 
179 	cleanup();
180 	tst_exit();
181 }
182 
183 /*
184  * set_shmat() - Attach the shared memory and return the pointer.  Use
185  *		 this seperate routine to avoid code duplication in
186  *		 stat_setup() below.
187  */
set_shmat(void)188 void *set_shmat(void)
189 {
190 	void *rval;
191 
192 	/* attach the shared memory */
193 	rval = shmat(shm_id_1, 0, 0);
194 
195 	/*
196 	 * if shmat() fails, the only thing we can do is
197 	 * print a message to that effect.
198 	 */
199 	if (rval == (void *)-1) {
200 		tst_resm(TBROK, "shmat() failed - %s", strerror(errno));
201 		cleanup();
202 	}
203 
204 	return rval;
205 }
206 
207 /*
208  * stat_setup() - Set up for the IPC_STAT command with shmctl().
209  *		  Make things interesting by forking some children
210  *		  that will either attach or inherit the shared memory.
211  */
stat_setup(void)212 void stat_setup(void)
213 {
214 	void *set_shmat();
215 	pid_t pid;
216 
217 	/*
218 	 * The first time through, let the children attach the memory.
219 	 * The second time through, attach the memory first and let
220 	 * the children inherit the memory.
221 	 */
222 
223 	if (stat_time == SECOND)
224 		/*
225 		 * use the global "set_shared" variable here so that
226 		 * it can be removed in the stat_func() routine.
227 		 */
228 		set_shared = set_shmat();
229 
230 	tst_flush();
231 	for (stat_i = 0; stat_i < N_ATTACH; stat_i++) {
232 		pid = FORK_OR_VFORK();
233 		if (pid == -1)
234 			tst_brkm(TBROK, cleanup, "could not fork");
235 
236 		if (pid == 0) {
237 #ifdef UCLINUX
238 			if (self_exec(argv0, "ddd", stat_i, stat_time,
239 				      shm_id_1) < 0)
240 				tst_brkm(TBROK, cleanup, "could not self_exec");
241 #else
242 			do_child();
243 #endif
244 
245 		} else {
246 			/* save the child's pid for cleanup later */
247 			pid_arr[stat_i] = pid;
248 			TST_PROCESS_STATE_WAIT(cleanup, pid, 'S');
249 		}
250 	}
251 }
252 
do_child(void)253 void do_child(void)
254 {
255 	void *test;
256 
257 	if (stat_time == FIRST)
258 		test = set_shmat();
259 	else
260 		test = set_shared;
261 
262 	memcpy(test, &stat_i, sizeof(stat_i));
263 
264 	/* pause until we get a signal from stat_cleanup() */
265 	pause();
266 
267 	/* now we're back - detach the memory and exit */
268 	if (shmdt(test) == -1)
269 		tst_resm(TBROK, "shmdt() failed - %d", errno);
270 
271 	tst_exit();
272 }
273 
274 /*
275  * func_istat() - check the functionality of the IPC_STAT command with shmctl()
276  *		 by looking at the pid of the creator, the segement size,
277  *		 the number of attaches and the mode.
278  */
func_istat(int ret)279 void func_istat(int ret)
280 {
281 	int fail = 0;
282 	pid_t pid;
283 
284 	/* check perm, pid, nattach and size */
285 
286 	pid = getpid();
287 
288 	if (buf.shm_cpid != pid) {
289 		tst_resm(TFAIL, "creator pid is incorrect");
290 		fail = 1;
291 	}
292 
293 	if (!fail && buf.shm_segsz != SHM_SIZE) {
294 		tst_resm(TFAIL, "segment size is incorrect");
295 		fail = 1;
296 	}
297 
298 	/*
299 	 * The first time through, only the children attach the memory, so
300 	 * the attaches equal N_ATTACH + stat_time (0).  The second time
301 	 * through, the parent attaches the memory and the children inherit
302 	 * that memory so the attaches equal N_ATTACH + stat_time (1).
303 	 */
304 	if (!fail && buf.shm_nattch != N_ATTACH + stat_time) {
305 		tst_resm(TFAIL, "# of attaches is incorrect - %ld",
306 			 buf.shm_nattch);
307 		fail = 1;
308 	}
309 
310 	/* use MODE_MASK to make sure we are comparing the last 9 bits */
311 	if (!fail && (buf.shm_perm.mode & MODE_MASK) !=
312 			((SHM_RW) & MODE_MASK)) {
313 		tst_resm(TFAIL, "segment mode is incorrect");
314 		fail = 1;
315 	}
316 
317 	stat_cleanup();
318 
319 	/* save the change time for use in the next test */
320 	save_time = buf.shm_ctime;
321 
322 	if (fail)
323 		return;
324 
325 	tst_resm(TPASS, "pid, size, # of attaches and mode are correct "
326 		 "- pass #%d", stat_time);
327 }
328 
329 /*
330  * stat_cleanup() - signal the children to clean up after themselves and
331  *		    have the parent make dessert, er, um, make that remove
332  *		    the shared memory that is no longer needed.
333  */
stat_cleanup(void)334 void stat_cleanup(void)
335 {
336 	int i;
337 
338 	/* wake up the childern so they can detach the memory and exit */
339 	for (i = 0; i < N_ATTACH; i++) {
340 		if (kill(pid_arr[i], SIGUSR1) == -1)
341 			tst_brkm(TBROK, cleanup, "kill failed");
342 	}
343 
344 	/* remove the parent's shared memory the second time through */
345 	if (stat_time == SECOND) {
346 		if (shmdt(set_shared) == -1)
347 			tst_resm(TINFO, "shmdt() failed");
348 	}
349 
350 	for (i = 0; i < N_ATTACH; i++) {
351 		if (waitpid(pid_arr[i], NULL, 0) == -1)
352 			tst_brkm(TBROK, cleanup, "waitpid failed");
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");
412 	else
413 		tst_resm(TFAIL, "shared memory id is incorrect");
414 }
415 
func_lock(int ret)416 static void func_lock(int ret)
417 {
418 	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
419 		tst_resm(TBROK, "stat failed in func_lock()");
420 		return;
421 	}
422 
423 	if (buf.shm_perm.mode & SHM_LOCKED)
424 		tst_resm(TPASS, "SHM_LOCK is set");
425 	else
426 		tst_resm(TFAIL, "SHM_LOCK is cleared");
427 }
428 
func_unlock(int ret)429 static void func_unlock(int ret)
430 {
431 	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
432 		tst_resm(TBROK, "stat failed in func_unlock()");
433 		return;
434 	}
435 
436 	if (buf.shm_perm.mode & SHM_LOCKED)
437 		tst_resm(TFAIL, "SHM_LOCK is set");
438 	else
439 		tst_resm(TPASS, "SHM_LOCK is cleared");
440 }
441 
442 
443 /*
444  * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
445  */
func_rmid(int ret)446 void func_rmid(int ret)
447 {
448 	/* Do another shmctl() - we should get EINVAL */
449 	if (shmctl(shm_id_1, IPC_STAT, &buf) != -1)
450 		tst_brkm(TBROK, cleanup, "shmctl succeeded on expected fail");
451 
452 	if (errno != EINVAL)
453 		tst_resm(TFAIL, "returned unexpected errno %d", errno);
454 	else
455 		tst_resm(TPASS, "shared memory appears to be removed");
456 
457 	shm_id_1 = -1;
458 }
459 
460 /*
461  * sighandler() - handle signals, in this case SIGUSR1 is the only one expected
462  */
sighandler(int sig)463 void sighandler(int sig)
464 {
465 	if (sig != SIGUSR1)
466 		tst_resm(TBROK, "received unexpected signal %d", sig);
467 }
468 
setup(void)469 void setup(void)
470 {
471 	tst_sig(FORK, sighandler, cleanup);
472 
473 	TEST_PAUSE;
474 
475 	tst_tmpdir();
476 
477 	shmkey = getipckey();
478 }
479 
cleanup(void)480 void cleanup(void)
481 {
482 	rm_shm(shm_id_1);
483 
484 	tst_rmdir();
485 }
486