1 /******************************************************************************/
2 /* Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd */
3 /* Author(s): Takahiro Yasui <takahiro.yasui.mp@hitachi.com>, */
4 /* Yumiko Sugita <yumiko.sugita.yf@hitachi.com>, */
5 /* Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp> */
6 /* Porting from Crackerjack to LTP is done by */
7 /* Manas Kumar Nayak maknayak@in.ibm.com> */
8 /* */
9 /* This program is free software; you can redistribute it and/or modify */
10 /* it under the terms of the GNU General Public License as published by */
11 /* the Free Software Foundation; either version 2 of the License, or */
12 /* (at your option) any later version. */
13 /* */
14 /* This program is distributed in the hope that it will be useful, */
15 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
16 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
17 /* the GNU General Public License for more details. */
18 /* */
19 /* You should have received a copy of the GNU General Public License */
20 /* along with this program; if not, write to the Free Software Foundation, */
21 /* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
22 /* */
23 /******************************************************************************/
24 /******************************************************************************/
25 /* */
26 /* Description: This tests the mq_notify() syscall */
27 /* */
28 /******************************************************************************/
29 #define _XOPEN_SOURCE 600
30 #include <sys/syscall.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/uio.h>
34 #include <getopt.h>
35 #include <libgen.h>
36 #include <limits.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <mqueue.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45
46 #include "../utils/include_j_h.h"
47
48 #include "test.h"
49 #include "linux_syscall_numbers.h"
50
51 char *TCID = "mq_notify01";
52 int testno;
53 int TST_TOTAL = 1;
54
cleanup(void)55 static void cleanup(void)
56 {
57 tst_rmdir();
58 }
59
setup(void)60 static void setup(void)
61 {
62 TEST_PAUSE;
63 tst_tmpdir();
64 }
65
66 #define SYSCALL_NAME "mq_notify"
67
68 static int opt_debug;
69 static char *progname;
70 static int notified;
71 static int cmp_ok;
72
73 enum test_type {
74 NORMAL,
75 FD_NONE,
76 FD_NOT_EXIST,
77 FD_FILE,
78 ALREADY_REGISTERED,
79 };
80
81 struct test_case {
82 int notify;
83 int ttype;
84 int ret;
85 int err;
86 };
87
88 #define MAX_MSGSIZE 8192
89 #define MSG_SIZE 16
90 #define USER_DATA 0x12345678
91
92 static struct test_case tcase[] = {
93 { // case00
94 .ttype = NORMAL,
95 .notify = SIGEV_NONE,
96 .ret = 0,
97 .err = 0,
98 },
99 { // case01
100 .ttype = NORMAL,
101 .notify = SIGEV_SIGNAL,
102 .ret = 0,
103 .err = 0,
104 },
105 { // case02
106 .ttype = NORMAL,
107 .notify = SIGEV_THREAD,
108 .ret = 0,
109 .err = 0,
110 },
111 { // case03
112 .ttype = FD_NONE,
113 .notify = SIGEV_NONE,
114 .ret = -1,
115 .err = EBADF,
116 },
117 { // case04
118 .ttype = FD_NOT_EXIST,
119 .notify = SIGEV_NONE,
120 .ret = -1,
121 .err = EBADF,
122 },
123 { // case05
124 .ttype = FD_FILE,
125 .notify = SIGEV_NONE,
126 .ret = -1,
127 .err = EBADF,
128 },
129 { // case06
130 .ttype = ALREADY_REGISTERED,
131 .notify = SIGEV_NONE,
132 .ret = -1,
133 .err = EBUSY,
134 },
135 };
136
sigfunc(int signo,siginfo_t * info,void * data)137 static void sigfunc(int signo, siginfo_t * info, void *data)
138 {
139 if (opt_debug) {
140 tst_resm(TINFO, "si_code E:%d,\tR:%d", info->si_code,
141 SI_MESGQ);
142 tst_resm(TINFO, "si_signo E:%d,\tR:%d", info->si_signo,
143 SIGUSR1);
144 tst_resm(TINFO, "si_value E:0x%x,\tR:0x%x",
145 info->si_value.sival_int, USER_DATA);
146 tst_resm(TINFO, "si_pid E:%d,\tR:%d", info->si_pid, getpid());
147 tst_resm(TINFO, "si_uid E:%d,\tR:%d", info->si_uid, getuid());
148 }
149 cmp_ok = info->si_code == SI_MESGQ &&
150 info->si_signo == SIGUSR1 &&
151 info->si_value.sival_int == USER_DATA &&
152 info->si_pid == getpid() && info->si_uid == getuid();
153 notified = 1;
154 }
155
tfunc(union sigval sv)156 static void tfunc(union sigval sv)
157 {
158 cmp_ok = sv.sival_int == USER_DATA;
159 notified = 1;
160 }
161
do_test(struct test_case * tc)162 static int do_test(struct test_case *tc)
163 {
164 int sys_ret;
165 int sys_errno;
166 int result = RESULT_OK;
167 int rc, i, fd = -1;
168 struct sigevent ev;
169 struct sigaction sigact;
170 struct timespec abs_timeout;
171 char smsg[MAX_MSGSIZE];
172
173 notified = cmp_ok = 1;
174
175 /* Don't timeout. */
176 abs_timeout.tv_sec = 0;
177 abs_timeout.tv_nsec = 0;
178
179 /*
180 * When test ended with SIGTERM etc, mq discriptor is left remains.
181 * So we delete it first.
182 */
183 mq_unlink(QUEUE_NAME);
184
185 switch (tc->ttype) {
186 case FD_NOT_EXIST:
187 fd = INT_MAX - 1;
188 /* fallthrough */
189 case FD_NONE:
190 break;
191 case FD_FILE:
192 TEST(fd = open("/", O_RDONLY));
193 if (TEST_RETURN < 0) {
194 tst_resm(TFAIL, "can't open \"/\".");
195 result = 1;
196 goto EXIT;
197 }
198 break;
199 default:
200 /*
201 * Open message queue
202 */
203 TEST(fd =
204 mq_open(QUEUE_NAME, O_CREAT | O_EXCL | O_RDWR, S_IRWXU,
205 NULL));
206 if (TEST_RETURN < 0) {
207 tst_resm(TFAIL | TTERRNO, "mq_open failed");
208 result = 1;
209 goto EXIT;
210 }
211 }
212
213 /*
214 * Set up struct sigevent
215 */
216 ev.sigev_notify = tc->notify;
217
218 switch (tc->notify) {
219 case SIGEV_SIGNAL:
220 notified = cmp_ok = 0;
221 ev.sigev_signo = SIGUSR1;
222 ev.sigev_value.sival_int = USER_DATA;
223
224 memset(&sigact, 0, sizeof(sigact));
225 sigact.sa_sigaction = sigfunc;
226 sigact.sa_flags = SA_SIGINFO;
227 TEST(rc = sigaction(SIGUSR1, &sigact, NULL));
228 break;
229 case SIGEV_THREAD:
230 notified = cmp_ok = 0;
231 ev.sigev_notify_function = tfunc;
232 ev.sigev_notify_attributes = NULL;
233 ev.sigev_value.sival_int = USER_DATA;
234 break;
235 }
236
237 if (tc->ttype == ALREADY_REGISTERED) {
238 TEST(rc = mq_notify(fd, &ev));
239 if (TEST_RETURN < 0) {
240 tst_resm(TFAIL | TTERRNO, "mq_notify failed");
241 result = 1;
242 goto EXIT;
243 }
244 }
245
246 /*
247 * Execute system call
248 */
249 errno = 0;
250 sys_ret = mq_notify(fd, &ev);
251 sys_errno = errno;
252 if (sys_ret < 0)
253 goto TEST_END;
254
255 /*
256 * Prepare send message
257 */
258 for (i = 0; i < MSG_SIZE; i++)
259 smsg[i] = i;
260 TEST(rc = mq_timedsend(fd, smsg, MSG_SIZE, 0, &abs_timeout));
261 if (rc < 0) {
262 tst_resm(TFAIL | TTERRNO, "mq_timedsend failed");
263 result = 1;
264 goto EXIT;
265 }
266
267 while (!notified)
268 usleep(10000);
269
270 TEST_END:
271 /*
272 * Check results
273 */
274 result |= (sys_ret != 0 && sys_errno != tc->err) || !cmp_ok;
275 PRINT_RESULT_CMP(sys_ret >= 0, tc->ret, tc->err, sys_ret, sys_errno,
276 cmp_ok);
277
278 EXIT:
279 if (fd >= 0) {
280 close(fd);
281 mq_unlink(QUEUE_NAME);
282 }
283
284 return result;
285 }
286
usage(const char * progname)287 static void usage(const char *progname)
288 {
289 tst_resm(TINFO, "usage: %s [options]", progname);
290 tst_resm(TINFO, "This is a regression test program of %s system call.",
291 SYSCALL_NAME);
292 tst_resm(TINFO, "options:");
293 tst_resm(TINFO, " -d --debug Show debug messages");
294 tst_resm(TINFO, " -h --help Show this message");
295 }
296
main(int ac,char ** av)297 int main(int ac, char **av)
298 {
299 int result = RESULT_OK;
300 int c;
301 int i;
302 int lc;
303
304 struct option long_options[] = {
305 {"debug", no_argument, 0, 'd'},
306 {"help", no_argument, 0, 'h'},
307 {NULL, 0, NULL, 0}
308 };
309
310 progname = basename(av[0]);
311
312 tst_parse_opts(ac, av, NULL, NULL);
313
314 setup();
315
316 for (lc = 0; TEST_LOOPING(lc); ++lc) {
317 tst_count = 0;
318 for (testno = 0; testno < TST_TOTAL; ++testno) {
319 TEST(c = getopt_long(ac, av, "dh", long_options, NULL));
320 while (TEST_RETURN != -1) {
321 switch (c) {
322 case 'd':
323 opt_debug = 1;
324 break;
325 default:
326 usage(progname);
327 }
328 }
329
330 if (ac != optind) {
331 tst_resm(TINFO, "Options are not match.");
332 usage(progname);
333 }
334
335 for (i = 0; i < (int)(sizeof(tcase) / sizeof(tcase[0]));
336 i++) {
337 int ret;
338 tst_resm(TINFO, "(case%02d) START", i);
339 ret = do_test(&tcase[i]);
340 tst_resm(TINFO, "(case%02d) END => %s",
341 i, (ret == 0) ? "OK" : "NG");
342 result |= ret;
343 }
344
345 switch (result) {
346 case RESULT_OK:
347 tst_resm(TPASS, "mq_notify call succeeded");
348 break;
349
350 default:
351 tst_brkm(TFAIL, cleanup, "mq_notify failed");
352 break;
353 }
354
355 }
356 }
357 cleanup();
358 tst_exit();
359 }
360