[CRIU] [PATCH 1/2] zdtm: add one more test case to create a process tree (v2)
Andrey Vagin
avagin at openvz.org
Thu Mar 28 16:01:55 EDT 2013
From: Andrew Vagin <avagin at openvz.org>
Actually this test case is a frame work for creating a process tree.
It creates a process tree according with a predefined sequence of
actions.
This test case create the following process tree:
5 5 \_ session02 ( 0)
6 6 \_ session02 ( 1)
8 7 | \_ session02 ( 3)
15 12 | \_ session02 (10)
10 10 \_ session02 ( 5)
11 7 \_ session02 ( 6)
13 12 \_ session02 ( 8)
v2: improve log messages
Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
test/zdtm/live/static/Makefile | 1 +
test/zdtm/live/static/session02.c | 328 ++++++++++++++++++++++++++++++++++++++
2 files changed, 329 insertions(+)
create mode 100644 test/zdtm/live/static/session02.c
diff --git a/test/zdtm/live/static/Makefile b/test/zdtm/live/static/Makefile
index ff1277d..345ccc8 100644
--- a/test/zdtm/live/static/Makefile
+++ b/test/zdtm/live/static/Makefile
@@ -84,6 +84,7 @@ TST_NOFILE = \
mountpoints \
netns \
session01 \
+ session02 \
socket-ext \
unhashed_proc \
cow00 \
diff --git a/test/zdtm/live/static/session02.c b/test/zdtm/live/static/session02.c
new file mode 100644
index 0000000..201e469
--- /dev/null
+++ b/test/zdtm/live/static/session02.c
@@ -0,0 +1,328 @@
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <sched.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/user.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a crazy process tree";
+const char *test_author = "Andrew Vagin <avagin at parallels.com>";
+
+struct process
+{
+ pid_t pid;
+ pid_t sid;
+ int sks[2];
+ int dead;
+};
+
+struct process *processes;
+int nr_processes = 20;
+int current = 0;
+
+static void cleanup()
+{
+ int i;
+
+ for (i = 0; i < nr_processes; i++) {
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid <= 0)
+ continue;
+
+ kill(processes[i].pid, SIGKILL);
+ }
+}
+
+enum commands
+{
+ TEST_FORK,
+ TEST_WAIT,
+ TEST_SUBREAPER,
+ TEST_SETSID,
+ TEST_DIE
+};
+
+struct command
+{
+ enum commands cmd;
+ int arg1;
+ int arg2;
+};
+
+static void handle_command();
+
+static void mainloop()
+{
+ while (1)
+ handle_command();
+}
+
+#define CLONE_STACK_SIZE PAGE_SIZE
+/* All arguments should be above stack, because it grows down */
+struct clone_args {
+ char stack[CLONE_STACK_SIZE];
+ char stack_ptr[0];
+ int id;
+};
+
+static int clone_func(void *_arg)
+{
+ struct clone_args *args = (struct clone_args *) _arg;
+
+ current = args->id;
+
+ test_msg("%3d: Hello. My pid is %d\n", args->id, getpid());
+ mainloop();
+ exit(0);
+}
+
+static int make_child(int id, int flags)
+{
+ struct clone_args args;
+ pid_t cid;
+
+ args.id = id;
+
+ cid = clone(clone_func, args.stack_ptr,
+ flags | SIGCHLD, &args);
+
+ if (cid < 0)
+ err("clone(%d, %d)", id, flags);
+
+ processes[id].pid = cid;
+
+ return cid;
+}
+
+static void handle_command()
+{
+ int sk = processes[current].sks[0], ret, status = 0;
+ struct command cmd;
+
+ ret = read(sk, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd)) {
+ err("Unable to get command");
+ goto err;
+ }
+
+ switch (cmd.cmd) {
+ case TEST_FORK:
+ {
+ pid_t pid;
+
+ pid = make_child(cmd.arg1, cmd.arg2);
+ if (pid == -1) {
+ status = -1;
+ goto err;
+ }
+
+ test_msg("%3d: fork(%d, %x) = %d\n",
+ current, cmd.arg1, cmd.arg2, pid);
+ processes[cmd.arg1].pid = pid;
+ }
+ break;
+ case TEST_WAIT:
+ test_msg("%3d: wait(%d) = %d\n", current,
+ cmd.arg1, processes[cmd.arg1].pid);
+
+ if (waitpid(processes[cmd.arg1].pid, NULL, 0) == -1) {
+ err("waitpid(%d)", processes[cmd.arg1].pid);
+ status = -1;
+ }
+ break;
+ case TEST_SUBREAPER:
+ test_msg("%3d: subreaper(%d)\n", current, cmd.arg1);
+ if (prctl(PR_SET_CHILD_SUBREAPER, cmd.arg1, 0, 0, 0) == -1) {
+ err("PR_SET_CHILD_SUBREAPER");
+ status = -1;
+ }
+ break;
+ case TEST_SETSID:
+ test_msg("%3d: setsid()\n", current);
+ if(setsid() == -1) {
+ err("setsid");
+ status = -1;
+ }
+ break;
+ case TEST_DIE:
+ test_msg("%3d: die()\n", current);
+ processes[current].dead = 1;
+ shutdown(sk, SHUT_RDWR);
+ exit(0);
+ }
+
+ ret = write(sk, &status, sizeof(status));
+ if (ret != sizeof(status)) {
+ err("Unable to answer");
+ goto err;
+ }
+
+ if (status < 0)
+ goto err;
+
+ return;
+err:
+ shutdown(sk, SHUT_RDWR);
+ exit(1);
+}
+
+static int send_command(int id, enum commands op, int arg1, int arg2)
+{
+ int sk = processes[id].sks[1], ret, status;
+ struct command cmd = {op, arg1, arg2};
+
+ if (op == TEST_FORK) {
+ if (processes[arg1].pid) {
+ err("%d is busy\n", arg1);
+ return -1;
+ }
+ }
+
+ ret = write(sk, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd)) {
+ err("Unable to send command");
+ goto err;
+ }
+
+ status = 0;
+ ret = read(sk, &status, sizeof(status));
+ if (ret != sizeof(status) && !(status == 0 && op == TEST_DIE)) {
+ err("Unable to get answer");
+ goto err;
+ }
+
+ if (status) {
+ err("The command(%d, %d, %d) failed");
+ goto err;
+ }
+
+ return 0;
+err:
+ cleanup();
+ exit(1);
+}
+
+int main(int argc, char ** argv)
+{
+ int pid, i;
+ int fail_cnt = 0;
+
+ test_init(argc, argv);
+
+ processes = mmap(NULL, PAGE_SIZE, PROT_WRITE | PROT_READ,
+ MAP_SHARED | MAP_ANONYMOUS, 0, 0);
+ if (processes == NULL) {
+ err("Unable to map share memory");
+ return 1;
+ }
+
+ for (i = 0; i < nr_processes; i++) {
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, processes[i].sks) == -1) {
+ err("socketpair");
+ return 1;
+ }
+ }
+
+ pid = make_child(0, 0);
+ if (pid < 0)
+ return -1;
+
+ /*
+ * 5 5 \_ session02 ( 0)
+ * 6 6 \_ session02 ( 1)
+ * 8 7 | \_ session02 ( 3)
+ * 15 12 | \_ session02 (10)
+ * 10 10 \_ session02 ( 5)
+ * 11 7 \_ session02 ( 6)
+ * 13 12 \_ session02 ( 8)
+ */
+
+ send_command(0, TEST_SUBREAPER, 1, 0);
+ send_command(0, TEST_SETSID, 0, 0);
+
+ send_command(0, TEST_FORK, 1, 0);
+ send_command(1, TEST_FORK, 2, 0);
+
+ send_command(2, TEST_SETSID, 0, 0);
+ send_command(2, TEST_FORK, 3, CLONE_PARENT);
+ send_command(2, TEST_DIE, 0, 0);
+ send_command(1, TEST_WAIT, 2, 0);
+
+ send_command(3, TEST_FORK, 4, 0);
+ send_command(4, TEST_FORK, 5, 0);
+ send_command(5, TEST_FORK, 6, 0);
+
+ send_command(5, TEST_FORK, 7, 0);
+ send_command(7, TEST_SETSID, 0, 0);
+ send_command(7, TEST_FORK, 8, CLONE_PARENT);
+ send_command(7, TEST_FORK, 9, CLONE_PARENT);
+ send_command(7, TEST_DIE, 0, 0);
+ send_command(5, TEST_WAIT, 7, 0);
+
+ send_command(9, TEST_FORK, 10, 0);
+ send_command(1, TEST_SUBREAPER, 1, 0);
+ send_command(9, TEST_DIE, 0, 0);
+ send_command(5, TEST_WAIT, 9, 0);
+ send_command(1, TEST_SUBREAPER, 0, 0);
+
+ send_command(4, TEST_DIE, 0, 0);
+ send_command(3, TEST_WAIT, 4, 0);
+
+ send_command(1, TEST_SETSID, 0, 0);
+ send_command(5, TEST_SETSID, 0, 0);
+
+ for (i = 0; i < nr_processes; i++) {
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid == 0)
+ continue;
+
+ processes[i].sid = getsid(processes[i].pid);
+ if (processes[i].sid == -1) {
+ err("getsid(%d)", i);
+ goto err;
+ }
+ }
+
+ test_daemon();
+
+ test_waitsig();
+
+ for (i = 0; i < nr_processes; i++) {
+ pid_t sid;
+
+ if (processes[i].dead)
+ continue;
+ if (processes[i].pid == 0)
+ continue;
+
+ sid = getsid(processes[i].pid);
+ if (sid == -1) {
+ err("getsid(%d)", i);
+ goto err;
+ }
+
+ if (sid != processes[i].sid) {
+ fail("%d, %d: wrong sid %d (expected %d)",
+ i, processes[i].pid, sid, processes[i].sid);
+ fail_cnt++;
+ }
+ }
+
+ if (fail_cnt)
+ goto err;
+
+ pass();
+
+ return 0;
+err:
+ cleanup();
+ return 1;
+}
--
1.7.11.7
More information about the CRIU
mailing list