Home | History | Annotate | Download | only in pidns
      1 /*
      2 * Copyright (c) International Business Machines Corp., 2007
      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: pidns20.c
     17 * *
     18 * * Description:
     19 * *  The pidns20.c testcase verifies that signal handler of SIGUSR1 is called
     20 * *  (and cinit is NOT terminated) when:
     21 * *    - container-init blocks SIGUSR1,
     22 * *    - parent queues SIGUSR1 and
     23 * *    - a handler is specified for SIGUSR1 before it is unblocked.
     24 * *
     25 * * Test Assertion & Strategy:
     26 * *  Create a PID namespace container.
     27 * *  Block SIGUSR1 signal inside it.
     28 * *  Let parent to deliver SIGUSR1 signal to container.
     29 * *  Redefine SIGUSR1 handler of cinit to user function.
     30 * *  Unblock SIGUSR1 from blocked queue.
     31 * *  Check if user function is called.
     32 * *
     33 * * Usage: <for command-line>
     34 * *  pidns20
     35 * *
     36 * * History:
     37 * *  DATE      NAME                             DESCRIPTION
     38 * *  13/11/08  Gowrishankar M 			Creation of this test.
     39 * *            <gowrishankar.m (at) in.ibm.com>
     40 *
     41 ******************************************************************************/
     42 #define _GNU_SOURCE 1
     43 #include <sys/wait.h>
     44 #include <sys/types.h>
     45 #include <signal.h>
     46 #include <stdlib.h>
     47 #include <unistd.h>
     48 #include <stdio.h>
     49 #include "pidns_helper.h"
     50 #include "test.h"
     51 
     52 char *TCID = "pidns20";
     53 int TST_TOTAL = 1;
     54 
     55 int parent_cinit[2];
     56 int cinit_parent[2];
     57 int broken = 1;			/* broken should be 0 when test completes properly */
     58 
     59 #define CHILD_PID       1
     60 #define PARENT_PID      0
     61 
     62 /*
     63  * child_signal_handler() - to handle SIGUSR1
     64  */
     65 static void child_signal_handler(int sig, siginfo_t * si, void *unused)
     66 {
     67 	if (si->si_signo != SIGUSR1)
     68 		tst_resm(TBROK, "cinit: recieved %s unexpectedly!",
     69 			 strsignal(si->si_signo));
     70 	else
     71 		tst_resm(TPASS, "cinit: user function is called as expected");
     72 
     73 	/* Disable broken flag */
     74 	broken = 0;
     75 }
     76 
     77 /*
     78  * child_fn() - Inside container
     79  */
     80 int child_fn(void *arg)
     81 {
     82 	pid_t pid, ppid;
     83 	sigset_t newset;
     84 	struct sigaction sa;
     85 	char buf[5];
     86 
     87 	/* Setup pipe read and write ends */
     88 	pid = getpid();
     89 	ppid = getppid();
     90 
     91 	if (pid != CHILD_PID || ppid != PARENT_PID) {
     92 		printf("cinit: pidns was not created properly\n");
     93 		exit(1);
     94 	}
     95 
     96 	/* Setup pipes to communicate with parent */
     97 	close(cinit_parent[0]);
     98 	close(parent_cinit[1]);
     99 
    100 	/* Block SIGUSR1 signal */
    101 	sigemptyset(&newset);
    102 	sigaddset(&newset, SIGUSR1);
    103 	if (sigprocmask(SIG_BLOCK, &newset, 0) == -1) {
    104 		perror("cinit: sigprocmask() failed");
    105 		exit(1);
    106 	}
    107 	tst_resm(TINFO, "cinit: blocked SIGUSR1");
    108 
    109 	/* Let parent to queue SIGUSR1 in pending */
    110 	if (write(cinit_parent[1], "c:go", 5) != 5) {
    111 		perror("cinit: pipe is broken to write");
    112 		exit(1);
    113 	}
    114 
    115 	/* Check if parent has queued up SIGUSR1 */
    116 	read(parent_cinit[0], buf, 5);
    117 	if (strcmp(buf, "p:go") != 0) {
    118 		printf("cinit: parent did not respond!\n");
    119 		exit(1);
    120 	}
    121 
    122 	/* Now redefine handler for SIGUSR1 */
    123 	sa.sa_flags = SA_SIGINFO;
    124 	sigfillset(&sa.sa_mask);
    125 	sa.sa_sigaction = child_signal_handler;
    126 	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
    127 		perror("cinit: sigaction failed");
    128 		exit(1);
    129 	}
    130 
    131 	/* Unblock traffic on SIGUSR1 queue */
    132 	tst_resm(TINFO, "cinit: unblocking SIGUSR1");
    133 	sigprocmask(SIG_UNBLOCK, &newset, 0);
    134 
    135 	/* Check if new handler is called */
    136 	if (broken == 1) {
    137 		printf("cinit: broken flag didn't change\n");
    138 		exit(1);
    139 	}
    140 
    141 	/* Cleanup and exit */
    142 	close(cinit_parent[1]);
    143 	close(parent_cinit[0]);
    144 	exit(0);
    145 }
    146 
    147 static void setup(void)
    148 {
    149 	tst_require_root();
    150 	check_newpid();
    151 }
    152 
    153 int main(int argc, char *argv[])
    154 {
    155 	int status;
    156 	char buf[5];
    157 	pid_t cpid;
    158 
    159 	setup();
    160 
    161 	/* Create pipes for intercommunication */
    162 	if (pipe(parent_cinit) == -1 || pipe(cinit_parent) == -1) {
    163 		tst_brkm(TBROK | TERRNO, NULL, "pipe failed");
    164 	}
    165 
    166 	cpid = ltp_clone_quick(CLONE_NEWPID | SIGCHLD, child_fn, NULL);
    167 	if (cpid == -1) {
    168 		tst_brkm(TBROK | TERRNO, NULL, "clone failed");
    169 	}
    170 
    171 	/* Setup pipe read and write ends */
    172 	close(cinit_parent[1]);
    173 	close(parent_cinit[0]);
    174 
    175 	/* Is container ready */
    176 	read(cinit_parent[0], buf, 5);
    177 	if (strcmp(buf, "c:go") != 0) {
    178 		tst_brkm(TBROK, NULL, "parent: container did not respond!");
    179 	}
    180 
    181 	/* Enqueue SIGUSR1 in pending signal queue of container */
    182 	if (kill(cpid, SIGUSR1) == -1) {
    183 		tst_brkm(TBROK | TERRNO, NULL, "kill() failed");
    184 	}
    185 
    186 	tst_resm(TINFO, "parent: signalled SIGUSR1 to container");
    187 	if (write(parent_cinit[1], "p:go", 5) != 5) {
    188 		tst_brkm(TBROK | TERRNO, NULL, "write failed");
    189 	}
    190 
    191 	/* collect exit status of child */
    192 	if (wait(&status) == -1) {
    193 		tst_brkm(TBROK | TERRNO, NULL, "wait failed");
    194 	}
    195 
    196 	if (WIFSIGNALED(status)) {
    197 		if (WTERMSIG(status) == SIGUSR1)
    198 			tst_resm(TFAIL,
    199 				 "user function was not called inside cinit");
    200 		else
    201 			tst_resm(TBROK,
    202 				 "cinit was terminated by %d",
    203 				 WTERMSIG(status));
    204 	}
    205 
    206 	/* Cleanup and exit */
    207 	close(parent_cinit[1]);
    208 	close(cinit_parent[0]);
    209 	tst_exit();
    210 }
    211