[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