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