mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-31 14:25:49 +00:00
zdtm: add one more test case to create a process tree (v2)
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@openvz.org> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
committed by
Pavel Emelyanov
parent
d7829276be
commit
cc97a2d012
@@ -84,6 +84,7 @@ TST_NOFILE = \
|
||||
mountpoints \
|
||||
netns \
|
||||
session01 \
|
||||
session02 \
|
||||
socket-ext \
|
||||
unhashed_proc \
|
||||
cow00 \
|
||||
|
328
test/zdtm/live/static/session02.c
Normal file
328
test/zdtm/live/static/session02.c
Normal file
@@ -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@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;
|
||||
}
|
Reference in New Issue
Block a user