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