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