[CRIU] [PATCHv4 4/4] crtools: cr_service() and fixes for it
Ruslan Kuprieiev
kupruser at gmail.com
Fri Sep 6 08:22:35 EDT 2013
On 09/06/2013 08:47 AM, Andrew Vagin wrote:
> On Tue, Sep 03, 2013 at 06:44:35PM +0400, Ruslan Kuprieiev wrote:
>> cr_service() is function to start daemon, that will create unix
>> domain socket and will listen for requests from libcriu on it. If
>> request is obtained, it will fork() child, that will recv, process
>> arguments and dump program.
>> diff --git a/cr-service.c b/cr-service.c
>> index e921828..7ac1a04 100644
>> --- a/cr-service.c
>> +++ b/cr-service.c
>> @@ -1,4 +1,260 @@
>> +#ifndef _GNU_SOURCE
>> +#define _GNU_SOURCE
>> +#endif
>> +
>> +#include <unistd.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <errno.h>
>> +#include <string.h>
>> +#include <sys/types.h>
>> +#include <sys/socket.h>
>> +#include <sys/un.h>
>> +#include <sys/wait.h>
>> +#include <sys/stat.h>
>> +
>> +#include "crtools.h"
>> +#include "util-pie.h"
>> +#include "log.h"
>> +#include "libcriu.h"
>> +#include "version.h"
>> +#include "protobuf/criu-dump-args.pb-c.h"
>> +
>> +
>> +static CriuDumpArgs *args;
>> +static int client_fd;
>> +
>> +static int criu_recv_dump_args(int socket_fd)
>> +{
>> + int len;
>> + unsigned char *buf;
>> + /*
>> + * Recv the size of args and then
>> + * recv args.
>> + */
>> + if (recv(socket_fd, &len, sizeof(len), 0) == -1) {
>> + pr_perror("Can't recv size of args");
>> + return -1;
>> + }
>> +
>> + buf = malloc(len);
> Use xmallo/xfree
>
>> + if (buf == NULL) {
>> + pr_perror("Can't allocate memory");
>> + return -1;
>> + }
>> +
>> + errno = 0;
> isn't required
>
>> + if (recv(socket_fd, buf, len, MSG_WAITALL) == -1) {
>> + if (errno == EAGAIN || errno == EWOULDBLOCK)
>> + pr_perror("Recv timeout");
>> + pr_perror("Can't recv args");
>> + return -1;
>> + }
>> +
>> + args = criu_dump_args__unpack(NULL, len, buf);
>> + if (args == NULL) {
>> + pr_perror("Can't unpack msg");
> Usually such messages is printed from criu_dump_args__unpack
>> + return -1;
>> + }
>> +
>> + /*
>> + * Recv opened dir fd, where to put images
>> + */
>> + args->images_dir_fd = recv_fd(socket_fd);
>> + if (args->images_dir_fd == -1) {
>> + pr_perror("Can't recv file descriptor");
> recv_fd prints a similar error message
>
>> + return -1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * This function is needed in sk-unix.c,
>> + * to recognize connection to service socket
>> + * and dump it properly.
>> + */
>> +int get_lib_sk_ino(void)
>> +{
>> + struct stat st;
>> +
>> + if (fstat(client_fd, &st)) {
>> + pr_perror("Can't get library socket stat");
>> + return -1;
>> + }
>> +
>> + return st.st_ino;
>> +}
>> +
>> +static int get_client_pid(void)
>> +{
>> + struct ucred ids;
>> + socklen_t ids_len;
>> + ids_len = sizeof(struct ucred);
>> +
>> + if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ids, &ids_len)) {
>> + pr_perror("Can't get socket options.");
>> + return -1;
>> + }
>> +
>> + return ids.pid;
>> +}
>> +
>> +static int child_work(void)
>> +{
>> + int pid;
>> + int ret;
>> +
>> + if (criu_recv_dump_args(client_fd) == -1) {
>> + pr_perror("Can't recv args");
>> + goto child_err;
>> + }
>> +
>> + /* check version */
>> + if (args->version_major != CRIU_VERSION_MAJOR ||
>> + args->version_minor != CRIU_VERSION_MINOR) {
>> + pr_perror("Looks like criu (version %s) is out-of-date. "
>> + "Please, upgrade criu.", version);
>> + ret = CRIU_OLD_VERSION;
>> + goto child_out;
>> + }
>> +
>> + pid = get_client_pid();
>> + if (pid == -1) {
>> + pr_perror("Can't get pid");
>> + goto child_err;
>> + }
>> +
>> + /* going to dir, where to place images*/
>> + if (fchdir(args->images_dir_fd)) {
>> + pr_perror("Can't change directory.");
>> + goto child_err;
>> + }
>> +
>> + /* initiate log file in imgs dir */
>> + opts.output = malloc(sizeof(char)*PATH_MAX);
>> + strcpy(opts.output, getcwd(NULL, PATH_MAX));
>> + strcat(opts.output, "/dump.log");
>> +
>> + if (open_image_dir() < 0) {
>> + pr_perror("Can't open current directory.");
>> + goto child_err;
>> + }
>> +
>> + /* FIXME: log level of dump.log is the same as log level,
>> + * which was set on start of criu service. Add log level
>> + * to args.
>> + */
>> + if (log_init(opts.output) == -1) {
>> + pr_perror("Can't initiate log.");
>> + goto child_err;
>> + }
>> +
>> + /* checking dump flags from client */
>> + if (args->flags & CRIU_DUMP_AND_CONTINUE) {
>> + opts.final_state = TASK_ALIVE;
>> + args->flags &= CRIU_DUMP_AND_CONTINUE;
>> + ret = CRIU_DUMP_SUCCESS; /* pre set */
>> + }
>> +
>> + if (args->flags != 0) {
>> + /*
>> + * FIXME add flag for bad args to libcriu.h
>> + */
>> + pr_perror("Bad flags");
>> + goto child_err;
>> + }
>> +
>> + /* dump */
>> + if (cr_dump_tasks(pid) < 0) {
>> + pr_perror("Dump failed.");
>> + goto child_err;
>> + }
>> +
>> + close(client_fd);
>> + exit(0);
>> +
>> +child_err:
>> + ret = CRIU_DUMP_FAIL;
>> +child_out:
>> + if (send(client_fd, &ret, sizeof(ret), 0) == -1)
>> + pr_perror("Can't send msg to lib");
> I think we will use a protobuf message here too.
>
>> +
>> + close(client_fd);
>> + exit(1);
>> +}
>> +
>> int cr_service(void)
>> {
>> + 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;
>> +
>> + server_fd = socket(AF_LOCAL, SOCK_STREAM, 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;
>> + }
>> +
>> + /* 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;
>> + }
> Should this service daemonizes here? Look at page-server for example.
>
>> +
>> + if (listen(server_fd, 16) == -1) {
>> + pr_perror("Can't listen for socket connections.");
>> + return -1;
>> + }
>> +
>> + /* FIXME Do not ignore children's return values */
>> + signal(SIGCHLD, SIG_IGN);
>> +
>> + while (1) {
>> + pr_info("Waiting for connection...\n");
>> +
>> + client_fd = accept(server_fd, &client_addr, &client_addr_len);
>> + if (client_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:
>> + exit(child_work());
>> + default:
>> + close(client_fd);
>> + }
>> + }
>> +
>> return 0;
>> }
>> diff --git a/include/crtools.h b/include/crtools.h
>> index d867522..ce7466d 100644
>> --- a/include/crtools.h
>> +++ b/include/crtools.h
>> @@ -121,6 +121,8 @@ int cr_check(void);
>> int cr_exec(int pid, char **opts);
>> int cr_service(void);
>>
>> +int get_lib_sk_ino(void);
>> +
>> #define O_DUMP (O_RDWR | O_CREAT | O_EXCL)
>> #define O_SHOW (O_RDONLY)
>> #define O_RSTR (O_RDONLY)
>> diff --git a/include/image.h b/include/image.h
>> index 5cd7741..b201226 100644
>> --- a/include/image.h
>> +++ b/include/image.h
>> @@ -18,6 +18,7 @@
>> #define REMAP_GHOST (1 << 31)
>>
>> #define USK_EXTERN (1 << 0)
>> +#define USK_LIB_CTRL (1 << 1)
>>
>> #define VMA_AREA_NONE (0 << 0)
>> #define VMA_AREA_REGULAR (1 << 0) /* Dumpable area */
>> diff --git a/sk-unix.c b/sk-unix.c
>> index cf024a5..365c5ac 100644
>> --- a/sk-unix.c
>> +++ b/sk-unix.c
>> @@ -21,6 +21,7 @@
>> #include "sockets.h"
>> #include "sk-queue.h"
>> #include "mount.h"
>> +#include "libcriu.h"
>>
>> #include "protobuf.h"
>> #include "protobuf/sk-unix.pb-c.h"
>> @@ -138,6 +139,17 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
>> ue.opts = &skopts;
>> ue.uflags = 0;
>>
>> + /*
>> + * Check if this is library socket,
>> + * that is connected to criu service.
>> + * Dump it like closed one and mark it for restore.
>> + */
>> + if (ue.peer == get_lib_sk_ino()) {
>> + ue.state = TCP_CLOSE;
>> + ue.peer = 0;
>> + ue.uflags |= USK_LIB_CTRL;
>> + }
>> +
>> if (sk->namelen && *sk->name) {
>> ue.file_perms = &perms;
>>
>> @@ -716,7 +728,23 @@ static int open_unixsk_standalone(struct unix_sk_info *ui)
>> pr_info("Opening standalone socket (id %#x ino %#x peer %#x)\n",
>> ui->ue->id, ui->ue->ino, ui->ue->peer);
>>
>> - if ((ui->ue->state == TCP_ESTABLISHED) && !ui->ue->peer) {
>> + if (ui->ue->uflags & USK_LIB_CTRL) {
>> + int sks[2];
>> +
>> + if (socketpair(PF_UNIX, ui->ue->type, 0, sks)) {
>> + pr_perror("Can't create socketpair");
>> + return -1;
>> + }
>> +
>> + int msg = CRIU_RESUME;
>> + if (write(sks[1], &msg, sizeof(msg)) != sizeof(msg)) {
>> + pr_perror("Can't write msg for lib to socket");
>> + return -1;
>> + }
>> +
>> + close(sks[1]);
>> + sk = sks[0];
>> + } else if ((ui->ue->state == TCP_ESTABLISHED) && !ui->ue->peer) {
>> int ret, sks[2];
>>
>> if (ui->ue->type != SOCK_STREAM) {
>>
>> _______________________________________________
>> CRIU mailing list
>> CRIU at openvz.org
>> https://lists.openvz.org/mailman/listinfo/criu
Thanks=).
Now i`m rewriting it to use protobuf and trying to fix my mistakes.
By the way, as we decided to use protobuf, i wonder how we should check
versions сompatibility?
Am i right, that we just need to give user *.proto files, and if
cr_service() would get request that differs from structure it was
compiled with, protobuf would not unpack it?
More information about the CRIU
mailing list