Commit b9b0730c authored by Andrey Vagin's avatar Andrey Vagin Committed by Pavel Emelyanov

ptrace: split task_seize into seize_catch_task and seize_wait_task

It's preparation to use a freezer cgroup for freezing tasks.
Signed-off-by: 's avatarAndrey Vagin <avagin@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 46e8aeed
...@@ -130,7 +130,10 @@ int cr_exec(int pid, char **opt) ...@@ -130,7 +130,10 @@ int cr_exec(int pid, char **opt)
goto out; goto out;
} }
prev_state = ret = seize_task(pid, -1, &creds); if (seize_catch_task(pid))
goto out;
prev_state = ret = seize_wait_task(pid, -1, &creds);
if (ret < 0) { if (ret < 0) {
pr_err("Can't seize task %d\n", pid); pr_err("Can't seize task %d\n", pid);
goto out; goto out;
......
...@@ -67,7 +67,8 @@ struct ptrace_peeksiginfo_args { ...@@ -67,7 +67,8 @@ struct ptrace_peeksiginfo_args {
#define SI_EVENT(_si_code) (((_si_code) & 0xFFFF) >> 8) #define SI_EVENT(_si_code) (((_si_code) & 0xFFFF) >> 8)
extern int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds); extern int seize_catch_task(pid_t pid);
extern int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds);
extern int suspend_seccomp(pid_t pid); extern int suspend_seccomp(pid_t pid);
extern int unseize_task(pid_t pid, int orig_state, int state); 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); extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes);
......
...@@ -52,6 +52,39 @@ int suspend_seccomp(pid_t pid) ...@@ -52,6 +52,39 @@ int suspend_seccomp(pid_t pid)
return 0; return 0;
} }
int seize_catch_task(pid)
{
int ret;
ret = ptrace(PTRACE_SEIZE, pid, NULL, 0);
if (ret) {
/*
* ptrace API doesn't allow to distinguish
* attaching to zombie from other errors.
* All errors will be handled in seize_wait_task().
*/
pr_warn("Unable to interrupt task: %d (%s)\n", pid, strerror(errno));
return ret;
}
/*
* If we SEIZE-d the task stop it before going
* and reading its stat from proc. Otherwise task
* may die _while_ we're doing it and we'll have
* inconsistent seize/state pair.
*
* If task dies after we seize it but before we
* do this interrupt, we'll notice it via proc.
*/
ret = ptrace(PTRACE_INTERRUPT, pid, NULL, NULL);
if (ret < 0) {
pr_warn("SEIZE %d: can't interrupt task: %s", pid, strerror(errno));
ptrace(PTRACE_DETACH, pid, NULL, NULL);
}
return ret;
}
/* /*
* This routine seizes task putting it into a special * This routine seizes task putting it into a special
* state where we can manipulate the task via ptrace * state where we can manipulate the task via ptrace
...@@ -59,12 +92,11 @@ int suspend_seccomp(pid_t pid) ...@@ -59,12 +92,11 @@ int suspend_seccomp(pid_t pid)
* of it so the task would not know if it was saddled * of it so the task would not know if it was saddled
* up with someone else. * up with someone else.
*/ */
int seize_wait_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds)
int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds)
{ {
siginfo_t si; siginfo_t si;
int status; int status;
int ret, ret2, ptrace_errno, wait_errno = 0; int ret = 0, ret2, wait_errno = 0;
struct proc_status_creds cr; struct proc_status_creds cr;
/* /*
...@@ -72,26 +104,6 @@ int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds) ...@@ -72,26 +104,6 @@ int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds)
*/ */
memzero(&cr, sizeof(struct proc_status_creds)); memzero(&cr, sizeof(struct proc_status_creds));
ret = ptrace(PTRACE_SEIZE, pid, NULL, 0);
ptrace_errno = errno;
if (ret == 0) {
/*
* If we SEIZE-d the task stop it before going
* and reading its stat from proc. Otherwise task
* may die _while_ we're doing it and we'll have
* inconsistent seize/state pair.
*
* If task dies after we seize it but before we
* do this interrupt, we'll notice it via proc.
*/
ret = ptrace(PTRACE_INTERRUPT, pid, NULL, NULL);
if (ret < 0) {
pr_perror("SEIZE %d: can't interrupt task", pid);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
goto err;
}
}
/* /*
* It's ugly, but the ptrace API doesn't allow to distinguish * It's ugly, but the ptrace API doesn't allow to distinguish
* attaching to zombie from other errors. Thus we have to parse * attaching to zombie from other errors. Thus we have to parse
...@@ -100,10 +112,9 @@ int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds) ...@@ -100,10 +112,9 @@ int seize_task(pid_t pid, pid_t ppid, struct proc_status_creds **creds)
*/ */
try_again: try_again:
if (!ret) {
ret = wait4(pid, &status, __WALL, NULL); ret = wait4(pid, &status, __WALL, NULL);
wait_errno = errno; wait_errno = errno;
}
ret2 = parse_pid_status(pid, &cr); ret2 = parse_pid_status(pid, &cr);
if (ret2) if (ret2)
...@@ -119,8 +130,8 @@ try_again: ...@@ -119,8 +130,8 @@ try_again:
if (pid == getpid()) if (pid == getpid())
pr_err("The criu itself is within dumped tree.\n"); pr_err("The criu itself is within dumped tree.\n");
else else
pr_err("Unseizable non-zombie %d found, state %c, err %d/%d/%d\n", pr_err("Unseizable non-zombie %d found, state %c, err %d/%d\n",
pid, cr.state, ret, ptrace_errno, wait_errno); pid, cr.state, ret, wait_errno);
return -1; return -1;
} }
......
...@@ -58,7 +58,10 @@ static int collect_children(struct pstree_item *item) ...@@ -58,7 +58,10 @@ static int collect_children(struct pstree_item *item)
goto free; goto free;
} }
ret = seize_task(pid, item->pid.real, &dmpi(c)->pi_creds); /* fails when meets a zombie */
seize_catch_task(pid);
ret = seize_wait_task(pid, item->pid.real, &dmpi(c)->pi_creds);
if (ret < 0) { if (ret < 0) {
/* /*
* Here is a race window between parse_children() and seize(), * Here is a race window between parse_children() and seize(),
...@@ -207,7 +210,10 @@ static int collect_threads(struct pstree_item *item) ...@@ -207,7 +210,10 @@ static int collect_threads(struct pstree_item *item)
pr_info("\tSeizing %d's %d thread\n", pr_info("\tSeizing %d's %d thread\n",
item->pid.real, pid); item->pid.real, pid);
ret = seize_task(pid, item_ppid(item), &dmpi(item)->pi_creds); if (seize_catch_task(pid))
continue;
ret = seize_wait_task(pid, item_ppid(item), &dmpi(item)->pi_creds);
if (ret < 0) { if (ret < 0) {
/* /*
* Here is a race window between parse_threads() and seize(), * Here is a race window between parse_threads() and seize(),
...@@ -316,7 +322,11 @@ int collect_pstree(pid_t pid) ...@@ -316,7 +322,11 @@ int collect_pstree(pid_t pid)
return -1; return -1;
root_item->pid.real = pid; root_item->pid.real = pid;
ret = seize_task(pid, -1, &dmpi(root_item)->pi_creds);
if (seize_catch_task(pid))
goto err;
ret = seize_wait_task(pid, -1, &dmpi(root_item)->pi_creds);
if (ret < 0) if (ret < 0)
goto err; goto err;
pr_info("Seized task %d, state %d\n", pid, ret); pr_info("Seized task %d, state %d\n", pid, ret);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment