[CRIU] [PATCH 2/3] crtools: split cr-dump.c in two files
Andrey Vagin
avagin at openvz.org
Mon Aug 3 19:18:23 PDT 2015
Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
Makefile.crtools | 1 +
cr-dump.c | 380 +--------------------------------------------------
include/proc_parse.h | 2 +
include/seize.h | 7 +
proc_parse.c | 61 +++++++++
seize.c | 337 +++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 409 insertions(+), 379 deletions(-)
create mode 100644 include/seize.h
create mode 100644 seize.c
diff --git a/Makefile.crtools b/Makefile.crtools
index a331684..42c5a7c 100644
--- a/Makefile.crtools
+++ b/Makefile.crtools
@@ -73,6 +73,7 @@ obj-y += sd-daemon.o
obj-y += plugin.o
obj-y += cr-errno.o
obj-y += pie/pie-relocs.o
+obj-y += seize.o
ifneq ($(MAKECMDGOALS),clean)
incdeps := y
diff --git a/cr-dump.c b/cr-dump.c
index af1b281..806544d 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -79,11 +79,10 @@
#include "security.h"
#include "lsm.h"
#include "seccomp.h"
+#include "seize.h"
#include "asm/dump.h"
-#define NR_ATTEMPTS 5
-
static char loc_buf[PAGE_SIZE];
static void close_vma_file(struct vma_area *vma)
@@ -713,353 +712,6 @@ err:
return ret;
}
-static int parse_children(pid_t pid, pid_t **_c, int *_n)
-{
- pid_t *ch = NULL;
- int nr = 0;
- DIR *dir;
- struct dirent *de;
-
- dir = opendir_proc(pid, "task");
- if (dir == NULL)
- return -1;
-
- while ((de = readdir(dir))) {
- int fd, len;
- char *pos;
-
- if (dir_dots(de))
- continue;
-
- fd = open_proc(pid, "task/%s/children", de->d_name);
- if (fd < 0)
- goto err;
-
- len = read(fd, loc_buf, sizeof(loc_buf) - 1);
- close(fd);
- if (len < 0)
- goto err;
-
- loc_buf[len] = '\0';
- pos = loc_buf;
- while (1) {
- pid_t val, *tmp;
-
- val = strtol(pos, &pos, 0);
- if (!val) {
- BUG_ON(*pos != '\0');
- break;
- }
-
- tmp = xrealloc(ch, (nr + 1) * sizeof(pid_t));
- if (!tmp)
- goto err;
-
- ch = tmp;
- ch[nr] = val;
- nr++;
- pos++; /* space goes after each pid */
- }
- }
-
- *_c = ch;
- *_n = nr;
-
- closedir(dir);
- return 0;
-err:
- closedir(dir);
- xfree(ch);
- return -1;
-}
-
-static inline bool child_collected(struct pstree_item *i, pid_t pid)
-{
- struct pstree_item *c;
-
- list_for_each_entry(c, &i->children, sibling)
- if (c->pid.real == pid)
- return true;
-
- return false;
-}
-
-static int collect_task(struct pstree_item *item);
-static int collect_children(struct pstree_item *item)
-{
- pid_t *ch;
- int ret, i, nr_children, nr_inprogress;
-
- ret = parse_children(item->pid.real, &ch, &nr_children);
- if (ret < 0)
- return ret;
-
- nr_inprogress = 0;
- for (i = 0; i < nr_children; i++) {
- struct pstree_item *c;
- pid_t pid = ch[i];
-
- /* Is it already frozen? */
- if (child_collected(item, pid))
- continue;
-
- nr_inprogress++;
-
- pr_info("Seized task %d, state %d\n", pid, ret);
-
- c = alloc_pstree_item();
- if (c == NULL) {
- ret = -1;
- goto free;
- }
-
- ret = seize_task(pid, item->pid.real, &dmpi(c)->pi_creds);
- if (ret < 0) {
- /*
- * Here is a race window between parse_children() and seize(),
- * so the task could die for these time.
- * Don't worry, will try again on the next attempt. The number
- * of attempts is restricted, so it will exit if something
- * really wrong.
- */
- ret = 0;
- xfree(c);
- continue;
- }
-
- c->pid.real = pid;
- c->parent = item;
- c->state = ret;
- list_add_tail(&c->sibling, &item->children);
-
- /* Here is a recursive call (Depth-first search) */
- ret = collect_task(c);
- if (ret < 0)
- goto free;
- }
-free:
- xfree(ch);
- return ret < 0 ? ret : nr_inprogress;
-}
-
-static void unseize_task_and_threads(const struct pstree_item *item, int st)
-{
- int i;
-
- if (item->state == TASK_DEAD)
- return;
-
- /*
- * The st is the state we want to switch tasks into,
- * the item->state is the state task was in when we seized one.
- */
-
- unseize_task(item->pid.real, item->state, st);
-
- if (st == TASK_DEAD)
- return;
-
- for (i = 1; i < item->nr_threads; i++)
- if (ptrace(PTRACE_DETACH, item->threads[i].real, NULL, NULL))
- pr_perror("Unable to detach from %d", item->threads[i].real);
-}
-
-static void pstree_wait(struct pstree_item *root_item)
-{
- struct pstree_item *item = root_item;
- int pid, status, i;
-
- for_each_pstree_item(item) {
-
- if (item->state == TASK_DEAD)
- continue;
-
- for (i = 0; i < item->nr_threads; i++) {
- pid = wait4(-1, &status, __WALL, NULL);
- if (pid < 0) {
- pr_perror("wait4 failed");
- break;
- } else {
- if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
- pr_err("Unexpected exit code %d of %d\n", status, pid);
- BUG();
- }
- }
- }
- }
- pid = wait4(-1, &status, __WALL, NULL);
- if (pid > 0) {
- pr_err("Unexpected child %d", pid);
- BUG();
- }
-}
-
-static void pstree_switch_state(struct pstree_item *root_item, int st)
-{
- struct pstree_item *item = root_item;
-
- pr_info("Unfreezing tasks into %d\n", st);
- for_each_pstree_item(item)
- unseize_task_and_threads(item, st);
-
- if (st == TASK_DEAD)
- pstree_wait(root_item);
-}
-
-static pid_t item_ppid(const struct pstree_item *item)
-{
- item = item->parent;
- return item ? item->pid.real : -1;
-}
-
-static inline bool thread_collected(struct pstree_item *i, pid_t tid)
-{
- int t;
-
- if (i->pid.real == tid) /* thread leader is collected as task */
- return true;
-
- for (t = 0; t < i->nr_threads; t++)
- if (tid == i->threads[t].real)
- return true;
-
- return false;
-}
-
-static int collect_threads(struct pstree_item *item)
-{
- struct pid *threads = NULL;
- int nr_threads = 0, i = 0, ret, nr_inprogress, nr_stopped = 0;
-
- ret = parse_threads(item->pid.real, &threads, &nr_threads);
- if (ret < 0)
- goto err;
-
- if ((item->state == TASK_DEAD) && (nr_threads > 1)) {
- pr_err("Zombies with threads are not supported\n");
- goto err;
- }
-
- /* The number of threads can't be less than allready frozen */
- item->threads = xrealloc(item->threads, nr_threads * sizeof(struct pid));
- if (item->threads == NULL)
- return -1;
-
- if (item->nr_threads == 0) {
- item->threads[0].real = item->pid.real;
- item->nr_threads = 1;
- }
-
- nr_inprogress = 0;
- for (i = 0; i < nr_threads; i++) {
- pid_t pid = threads[i].real;
-
- if (thread_collected(item, pid))
- continue;
-
- nr_inprogress++;
-
- pr_info("\tSeizing %d's %d thread\n",
- item->pid.real, pid);
-
- ret = seize_task(pid, item_ppid(item), &dmpi(item)->pi_creds);
- if (ret < 0) {
- /*
- * Here is a race window between parse_threads() and seize(),
- * so the task could die for these time.
- * Don't worry, will try again on the next attempt. The number
- * of attempts is restricted, so it will exit if something
- * really wrong.
- */
- continue;
- }
-
- BUG_ON(item->nr_threads + 1 > nr_threads);
- item->threads[item->nr_threads].real = pid;
- item->nr_threads++;
-
- if (ret == TASK_DEAD) {
- pr_err("Zombie thread not supported\n");
- goto err;
- }
-
- if (ret == TASK_STOPPED) {
- nr_stopped++;
- }
- }
-
- if (nr_stopped && nr_stopped != nr_inprogress) {
- pr_err("Individually stopped threads not supported\n");
- goto err;
- }
-
- xfree(threads);
- return nr_inprogress;
-
-err:
- xfree(threads);
- return -1;
-}
-
-static int collect_loop(struct pstree_item *item,
- int (*collect)(struct pstree_item *))
-{
- int attempts = NR_ATTEMPTS, nr_inprogress = 1;
-
- /*
- * While we scan the proc and seize the children/threads
- * new ones can appear (with clone(CLONE_PARENT) or with
- * pthread_create). Thus, after one go, we need to repeat
- * the scan-and-freeze again collecting new arrivals. As
- * new guys may appear again we do NR_ATTEMPTS passes and
- * fail to seize the item if new tasks/threads still
- * appear.
- */
-
- while (nr_inprogress > 0 && attempts) {
- attempts--;
- nr_inprogress = collect(item);
- }
-
- /*
- * We may fail to collect items or run out of attempts.
- * In the former case nr_inprogress will be negative, in
- * the latter -- positive. Thus it's enough just to check
- * for "no more new stuff" and say "we're OK" if so.
- */
-
- return (nr_inprogress == 0) ? 0 : -1;
-}
-
-static int collect_task(struct pstree_item *item)
-{
- int ret;
-
- ret = collect_loop(item, collect_threads);
- if (ret < 0)
- goto err_close;
-
- /* Depth-first search (DFS) is used for traversing a process tree. */
- ret = collect_loop(item, collect_children);
- if (ret < 0)
- goto err_close;
-
- if ((item->state == TASK_DEAD) && !list_empty(&item->children)) {
- pr_err("Zombie with children?! O_o Run, run, run!\n");
- goto err_close;
- }
-
- if (pstree_alloc_cores(item))
- goto err_close;
-
- pr_info("Collected %d in %d state\n", item->pid.real, item->state);
- return 0;
-
-err_close:
- close_pid_proc();
- return -1;
-}
-
static int collect_pstree_ids_predump(void)
{
struct pstree_item *item;
@@ -1101,36 +753,6 @@ int collect_pstree_ids(void)
return 0;
}
-static int collect_pstree(pid_t pid)
-{
- int ret;
-
- timing_start(TIME_FREEZING);
-
- root_item = alloc_pstree_item();
- if (root_item == NULL)
- return -1;
-
- root_item->pid.real = pid;
- ret = seize_task(pid, -1, &dmpi(root_item)->pi_creds);
- if (ret < 0)
- goto err;
- pr_info("Seized task %d, state %d\n", pid, ret);
- root_item->state = ret;
-
- ret = collect_task(root_item);
- if (ret < 0)
- goto err;
-
- timing_stop(TIME_FREEZING);
- timing_start(TIME_FROZEN);
-
- return 0;
-err:
- pstree_switch_state(root_item, TASK_ALIVE);
- return -1;
-}
-
static int collect_file_locks(void)
{
return parse_file_locks();
diff --git a/include/proc_parse.h b/include/proc_parse.h
index 20bd5bc..d084e76 100644
--- a/include/proc_parse.h
+++ b/include/proc_parse.h
@@ -242,4 +242,6 @@ int parse_cgroups(struct list_head *cgroups, unsigned int *n_cgroups);
/* callback for AUFS support */
extern int aufs_parse(struct mount_info *mi);
+int parse_children(pid_t pid, pid_t **_c, int *_n);
+
#endif /* __CR_PROC_PARSE_H__ */
diff --git a/include/seize.h b/include/seize.h
new file mode 100644
index 0000000..3c23e24
--- /dev/null
+++ b/include/seize.h
@@ -0,0 +1,7 @@
+#ifndef __CR_SEIZE_H__
+#define __CR_SEIZE_H__
+
+extern int collect_pstree(pid_t pid);
+extern void pstree_switch_state(struct pstree_item *root_item, int st);
+
+#endif
diff --git a/proc_parse.c b/proc_parse.c
index c5afa1e..95d6505 100644
--- a/proc_parse.c
+++ b/proc_parse.c
@@ -2089,3 +2089,64 @@ bool proc_status_creds_eq(struct proc_status_creds *o1, struct proc_status_creds
{
return memcmp(o1, o2, sizeof(struct proc_status_creds)) == 0;
}
+
+int parse_children(pid_t pid, pid_t **_c, int *_n)
+{
+ pid_t *ch = NULL;
+ int nr = 0;
+ DIR *dir;
+ struct dirent *de;
+
+ dir = opendir_proc(pid, "task");
+ if (dir == NULL)
+ return -1;
+
+ while ((de = readdir(dir))) {
+ int fd, len;
+ char *pos;
+
+ if (dir_dots(de))
+ continue;
+
+ fd = open_proc(pid, "task/%s/children", de->d_name);
+ if (fd < 0)
+ goto err;
+
+ len = read(fd, buf, BUF_SIZE);
+ close(fd);
+ if (len < 0)
+ goto err;
+
+ buf[len] = '\0';
+ pos = buf;
+ while (1) {
+ pid_t val, *tmp;
+
+ val = strtol(pos, &pos, 0);
+ if (!val) {
+ BUG_ON(*pos != '\0');
+ break;
+ }
+
+ tmp = xrealloc(ch, (nr + 1) * sizeof(pid_t));
+ if (!tmp)
+ goto err;
+
+ ch = tmp;
+ ch[nr] = val;
+ nr++;
+ pos++; /* space goes after each pid */
+ }
+ }
+
+ *_c = ch;
+ *_n = nr;
+
+ closedir(dir);
+ return 0;
+err:
+ closedir(dir);
+ xfree(ch);
+ return -1;
+}
+
diff --git a/seize.c b/seize.c
new file mode 100644
index 0000000..e9be332
--- /dev/null
+++ b/seize.c
@@ -0,0 +1,337 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <time.h>
+
+#include "compiler.h"
+#include "cr_options.h"
+#include "pstree.h"
+#include "ptrace.h"
+#include "seize.h"
+#include "stats.h"
+#include "xmalloc.h"
+#include "util.h"
+
+#define NR_ATTEMPTS 5
+
+static inline bool child_collected(struct pstree_item *i, pid_t pid)
+{
+ struct pstree_item *c;
+
+ list_for_each_entry(c, &i->children, sibling)
+ if (c->pid.real == pid)
+ return true;
+
+ return false;
+}
+
+static int collect_task(struct pstree_item *item);
+static int collect_children(struct pstree_item *item)
+{
+ pid_t *ch;
+ int ret, i, nr_children, nr_inprogress;
+
+ ret = parse_children(item->pid.real, &ch, &nr_children);
+ if (ret < 0)
+ return ret;
+
+ nr_inprogress = 0;
+ for (i = 0; i < nr_children; i++) {
+ struct pstree_item *c;
+ pid_t pid = ch[i];
+
+ /* Is it already frozen? */
+ if (child_collected(item, pid))
+ continue;
+
+ nr_inprogress++;
+
+ pr_info("Seized task %d, state %d\n", pid, ret);
+
+ c = alloc_pstree_item();
+ if (c == NULL) {
+ ret = -1;
+ goto free;
+ }
+
+ ret = seize_task(pid, item->pid.real, &dmpi(c)->pi_creds);
+ if (ret < 0) {
+ /*
+ * Here is a race window between parse_children() and seize(),
+ * so the task could die for these time.
+ * Don't worry, will try again on the next attempt. The number
+ * of attempts is restricted, so it will exit if something
+ * really wrong.
+ */
+ ret = 0;
+ xfree(c);
+ continue;
+ }
+
+ c->pid.real = pid;
+ c->parent = item;
+ c->state = ret;
+ list_add_tail(&c->sibling, &item->children);
+
+ /* Here is a recursive call (Depth-first search) */
+ ret = collect_task(c);
+ if (ret < 0)
+ goto free;
+ }
+free:
+ xfree(ch);
+ return ret < 0 ? ret : nr_inprogress;
+}
+
+static void unseize_task_and_threads(const struct pstree_item *item, int st)
+{
+ int i;
+
+ if (item->state == TASK_DEAD)
+ return;
+
+ /*
+ * The st is the state we want to switch tasks into,
+ * the item->state is the state task was in when we seized one.
+ */
+
+ unseize_task(item->pid.real, item->state, st);
+
+ if (st == TASK_DEAD)
+ return;
+
+ for (i = 1; i < item->nr_threads; i++)
+ if (ptrace(PTRACE_DETACH, item->threads[i].real, NULL, NULL))
+ pr_perror("Unable to detach from %d", item->threads[i].real);
+}
+
+static void pstree_wait(struct pstree_item *root_item)
+{
+ struct pstree_item *item = root_item;
+ int pid, status, i;
+
+ for_each_pstree_item(item) {
+
+ if (item->state == TASK_DEAD)
+ continue;
+
+ for (i = 0; i < item->nr_threads; i++) {
+ pid = wait4(-1, &status, __WALL, NULL);
+ if (pid < 0) {
+ pr_perror("wait4 failed");
+ break;
+ } else {
+ if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
+ pr_err("Unexpected exit code %d of %d\n", status, pid);
+ BUG();
+ }
+ }
+ }
+ }
+ pid = wait4(-1, &status, __WALL, NULL);
+ if (pid > 0) {
+ pr_err("Unexpected child %d", pid);
+ BUG();
+ }
+}
+
+void pstree_switch_state(struct pstree_item *root_item, int st)
+{
+ struct pstree_item *item = root_item;
+
+ pr_info("Unfreezing tasks into %d\n", st);
+ for_each_pstree_item(item)
+ unseize_task_and_threads(item, st);
+
+ if (st == TASK_DEAD)
+ pstree_wait(root_item);
+}
+
+static pid_t item_ppid(const struct pstree_item *item)
+{
+ item = item->parent;
+ return item ? item->pid.real : -1;
+}
+
+static inline bool thread_collected(struct pstree_item *i, pid_t tid)
+{
+ int t;
+
+ if (i->pid.real == tid) /* thread leader is collected as task */
+ return true;
+
+ for (t = 0; t < i->nr_threads; t++)
+ if (tid == i->threads[t].real)
+ return true;
+
+ return false;
+}
+
+static int collect_threads(struct pstree_item *item)
+{
+ struct pid *threads = NULL;
+ int nr_threads = 0, i = 0, ret, nr_inprogress, nr_stopped = 0;
+
+ ret = parse_threads(item->pid.real, &threads, &nr_threads);
+ if (ret < 0)
+ goto err;
+
+ if ((item->state == TASK_DEAD) && (nr_threads > 1)) {
+ pr_err("Zombies with threads are not supported\n");
+ goto err;
+ }
+
+ /* The number of threads can't be less than allready frozen */
+ item->threads = xrealloc(item->threads, nr_threads * sizeof(struct pid));
+ if (item->threads == NULL)
+ return -1;
+
+ if (item->nr_threads == 0) {
+ item->threads[0].real = item->pid.real;
+ item->nr_threads = 1;
+ }
+
+ nr_inprogress = 0;
+ for (i = 0; i < nr_threads; i++) {
+ pid_t pid = threads[i].real;
+
+ if (thread_collected(item, pid))
+ continue;
+
+ nr_inprogress++;
+
+ pr_info("\tSeizing %d's %d thread\n",
+ item->pid.real, pid);
+
+ ret = seize_task(pid, item_ppid(item), &dmpi(item)->pi_creds);
+ if (ret < 0) {
+ /*
+ * Here is a race window between parse_threads() and seize(),
+ * so the task could die for these time.
+ * Don't worry, will try again on the next attempt. The number
+ * of attempts is restricted, so it will exit if something
+ * really wrong.
+ */
+ continue;
+ }
+
+ BUG_ON(item->nr_threads + 1 > nr_threads);
+ item->threads[item->nr_threads].real = pid;
+ item->nr_threads++;
+
+ if (ret == TASK_DEAD) {
+ pr_err("Zombie thread not supported\n");
+ goto err;
+ }
+
+ if (ret == TASK_STOPPED) {
+ nr_stopped++;
+ }
+ }
+
+ if (nr_stopped && nr_stopped != nr_inprogress) {
+ pr_err("Individually stopped threads not supported\n");
+ goto err;
+ }
+
+ xfree(threads);
+ return nr_inprogress;
+
+err:
+ xfree(threads);
+ return -1;
+}
+
+static int collect_loop(struct pstree_item *item,
+ int (*collect)(struct pstree_item *))
+{
+ int attempts = NR_ATTEMPTS, nr_inprogress = 1;
+
+ /*
+ * While we scan the proc and seize the children/threads
+ * new ones can appear (with clone(CLONE_PARENT) or with
+ * pthread_create). Thus, after one go, we need to repeat
+ * the scan-and-freeze again collecting new arrivals. As
+ * new guys may appear again we do NR_ATTEMPTS passes and
+ * fail to seize the item if new tasks/threads still
+ * appear.
+ */
+
+ while (nr_inprogress > 0 && attempts) {
+ attempts--;
+ nr_inprogress = collect(item);
+ }
+
+ /*
+ * We may fail to collect items or run out of attempts.
+ * In the former case nr_inprogress will be negative, in
+ * the latter -- positive. Thus it's enough just to check
+ * for "no more new stuff" and say "we're OK" if so.
+ */
+
+ return (nr_inprogress == 0) ? 0 : -1;
+}
+
+static int collect_task(struct pstree_item *item)
+{
+ int ret;
+
+ ret = collect_loop(item, collect_threads);
+ if (ret < 0)
+ goto err_close;
+
+ /* Depth-first search (DFS) is used for traversing a process tree. */
+ ret = collect_loop(item, collect_children);
+ if (ret < 0)
+ goto err_close;
+
+ if ((item->state == TASK_DEAD) && !list_empty(&item->children)) {
+ pr_err("Zombie with children?! O_o Run, run, run!\n");
+ goto err_close;
+ }
+
+ if (pstree_alloc_cores(item))
+ goto err_close;
+
+ pr_info("Collected %d in %d state\n", item->pid.real, item->state);
+ return 0;
+
+err_close:
+ close_pid_proc();
+ return -1;
+}
+
+int collect_pstree(pid_t pid)
+{
+ int ret;
+
+ timing_start(TIME_FREEZING);
+
+ root_item = alloc_pstree_item();
+ if (root_item == NULL)
+ return -1;
+
+ root_item->pid.real = pid;
+ ret = seize_task(pid, -1, &dmpi(root_item)->pi_creds);
+ if (ret < 0)
+ goto err;
+ pr_info("Seized task %d, state %d\n", pid, ret);
+ root_item->state = ret;
+
+ ret = collect_task(root_item);
+ if (ret < 0)
+ goto err;
+
+ timing_stop(TIME_FREEZING);
+ timing_start(TIME_FROZEN);
+
+ return 0;
+err:
+ pstree_switch_state(root_item, TASK_ALIVE);
+ return -1;
+}
+
--
2.1.0
More information about the CRIU
mailing list