From 1a0358b8244149cc6fd6c746ce37bb673d6c5dc7 Mon Sep 17 00:00:00 2001
From: "fu.lin" <fulin10@huawei.com>
Date: Tue, 26 Oct 2021 11:13:27 +0800
Subject: [PATCH 48/54] unix sk: improve dgram robustness

We should try out best to ensure the success of criu. As for unix dgram
socket, criu use re-connect instead of repair instead of unix stream
socket. Therefore, this patch does the following things:

- detect unix dgram unix sock file when criu dumps unix dgram socket
- add the fault tolerance of unix dgram socket connecting (focus on the
  condition of `/dev/log` disappearance when rsyslog restart)

Signed-off-by: fu.lin <fulin10@huawei.com>
---
 criu/sk-unix.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 97 insertions(+), 2 deletions(-)

diff --git a/criu/sk-unix.c b/criu/sk-unix.c
index 14e8adc..a9424df 100644
--- a/criu/sk-unix.c
+++ b/criu/sk-unix.c
@@ -11,6 +11,7 @@
 #include <stdlib.h>
 #include <dlfcn.h>
 #include <libgen.h>
+#include <time.h>
 
 #include "libnetlink.h"
 #include "cr_options.h"
@@ -357,6 +358,58 @@ err:
 	return -ENOENT;
 }
 
+static int unix_resolve_dgram_name(int lfd, uint32_t id, struct unix_sk_desc *sk,
+					struct unix_sk_desc *peer)
+{
+	char *name = peer->name;
+	char rpath[PATH_MAX];
+	struct stat st;
+	struct ns_id *ns;
+	int mntns_root;
+
+	/* The unnamed or abstrace unix socket contion. */
+	if (peer->namelen == 0 || name[0] == '\0')
+		return 0;
+
+	if (name[0] != '/') {
+		pr_warn("Not support relative path, following the original rule."
+			" socket %#x(%s) ino %d peer %d\n",
+				id, name, sk->sd.ino, sk->peer_ino);
+		return 0;
+	} else if (kdat.sk_unix_file && (root_ns_mask & CLONE_NEWNS)) {
+		pr_warn("Not support mnt namespace, following the original rule."
+			" socket %#x(%s) ino %d peer %d\n",
+				id, name, sk->sd.ino, sk->peer_ino);
+		return 0;
+	}
+
+	ns = lookup_ns_by_id(root_item->ids->mnt_ns_id, &mnt_ns_desc);
+	if (!ns) {
+		pr_err("Can't find ns. socket %#x(%s) ino %d peer %d\n",
+				id, name, sk->sd.ino, sk->peer_ino);
+		goto err;
+	}
+
+	mntns_root = mntns_get_root_fd(ns);
+	if (mntns_root < 0) {
+		pr_err("Can't get root fd. socket %#x(%s) ino %d peer %d\n",
+				id, name, sk->sd.ino, sk->peer_ino);
+		goto err;
+	}
+
+	snprintf(rpath, sizeof(rpath), ".%s", name);
+	if (fstatat(mntns_root, rpath, &st, 0)) {
+		pr_err("Can't stat the connected DGRAM type socket id %#x,"
+				" peer ino %d path '%s'\n",
+			id, sk->peer_ino, name);
+		goto err;
+	}
+
+	return 0;
+err:
+	return -ENOENT;
+}
+
 static int dump_one_unix_fd(int lfd, uint32_t id, const struct fd_parms *p)
 {
 	struct unix_sk_desc *sk, *peer;
@@ -483,6 +536,18 @@ static int dump_one_unix_fd(int lfd, uint32_t id, const struct fd_parms *p)
 			ret = getsockopt(lfd, SOL_TCP, TCP_REPAIR_OPTIONS, &ue->repair_ino, &len);
 			if (ret < 0)
 				goto err;
+		} else if (peer->name && sk->type == SOCK_DGRAM) {
+			/*
+			 *  As for unix stream socket, we can use the kernel]
+			 *  feature which cmdline is `unix_stream_restore_enable`
+			 *  to dump/restore it. Because of the feature, we can
+			 *  not consider the unix stream socket file status.
+			 *  But as for unix dgram socket, it's different. We
+			 *  must ensure the existence of the socket file when
+			 *  dump/restore, otherwise it will fail.
+			 */
+			if (unix_resolve_dgram_name(lfd, id, sk, peer) != 0)
+				goto err;
 		}
 
 		/*
@@ -1340,6 +1405,33 @@ err:
 	return -1;
 }
 
+/*
+ * Sometimes, `/dev/log` will disappear because of the restart of rsyslog when
+ * rotating, criu try to connect `/dev/log` will report error at this time. We
+ * should try our best to ensure the success of criu restoration. Therefore,
+ * retry three times here.
+ */
+static int unix_dgram_reconnect(int fd, struct sockaddr_un *addr, int len)
+{
+	int retval = 0;
+	struct timespec tim = {
+		.tv_sec = 0,
+		.tv_nsec = 5e+8,
+	};
+
+	for (int i = 0; i < 3; i++) {
+		nanosleep(&tim, NULL);
+		pr_warn("Can't connect unix socket(%s), %d retry\n",
+					addr->sun_path, i);
+		retval = connect(fd, (struct sockaddr *)addr,
+					sizeof(addr->sun_family) + len);
+		if (retval == 0)
+			break;
+	}
+
+	return retval;
+}
+
 static int post_open_standalone(struct file_desc *d, int fd)
 {
 	int fdstore_fd = -1, procfs_self_dir = -1, len;
@@ -1421,8 +1513,11 @@ static int post_open_standalone(struct file_desc *d, int fd)
 			goto err_revert_and_exit;
 		}
 	} else if (connect(fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + len) < 0) {
-		pr_perror("Can't connect %d socket", ui->ue->ino);
-		goto err_revert_and_exit;
+		if (ui->ue->type != SOCK_DGRAM || errno != ENOENT
+				|| unix_dgram_reconnect(fd, &addr, len) != 0) {
+			pr_perror("Can't connect %d socket", ui->ue->ino);
+			goto err_revert_and_exit;
+		}
 	}
 	mutex_unlock(mutex_ghost);
 
-- 
2.33.1

