Home | History | Annotate | Download | only in mq_notify
      1 /*
      2  * Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd
      3  *          Author(s): Takahiro Yasui <takahiro.yasui.mp (at) hitachi.com>,
      4  *		       Yumiko Sugita <yumiko.sugita.yf (at) hitachi.com>,
      5  *		       Satoshi Fujiwara <sa-fuji (at) sdl.hitachi.co.jp>
      6  * Copyright (c) 2016 Linux Test Project
      7  *
      8  * This program is free software;  you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation; either version 2 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     16  * the GNU General Public License for more details.
     17  */
     18 #define _XOPEN_SOURCE 600
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 #include <limits.h>
     22 #include <errno.h>
     23 #include <unistd.h>
     24 #include <string.h>
     25 #include <mqueue.h>
     26 #include <signal.h>
     27 #include <stdlib.h>
     28 #include <fcntl.h>
     29 
     30 #include "tst_test.h"
     31 #include "tst_safe_posix_ipc.h"
     32 
     33 #define MAX_MSGSIZE     8192
     34 #define MSG_SIZE	16
     35 #define USER_DATA       0x12345678
     36 #define QUEUE_NAME	"/test_mqueue"
     37 
     38 static char *str_debug;
     39 static char smsg[MAX_MSGSIZE];
     40 
     41 static volatile sig_atomic_t notified, cmp_ok;
     42 static siginfo_t info;
     43 
     44 enum test_type {
     45 	NORMAL,
     46 	FD_NONE,
     47 	FD_NOT_EXIST,
     48 	FD_FILE,
     49 	ALREADY_REGISTERED,
     50 };
     51 
     52 struct test_case {
     53 	int notify;
     54 	int ttype;
     55 	const char *desc;
     56 	int ret;
     57 	int err;
     58 };
     59 
     60 #define TYPE_NAME(x) .ttype = x, .desc = #x
     61 static struct test_case tcase[] = {
     62 	{
     63 		TYPE_NAME(NORMAL),
     64 		.notify = SIGEV_NONE,
     65 		.ret = 0,
     66 		.err = 0,
     67 	},
     68 	{
     69 		TYPE_NAME(NORMAL),
     70 		.notify = SIGEV_SIGNAL,
     71 		.ret = 0,
     72 		.err = 0,
     73 	},
     74 	{
     75 		TYPE_NAME(NORMAL),
     76 		.notify = SIGEV_THREAD,
     77 		.ret = 0,
     78 		.err = 0,
     79 	},
     80 	{
     81 		TYPE_NAME(FD_NONE),
     82 		.notify = SIGEV_NONE,
     83 		.ret = -1,
     84 		.err = EBADF,
     85 	},
     86 	{
     87 		TYPE_NAME(FD_NOT_EXIST),
     88 		.notify = SIGEV_NONE,
     89 		.ret = -1,
     90 		.err = EBADF,
     91 	},
     92 	{
     93 		TYPE_NAME(FD_FILE),
     94 		.notify = SIGEV_NONE,
     95 		.ret = -1,
     96 		.err = EBADF,
     97 	},
     98 	{
     99 		TYPE_NAME(ALREADY_REGISTERED),
    100 		.notify = SIGEV_NONE,
    101 		.ret = -1,
    102 		.err = EBUSY,
    103 	},
    104 };
    105 
    106 static void setup(void)
    107 {
    108 	int i;
    109 
    110 	for (i = 0; i < MSG_SIZE; i++)
    111 		smsg[i] = i;
    112 }
    113 static void sigfunc(int signo LTP_ATTRIBUTE_UNUSED, siginfo_t *si,
    114 	void *data LTP_ATTRIBUTE_UNUSED)
    115 {
    116 	if (str_debug)
    117 		memcpy(&info, si, sizeof(info));
    118 
    119 	cmp_ok = si->si_code == SI_MESGQ &&
    120 	    si->si_signo == SIGUSR1 &&
    121 	    si->si_value.sival_int == USER_DATA &&
    122 	    si->si_pid == getpid() && si->si_uid == getuid();
    123 	notified = 1;
    124 }
    125 
    126 static void tfunc(union sigval sv)
    127 {
    128 	cmp_ok = sv.sival_int == USER_DATA;
    129 	notified = 1;
    130 }
    131 
    132 static void do_test(unsigned int i)
    133 {
    134 	int rc, fd = -1;
    135 	struct sigevent ev;
    136 	struct sigaction sigact;
    137 	struct timespec abs_timeout;
    138 	struct test_case *tc = &tcase[i];
    139 
    140 	notified = cmp_ok = 1;
    141 
    142 	/* Don't timeout. */
    143 	abs_timeout.tv_sec = 0;
    144 	abs_timeout.tv_nsec = 0;
    145 
    146 	/*
    147 	 * When test ended with SIGTERM etc, mq descriptor is left remains.
    148 	 * So we delete it first.
    149 	 */
    150 	mq_unlink(QUEUE_NAME);
    151 
    152 	switch (tc->ttype) {
    153 	case FD_NONE:
    154 		break;
    155 	case FD_NOT_EXIST:
    156 		fd = INT_MAX - 1;
    157 		break;
    158 	case FD_FILE:
    159 		fd = open("/", O_RDONLY);
    160 		if (fd < 0) {
    161 			tst_res(TBROK | TERRNO, "can't open \"/\".");
    162 			goto CLEANUP;
    163 		}
    164 		break;
    165 	default:
    166 		fd = SAFE_MQ_OPEN(QUEUE_NAME, O_CREAT | O_EXCL | O_RDWR, S_IRWXU, NULL);
    167 	}
    168 
    169 	ev.sigev_notify = tc->notify;
    170 
    171 	switch (tc->notify) {
    172 	case SIGEV_SIGNAL:
    173 		notified = cmp_ok = 0;
    174 		ev.sigev_signo = SIGUSR1;
    175 		ev.sigev_value.sival_int = USER_DATA;
    176 
    177 		memset(&sigact, 0, sizeof(sigact));
    178 		sigact.sa_sigaction = sigfunc;
    179 		sigact.sa_flags = SA_SIGINFO;
    180 		rc = sigaction(SIGUSR1, &sigact, NULL);
    181 		break;
    182 	case SIGEV_THREAD:
    183 		notified = cmp_ok = 0;
    184 		ev.sigev_notify_function = tfunc;
    185 		ev.sigev_notify_attributes = NULL;
    186 		ev.sigev_value.sival_int = USER_DATA;
    187 		break;
    188 	}
    189 
    190 	if (tc->ttype == ALREADY_REGISTERED) {
    191 		rc = mq_notify(fd, &ev);
    192 		if (rc < 0) {
    193 			tst_res(TBROK | TERRNO, "mq_notify failed");
    194 			goto CLEANUP;
    195 		}
    196 	}
    197 
    198 	/* test */
    199 	TEST(mq_notify(fd, &ev));
    200 	if (TEST_RETURN >= 0) {
    201 		rc = mq_timedsend(fd, smsg, MSG_SIZE, 0, &abs_timeout);
    202 		if (rc < 0) {
    203 			tst_res(TFAIL | TTERRNO, "mq_timedsend failed");
    204 			goto CLEANUP;
    205 		}
    206 
    207 		while (!notified)
    208 			usleep(10000);
    209 
    210 		if (str_debug && tc->notify == SIGEV_SIGNAL) {
    211 			tst_res(TINFO, "si_code  E:%d,\tR:%d",
    212 				info.si_code, SI_MESGQ);
    213 			tst_res(TINFO, "si_signo E:%d,\tR:%d",
    214 				info.si_signo, SIGUSR1);
    215 			tst_res(TINFO, "si_value E:0x%x,\tR:0x%x",
    216 				info.si_value.sival_int, USER_DATA);
    217 			tst_res(TINFO, "si_pid   E:%d,\tR:%d",
    218 				info.si_pid, getpid());
    219 			tst_res(TINFO, "si_uid   E:%d,\tR:%d",
    220 				info.si_uid, getuid());
    221 		}
    222 	}
    223 
    224 	if ((TEST_RETURN != 0 && TEST_ERRNO != tc->err) || !cmp_ok) {
    225 		tst_res(TFAIL | TTERRNO, "%s r/w check returned: %ld, "
    226 			"expected: %d, expected errno: %s (%d)", tc->desc,
    227 			TEST_RETURN, tc->ret, tst_strerrno(tc->err), tc->err);
    228 	} else {
    229 		tst_res(TPASS | TTERRNO, "%s returned: %ld",
    230 			tc->desc, TEST_RETURN);
    231 	}
    232 
    233 CLEANUP:
    234 	if (fd >= 0) {
    235 		close(fd);
    236 		mq_unlink(QUEUE_NAME);
    237 	}
    238 }
    239 
    240 static struct tst_option options[] = {
    241 	{"d", &str_debug, "Print debug messages"},
    242 	{NULL, NULL, NULL}
    243 };
    244 
    245 static struct tst_test test = {
    246 	.tid = "mq_notify01",
    247 	.tcnt = ARRAY_SIZE(tcase),
    248 	.test = do_test,
    249 	.options = options,
    250 	.setup = setup,
    251 };
    252