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
51char *TCID = "mq_notify01";
52int testno;
53int TST_TOTAL = 1;
54
55static void cleanup(void)
56{
57	tst_rmdir();
58}
59
60static void setup(void)
61{
62	TEST_PAUSE;
63	tst_tmpdir();
64}
65
66#define SYSCALL_NAME    "mq_notify"
67
68static int opt_debug;
69static char *progname;
70static int notified;
71static int cmp_ok;
72
73enum test_type {
74	NORMAL,
75	FD_NONE,
76	FD_NOT_EXIST,
77	FD_FILE,
78	ALREADY_REGISTERED,
79};
80
81struct 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
92static 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
137static 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
156static void tfunc(union sigval sv)
157{
158	cmp_ok = sv.sival_int == USER_DATA;
159	notified = 1;
160}
161
162static 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
270TEST_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
278EXIT:
279	if (fd >= 0) {
280		close(fd);
281		mq_unlink(QUEUE_NAME);
282	}
283
284	return result;
285}
286
287static 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
297int 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