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
25 * shmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as
26 * they are used with shmctl()
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.
36 * if doing functionality testing
37 * call the correct test function
38 * if the conditions are correct,
39 * issue a PASS message
41 * issue a FAIL message
43 * issue a PASS message
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.
56 * 03/2001 - Written by Wayne Boyer
64 char *TCID
= "shmctl01";
73 int stat_time
; /* set to either FIRST or SECOND for IPC_STAT tests */
79 pid_t pid_arr
[N_ATTACH
];
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 */
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 */
107 {IPC_STAT
, func_stat
, stat_setup
},
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
},
115 {IPC_SET
, func_set
, set_setup
},
117 {IPC_RMID
, func_rmid
, NULL
}
120 int TST_TOTAL
= (sizeof(TC
) / sizeof(*TC
));
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 */
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
);
144 maybe_run_child(do_child
, "ddd", &stat_i
, &stat_time
, &shm_id_1
);
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 */
155 /* initialize stat_time */
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
|
165 tst_brkm(TBROK
, cleanup
, "couldn't create the shared"
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
));
192 if (STD_FUNCTIONAL_TEST
) {
193 (*TC
[i
].func_test
)();
195 tst_resm(TPASS
, "call succeeded");
197 /* now perform command related cleanup */
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.
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
));
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.
249 void *set_shmat(void);
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();
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 */
274 if (self_exec(argv0
, "ddd", stat_i
, stat_time
,
276 tst_brkm(TBROK
, cleanup
, "could not self_exec");
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 */
300 if (stat_time
== FIRST
) {
306 /* do an assignement for fun */
307 *(int *)test
= stat_i
;
309 /* pause until we get a signal from stat_cleanup() */
312 /* now we're back - detach the memory and exit */
313 if (shmdt(test
) == -1) {
314 tst_resm(TBROK
, "shmdt() failed - %d", errno
);
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.
330 /* check perm, pid, nattach and size */
334 if (buf
.shm_cpid
!= pid
) {
335 tst_resm(TFAIL
, "creator pid is incorrect");
339 if (!fail
&& buf
.shm_segsz
!= SHM_SIZE
) {
340 tst_resm(TFAIL
, "segment size is incorrect");
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",
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");
364 /* save the change time for use in the next test */
365 save_time
= buf
.shm_ctime
;
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.
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");
403 * set_setup() - set up for the IPC_SET command with shmctl()
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 */
416 * func_set() - check the functionality of the IPC_SET command with shmctl()
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()");
429 if ((buf
.shm_perm
.mode
& MODE_MASK
) !=
430 ((SHM_RW
| NEWMODE
) & MODE_MASK
)) {
431 tst_resm(TFAIL
, "new mode is incorrect");
435 if (!fail
&& save_time
>= buf
.shm_ctime
) {
436 tst_resm(TFAIL
, "change time is incorrect");
444 tst_resm(TPASS
, "new mode and change time are correct");
448 * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
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
);
461 tst_resm(TPASS
, "shared memory appears to be removed");
468 * sighandler() - handle signals, in this case SIGUSR1 is the only one expected
473 if (sig
!= SIGUSR1
) {
474 tst_resm(TINFO
, "received unexpected signal %d", sig
);
479 * setup() - performs all the ONE TIME setup for this test.
484 /* capture signals */
485 tst_sig(FORK
, sighandler
, cleanup
);
487 /* Pause if that option was specified */
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.
497 /* get an IPC resource key */
498 shmkey
= getipckey();
502 * cleanup() - performs all the ONE TIME cleanup for this test at completion
508 /* if it exists, remove the shared memory segment */
511 /* Remove the temporary directory */
515 * print timing stats if that option was specified.
516 * print errno log if that option was specified.
520 /* exit with return code appropriate for results */