/* * Copyright (c) Crackerjack Project., 2007-2008, Hitachi, Ltd * Copyright (c) 2017 Petr Vorel <pvorel@suse.cz> * * Authors: * Takahiro Yasui <takahiro.yasui.mp@hitachi.com>, * Yumiko Sugita <yumiko.sugita.yf@hitachi.com>, * Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <limits.h> #include <errno.h> #include "tst_test.h" #include "tst_safe_posix_ipc.h" static int fd, fd_root, fd_nonblock, fd_maxint = INT_MAX - 1, fd_invalid = -1; #include "mq.h" #define USER_DATA 0x12345678 static char *str_debug; static volatile sig_atomic_t notified, cmp_ok; static siginfo_t info; struct test_case { int *fd; int already_registered; int notify; int ret; int err; }; static struct test_case tcase[] = { { .fd = &fd, .notify = SIGEV_NONE, .ret = 0, .err = 0, }, { .fd = &fd, .notify = SIGEV_SIGNAL, .ret = 0, .err = 0, }, { .fd = &fd, .notify = SIGEV_THREAD, .ret = 0, .err = 0, }, { .fd = &fd_invalid, .notify = SIGEV_NONE, .ret = -1, .err = EBADF, }, { .fd = &fd_maxint, .notify = SIGEV_NONE, .ret = -1, .err = EBADF, }, { .fd = &fd_root, .notify = SIGEV_NONE, .ret = -1, .err = EBADF, }, { .fd = &fd, .notify = SIGEV_NONE, .already_registered = 1, .ret = -1, .err = EBUSY, }, }; static void sigfunc(int signo LTP_ATTRIBUTE_UNUSED, siginfo_t *si, void *data LTP_ATTRIBUTE_UNUSED) { if (str_debug) memcpy(&info, si, sizeof(info)); cmp_ok = si->si_code == SI_MESGQ && si->si_signo == SIGUSR1 && si->si_value.sival_int == USER_DATA && si->si_pid == getpid() && si->si_uid == getuid(); notified = 1; } static void tfunc(union sigval sv) { cmp_ok = sv.sival_int == USER_DATA; notified = 1; } static void do_test(unsigned int i) { struct sigaction sigact; struct test_case *tc = &tcase[i]; struct sigevent ev; ev.sigev_notify = tc->notify; notified = cmp_ok = 1; switch (tc->notify) { case SIGEV_SIGNAL: notified = cmp_ok = 0; ev.sigev_signo = SIGUSR1; ev.sigev_value.sival_int = USER_DATA; memset(&sigact, 0, sizeof(sigact)); sigact.sa_sigaction = sigfunc; sigact.sa_flags = SA_SIGINFO; if (sigaction(SIGUSR1, &sigact, NULL) == -1) { tst_res(TFAIL | TTERRNO, "sigaction failed"); return; } break; case SIGEV_THREAD: notified = cmp_ok = 0; ev.sigev_notify_function = tfunc; ev.sigev_notify_attributes = NULL; ev.sigev_value.sival_int = USER_DATA; break; } if (tc->already_registered && mq_notify(*tc->fd, &ev) == -1) { tst_res(TFAIL | TERRNO, "mq_notify(%d, %p) failed", fd, &ev); return; } TEST(mq_notify(*tc->fd, &ev)); if (TEST_RETURN < 0) { if (tc->err != TEST_ERRNO) tst_res(TFAIL | TTERRNO, "mq_notify failed unexpectedly, expected %s", tst_strerrno(tc->err)); else tst_res(TPASS | TTERRNO, "mq_notify failed expectedly"); /* unregister notification */ if (*tc->fd == fd) mq_notify(*tc->fd, NULL); return; } TEST(mq_timedsend(*tc->fd, smsg, MSG_LENGTH, 0, &((struct timespec){0}))); if (*tc->fd == fd) cleanup_queue(fd); if (TEST_RETURN < 0) { tst_res(TFAIL | TTERRNO, "mq_timedsend failed"); return; } while (!notified) usleep(10000); if (str_debug && tc->notify == SIGEV_SIGNAL) { tst_res(TINFO, "si_code E:%d,\tR:%d", info.si_code, SI_MESGQ); tst_res(TINFO, "si_signo E:%d,\tR:%d", info.si_signo, SIGUSR1); tst_res(TINFO, "si_value E:0x%x,\tR:0x%x", info.si_value.sival_int, USER_DATA); tst_res(TINFO, "si_pid E:%d,\tR:%d", info.si_pid, getpid()); tst_res(TINFO, "si_uid E:%d,\tR:%d", info.si_uid, getuid()); } if (TEST_RETURN < 0) { if (tc->err != TEST_ERRNO) tst_res(TFAIL | TTERRNO, "mq_timedsend failed unexpectedly, expected %s", tst_strerrno(tc->err)); else tst_res(TPASS | TTERRNO, "mq_timedsend failed expectedly"); return; } if (tc->ret != TEST_RETURN) { tst_res(TFAIL, "mq_timedsend returned %ld, expected %d", TEST_RETURN, tc->ret); return; } tst_res(TPASS, "mq_notify and mq_timedsend exited expectedly"); } static struct tst_option options[] = { {"d", &str_debug, "Print debug messages"}, {NULL, NULL, NULL} }; static struct tst_test test = { .tcnt = ARRAY_SIZE(tcase), .test = do_test, .options = options, .setup = setup_common, .cleanup = cleanup_common, };