1 /* 2 * 3 * Copyright (c) International Business Machines Corp., 2001 4 * 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. 9 * 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. 14 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 /* 21 * NAME 22 * msgsnd05.c 23 * 24 * DESCRIPTION 25 * msgsnd05 - test for EINTR error 26 * 27 * ALGORITHM 28 * create a message queue with read/write permissions 29 * initialize a message buffer with a known message and type 30 * enqueue the message in a loop until the queue is full 31 * loop if that option was specified 32 * fork a child process 33 * child attempts to enqueue a message to the full queue and sleeps 34 * parent sends a SIGHUP to the child then waits for the child to complete 35 * child get a return from msgsnd() 36 * check the errno value 37 * issue a PASS message if we get EINTR 38 * otherwise, the tests fails 39 * issue a FAIL message 40 * child exits, parent calls cleanup 41 * 42 * USAGE: <for command-line> 43 * msgsnd05 [-c n] [-e] [-i n] [-I x] [-P x] [-t] 44 * where, -c n : Run n copies concurrently. 45 * -e : Turn on errno logging. 46 * -i n : Execute test n times. 47 * -I x : Execute test for x seconds. 48 * -P x : Pause for x seconds between iterations. 49 * -t : Turn on syscall timing. 50 * 51 * HISTORY 52 * 03/2001 - Written by Wayne Boyer 53 * 14/03/2008 Matthieu Fertr (Matthieu.Fertre (at) irisa.fr) 54 * - Fix concurrency issue. Due to the use of usleep function to 55 * synchronize processes, synchronization issues can occur on a loaded 56 * system. Fix this by using pipes to synchronize processes. 57 * 58 * RESTRICTIONS 59 * none 60 */ 61 62 #include "test.h" 63 64 #include "ipcmsg.h" 65 66 #include <sys/types.h> 67 #include <sys/wait.h> 68 69 void do_child(void); 70 void cleanup(void); 71 void setup(void); 72 #ifdef UCLINUX 73 #define PIPE_NAME "msgsnd05" 74 void do_child_uclinux(void); 75 #endif 76 77 char *TCID = "msgsnd05"; 78 int TST_TOTAL = 1; 79 80 int msg_q_1 = -1; /* The message queue id created in setup */ 81 82 MSGBUF msg_buf; 83 84 int main(int ac, char **av) 85 { 86 int lc; 87 pid_t c_pid; 88 89 tst_parse_opts(ac, av, NULL, NULL); 90 91 #ifdef UCLINUX 92 maybe_run_child(&do_child_uclinux, "d", &msg_q_1); 93 #endif 94 95 setup(); /* global setup */ 96 97 /* The following loop checks looping state if -i option given */ 98 99 for (lc = 0; TEST_LOOPING(lc); lc++) { 100 /* reset tst_count in case we are looping */ 101 tst_count = 0; 102 103 /* 104 * fork a child that will attempt to write a message 105 * to the queue without IPC_NOWAIT 106 */ 107 if ((c_pid = FORK_OR_VFORK()) == -1) { 108 tst_brkm(TBROK, cleanup, "could not fork"); 109 } 110 111 if (c_pid == 0) { /* child */ 112 /* 113 * Attempt to write another message to the full queue. 114 * Without the IPC_NOWAIT flag, the child sleeps 115 */ 116 117 #ifdef UCLINUX 118 if (self_exec(av[0], "d", msg_q_1) < 0) { 119 tst_brkm(TBROK, cleanup, "could not self_exec"); 120 } 121 #else 122 do_child(); 123 #endif 124 } else { 125 TST_PROCESS_STATE_WAIT(cleanup, c_pid, 'S'); 126 127 /* send a signal that must be caught to the child */ 128 if (kill(c_pid, SIGHUP) == -1) 129 tst_brkm(TBROK, cleanup, "kill failed"); 130 131 waitpid(c_pid, NULL, 0); 132 } 133 } 134 135 cleanup(); 136 137 tst_exit(); 138 } 139 140 /* 141 * do_child() 142 */ 143 void do_child(void) 144 { 145 TEST(msgsnd(msg_q_1, &msg_buf, MSGSIZE, 0)); 146 147 if (TEST_RETURN != -1) { 148 tst_resm(TFAIL, "call succeeded when error expected"); 149 exit(-1); 150 } 151 152 switch (TEST_ERRNO) { 153 case EINTR: 154 tst_resm(TPASS, "expected failure - errno = %d : %s", 155 TEST_ERRNO, strerror(TEST_ERRNO)); 156 break; 157 default: 158 tst_resm(TFAIL, 159 "call failed with an unexpected error - %d : %s", 160 TEST_ERRNO, strerror(TEST_ERRNO)); 161 break; 162 } 163 164 exit(0); 165 } 166 167 void sighandler(int sig) 168 { 169 if (sig == SIGHUP) 170 return; 171 else 172 tst_brkm(TBROK, NULL, "received unexpected signal %d", sig); 173 } 174 175 #ifdef UCLINUX 176 /* 177 * do_child_uclinux() - capture signals, initialize buffer, then run do_child() 178 */ 179 void do_child_uclinux(void) 180 { 181 /* initialize the message buffer */ 182 init_buf(&msg_buf, MSGTYPE, MSGSIZE); 183 184 tst_sig(FORK, sighandler, cleanup); 185 186 do_child(); 187 } 188 #endif 189 190 /* 191 * setup() - performs all the ONE TIME setup for this test. 192 */ 193 void setup(void) 194 { 195 /* capture signals in our own handler */ 196 tst_sig(FORK, sighandler, cleanup); 197 198 TEST_PAUSE; 199 200 /* 201 * Create a temporary directory and cd into it. 202 * This helps to ensure that a unique msgkey is created. 203 * See ../lib/libipc.c for more information. 204 */ 205 tst_tmpdir(); 206 207 msgkey = getipckey(); 208 209 /* create a message queue with read/write permission */ 210 if ((msg_q_1 = msgget(msgkey, IPC_CREAT | IPC_EXCL | MSG_RW)) == -1) { 211 tst_brkm(TBROK, cleanup, "Can't create message queue"); 212 } 213 214 /* initialize the message buffer */ 215 init_buf(&msg_buf, MSGTYPE, MSGSIZE); 216 217 /* write messages to the queue until it is full */ 218 while (msgsnd(msg_q_1, &msg_buf, MSGSIZE, IPC_NOWAIT) != -1) { 219 msg_buf.mtype += 1; 220 } 221 } 222 223 /* 224 * cleanup() - performs all the ONE TIME cleanup for this test at completion 225 * or premature exit. 226 */ 227 void cleanup(void) 228 { 229 /* if it exists, remove the message queue that was created */ 230 rm_queue(msg_q_1); 231 232 tst_rmdir(); 233 234 } 235