mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-07-19 01:26:22 +00:00
416 lines
7.8 KiB
C
416 lines
7.8 KiB
C
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <sys/wait.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sched.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <sys/prctl.h>
|
|
#include <grp.h>
|
|
|
|
#include "zdtmtst.h"
|
|
#include "lock.h"
|
|
#include "ns.h"
|
|
|
|
futex_t sig_received;
|
|
/* clang-format off */
|
|
static struct {
|
|
futex_t stage;
|
|
} *test_shared_state;
|
|
/* clang-format on */
|
|
|
|
enum {
|
|
TEST_INIT_STAGE = 0,
|
|
TEST_RUNNING_STAGE,
|
|
TEST_FINI_STAGE,
|
|
TEST_FAIL_STAGE,
|
|
};
|
|
|
|
static int parent;
|
|
|
|
extern int criu_status_in, criu_status_in_peer, criu_status_out;
|
|
|
|
static void sig_hand(int signo)
|
|
{
|
|
if (parent)
|
|
futex_set_and_wake(&test_shared_state->stage, TEST_FAIL_STAGE);
|
|
futex_set_and_wake(&sig_received, signo);
|
|
if (criu_status_in >= 0)
|
|
close(criu_status_in);
|
|
}
|
|
|
|
static char *outfile;
|
|
TEST_OPTION(outfile, string, "output file", 1);
|
|
char *pidfile;
|
|
TEST_OPTION(pidfile, string, "file to store pid", 1);
|
|
|
|
static pid_t master_pid = 0;
|
|
|
|
int test_fork_id(int id)
|
|
{
|
|
return fork();
|
|
}
|
|
|
|
static int cwd = -1;
|
|
|
|
static void test_fini(void)
|
|
{
|
|
char path[PATH_MAX];
|
|
|
|
if (getpid() != master_pid)
|
|
return;
|
|
|
|
snprintf(path, sizeof(path), "%s%s", outfile, INPROGRESS);
|
|
renameat(cwd, path, cwd, outfile);
|
|
|
|
unlinkat(cwd, pidfile, 0);
|
|
}
|
|
|
|
static void setup_outfile(void)
|
|
{
|
|
if (!access(outfile, F_OK) || errno != ENOENT) {
|
|
fprintf(stderr, "Output file %s appears to exist, aborting\n", outfile);
|
|
exit(1);
|
|
}
|
|
|
|
cwd = open(".", O_RDONLY);
|
|
if (cwd < 0) {
|
|
fprintf(stderr, "Unable to open\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (atexit(test_fini)) {
|
|
fprintf(stderr, "Can't register exit function\n");
|
|
exit(1);
|
|
}
|
|
if (test_log_init(outfile, INPROGRESS))
|
|
exit(1);
|
|
}
|
|
|
|
static void redir_stdfds(void)
|
|
{
|
|
int nullfd;
|
|
|
|
nullfd = open("/dev/null", O_RDWR);
|
|
if (nullfd < 0) {
|
|
pr_perror("Can't open /dev/null");
|
|
exit(1);
|
|
}
|
|
|
|
dup2(nullfd, STDIN_FILENO);
|
|
if (nullfd > 2)
|
|
close(nullfd);
|
|
}
|
|
|
|
void test_ext_init(int argc, char **argv)
|
|
{
|
|
parseargs(argc, argv);
|
|
if (test_log_init(outfile, ".external"))
|
|
exit(1);
|
|
}
|
|
|
|
#define PIPE_RD 0
|
|
#define PIPE_WR 1
|
|
|
|
int init_notify(void)
|
|
{
|
|
char *val;
|
|
int ret;
|
|
int p[2];
|
|
|
|
val = getenv("ZDTM_NOTIFY_FDIN");
|
|
if (!val)
|
|
return 0;
|
|
criu_status_in = atoi(val);
|
|
|
|
val = getenv("ZDTM_NOTIFY_FDOUT");
|
|
if (!val)
|
|
return -1;
|
|
criu_status_out = atoi(val);
|
|
|
|
if (pipe(p)) {
|
|
fprintf(stderr, "Unable to create a pipe: %m\n");
|
|
return -1;
|
|
}
|
|
criu_status_in_peer = p[PIPE_WR];
|
|
|
|
ret = dup2(p[PIPE_RD], criu_status_in);
|
|
if (ret < 0) {
|
|
fprintf(stderr, "dup2() failed: %m\n");
|
|
close(p[PIPE_RD]);
|
|
close(p[PIPE_WR]);
|
|
return -1;
|
|
}
|
|
close(p[PIPE_RD]);
|
|
|
|
if (pipe(p)) {
|
|
fprintf(stderr, "Unable to create a pipe: %m\n");
|
|
goto err_pipe_in;
|
|
}
|
|
close(p[PIPE_RD]);
|
|
|
|
ret = dup2(p[PIPE_WR], criu_status_out);
|
|
if (ret < 0) {
|
|
fprintf(stderr, "dup2() failed: %m\n");
|
|
goto err_pipe_out;
|
|
}
|
|
|
|
close(p[PIPE_WR]);
|
|
return 0;
|
|
err_pipe_out:
|
|
close(p[PIPE_RD]);
|
|
close(p[PIPE_WR]);
|
|
err_pipe_in:
|
|
close(criu_status_in);
|
|
close(criu_status_in_peer);
|
|
return -1;
|
|
}
|
|
|
|
int write_pidfile(int pid)
|
|
{
|
|
int fd = -1;
|
|
char tmp[] = ".zdtm.pidfile.XXXXXX";
|
|
|
|
fd = mkstemp(tmp);
|
|
if (fd == -1) {
|
|
fprintf(stderr, "Can't create the file %s: %m\n", tmp);
|
|
return -1;
|
|
}
|
|
|
|
if (fchmod(fd, 0666) < 0) {
|
|
fprintf(stderr, "Can't fchmod %s: %m\n", tmp);
|
|
goto err_c;
|
|
}
|
|
|
|
if (dprintf(fd, "%d", pid) == -1) {
|
|
fprintf(stderr, "Can't write in the file %s: %m\n", tmp);
|
|
goto err_c;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
if (rename(tmp, pidfile) < 0) {
|
|
fprintf(stderr, "Can't rename %s to %s: %m\n", tmp, pidfile);
|
|
goto err_u;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_c:
|
|
close(fd);
|
|
err_u:
|
|
unlink(tmp);
|
|
return -1;
|
|
}
|
|
|
|
void test_init(int argc, char **argv)
|
|
{
|
|
pid_t pid;
|
|
char *val;
|
|
struct sigaction sa = {
|
|
.sa_handler = sig_hand,
|
|
.sa_flags = SA_RESTART,
|
|
};
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
parseargs(argc, argv);
|
|
|
|
val = getenv("ZDTM_NEWNS");
|
|
if (val) {
|
|
if (!strcmp(val, "1")) {
|
|
ns_create(argc, argv);
|
|
exit(1);
|
|
}
|
|
|
|
if (!strcmp(val, "2")) {
|
|
test_log_init(outfile, "ns");
|
|
redir_stdfds();
|
|
ns_init(argc, argv);
|
|
}
|
|
} else if (init_notify()) {
|
|
fprintf(stderr, "Can't init pre-dump notification: %m");
|
|
exit(1);
|
|
}
|
|
|
|
val = getenv("ZDTM_ROOTLESS");
|
|
if (!val) {
|
|
val = getenv("ZDTM_GROUPS");
|
|
if (val) {
|
|
char *tok = NULL;
|
|
unsigned int size = 0, groups[NGROUPS_MAX];
|
|
|
|
tok = strtok(val, " ");
|
|
while (tok) {
|
|
size++;
|
|
groups[size - 1] = atoi(tok);
|
|
tok = strtok(NULL, " ");
|
|
}
|
|
|
|
if (setgroups(size, groups)) {
|
|
fprintf(stderr, "Can't set groups: %m");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
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 gid: %m");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (prctl(PR_SET_DUMPABLE, 1)) {
|
|
fprintf(stderr, "Can't set the dumpable flag");
|
|
exit(1);
|
|
}
|
|
|
|
if (sigaction(SIGTERM, &sa, NULL)) {
|
|
fprintf(stderr, "Can't set SIGTERM handler: %m\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (sigaction(SIGCHLD, &sa, NULL)) {
|
|
fprintf(stderr, "Can't set SIGCHLD handler: %m\n");
|
|
exit(1);
|
|
}
|
|
|
|
setup_outfile();
|
|
redir_stdfds();
|
|
|
|
test_shared_state = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
|
|
if (test_shared_state == MAP_FAILED) {
|
|
pr_perror("Unable to map a shared memory");
|
|
exit(1);
|
|
}
|
|
|
|
futex_init(&test_shared_state->stage);
|
|
futex_set(&test_shared_state->stage, TEST_INIT_STAGE);
|
|
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
pr_perror("Daemonizing failed");
|
|
exit(1);
|
|
}
|
|
|
|
parent = 1;
|
|
if (pid) { /* parent will exit when the child is ready */
|
|
futex_wait_while(&test_shared_state->stage, TEST_INIT_STAGE);
|
|
|
|
if (futex_get(&test_shared_state->stage) == TEST_FAIL_STAGE) {
|
|
int ret;
|
|
if (waitpid(pid, &ret, 0) != pid) {
|
|
pr_perror("Unable to wait %d", pid);
|
|
exit(1);
|
|
}
|
|
|
|
if (WIFEXITED(ret)) {
|
|
pr_err("Test exited unexpectedly with code %d\n", WEXITSTATUS(ret));
|
|
exit(1);
|
|
}
|
|
if (WIFSIGNALED(ret)) {
|
|
pr_err("Test exited on unexpected signal %d\n", WTERMSIG(ret));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (write_pidfile(pid))
|
|
exit(1);
|
|
|
|
_exit(0);
|
|
}
|
|
parent = 0;
|
|
|
|
if (setsid() < 0) {
|
|
pr_perror("Can't become session group leader");
|
|
exit(1);
|
|
}
|
|
|
|
/* record the test pid to remember the ownership of the pidfile */
|
|
master_pid = getpid();
|
|
|
|
sa.sa_handler = SIG_DFL;
|
|
if (sigaction(SIGCHLD, &sa, NULL)) {
|
|
pr_perror("Can't reset SIGCHLD handler");
|
|
exit(1);
|
|
}
|
|
|
|
srand48(time(NULL)); /* just in case we need it */
|
|
}
|
|
|
|
void test_daemon(void)
|
|
{
|
|
futex_set_and_wake(&test_shared_state->stage, TEST_RUNNING_STAGE);
|
|
}
|
|
|
|
int test_go(void)
|
|
{
|
|
return !futex_get(&sig_received);
|
|
}
|
|
|
|
void test_waitsig(void)
|
|
{
|
|
futex_wait_while(&sig_received, 0);
|
|
}
|
|
|
|
int test_wait_pre_dump(void)
|
|
{
|
|
int ret;
|
|
|
|
if (criu_status_in < 0) {
|
|
pr_err("Fd criu_status_in is not initialized\n");
|
|
return -1;
|
|
}
|
|
|
|
if (read(criu_status_in, &ret, sizeof(ret)) != sizeof(ret)) {
|
|
if (errno != EBADF || !futex_get(&sig_received))
|
|
pr_perror("Can't wait pre-dump");
|
|
return -1;
|
|
}
|
|
pr_err("pre-dump\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int test_wait_pre_dump_ack(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (criu_status_out < 0) {
|
|
pr_err("Fd criu_status_out is not initialized\n");
|
|
return -1;
|
|
}
|
|
|
|
pr_err("pre-dump-ack\n");
|
|
if (write(criu_status_out, &ret, sizeof(ret)) != sizeof(ret)) {
|
|
pr_perror("Can't reply to pre-dump notify");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
pid_t sys_clone_unified(unsigned long flags, void *child_stack, void *parent_tid, void *child_tid, unsigned long newtls)
|
|
{
|
|
#ifdef __x86_64__
|
|
return (pid_t)syscall(__NR_clone, flags, child_stack, parent_tid, child_tid, newtls);
|
|
#elif (__i386__ || __arm__ || __aarch64__ || __powerpc64__ || __mips__ || __loongarch64 || __riscv)
|
|
return (pid_t)syscall(__NR_clone, flags, child_stack, parent_tid, newtls, child_tid);
|
|
#elif __s390x__
|
|
return (pid_t)syscall(__NR_clone, child_stack, flags, parent_tid, child_tid, newtls);
|
|
#else
|
|
#error "Unsupported architecture"
|
|
#endif
|
|
}
|