Commit 6aaf6564 authored by Andrey Vagin's avatar Andrey Vagin Committed by Pavel Emelyanov

zdtm: move all logic about namespaces from test_init in libzdtm

A test will be executed in new set of namespaces if the environment
variable ZDTM_NEWNS is set. A pid of the root task will be written in
$ZDTM_PIDFILE and a root fyle system will be changed on $ZDTM_ROOT.
Signed-off-by: 's avatarAndrey Vagin <avagin@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent aabb56bd
......@@ -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
......
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})
......
#define _GNU_SOURCE
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <sched.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/limits.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/param.h>
#include <errno.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <signal.h>
#include <sched.h>
#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;
}
#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");
......@@ -112,8 +48,12 @@ again:
continue;
if (!strcmp(mp, "/proc"))
continue;
if (root && !strcmp(mp, root))
continue;
umount(mp);
fprintf(stderr, "%s\n", mp);
if (umount(mp))
fprintf(stderr, "%s - %m\n", mp);
fs_cnt++;
}
......@@ -130,25 +70,134 @@ 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];
};
int fn(void *_arg)
static void ns_sig_hand(int signo)
{
struct sigaction sa = {
.sa_handler = sig_hand,
.sa_flags = SA_RESTART,
};
char cmd[256];
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(status_pipe[0]);
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;
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;
}
ret = fcntl(status_pipe[1], F_SETFD, FD_CLOEXEC);
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);
......@@ -168,43 +217,64 @@ int fn(void *_arg)
}
/* Start test */
snprintf(cmd, sizeof(cmd), "make -C %s %s.pid", dir, name);
ret = system(cmd);
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[1], &ret, sizeof(ret));
close(status_pipe[1]);
write(status_pipe, &ret, sizeof(ret));
close(status_pipe);
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;
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);
return 0;
exit(1);
}
int main(int argc, char *argv[])
#define STACK_SIZE (8 * 4096)
void ns_create(int argc, char **argv)
{
void *stack;
pid_t pid;
int ret, status, fd;
int ret, status;
static struct ns_exec_args args;
int fd;
if (argc < 4)
exit(1);
strcpy(dir, argv[1]);
strcpy(name, argv[2]);
strcpy(pidfile, argv[3]);
args.argc = argc;
args.argv = argv;
stack = mmap(NULL, STACK_SIZE, PROT_WRITE | PROT_READ,
MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS, -1, 0);
......@@ -212,34 +282,42 @@ int main(int argc, char *argv[])
fprintf(stderr, "Can't map stack %m\n");
exit(1);
}
ret = pipe(status_pipe);
ret = pipe(args.status_pipe);
if (ret) {
fprintf(stderr, "Pipe() failed %m\n");
exit(1);
}
pid = clone(fn, stack + STACK_SIZE, CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
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(status_pipe[1]);
close(args.status_pipe[1]);
status = 1;
ret = read(status_pipe[0], &status, sizeof(status));
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 a pid file %s: %m", pidfile);
return 1;
fprintf(stderr, "Can't create the file %s: %m\n", pidfile);
exit(1);
}
ret = dprintf(fd, "%d", pid);
if (ret == -1) {
fprintf(stderr, "Can't write in a pid file\n");
return 1;
if (dprintf(fd, "%d", pid) == -1) {
fprintf(stderr, "Can't write in the file %s: %m\n", pidfile);
exit(1);
}
close(fd);
return 0;
exit(0);
}
#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
......@@ -10,10 +10,14 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <sched.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#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);
......
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
......
......@@ -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);
......
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)
......
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 \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment