[RFC PATCH 7/9] Implement testing framework for pidns
Ákos Uzonyi
uzonyi.akos at gmail.com
Sat Jun 27 17:20:54 UTC 2020
---
tests/.gitignore | 1 +
tests/Makefile.am | 1 +
tests/init.sh | 22 ++++++
tests/pidns.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++
tests/pidns.h | 36 ++++++++++
5 files changed, 233 insertions(+)
create mode 100644 tests/pidns.c
create mode 100644 tests/pidns.h
diff --git a/tests/.gitignore b/tests/.gitignore
index 8739cc7f..f9a4eae0 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -501,6 +501,7 @@ pidfd_open-P
pidfd_open-y
pidfd_open-yy
pidfd_send_signal
+pidns
pipe
pipe2
pkey_alloc
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 28940329..7bd00631 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -44,6 +44,7 @@ libtests_a_SOURCES = \
libsocketcall.c \
lock_file.c \
overflowuid.c \
+ pidns.c \
pipe_maxfd.c \
print_quoted_string.c \
print_time.c \
diff --git a/tests/init.sh b/tests/init.sh
index d78e697b..f4f8829e 100644
--- a/tests/init.sh
+++ b/tests/init.sh
@@ -387,6 +387,28 @@ test_prog_set()
test_pure_prog_set "$@" < "$srcdir/$NAME.in"
}
+test_pidns_run_strace()
+{
+ local syscalls parent_pid log_filtered
+ log_filtered="log.filtered"
+
+ run_prog > /dev/null
+ run_strace -Y -f -e signal=none $@ $args > "$EXP"
+ parent_pid="$(tail -n 1 $LOG | cut -d' ' -f1)"
+ grep -E -v "^$parent_pid " "$LOG" > "$log_filtered"
+ match_diff "$log_filtered" "$EXP"
+}
+
+test_pidns()
+{
+ require_min_kernel_version_or_skip 3.8
+ check_prog unshare
+
+ test_pidns_run_strace "$@"
+ STRACE="unshare -Urpf $STRACE"
+ test_pidns_run_strace "$@"
+}
+
check_prog cat
check_prog rm
diff --git a/tests/pidns.c b/tests/pidns.c
new file mode 100644
index 00000000..c6df9b51
--- /dev/null
+++ b/tests/pidns.c
@@ -0,0 +1,173 @@
+#include "tests.h"
+#include "scno.h"
+#include "limits.h"
+#include "pidns.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <stdarg.h>
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+bool pidns_translation = false;
+bool pidns_unshared = false;
+
+/* Our PIDs in strace's namespace */
+pid_t pidns_strace_ids[PT_COUNT];
+
+void
+pidns_printf(const char *format, ...)
+{
+ if (pidns_translation)
+ printf("%-5d ", pidns_strace_ids[PT_TID]);
+
+ va_list args;
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+}
+
+const char *
+pidns_pid2str(enum pid_type type)
+{
+ static const char format[] = " /* %d in strace's PID NS */";
+ static char buf[sizeof(format) + sizeof(int) * 3];
+
+ if (!pidns_unshared || !pidns_strace_ids[type])
+ return "";
+
+ snprintf(buf, sizeof(buf), format, pidns_strace_ids[type]);
+ return buf;
+}
+
+static void
+pidns_fill_ids(int *strace_ids_pipe)
+{
+ if (strace_ids_pipe) {
+ read(strace_ids_pipe[0], pidns_strace_ids, sizeof(pidns_strace_ids));
+ close(strace_ids_pipe[0]);
+ close(strace_ids_pipe[1]);
+
+ if (pidns_strace_ids[PT_SID])
+ setsid();
+ } else {
+ pidns_strace_ids[PT_TID] = syscall(__NR_gettid);
+ pidns_strace_ids[PT_TGID] = getpid();
+ pidns_strace_ids[PT_PGID] = getpgid(0);
+ pidns_strace_ids[PT_SID] = getsid(0);
+ }
+}
+
+static pid_t
+pidns_fork(int *strace_ids_pipe, pid_t pgid, bool new_sid)
+{
+ if (strace_ids_pipe && pipe(strace_ids_pipe) < 0)
+ perror_msg_and_fail("pipe");
+
+ fflush(stdout);
+ pid_t pid = fork();
+ if (pid < 0)
+ perror_msg_and_fail("fork");
+ if (!pid)
+ return 0;
+
+ pidns_strace_ids[PT_TID] = pid;
+ pidns_strace_ids[PT_TGID] = pid;
+ pidns_strace_ids[PT_PGID] = 0;
+ pidns_strace_ids[PT_SID] = 0;
+
+ if (!pgid)
+ pgid = pid;
+
+ if (pgid > 0) {
+ if (setpgid(pid, pgid) < 0)
+ perror_msg_and_fail("setpgid");
+
+ pidns_strace_ids[PT_PGID] = pgid;
+ }
+
+ if (new_sid) {
+ pidns_strace_ids[PT_SID] = pid;
+ pidns_strace_ids[PT_PGID] = pid;
+ }
+
+ if (strace_ids_pipe) {
+ write(strace_ids_pipe[1], pidns_strace_ids, sizeof(pidns_strace_ids));
+ close(strace_ids_pipe[0]);
+ close(strace_ids_pipe[1]);
+ }
+
+ /* WNOWAIT: leave the zombie, to be able to use it as a process group */
+ siginfo_t siginfo;
+ if (waitid(P_PID, pid, &siginfo, WEXITED | WNOWAIT) < 0)
+ perror_msg_and_fail("wait");
+ if (siginfo.si_code != CLD_EXITED || siginfo.si_status)
+ error_msg_and_fail("child terminated with nonzero exit status");
+
+ return pid;
+}
+
+void
+pidns_test_init(void)
+{
+ pidns_translation = false;
+ pidns_fill_ids(NULL);
+}
+
+void
+pidns_test_init_Y(void)
+{
+ pidns_translation = true;
+
+ if (!pidns_fork(NULL, -1, false)) {
+ pidns_fill_ids(NULL);
+ return;
+ }
+
+ /* Unshare user namespace too, so we do not need to be root */
+ if (unshare(CLONE_NEWUSER | CLONE_NEWPID) < 0)
+ perror_msg_and_fail("unshare");
+
+ pidns_unshared = true;
+
+ /* Create sleeping process to keep PID namespace alive */
+ pid_t pause_pid = fork();
+ if (!pause_pid) {
+ pause();
+ _exit(0);
+ }
+
+ int strace_ids_pipe[2];
+
+ if (!pidns_fork(strace_ids_pipe, -1, false))
+ goto pidns_test_init_run_test;
+
+ if (!pidns_fork(strace_ids_pipe, -1, true))
+ goto pidns_test_init_run_test;
+
+ pid_t pgid;
+ if (!(pgid = pidns_fork(strace_ids_pipe, 0, false)))
+ goto pidns_test_init_run_test;
+
+ if (!pidns_fork(strace_ids_pipe, pgid, false))
+ goto pidns_test_init_run_test;
+
+ kill(pause_pid, SIGKILL);
+ while (wait(NULL) > 0);
+ if (errno != ECHILD)
+ perror_msg_and_fail("wait");
+
+ exit(0);
+
+pidns_test_init_run_test:
+ pidns_fill_ids(strace_ids_pipe);
+}
diff --git a/tests/pidns.h b/tests/pidns.h
new file mode 100644
index 00000000..412a4b37
--- /dev/null
+++ b/tests/pidns.h
@@ -0,0 +1,36 @@
+#ifndef STRACE_PIDNS_H
+#define STRACE_PIDNS_H
+
+#include <sys/types.h>
+
+enum pid_type {
+ PT_TID,
+ PT_TGID,
+ PT_PGID,
+ PT_SID,
+
+ PT_COUNT,
+ PT_NONE = -1
+};
+
+/* Prints leader (process tid) before printf */
+void pidns_printf(const char *format, ...);
+
+/*
+ * Returns a static buffer containing the translation of our PID
+ */
+const char *pidns_pid2str(enum pid_type type);
+
+/*
+ * Init pidns testing, when strace is run without the -Y flag
+ * Should be called at the beginning of the test's main function
+ */
+void pidns_test_init(void);
+
+/*
+ * Init pidns testing, when strace is run with the -Y flag
+ * Should be called at the beginning of the test's main function
+ */
+void pidns_test_init_Y(void);
+
+#endif
\ No newline at end of file
--
2.27.0
More information about the Strace-devel
mailing list