diff --git a/test/zdtm.sh b/test/zdtm.sh index 10f00f86c..4aad7c53d 100644 --- a/test/zdtm.sh +++ b/test/zdtm.sh @@ -91,11 +91,20 @@ ARGS="" PID="" PIDNS="" +umount_zdtm_root() +{ + [ -z "$ZDTM_ROOT" ] && return; + umount -l "$ZDTM_ROOT" + rmdir "$ZDTM_ROOT" +} +trap umount_zdtm_root EXIT + start_test() { local tdir=$1 local tname=$2 - local tpid=$tdir/$tname.init.pid + export ZDTM_ROOT + TPID=`readlink -f $tdir`/$tname.init.pid killall -9 $tname &> /dev/null make -C $tdir cleanout @@ -104,12 +113,23 @@ start_test() make -C $tdir $tname.pid PID=`cat $test.pid` || return 1 else - $TINIT $tdir $tname $tpid || { + if [ -z "$ZDTM_ROOT" ]; then + mkdir dump + ZDTM_ROOT=`mktemp -d dump/crtools-root.XXXXXX` + ZDTM_ROOT=`readlink -f $ZDTM_ROOT` + mount --bind . $ZDTM_ROOT || return 1 + fi + ( export ZDTM_NEWNS=1 + export ZDTM_PIDFILE=$TPID + cd $ZDTM_ROOT + rm -f $ZDTM_PIDFILE + make -C $tdir $tname.pid || { echo ERROR: fail to start $tdir/$tname return 1; } + ) - PID=`cat $tpid` + PID=`cat "$TPID"` ps -p $PID || return 1 fi } @@ -122,7 +142,7 @@ stop_test() if [ -z "$PIDNS" ]; then make -C $tdir $tname.out else - killall test_init + kill `cat "$TPID"` fi } @@ -170,7 +190,7 @@ run_test() DUMP_PATH=`pwd`/$ddump if [ -n "$PIDNS" ]; then - args="--namespace pid --namespace mnt $args" + args="-n uts -n ipc -n net -n pid -n mnt --root $ZDTM_ROOT --pidfile $TPID $args" fi echo Dump $PID diff --git a/test/zdtm/lib/Makefile b/test/zdtm/lib/Makefile index 0162e8c6e..be8cf36be 100644 --- a/test/zdtm/lib/Makefile +++ b/test/zdtm/lib/Makefile @@ -1,8 +1,8 @@ -CFLAGS = -g -O2 -Wall -Werror -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 +CFLAGS = --static -g -O2 -Wall -Werror -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 LIB = libzdtmtst.a -LIBSRC = datagen.c msg.c parseargs.c test.c streamutil.c lock.c +LIBSRC = datagen.c msg.c parseargs.c test.c streamutil.c lock.c ns.c LIBOBJ = $(LIBSRC:%.c=%.o) LIBDEP = $(LIBSRC:%.c=%.d) @@ -10,7 +10,7 @@ DEPEND.c = $(COMPILE.c) -MM -MP %.d: %.c $(DEPEND.c) $(OUTPUT_OPTION) $< -all: $(LIB) test_init +all: $(LIB) install: all $(LIB): $(LIB)(${LIBOBJ}) diff --git a/test/zdtm/lib/ns.c b/test/zdtm/lib/ns.c new file mode 100644 index 000000000..c738f753e --- /dev/null +++ b/test/zdtm/lib/ns.c @@ -0,0 +1,323 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ns.h" + +extern int pivot_root(const char *new_root, const char *put_old); +static int prepare_mntns() +{ + FILE *f; + unsigned fs_cnt, fs_cnt_last = 0; + char buf[1024]; + char *root; + + root = getenv("ZDTM_ROOT"); + fprintf(stderr, "'%s'\n", root); +again: + fs_cnt = 0; + f = fopen("/proc/self/mountinfo", "r"); + if (!f) { + fprintf(stderr, "Can't open mountinfo"); + return -1; + } + + while (fgets(buf, sizeof(buf), f) != NULL) { + char *mp = buf, *end; + + mp = strchr(mp, ' ') + 1; + mp = strchr(mp, ' ') + 1; + mp = strchr(mp, ' ') + 1; + mp = strchr(mp, ' ') + 1; + end = strchr(mp, ' '); + *end = '\0'; + + if (!strcmp(mp, "/")) + continue; + if (!strcmp(mp, "/proc")) + continue; + if (root && !strcmp(mp, root)) + continue; + + fprintf(stderr, "%s\n", mp); + if (umount(mp)) + fprintf(stderr, "%s - %m\n", mp); + fs_cnt++; + } + + fclose(f); + + if (fs_cnt == 0) + goto done; + + if (fs_cnt != fs_cnt_last) { + fs_cnt_last = fs_cnt; + goto again; + } + + fprintf(stderr, "Can't umount all the filesystems"); + return -1; +done: + if (root) { + int dfd; + + dfd = open(".", O_RDONLY); + if (dfd == -1) { + fprintf(stderr, "open(.) failed: %m\n"); + return -1; + } + + if (umount("/proc")) { + fprintf(stderr, "umount(/proc) failed: %m\n"); + return -1; + } + if (chdir(root)) { + fprintf(stderr, "chdir(%s) failed: %m\n", root); + return -1; + } + if (mkdir("old", 0777) && errno != EEXIST) { + fprintf(stderr, "mkdir(old) failed: %m\n"); + return -1; + } + if (pivot_root(".", "./old")) { + fprintf(stderr, "pivot_root(., ./old) failed: %m\n"); + return -1; + } + if (umount("./old")) { + fprintf(stderr, "umount(./old) failed: %m\n"); + return -1; + } + if (mkdir("proc", 0777) && errno != EEXIST) { + fprintf(stderr, "mkdir(proc) failed: %m\n"); + return -1; + } + if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL)) { + fprintf(stderr, "mount(/proc) failed: %m\n"); + return -1; + } + if (fchdir(dfd)) { + fprintf(stderr, "fchdir() failed: %m\n"); + return -1; + } + close(dfd); + } + mkdir("/dev", 0777); + mknod("/dev/null", 0777 | S_IFCHR, makedev(1, 3)); + return 0; +} +struct ns_exec_args { + int argc; + char **argv; + int status_pipe[2]; +}; + +static void ns_sig_hand(int signo) +{ + int status, len = 0; + pid_t pid; + char buf[128] = ""; + + if (signo == SIGTERM) { + sig_received = signo; + len = snprintf(buf, sizeof(buf), "Time to stop and check\n"); + goto write_out; + } + + while (1) { + pid = waitpid(-1, &status, WNOHANG); + if (pid == 0) + return; + if (pid == -1) { + if (errno == ECHILD) { + if (sig_received) + return; + sig_received = signo; + len = snprintf(buf, sizeof(buf), + "All test processes exited\n"); + } else { + len = snprintf(buf, sizeof(buf), + "wait() failed: %m\n"); + } + goto write_out; + } + if (status) + fprintf(stderr, "%d return %d\n", pid, status); + } + + return; +write_out: + /* fprintf can't be used in a sighandler due to glibc locks */ + write(STDERR_FILENO, buf, MAX(len, sizeof(buf))); +} + +#define STATUS_FD 255 +int ns_exec(void *_arg) +{ + struct ns_exec_args *args = (struct ns_exec_args *) _arg; + int ret; + + close(args->status_pipe[0]); + + system("ip link set up dev lo"); + + ret = dup2(args->status_pipe[1], STATUS_FD); + if (ret < 0) { + fprintf(stderr, "dup2() failed: %m\n"); + return -1; + } + close(args->status_pipe[1]); + if (prepare_mntns()) + return -1; + + setenv("ZDTM_EXE", "1", 0); + execvp(args->argv[0], args->argv); + fprintf(stderr, "exec(%s) failed: %m\n", args->argv[0]); + return -1; +} + +int ns_init(int argc, char **argv) +{ + struct sigaction sa = { + .sa_handler = ns_sig_hand, + .sa_flags = SA_RESTART, + }; + int ret, fd, status_pipe = STATUS_FD; + char buf[128]; + pid_t pid; + + ret = fcntl(status_pipe, F_SETFD, FD_CLOEXEC); + if (ret == -1) { + fprintf(stderr, "fcntl failed %m\n"); + exit(1); + } + + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGTERM); + sigaddset(&sa.sa_mask, SIGCHLD); + + if (sigaction(SIGTERM, &sa, NULL)) { + fprintf(stderr, "Can't set SIGTERM handler: %m\n"); + exit(1); + } + if (sigaction(SIGCHLD, &sa, NULL)) { + fprintf(stderr, "Can't set SIGCHLD handler: %m\n"); + exit(1); + } + + /* Start test */ + pid = fork(); + if (pid < 0) { + fprintf(stderr, "fork() failed: %m\n"); + exit(1); + } else if (pid == 0) { + ret = execvp(argv[0], argv); + fprintf(stderr, "exec(%s) failed: %m\n", argv[0]); + return ret; + } + ret = 1; + waitpid(pid, &ret, 0); + + /* Daemonize */ + write(status_pipe, &ret, sizeof(ret)); + close(status_pipe); + if (ret) + return ret; + + /* suspend/resume */ + test_waitsig(); + + parseargs(argc, argv); + + fd = open(pidfile, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "open(%s) failed: %m\n", pidfile); + exit(1); + } + ret = read(fd, buf, sizeof(buf) - 1); + buf[ret] = '\0'; + if (ret == -1) { + fprintf(stderr, "read() failed: %m\n"); + exit(1); + } + + pid = atoi(buf); + fprintf(stderr, "kill(%d, SIGTERM)\n", pid); + if (pid > 0) + kill(pid, SIGTERM); + + ret = 0; + while (ret != -1) + ret = wait(NULL); + + exit(1); +} + +#define STACK_SIZE (8 * 4096) +void ns_create(int argc, char **argv) +{ + void *stack; + pid_t pid; + int ret, status; + static struct ns_exec_args args; + int fd; + + args.argc = argc; + args.argv = argv; + + stack = mmap(NULL, STACK_SIZE, PROT_WRITE | PROT_READ, + MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS, -1, 0); + if (stack == MAP_FAILED) { + fprintf(stderr, "Can't map stack %m\n"); + exit(1); + } + ret = pipe(args.status_pipe); + if (ret) { + fprintf(stderr, "Pipe() failed %m\n"); + exit(1); + } + pid = clone(ns_exec, stack + STACK_SIZE, + CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUTS | + CLONE_NEWNET | CLONE_NEWIPC | SIGCHLD, &args); + if (pid < 0) { + fprintf(stderr, "clone() failed: %m\n"); + exit(1); + } + close(args.status_pipe[1]); + + status = 1; + ret = read(args.status_pipe[0], &status, sizeof(status)); + if (ret != sizeof(status) || status) + exit(1); + ret = read(args.status_pipe[0], &status, sizeof(status)); + if (ret != 0) + exit(1); + + pidfile = getenv("ZDTM_PIDFILE"); + if (pidfile == NULL) + exit(1); + fd = open(pidfile, O_CREAT | O_EXCL | O_WRONLY, 0666); + if (fd == -1) { + fprintf(stderr, "Can't create the file %s: %m\n", pidfile); + exit(1); + } + if (dprintf(fd, "%d", pid) == -1) { + fprintf(stderr, "Can't write in the file %s: %m\n", pidfile); + exit(1); + } + close(fd); + + exit(0); +} + diff --git a/test/zdtm/lib/ns.h b/test/zdtm/lib/ns.h new file mode 100644 index 000000000..c79cb47be --- /dev/null +++ b/test/zdtm/lib/ns.h @@ -0,0 +1,13 @@ +#ifndef __ZDTM_NS__ +#define __ZDTM_NS__ + +extern volatile sig_atomic_t sig_received; +extern char *pidfile; + +extern void ns_create(int argc, char **argv); +extern int ns_init(int argc, char **argv); + +extern void test_waitsig(void); +extern void parseargs(int, char **); + +#endif diff --git a/test/zdtm/lib/test.c b/test/zdtm/lib/test.c index 1c89c5a15..8b842df38 100644 --- a/test/zdtm/lib/test.c +++ b/test/zdtm/lib/test.c @@ -10,10 +10,14 @@ #include #include #include +#include +#include +#include #include "zdtmtst.h" +#include "ns.h" -static volatile sig_atomic_t sig_received = 0; +volatile sig_atomic_t sig_received = 0; static void sig_hand(int signo) { @@ -22,7 +26,7 @@ static void sig_hand(int signo) static char *outfile; TEST_OPTION(outfile, string, "output file", 1); -static char *pidfile; +char *pidfile; TEST_OPTION(pidfile, string, "file to store pid", 1); static pid_t master_pid = 0; @@ -85,12 +89,27 @@ void test_init(int argc, char **argv) pid_t pid; static FILE *pidf; + char *val; struct sigaction sa = { .sa_handler = sig_hand, .sa_flags = SA_RESTART, }; sigemptyset(&sa.sa_mask); + val = getenv("ZDTM_NEWNS"); + if (val) { + unsetenv("ZDTM_NEWNS"); + ns_create(argc, argv); + exit(1); + } + + val = getenv("ZDTM_EXE"); + if (val) { + unsetenv("ZDTM_EXE"); + ns_init(argc, argv); + exit(1); + } + if (sigaction(SIGTERM, &sa, NULL)) { fprintf(stderr, "Can't set SIGTERM handler: %m\n"); exit(1); diff --git a/test/zdtm/lib/test_init.c b/test/zdtm/lib/test_init.c deleted file mode 100644 index 73b4c538d..000000000 --- a/test/zdtm/lib/test_init.c +++ /dev/null @@ -1,245 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define STACK_SIZE (8 * 4096) -#ifndef CLONE_NEWPID -/* CLONE_NEWPID since Linux 2.6.24 */ -#define CLONE_NEWPID 0x20000000 -#endif - -static int sig_received; -static char dir[PATH_MAX]; -static char name[PATH_MAX]; -static char pidfile[PATH_MAX]; -int status_pipe[2]; - -static void sig_hand(int signo) -{ - int status, len = 0; - pid_t pid; - char buf[128] = ""; - - if (signo == SIGTERM) { - sig_received = signo; - len = snprintf(buf, sizeof(buf), "Time to stop and check\n"); - goto write_out; - } - - while (1) { - pid = waitpid(-1, &status, WNOHANG); - if (pid == 0) - return; - if (pid == -1) { - if (errno == ECHILD) { - if (sig_received) - return; - sig_received = signo; - len = snprintf(buf, sizeof(buf), - "All test processes exited\n"); - } else { - len = snprintf(buf, sizeof(buf), - "wait() failed: %m\n"); - } - goto write_out; - } - if (status) - fprintf(stderr, "%d return %d\n", pid, status); - } - - return; -write_out: - /* fprintf can't be used in a sighandler due to glibc locks */ - write(STDERR_FILENO, buf, MAX(len, sizeof(buf))); -} - -void test_waitsig(void) -{ - sigset_t mask, oldmask; - - /* Set up the mask of signals to temporarily block. */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); - - /* Wait for a signal to arrive. */ - sigprocmask(SIG_BLOCK, &mask, &oldmask); - while (!sig_received) - sigsuspend (&oldmask); - sigprocmask (SIG_UNBLOCK, &mask, NULL); - - sig_received = 0; -} - -static int prepare_mntns() -{ - FILE *f; - unsigned fs_cnt, fs_cnt_last = 0; - char buf[1024]; - -again: - fs_cnt = 0; - f = fopen("/proc/self/mountinfo", "r"); - if (!f) { - fprintf(stderr, "Can't open mountinfo"); - return -1; - } - - while (fgets(buf, sizeof(buf), f) != NULL) { - char *mp = buf, *end; - - mp = strchr(mp, ' ') + 1; - mp = strchr(mp, ' ') + 1; - mp = strchr(mp, ' ') + 1; - mp = strchr(mp, ' ') + 1; - end = strchr(mp, ' '); - *end = '\0'; - - if (!strcmp(mp, "/")) - continue; - if (!strcmp(mp, "/proc")) - continue; - - umount(mp); - fs_cnt++; - } - - fclose(f); - - if (fs_cnt == 0) - goto done; - - if (fs_cnt != fs_cnt_last) { - fs_cnt_last = fs_cnt; - goto again; - } - - fprintf(stderr, "Can't umount all the filesystems"); - return -1; -done: - mknod("/dev/null", 0777 | S_IFCHR, makedev(1, 3)); - return 0; -} - -int fn(void *_arg) -{ - struct sigaction sa = { - .sa_handler = sig_hand, - .sa_flags = SA_RESTART, - }; - char cmd[256]; - int ret; - - close(status_pipe[0]); - - if (prepare_mntns()) - return 1; - - ret = fcntl(status_pipe[1], F_SETFD, FD_CLOEXEC); - if (ret == -1) { - fprintf(stderr, "fcntl failed %m\n"); - exit(1); - } - - sigemptyset(&sa.sa_mask); - sigaddset(&sa.sa_mask, SIGTERM); - sigaddset(&sa.sa_mask, SIGCHLD); - - if (sigaction(SIGTERM, &sa, NULL)) { - fprintf(stderr, "Can't set SIGTERM handler: %m\n"); - exit(1); - } - if (sigaction(SIGCHLD, &sa, NULL)) { - fprintf(stderr, "Can't set SIGCHLD handler: %m\n"); - exit(1); - } - - /* Start test */ - snprintf(cmd, sizeof(cmd), "make -C %s %s.pid", dir, name); - ret = system(cmd); - - /* Daemonize */ - write(status_pipe[1], &ret, sizeof(ret)); - close(status_pipe[1]); - if (ret) - return ret; - - /* suspend/resume */ - test_waitsig(); - - /* Stop test */ - snprintf(cmd, sizeof(cmd), "make -C %s %s.out", dir, name); - ret = system(cmd); - if (ret) - return ret; - - ret = 0; - while (ret != -1) - ret = wait(NULL); - - return 0; -} - -int main(int argc, char *argv[]) -{ - void *stack; - pid_t pid; - int ret, status, fd; - - if (argc < 4) - exit(1); - - strcpy(dir, argv[1]); - strcpy(name, argv[2]); - strcpy(pidfile, argv[3]); - - stack = mmap(NULL, STACK_SIZE, PROT_WRITE | PROT_READ, - MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS, -1, 0); - if (stack == MAP_FAILED) { - fprintf(stderr, "Can't map stack %m\n"); - exit(1); - } - ret = pipe(status_pipe); - if (ret) { - fprintf(stderr, "Pipe() failed %m\n"); - exit(1); - } - pid = clone(fn, stack + STACK_SIZE, CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL); - if (pid < 0) { - fprintf(stderr, "clone() failed: %m\n"); - exit(1); - } - close(status_pipe[1]); - - status = 1; - ret = read(status_pipe[0], &status, sizeof(status)); - if (ret != sizeof(status) || status) - exit(1); - - fd = open(pidfile, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (fd == -1) { - fprintf(stderr, "Can't create a pid file %s: %m", pidfile); - return 1; - } - ret = dprintf(fd, "%d", pid); - if (ret == -1) { - fprintf(stderr, "Can't write in a pid file\n"); - return 1; - } - close(fd); - - return 0; -} diff --git a/test/zdtm/live/static/Makefile b/test/zdtm/live/static/Makefile index 70372a0ee..45714bc42 100644 --- a/test/zdtm/live/static/Makefile +++ b/test/zdtm/live/static/Makefile @@ -1,7 +1,7 @@ LIBDIR = ../../lib LIB = $(LIBDIR)/libzdtmtst.a override CPPFLAGS += -I$(LIBDIR) -CFLAGS = -g -O2 -Wall -Werror -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 +CFLAGS = --static -g -O2 -Wall -Werror -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 TST_NOFILE = \ busyloop00 \ @@ -173,9 +173,9 @@ futex: override LDFLAGS += -pthread futex-rl.o: override CFLAGS += -pthread futex-rl: override LDFLAGS += -pthread jobctl00: override LDLIBS += -lutil -socket_listen: override LDLIBS += -lrt -socket_aio: override LDLIBS += -lrt -uptime_grow: override LDLIBS += -lrt +socket_listen: override LDLIBS += -lrt -pthread +socket_aio: override LDLIBS += -lrt -pthread +uptime_grow: override LDLIBS += -lrt -pthread unlink_largefile: override CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE inotify_system_nodel: override CFLAGS += -DNODEL pthread00: override LDLIBS += -pthread diff --git a/test/zdtm/live/static/inotify00.c b/test/zdtm/live/static/inotify00.c index 79d653ca7..06780d64c 100644 --- a/test/zdtm/live/static/inotify00.c +++ b/test/zdtm/live/static/inotify00.c @@ -35,8 +35,6 @@ int main (int argc, char *argv[]) wd = 0; wd |= inotify_add_watch(fd, "/", IN_ALL_EVENTS); - wd |= inotify_add_watch(fd, "/root", IN_ALL_EVENTS); - wd |= inotify_add_watch(fd, "/boot", IN_ALL_EVENTS); if (wd < 0) { fail("inotify_add_watch failed"); exit(1); diff --git a/test/zdtm/live/streaming/Makefile b/test/zdtm/live/streaming/Makefile index 7aeced5fe..0fb89f677 100644 --- a/test/zdtm/live/streaming/Makefile +++ b/test/zdtm/live/streaming/Makefile @@ -1,7 +1,7 @@ LIBDIR = ../../lib LIB = $(LIBDIR)/libzdtmtst.a override CPPFLAGS += -I$(LIBDIR) -CFLAGS = -g -O2 -Wall -Werror -fno-strict-aliasing +CFLAGS = --static -g -O2 -Wall -Werror -fno-strict-aliasing TST_NOFILE = \ pipe_loop00 \ @@ -57,7 +57,7 @@ wait_stop: $(TST): $(LIB) -file_aio: override LDLIBS += -lrt +file_aio: override LDLIBS += -lrt -pthread $(LIB): force $(MAKE) -C $(LIBDIR) diff --git a/test/zdtm/live/transition/Makefile b/test/zdtm/live/transition/Makefile index 64e004b07..19ded320a 100644 --- a/test/zdtm/live/transition/Makefile +++ b/test/zdtm/live/transition/Makefile @@ -1,7 +1,7 @@ LIBDIR = ../../lib LIB = $(LIBDIR)/libzdtmtst.a override CPPFLAGS += -I$(LIBDIR) -CFLAGS = -g -O2 -Wall -Werror +CFLAGS = --static -g -O2 -Wall -Werror TST_NOFILE = \ file_read \