| /* |
| * Copyright (c) International Business Machines Corp., 2007 |
| * 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 will 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, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| *************************************************************************** |
| * File: pidns20.c |
| * * |
| * * Description: |
| * * The pidns20.c testcase verifies that signal handler of SIGUSR1 is called |
| * * (and cinit is NOT terminated) when: |
| * * - container-init blocks SIGUSR1, |
| * * - parent queues SIGUSR1 and |
| * * - a handler is specified for SIGUSR1 before it is unblocked. |
| * * |
| * * Test Assertion & Strategy: |
| * * Create a PID namespace container. |
| * * Block SIGUSR1 signal inside it. |
| * * Let parent to deliver SIGUSR1 signal to container. |
| * * Redefine SIGUSR1 handler of cinit to user function. |
| * * Unblock SIGUSR1 from blocked queue. |
| * * Check if user function is called. |
| * * |
| * * Usage: <for command-line> |
| * * pidns20 |
| * * |
| * * History: |
| * * DATE NAME DESCRIPTION |
| * * 13/11/08 Gowrishankar M Creation of this test. |
| * * <gowrishankar.m@in.ibm.com> |
| * |
| ******************************************************************************/ |
| #define _GNU_SOURCE 1 |
| #include <sys/wait.h> |
| #include <sys/types.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include "usctest.h" |
| #include "test.h" |
| #include <libclone.h> |
| #include "pidns_helper.h" |
| |
| char *TCID = "pidns20"; |
| int TST_TOTAL = 1; |
| |
| int errno; |
| int parent_cinit[2]; |
| int cinit_parent[2]; |
| int broken = 1; /* broken should be 0 when test completes properly */ |
| |
| #define CHILD_PID 1 |
| #define PARENT_PID 0 |
| |
| void cleanup() |
| { |
| } |
| |
| /* |
| * child_signal_handler() - to handle SIGUSR1 |
| */ |
| static void child_signal_handler(int sig, siginfo_t * si, void *unused) |
| { |
| if (si->si_signo != SIGUSR1) |
| tst_resm(TBROK, "cinit: recieved %s unexpectedly!", |
| strsignal(si->si_signo)); |
| else |
| tst_resm(TPASS, "cinit: user function is called as expected"); |
| |
| /* Disable broken flag */ |
| broken = 0; |
| } |
| |
| /* |
| * child_fn() - Inside container |
| */ |
| int child_fn(void *arg) |
| { |
| pid_t pid, ppid; |
| sigset_t newset; |
| struct sigaction sa; |
| char buf[5]; |
| |
| /* Setup pipe read and write ends */ |
| pid = getpid(); |
| ppid = getppid(); |
| |
| if (pid != CHILD_PID || ppid != PARENT_PID) { |
| printf("cinit: pidns was not created properly\n"); |
| exit(1); |
| } |
| |
| /* Setup pipes to communicate with parent */ |
| close(cinit_parent[0]); |
| close(parent_cinit[1]); |
| |
| /* Block SIGUSR1 signal */ |
| sigemptyset(&newset); |
| sigaddset(&newset, SIGUSR1); |
| if (sigprocmask(SIG_BLOCK, &newset, 0) == -1) { |
| perror("cinit: sigprocmask() failed"); |
| exit(1); |
| } |
| tst_resm(TINFO, "cinit: blocked SIGUSR1"); |
| |
| /* Let parent to queue SIGUSR1 in pending */ |
| if (write(cinit_parent[1], "c:go", 5) != 5) { |
| perror("cinit: pipe is broken to write"); |
| exit(1); |
| } |
| |
| /* Check if parent has queued up SIGUSR1 */ |
| read(parent_cinit[0], buf, 5); |
| if (strcmp(buf, "p:go") != 0) { |
| printf("cinit: parent did not respond!\n"); |
| exit(1); |
| } |
| |
| /* Now redefine handler for SIGUSR1 */ |
| sa.sa_flags = SA_SIGINFO; |
| sigfillset(&sa.sa_mask); |
| sa.sa_sigaction = child_signal_handler; |
| if (sigaction(SIGUSR1, &sa, NULL) == -1) { |
| perror("cinit: sigaction failed"); |
| exit(1); |
| } |
| |
| /* Unblock traffic on SIGUSR1 queue */ |
| tst_resm(TINFO, "cinit: unblocking SIGUSR1"); |
| sigprocmask(SIG_UNBLOCK, &newset, 0); |
| |
| /* Check if new handler is called */ |
| if (broken == 1) { |
| printf("cinit: broken flag didn't change\n"); |
| exit(1); |
| } |
| |
| /* Cleanup and exit */ |
| close(cinit_parent[1]); |
| close(parent_cinit[0]); |
| exit(0); |
| } |
| |
| static void setup(void) |
| { |
| tst_require_root(NULL); |
| check_newpid(); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int status; |
| char buf[5]; |
| pid_t cpid; |
| |
| setup(); |
| |
| /* Create pipes for intercommunication */ |
| if (pipe(parent_cinit) == -1 || pipe(cinit_parent) == -1) { |
| tst_brkm(TBROK | TERRNO, cleanup, "pipe failed"); |
| } |
| |
| cpid = ltp_clone_quick(CLONE_NEWPID | SIGCHLD, child_fn, NULL); |
| if (cpid == -1) { |
| tst_brkm(TBROK | TERRNO, cleanup, "clone failed"); |
| } |
| |
| /* Setup pipe read and write ends */ |
| close(cinit_parent[1]); |
| close(parent_cinit[0]); |
| |
| /* Is container ready */ |
| read(cinit_parent[0], buf, 5); |
| if (strcmp(buf, "c:go") != 0) { |
| tst_brkm(TBROK, cleanup, "parent: container did not respond!"); |
| } |
| |
| /* Enqueue SIGUSR1 in pending signal queue of container */ |
| if (kill(cpid, SIGUSR1) == -1) { |
| tst_brkm(TBROK | TERRNO, cleanup, "kill() failed"); |
| } |
| |
| tst_resm(TINFO, "parent: signalled SIGUSR1 to container"); |
| if (write(parent_cinit[1], "p:go", 5) != 5) { |
| tst_brkm(TBROK | TERRNO, cleanup, "write failed"); |
| } |
| |
| /* collect exit status of child */ |
| if (wait(&status) == -1) { |
| tst_brkm(TBROK | TERRNO, cleanup, "wait failed"); |
| } |
| |
| if (WIFSIGNALED(status)) { |
| if (WTERMSIG(status) == SIGUSR1) |
| tst_resm(TFAIL, |
| "user function was not called inside cinit"); |
| else |
| tst_resm(TBROK, |
| "cinit was terminated by %d", |
| WTERMSIG(status)); |
| } |
| |
| /* Cleanup and exit */ |
| close(parent_cinit[1]); |
| close(cinit_parent[0]); |
| cleanup(); |
| tst_exit(); |
| } |