[CRIU] [PATCH 17/78] infect: Move seize_wait_task -> compel_wait_task

Cyrill Gorcunov gorcunov at openvz.org
Mon Nov 7 08:36:02 PST 2016


From: Pavel Emelyanov <xemul at virtuozzo.com>

Signed-off-by: Pavel Emelyanov <xemul at virtuozzo.com>
---
 criu/cr-exec.c            |   3 +-
 criu/include/infect.h     |  22 ++++++
 criu/include/proc_parse.h |   2 +-
 criu/include/ptrace.h     |  11 ---
 criu/infect.c             | 191 +++++++++++++++++++++++++++++++++++++++++++++-
 criu/ptrace.c             | 162 ---------------------------------------
 criu/seize.c              |   7 +-
 7 files changed, 219 insertions(+), 179 deletions(-)

diff --git a/criu/cr-exec.c b/criu/cr-exec.c
index 8901750dbe2b..718cb6cb5565 100644
--- a/criu/cr-exec.c
+++ b/criu/cr-exec.c
@@ -3,6 +3,7 @@
 #include <stdlib.h>
 #include "crtools.h"
 #include "proc_parse.h"
+#include "ptrace.h"
 #include "pstree.h"
 #include "parasite-syscall.h"
 #include "vma.h"
@@ -142,7 +143,7 @@ int cr_exec(int pid, char **opt)
 	 * mess with creds in this use case anyway.
 	 */
 
-	prev_state = ret = seize_wait_task(pid, -1, parse_pid_status, &creds.s);
+	prev_state = ret = compel_wait_task(pid, -1, parse_pid_status, &creds.s);
 	if (ret < 0) {
 		pr_err("Can't seize task %d\n", pid);
 		goto out;
diff --git a/criu/include/infect.h b/criu/include/infect.h
index d148a78e3574..dbd721b14a7a 100644
--- a/criu/include/infect.h
+++ b/criu/include/infect.h
@@ -1,4 +1,26 @@
 #ifndef __COMPEL_INFECT_H__
 #define __COMPEL_INFECT_H__
 extern int compel_stop_task(int pid);
+
+struct seize_task_status {
+	char			state;
+	int			ppid;
+	unsigned long long	sigpnd;
+	unsigned long long	shdpnd;
+	int			seccomp_mode;
+};
+
+extern int compel_wait_task(int pid, int ppid,
+		int (*get_status)(int pid, struct seize_task_status *),
+		struct seize_task_status *st);
+
+/*
+ * FIXME -- these should be mapped to pid.h's
+ */
+
+#define TASK_ALIVE		0x1
+#define TASK_DEAD		0x2
+#define TASK_STOPPED		0x3
+#define TASK_ZOMBIE		0x6
+
 #endif
diff --git a/criu/include/proc_parse.h b/criu/include/proc_parse.h
index d5fbaf7a0b0a..04359e247c74 100644
--- a/criu/include/proc_parse.h
+++ b/criu/include/proc_parse.h
@@ -3,7 +3,7 @@
 
 #include <sys/types.h>
 
-#include "ptrace.h"
+#include "infect.h"
 #include "images/seccomp.pb-c.h"
 
 #define PROC_TASK_COMM_LEN	32
diff --git a/criu/include/ptrace.h b/criu/include/ptrace.h
index 29f27d5b517b..e96d29127c7a 100644
--- a/criu/include/ptrace.h
+++ b/criu/include/ptrace.h
@@ -72,17 +72,6 @@ struct ptrace_peeksiginfo_args {
 
 #define SI_EVENT(_si_code)	(((_si_code) & 0xFFFF) >> 8)
 
-struct seize_task_status {
-	char			state;
-	int			ppid;
-	unsigned long long	sigpnd;
-	unsigned long long	shdpnd;
-	int			seccomp_mode;
-};
-
-extern int seize_wait_task(pid_t pid, pid_t ppid,
-		int (*get_status)(int pid, struct seize_task_status *),
-		struct seize_task_status *st);
 extern int suspend_seccomp(pid_t pid);
 extern int unseize_task(pid_t pid, int orig_state, int state);
 extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes);
diff --git a/criu/infect.c b/criu/infect.c
index 0a625b4d1932..de48b428c63e 100644
--- a/criu/infect.c
+++ b/criu/infect.c
@@ -1,9 +1,26 @@
+#include <sys/wait.h>
+#include <sys/types.h>
 #include <sys/ptrace.h>
+#include <unistd.h>
 #include <errno.h>
+#include <signal.h>
+#include <linux/seccomp.h>
 
 #include "log.h"
 #include "infect.h"
 
+#define PTRACE_EVENT_STOP	128
+
+#ifndef SECCOMP_MODE_DISABLED
+#define SECCOMP_MODE_DISABLED 0
+#endif
+
+#ifndef PTRACE_O_SUSPEND_SECCOMP
+# define PTRACE_O_SUSPEND_SECCOMP (1 << 21)
+#endif
+
+#define SI_EVENT(_si_code)	(((_si_code) & 0xFFFF) >> 8)
+
 int compel_stop_task(int pid)
 {
 	int ret;
@@ -13,7 +30,7 @@ int compel_stop_task(int pid)
 		/*
 		 * ptrace API doesn't allow to distinguish
 		 * attaching to zombie from other errors.
-		 * All errors will be handled in seize_wait_task().
+		 * All errors will be handled in compel_wait_task().
 		 */
 		pr_warn("Unable to interrupt task: %d (%s)\n", pid, strerror(errno));
 		return ret;
@@ -38,3 +55,175 @@ int compel_stop_task(int pid)
 	return ret;
 }
 
+static int skip_sigstop(int pid, int nr_signals)
+{
+	int i, status, ret;
+
+	/*
+	 * 1) SIGSTOP is queued, but isn't handled yet:
+	 * SGISTOP can't be blocked, so we need to wait when the kernel
+	 * handles this signal.
+	 *
+	 * Otherwise the process will be stopped immediately after
+	 * starting it.
+	 *
+	 * 2) A seized task was stopped:
+	 * PTRACE_SEIZE doesn't affect signal or group stop state.
+	 * Currently ptrace reported that task is in stopped state.
+	 * We need to start task again, and it will be trapped
+	 * immediately, because we sent PTRACE_INTERRUPT to it.
+	 */
+	for (i = 0; i < nr_signals; i++) {
+		ret = ptrace(PTRACE_CONT, pid, 0, 0);
+		if (ret) {
+			pr_perror("Unable to start process");
+			return -1;
+		}
+
+		ret = wait4(pid, &status, __WALL, NULL);
+		if (ret < 0) {
+			pr_perror("SEIZE %d: can't wait task", pid);
+			return -1;
+		}
+
+		if (!WIFSTOPPED(status)) {
+			pr_err("SEIZE %d: task not stopped after seize\n", pid);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int do_suspend_seccomp(pid_t pid)
+{
+	if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_SUSPEND_SECCOMP) < 0) {
+		pr_perror("suspending seccomp failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * This routine seizes task putting it into a special
+ * state where we can manipulate the task via ptrace
+ * interface, and finally we can detach ptrace out of
+ * of it so the task would not know if it was saddled
+ * up with someone else.
+ */
+int compel_wait_task(int pid, int ppid,
+		int (*get_status)(int pid, struct seize_task_status *),
+		struct seize_task_status *ss)
+{
+	siginfo_t si;
+	int status, nr_sigstop;
+	int ret = 0, ret2, wait_errno = 0;
+
+	/*
+	 * It's ugly, but the ptrace API doesn't allow to distinguish
+	 * attaching to zombie from other errors. Thus we have to parse
+	 * the target's /proc/pid/stat. Sad, but parse whatever else
+	 * we might need at that early point.
+	 */
+
+try_again:
+
+	ret = wait4(pid, &status, __WALL, NULL);
+	if (ret < 0) {
+		/*
+		 * wait4() can expectedly fail only in a first time
+		 * if a task is zombie. If we are here from try_again,
+		 * this means that we are tracing this task.
+		 *
+		 * So here we can be only once in this function.
+		 */
+		wait_errno = errno;
+	}
+
+	ret2 = get_status(pid, ss);
+	if (ret2)
+		goto err;
+
+	if (ret < 0 || WIFEXITED(status) || WIFSIGNALED(status)) {
+		if (ss->state != 'Z') {
+			if (pid == getpid())
+				pr_err("The criu itself is within dumped tree.\n");
+			else
+				pr_err("Unseizable non-zombie %d found, state %c, err %d/%d\n",
+						pid, ss->state, ret, wait_errno);
+			return -1;
+		}
+
+		if (ret < 0)
+			return TASK_ZOMBIE;
+		else
+			return TASK_DEAD;
+	}
+
+	if ((ppid != -1) && (ss->ppid != ppid)) {
+		pr_err("Task pid reused while suspending (%d: %d -> %d)\n",
+				pid, ppid, ss->ppid);
+		goto err;
+	}
+
+	if (!WIFSTOPPED(status)) {
+		pr_err("SEIZE %d: task not stopped after seize\n", pid);
+		goto err;
+	}
+
+	ret = ptrace(PTRACE_GETSIGINFO, pid, NULL, &si);
+	if (ret < 0) {
+		pr_perror("SEIZE %d: can't read signfo", pid);
+		goto err;
+	}
+
+	if (SI_EVENT(si.si_code) != PTRACE_EVENT_STOP) {
+		/*
+		 * Kernel notifies us about the task being seized received some
+		 * event other than the STOP, i.e. -- a signal. Let the task
+		 * handle one and repeat.
+		 */
+
+		if (ptrace(PTRACE_CONT, pid, NULL,
+					(void *)(unsigned long)si.si_signo)) {
+			pr_perror("Can't continue signal handling, aborting");
+			goto err;
+		}
+
+		ret = 0;
+		goto try_again;
+	}
+
+	if (ss->seccomp_mode != SECCOMP_MODE_DISABLED && do_suspend_seccomp(pid) < 0)
+		goto err;
+
+	nr_sigstop = 0;
+	if (ss->sigpnd & (1 << (SIGSTOP - 1)))
+		nr_sigstop++;
+	if (ss->shdpnd & (1 << (SIGSTOP - 1)))
+		nr_sigstop++;
+	if (si.si_signo == SIGSTOP)
+		nr_sigstop++;
+
+	if (nr_sigstop) {
+		if (skip_sigstop(pid, nr_sigstop))
+			goto err_stop;
+
+		return TASK_STOPPED;
+	}
+
+	if (si.si_signo == SIGTRAP)
+		return TASK_ALIVE;
+	else {
+		pr_err("SEIZE %d: unsupported stop signal %d\n", pid, si.si_signo);
+		goto err;
+	}
+
+err_stop:
+	kill(pid, SIGSTOP);
+err:
+	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
+		pr_perror("Unable to detach from %d", pid);
+	return -1;
+}
+
diff --git a/criu/ptrace.c b/criu/ptrace.c
index 2bbf67ade74d..f013346e32a6 100644
--- a/criu/ptrace.c
+++ b/criu/ptrace.c
@@ -68,168 +68,6 @@ int suspend_seccomp(pid_t pid)
 	return 0;
 }
 
-static int skip_sigstop(int pid, int nr_signals)
-{
-	int i, status, ret;
-
-	/*
-	 * 1) SIGSTOP is queued, but isn't handled yet:
-	 * SGISTOP can't be blocked, so we need to wait when the kernel
-	 * handles this signal.
-	 *
-	 * Otherwise the process will be stopped immediately after
-	 * starting it.
-	 *
-	 * 2) A seized task was stopped:
-	 * PTRACE_SEIZE doesn't affect signal or group stop state.
-	 * Currently ptrace reported that task is in stopped state.
-	 * We need to start task again, and it will be trapped
-	 * immediately, because we sent PTRACE_INTERRUPT to it.
-	 */
-	for (i = 0; i < nr_signals; i++) {
-		ret = ptrace(PTRACE_CONT, pid, 0, 0);
-		if (ret) {
-			pr_perror("Unable to start process");
-			return -1;
-		}
-
-		ret = wait4(pid, &status, __WALL, NULL);
-		if (ret < 0) {
-			pr_perror("SEIZE %d: can't wait task", pid);
-			return -1;
-		}
-
-		if (!WIFSTOPPED(status)) {
-			pr_err("SEIZE %d: task not stopped after seize\n", pid);
-			return -1;
-		}
-	}
-	return 0;
-}
-
-/*
- * This routine seizes task putting it into a special
- * state where we can manipulate the task via ptrace
- * interface, and finally we can detach ptrace out of
- * of it so the task would not know if it was saddled
- * up with someone else.
- */
-int seize_wait_task(pid_t pid, pid_t ppid,
-		int (*get_status)(int pid, struct seize_task_status *),
-		struct seize_task_status *ss)
-{
-	siginfo_t si;
-	int status, nr_sigstop;
-	int ret = 0, ret2, wait_errno = 0;
-
-	/*
-	 * It's ugly, but the ptrace API doesn't allow to distinguish
-	 * attaching to zombie from other errors. Thus we have to parse
-	 * the target's /proc/pid/stat. Sad, but parse whatever else
-	 * we might need at that early point.
-	 */
-
-try_again:
-
-	ret = wait4(pid, &status, __WALL, NULL);
-	if (ret < 0) {
-		/*
-		 * wait4() can expectedly fail only in a first time
-		 * if a task is zombie. If we are here from try_again,
-		 * this means that we are tracing this task.
-		 *
-		 * So here we can be only once in this function.
-		 */
-		wait_errno = errno;
-	}
-
-	ret2 = get_status(pid, ss);
-	if (ret2)
-		goto err;
-
-	if (ret < 0 || WIFEXITED(status) || WIFSIGNALED(status)) {
-		if (ss->state != 'Z') {
-			if (pid == getpid())
-				pr_err("The criu itself is within dumped tree.\n");
-			else
-				pr_err("Unseizable non-zombie %d found, state %c, err %d/%d\n",
-						pid, ss->state, ret, wait_errno);
-			return -1;
-		}
-
-		if (ret < 0)
-			return TASK_ZOMBIE;
-		else
-			return TASK_DEAD;
-	}
-
-	if ((ppid != -1) && (ss->ppid != ppid)) {
-		pr_err("Task pid reused while suspending (%d: %d -> %d)\n",
-				pid, ppid, ss->ppid);
-		goto err;
-	}
-
-	if (!WIFSTOPPED(status)) {
-		pr_err("SEIZE %d: task not stopped after seize\n", pid);
-		goto err;
-	}
-
-	ret = ptrace(PTRACE_GETSIGINFO, pid, NULL, &si);
-	if (ret < 0) {
-		pr_perror("SEIZE %d: can't read signfo", pid);
-		goto err;
-	}
-
-	if (SI_EVENT(si.si_code) != PTRACE_EVENT_STOP) {
-		/*
-		 * Kernel notifies us about the task being seized received some
-		 * event other than the STOP, i.e. -- a signal. Let the task
-		 * handle one and repeat.
-		 */
-
-		if (ptrace(PTRACE_CONT, pid, NULL,
-					(void *)(unsigned long)si.si_signo)) {
-			pr_perror("Can't continue signal handling, aborting");
-			goto err;
-		}
-
-		ret = 0;
-		goto try_again;
-	}
-
-	if (ss->seccomp_mode != SECCOMP_MODE_DISABLED && suspend_seccomp(pid) < 0)
-		goto err;
-
-	nr_sigstop = 0;
-	if (ss->sigpnd & (1 << (SIGSTOP - 1)))
-		nr_sigstop++;
-	if (ss->shdpnd & (1 << (SIGSTOP - 1)))
-		nr_sigstop++;
-	if (si.si_signo == SIGSTOP)
-		nr_sigstop++;
-
-	if (nr_sigstop) {
-		if (skip_sigstop(pid, nr_sigstop))
-			goto err_stop;
-
-		return TASK_STOPPED;
-	}
-
-	if (si.si_signo == SIGTRAP)
-		return TASK_ALIVE;
-	else {
-		pr_err("SEIZE %d: unsupported stop signal %d\n", pid, si.si_signo);
-		goto err;
-	}
-
-err_stop:
-	kill(pid, SIGSTOP);
-err:
-	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
-		pr_perror("Unable to detach from %d", pid);
-	return -1;
-}
-
 int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes)
 {
 	unsigned long w;
diff --git a/criu/seize.c b/criu/seize.c
index 8afcae7bdaa1..1b10224a05c0 100644
--- a/criu/seize.c
+++ b/criu/seize.c
@@ -15,6 +15,7 @@
 #include "cr-errno.h"
 #include "pstree.h"
 #include "criu-log.h"
+#include "ptrace.h"
 #include "proc_parse.h"
 #include "seize.h"
 #include "stats.h"
@@ -489,7 +490,7 @@ static int collect_children(struct pstree_item *item)
 			goto free;
 		}
 
-		ret = seize_wait_task(pid, item->pid.real, parse_pid_status, &creds->s);
+		ret = compel_wait_task(pid, item->pid.real, parse_pid_status, &creds->s);
 		if (ret < 0) {
 			/*
 			 * Here is a race window between parse_children() and seize(),
@@ -714,7 +715,7 @@ static int collect_threads(struct pstree_item *item)
 		if (!opts.freeze_cgroup && compel_stop_task(pid))
 			continue;
 
-		ret = seize_wait_task(pid, item_ppid(item), parse_pid_status, &t_creds.s);
+		ret = compel_wait_task(pid, item_ppid(item), parse_pid_status, &t_creds.s);
 		if (ret < 0) {
 			/*
 			 * Here is a race window between parse_threads() and seize(),
@@ -852,7 +853,7 @@ int collect_pstree(void)
 	if (!creds)
 		goto err;
 
-	ret = seize_wait_task(pid, -1, parse_pid_status, &creds->s);
+	ret = compel_wait_task(pid, -1, parse_pid_status, &creds->s);
 	if (ret < 0)
 		goto err;
 
-- 
2.7.4



More information about the CRIU mailing list