asterinas/test/initramfs/src/apps/clone3/clone_parent.c

168 lines
4.3 KiB
C

// SPDX-License-Identifier: MPL-2.0
#define _GNU_SOURCE
#include <linux/sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sched.h>
#include <fcntl.h>
#include "../test.h"
pid_t sys_clone3(struct clone_args *args)
{
return syscall(SYS_clone3, args, sizeof(struct clone_args));
}
FN_TEST(clone_process)
{
pid_t pid = getpid();
TEST_ERRNO(wait4(-1, NULL, 0, NULL), ECHILD);
pid_t child_pid = TEST_SUCC(fork());
if (child_pid == 0) {
// Child process
CHECK_WITH(getppid(), _ret == pid);
// CLONE_PARENT cannot be used if exit_signal is not 0
struct clone_args args = { .flags = CLONE_PARENT,
.exit_signal = SIGCHLD };
CHECK_WITH(sys_clone3(&args), errno == EINVAL);
args.exit_signal = 0;
// Check process group and session
pid_t sid = CHECK(setsid());
pid_t pgid = CHECK(getpgrp());
pid_t grandchild_pid = CHECK(sys_clone3(&args));
if (grandchild_pid == 0) {
// Grandchild process 1
CHECK_WITH(getppid(), _ret == pid);
CHECK_WITH(getpgrp(), _ret == pgid);
CHECK_WITH(getsid(0), _ret == sid);
exit(EXIT_SUCCESS);
}
// Check files
int pipefds[2];
CHECK(pipe(pipefds));
grandchild_pid = CHECK(sys_clone3(&args));
if (grandchild_pid == 0) {
// Grandchild process 2
CHECK(close(pipefds[0]));
char buf[1] = { 'a' };
CHECK_WITH(write(pipefds[1], buf, 1), _ret == 1);
exit(EXIT_SUCCESS);
}
CHECK(close(pipefds[1]));
char buf[1];
CHECK_WITH(read(pipefds[0], buf, 1),
_ret == 1 && buf[0] == 'a');
// Check child subreaper
CHECK(prctl(PR_SET_CHILD_SUBREAPER, 1));
int subreaper = 0;
CHECK_WITH(prctl(PR_GET_CHILD_SUBREAPER, &subreaper),
subreaper == 1);
grandchild_pid = CHECK(sys_clone3(&args));
if (grandchild_pid == 0) {
// Grandchild process 3
CHECK_WITH(prctl(PR_GET_CHILD_SUBREAPER, &subreaper),
subreaper == 0);
exit(EXIT_SUCCESS);
}
CHECK_WITH(wait4(-1, NULL, 0, NULL), errno == ECHILD);
exit(EXIT_SUCCESS);
}
// Parent process
int status = 0;
TEST_RES(wait4(-1, &status, 0, NULL),
WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
TEST_RES(wait4(-1, &status, 0, NULL),
WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
TEST_RES(wait4(-1, &status, 0, NULL),
WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
TEST_RES(wait4(-1, &status, 0, NULL),
WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
TEST_ERRNO(wait4(-1, NULL, 0, NULL), ECHILD);
}
END_TEST()
int pipefds[2];
// A stack for the new thread
#define STACK_SIZE (1024 * 1024) // 1MB stack
char child_stack[STACK_SIZE];
int child_function(void *arg)
{
char buf[1] = { 'a' };
CHECK(write(pipefds[1], buf, 1));
return 0;
}
FN_TEST(clone_thread)
{
char buf[1] = { 0 };
TEST_SUCC((pipe(pipefds)));
// Clone
TEST_SUCC(clone(child_function, child_stack + STACK_SIZE,
CLONE_PARENT | CLONE_THREAD | CLONE_VM | CLONE_SIGHAND,
NULL));
TEST_RES(read(pipefds[0], buf, 1), buf[0] == 'a');
// Clone
TEST_SUCC(clone(child_function, child_stack + STACK_SIZE,
CLONE_PARENT | CLONE_THREAD | CLONE_VM | CLONE_SIGHAND |
SIGCHLD,
NULL));
TEST_RES(read(pipefds[0], buf, 1), buf[0] == 'a');
// Clone3
struct clone_args args = { .flags = CLONE_PARENT | CLONE_THREAD |
CLONE_VM | CLONE_SIGHAND,
.exit_signal = SIGCHLD };
TEST_ERRNO(sys_clone3(&args), EINVAL);
// Clone3
args.flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND;
TEST_ERRNO(sys_clone3(&args), EINVAL);
}
END_TEST()
// The init process in each PID namespaces can not specify the CLONE_PARENT flags.
// The whole test case is commented out since Asterinas does not support PID namespace.
// FN_TEST(clone_init_process)
// {
// struct clone_args args = { .flags = CLONE_NEWPID };
//
// int child_pid = TEST_SUCC(sys_clone3(&args));
//
// if (child_pid == 0) {
// // Child process
// CHECK_WITH(getpid(), _ret == 1);
//
// args.flags = CLONE_PARENT;
// CHECK_WITH(sys_clone3(&args), errno == EINVAL);
//
// args.flags = CLONE_PARENT | CLONE_THREAD | CLONE_VM |
// CLONE_SIGHAND;
// CHECK_WITH(sys_clone3(&args), errno == EINVAL);
//
// exit(EXIT_SUCCESS);
// }
//
// int status = 0;
// TEST_RES(wait4(-1, &status, 0, NULL),
// _ret == child_pid && WIFEXITED(status) &&
// WEXITSTATUS(status) == EXIT_SUCCESS);
// TEST_ERRNO(wait4(-1, NULL, 0, NULL), ECHILD);
// }
// END_TEST()