Beispiel #1
0
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;
		}

		if (!opts.freeze_cgroup)
			/* fails when meets a zombie */
			seize_catch_task(pid);

		ret = seize_wait_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;
}
Beispiel #2
0
int cr_exec(int pid, char **opt)
{
	char *sys_name = opt[0];
	struct syscall_exec_desc *si;
	struct parasite_ctl *ctl;
	struct vm_area_list vmas;
	int ret = -1, prev_state;
	struct proc_status_creds *creds;

	if (!sys_name) {
		pr_err("Syscall name required\n");
		goto out;
	}

	si = find_syscall(sys_name);
	if (!si) {
		pr_err("Unknown syscall [%s]\n", sys_name);
		goto out;
	}

	if (seize_catch_task(pid))
		goto out;

	prev_state = ret = seize_wait_task(pid, -1, &creds);
	if (ret < 0) {
		pr_err("Can't seize task %d\n", pid);
		goto out;
	}

	/*
	 * We don't seize a task's threads here, and there is no reason to
	 * compare threads' creds in this use case anyway, so let's just free
	 * the creds.
	 */
	free(creds);

	ret = collect_mappings(pid, &vmas, NULL);
	if (ret) {
		pr_err("Can't collect vmas for %d\n", pid);
		goto out_unseize;
	}

	ctl = parasite_prep_ctl(pid, &vmas);
	if (!ctl) {
		pr_err("Can't prep ctl %d\n", pid);
		goto out_unseize;
	}

	ret = execute_syscall(ctl, si, opt + 1);
	if (ret < 0)
		pr_err("Can't execute syscall remotely\n");

	parasite_cure_seized(ctl);
out_unseize:
	unseize_task(pid, prev_state, prev_state);
out:
	return ret;
}
Beispiel #3
0
int collect_pstree(pid_t pid)
{
	int ret;

	timing_start(TIME_FREEZING);

	if (opts.freeze_cgroup && freeze_processes())
		return -1;

	root_item = alloc_pstree_item();
	if (root_item == NULL)
		return -1;

	root_item->pid.real = pid;

	if (!opts.freeze_cgroup && seize_catch_task(pid)) {
		set_cr_errno(ESRCH);
		goto err;
	}

	ret = seize_wait_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;
}
Beispiel #4
0
static int freeze_processes(void)
{
	int i, ret, fd, exit_code = -1;
	char path[PATH_MAX];
	const char *state = thawed;
	FILE *f;

	snprintf(path, sizeof(path), "%s/freezer.state", opts.freeze_cgroup);
	fd = open(path, O_RDWR);
	if (fd < 0) {
		pr_perror("Unable to open %s", path);
		return -1;
	}
	state = get_freezer_state(fd);
	if (!state) {
		close(fd);
		return -1;
	}
	if (state == thawed) {
		freezer_thawed = true;

		lseek(fd, 0, SEEK_SET);
		if (write(fd, frozen, sizeof(frozen)) != sizeof(frozen)) {
			pr_perror("Unable to freeze tasks");
			close(fd);
			return -1;
		}
	}

	/*
	 * There is not way to wait a specified state, so we need to poll the
	 * freezer.state.
	 * Here is one extra attempt to check that everything are frozen.
	 */
	for (i = 0; i <= NR_ATTEMPTS; i++) {
		struct timespec req = {};
		u64 timeout;

		/*
		 * New tasks can appear while a freezer state isn't
		 * frozen, so we need to catch all new tasks.
		 */
		snprintf(path, sizeof(path), "%s/tasks", opts.freeze_cgroup);
		f = fopen(path, "r");
		if (f == NULL) {
			pr_perror("Unable to open %s", path);
			goto err;
		}
		while (fgets(path, sizeof(path), f)) {
			pid_t pid;

			pid = atoi(path);

			/*
			 * Here we are going to skip tasks which are already traced.
			 * Ptraced tasks looks like children for us, so if
			 * a task isn't ptraced yet, waitpid() will return a error.
			 */
			ret = wait4(pid, NULL, __WALL | WNOHANG, NULL);
			if (ret == 0)
				continue;

			if (seize_catch_task(pid) && state == frozen) {
				char buf[] = "/proc/XXXXXXXXXX/exe";
				struct stat st;

				/* skip kernel threads */
				snprintf(buf, sizeof(buf), "/proc/%d/exe", pid);
				if (stat(buf, &st) == -1 && errno == ENOENT)
					continue;

				/* fails when meets a zombie */
				fclose(f);
				goto err;
			}
		}
		fclose(f);

		if (state == frozen)
			break;

		state = get_freezer_state(fd);
		if (!state)
			goto err;

		if (state == frozen) {
			/*
			 * Enumerate all tasks one more time to collect all new
			 * tasks, which can be born while the cgroup is being frozen.
			 */

			continue;
		}

		timeout = 10000000 * i;
		req.tv_nsec = timeout % 1000000000;
		req.tv_sec = timeout / 1000000000;
		nanosleep(&req, NULL);
	}

	if (i > NR_ATTEMPTS) {
		pr_err("Unable to freeze cgroup %s\n", opts.freeze_cgroup);
		goto err;
	}

	exit_code = 0;
err:
	if (exit_code == 0 || freezer_thawed) {
		lseek(fd, 0, SEEK_SET);
		if (write(fd, thawed, sizeof(thawed)) != sizeof(thawed)) {
			pr_perror("Unable to thaw tasks");
			exit_code = -1;
		}
	}
	if (close(fd)) {
		pr_perror("Unable to thaw tasks");
		return -1;
	}

	return exit_code;
}
Beispiel #5
0
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);

		if (!opts.freeze_cgroup && seize_catch_task(pid))
			continue;

		ret = seize_wait_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;
}