2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-22 18:07:57 +00:00
criu/lib/criu.c

348 lines
5.6 KiB
C
Raw Normal View History

#include "version.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include "criu.h"
#include "rpc.pb-c.h"
#include "cr-service-const.h"
const char *criu_lib_version = CRIU_VERSION;
static char *service_address = CR_DEFAULT_SERVICE_ADDRESS;
static CriuOpts *opts;
static int saved_errno;
void criu_set_service_address(char *path)
{
if (path)
service_address = path;
else
service_address = CR_DEFAULT_SERVICE_ADDRESS;
}
int criu_init_opts(void)
{
if (opts)
criu_opts__free_unpacked(opts, NULL);
opts = malloc(sizeof(CriuOpts));
if (opts == NULL) {
perror("Can't allocate memory for criu opts");
return -1;
}
criu_opts__init(opts);
return 0;
}
void criu_set_pid(int pid)
{
opts->has_pid = true;
opts->pid = pid;
}
void criu_set_images_dir_fd(int fd)
{
opts->images_dir_fd = fd;
}
void criu_set_work_dir_fd(int fd)
{
opts->has_work_dir_fd = true;
opts->work_dir_fd = fd;
}
void criu_set_leave_running(bool leave_running)
{
opts->has_leave_running = true;
opts->leave_running = leave_running;
}
void criu_set_ext_unix_sk(bool ext_unix_sk)
{
opts->has_ext_unix_sk = true;
opts->ext_unix_sk = ext_unix_sk;
}
void criu_set_tcp_established(bool tcp_established)
{
opts->has_tcp_established = true;
opts->tcp_established = tcp_established;
}
void criu_set_evasive_devices(bool evasive_devices)
{
opts->has_evasive_devices = true;
opts->evasive_devices = evasive_devices;
}
void criu_set_shell_job(bool shell_job)
{
opts->has_shell_job = true;
opts->shell_job = shell_job;
}
void criu_set_file_locks(bool file_locks)
{
opts->has_file_locks = true;
opts->file_locks = file_locks;
}
void criu_set_log_level(int log_level)
{
opts->has_log_level = true;
opts->log_level = log_level;
}
void criu_set_root(char *root)
{
opts->root = strdup(root);
}
void criu_set_log_file(char *log_file)
{
opts->log_file = strdup(log_file);
}
void criu_set_cpu_cap(unsigned int cap)
{
opts->has_cpu_cap = true;
opts->cpu_cap = cap;
}
int criu_set_exec_cmd(int argc, char *argv[])
criu: Add exec-cmd option (v3) The --exec-cmd option specifies a command that will be execvp()-ed on successful restore. This way the command specified here will become the parent process of the restored process tree. Waiting for the restored processes to finish is responsibility of this command. All service FDs are closed before we call execvp(). Standad output and error of the command are redirected to the log file when we are restoring through the RPC service. This option will be used when restoring LinuX Containers and it seems helpful for perf or other use cases when restored processes must be supervised by a parent. Two directions were researched in order to integrate CRIU and LXC: 1. We tell to CRIU, that after restoring container is should execve() lxc properly explaining to it that there's a new container hanging around. 2. We make LXC set himself as child subreaper, then fork() criu and ask it to detach (-d) from restore container afterwards. Being a subreaper, it should get the container's init into his child list after it. The main reason for choosing the first option is that the second one can't work with the RPC service. If we call restore via the service then criu service will be the top-most task in the hierarchy and will not be able to reparent the restore trees to any other task in the system. Calling execve from service worker sub-task (and daemonizing it) should solve this. Signed-off-by: Deyan Doychev <deyandoichev@gmail.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-03-22 20:14:00 +04:00
{
int i;
criu: Add exec-cmd option (v3) The --exec-cmd option specifies a command that will be execvp()-ed on successful restore. This way the command specified here will become the parent process of the restored process tree. Waiting for the restored processes to finish is responsibility of this command. All service FDs are closed before we call execvp(). Standad output and error of the command are redirected to the log file when we are restoring through the RPC service. This option will be used when restoring LinuX Containers and it seems helpful for perf or other use cases when restored processes must be supervised by a parent. Two directions were researched in order to integrate CRIU and LXC: 1. We tell to CRIU, that after restoring container is should execve() lxc properly explaining to it that there's a new container hanging around. 2. We make LXC set himself as child subreaper, then fork() criu and ask it to detach (-d) from restore container afterwards. Being a subreaper, it should get the container's init into his child list after it. The main reason for choosing the first option is that the second one can't work with the RPC service. If we call restore via the service then criu service will be the top-most task in the hierarchy and will not be able to reparent the restore trees to any other task in the system. Calling execve from service worker sub-task (and daemonizing it) should solve this. Signed-off-by: Deyan Doychev <deyandoichev@gmail.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-03-22 20:14:00 +04:00
opts->n_exec_cmd = argc;
opts->exec_cmd = malloc((argc) * sizeof(char *));
if (opts->exec_cmd) {
for (i = 0; i < argc; i++) {
opts->exec_cmd[i] = strdup(argv[i]);
if (!opts->exec_cmd[i]) {
while (i > 0)
free(opts->exec_cmd[i--]);
free(opts->exec_cmd);
opts->n_exec_cmd = 0;
opts->exec_cmd = NULL;
goto out;
}
}
return 0;
}
out:
return -ENOMEM;
criu: Add exec-cmd option (v3) The --exec-cmd option specifies a command that will be execvp()-ed on successful restore. This way the command specified here will become the parent process of the restored process tree. Waiting for the restored processes to finish is responsibility of this command. All service FDs are closed before we call execvp(). Standad output and error of the command are redirected to the log file when we are restoring through the RPC service. This option will be used when restoring LinuX Containers and it seems helpful for perf or other use cases when restored processes must be supervised by a parent. Two directions were researched in order to integrate CRIU and LXC: 1. We tell to CRIU, that after restoring container is should execve() lxc properly explaining to it that there's a new container hanging around. 2. We make LXC set himself as child subreaper, then fork() criu and ask it to detach (-d) from restore container afterwards. Being a subreaper, it should get the container's init into his child list after it. The main reason for choosing the first option is that the second one can't work with the RPC service. If we call restore via the service then criu service will be the top-most task in the hierarchy and will not be able to reparent the restore trees to any other task in the system. Calling execve from service worker sub-task (and daemonizing it) should solve this. Signed-off-by: Deyan Doychev <deyandoichev@gmail.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-03-22 20:14:00 +04:00
}
static CriuResp *recv_resp(int socket_fd)
{
unsigned char buf[CR_MAX_MSG_SIZE];
int len;
CriuResp *msg = 0;
len = read(socket_fd, buf, CR_MAX_MSG_SIZE);
if (len == -1) {
perror("Can't read response");
goto err;
}
msg = criu_resp__unpack(NULL, len, buf);
if (!msg) {
perror("Failed unpacking response");
goto err;
}
return msg;
err:
saved_errno = errno;
return NULL;
}
static int send_req(int socket_fd, CriuReq *req)
{
unsigned char buf[CR_MAX_MSG_SIZE];
int len;
len = criu_req__get_packed_size(req);
if (criu_req__pack(req, buf) != len) {
perror("Failed packing request");
goto err;
}
if (write(socket_fd, buf, len) == -1) {
perror("Can't send request");
goto err;
}
return 0;
err:
saved_errno = errno;
return -1;
}
static int criu_connect(void)
{
int fd, ret;
struct sockaddr_un addr;
socklen_t addr_len;
fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
if (fd < 0) {
saved_errno = errno;
perror("Can't create socket");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_LOCAL;
strncpy(addr.sun_path, service_address, sizeof(addr.sun_path));
addr_len = strlen(addr.sun_path) + sizeof(addr.sun_family);
ret = connect(fd, (struct sockaddr *) &addr, addr_len);
if (ret < 0) {
saved_errno = errno;
perror("Can't connect to socket");
close(fd);
return -1;
}
return fd;
}
static int send_req_and_recv_resp(CriuReq *req, CriuResp **resp)
{
int fd;
int ret = 0;
fd = criu_connect();
if (fd < 0) {
perror("Can't connect to criu");
ret = ECONNREFUSED;
goto exit;
}
if (send_req(fd, req) < 0) {
ret = ECOMM;
goto exit;
}
*resp = recv_resp(fd);
if (!*resp) {
perror("Can't receive response");
ret = ECOMM;
goto exit;
}
if ((*resp)->type != req->type) {
if ((*resp)->type == CRIU_REQ_TYPE__EMPTY &&
(*resp)->success == false)
ret = EINVAL;
else {
perror("Unexpected response type");
ret = EBADMSG;
}
}
exit:
if (fd >= 0)
close(fd);
return -ret;
}
int criu_check(void)
{
int ret = -1;
CriuReq req = CRIU_REQ__INIT;
CriuResp *resp = NULL;
saved_errno = 0;
req.type = CRIU_REQ_TYPE__CHECK;
ret = send_req_and_recv_resp(&req, &resp);
if (ret)
goto exit;
ret = resp->success ? 0 : -EBADE;
exit:
if (resp)
criu_resp__free_unpacked(resp, NULL);
errno = saved_errno;
return ret;
}
int criu_dump(void)
{
int ret = -1;
CriuReq req = CRIU_REQ__INIT;
CriuResp *resp = NULL;
saved_errno = 0;
req.type = CRIU_REQ_TYPE__DUMP;
req.opts = opts;
ret = send_req_and_recv_resp(&req, &resp);
if (ret)
goto exit;
if (resp->success) {
if (resp->dump->has_restored && resp->dump->restored)
ret = 1;
else
ret = 0;
} else
ret = -EBADE;
exit:
if (resp)
criu_resp__free_unpacked(resp, NULL);
errno = saved_errno;
return ret;
}
int criu_restore(void)
{
int ret = -1;
CriuReq req = CRIU_REQ__INIT;
CriuResp *resp = NULL;
saved_errno = 0;
req.type = CRIU_REQ_TYPE__RESTORE;
req.opts = opts;
ret = send_req_and_recv_resp(&req, &resp);
if (ret)
goto exit;
if (resp->success)
ret = resp->restore->pid;
else
ret = -EBADE;
exit:
if (resp)
criu_resp__free_unpacked(resp, NULL);
errno = saved_errno;
return ret;
}