Home | History | Annotate | Download | only in pidns
      1 /*
      2 * Copyright (c) Bull S.A.S. 2008
      3 * This program is free software; you can redistribute it and/or modify
      4 * it under the terms of the GNU General Public License as published by
      5 * the Free Software Foundation; either version 2 of the License, or
      6 * (at your option) any later version.
      7 * This program is distributed in the hope that it will be useful,
      8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
     10 * the GNU General Public License for more details.
     11 * You should have received a copy of the GNU General Public License
     12 * along with this program; if not, write to the Free Software
     13 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     14 *
     15 ***************************************************************************
     16 * File: pidns31.c
     17 *
     18 *   Description:
     19 *    This testcase checks if the si_pid is correctly set when a process
     20 *    that has registered for notification on a posix mqueue is in an
     21 *    ancestor namespace wrt the process that sends a message to that posix
     22 *    mqueue.
     23 *
     24 *   Test Assertion & Strategy:
     25 *    Parent                                   Child
     26 *    --------------------------------------------------------------------------
     27 *    Create a POSIX mqueue.
     28 *    Create a PID namespace container.
     29 *    Register for notification when a
     30 *       message arrives in that mqueue
     31 *    Install a handler for SIGUSR1.
     32 *                                             Open that mqueue for writing
     33 *                                             Write something to the mqueue.
     34 *    Inside the handler, check that
     35 *       si_pid is set to the child's pid
     36 *
     37 *   Usage: <for command-line>
     38 *    pidns31
     39 *
     40 *   History:
     41 *    DATE      NAME                             DESCRIPTION
     42 *    04/12/08  Nadia Derbey               Creation of this test.
     43 *              <Nadia.Derbey (at) bull.net>
     44 *
     45 ******************************************************************************/
     46 #ifndef _GNU_SOURCE
     47 #define _GNU_SOURCE
     48 #endif
     49 #include <sys/wait.h>
     50 #include <sys/types.h>
     51 #include <signal.h>
     52 #include <stdlib.h>
     53 #include <unistd.h>
     54 #include <stdio.h>
     55 #include <mqueue.h>
     56 #include "lapi/syscalls.h"
     57 #include "pidns_helper.h"
     58 #include "test.h"
     59 
     60 char *TCID = "pidns31";
     61 int TST_TOTAL = 1;
     62 
     63 char *mqname = "mq1";
     64 int result = TFAIL;
     65 
     66 int father_to_child[2];
     67 
     68 #define CHILD_PID       1
     69 #define PARENT_PID      0
     70 
     71 #define MSG      "HOW ARE YOU"
     72 #define MSG_PRIO 1
     73 
     74 #define NO_STEP -1
     75 #define F_STEP_0 0x00
     76 #define F_STEP_1 0x01
     77 #define F_STEP_2 0x02
     78 #define F_STEP_3 0x03
     79 #define C_STEP_0 0x10
     80 #define C_STEP_1 0x11
     81 
     82 struct notify_info {
     83 	mqd_t mqd;
     84 	pid_t pid;
     85 };
     86 
     87 static void remove_pipe(int *fd)
     88 {
     89 	close(fd[0]);
     90 	close(fd[1]);
     91 }
     92 
     93 static void remove_mqueue(mqd_t mqd)
     94 {
     95 	mq_close(mqd);
     96 	ltp_syscall(__NR_mq_unlink, mqname);
     97 }
     98 
     99 /*
    100  * steps F_STEP_XX : called from main
    101  * steps C_STEP_XX : called from child_fn
    102  */
    103 static void cleanup_resources(int step, mqd_t mqd)
    104 {
    105 	switch (step) {
    106 	case C_STEP_1:
    107 		close(father_to_child[0]);
    108 		/* fall through */
    109 	case C_STEP_0:
    110 		mq_close(mqd);
    111 		break;
    112 
    113 	case F_STEP_3:
    114 		remove_mqueue(mqd);
    115 		close(father_to_child[1]);
    116 		break;
    117 
    118 	case F_STEP_2:
    119 		ltp_syscall(__NR_mq_notify, mqd, NULL);
    120 		/* fall through */
    121 	case F_STEP_1:
    122 		remove_mqueue(mqd);
    123 		/* fall through */
    124 	case F_STEP_0:
    125 		remove_pipe(father_to_child);
    126 		break;
    127 	default:
    128 		tst_resm(TWARN, "Unknown code - no resource removed.");
    129 		break;
    130 	}
    131 }
    132 
    133 /*
    134  * cleanup_mqueue() - performs all ONE TIME cleanup for this test at
    135  *             completion or premature exit.
    136  * step == -1 means no local resource to remove.
    137  */
    138 void cleanup_mqueue(int result, int step, mqd_t mqd)
    139 {
    140 	if (step != NO_STEP)
    141 		cleanup_resources(step, mqd);
    142 
    143 	tst_exit();
    144 }
    145 
    146 /*
    147  * child_fn() - Inside container
    148  */
    149 int child_fn(void *arg)
    150 {
    151 	pid_t pid, ppid;
    152 	mqd_t mqd;
    153 	char buf[5];
    154 
    155 	/* Set process id and parent pid */
    156 	pid = getpid();
    157 	ppid = getppid();
    158 
    159 	if (pid != CHILD_PID || ppid != PARENT_PID) {
    160 		tst_resm(TBROK, "cinit: pidns is not created");
    161 		cleanup_mqueue(TBROK, NO_STEP, 0);
    162 	}
    163 
    164 	/* Close the appropriate end of pipe */
    165 	close(father_to_child[1]);
    166 
    167 	/* Is parent ready to receive a message? */
    168 	read(father_to_child[0], buf, 5);
    169 	if (strcmp(buf, "f:ok")) {
    170 		tst_resm(TBROK, "cinit: parent did not send the message!");
    171 		cleanup_mqueue(TBROK, NO_STEP, 0);
    172 	}
    173 	tst_resm(TINFO, "cinit: my father is ready to receive a message");
    174 
    175 	mqd = ltp_syscall(__NR_mq_open, mqname, O_WRONLY, 0, NULL);
    176 	if (mqd == (mqd_t) - 1) {
    177 		tst_resm(TBROK, "cinit: mq_open() failed (%s)",
    178 			 strerror(errno));
    179 		cleanup_mqueue(TBROK, NO_STEP, 0);
    180 	}
    181 	tst_resm(TINFO, "cinit: mq_open succeeded");
    182 
    183 	if (mq_send(mqd, MSG, strlen(MSG), MSG_PRIO) == (mqd_t) - 1) {
    184 		tst_resm(TBROK, "cinit: mq_send() failed (%s)",
    185 			 strerror(errno));
    186 		cleanup_mqueue(TBROK, C_STEP_0, mqd);
    187 	}
    188 	tst_resm(TINFO, "cinit: mq_send() succeeded");
    189 
    190 	/* Cleanup and exit */
    191 	cleanup_resources(C_STEP_1, mqd);
    192 	exit(0);
    193 }
    194 
    195 /*
    196  * father_signal_handler()
    197  */
    198 static void father_signal_handler(int sig, siginfo_t * si, void *unused)
    199 {
    200 	char buf[256];
    201 	struct mq_attr attr;
    202 	struct notify_info *info;
    203 
    204 	if (si->si_signo != SIGUSR1) {
    205 		tst_resm(TBROK, "father: received %s unexpectedly",
    206 			 strsignal(si->si_signo));
    207 		return;
    208 	}
    209 
    210 	if (si->si_code != SI_MESGQ) {
    211 		tst_resm(TBROK, "father: expected signal code SI_MESGQ - "
    212 			 "Got %d", si->si_code);
    213 		return;
    214 	}
    215 
    216 	if (!si->si_ptr) {
    217 		tst_resm(TBROK, "father: expected si_ptr - Got NULL");
    218 		return;
    219 	}
    220 
    221 	info = (struct notify_info *)si->si_ptr;
    222 
    223 	if (si->si_pid != info->pid) {
    224 		tst_resm(TFAIL,
    225 			 "father: expected signal originator PID = %d - Got %d",
    226 			 info->pid, si->si_pid);
    227 		return;
    228 	}
    229 
    230 	tst_resm(TPASS, "father: signal originator PID = %d", si->si_pid);
    231 	result = TPASS;
    232 
    233 	/*
    234 	 * Now read the message - Be silent on errors since this is not the
    235 	 * test purpose.
    236 	 */
    237 	if (!mq_getattr(info->mqd, &attr))
    238 		mq_receive(info->mqd, buf, attr.mq_msgsize, NULL);
    239 }
    240 
    241 static void setup(void)
    242 {
    243 	tst_require_root();
    244 	check_newpid();
    245 }
    246 
    247 /***********************************************************************
    248 *   M A I N
    249 ***********************************************************************/
    250 
    251 int main(int argc, char *argv[])
    252 {
    253 	pid_t cpid;
    254 	mqd_t mqd;
    255 	struct sigevent notif;
    256 	struct sigaction sa;
    257 	int status;
    258 	struct notify_info info;
    259 
    260 	setup();
    261 
    262 	if (pipe(father_to_child) == -1) {
    263 		tst_resm(TBROK, "parent: pipe() failed. aborting!");
    264 		cleanup_mqueue(TBROK, NO_STEP, 0);
    265 	}
    266 
    267 	ltp_syscall(__NR_mq_unlink, mqname);
    268 	mqd =
    269 	    ltp_syscall(__NR_mq_open, mqname, O_RDWR | O_CREAT | O_EXCL, 0777,
    270 		    NULL);
    271 	if (mqd == (mqd_t) - 1) {
    272 		tst_resm(TBROK, "parent: mq_open() failed (%s)",
    273 			 strerror(errno));
    274 		cleanup_mqueue(TBROK, F_STEP_0, 0);
    275 	}
    276 	tst_resm(TINFO, "parent: successfully created posix mqueue");
    277 
    278 	/* container creation on PID namespace */
    279 	cpid = ltp_clone_quick(CLONE_NEWPID | SIGCHLD, child_fn, NULL);
    280 	if (cpid < 0) {
    281 		tst_resm(TBROK, "parent: clone() failed(%s)", strerror(errno));
    282 		cleanup_mqueue(TBROK, F_STEP_1, mqd);
    283 	}
    284 	tst_resm(TINFO, "parent: successfully created child (pid = %d)", cpid);
    285 
    286 	/* Register for notification on message arrival */
    287 	notif.sigev_notify = SIGEV_SIGNAL;
    288 	notif.sigev_signo = SIGUSR1;
    289 	info.mqd = mqd;
    290 	info.pid = cpid;
    291 	notif.sigev_value.sival_ptr = &info;
    292 	if (ltp_syscall(__NR_mq_notify, mqd, &notif) == (mqd_t) -1) {
    293 		tst_resm(TBROK, "parent: mq_notify() failed (%s)",
    294 			 strerror(errno));
    295 		cleanup_mqueue(TBROK, F_STEP_1, mqd);
    296 	}
    297 	tst_resm(TINFO, "parent: successfully registered for notification");
    298 
    299 	/* Define handler for SIGUSR1 */
    300 	sa.sa_flags = SA_SIGINFO;
    301 	sigemptyset(&sa.sa_mask);
    302 	sa.sa_sigaction = father_signal_handler;
    303 	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
    304 		tst_resm(TBROK, "parent: sigaction() failed(%s)",
    305 			 strerror(errno));
    306 		cleanup_mqueue(TBROK, F_STEP_2, mqd);
    307 	}
    308 	tst_resm(TINFO, "parent: successfully registered handler for SIGUSR1");
    309 
    310 	/* Close the appropriate end of pipe */
    311 	close(father_to_child[0]);
    312 
    313 	/* Tell the child a message can be sent */
    314 	if (write(father_to_child[1], "f:ok", 5) != 5) {
    315 		tst_resm(TBROK, "parent: pipe is broken(%s)", strerror(errno));
    316 		cleanup_mqueue(TBROK, F_STEP_2, mqd);
    317 	}
    318 
    319 	sleep(3);
    320 
    321 	/* Wait for child to finish */
    322 	if (wait(&status) == -1) {
    323 		tst_resm(TBROK, "parent: wait() failed(%s)", strerror(errno));
    324 		cleanup_mqueue(TBROK, F_STEP_1, mqd);
    325 	}
    326 
    327 	cleanup_mqueue(result, F_STEP_3, mqd);
    328 
    329 	tst_exit();
    330 }
    331