vm: fix region reporting bug
[minix.git] / test / ipc / shmctl / shmctl01.c
blobb308479b8faa11fa12a990ae31ef0f0ecf6711a2
1 /*
3 * Copyright (c) International Business Machines Corp., 2001
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * NAME
22 * shmctl01.c
24 * DESCRIPTION
25 * shmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as
26 * they are used with shmctl()
28 * ALGORITHM
29 * loop if that option was specified
30 * create a shared memory segment with read and write permission
31 * set up any test case specific conditions
32 * call shmctl() using the TEST macro
33 * check the return code
34 * if failure, issue a FAIL message.
35 * otherwise,
36 * if doing functionality testing
37 * call the correct test function
38 * if the conditions are correct,
39 * issue a PASS message
40 * otherwise
41 * issue a FAIL message
42 * otherwise
43 * issue a PASS message
44 * call cleanup
46 * USAGE: <for command-line>
47 * shmctl01 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
48 * where, -c n : Run n copies concurrently.
49 * -f : Turn off functionality Testing.
50 * -i n : Execute test n times.
51 * -I x : Execute test for x seconds.
52 * -P x : Pause for x seconds between iterations.
53 * -t : Turn on syscall timing.
55 * HISTORY
56 * 03/2001 - Written by Wayne Boyer
58 * RESTRICTIONS
59 * none
62 #include "ipcshm.h"
64 char *TCID = "shmctl01";
65 extern int Tst_count;
67 int shm_id_1 = -1;
68 struct shmid_ds buf;
69 long save_time;
71 #define FIRST 0
72 #define SECOND 1
73 int stat_time; /* set to either FIRST or SECOND for IPC_STAT tests */
75 void *set_shared;
77 #define N_ATTACH 4
79 pid_t pid_arr[N_ATTACH];
81 void sighandler(int);
84 * These are the various setup and check functions for the commands
85 * that we are checking.
88 /* Setup, cleanup and check routines for IPC_STAT */
89 void stat_setup(void), func_stat(void);
90 void stat_cleanup(void);
92 /* Setup and check routines for IPC_SET */
93 void set_setup(void), func_set(void);
95 /* Check routine for IPC_RMID */
96 void func_rmid(void);
98 /* Child function */
99 void do_child(void);
101 struct test_case_t {
102 int cmd; /* the command to test */
103 void (*func_test)(void); /* the test function */
104 void (*func_setup)(void); /* the setup function if necessary */
105 } TC[] = {
107 {IPC_STAT, func_stat, stat_setup},
109 #ifndef UCLINUX
110 /* The second test is not applicable to uClinux; shared memory segments
111 are detached on exec(), so cannot be passed to uClinux children. */
112 {IPC_STAT, func_stat, stat_setup},
113 #endif
115 {IPC_SET, func_set, set_setup},
117 {IPC_RMID, func_rmid, NULL}
120 int TST_TOTAL = (sizeof(TC) / sizeof(*TC));
122 #define NEWMODE 0066
124 #ifdef UCLINUX
125 static char *argv0;
126 #endif
128 static int stat_i; /* Shared between do_child and stat_setup */
130 int main(int ac, char **av)
132 int lc; /* loop counter */
133 char *msg; /* message returned from parse_opts */
134 int i;
135 void check_functionality(void);
137 /* parse standard options */
138 if ((msg = parse_opts(ac, av, (option_t *)NULL, NULL)) != (char *)NULL){
139 tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
142 #ifdef UCLINUX
143 argv0 = av[0];
144 maybe_run_child(do_child, "ddd", &stat_i, &stat_time, &shm_id_1);
145 #endif
147 setup(); /* global setup */
149 /* The following loop checks looping state if -i option given */
151 for (lc = 0; TEST_LOOPING(lc); lc++) {
152 /* reset Tst_count in case we are looping */
153 Tst_count = 0;
155 /* initialize stat_time */
156 stat_time = FIRST;
159 * Create a shared memory segment with read and write
160 * permissions. Do this here instead of in setup()
161 * so that looping (-i) will work correctly.
163 if ((shm_id_1 = shmget(shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL |
164 SHM_RW)) == -1) {
165 tst_brkm(TBROK, cleanup, "couldn't create the shared"
166 " memory segment");
169 /* loop through the test cases */
170 for (i=0; i<TST_TOTAL; i++) {
173 * if needed, set up any required conditions by
174 * calling the appropriate setup function
176 if (TC[i].func_setup != NULL) {
177 (*TC[i].func_setup)();
181 * Use TEST macro to make the call
184 TEST(shmctl(shm_id_1, TC[i].cmd, &buf));
186 if (TEST_RETURN == -1) {
187 tst_resm(TFAIL, "%s call failed - errno "
188 "= %d : %s", TCID, TEST_ERRNO,
189 strerror(TEST_ERRNO));
190 continue;
192 if (STD_FUNCTIONAL_TEST) {
193 (*TC[i].func_test)();
194 } else {
195 tst_resm(TPASS, "call succeeded");
197 /* now perform command related cleanup */
198 switch(TC[i].cmd) {
199 case IPC_STAT:
200 stat_cleanup();
201 break;
202 case IPC_RMID:
203 shm_id_1 = -1;
204 break;
210 cleanup();
212 /*NOTREACHED*/
213 return(0);
217 * set_shmat() - Attach the shared memory and return the pointer. Use
218 * this seperate routine to avoid code duplication in
219 * stat_setup() below.
221 void *
222 set_shmat(void)
224 void *rval;
226 /* attach the shared memory */
227 rval = shmat(shm_id_1, 0, 0);
230 * if shmat() fails, the only thing we can do is
231 * print a message to that effect.
233 if (rval == (void *)-1) {
234 tst_resm(TBROK, "shmat() failed - %s", strerror(errno));
235 cleanup();
238 return rval;
242 * stat_setup() - Set up for the IPC_STAT command with shmctl().
243 * Make things interesting by forking some children
244 * that will either attach or inherit the shared memory.
246 void
247 stat_setup(void)
249 void *set_shmat(void);
250 pid_t pid;
253 * The first time through, let the children attach the memory.
254 * The second time through, attach the memory first and let
255 * the children inherit the memory.
258 if (stat_time == SECOND) {
260 * use the global "set_shared" variable here so that
261 * it can be removed in the stat_func() routine.
263 set_shared = set_shmat();
266 tst_flush();
267 for (stat_i=0; stat_i<N_ATTACH; stat_i++) {
268 if ((pid = fork()) == -1) {
269 tst_brkm(TBROK, cleanup, "could not fork");
272 if (pid == 0) { /* child */
273 #ifdef UCLINUX
274 if (self_exec(argv0, "ddd", stat_i, stat_time,
275 shm_id_1) < 0) {
276 tst_brkm(TBROK, cleanup, "could not self_exec");
278 #else
279 do_child();
280 #endif
282 } else { /* parent */
283 /* save the child's pid for cleanup later */
284 pid_arr[stat_i] = pid;
287 /* sleep briefly to ensure correct execution order */
288 usleep(250000);
292 * do_child
294 void
295 do_child()
297 int rval;
298 void *test;
300 if (stat_time == FIRST) {
301 test = set_shmat();
302 } else {
303 test = set_shared;
306 /* do an assignement for fun */
307 *(int *)test = stat_i;
309 /* pause until we get a signal from stat_cleanup() */
310 rval = pause();
312 /* now we're back - detach the memory and exit */
313 if (shmdt(test) == -1) {
314 tst_resm(TBROK, "shmdt() failed - %d", errno);
316 tst_exit();
320 * func_stat() - check the functionality of the IPC_STAT command with shmctl()
321 * by looking at the pid of the creator, the segement size,
322 * the number of attaches and the mode.
324 void
325 func_stat()
327 int fail = 0;
328 pid_t pid;
330 /* check perm, pid, nattach and size */
332 pid = getpid();
334 if (buf.shm_cpid != pid) {
335 tst_resm(TFAIL, "creator pid is incorrect");
336 fail = 1;
339 if (!fail && buf.shm_segsz != SHM_SIZE) {
340 tst_resm(TFAIL, "segment size is incorrect");
341 fail = 1;
345 * The first time through, only the children attach the memory, so
346 * the attaches equal N_ATTACH + stat_time (0). The second time
347 * through, the parent attaches the memory and the children inherit
348 * that memory so the attaches equal N_ATTACH + stat_time (1).
350 if (!fail && buf.shm_nattch != N_ATTACH + stat_time) {
351 tst_resm(TFAIL, "# of attaches is incorrect - %d",
352 buf.shm_nattch);
353 fail = 1;
356 /* use MODE_MASK to make sure we are comparing the last 9 bits */
357 if (!fail && (buf.shm_perm.mode & MODE_MASK) != ((SHM_RW) & MODE_MASK)) {
358 tst_resm(TFAIL, "segment mode is incorrect");
359 fail = 1;
362 stat_cleanup();
364 /* save the change time for use in the next test */
365 save_time = buf.shm_ctime;
367 if (fail) {
368 return;
371 tst_resm(TPASS, "pid, size, # of attaches and mode are correct "
372 "- pass #%d", stat_time);
376 * stat_cleanup() - signal the children to clean up after themselves and
377 * have the parent make dessert, er, um, make that remove
378 * the shared memory that is no longer needed.
380 void
381 stat_cleanup()
383 int i;
385 /* wake up the childern so they can detach the memory and exit */
386 for (i=0; i<N_ATTACH; i++) {
387 if(kill(pid_arr[i], SIGUSR1) == -1) {
388 tst_brkm(TBROK, cleanup, "kill failed");
392 /* remove the parent's shared memory the second time through */
393 if (stat_time == SECOND) {
394 if (shmdt(set_shared) == -1) {
395 tst_resm(TINFO, "shmdt() failed");
399 stat_time++;
403 * set_setup() - set up for the IPC_SET command with shmctl()
405 void
406 set_setup()
408 /* set up a new mode for the shared memory segment */
409 buf.shm_perm.mode = SHM_RW | NEWMODE;
411 /* sleep for one second to get a different shm_ctime value */
412 sleep(1);
416 * func_set() - check the functionality of the IPC_SET command with shmctl()
418 void
419 func_set()
421 int fail = 0;
423 /* first stat the shared memory to get the new data */
424 if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
425 tst_resm(TBROK, "stat failed in func_set()");
426 return;
429 if ((buf.shm_perm.mode & MODE_MASK) !=
430 ((SHM_RW | NEWMODE) & MODE_MASK)) {
431 tst_resm(TFAIL, "new mode is incorrect");
432 fail = 1;
435 if (!fail && save_time >= buf.shm_ctime) {
436 tst_resm(TFAIL, "change time is incorrect");
437 fail = 1;
440 if (fail) {
441 return;
444 tst_resm(TPASS, "new mode and change time are correct");
448 * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
450 void
451 func_rmid()
453 /* Do another shmctl() - we should get EINVAL */
454 if (shmctl(shm_id_1, IPC_STAT, &buf) != -1) {
455 tst_brkm(TBROK, cleanup, "shmctl succeeded on expected fail");
458 if (errno != EINVAL) {
459 tst_resm(TFAIL, "returned unexpected errno %d", errno);
460 } else {
461 tst_resm(TPASS, "shared memory appears to be removed");
464 shm_id_1 = -1;
468 * sighandler() - handle signals, in this case SIGUSR1 is the only one expected
470 void
471 sighandler(sig)
473 if (sig != SIGUSR1) {
474 tst_resm(TINFO, "received unexpected signal %d", sig);
479 * setup() - performs all the ONE TIME setup for this test.
481 void
482 setup(void)
484 /* capture signals */
485 tst_sig(FORK, sighandler, cleanup);
487 /* Pause if that option was specified */
488 TEST_PAUSE;
491 * Create a temporary directory and cd into it.
492 * This helps to ensure that a unique msgkey is created.
493 * See ../lib/libipc.c for more information.
495 tst_tmpdir();
497 /* get an IPC resource key */
498 shmkey = getipckey();
502 * cleanup() - performs all the ONE TIME cleanup for this test at completion
503 * or premature exit.
505 void
506 cleanup(void)
508 /* if it exists, remove the shared memory segment */
509 rm_shm(shm_id_1);
511 /* Remove the temporary directory */
512 tst_rmdir();
515 * print timing stats if that option was specified.
516 * print errno log if that option was specified.
518 TEST_CLEANUP;
520 /* exit with return code appropriate for results */
521 tst_exit();