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