2012-06-26 14:51:00 +04:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2013-11-06 17:21:11 +04:00
|
|
|
#include "cr_options.h"
|
2012-06-26 14:51:00 +04:00
|
|
|
#include "pstree.h"
|
|
|
|
#include "util.h"
|
2013-01-11 18:16:23 +04:00
|
|
|
#include "lock.h"
|
2013-01-17 18:10:29 +04:00
|
|
|
#include "namespaces.h"
|
2013-01-19 00:30:42 +04:00
|
|
|
#include "files.h"
|
2013-03-27 20:11:00 +04:00
|
|
|
#include "tty.h"
|
2014-04-21 18:23:14 +04:00
|
|
|
#include "mount.h"
|
2013-02-22 15:41:27 +04:00
|
|
|
#include "asm/dump.h"
|
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
#include "protobuf.h"
|
|
|
|
#include "protobuf/pstree.pb-c.h"
|
|
|
|
|
2012-06-26 14:51:00 +04:00
|
|
|
struct pstree_item *root_item;
|
|
|
|
|
2013-05-24 16:20:04 +04:00
|
|
|
void core_entry_free(CoreEntry *core)
|
|
|
|
{
|
2014-04-15 21:59:05 +04:00
|
|
|
if (core->tc && core->tc->timers)
|
|
|
|
xfree(core->tc->timers->posix);
|
2014-03-13 15:23:12 +04:00
|
|
|
arch_free_thread_info(core);
|
|
|
|
xfree(core);
|
2013-05-24 16:20:04 +04:00
|
|
|
}
|
|
|
|
|
2014-03-14 18:38:00 +04:00
|
|
|
#ifndef RLIM_NLIMITS
|
|
|
|
# define RLIM_NLIMITS 16
|
|
|
|
#endif
|
|
|
|
|
2014-03-13 15:23:12 +04:00
|
|
|
CoreEntry *core_entry_alloc(int th, int tsk)
|
2013-05-24 16:20:04 +04:00
|
|
|
{
|
2014-03-13 15:23:12 +04:00
|
|
|
size_t sz;
|
|
|
|
CoreEntry *core = NULL;
|
|
|
|
void *m;
|
|
|
|
|
|
|
|
sz = sizeof(CoreEntry);
|
2014-03-13 17:17:26 +04:00
|
|
|
if (tsk) {
|
2014-03-13 15:23:12 +04:00
|
|
|
sz += sizeof(TaskCoreEntry) + TASK_COMM_LEN;
|
2014-03-13 17:17:26 +04:00
|
|
|
if (th) {
|
|
|
|
sz += sizeof(TaskRlimitsEntry);
|
|
|
|
sz += RLIM_NLIMITS * sizeof(RlimitEntry *);
|
|
|
|
sz += RLIM_NLIMITS * sizeof(RlimitEntry);
|
2014-04-15 21:58:31 +04:00
|
|
|
sz += sizeof(TaskTimersEntry);
|
2014-04-15 21:58:49 +04:00
|
|
|
sz += 3 * sizeof(ItimerEntry); /* 3 for real, virt and prof */
|
2014-03-13 17:17:26 +04:00
|
|
|
}
|
|
|
|
}
|
2014-03-13 15:23:12 +04:00
|
|
|
if (th)
|
|
|
|
sz += sizeof(ThreadCoreEntry) + sizeof(ThreadSasEntry);
|
|
|
|
|
|
|
|
m = xmalloc(sz);
|
|
|
|
if (m) {
|
|
|
|
core = xptr_pull(&m, CoreEntry);
|
|
|
|
core_entry__init(core);
|
|
|
|
core->mtype = CORE_ENTRY__MARCH;
|
|
|
|
|
|
|
|
if (tsk) {
|
|
|
|
core->tc = xptr_pull(&m, TaskCoreEntry);
|
|
|
|
task_core_entry__init(core->tc);
|
|
|
|
core->tc->comm = xptr_pull_s(&m, TASK_COMM_LEN);
|
|
|
|
memzero(core->tc->comm, TASK_COMM_LEN);
|
2014-03-13 17:17:26 +04:00
|
|
|
|
|
|
|
if (th) {
|
|
|
|
TaskRlimitsEntry *rls;
|
2014-04-15 21:58:49 +04:00
|
|
|
TaskTimersEntry *tte;
|
2014-03-13 17:17:26 +04:00
|
|
|
int i;
|
|
|
|
|
2014-04-15 22:00:11 +04:00
|
|
|
rls = core->tc->rlimits = xptr_pull(&m, TaskRlimitsEntry);
|
2014-03-13 17:17:26 +04:00
|
|
|
task_rlimits_entry__init(rls);
|
|
|
|
|
|
|
|
rls->n_rlimits = RLIM_NLIMITS;
|
|
|
|
rls->rlimits = xptr_pull_s(&m, sizeof(RlimitEntry *) * RLIM_NLIMITS);
|
|
|
|
|
|
|
|
for (i = 0; i < RLIM_NLIMITS; i++) {
|
|
|
|
rls->rlimits[i] = xptr_pull(&m, RlimitEntry);
|
|
|
|
rlimit_entry__init(rls->rlimits[i]);
|
|
|
|
}
|
2014-04-15 21:58:31 +04:00
|
|
|
|
2014-04-15 21:58:49 +04:00
|
|
|
tte = core->tc->timers = xptr_pull(&m, TaskTimersEntry);
|
|
|
|
task_timers_entry__init(tte);
|
|
|
|
tte->real = xptr_pull(&m, ItimerEntry);
|
|
|
|
itimer_entry__init(tte->real);
|
|
|
|
tte->virt = xptr_pull(&m, ItimerEntry);
|
|
|
|
itimer_entry__init(tte->virt);
|
|
|
|
tte->prof = xptr_pull(&m, ItimerEntry);
|
|
|
|
itimer_entry__init(tte->prof);
|
2014-03-13 17:17:26 +04:00
|
|
|
}
|
2014-03-13 15:23:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (th) {
|
|
|
|
core->thread_core = xptr_pull(&m, ThreadCoreEntry);
|
|
|
|
thread_core_entry__init(core->thread_core);
|
|
|
|
core->thread_core->sas = xptr_pull(&m, ThreadSasEntry);
|
|
|
|
thread_sas_entry__init(core->thread_core->sas);
|
|
|
|
|
|
|
|
if (arch_alloc_thread_info(core)) {
|
|
|
|
xfree(core);
|
|
|
|
core = NULL;
|
|
|
|
}
|
|
|
|
}
|
2013-05-24 16:20:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return core;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pstree_alloc_cores(struct pstree_item *item)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
item->core = xzalloc(sizeof(*item->core) * item->nr_threads);
|
|
|
|
if (!item->core)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0; i < item->nr_threads; i++) {
|
2013-05-24 16:20:08 +04:00
|
|
|
if (item->threads[i].real == item->pid.real)
|
2013-05-24 16:20:04 +04:00
|
|
|
item->core[i] = core_entry_alloc(1, 1);
|
2013-05-24 16:20:08 +04:00
|
|
|
else
|
2013-05-24 16:20:04 +04:00
|
|
|
item->core[i] = core_entry_alloc(1, 0);
|
|
|
|
|
|
|
|
if (!item->core[i])
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
pstree_free_cores(item);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pstree_free_cores(struct pstree_item *item)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (item->core) {
|
|
|
|
for (i = 1; i < item->nr_threads; i++)
|
|
|
|
core_entry_free(item->core[i]);
|
|
|
|
xfree(item->core);
|
|
|
|
item->core = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-26 14:51:00 +04:00
|
|
|
void free_pstree(struct pstree_item *root_item)
|
|
|
|
{
|
|
|
|
struct pstree_item *item = root_item, *parent;
|
|
|
|
|
|
|
|
while (item) {
|
|
|
|
if (!list_empty(&item->children)) {
|
2012-10-08 18:59:26 +04:00
|
|
|
item = list_first_entry(&item->children, struct pstree_item, sibling);
|
2012-06-26 14:51:00 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent = item->parent;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_del(&item->sibling);
|
2013-05-24 16:20:04 +04:00
|
|
|
pstree_free_cores(item);
|
2012-06-26 14:51:00 +04:00
|
|
|
xfree(item->threads);
|
|
|
|
xfree(item);
|
|
|
|
item = parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pstree_item *__alloc_pstree_item(bool rst)
|
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
2014-09-29 22:04:52 +04:00
|
|
|
int sz;
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2013-09-23 14:33:24 +04:00
|
|
|
if (!rst) {
|
2014-09-29 22:04:52 +04:00
|
|
|
sz = sizeof(*item) + sizeof(struct dmp_info);
|
|
|
|
item = xzalloc(sz);
|
2013-09-23 14:33:24 +04:00
|
|
|
if (!item)
|
|
|
|
return NULL;
|
|
|
|
} else {
|
2014-09-29 22:04:39 +04:00
|
|
|
sz = sizeof(*item) + sizeof(struct rst_info);
|
|
|
|
item = shmalloc(sz);
|
2013-09-23 14:33:24 +04:00
|
|
|
if (!item)
|
|
|
|
return NULL;
|
2014-09-29 22:04:39 +04:00
|
|
|
|
|
|
|
memset(item, 0, sz);
|
|
|
|
vm_area_list_init(&rsti(item)->vmas);
|
2013-09-23 14:33:24 +04:00
|
|
|
}
|
2012-06-26 14:51:00 +04:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&item->children);
|
2012-10-08 18:59:26 +04:00
|
|
|
INIT_LIST_HEAD(&item->sibling);
|
2012-10-03 17:25:09 +04:00
|
|
|
|
2012-06-26 14:51:00 +04:00
|
|
|
item->pid.virt = -1;
|
|
|
|
item->pid.real = -1;
|
|
|
|
item->born_sid = -1;
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2014-10-09 17:42:00 +04:00
|
|
|
struct pstree_item *alloc_pstree_helper(void)
|
|
|
|
{
|
|
|
|
struct pstree_item *ret;
|
|
|
|
|
|
|
|
ret = alloc_pstree_item_with_rst();
|
|
|
|
if (ret) {
|
|
|
|
ret->state = TASK_HELPER;
|
|
|
|
rsti(ret)->clone_flags = CLONE_FILES | CLONE_FS;
|
|
|
|
task_entries->nr_helpers++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-10-03 17:23:41 +04:00
|
|
|
/* Deep first search on children */
|
2012-06-26 14:51:00 +04:00
|
|
|
struct pstree_item *pstree_item_next(struct pstree_item *item)
|
|
|
|
{
|
2012-10-03 17:23:41 +04:00
|
|
|
if (!list_empty(&item->children))
|
2012-10-08 18:59:26 +04:00
|
|
|
return list_first_entry(&item->children, struct pstree_item, sibling);
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2012-10-03 17:23:41 +04:00
|
|
|
while (item->parent) {
|
2012-10-08 18:59:26 +04:00
|
|
|
if (item->sibling.next != &item->parent->children)
|
|
|
|
return list_entry(item->sibling.next, struct pstree_item, sibling);
|
2012-10-03 17:23:41 +04:00
|
|
|
item = item->parent;
|
2012-06-26 14:51:00 +04:00
|
|
|
}
|
|
|
|
|
2012-10-03 17:23:41 +04:00
|
|
|
return NULL;
|
2012-06-26 14:51:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int dump_pstree(struct pstree_item *root_item)
|
|
|
|
{
|
|
|
|
struct pstree_item *item = root_item;
|
2012-07-17 07:29:36 +04:00
|
|
|
PstreeEntry e = PSTREE_ENTRY__INIT;
|
2012-06-26 14:51:00 +04:00
|
|
|
int ret = -1, i;
|
2014-09-29 12:48:53 +04:00
|
|
|
struct cr_img *img;
|
2012-06-26 14:51:00 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping pstree (pid: %d)\n", root_item->pid.real);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-10-18 15:51:58 +04:00
|
|
|
/*
|
|
|
|
* Make sure we're dumping session leader, if not an
|
|
|
|
* appropriate option must be passed.
|
|
|
|
*
|
|
|
|
* Also note that if we're not a session leader we
|
|
|
|
* can't get the situation where the leader sits somewhere
|
|
|
|
* deeper in process tree, thus top-level checking for
|
|
|
|
* leader is enough.
|
|
|
|
*/
|
|
|
|
if (root_item->pid.virt != root_item->sid) {
|
|
|
|
if (!opts.shell_job) {
|
2013-03-27 20:11:00 +04:00
|
|
|
pr_err("The root process %d is not a session leader. "
|
|
|
|
"Consider using --" OPT_SHELL_JOB " option\n", item->pid.virt);
|
2012-10-18 15:51:58 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_PSTREE, O_DUMP);
|
|
|
|
if (!img)
|
2012-06-26 14:51:00 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
for_each_pstree_item(item) {
|
|
|
|
pr_info("Process: %d(%d)\n", item->pid.virt, item->pid.real);
|
|
|
|
|
|
|
|
e.pid = item->pid.virt;
|
|
|
|
e.ppid = item->parent ? item->parent->pid.virt : 0;
|
|
|
|
e.pgid = item->pgid;
|
|
|
|
e.sid = item->sid;
|
2012-07-17 07:29:36 +04:00
|
|
|
e.n_threads = item->nr_threads;
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
e.threads = xmalloc(sizeof(e.threads[0]) * e.n_threads);
|
|
|
|
if (!e.threads)
|
2012-06-26 14:51:00 +04:00
|
|
|
goto err;
|
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
for (i = 0; i < item->nr_threads; i++)
|
|
|
|
e.threads[i] = item->threads[i].virt;
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
ret = pb_write_one(img, &e, PB_PSTREE);
|
2012-07-17 07:29:36 +04:00
|
|
|
xfree(e.threads);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
2012-06-26 14:51:00 +04:00
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
pr_info("----------------------------------------\n");
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(img);
|
2012-06-26 14:51:00 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int max_pid = 0;
|
2012-10-18 15:51:58 +04:00
|
|
|
|
2012-12-10 15:54:12 +03:00
|
|
|
static int prepare_pstree_for_shell_job(void)
|
2012-10-18 15:51:58 +04:00
|
|
|
{
|
|
|
|
pid_t current_sid = getsid(getpid());
|
|
|
|
pid_t current_gid = getpgid(getpid());
|
|
|
|
|
|
|
|
struct pstree_item *pi;
|
|
|
|
|
|
|
|
pid_t old_sid;
|
|
|
|
pid_t old_gid;
|
|
|
|
|
|
|
|
if (!opts.shell_job)
|
|
|
|
return 0;
|
|
|
|
|
2013-04-01 20:10:44 +04:00
|
|
|
if (root_item->sid == root_item->pid.virt)
|
|
|
|
return 0;
|
|
|
|
|
2012-10-18 15:51:58 +04:00
|
|
|
/*
|
|
|
|
* Migration of a root task group leader is a bit tricky.
|
|
|
|
* When a task yields SIGSTOP, the kernel notifies the parent
|
|
|
|
* with SIGCHLD. This means when task is running in a
|
|
|
|
* shell, the shell obtains SIGCHLD and sends a task to
|
|
|
|
* the background.
|
|
|
|
*
|
|
|
|
* The situation gets changed once we restore the
|
|
|
|
* program -- our tool become an additional stub between
|
|
|
|
* the restored program and the shell. So to be able to
|
|
|
|
* notify the shell with SIGCHLD from our restored
|
|
|
|
* program -- we make the root task to inherit the
|
|
|
|
* process group from us.
|
|
|
|
*
|
|
|
|
* Not that clever solution but at least it works.
|
|
|
|
*/
|
|
|
|
|
2012-12-10 15:54:12 +03:00
|
|
|
old_sid = root_item->sid;
|
|
|
|
old_gid = root_item->pgid;
|
2012-10-18 15:51:58 +04:00
|
|
|
|
|
|
|
pr_info("Migrating process tree (GID %d->%d SID %d->%d)\n",
|
|
|
|
old_gid, current_gid, old_sid, current_sid);
|
|
|
|
|
|
|
|
for_each_pstree_item(pi) {
|
|
|
|
if (pi->pgid == old_gid)
|
|
|
|
pi->pgid = current_gid;
|
|
|
|
if (pi->sid == old_sid)
|
|
|
|
pi->sid = current_sid;
|
|
|
|
}
|
|
|
|
|
|
|
|
max_pid = max((int)current_sid, max_pid);
|
|
|
|
max_pid = max((int)current_gid, max_pid);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-10 15:50:47 +03:00
|
|
|
static int read_pstree_image(void)
|
2012-06-26 14:51:00 +04:00
|
|
|
{
|
2014-09-29 12:48:53 +04:00
|
|
|
int ret = 0, i;
|
|
|
|
struct cr_img *img;
|
2012-06-26 14:51:00 +04:00
|
|
|
struct pstree_item *pi, *parent = NULL;
|
|
|
|
|
|
|
|
pr_info("Reading image tree\n");
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_PSTREE, O_RSTR);
|
|
|
|
if (!img)
|
2014-09-29 12:48:10 +04:00
|
|
|
return -1;
|
2012-06-26 14:51:00 +04:00
|
|
|
|
|
|
|
while (1) {
|
2012-07-17 07:29:36 +04:00
|
|
|
PstreeEntry *e;
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
ret = pb_read_one_eof(img, &e, PB_PSTREE);
|
2012-06-26 14:51:00 +04:00
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
pi = alloc_pstree_item_with_rst();
|
|
|
|
if (pi == NULL)
|
|
|
|
break;
|
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
pi->pid.virt = e->pid;
|
2012-09-17 18:22:13 +04:00
|
|
|
max_pid = max((int)e->pid, max_pid);
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
pi->pgid = e->pgid;
|
2012-09-17 18:22:13 +04:00
|
|
|
max_pid = max((int)e->pgid, max_pid);
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
pi->sid = e->sid;
|
2012-09-17 18:22:13 +04:00
|
|
|
max_pid = max((int)e->sid, max_pid);
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
if (e->ppid == 0) {
|
2012-09-28 14:01:17 +04:00
|
|
|
if (root_item) {
|
|
|
|
pr_err("Parent missed on non-root task "
|
|
|
|
"with pid %d, image corruption!\n", e->pid);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-06-26 14:51:00 +04:00
|
|
|
root_item = pi;
|
|
|
|
pi->parent = NULL;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Fast path -- if the pstree image is not edited, the
|
|
|
|
* parent of any item should have already being restored
|
|
|
|
* and sit among the last item's ancestors.
|
|
|
|
*/
|
|
|
|
while (parent) {
|
2012-07-17 07:29:36 +04:00
|
|
|
if (parent->pid.virt == e->ppid)
|
2012-06-26 14:51:00 +04:00
|
|
|
break;
|
|
|
|
parent = parent->parent;
|
|
|
|
}
|
|
|
|
|
2012-10-03 17:29:04 +04:00
|
|
|
if (parent == NULL) {
|
|
|
|
for_each_pstree_item(parent) {
|
2012-07-17 07:29:36 +04:00
|
|
|
if (parent->pid.virt == e->ppid)
|
2012-06-26 14:51:00 +04:00
|
|
|
break;
|
2012-10-03 17:29:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parent == NULL) {
|
2012-11-23 16:43:33 +04:00
|
|
|
pr_err("Can't find a parent for %d\n", pi->pid.virt);
|
2012-10-03 17:29:04 +04:00
|
|
|
pstree_entry__free_unpacked(e, NULL);
|
|
|
|
xfree(pi);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-06-26 14:51:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
pi->parent = parent;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_add(&pi->sibling, &parent->children);
|
2012-06-26 14:51:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
parent = pi;
|
|
|
|
|
2012-07-17 07:29:36 +04:00
|
|
|
pi->nr_threads = e->n_threads;
|
|
|
|
pi->threads = xmalloc(e->n_threads * sizeof(struct pid));
|
2012-06-26 14:51:00 +04:00
|
|
|
if (!pi->threads)
|
|
|
|
break;
|
|
|
|
|
2013-09-23 14:33:32 +04:00
|
|
|
for (i = 0; i < e->n_threads; i++) {
|
|
|
|
pi->threads[i].real = -1;
|
2012-07-17 07:29:36 +04:00
|
|
|
pi->threads[i].virt = e->threads[i];
|
2014-10-29 15:28:00 +04:00
|
|
|
max_pid = max((int)e->threads[i], max_pid);
|
2013-09-23 14:33:32 +04:00
|
|
|
}
|
2012-06-26 14:51:00 +04:00
|
|
|
|
2012-12-04 17:22:45 +03:00
|
|
|
task_entries->nr_threads += e->n_threads;
|
2012-06-26 14:51:00 +04:00
|
|
|
task_entries->nr_tasks++;
|
2012-07-17 07:29:36 +04:00
|
|
|
|
|
|
|
pstree_entry__free_unpacked(e, NULL);
|
2013-01-11 18:16:21 +04:00
|
|
|
|
2014-09-29 12:48:31 +04:00
|
|
|
{
|
2014-09-29 12:48:53 +04:00
|
|
|
struct cr_img *img;
|
2014-09-29 12:48:31 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_IDS, O_RSTR, pi->pid.virt);
|
|
|
|
if (!img) {
|
2014-09-29 12:48:31 +04:00
|
|
|
if (errno == ENOENT)
|
|
|
|
continue;
|
|
|
|
goto err;
|
|
|
|
}
|
2014-09-29 12:48:53 +04:00
|
|
|
ret = pb_read_one(img, &pi->ids, PB_IDS);
|
|
|
|
close_image(img);
|
|
|
|
}
|
2014-09-29 12:48:31 +04:00
|
|
|
|
2013-01-11 18:16:21 +04:00
|
|
|
if (ret != 1)
|
|
|
|
goto err;
|
|
|
|
|
2014-04-21 18:23:14 +04:00
|
|
|
if (pi->ids->has_mnt_ns_id) {
|
|
|
|
if (rst_add_ns_id(pi->ids->mnt_ns_id, pi->pid.virt, &mnt_ns_desc))
|
|
|
|
goto err;
|
|
|
|
}
|
2012-06-26 14:51:00 +04:00
|
|
|
}
|
2012-09-28 14:01:17 +04:00
|
|
|
err:
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(img);
|
2012-06-26 14:51:00 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-12-10 15:50:47 +03:00
|
|
|
static int prepare_pstree_ids(void)
|
2012-06-26 14:51:00 +04:00
|
|
|
{
|
|
|
|
struct pstree_item *item, *child, *helper, *tmp;
|
|
|
|
LIST_HEAD(helpers);
|
|
|
|
|
2012-10-18 15:51:58 +04:00
|
|
|
pid_t current_pgid = getpgid(getpid());
|
|
|
|
|
2012-06-26 14:51:00 +04:00
|
|
|
/*
|
|
|
|
* Some task can be reparented to init. A helper task should be added
|
|
|
|
* for restoring sid of such tasks. The helper tasks will be exited
|
|
|
|
* immediately after forking children and all children will be
|
|
|
|
* reparented to init.
|
|
|
|
*/
|
2012-10-08 18:59:26 +04:00
|
|
|
list_for_each_entry(item, &root_item->children, sibling) {
|
2012-10-03 17:29:26 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If a child belongs to the root task's session or it's
|
|
|
|
* a session leader himself -- this is a simple case, we
|
|
|
|
* just proceed in a normal way.
|
|
|
|
*/
|
2012-06-26 14:51:00 +04:00
|
|
|
if (item->sid == root_item->sid || item->sid == item->pid.virt)
|
|
|
|
continue;
|
|
|
|
|
2014-10-09 17:42:00 +04:00
|
|
|
helper = alloc_pstree_helper();
|
2012-06-26 14:51:00 +04:00
|
|
|
if (helper == NULL)
|
|
|
|
return -1;
|
|
|
|
helper->sid = item->sid;
|
|
|
|
helper->pgid = item->sid;
|
|
|
|
helper->pid.virt = item->sid;
|
|
|
|
helper->parent = root_item;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_add_tail(&helper->sibling, &helpers);
|
2012-06-26 14:51:00 +04:00
|
|
|
|
|
|
|
pr_info("Add a helper %d for restoring SID %d\n",
|
|
|
|
helper->pid.virt, helper->sid);
|
|
|
|
|
2012-10-08 18:59:26 +04:00
|
|
|
child = list_entry(item->sibling.prev, struct pstree_item, sibling);
|
2012-06-26 14:51:00 +04:00
|
|
|
item = child;
|
|
|
|
|
2012-10-03 17:29:26 +04:00
|
|
|
/*
|
|
|
|
* Stack on helper task all children with target sid.
|
|
|
|
*/
|
2012-10-08 18:59:26 +04:00
|
|
|
list_for_each_entry_safe_continue(child, tmp, &root_item->children, sibling) {
|
2012-06-26 14:51:00 +04:00
|
|
|
if (child->sid != helper->sid)
|
|
|
|
continue;
|
|
|
|
if (child->sid == child->pid.virt)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pr_info("Attach %d to the temporary task %d\n",
|
|
|
|
child->pid.virt, helper->pid.virt);
|
|
|
|
|
|
|
|
child->parent = helper;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_move(&child->sibling, &helper->children);
|
2012-06-26 14:51:00 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to connect helpers to session leaders */
|
|
|
|
for_each_pstree_item(item) {
|
|
|
|
if (!item->parent) /* skip the root task */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (item->state == TASK_HELPER)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (item->sid != item->pid.virt) {
|
|
|
|
struct pstree_item *parent;
|
|
|
|
|
|
|
|
if (item->parent->sid == item->sid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* the task could fork a child before and after setsid() */
|
|
|
|
parent = item->parent;
|
|
|
|
while (parent && parent->pid.virt != item->sid) {
|
|
|
|
if (parent->born_sid != -1 && parent->born_sid != item->sid) {
|
2012-10-03 17:21:54 +04:00
|
|
|
pr_err("Can't determinate with which sid (%d or %d)"
|
2012-06-26 14:51:00 +04:00
|
|
|
"the process %d was born\n",
|
|
|
|
parent->born_sid, item->sid, parent->pid.virt);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
parent->born_sid = item->sid;
|
|
|
|
pr_info("%d was born with sid %d\n", parent->pid.virt, item->sid);
|
|
|
|
parent = parent->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent == NULL) {
|
|
|
|
pr_err("Can't find a session leader for %d\n", item->sid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Session leader %d\n", item->sid);
|
|
|
|
|
|
|
|
/* Try to find helpers, who should be connected to the leader */
|
2012-10-08 18:59:26 +04:00
|
|
|
list_for_each_entry(child, &helpers, sibling) {
|
2012-06-26 14:51:00 +04:00
|
|
|
if (child->state != TASK_HELPER)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (child->sid != item->sid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
child->pgid = item->pgid;
|
|
|
|
child->pid.virt = ++max_pid;
|
|
|
|
child->parent = item;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_move(&child->sibling, &item->children);
|
2012-06-26 14:51:00 +04:00
|
|
|
|
|
|
|
pr_info("Attach %d to the task %d\n",
|
|
|
|
child->pid.virt, item->pid.virt);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* All other helpers are session leaders for own sessions */
|
|
|
|
list_splice(&helpers, &root_item->children);
|
|
|
|
|
2012-07-02 15:25:00 +04:00
|
|
|
/* Add a process group leader if it is absent */
|
|
|
|
for_each_pstree_item(item) {
|
|
|
|
struct pstree_item *gleader;
|
|
|
|
|
|
|
|
if (!item->pgid || item->pid.virt == item->pgid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for_each_pstree_item(gleader) {
|
|
|
|
if (gleader->pid.virt == item->pgid)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-09-27 04:38:00 +04:00
|
|
|
if (gleader) {
|
2014-09-29 22:04:39 +04:00
|
|
|
rsti(item)->pgrp_leader = gleader;
|
2012-07-02 15:25:00 +04:00
|
|
|
continue;
|
2013-09-27 04:38:00 +04:00
|
|
|
}
|
2012-07-02 15:25:00 +04:00
|
|
|
|
2012-10-18 15:51:58 +04:00
|
|
|
/*
|
|
|
|
* If the PGID is eq to current one -- this
|
|
|
|
* means we're inheriting group from the current
|
|
|
|
* task so we need to escape creating a helper here.
|
|
|
|
*/
|
|
|
|
if (current_pgid == item->pgid)
|
|
|
|
continue;
|
|
|
|
|
2014-10-09 17:42:00 +04:00
|
|
|
helper = alloc_pstree_helper();
|
2012-07-02 15:25:00 +04:00
|
|
|
if (helper == NULL)
|
|
|
|
return -1;
|
|
|
|
helper->sid = item->sid;
|
|
|
|
helper->pgid = item->pgid;
|
|
|
|
helper->pid.virt = item->pgid;
|
|
|
|
helper->parent = item;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_add(&helper->sibling, &item->children);
|
2014-09-29 22:04:39 +04:00
|
|
|
rsti(item)->pgrp_leader = helper;
|
2012-07-02 15:25:00 +04:00
|
|
|
|
|
|
|
pr_info("Add a helper %d for restoring PGID %d\n",
|
|
|
|
helper->pid.virt, helper->pgid);
|
|
|
|
}
|
|
|
|
|
2013-01-19 00:04:04 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-19 01:16:19 +04:00
|
|
|
static unsigned long get_clone_mask(TaskKobjIdsEntry *i,
|
|
|
|
TaskKobjIdsEntry *p)
|
|
|
|
{
|
|
|
|
unsigned long mask = 0;
|
|
|
|
|
|
|
|
if (i->files_id == p->files_id)
|
|
|
|
mask |= CLONE_FILES;
|
|
|
|
if (i->pid_ns_id != p->pid_ns_id)
|
|
|
|
mask |= CLONE_NEWPID;
|
|
|
|
if (i->net_ns_id != p->net_ns_id)
|
|
|
|
mask |= CLONE_NEWNET;
|
|
|
|
if (i->ipc_ns_id != p->ipc_ns_id)
|
|
|
|
mask |= CLONE_NEWIPC;
|
|
|
|
if (i->uts_ns_id != p->uts_ns_id)
|
|
|
|
mask |= CLONE_NEWUTS;
|
|
|
|
if (i->mnt_ns_id != p->mnt_ns_id)
|
|
|
|
mask |= CLONE_NEWNS;
|
2014-10-14 15:38:00 +04:00
|
|
|
if (i->user_ns_id != p->user_ns_id)
|
|
|
|
mask |= CLONE_NEWUSER;
|
2013-01-19 01:16:19 +04:00
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2013-01-19 00:04:04 +04:00
|
|
|
static int prepare_pstree_kobj_ids(void)
|
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
|
|
|
|
2013-01-11 18:16:23 +04:00
|
|
|
/* Find a process with minimal pid for shared fd tables */
|
|
|
|
for_each_pstree_item(item) {
|
|
|
|
struct pstree_item *parent = item->parent;
|
2013-01-19 01:16:19 +04:00
|
|
|
TaskKobjIdsEntry *ids;
|
|
|
|
unsigned long cflags;
|
|
|
|
|
|
|
|
if (!item->ids) {
|
|
|
|
if (item == root_item) {
|
|
|
|
cflags = opts.rst_namespaces_flags;
|
|
|
|
goto set_mask;
|
|
|
|
}
|
2013-01-11 18:16:23 +04:00
|
|
|
|
|
|
|
continue;
|
2013-01-19 01:16:19 +04:00
|
|
|
}
|
2013-01-11 18:16:23 +04:00
|
|
|
|
2013-01-19 01:16:19 +04:00
|
|
|
if (parent)
|
|
|
|
ids = parent->ids;
|
|
|
|
else
|
|
|
|
ids = root_ids;
|
2013-01-17 18:10:29 +04:00
|
|
|
|
2013-03-18 22:41:56 +04:00
|
|
|
/*
|
|
|
|
* Add some sanity check on image data.
|
|
|
|
*/
|
|
|
|
if (unlikely(!ids)) {
|
|
|
|
pr_err("No kIDs provided, image corruption\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-01-19 01:16:19 +04:00
|
|
|
cflags = get_clone_mask(item->ids, ids);
|
2013-01-11 18:16:23 +04:00
|
|
|
|
2013-01-19 01:16:19 +04:00
|
|
|
if (cflags & CLONE_FILES) {
|
2013-01-19 00:30:42 +04:00
|
|
|
int ret;
|
2013-01-11 18:16:23 +04:00
|
|
|
|
2013-03-18 22:42:28 +04:00
|
|
|
/*
|
|
|
|
* There might be a case when kIDs for
|
|
|
|
* root task are the same as in root_ids,
|
|
|
|
* thus it's image corruption and we should
|
|
|
|
* exit out.
|
|
|
|
*/
|
|
|
|
if (unlikely(!item->parent)) {
|
|
|
|
pr_err("Image corruption on kIDs data\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-01-19 00:30:42 +04:00
|
|
|
ret = shared_fdt_prepare(item);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2013-01-19 01:16:19 +04:00
|
|
|
|
|
|
|
set_mask:
|
2014-09-29 22:04:39 +04:00
|
|
|
rsti(item)->clone_flags = cflags;
|
2014-05-12 14:20:17 +04:00
|
|
|
if (parent)
|
|
|
|
/*
|
|
|
|
* Mount namespaces are setns()-ed at
|
|
|
|
* restore_task_mnt_ns() explicitly,
|
|
|
|
* no need in creating it with its own
|
|
|
|
* temporary namespace
|
|
|
|
*/
|
2014-09-29 22:04:39 +04:00
|
|
|
rsti(item)->clone_flags &= ~CLONE_NEWNS;
|
2013-01-19 01:16:19 +04:00
|
|
|
|
2014-04-21 18:23:21 +04:00
|
|
|
cflags &= CLONE_ALLNS;
|
|
|
|
|
2013-01-19 01:16:19 +04:00
|
|
|
if (item == root_item) {
|
|
|
|
pr_info("Will restore in %lx namespaces\n", cflags);
|
2014-04-21 18:23:22 +04:00
|
|
|
root_ns_mask = cflags;
|
|
|
|
} else if (cflags & ~(root_ns_mask & CLONE_SUBNS)) {
|
2014-04-21 18:23:21 +04:00
|
|
|
/*
|
|
|
|
* Namespaces from CLONE_SUBNS can be nested, but in
|
|
|
|
* this case nobody can't share external namespaces of
|
|
|
|
* these types.
|
|
|
|
*
|
|
|
|
* Workaround for all other namespaces --
|
|
|
|
* all tasks should be in one namespace. And
|
|
|
|
* this namespace is either inherited from the
|
|
|
|
* criu or is created for the init task (only)
|
|
|
|
*/
|
2013-01-19 01:16:19 +04:00
|
|
|
pr_err("Can't restore sub-task in NS\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2013-01-11 18:16:23 +04:00
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:22 +04:00
|
|
|
pr_debug("NS mask to use %lx\n", root_ns_mask);
|
2012-06-26 14:51:00 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-10 15:50:47 +03:00
|
|
|
int prepare_pstree(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = read_pstree_image();
|
|
|
|
if (!ret)
|
|
|
|
/*
|
|
|
|
* Shell job may inherit sid/pgid from the current
|
|
|
|
* shell, not from image. Set things up for this.
|
|
|
|
*/
|
2012-12-10 15:54:12 +03:00
|
|
|
ret = prepare_pstree_for_shell_job();
|
2013-01-19 00:04:04 +04:00
|
|
|
if (!ret)
|
|
|
|
/*
|
|
|
|
* Walk the collected tree and prepare for restoring
|
|
|
|
* of shared objects at clone time
|
|
|
|
*/
|
|
|
|
ret = prepare_pstree_kobj_ids();
|
2012-12-10 15:50:47 +03:00
|
|
|
if (!ret)
|
|
|
|
/*
|
|
|
|
* Session/Group leaders might be dead. Need to fix
|
|
|
|
* pstree with properly injected helper tasks.
|
|
|
|
*/
|
|
|
|
ret = prepare_pstree_ids();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-06-26 14:51:00 +04:00
|
|
|
bool restore_before_setsid(struct pstree_item *child)
|
|
|
|
{
|
|
|
|
int csid = child->born_sid == -1 ? child->sid : child->born_sid;
|
|
|
|
|
|
|
|
if (child->parent->born_sid == csid)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2013-01-17 16:09:31 +08:00
|
|
|
|
|
|
|
bool pid_in_pstree(pid_t pid)
|
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
|
|
|
|
|
|
|
for_each_pstree_item(item) {
|
|
|
|
if (item->pid.real == pid)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|