[CRIU] [PATCHv4 4/4] crtools: cr_service() and fixes for it
Ruslan Kuprieiev
kupruser at gmail.com
Tue Sep 3 10:44:35 EDT 2013
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.
-------------- next part --------------
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);
+ if (buf == NULL) {
+ pr_perror("Can't allocate memory");
+ return -1;
+ }
+
+ errno = 0;
+ 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");
+ 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");
+ 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");
+
+ 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;
+ }
+
+ 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) {
More information about the CRIU
mailing list