コード例 #1
0
ファイル: netns_sub.c プロジェクト: 0x7f454c46/criu
int main(int argc, char **argv)
{
	task_waiter_t lock;
	pid_t pid1, pid2, pid3, pid0 = getpid();
	int status = -1, sk;

	test_init(argc, argv);
	task_waiter_init(&lock);

	sk = create_socket(0);
	if (sk < 0)
		return 1;

	pid1 = fork();
	if (pid1 < 0) {
		pr_perror("fork");
		return -1;
	}
	if (pid1 == 0) {
		close(sk);
		unshare(CLONE_NEWNET);
		sk = create_socket(1);
		if (sk < 0)
			return 1;

		pid3 = fork();
		if (pid3 < 0) {
			pr_perror("fork");
			return 1;
		}
		if (pid3 == 0) {
			char ns[] = "/proc/0123456789/ns/net";
			int fd;

			snprintf(ns, sizeof(ns), "/proc/%d/ns/net", pid0);
			fd = open(ns, O_RDONLY);
			if (fd < 0)
				return 1;

			if (setns(fd, 0))
				return 1;
			close(fd);

			task_waiter_complete(&lock, 3);
			test_waitsig();

			if (check_socket(0, true))
				return 1;
			if (check_socket(2, false))
				return 1;
			if (check_socket(1, false))
				return 1;

			return 0;
		}
		/* This socket will be alive in the 3 process */
		close(sk);

		task_waiter_complete(&lock, 1);
		test_waitsig();

		if (check_socket(1, true))
			return 1;

		kill(pid3, SIGTERM);
		waitpid(pid3, &status, 0);
		if (status) {
			fail();
			return 1;
		}

		return 0;
	}
	pid2 = fork();
	if (pid2 < 0) {
		pr_perror("fork");
		return -1;
	}
	if (pid2 == 0) {
		unshare(CLONE_NEWNET);
		sk = create_socket(2);
		if (sk < 0)
			return 1;
		task_waiter_complete(&lock, 2);

		test_waitsig();

		if (check_socket(0, false))
			return 1;
		if (check_socket(1, false))
			return 1;
		if (check_socket(2, true))
			return 1;

		return 0;
	}
	close(sk);
	task_waiter_wait4(&lock, 1);
	task_waiter_wait4(&lock, 2);
	task_waiter_wait4(&lock, 3);

	test_daemon();
	test_waitsig();

	kill(pid1, SIGTERM);
	waitpid(pid1, &status, 0);
	if (status) {
		fail();
		return 1;
	}
	kill(pid2, SIGTERM);
	status = -1;
	waitpid(pid2, &status, 0);
	if (status) {
		fail();
		return 1;
	}
	pass();
	return 0;
}
コード例 #2
0
ファイル: pty02.c プロジェクト: blindFS/criu
int main(int argc, char *argv[])
{
	char buf[sizeof(teststr)];
	int master, slave, ret;
	char *slavename;
	task_waiter_t t;
	pid_t pid;

	test_init(argc, argv);

	master = open("/dev/ptmx", O_RDWR);
	if (master == -1) {
		err("open(%s) failed", "/dev/ptmx");
		return 1;
	}

	grantpt(master);
	unlockpt(master);

	slavename = ptsname(master);
	slave = open(slavename, O_RDWR);
	if (slave == -1) {
		err("open(%s) failed", slavename);
		return 1;
	}

	task_waiter_init(&t);

	pid = test_fork();
	if (pid == 0) {
		int new_master, ret;

		new_master = dup(master);
		if (new_master < 0) {
			err("can't dup master\n");
			exit_shot_parent(1);
		}

		task_waiter_complete_current(&t);

		ret = write(new_master, teststr, sizeof(teststr) - 1);
		if (ret != sizeof(teststr) - 1) {
			err("write(new_master) failed");
			exit_shot_parent(1);
		}

		task_waiter_wait4(&t, 1);

		close(new_master);
		exit(0);
	} else if (pid < 0) {
		err("test_fork failed: %m\n");
		exit(1);
	}

	task_waiter_wait4(&t, pid);
	close(master);

	test_daemon();
	test_waitsig();

	signal(SIGHUP, SIG_IGN);
	task_waiter_complete(&t, 1);

	ret = read(slave, buf, sizeof(teststr) - 1);
	if (ret != sizeof(teststr) - 1) {
		err("read(slave) failed");
		return 1;
	}

	if (strncmp(teststr, buf, sizeof(teststr) - 1)) {
		fail("data mismatch");
		return 1;
	}

	close(slave);

	pass();

	return 0;
}
コード例 #3
0
ファイル: mntns_remap.c プロジェクト: eabatalov/criu
int main(int argc, char **argv)
{
	task_waiter_t t;
	pid_t pid;

	test_init(argc, argv);

	mkdir(dirname, 0755);
	if (mount("zdtm", dirname, "tmpfs", 0, NULL)) {
		pr_perror("mount");
		return 1;
	}
	if (chdir(dirname)) {
		pr_perror("chdir");
		return 1;
	}
	mkdir("1", 0755);
	mkdir("2", 0755);
	if (mount("1", "1", NULL, MS_BIND, NULL)) {
		pr_perror("mount");
		return 1;
	}
	if (mount(NULL, "1", NULL, MS_PRIVATE, NULL)) {
		pr_perror("mount");
		return 1;
	}
	if (mount("zdtm", "2", "tmpfs", 0, NULL)) {
		pr_perror("mount");
		return 1;
	}
	mkdir("1/a", 0755);
	mkdir("2/a", 0755);
	if (mount("1/a", "1/a", NULL, MS_BIND, NULL)) {
		pr_perror("mount");
		return 1;
	}
	if (mount(NULL, "1/a", NULL, MS_SHARED, NULL)) {
		pr_perror("mount");
		return 1;
	}
	if (mount("1/a", "2/a", NULL, MS_BIND, NULL)) {
		pr_perror("mount");
		return 1;
	}
	mkdir("1/a/c", 0755);
	if (mount("zdtm", "1/a/c", "tmpfs", 0, NULL)) {
		pr_perror("mount");
		return 1;
	}
	if (mount("2", "1", NULL, MS_MOVE, NULL)) {
		pr_perror("mount");
		return 1;
	}

	task_waiter_init(&t);

	pid = fork();
	if (pid < 0)
		return -1;

	if (pid == 0) {
		if (unshare(CLONE_NEWNS))
			return 1;
		task_waiter_complete_current(&t);
		test_waitsig();
		return 0;
	}

	task_waiter_wait4(&t, pid);
	test_daemon();
	test_waitsig();

	kill(pid, SIGTERM);
	wait(NULL);

	pass();

	return 0;
}
コード例 #4
0
int main(int argc, char **argv)
{
	pthread_t diff_cred_thread;
	cap_t newcaps;
	int maingroup;
	int mainuser;
	int ret;

	test_init(argc, argv);
	task_waiter_init(&t);

	if (getuid() != 0) {
		fail("Test is expected to be run with root privileges\n");
		exit(1);
	}

	test_msg("Acquiring CAP_SETGID and CAP_SETUID...\n");

	newcaps = cap_from_text("cap_setgid,cap_setuid=+eip");
	if (!newcaps) {
		pr_perror("Failed to get capability struct\n");
		exit(1);
	}
	ret = cap_set_proc(newcaps);
	if (ret) {
		pr_perror("Failed to set capabilities for the process\n");
		exit(1);
	}
	ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
	if (ret) {
		pr_perror("Unable to set KEEPCAPS\n");
		exit(1);
	}

	test_msg("Main thread runs as UID: %d; GID: %d\n", getuid(), getgid());
	gid = 99;
	uid = 99;
	maingroup = 8;
	mainuser = 12;

	test_msg("Creating thread with different UID/GID\n");
	ret = pthread_create(&diff_cred_thread, NULL, &chg_uid_gid, NULL);
	task_waiter_wait4(&t, 1);

	test_msg("Relinquishing root privileges\n");
	ret = syscall(SYS_setresgid, maingroup, maingroup, maingroup);
	if (ret >= 0) {
		ret = syscall(SYS_setresuid, mainuser, mainuser, mainuser);
	} else if (ret < 0) {
		pr_perror("Failed to drop privileges\n");
		exit(1);
	}
	test_msg("Now main thread runs as UID: %d; GID: %d\n", getuid(), getgid());
	if (gid == getgid() || uid == getuid()) {
		pr_perror("Thread credentials match\n");
		exit(1);
	}
	test_msg("Main thread is waiting for signal\n");

	test_daemon();
	test_waitsig();

	if (gid == getgid() || uid == getuid()) {
		pr_perror("Thread credentials match after restore\n");
		exit(1);
	}

	pthread_mutex_lock(&mutex);
	done = 1;
	pthread_cond_signal(&cond);
	pthread_mutex_unlock(&mutex);
	pthread_join(diff_cred_thread, NULL);
	test_msg("Threads joined\n");

	pass();

	return 0;
}
コード例 #5
0
ファイル: socket_aio.c プロジェクト: Snorch/criu
int main(int argc, char **argv)
{
	char buf[BUF_SIZE];
	int fd, fd_s;
	struct aiocb aiocb;
	int status;
	pid_t pid;
	int ret, res;
	const struct aiocb   *aioary[1];
	task_waiter_t child_waiter;

	test_init(argc, argv);

	task_waiter_init(&child_waiter);

	if ((fd_s = tcp_init_server(AF_INET, &port)) < 0) {
		pr_err("initializing server failed\n");
		return 1;
	}

	pid = test_fork();
	if (pid < 0) {
		pr_perror("fork failed");
		return 1;
	}

	if (pid == 0) {
		/*
		 * Chiled is client of TCP connection
		 */
		close(fd_s);
		fd = tcp_init_client(AF_INET, "127.0.0.1", port);
		if (fd < 0)
			return 1;

		memset(&aiocb, 0, sizeof(struct aiocb));
		aiocb.aio_fildes = fd;
		aiocb.aio_buf = buf;
		aiocb.aio_nbytes = BUF_SIZE;
		ret = aio_read(&aiocb);
		if (ret < 0) {
			pr_perror("aio_read failed");
			return 1;
		}

		task_waiter_complete_current(&child_waiter);

		/* Wait for request completion */
		aioary[0] = &aiocb;
		ret = aio_error(&aiocb);
#ifdef DEBUG
		test_msg(".");
#endif
		res = 0;
again:
		if (aio_suspend(aioary, 1, NULL) < 0 && errno != EINTR) {
			pr_perror("aio_suspend failed");
			res = 1;
		}

		ret = aio_error(&aiocb);
		if (!res && ret == EINPROGRESS) {
#ifdef DEBUG
			test_msg("restart aio_suspend\n");
#endif
			goto again;
		}
		if (ret != 0) {
			pr_err("Error at aio_error(): %s\n", strerror(ret));
			res = 1;
		}

		if (aio_return(&aiocb) != BUF_SIZE) {
			pr_perror("Error at aio_return()");
			res = 1;
		}

		close(fd);
		return res;
	}

	/*
	 * parent is server of TCP connection
	 */
	fd = tcp_accept_server(fd_s);
	close(fd_s);
	if (fd < 0) {
		pr_err("can't accept client connection\n");
		goto error;
	}

	task_waiter_wait4(&child_waiter, pid);

	test_daemon();
	test_waitsig();

	if (write(fd, buf, BUF_SIZE) < BUF_SIZE) {
		pr_perror("can't write");
		goto error;
	}
	close(fd);


	if (wait(&status) < 0) {
		pr_perror("wait failed");
		goto error;
	}

	if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
		pr_err("child failed with exit code %d\n", WEXITSTATUS(status));
		return 1;
	}

	pass();
	return 0;
error:
	kill(pid, SIGKILL);
	wait(&status);
	return -1;
}
コード例 #6
0
int main(int argc, char **argv)
{
	char path[PATH_MAX];
	pid_t pid;
	int status, i;
	task_waiter_t t;

	test_init(argc, argv);

	task_waiter_init(&t);

	snprintf(path, sizeof(path), "%s/fs", dirname);
	if (mkdir(dirname, 0700)) {
		pr_perror("mkdir");
		return 1;
	}

	if (mount(NULL, "/", NULL, MS_SHARED, NULL)) {
		pr_perror("mount");
		return 1;
	}

	if (mount("zdtm_fs", dirname, "tmpfs", 0, NULL)) {
		pr_perror("mount");
		return 1;
	}

	if (mount(NULL, dirname, NULL, MS_PRIVATE, NULL)) {
		pr_perror("mount");
		return 1;
	}

	if (mkdir(path, 0700)) {
		pr_perror("mkdir");
		return 1;
	}

	if (mount("zdtm_fs", path, "tmpfs", 0, NULL)) {
		pr_perror("mount");
		return 1;
	}

	for (i = 0; i < 2; i++) {
		pid = fork();
		if (pid < 0) {
			pr_perror("fork");
			return 1;
		}
		if (pid == 0) {
			unshare(CLONE_NEWNS);

			task_waiter_complete(&t, 1);
			task_waiter_wait4(&t, 2);

			return 0;
		}
	}

	for (i = 0; i < 2; i++)
		task_waiter_wait4(&t, 1);

	test_daemon();
	test_waitsig();

	if (umount(path)) {
		pr_perror("Unable to umount %s", path);
		return 1;
	}
	if (umount(dirname)) {
		pr_perror("Unable to umount %s", dirname);
		return 1;
	}

	for (i = 0; i < 2; i++) {
		task_waiter_complete(&t, 2);

		if (waitpid(-1, &status, 0) < 0) {
			pr_perror("waitpid %d", pid);
			return 1;
		}

		if (status) {
			pr_err("%d/%d/%d/%d\n", WIFEXITED(status), WEXITSTATUS(status), WIFSIGNALED(status), WTERMSIG(status));
			return 1;
		}
	}

	pass();

	return 0;
}
コード例 #7
0
ファイル: tty00.c プロジェクト: 0x7f454c46/criu
int main(int argc, char ** argv)
{
	int fdm, fds, status;
	task_waiter_t t;
	char *slavename;
	pid_t pid;

	test_init(argc, argv);

	task_waiter_init(&t);

	fdm = open("/dev/ptmx", O_RDWR);
	if (fdm == -1) {
		pr_perror("Can't open a master pseudoterminal");
		return 1;
	}

	grantpt(fdm);
	unlockpt(fdm);
	slavename = ptsname(fdm);

	pid = test_fork();
	if (pid < 0) {
		pr_perror("fork() failed");
		return 1;
	}

	if (pid == 0) {
		close(fdm);
		signal(SIGHUP, sighup_handler);

		if (setsid() == -1)
			return 1;

		/* set up a controlling terminal */
		fds = open(slavename, O_RDWR);
		if (fds == -1) {
			pr_perror("Can't open a slave pseudoterminal %s", slavename);
			return 1;
		}

		if (ioctl(fdm, TIOCSCTTY, 1) < 0) {
			pr_perror("Can't setup a controlling terminal");
			return 1;
		}
		close(fds);

		task_waiter_complete_current(&t);

		test_waitsig();
		if (sighup)
			return 0;
		return 1;
	}

	task_waiter_wait4(&t, pid);

	test_daemon();

	test_waitsig();

	close(fdm);

	if (kill(pid, SIGTERM) == -1) {
		pr_perror("kill failed");
		return 1;
	}

	pid = waitpid(pid, &status, 0);
	if (pid < 0)
		return 1;

	if (WIFEXITED(status)) {
		if (WEXITSTATUS(status)) {
			fail("The child returned %d", WEXITSTATUS(status));
			return 1;
		}
	} else {
		test_msg("The child has been killed by %d\n", WTERMSIG(status));
		return 1;
	}

	pass();

	return 0;
}
コード例 #8
0
ファイル: socket-ext.c プロジェクト: cyrillos/criu
int main(int argc, char *argv[])
{
	struct sockaddr_un addr;
	unsigned int addrlen;
	task_waiter_t lock;

	char dir[] = "/tmp/zdtm.unix.sock.XXXXXX";
	char *path;
	pid_t pid;
	int ret, sk;
	char *val;

	unsetenv("ZDTM_GROUPS");
	val = getenv("ZDTM_GID");
	if (val && (setgid(atoi(val)) == -1)) {
		fprintf(stderr, "Can't set gid: %m");
		exit(1);
	}

	val = getenv("ZDTM_UID");
	if (val && (setuid(atoi(val)) == -1)) {
		fprintf(stderr, "Can't set uid: %m");
		exit(1);
	}

	if (mkdtemp(dir) < 0) {
		pr_perror("mkdtemp(%s) failed", dir);
		return 1;
	}
	chmod(dir, 0777);
	addr.sun_family = AF_UNIX;
	snprintf(addr.sun_path, sizeof(addr.sun_path),
			"%s/%s", dir, "sock");
	path = addr.sun_path;
	addrlen = sizeof(addr.sun_family) + strlen(path);

	task_waiter_init(&lock);

	pid = fork();
	if (pid < 0) {
		pr_perror("fork() failed");
		return 1;
	} else if (pid == 0) {
		char c;
		test_ext_init(argc, argv);

		sk = socket(AF_UNIX, SOCK_DGRAM, 0);
		if (sk < 0) {
			pr_perror("Can't create socket");
			return 1;
		}
		ret = bind(sk, (struct sockaddr *) &addr, addrlen);
		if (ret < 0) {
			pr_perror("Can't bind socket to %s", path);
			return 1;
		}
		chmod(dir, 0777);
		chmod(path, 0777);
		test_msg("The external socket %s\n", path);
		task_waiter_complete(&lock, 1);
		task_waiter_fini(&lock);

		recv(sk, &c, sizeof(c), 0);

		return 0;
	}

	task_waiter_wait4(&lock, 1);
	task_waiter_fini(&lock);

	test_init(argc, argv);

	sk = socket(AF_UNIX, SOCK_DGRAM, 0);
	if (sk < 0) {
		pr_perror("Can't create socket");
		return 1;
	}

	ret = connect(sk, (struct sockaddr *) &addr, addrlen);
	if (ret < 0) {
		pr_perror("Can't connect socket");
		return 1;
	}


	test_daemon();
	test_waitsig();

	if (unlink(path)) {
		pr_perror("Unable to remove %s\n", path);
		return 1;
	}
	if (rmdir(dir)) {
		pr_perror("Unable to remove %s", dir);
		return 1;
	}

	ret = send(sk, "H", 1, 0);
	if (ret != 1) {
		pr_perror("Can't send a symbol");
		fail();
		return 1;
	}

	pass();
	return 0;
}
コード例 #9
0
ファイル: mntns_root_bind.c プロジェクト: balamark/criu
int main(int argc, char **argv)
{
	char path[PATH_MAX], bpath[PATH_MAX], spath[PATH_MAX], bspath[PATH_MAX];
	pid_t pid;
	int status;
	task_waiter_t t;

	test_init(argc, argv);

	task_waiter_init(&t);

	mount(NULL, "/", NULL, MS_SHARED, NULL);

	snprintf(path, sizeof(path), "%s/test", dirname);
	snprintf(bpath, sizeof(bpath), "%s/test.bind", dirname);
	snprintf(spath, sizeof(spath), "%s/test/sub", dirname);
	snprintf(bspath, sizeof(bspath), "%s/test.bind/sub", dirname);

	if (mkdir(dirname, 0700) ||
	    mkdir(path, 0700) ||
	    mkdir(spath, 0700) ||
	    mkdir(bpath, 0700)) {
		err("mkdir");
		return 1;
	}

	pid = fork();
	if (pid < 0) {
		err("fork");
		return 1;
	}
	if (pid == 0) {
		unshare(CLONE_NEWNS);
		if (mount(path, bpath, NULL, MS_BIND, NULL)) {
			err("mount");
			return 1;
		}

		task_waiter_complete(&t, 1);
		task_waiter_wait4(&t, 2);

		if (access(bspath, F_OK)) {
			fail("%s isn't accessiable", bspath);
			return 1;
		}


		if (umount2(bpath, MNT_DETACH)) {
			fail("umount");
			return 1;
		}

		return 0;
	}

	task_waiter_wait4(&t, 1);

	if (mount("test", spath, "tmpfs", 0, NULL)) {
		err("mount");
		return 1;
	}

	test_daemon();
	test_waitsig();

	task_waiter_complete(&t, 2);

	if (waitpid(pid, &status, 0) != pid) {
		err("waitpid %d", pid);
		return 1;
	}

	if (status) {
		err("%d/%d/%d/%d", WIFEXITED(status), WEXITSTATUS(status), WIFSIGNALED(status), WTERMSIG(status));
		return 1;
	}

	pass();

	return 0;
}
コード例 #10
0
ファイル: mntns_ghost.c プロジェクト: 0x7f454c46/criu-1
int main(int argc, char **argv)
{
	task_waiter_t lock;
	pid_t pid = -1;
	int status = 1;

	task_waiter_init(&lock);
	test_init(argc, argv);

	pid = fork();
	if (pid < 0) {
		pr_perror("fork");
		return 1;
	}

	if (pid == 0) {
		int fd;
		DIR *d;
		struct dirent *de;

		if (unshare(CLONE_NEWNS)) {
			pr_perror("unshare");
			return 1;
		}
		if (mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL)) {
			pr_perror("mount");
			return 1;
		}

		if (mkdir(dirname, 0600) < 0) {
			pr_perror("mkdir");
			return 1;
		}

		if (mount(dirname, dirname, NULL, MS_BIND, NULL)) {
			pr_perror("mount");
			return 1;
		}
		if (chdir(dirname))
			return 1;

		fd = open("test.ghost", O_CREAT | O_WRONLY, 0600);
		if (fd < 0) {
			pr_perror("open");
			return 1;
		}

		if (unlink("test.ghost")) {
			pr_perror("unlink");
			return 1;
		}

		task_waiter_complete(&lock, 1);
		test_waitsig();

		if (close(fd)) {
			pr_perror("close");
			return 1;
		}
		d = opendir(".");
		if (d == NULL) {
			pr_perror("opendir");
			return 1;
		}
		while ((de = readdir(d)) != NULL) {
			if (!strcmp(de->d_name, "."))
				continue;
			if (!strcmp(de->d_name, ".."))
				continue;
			pr_err("%s\n", de->d_name);
		}
		closedir(d);

		return 0;
	}

	task_waiter_wait4(&lock, 1);
	test_daemon();
	test_waitsig();


	kill(pid, SIGTERM);
	wait(&status);
	if (status) {
		fail("Test died");
		return 1;
	}
	pass();

	return 0;
}
コード例 #11
0
ファイル: mntns_shared_bind.c プロジェクト: cyrillos/criu
int main(int argc, char **argv)
{
	char path[PATH_MAX], bpath[PATH_MAX], spath[PATH_MAX];
	pid_t pid;
	int status;
	task_waiter_t t;

	test_init(argc, argv);

	task_waiter_init(&t);

	snprintf(path, sizeof(path), "%s/test", dirname);
	snprintf(bpath, sizeof(bpath), "%s/test.bind", dirname);
	snprintf(spath, sizeof(spath), "%s/test/sub", dirname);
	if (mkdir(dirname, 0700)) {
		pr_perror("mkdir");
		return 1;
	}

	if (mount(NULL, "/", NULL, MS_SHARED, NULL)) {
		pr_perror("mount");
		return 1;
	}

#ifdef SHARED_BIND02
	/* */
	if (mount(dirname, dirname, "tmpfs", 0, NULL) ||
	    mount(NULL, dirname, NULL, MS_SHARED, NULL)) {
		pr_perror("mount");
		return 1;
	}
#endif

	if (mkdir(path, 0700) ||
	    mkdir(spath, 0700) ||
	    mkdir(bpath, 0700)) {
		pr_perror("mkdir");
		return 1;
	}

	pid = fork();
	if (pid < 0) {
		pr_perror("fork");
		return 1;
	}
	if (pid == 0) {
		if (unshare(CLONE_NEWNS)) {
			pr_perror("unshare");
			return 1;
		}
		if (mount(path, bpath, NULL, MS_BIND, NULL)) {
			pr_perror("mount");
			return 1;
		}

		task_waiter_complete(&t, 1);
		task_waiter_wait4(&t, 2);
		if (umount(spath)) {
			task_waiter_complete(&t, 2);
			fail("umount");
			return 1;
		}
		task_waiter_complete(&t, 3);
		task_waiter_wait4(&t, 4);

		return 0;
	}

	task_waiter_wait4(&t, 1);

	if (mount("test", spath, "tmpfs", 0, NULL)) {
		pr_perror("mount");
		return 1;
	}


	test_daemon();
	test_waitsig();

	task_waiter_complete(&t, 2);
	task_waiter_wait4(&t, 3);

	if (umount(bpath)) {
		task_waiter_complete(&t, 2);
		fail("umount");
		return 1;
	}

	task_waiter_complete(&t, 4);

	if (waitpid(pid, &status, 0) != pid) {
		pr_perror("waitpid %d", pid);
		return 1;
	}

	if (status) {
		pr_perror("%d/%d/%d/%d", WIFEXITED(status), WEXITSTATUS(status), WIFSIGNALED(status), WTERMSIG(status));
		return 1;
	}

	pass();

	return 0;
}