diff --git a/cr-service.c b/cr-service.c index 2824d4931..79ba032bb 100644 --- a/cr-service.c +++ b/cr-service.c @@ -1,6 +1,289 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crtools.h" +#include "util-pie.h" +#include "log.h" #include "cr-service.h" -int cr_service() +struct _cr_service_client *cr_service_client; + +static int recv_criu_msg(int socket_fd, CriuMsg **msg) { + unsigned char buf[MAX_MSG_SIZE]; + int len; + + len = read(socket_fd, buf, MAX_MSG_SIZE); + if (len == -1) { + puts("Can't read request"); + return -1; + } + + *msg = criu_msg__unpack(NULL, len, buf); + if (!*msg) { + puts("Failed unpacking request"); + return -1; + } + + return 0; +} + +static int send_criu_msg(int socket_fd, CriuMsg *msg) +{ + unsigned char buf[MAX_MSG_SIZE]; + int len; + + len = criu_msg__get_packed_size(msg); + + if (criu_msg__pack(msg, buf) != len) { + pr_perror("Failed packing response"); + return -1; + } + + if (write(socket_fd, buf, len) == -1) { + pr_perror("Can't send response"); + return -1; + } + + return 0; +} + +int send_criu_dump_resp(int socket_fd, CriuDumpResp *resp) +{ + CriuMsg msg = CRIU_MSG__INIT; + + msg.type = CRIU_MSG__TYPE__DUMPRESP; + msg.dump_resp = resp; + + return send_criu_msg(socket_fd, &msg); +} + +static int setup_dump_from_req(CriuDumpReq *req) +{ + struct ucred ids; + struct stat st; + socklen_t ids_len = sizeof(struct ucred); + char images_dir_path[PATH_MAX]; + + if (getsockopt(cr_service_client->sk_fd, SOL_SOCKET, SO_PEERCRED, + &ids, &ids_len)) { + pr_perror("Can't get socket options."); + return -1; + } + + cr_service_client->pid = ids.pid; + cr_service_client->uid = ids.uid; + + if (req->pid == 0) + req->pid = ids.pid; + + if (fstat(cr_service_client->sk_fd, &st)) { + pr_perror("Can't get socket stat"); + return -1; + } + + cr_service_client->sk_ino = st.st_ino; + + /* going to dir, where to place images*/ + sprintf(images_dir_path, "/proc/%d/fd/%d", + cr_service_client->pid, req->images_dir_fd); + + if (chdir(images_dir_path)) { + pr_perror("Can't chdir to images directory"); + return -1; + } + + if (open_image_dir() < 0) + return -1; + + log_closedir(); + + /* initiate log file in imgs dir */ + opts.output = "./dump.log"; + + log_set_loglevel(req->log_level); + if (log_init(opts.output) == -1) { + pr_perror("Can't initiate log."); + return -1; + } + + /* checking dump flags from client */ + if (req->leave_running) + opts.final_state = TASK_ALIVE; + + opts.ext_unix_sk = req->ext_unix_sk; + opts.tcp_established_ok = req->tcp_established; + opts.evasive_devices = req->evasive_devices; + opts.shell_job = req->shell_job; + opts.handle_file_locks = req->file_locks; + + return 0; +} + +static int dump_using_req(CriuDumpReq *req) +{ + CriuDumpResp resp = CRIU_DUMP_RESP__INIT; + + if (setup_dump_from_req(req) == -1) { + pr_perror("Arguments treating fail"); + goto exit; + } + + if (cr_dump_tasks(req->pid) == -1) { + pr_perror("Dump fail"); + goto exit; + } + + resp.success = true; + +exit: + if (req->leave_running) { + if (send_criu_dump_resp(cr_service_client->sk_fd, + &resp) == -1) { + pr_perror("Can't send response"); + resp.success = false; + } + } + + close(cr_service_client->sk_fd); + return resp.success ? 0 : 1; +} + +int cr_service(bool daemon_mode) +{ + int server_fd; + int child_pid; + + struct sockaddr_un server_addr; + struct sockaddr_un client_addr; + + socklen_t server_addr_len; + socklen_t client_addr_len; + + CriuMsg *msg = 0; + CriuDumpResp resp = CRIU_DUMP_RESP__INIT; + + cr_service_client = malloc(sizeof(struct _cr_service_client)); + + server_fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0); + if (server_fd == -1) { + pr_perror("Can't initialize service socket."); + return -1; + } + + memset(&server_addr, 0, sizeof(server_addr)); + memset(&client_addr, 0, sizeof(client_addr)); + server_addr.sun_family = AF_LOCAL; + + if (opts.addr == NULL) + opts.addr = CR_DEFAULT_SERVICE_ADDRESS; + + strcpy(server_addr.sun_path, opts.addr); + + server_addr_len = strlen(server_addr.sun_path) + + sizeof(server_addr.sun_family); + client_addr_len = sizeof(client_addr); + + unlink(server_addr.sun_path); + + if (bind(server_fd, (struct sockaddr *) &server_addr, + server_addr_len) == -1) { + pr_perror("Can't bind."); + return -1; + } + + pr_info("The service socket is bound to %s\n", server_addr.sun_path); + + /* change service socket permissions, so anyone can connect to it */ + if (chmod(server_addr.sun_path, 0666)) { + pr_perror("Can't change permissions of the service socket."); + return -1; + } + + if (listen(server_fd, 16) == -1) { + pr_perror("Can't listen for socket connections."); + return -1; + } + + if (daemon_mode) { + if (daemon(0, 0) == -1) { + pr_perror("Can't run service server in the background"); + return -errno; + } + } + + /* FIXME Do not ignore children's return values */ + signal(SIGCHLD, SIG_IGN); + + while (1) { + pr_info("Waiting for connection...\n"); + + cr_service_client->sk_fd = accept(server_fd, + &client_addr, + &client_addr_len); + if (cr_service_client->sk_fd == -1) { + pr_perror("Can't accept connection."); + continue; + } + + pr_info("Connected.\n"); + + switch (child_pid = fork()) { + case -1: + pr_perror("Can't fork a child."); + continue; + + case 0: + if (recv_criu_msg(cr_service_client->sk_fd, + &msg) == -1) { + pr_perror("Can't recv request"); + goto err; + } + + switch (msg->type) { + case CRIU_MSG__TYPE__EMPTY: + pr_perror("Empty msg"); + goto err; + + case CRIU_MSG__TYPE__DUMPREQ: + exit(dump_using_req(msg->dump_req)); + + default: + pr_perror("Invalid request"); + goto err; + } + +err: + /* + * FIXME We're using CriuDumpResp here for now, + * but, when more requests will be added, + * they might require some special response, + * so we will need to use here some atomic resp, + * and extend it where needed. + */ + if (send_criu_dump_resp(cr_service_client->sk_fd, + &resp) == -1) + pr_perror("Can't send responce"); + + close(cr_service_client->sk_fd); + exit(-1); + + default: + close(cr_service_client->sk_fd); + } + } + return 0; } diff --git a/crtools.c b/crtools.c index 788710b10..9ce4496ac 100644 --- a/crtools.c +++ b/crtools.c @@ -336,7 +336,7 @@ int main(int argc, char *argv[]) return cr_page_server(opts.restore_detach); if (!strcmp(argv[optind], "service")) - return cr_service(); + return cr_service(opts.restore_detach); pr_msg("Unknown command \"%s\"\n", argv[optind]); usage: @@ -348,6 +348,7 @@ usage: " criu check [--ms]\n" " criu exec -p PID \n" " criu page-server\n" +" criu service []\n" "\n" "Commands:\n" " dump checkpoint a process/tree identified by pid\n" @@ -357,6 +358,7 @@ usage: " check checks whether the kernel support is up-to-date\n" " exec execute a system call by other task\n" " page-server launch page server\n" +" service launch service\n" ); if (argc < 2) { @@ -402,8 +404,8 @@ usage: " --prev-images-dir DIR path to images from previous dump (relative to -D)\n" " --page-server send pages to page server (see options below as well)\n" "\n" -"Page server options\n" -" --address ADDR address of page server\n" +"Page/Service server options\n" +" --address ADDR address of server or service\n" " --port PORT port of page server\n" " -d|--daemon run in the background after creating socket\n" "\n" diff --git a/include/cr-service.h b/include/cr-service.h index f99d9f188..fd5e3e649 100644 --- a/include/cr-service.h +++ b/include/cr-service.h @@ -1,6 +1,22 @@ #ifndef __CR_SERVICE_H__ #define __CR_SERVICE_H__ -int cr_service(void); +#include "protobuf/rpc.pb-c.h" + +#define CR_DEFAULT_SERVICE_ADDRESS "/tmp/criu_service.socket" +#define MAX_MSG_SIZE 1024 + +int cr_service(bool deamon_mode); + +int send_criu_dump_resp(int socket_fd, CriuDumpResp *resp); + +struct _cr_service_client { + int sk_ino; + int uid; + int pid; + int sk_fd; +}; + +extern struct _cr_service_client *cr_service_client; #endif