[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