2012-07-18 07:23:05 +04:00
|
|
|
#include <sys/time.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2011-12-19 21:05:02 +04:00
|
|
|
#include <stdarg.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
#include <signal.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
|
|
|
|
#include <sys/sendfile.h>
|
|
|
|
|
2012-10-17 00:23:25 +04:00
|
|
|
#include <sched.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
|
2012-10-11 15:59:43 +04:00
|
|
|
#include "protobuf.h"
|
|
|
|
#include "protobuf/fdinfo.pb-c.h"
|
|
|
|
#include "protobuf/fs.pb-c.h"
|
|
|
|
#include "protobuf/mm.pb-c.h"
|
|
|
|
#include "protobuf/creds.pb-c.h"
|
|
|
|
#include "protobuf/core.pb-c.h"
|
2013-01-17 16:09:32 +08:00
|
|
|
#include "protobuf/file-lock.pb-c.h"
|
2013-01-10 20:08:38 +04:00
|
|
|
#include "protobuf/rlimit.pb-c.h"
|
2013-03-25 23:39:48 +04:00
|
|
|
#include "protobuf/siginfo.pb-c.h"
|
2012-10-11 15:59:43 +04:00
|
|
|
|
2013-01-09 17:02:47 +04:00
|
|
|
#include "asm/types.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "list.h"
|
2014-09-29 12:47:37 +04:00
|
|
|
#include "imgset.h"
|
2012-02-28 18:27:28 +04:00
|
|
|
#include "file-ids.h"
|
2012-04-09 18:02:00 +04:00
|
|
|
#include "kcmp-ids.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "compiler.h"
|
|
|
|
#include "crtools.h"
|
2013-11-06 17:21:11 +04:00
|
|
|
#include "cr_options.h"
|
2013-11-05 20:17:47 +04:00
|
|
|
#include "servicefd.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "syscall.h"
|
2011-12-19 21:57:59 +04:00
|
|
|
#include "ptrace.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "util.h"
|
2012-01-26 15:27:00 +04:00
|
|
|
#include "namespaces.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "image.h"
|
2012-01-13 20:52:35 +04:00
|
|
|
#include "proc_parse.h"
|
2012-03-28 17:36:00 +04:00
|
|
|
#include "parasite.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "parasite-syscall.h"
|
2012-03-29 16:40:10 +04:00
|
|
|
#include "files.h"
|
2012-06-20 20:00:30 +04:00
|
|
|
#include "files-reg.h"
|
2012-05-03 18:07:01 +04:00
|
|
|
#include "shmem.h"
|
2012-04-28 17:38:46 +04:00
|
|
|
#include "sk-inet.h"
|
2012-06-26 14:51:00 +04:00
|
|
|
#include "pstree.h"
|
2012-08-01 07:00:48 +04:00
|
|
|
#include "mount.h"
|
2012-09-12 20:00:54 +04:00
|
|
|
#include "tty.h"
|
2012-09-17 20:05:55 +04:00
|
|
|
#include "net.h"
|
2012-11-02 16:00:18 +03:00
|
|
|
#include "sk-packet.h"
|
2012-12-21 17:35:36 +04:00
|
|
|
#include "cpu.h"
|
2012-12-21 17:35:39 +04:00
|
|
|
#include "elf.h"
|
2014-05-08 16:37:00 +04:00
|
|
|
#include "cgroup.h"
|
2013-01-17 16:09:32 +08:00
|
|
|
#include "file-lock.h"
|
2013-03-12 21:23:06 +04:00
|
|
|
#include "page-xfer.h"
|
2013-04-15 13:02:09 +04:00
|
|
|
#include "kerndat.h"
|
2013-05-09 22:16:56 +04:00
|
|
|
#include "stats.h"
|
2013-05-08 16:24:38 +04:00
|
|
|
#include "mem.h"
|
2013-05-14 12:00:40 +04:00
|
|
|
#include "page-pipe.h"
|
2013-11-05 12:32:56 +04:00
|
|
|
#include "posix-timer.h"
|
2013-05-24 01:42:13 +04:00
|
|
|
#include "vdso.h"
|
2013-11-05 12:33:03 +04:00
|
|
|
#include "vma.h"
|
2013-09-13 13:44:21 +04:00
|
|
|
#include "cr-service.h"
|
2013-12-19 21:35:00 +04:00
|
|
|
#include "plugin.h"
|
2014-01-30 14:00:42 +04:00
|
|
|
#include "irmap.h"
|
2014-08-19 22:31:07 -07:00
|
|
|
#include "sysfs_parse.h"
|
2014-09-03 23:43:23 +04:00
|
|
|
#include "action-scripts.h"
|
2014-12-19 16:01:54 +03:00
|
|
|
#include "aio.h"
|
2015-01-29 22:59:27 +02:00
|
|
|
#include "security.h"
|
2015-05-06 16:18:42 -06:00
|
|
|
#include "lsm.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2013-01-09 17:27:49 +04:00
|
|
|
#include "asm/dump.h"
|
|
|
|
|
2013-12-20 14:45:15 +04:00
|
|
|
#define NR_ATTEMPTS 5
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
static char loc_buf[PAGE_SIZE];
|
|
|
|
|
2014-01-31 03:14:46 +04:00
|
|
|
static void close_vma_file(struct vma_area *vma)
|
|
|
|
{
|
|
|
|
if (vma->vm_file_fd < 0)
|
|
|
|
return;
|
2014-02-04 00:08:16 +04:00
|
|
|
if (vma->e->status & VMA_AREA_SOCKET)
|
2014-01-31 03:14:46 +04:00
|
|
|
return;
|
2014-01-31 20:31:06 +04:00
|
|
|
if (vma->file_borrowed)
|
|
|
|
return;
|
2014-01-31 03:14:46 +04:00
|
|
|
|
|
|
|
close(vma->vm_file_fd);
|
|
|
|
}
|
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
void free_mappings(struct vm_area_list *vma_area_list)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
struct vma_area *vma_area, *p;
|
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
list_for_each_entry_safe(vma_area, p, &vma_area_list->h, list) {
|
2014-01-31 03:14:46 +04:00
|
|
|
close_vma_file(vma_area);
|
2014-02-03 00:18:32 +04:00
|
|
|
if (!vma_area->file_borrowed)
|
2014-09-23 20:31:16 +04:00
|
|
|
free(vma_area->vmst);
|
2011-09-23 12:00:45 +04:00
|
|
|
free(vma_area);
|
|
|
|
}
|
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
INIT_LIST_HEAD(&vma_area_list->h);
|
|
|
|
vma_area_list->nr = 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
int collect_mappings(pid_t pid, struct vm_area_list *vma_area_list)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Collecting mappings (pid: %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2014-12-19 15:57:00 +03:00
|
|
|
ret = parse_smaps(pid, vma_area_list);
|
2012-03-02 19:28:46 +04:00
|
|
|
if (ret < 0)
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
|
2014-01-29 15:42:34 +04:00
|
|
|
pr_info("Collected, longest area occupies %lu pages\n", vma_area_list->longest);
|
2013-03-01 20:11:51 +04:00
|
|
|
pr_info_vma_list(&vma_area_list->h);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-10-17 00:23:25 +04:00
|
|
|
static int dump_sched_info(int pid, ThreadCoreEntry *tc)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct sched_param sp;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(SCHED_OTHER != 0); /* default in proto message */
|
|
|
|
|
|
|
|
ret = sched_getscheduler(pid);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("Can't get sched policy for %d", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("%d has %d sched policy\n", pid, ret);
|
|
|
|
tc->has_sched_policy = true;
|
|
|
|
tc->sched_policy = ret;
|
|
|
|
|
|
|
|
if ((ret == SCHED_RR) || (ret == SCHED_FIFO)) {
|
|
|
|
ret = sched_getparam(pid, &sp);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("Can't get sched param for %d", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("\tdumping %d prio for %d\n", sp.sched_priority, pid);
|
|
|
|
tc->has_sched_prio = true;
|
|
|
|
tc->sched_prio = sp.sched_priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The nice is ignored for RT sched policies, but is stored
|
|
|
|
* in kernel. Thus we have to take it with us in the image.
|
|
|
|
*/
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
ret = getpriority(PRIO_PROCESS, pid);
|
|
|
|
if (errno) {
|
|
|
|
pr_perror("Can't get nice for %d", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("\tdumping %d nice for %d\n", ret, pid);
|
|
|
|
tc->has_sched_nice = true;
|
|
|
|
tc->sched_nice = ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
struct cr_imgset *glob_imgset;
|
2012-03-25 20:24:53 +04:00
|
|
|
|
2012-08-21 19:59:07 +04:00
|
|
|
static int collect_fds(pid_t pid, struct parasite_drain_fd *dfds)
|
2012-03-28 17:36:00 +04:00
|
|
|
{
|
|
|
|
struct dirent *de;
|
|
|
|
DIR *fd_dir;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Collecting fds (pid: %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
|
|
|
fd_dir = opendir_proc(pid, "fd");
|
|
|
|
if (!fd_dir)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
while ((de = readdir(fd_dir))) {
|
2012-11-29 21:12:51 +03:00
|
|
|
if (dir_dots(de))
|
2012-03-28 17:36:00 +04:00
|
|
|
continue;
|
|
|
|
|
2012-08-21 19:59:07 +04:00
|
|
|
if (n > PARASITE_MAX_FDS - 1)
|
2012-03-28 17:36:00 +04:00
|
|
|
return -ENOMEM;
|
2012-08-21 19:59:07 +04:00
|
|
|
|
|
|
|
dfds->fds[n++] = atoi(de->d_name);
|
2012-03-28 17:36:00 +04:00
|
|
|
}
|
|
|
|
|
2012-08-21 19:59:07 +04:00
|
|
|
dfds->nr_fds = n;
|
2012-03-28 17:36:00 +04:00
|
|
|
pr_info("Found %d file descriptors\n", n);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
|
|
|
closedir(fd_dir);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:27 +04:00
|
|
|
static int fill_fd_params_special(int fd, struct fd_parms *p)
|
|
|
|
{
|
|
|
|
*p = FD_PARMS_INIT;
|
|
|
|
|
|
|
|
if (fstat(fd, &p->stat) < 0) {
|
|
|
|
pr_perror("Can't fstat exe link");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_fd_mntid(fd, &p->mnt_id))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-18 20:54:00 +04:00
|
|
|
static int dump_task_exe_link(pid_t pid, MmEntry *mm)
|
2011-12-07 17:11:00 +04:00
|
|
|
{
|
2014-04-21 18:23:27 +04:00
|
|
|
struct fd_parms params;
|
2014-02-04 19:25:33 +04:00
|
|
|
int fd, ret = 0;
|
2011-12-07 17:11:00 +04:00
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
fd = open_proc(pid, "exe");
|
2012-02-17 01:39:35 +04:00
|
|
|
if (fd < 0)
|
2012-02-07 19:32:11 +04:00
|
|
|
return -1;
|
2012-04-09 15:52:00 +04:00
|
|
|
|
2014-04-21 18:23:27 +04:00
|
|
|
if (fill_fd_params_special(fd, ¶ms))
|
2012-04-09 15:52:00 +04:00
|
|
|
return -1;
|
|
|
|
|
2014-04-21 18:23:29 +04:00
|
|
|
if (fd_id_generate_special(¶ms, &mm->exe_file_id))
|
2014-02-04 19:25:33 +04:00
|
|
|
ret = dump_one_reg_file(fd, mm->exe_file_id, ¶ms);
|
2012-04-09 15:52:00 +04:00
|
|
|
|
2012-03-29 16:40:50 +04:00
|
|
|
close(fd);
|
2012-02-07 19:20:17 +04:00
|
|
|
return ret;
|
2012-02-07 19:32:11 +04:00
|
|
|
}
|
2011-12-07 17:11:00 +04:00
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
static int dump_task_fs(pid_t pid, struct parasite_dump_misc *misc, struct cr_imgset *imgset)
|
2012-04-09 13:41:05 +04:00
|
|
|
{
|
2014-04-21 18:23:27 +04:00
|
|
|
struct fd_parms p;
|
2012-07-17 07:28:38 +04:00
|
|
|
FsEntry fe = FS_ENTRY__INIT;
|
2012-04-09 13:41:05 +04:00
|
|
|
int fd, ret;
|
|
|
|
|
2013-01-10 12:48:31 +03:00
|
|
|
fe.has_umask = true;
|
|
|
|
fe.umask = misc->umask;
|
|
|
|
|
2012-04-09 13:41:05 +04:00
|
|
|
fd = open_proc(pid, "cwd");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-04-21 18:23:27 +04:00
|
|
|
if (fill_fd_params_special(fd, &p))
|
2012-04-09 13:41:05 +04:00
|
|
|
return -1;
|
|
|
|
|
2014-04-21 18:23:29 +04:00
|
|
|
if (fd_id_generate_special(&p, &fe.cwd_id)) {
|
2014-02-04 19:25:33 +04:00
|
|
|
ret = dump_one_reg_file(fd, fe.cwd_id, &p);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2012-04-09 13:52:42 +04:00
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
fd = open_proc(pid, "root");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-04-21 18:23:27 +04:00
|
|
|
if (fill_fd_params_special(fd, &p))
|
2012-04-09 13:52:42 +04:00
|
|
|
return -1;
|
2012-04-09 13:41:05 +04:00
|
|
|
|
2014-04-21 18:23:29 +04:00
|
|
|
if (fd_id_generate_special(&p, &fe.root_id)) {
|
2014-02-04 19:25:33 +04:00
|
|
|
ret = dump_one_reg_file(fd, fe.root_id, &p);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2012-04-09 13:52:42 +04:00
|
|
|
|
2012-04-09 13:41:05 +04:00
|
|
|
close(fd);
|
2012-04-09 13:52:42 +04:00
|
|
|
|
2012-04-24 15:20:24 +04:00
|
|
|
pr_info("Dumping task cwd id %#x root id %#x\n",
|
2012-04-09 13:52:42 +04:00
|
|
|
fe.cwd_id, fe.root_id);
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
return pb_write_one(img_from_set(imgset, CR_FD_FS), &fe, PB_FS);
|
2012-04-09 13:41:05 +04:00
|
|
|
}
|
|
|
|
|
2013-01-10 20:08:38 +04:00
|
|
|
static inline u_int64_t encode_rlim(unsigned long val)
|
|
|
|
{
|
|
|
|
return val == RLIM_INFINITY ? -1 : val;
|
|
|
|
}
|
|
|
|
|
2014-03-13 14:30:49 +04:00
|
|
|
static int dump_task_rlimits(int pid, TaskRlimitsEntry *rls)
|
2014-03-13 14:30:47 +04:00
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
for (res = 0; res <rls->n_rlimits ; res++) {
|
|
|
|
struct rlimit lim;
|
|
|
|
|
|
|
|
if (prlimit(pid, res, NULL, &lim)) {
|
|
|
|
pr_perror("Can't get rlimit %d", res);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-03-14 15:46:42 +04:00
|
|
|
rls->rlimits[res]->cur = encode_rlim(lim.rlim_cur);
|
|
|
|
rls->rlimits[res]->max = encode_rlim(lim.rlim_max);
|
2014-03-13 14:30:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-03 00:18:32 +04:00
|
|
|
static int dump_filemap(pid_t pid, struct vma_area *vma_area,
|
2014-09-29 12:47:37 +04:00
|
|
|
const struct cr_imgset *imgset)
|
2012-03-21 11:33:54 +04:00
|
|
|
{
|
2012-07-19 09:39:00 +04:00
|
|
|
struct fd_parms p = FD_PARMS_INIT;
|
2014-02-04 00:08:16 +04:00
|
|
|
VmaEntry *vma = vma_area->e;
|
2014-02-04 19:25:33 +04:00
|
|
|
int ret = 0;
|
|
|
|
u32 id;
|
2012-03-21 11:33:54 +04:00
|
|
|
|
2014-09-23 20:31:16 +04:00
|
|
|
BUG_ON(!vma_area->vmst);
|
|
|
|
p.stat = *vma_area->vmst;
|
2014-09-23 20:32:00 +04:00
|
|
|
p.mnt_id = vma_area->mnt_id;
|
2012-04-09 12:57:38 +04:00
|
|
|
|
2014-08-19 22:31:07 -07:00
|
|
|
/*
|
|
|
|
* AUFS support to compensate for the kernel bug
|
|
|
|
* exposing branch pathnames in map_files.
|
|
|
|
*
|
|
|
|
* If the link found in vma_get_mapfile() pointed
|
|
|
|
* inside a branch, we should use the pathname
|
|
|
|
* from root that was saved in vma_area->aufs_rpath.
|
|
|
|
*/
|
|
|
|
if (vma_area->aufs_rpath) {
|
|
|
|
struct fd_link aufs_link;
|
|
|
|
|
|
|
|
strcpy(aufs_link.name, vma_area->aufs_rpath);
|
|
|
|
aufs_link.len = strlen(aufs_link.name);
|
|
|
|
p.link = &aufs_link;
|
|
|
|
}
|
|
|
|
|
2014-04-04 12:04:00 +04:00
|
|
|
/* Flags will be set during restore in get_filemap_fd() */
|
2012-03-21 11:33:54 +04:00
|
|
|
|
2014-04-21 18:23:29 +04:00
|
|
|
if (fd_id_generate_special(&p, &id))
|
2014-02-04 19:25:33 +04:00
|
|
|
ret = dump_one_reg_file(vma_area->vm_file_fd, id, &p);
|
|
|
|
|
|
|
|
vma->shmid = id;
|
|
|
|
return ret;
|
2012-03-21 11:33:54 +04:00
|
|
|
}
|
|
|
|
|
2013-04-04 13:09:59 +04:00
|
|
|
static int check_sysvipc_map_dump(pid_t pid, VmaEntry *vma)
|
|
|
|
{
|
2014-04-21 18:23:22 +04:00
|
|
|
if (root_ns_mask & CLONE_NEWIPC)
|
2013-04-04 13:09:59 +04:00
|
|
|
return 0;
|
|
|
|
|
2013-04-04 16:28:52 +00:00
|
|
|
pr_err("Task %d with SysVIPC shmem map @%"PRIx64" doesn't live in IPC ns\n",
|
2013-04-04 13:09:59 +04:00
|
|
|
pid, vma->start);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-02-10 14:26:29 +04:00
|
|
|
static int get_task_auxv(pid_t pid, MmEntry *mm)
|
2014-02-04 00:08:00 +04:00
|
|
|
{
|
2014-02-10 16:17:37 +04:00
|
|
|
auxv_t mm_saved_auxv[AT_VECTOR_SIZE];
|
|
|
|
int fd, i, ret;
|
2014-02-04 00:08:00 +04:00
|
|
|
|
2014-02-04 18:58:00 +04:00
|
|
|
pr_info("Obtaining task auvx ...\n");
|
2014-02-04 00:08:00 +04:00
|
|
|
|
|
|
|
fd = open_proc(pid, "auxv");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-02-10 16:17:37 +04:00
|
|
|
ret = read(fd, mm_saved_auxv, sizeof(mm_saved_auxv));
|
2014-02-10 14:26:29 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
ret = -1;
|
|
|
|
pr_perror("Error reading %d's auxv", pid);
|
|
|
|
goto err;
|
2014-02-10 16:17:37 +04:00
|
|
|
} else {
|
|
|
|
mm->n_mm_saved_auxv = ret / sizeof(auxv_t);
|
|
|
|
for (i = 0; i < mm->n_mm_saved_auxv; i++)
|
|
|
|
mm->mm_saved_auxv[i] = (u64)mm_saved_auxv[i];
|
|
|
|
}
|
2014-02-04 00:08:00 +04:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
close_safe(&fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_task_mm(pid_t pid, const struct proc_pid_stat *stat,
|
|
|
|
const struct parasite_dump_misc *misc,
|
|
|
|
const struct vm_area_list *vma_area_list,
|
2014-09-29 12:47:37 +04:00
|
|
|
const struct cr_imgset *imgset)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2014-02-04 00:08:00 +04:00
|
|
|
MmEntry mme = MM_ENTRY__INIT;
|
2011-09-23 12:00:45 +04:00
|
|
|
struct vma_area *vma_area;
|
2014-02-04 00:09:00 +04:00
|
|
|
int ret = -1, i = 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
2014-02-04 00:08:00 +04:00
|
|
|
pr_info("Dumping mm (pid: %d)\n", pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2014-02-04 00:09:00 +04:00
|
|
|
mme.n_vmas = vma_area_list->nr;
|
|
|
|
mme.vmas = xmalloc(mme.n_vmas * sizeof(VmaEntry *));
|
|
|
|
if (!mme.vmas)
|
|
|
|
goto err;
|
2012-03-26 17:43:29 +04:00
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
list_for_each_entry(vma_area, &vma_area_list->h, list) {
|
2014-02-04 00:08:16 +04:00
|
|
|
VmaEntry *vma = vma_area->e;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info_vma(vma_area);
|
|
|
|
|
2013-04-04 13:09:59 +04:00
|
|
|
if (!vma_entry_is(vma, VMA_AREA_REGULAR))
|
2012-04-09 12:57:38 +04:00
|
|
|
ret = 0;
|
2013-04-04 13:09:59 +04:00
|
|
|
else if (vma_entry_is(vma, VMA_AREA_SYSVIPC))
|
|
|
|
ret = check_sysvipc_map_dump(pid, vma);
|
2012-04-09 12:57:38 +04:00
|
|
|
else if (vma_entry_is(vma, VMA_ANON_SHARED))
|
2012-03-21 11:33:54 +04:00
|
|
|
ret = add_shmem_area(pid, vma);
|
|
|
|
else if (vma_entry_is(vma, VMA_FILE_PRIVATE) ||
|
|
|
|
vma_entry_is(vma, VMA_FILE_SHARED))
|
2014-09-29 12:47:37 +04:00
|
|
|
ret = dump_filemap(pid, vma_area, imgset);
|
2012-11-02 16:00:18 +03:00
|
|
|
else if (vma_entry_is(vma, VMA_AREA_SOCKET))
|
|
|
|
ret = dump_socket_map(vma_area);
|
2012-03-21 14:22:00 +04:00
|
|
|
else
|
|
|
|
ret = 0;
|
2012-03-21 11:33:54 +04:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2014-02-04 00:09:00 +04:00
|
|
|
|
|
|
|
mme.vmas[i++] = vma;
|
2014-12-19 16:01:54 +03:00
|
|
|
|
|
|
|
if (vma_entry_is(vma, VMA_AREA_AIORING)) {
|
|
|
|
ret = dump_aio_ring(&mme, vma_area);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2014-02-04 00:08:00 +04:00
|
|
|
mme.mm_start_code = stat->start_code;
|
|
|
|
mme.mm_end_code = stat->end_code;
|
|
|
|
mme.mm_start_data = stat->start_data;
|
|
|
|
mme.mm_end_data = stat->end_data;
|
|
|
|
mme.mm_start_stack = stat->start_stack;
|
|
|
|
mme.mm_start_brk = stat->start_brk;
|
|
|
|
|
|
|
|
mme.mm_arg_start = stat->arg_start;
|
|
|
|
mme.mm_arg_end = stat->arg_end;
|
|
|
|
mme.mm_env_start = stat->env_start;
|
|
|
|
mme.mm_env_end = stat->env_end;
|
|
|
|
|
|
|
|
mme.mm_brk = misc->brk;
|
|
|
|
|
2014-05-14 01:02:37 +04:00
|
|
|
mme.dumpable = misc->dumpable;
|
|
|
|
mme.has_dumpable = true;
|
|
|
|
|
2014-02-04 00:08:00 +04:00
|
|
|
mme.n_mm_saved_auxv = AT_VECTOR_SIZE;
|
|
|
|
mme.mm_saved_auxv = xmalloc(pb_repeated_size(&mme, mm_saved_auxv));
|
|
|
|
if (!mme.mm_saved_auxv)
|
|
|
|
goto err;
|
|
|
|
|
2014-02-10 14:26:29 +04:00
|
|
|
if (get_task_auxv(pid, &mme))
|
2014-02-04 00:08:00 +04:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (dump_task_exe_link(pid, &mme))
|
|
|
|
goto err;
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
ret = pb_write_one(img_from_set(imgset, CR_FD_MM), &mme, PB_MM);
|
2014-02-04 00:08:00 +04:00
|
|
|
xfree(mme.mm_saved_auxv);
|
2014-12-19 16:01:54 +03:00
|
|
|
free_aios(&mme);
|
2011-09-23 12:00:45 +04:00
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-09-13 13:44:21 +04:00
|
|
|
static int dump_task_creds(struct parasite_ctl *ctl,
|
2014-10-31 12:14:22 +03:00
|
|
|
const struct cr_imgset *fds)
|
2012-01-27 21:43:32 +04:00
|
|
|
{
|
2012-07-19 12:35:25 +04:00
|
|
|
CredsEntry ce = CREDS_ENTRY__INIT;
|
2012-01-27 21:43:32 +04:00
|
|
|
|
2014-08-14 06:47:00 +04:00
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping creds for %d)\n", ctl->pid.real);
|
|
|
|
pr_info("----------------------------------------\n");
|
2012-01-27 21:43:32 +04:00
|
|
|
|
2012-10-11 15:59:43 +04:00
|
|
|
if (parasite_dump_creds(ctl, &ce) < 0)
|
|
|
|
return -1;
|
2012-01-27 21:43:32 +04:00
|
|
|
|
2015-05-06 16:18:42 -06:00
|
|
|
if (collect_lsm_profile(ctl->pid.real, &ce) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
return pb_write_one(img_from_set(fds, CR_FD_CREDS), &ce, PB_CREDS);
|
2012-01-27 21:43:32 +04:00
|
|
|
}
|
|
|
|
|
2012-08-10 20:28:59 +04:00
|
|
|
static int get_task_futex_robust_list(pid_t pid, ThreadCoreEntry *info)
|
|
|
|
{
|
|
|
|
struct robust_list_head *head = NULL;
|
|
|
|
size_t len = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = sys_get_robust_list(pid, &head, &len);
|
2014-06-25 19:35:00 +04:00
|
|
|
if (ret == -ENOSYS) {
|
|
|
|
/*
|
|
|
|
* If the kernel says get_robust_list is not implemented, then
|
|
|
|
* check whether set_robust_list is also not implemented, in
|
|
|
|
* that case we can assume it is empty, since set_robust_list
|
|
|
|
* is the only way to populate it. This case is possible when
|
|
|
|
* "futex_cmpxchg_enabled" is unset in the kernel.
|
|
|
|
*
|
|
|
|
* The following system call should always fail, even if it is
|
|
|
|
* implemented, in which case it will return -EINVAL because
|
|
|
|
* len should be greater than zero.
|
|
|
|
*/
|
|
|
|
if (sys_set_robust_list(NULL, 0) != -ENOSYS)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
head = NULL;
|
|
|
|
len = 0;
|
|
|
|
} else if (ret) {
|
|
|
|
goto err;
|
2012-08-10 20:28:59 +04:00
|
|
|
}
|
|
|
|
|
2013-01-18 11:08:38 +04:00
|
|
|
info->futex_rla = encode_pointer(head);
|
2012-08-10 20:28:59 +04:00
|
|
|
info->futex_rla_len = (u32)len;
|
|
|
|
|
|
|
|
return 0;
|
2014-06-25 19:35:00 +04:00
|
|
|
|
|
|
|
err:
|
|
|
|
pr_err("Failed obtaining futex robust list on %d\n", pid);
|
|
|
|
return -1;
|
2012-08-10 20:28:59 +04:00
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
static int get_task_personality(pid_t pid, u32 *personality)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2014-09-17 19:10:00 +04:00
|
|
|
int fd, ret = -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2013-04-12 13:00:05 -07:00
|
|
|
pr_info("Obtaining personality ... ");
|
2012-04-10 15:39:21 +04:00
|
|
|
|
2014-09-17 19:10:00 +04:00
|
|
|
fd = open_proc(pid, "personality");
|
|
|
|
if (fd < 0)
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
|
2014-11-01 00:22:00 +04:00
|
|
|
ret = read(fd, loc_buf, sizeof(loc_buf) - 1);
|
2014-09-17 19:10:00 +04:00
|
|
|
close(fd);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2014-09-17 19:10:00 +04:00
|
|
|
if (ret >= 0) {
|
|
|
|
loc_buf[ret] = '\0';
|
|
|
|
*personality = atoi(loc_buf);
|
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-09 18:02:00 +04:00
|
|
|
static DECLARE_KCMP_TREE(vm_tree, KCMP_VM);
|
|
|
|
static DECLARE_KCMP_TREE(fs_tree, KCMP_FS);
|
|
|
|
static DECLARE_KCMP_TREE(files_tree, KCMP_FILES);
|
|
|
|
static DECLARE_KCMP_TREE(sighand_tree, KCMP_SIGHAND);
|
|
|
|
|
2013-01-11 18:16:22 +04:00
|
|
|
static int dump_task_kobj_ids(struct pstree_item *item)
|
2012-04-09 18:02:00 +04:00
|
|
|
{
|
|
|
|
int new;
|
|
|
|
struct kid_elem elem;
|
2013-01-11 18:16:22 +04:00
|
|
|
int pid = item->pid.real;
|
|
|
|
TaskKobjIdsEntry *ids = item->ids;
|
2012-04-09 18:02:00 +04:00
|
|
|
|
|
|
|
elem.pid = pid;
|
|
|
|
elem.idx = 0; /* really 0 for all */
|
|
|
|
elem.genid = 0; /* FIXME optimize */
|
|
|
|
|
|
|
|
new = 0;
|
2013-01-11 18:16:21 +04:00
|
|
|
ids->vm_id = kid_generate_gen(&vm_tree, &elem, &new);
|
|
|
|
if (!ids->vm_id || !new) {
|
2012-04-09 18:02:00 +04:00
|
|
|
pr_err("Can't make VM id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new = 0;
|
2013-01-11 18:16:21 +04:00
|
|
|
ids->fs_id = kid_generate_gen(&fs_tree, &elem, &new);
|
|
|
|
if (!ids->fs_id || !new) {
|
2012-04-09 18:02:00 +04:00
|
|
|
pr_err("Can't make FS id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new = 0;
|
2013-01-11 18:16:21 +04:00
|
|
|
ids->files_id = kid_generate_gen(&files_tree, &elem, &new);
|
2013-01-12 00:44:26 +04:00
|
|
|
if (!ids->files_id || (!new && !shared_fdtable(item))) {
|
2012-04-09 18:02:00 +04:00
|
|
|
pr_err("Can't make FILES id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new = 0;
|
2013-01-11 18:16:21 +04:00
|
|
|
ids->sighand_id = kid_generate_gen(&sighand_tree, &elem, &new);
|
|
|
|
if (!ids->sighand_id || !new) {
|
2012-04-09 18:02:00 +04:00
|
|
|
pr_err("Can't make IO id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-18 13:18:35 +04:00
|
|
|
int get_task_ids(struct pstree_item *item)
|
2013-01-11 18:16:21 +04:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2013-01-11 18:16:22 +04:00
|
|
|
item->ids = xmalloc(sizeof(*item->ids));
|
|
|
|
if (!item->ids)
|
2013-01-18 13:18:35 +04:00
|
|
|
goto err;
|
|
|
|
|
2013-01-11 18:16:22 +04:00
|
|
|
task_kobj_ids_entry__init(item->ids);
|
2013-01-11 18:16:21 +04:00
|
|
|
|
2013-01-17 17:48:19 +04:00
|
|
|
if (item->state != TASK_DEAD) {
|
|
|
|
ret = dump_task_kobj_ids(item);
|
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
2013-01-11 18:16:21 +04:00
|
|
|
|
2013-01-17 17:48:19 +04:00
|
|
|
ret = dump_task_ns_ids(item);
|
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
|
|
|
}
|
2013-01-18 13:18:33 +04:00
|
|
|
|
2013-01-18 13:18:35 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_free:
|
|
|
|
xfree(item->ids);
|
|
|
|
item->ids = NULL;
|
|
|
|
err:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
static int dump_task_ids(struct pstree_item *item, const struct cr_imgset *cr_imgset)
|
2013-01-18 13:18:35 +04:00
|
|
|
{
|
2014-09-29 12:47:37 +04:00
|
|
|
return pb_write_one(img_from_set(cr_imgset, CR_FD_IDS), item->ids, PB_IDS);
|
2013-01-11 18:16:21 +04:00
|
|
|
}
|
|
|
|
|
2013-07-16 19:47:52 +04:00
|
|
|
int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread *ti)
|
2013-07-10 16:09:23 +04:00
|
|
|
{
|
|
|
|
int ret;
|
2013-07-16 19:47:52 +04:00
|
|
|
ThreadCoreEntry *tc = core->thread_core;
|
2013-07-10 16:09:23 +04:00
|
|
|
|
|
|
|
ret = get_task_futex_robust_list(pid, tc);
|
|
|
|
if (!ret)
|
|
|
|
ret = dump_sched_info(pid, tc);
|
2013-07-16 19:47:52 +04:00
|
|
|
if (!ret) {
|
|
|
|
core_put_tls(core, ti->tls);
|
|
|
|
CORE_THREAD_ARCH_INFO(core)->clear_tid_addr = encode_pointer(ti->tid_addr);
|
|
|
|
BUG_ON(!tc->sas);
|
|
|
|
copy_sas(tc->sas, &ti->sas);
|
2014-06-27 19:26:52 +04:00
|
|
|
if (ti->pdeath_sig) {
|
|
|
|
tc->has_pdeath_sig = true;
|
|
|
|
tc->pdeath_sig = ti->pdeath_sig;
|
|
|
|
}
|
2013-07-16 19:47:52 +04:00
|
|
|
}
|
2013-07-10 16:09:23 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-01 11:14:44 +04:00
|
|
|
static int dump_task_core_all(struct pstree_item *item,
|
2013-04-08 20:11:46 +04:00
|
|
|
const struct proc_pid_stat *stat,
|
|
|
|
const struct parasite_dump_misc *misc,
|
2014-09-29 12:47:37 +04:00
|
|
|
const struct cr_imgset *cr_imgset)
|
2011-10-20 17:19:26 +04:00
|
|
|
{
|
2014-09-29 12:48:53 +04:00
|
|
|
struct cr_img *img;
|
2013-10-01 11:14:44 +04:00
|
|
|
CoreEntry *core = item->core[0];
|
|
|
|
pid_t pid = item->pid.real;
|
2012-04-10 15:41:16 +04:00
|
|
|
int ret = -1;
|
2011-10-20 17:19:26 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping core (pid: %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-07-19 13:23:01 +04:00
|
|
|
ret = get_task_personality(pid, &core->tc->personality);
|
2014-09-17 19:10:00 +04:00
|
|
|
if (ret < 0)
|
2013-05-24 16:20:04 +04:00
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-07-19 13:23:01 +04:00
|
|
|
strncpy((char *)core->tc->comm, stat->comm, TASK_COMM_LEN);
|
|
|
|
core->tc->flags = stat->flags;
|
2013-10-01 11:21:34 +04:00
|
|
|
core->tc->task_state = item->state;
|
2012-07-19 13:23:01 +04:00
|
|
|
core->tc->exit_code = 0;
|
2012-01-22 20:24:04 +04:00
|
|
|
|
2013-07-16 19:47:52 +04:00
|
|
|
ret = dump_thread_core(pid, core, &misc->ti);
|
2012-10-17 00:23:25 +04:00
|
|
|
if (ret)
|
2013-05-24 16:20:04 +04:00
|
|
|
goto err;
|
2012-10-17 00:23:25 +04:00
|
|
|
|
2014-04-15 22:00:11 +04:00
|
|
|
ret = dump_task_rlimits(pid, core->tc->rlimits);
|
2014-03-13 14:30:47 +04:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2014-05-08 16:42:40 +04:00
|
|
|
core->tc->has_cg_set = true;
|
|
|
|
ret = dump_task_cgroup(item, &core->tc->cg_set);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = img_from_set(cr_imgset, CR_FD_CORE);
|
|
|
|
ret = pb_write_one(img, core, PB_CORE);
|
2012-12-20 16:08:12 +04:00
|
|
|
if (ret < 0)
|
2013-05-24 16:20:04 +04:00
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2013-05-24 16:20:04 +04:00
|
|
|
err:
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-07 19:16:35 +04:00
|
|
|
static int parse_children(pid_t pid, pid_t **_c, int *_n)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-09-07 19:16:35 +04:00
|
|
|
pid_t *ch = NULL;
|
2014-09-17 19:11:00 +04:00
|
|
|
int nr = 0;
|
2012-09-07 19:16:31 +04:00
|
|
|
DIR *dir;
|
|
|
|
struct dirent *de;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-09-07 19:16:31 +04:00
|
|
|
dir = opendir_proc(pid, "task");
|
|
|
|
if (dir == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((de = readdir(dir))) {
|
2014-09-17 19:11:00 +04:00
|
|
|
int fd, len;
|
|
|
|
char *pos;
|
|
|
|
|
2012-11-29 21:12:51 +03:00
|
|
|
if (dir_dots(de))
|
2012-09-07 19:16:31 +04:00
|
|
|
continue;
|
|
|
|
|
2014-09-17 19:11:00 +04:00
|
|
|
fd = open_proc(pid, "task/%s/children", de->d_name);
|
|
|
|
if (fd < 0)
|
|
|
|
goto err;
|
|
|
|
|
2014-11-01 00:22:00 +04:00
|
|
|
len = read(fd, loc_buf, sizeof(loc_buf) - 1);
|
2014-09-17 19:11:00 +04:00
|
|
|
close(fd);
|
|
|
|
if (len < 0)
|
2011-12-25 18:19:16 +04:00
|
|
|
goto err;
|
2011-12-01 17:04:49 +04:00
|
|
|
|
2014-09-17 19:11:00 +04:00
|
|
|
loc_buf[len] = '\0';
|
|
|
|
pos = loc_buf;
|
|
|
|
while (1) {
|
|
|
|
pid_t val, *tmp;
|
2011-12-01 17:04:49 +04:00
|
|
|
|
2014-09-17 19:11:00 +04:00
|
|
|
val = strtol(pos, &pos, 0);
|
|
|
|
if (!val) {
|
|
|
|
BUG_ON(*pos != '\0');
|
|
|
|
break;
|
|
|
|
}
|
2011-12-25 18:19:16 +04:00
|
|
|
|
2014-09-17 19:11:00 +04:00
|
|
|
tmp = xrealloc(ch, (nr + 1) * sizeof(pid_t));
|
2011-12-25 18:19:16 +04:00
|
|
|
if (!tmp)
|
|
|
|
goto err;
|
2014-09-17 19:11:00 +04:00
|
|
|
|
2011-12-25 18:19:16 +04:00
|
|
|
ch = tmp;
|
2014-09-17 19:11:00 +04:00
|
|
|
ch[nr] = val;
|
2011-12-25 18:19:16 +04:00
|
|
|
nr++;
|
2014-09-17 19:11:00 +04:00
|
|
|
pos++; /* space goes after each pid */
|
2011-12-04 11:48:10 +04:00
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
*_c = ch;
|
2014-09-17 19:11:00 +04:00
|
|
|
*_n = nr;
|
2011-12-25 18:19:16 +04:00
|
|
|
|
2012-09-07 19:16:31 +04:00
|
|
|
closedir(dir);
|
2011-12-02 18:26:15 +04:00
|
|
|
return 0;
|
|
|
|
err:
|
2012-09-07 19:16:31 +04:00
|
|
|
closedir(dir);
|
2011-12-25 18:19:16 +04:00
|
|
|
xfree(ch);
|
2011-12-02 18:26:15 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-10 17:04:41 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-12-20 14:45:16 +04:00
|
|
|
static int collect_task(struct pstree_item *item);
|
2014-11-10 17:05:43 +04:00
|
|
|
static int collect_children(struct pstree_item *item)
|
2012-03-01 19:07:15 +04:00
|
|
|
{
|
2012-09-07 19:16:35 +04:00
|
|
|
pid_t *ch;
|
2013-12-20 14:45:16 +04:00
|
|
|
int ret, i, nr_children, nr_inprogress;
|
2012-05-31 14:50:00 +04:00
|
|
|
|
2012-09-07 19:16:31 +04:00
|
|
|
ret = parse_children(item->pid.real, &ch, &nr_children);
|
2012-05-31 14:50:00 +04:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-12-20 14:45:16 +04:00
|
|
|
nr_inprogress = 0;
|
2012-05-31 14:50:00 +04:00
|
|
|
for (i = 0; i < nr_children; i++) {
|
2014-11-10 17:04:54 +04:00
|
|
|
struct pstree_item *c;
|
2013-12-20 14:45:16 +04:00
|
|
|
pid_t pid = ch[i];
|
|
|
|
|
|
|
|
/* Is it already frozen? */
|
2014-11-10 17:04:41 +04:00
|
|
|
if (child_collected(item, pid))
|
2013-12-20 14:45:16 +04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
nr_inprogress++;
|
|
|
|
|
|
|
|
pr_info("Seized task %d, state %d\n", pid, ret);
|
|
|
|
|
2012-05-31 14:50:00 +04:00
|
|
|
c = alloc_pstree_item();
|
|
|
|
if (c == NULL) {
|
|
|
|
ret = -1;
|
|
|
|
goto free;
|
|
|
|
}
|
2013-12-20 14:45:16 +04:00
|
|
|
|
2014-10-31 12:14:23 +03:00
|
|
|
ret = seize_task(pid, item->pid.real);
|
2013-12-20 14:45:16 +04:00
|
|
|
if (ret < 0) {
|
2013-12-20 16:41:14 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-12-20 14:45:16 +04:00
|
|
|
ret = 0;
|
|
|
|
xfree(c);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-11-10 17:04:54 +04:00
|
|
|
c->pid.real = pid;
|
2012-05-31 14:50:00 +04:00
|
|
|
c->parent = item;
|
2013-12-20 14:45:16 +04:00
|
|
|
c->state = ret;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_add_tail(&c->sibling, &item->children);
|
2013-12-20 14:45:16 +04:00
|
|
|
|
|
|
|
/* Here is a recursive call (Depth-first search) */
|
|
|
|
ret = collect_task(c);
|
|
|
|
if (ret < 0)
|
|
|
|
goto free;
|
2012-05-31 14:50:00 +04:00
|
|
|
}
|
|
|
|
free:
|
|
|
|
xfree(ch);
|
2013-12-20 14:45:16 +04:00
|
|
|
return ret < 0 ? ret : nr_inprogress;
|
2012-03-01 19:07:15 +04:00
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static void unseize_task_and_threads(const struct pstree_item *item, int st)
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2013-12-19 16:59:46 +04:00
|
|
|
if (item->state == TASK_DEAD)
|
|
|
|
return;
|
|
|
|
|
2013-12-19 22:48:55 +04:00
|
|
|
/*
|
|
|
|
* The st is the state we want to switch tasks into,
|
|
|
|
* the item->state is the state task was in when we seized one.
|
|
|
|
*/
|
|
|
|
|
2013-12-20 20:36:12 +04:00
|
|
|
unseize_task(item->pid.real, item->state, st);
|
2013-12-19 16:59:41 +04:00
|
|
|
|
|
|
|
for (i = 1; i < item->nr_threads; i++)
|
|
|
|
ptrace(PTRACE_DETACH, item->threads[i].real, NULL, NULL);
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
}
|
|
|
|
|
2012-05-31 14:50:00 +04:00
|
|
|
static void pstree_switch_state(struct pstree_item *root_item, int st)
|
|
|
|
{
|
|
|
|
struct pstree_item *item = root_item;
|
2011-12-02 18:26:15 +04:00
|
|
|
|
2012-04-11 15:17:26 +04:00
|
|
|
pr_info("Unfreezing tasks into %d\n", st);
|
2012-05-31 14:50:00 +04:00
|
|
|
for_each_pstree_item(item)
|
2012-04-11 15:17:26 +04:00
|
|
|
unseize_task_and_threads(item, st);
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
}
|
|
|
|
|
2012-04-11 22:07:47 +04:00
|
|
|
static pid_t item_ppid(const struct pstree_item *item)
|
|
|
|
{
|
|
|
|
item = item->parent;
|
2012-06-22 00:38:00 +04:00
|
|
|
return item ? item->pid.real : -1;
|
2012-04-11 22:07:47 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 17:04:41 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-11-10 17:05:43 +04:00
|
|
|
static int collect_threads(struct pstree_item *item)
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
{
|
2014-11-10 17:05:06 +04:00
|
|
|
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;
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
|
2013-12-20 14:45:15 +04:00
|
|
|
if ((item->state == TASK_DEAD) && (nr_threads > 1)) {
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
pr_err("Zombies with threads are not supported\n");
|
2011-12-02 18:27:51 +04:00
|
|
|
goto err;
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2013-12-20 14:45:15 +04:00
|
|
|
/* 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;
|
|
|
|
|
2014-11-10 17:04:41 +04:00
|
|
|
if (thread_collected(item, pid))
|
2013-12-20 14:45:15 +04:00
|
|
|
continue;
|
2014-11-10 17:04:41 +04:00
|
|
|
|
2013-12-20 14:45:15 +04:00
|
|
|
nr_inprogress++;
|
|
|
|
|
2012-06-19 15:53:00 +04:00
|
|
|
pr_info("\tSeizing %d's %d thread\n",
|
2012-06-22 00:38:00 +04:00
|
|
|
item->pid.real, pid);
|
2013-12-20 14:45:15 +04:00
|
|
|
|
2014-10-31 12:14:23 +03:00
|
|
|
ret = seize_task(pid, item_ppid(item));
|
2013-12-20 14:45:15 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
/*
|
2013-12-20 16:41:14 +04:00
|
|
|
* 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.
|
2013-12-20 14:45:15 +04:00
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
BUG_ON(item->nr_threads + 1 > nr_threads);
|
|
|
|
item->threads[item->nr_threads].real = pid;
|
|
|
|
item->nr_threads++;
|
2012-02-01 17:03:51 +04:00
|
|
|
|
2012-03-01 19:03:09 +04:00
|
|
|
if (ret == TASK_DEAD) {
|
|
|
|
pr_err("Zombie thread not supported\n");
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == TASK_STOPPED) {
|
2014-04-09 11:13:11 -04:00
|
|
|
nr_stopped++;
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-09 11:13:11 -04:00
|
|
|
if (nr_stopped && nr_stopped != nr_inprogress) {
|
|
|
|
pr_err("Individually stopped threads not supported\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-11-10 17:05:06 +04:00
|
|
|
xfree(threads);
|
2013-12-20 14:45:15 +04:00
|
|
|
return nr_inprogress;
|
2014-11-10 17:05:06 +04:00
|
|
|
|
2011-12-02 18:27:51 +04:00
|
|
|
err:
|
2014-11-10 17:05:06 +04:00
|
|
|
xfree(threads);
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
return -1;
|
2012-01-19 19:22:59 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 17:05:18 +04:00
|
|
|
static int collect_loop(struct pstree_item *item,
|
|
|
|
int (*collect)(struct pstree_item *))
|
2012-03-01 19:02:03 +04:00
|
|
|
{
|
2014-11-10 17:05:18 +04:00
|
|
|
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.
|
|
|
|
*/
|
2012-03-01 19:02:03 +04:00
|
|
|
|
2013-12-20 14:45:15 +04:00
|
|
|
while (nr_inprogress > 0 && attempts) {
|
|
|
|
attempts--;
|
2014-11-10 17:05:18 +04:00
|
|
|
nr_inprogress = collect(item);
|
2013-12-20 14:45:15 +04:00
|
|
|
}
|
|
|
|
|
2014-11-10 17:05:18 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-12-20 14:45:15 +04:00
|
|
|
|
2014-11-10 17:05:18 +04:00
|
|
|
return (nr_inprogress == 0) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
2012-05-31 14:50:00 +04:00
|
|
|
static int collect_task(struct pstree_item *item)
|
2012-01-19 19:22:59 +04:00
|
|
|
{
|
2014-11-10 17:05:18 +04:00
|
|
|
int ret;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2014-11-10 17:05:43 +04:00
|
|
|
ret = collect_loop(item, collect_threads);
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_close;
|
|
|
|
|
2013-12-20 14:45:16 +04:00
|
|
|
/* Depth-first search (DFS) is used for traversing a process tree. */
|
2014-11-10 17:05:43 +04:00
|
|
|
ret = collect_loop(item, collect_children);
|
2014-11-10 17:05:18 +04:00
|
|
|
if (ret < 0)
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
goto err_close;
|
|
|
|
|
2012-05-31 14:50:00 +04:00
|
|
|
if ((item->state == TASK_DEAD) && !list_empty(&item->children)) {
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
pr_err("Zombie with children?! O_o Run, run, run!\n");
|
|
|
|
goto err_close;
|
|
|
|
}
|
|
|
|
|
2014-08-15 16:02:16 +03:00
|
|
|
if (pstree_alloc_cores(item))
|
|
|
|
goto err_close;
|
|
|
|
|
2012-06-22 00:38:00 +04:00
|
|
|
pr_info("Collected %d in %d state\n", item->pid.real, item->state);
|
2012-05-31 14:50:00 +04:00
|
|
|
return 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-01-12 23:50:45 +04:00
|
|
|
err_close:
|
2012-02-17 01:39:36 +04:00
|
|
|
close_pid_proc();
|
2012-05-31 14:50:00 +04:00
|
|
|
return -1;
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
}
|
|
|
|
|
2014-10-01 16:15:00 +04:00
|
|
|
static int collect_pstree_ids_predump(void)
|
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
|
|
|
struct {
|
|
|
|
struct pstree_item i;
|
|
|
|
struct dmp_info d;
|
|
|
|
} crt = { };
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This thing is normally done inside
|
|
|
|
* write_img_inventory().
|
|
|
|
*/
|
|
|
|
|
|
|
|
crt.i.state = TASK_ALIVE;
|
|
|
|
crt.i.pid.real = getpid();
|
|
|
|
|
|
|
|
if (predump_task_ns_ids(&crt.i))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for_each_pstree_item(item) {
|
|
|
|
if (item->state == TASK_DEAD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (predump_task_ns_ids(item))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:41 +04:00
|
|
|
int collect_pstree_ids(void)
|
2013-01-17 17:48:19 +04:00
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
|
|
|
|
|
|
|
for_each_pstree_item(item)
|
|
|
|
if (get_task_ids(item))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-08 16:00:53 +04:00
|
|
|
static int collect_pstree(pid_t pid)
|
2012-03-01 19:04:31 +04:00
|
|
|
{
|
2013-12-20 14:45:16 +04:00
|
|
|
int ret;
|
2012-03-01 19:11:29 +04:00
|
|
|
|
2013-05-09 22:21:08 +04:00
|
|
|
timing_start(TIME_FREEZING);
|
|
|
|
|
2013-12-20 14:45:16 +04:00
|
|
|
root_item = alloc_pstree_item();
|
|
|
|
if (root_item == NULL)
|
|
|
|
return -1;
|
2012-03-01 19:11:29 +04:00
|
|
|
|
2013-12-20 14:45:16 +04:00
|
|
|
root_item->pid.real = pid;
|
2014-10-31 12:14:23 +03:00
|
|
|
ret = seize_task(pid, -1);
|
2013-12-20 14:45:16 +04:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
pr_info("Seized task %d, state %d\n", pid, ret);
|
|
|
|
root_item->state = ret;
|
2012-03-01 19:11:29 +04:00
|
|
|
|
2013-12-20 14:45:16 +04:00
|
|
|
ret = collect_task(root_item);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
2012-03-01 19:11:29 +04:00
|
|
|
|
2013-05-09 22:21:08 +04:00
|
|
|
timing_stop(TIME_FREEZING);
|
|
|
|
timing_start(TIME_FROZEN);
|
|
|
|
|
2013-05-14 11:44:36 +04:00
|
|
|
return 0;
|
2013-12-20 14:45:16 +04:00
|
|
|
err:
|
|
|
|
pstree_switch_state(root_item, TASK_ALIVE);
|
|
|
|
return -1;
|
2012-03-01 19:04:31 +04:00
|
|
|
}
|
|
|
|
|
2013-05-08 16:00:53 +04:00
|
|
|
static int collect_file_locks(void)
|
2013-01-17 16:09:32 +08:00
|
|
|
{
|
2014-09-02 20:20:22 +04:00
|
|
|
return parse_file_locks();
|
2013-01-17 16:09:32 +08:00
|
|
|
}
|
|
|
|
|
2013-05-24 16:20:04 +04:00
|
|
|
static int dump_task_thread(struct parasite_ctl *parasite_ctl,
|
2013-05-24 16:20:08 +04:00
|
|
|
const struct pstree_item *item, int id)
|
2011-10-20 17:19:26 +04:00
|
|
|
{
|
2013-05-24 16:20:08 +04:00
|
|
|
struct pid *tid = &item->threads[id];
|
|
|
|
CoreEntry *core = item->core[id];
|
2012-06-22 00:38:00 +04:00
|
|
|
pid_t pid = tid->real;
|
2014-09-29 12:48:53 +04:00
|
|
|
int ret = -1;
|
|
|
|
struct cr_img *img;
|
2011-10-20 17:19:26 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping core for thread (pid: %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2013-05-24 16:20:08 +04:00
|
|
|
ret = parasite_dump_thread_seized(parasite_ctl, id, tid, core);
|
2012-02-21 09:25:17 +03:00
|
|
|
if (ret) {
|
2012-11-12 17:42:59 +04:00
|
|
|
pr_err("Can't dump thread for pid %d\n", pid);
|
2013-05-24 16:20:04 +04:00
|
|
|
goto err;
|
2012-02-15 18:36:32 +04:00
|
|
|
}
|
2012-02-21 09:25:17 +03:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_CORE, O_DUMP, tid->virt);
|
|
|
|
if (!img)
|
2013-05-24 16:20:04 +04:00
|
|
|
goto err;
|
2012-03-26 17:36:44 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
ret = pb_write_one(img, core, PB_CORE);
|
2011-10-20 17:19:26 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(img);
|
2011-10-20 17:19:26 +04:00
|
|
|
err:
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int dump_one_zombie(const struct pstree_item *item,
|
2012-03-26 17:36:44 +04:00
|
|
|
const struct proc_pid_stat *pps)
|
2012-01-22 20:28:30 +04:00
|
|
|
{
|
2012-07-19 13:23:01 +04:00
|
|
|
CoreEntry *core;
|
2014-09-29 12:48:53 +04:00
|
|
|
int ret = -1;
|
|
|
|
struct cr_img *img;
|
2012-01-22 20:28:30 +04:00
|
|
|
|
2013-01-11 18:16:21 +04:00
|
|
|
core = core_entry_alloc(0, 1);
|
2013-05-24 16:20:04 +04:00
|
|
|
if (!core)
|
|
|
|
return -1;
|
2012-01-22 20:28:30 +04:00
|
|
|
|
2013-07-12 18:12:12 +04:00
|
|
|
strncpy((char *)core->tc->comm, pps->comm, TASK_COMM_LEN);
|
2012-07-19 13:23:01 +04:00
|
|
|
core->tc->task_state = TASK_DEAD;
|
|
|
|
core->tc->exit_code = pps->exit_code;
|
2012-01-22 20:28:30 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_CORE, O_DUMP, item->pid.virt);
|
|
|
|
if (!img)
|
2013-05-24 16:20:04 +04:00
|
|
|
goto err;
|
2012-03-26 17:36:44 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
ret = pb_write_one(img, core, PB_CORE);
|
|
|
|
close_image(img);
|
2012-03-26 17:36:44 +04:00
|
|
|
err:
|
2013-05-24 16:20:04 +04:00
|
|
|
core_entry_free(core);
|
2012-03-26 17:36:44 +04:00
|
|
|
return ret;
|
2012-01-22 20:28:30 +04:00
|
|
|
}
|
|
|
|
|
2014-08-19 15:16:42 +04:00
|
|
|
#define SI_BATCH 32
|
|
|
|
|
2014-08-15 16:02:17 +03:00
|
|
|
static int dump_signal_queue(pid_t tid, SignalQueueEntry **sqe, bool group)
|
2013-03-25 23:39:48 +04:00
|
|
|
{
|
|
|
|
struct ptrace_peeksiginfo_args arg;
|
2014-08-19 15:17:55 +04:00
|
|
|
int ret;
|
2014-08-15 16:02:17 +03:00
|
|
|
SignalQueueEntry *queue = NULL;
|
2013-03-25 23:39:48 +04:00
|
|
|
|
2013-05-24 16:20:11 +04:00
|
|
|
pr_debug("Dump %s signals of %d\n", group ? "shared" : "private", tid);
|
|
|
|
|
2014-08-19 15:16:42 +04:00
|
|
|
arg.nr = SI_BATCH;
|
2013-03-25 23:39:48 +04:00
|
|
|
arg.flags = 0;
|
|
|
|
if (group)
|
|
|
|
arg.flags |= PTRACE_PEEKSIGINFO_SHARED;
|
2014-08-19 15:08:56 +04:00
|
|
|
arg.off = 0;
|
2013-03-25 23:39:48 +04:00
|
|
|
|
2014-08-15 16:02:17 +03:00
|
|
|
queue = xmalloc(sizeof(*queue));
|
|
|
|
if (!queue)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
signal_queue_entry__init(queue);
|
|
|
|
|
2014-08-19 15:19:11 +04:00
|
|
|
while (1) {
|
2014-08-19 15:17:55 +04:00
|
|
|
int nr, si_pos;
|
2014-08-19 15:16:42 +04:00
|
|
|
siginfo_t *si;
|
|
|
|
|
|
|
|
si = xmalloc(SI_BATCH * sizeof(*si));
|
|
|
|
if (!si) {
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
2014-08-19 15:09:56 +04:00
|
|
|
|
2014-08-19 15:16:42 +04:00
|
|
|
nr = ret = ptrace(PTRACE_PEEKSIGINFO, tid, &arg, si);
|
2014-08-19 15:09:56 +04:00
|
|
|
if (ret == 0)
|
2014-08-19 15:19:11 +04:00
|
|
|
break; /* Finished */
|
2014-08-19 15:09:56 +04:00
|
|
|
|
2013-03-25 23:39:48 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
if (errno == EIO) {
|
|
|
|
pr_warn("ptrace doesn't support PTRACE_PEEKSIGINFO\n");
|
|
|
|
ret = 0;
|
|
|
|
} else
|
|
|
|
pr_perror("ptrace");
|
2014-08-19 15:19:11 +04:00
|
|
|
|
2013-03-25 23:39:48 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-08-15 16:02:17 +03:00
|
|
|
queue->n_signals += nr;
|
|
|
|
queue->signals = xrealloc(queue->signals, sizeof(*queue->signals) * queue->n_signals);
|
|
|
|
if (!queue->signals) {
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
2013-03-25 23:39:48 +04:00
|
|
|
|
2014-08-19 15:17:55 +04:00
|
|
|
for (si_pos = queue->n_signals - nr;
|
|
|
|
si_pos < queue->n_signals; si_pos++) {
|
2014-08-19 15:12:10 +04:00
|
|
|
SiginfoEntry *se;
|
|
|
|
|
|
|
|
se = xmalloc(sizeof(*se));
|
|
|
|
if (!se) {
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
siginfo_entry__init(se);
|
|
|
|
se->siginfo.len = sizeof(siginfo_t);
|
2014-08-19 15:16:42 +04:00
|
|
|
se->siginfo.data = (void *)si++; /* XXX we don't free cores, but when
|
|
|
|
* we will, this would cause problems
|
|
|
|
*/
|
2014-08-19 15:17:55 +04:00
|
|
|
queue->signals[si_pos] = se;
|
2013-03-25 23:39:48 +04:00
|
|
|
}
|
2014-08-19 15:08:56 +04:00
|
|
|
|
2014-08-19 15:12:10 +04:00
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
|
2014-08-19 15:08:56 +04:00
|
|
|
arg.off += nr;
|
2013-03-25 23:39:48 +04:00
|
|
|
}
|
|
|
|
|
2014-08-15 16:02:17 +03:00
|
|
|
*sqe = queue;
|
2013-03-25 23:39:48 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-08-15 16:02:17 +03:00
|
|
|
static int dump_task_signals(pid_t pid, struct pstree_item *item)
|
2013-07-16 20:40:06 +04:00
|
|
|
{
|
|
|
|
int i, ret;
|
|
|
|
|
2014-08-15 16:02:17 +03:00
|
|
|
/* Dump private signals for each thread */
|
2013-07-16 20:40:06 +04:00
|
|
|
for (i = 0; i < item->nr_threads; i++) {
|
2014-08-15 16:02:17 +03:00
|
|
|
ret = dump_signal_queue(item->threads[i].real, &item->core[i]->thread_core->signals_p, false);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump private signals for thread %d\n", item->threads[i].real);
|
2013-07-16 20:40:06 +04:00
|
|
|
return -1;
|
2014-08-15 16:02:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dump shared signals */
|
|
|
|
ret = dump_signal_queue(pid, &item->core[0]->tc->signals_s, true);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump shared signals (pid: %d)\n", pid);
|
|
|
|
return -1;
|
2013-07-16 20:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-22 20:12:44 +04:00
|
|
|
static struct proc_pid_stat pps_buf;
|
|
|
|
|
2012-02-21 09:25:17 +03:00
|
|
|
static int dump_task_threads(struct parasite_ctl *parasite_ctl,
|
2012-03-06 14:20:00 +04:00
|
|
|
const struct pstree_item *item)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-01-22 20:13:59 +04:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < item->nr_threads; i++) {
|
|
|
|
/* Leader is already dumped */
|
2013-05-24 16:20:11 +04:00
|
|
|
if (item->pid.real == item->threads[i].real) {
|
2012-06-22 00:38:00 +04:00
|
|
|
item->threads[i].virt = item->pid.virt;
|
2013-05-24 16:20:11 +04:00
|
|
|
continue;
|
2012-06-19 15:53:00 +04:00
|
|
|
}
|
2013-05-24 16:20:11 +04:00
|
|
|
if (dump_task_thread(parasite_ctl, item, i))
|
2012-03-26 17:36:44 +04:00
|
|
|
return -1;
|
2012-01-22 20:13:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-10 15:28:29 +04:00
|
|
|
/*
|
|
|
|
* What this routine does is just reads pid-s of dead
|
|
|
|
* tasks in item's children list from item's ns proc.
|
|
|
|
*
|
|
|
|
* It does *not* find wihch real pid corresponds to
|
|
|
|
* which virtual one, but it's not required -- all we
|
|
|
|
* need to dump for zombie can be found in the same
|
|
|
|
* ns proc.
|
|
|
|
*/
|
|
|
|
|
2012-09-07 19:17:18 +04:00
|
|
|
static int fill_zombies_pids(struct pstree_item *item)
|
|
|
|
{
|
|
|
|
struct pstree_item *child;
|
|
|
|
int i, nr;
|
|
|
|
pid_t *ch;
|
|
|
|
|
2014-11-10 17:42:22 +04:00
|
|
|
/*
|
|
|
|
* Pids read here are virtual -- caller has set up
|
|
|
|
* the proc of target pid namespace.
|
|
|
|
*/
|
2012-09-07 19:17:18 +04:00
|
|
|
if (parse_children(item->pid.virt, &ch, &nr) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-11-10 17:42:22 +04:00
|
|
|
/*
|
|
|
|
* Step 1 -- filter our ch's pid of alive tasks
|
|
|
|
*/
|
2012-10-08 18:59:26 +04:00
|
|
|
list_for_each_entry(child, &item->children, sibling) {
|
2012-09-07 19:17:18 +04:00
|
|
|
if (child->pid.virt < 0)
|
|
|
|
continue;
|
|
|
|
for (i = 0; i < nr; i++) {
|
|
|
|
if (ch[i] == child->pid.virt) {
|
|
|
|
ch[i] = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-10 17:42:22 +04:00
|
|
|
/*
|
|
|
|
* Step 2 -- assign remaining pids from ch on
|
|
|
|
* children's items in arbitrary order. The caller
|
|
|
|
* will then re-read everything needed to dump
|
|
|
|
* zombies using newly obtained virtual pids.
|
|
|
|
*/
|
2012-09-07 19:17:18 +04:00
|
|
|
i = 0;
|
2012-10-08 18:59:26 +04:00
|
|
|
list_for_each_entry(child, &item->children, sibling) {
|
2012-09-07 19:17:18 +04:00
|
|
|
if (child->pid.virt > 0)
|
|
|
|
continue;
|
|
|
|
for (; i < nr; i++) {
|
|
|
|
if (ch[i] < 0)
|
|
|
|
continue;
|
|
|
|
child->pid.virt = ch[i];
|
|
|
|
ch[i] = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
BUG_ON(i == nr);
|
|
|
|
}
|
|
|
|
|
2013-04-05 01:44:31 +04:00
|
|
|
xfree(ch);
|
|
|
|
|
2012-09-07 19:17:18 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-18 23:34:30 +04:00
|
|
|
static int dump_zombies(void)
|
2012-09-07 19:17:18 +04:00
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
2013-08-11 19:51:02 +04:00
|
|
|
int ret = -1;
|
2014-04-21 18:23:22 +04:00
|
|
|
int pidns = root_ns_mask & CLONE_NEWPID;
|
2012-09-07 19:17:18 +04:00
|
|
|
|
2013-10-10 15:07:01 +04:00
|
|
|
if (pidns && set_proc_fd(get_service_fd(CR_PROC_FD_OFF)))
|
2013-08-11 19:51:02 +04:00
|
|
|
return -1;
|
2012-09-07 19:17:18 +04:00
|
|
|
|
2013-10-01 11:17:28 +04:00
|
|
|
/*
|
|
|
|
* We dump zombies separately becase for pid-ns case
|
|
|
|
* we'd have to resolve their pids w/o parasite via
|
|
|
|
* target ns' proc.
|
|
|
|
*/
|
|
|
|
|
2012-09-07 19:17:18 +04:00
|
|
|
for_each_pstree_item(item) {
|
|
|
|
if (item->state != TASK_DEAD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (item->pid.virt < 0) {
|
|
|
|
if (!pidns)
|
|
|
|
item->pid.virt = item->pid.real;
|
|
|
|
else if (root_item == item) {
|
|
|
|
pr_err("A root task is dead\n");
|
|
|
|
goto err;
|
|
|
|
} else if (fill_zombies_pids(item->parent))
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2013-04-12 13:00:05 -07:00
|
|
|
pr_info("Obtaining zombie stat ... ");
|
2012-09-07 19:17:18 +04:00
|
|
|
if (parse_pid_stat(item->pid.virt, &pps_buf) < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
item->sid = pps_buf.sid;
|
|
|
|
item->pgid = pps_buf.pgid;
|
|
|
|
|
|
|
|
BUG_ON(!list_empty(&item->children));
|
|
|
|
if (dump_one_zombie(item, &pps_buf) < 0)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
if (pidns)
|
|
|
|
close_proc();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
static int pre_dump_one_task(struct pstree_item *item, struct list_head *ctls)
|
|
|
|
{
|
|
|
|
pid_t pid = item->pid.real;
|
|
|
|
struct vm_area_list vmas;
|
|
|
|
struct parasite_ctl *parasite_ctl;
|
|
|
|
int ret = -1;
|
|
|
|
struct parasite_dump_misc misc;
|
|
|
|
|
2013-10-02 13:27:32 +04:00
|
|
|
INIT_LIST_HEAD(&vmas.h);
|
|
|
|
vmas.nr = 0;
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
pr_info("========================================\n");
|
|
|
|
pr_info("Pre-dumping task (pid: %d)\n", pid);
|
|
|
|
pr_info("========================================\n");
|
|
|
|
|
|
|
|
if (item->state == TASK_STOPPED) {
|
|
|
|
pr_warn("Stopped tasks are not supported\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->state == TASK_DEAD)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = collect_mappings(pid, &vmas);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Collect mappings (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -1;
|
2014-11-07 17:53:00 +04:00
|
|
|
parasite_ctl = parasite_infect_seized(pid, item, &vmas);
|
2013-05-14 12:00:40 +04:00
|
|
|
if (!parasite_ctl) {
|
|
|
|
pr_err("Can't infect (pid: %d) with parasite\n", pid);
|
|
|
|
goto err_free;
|
|
|
|
}
|
|
|
|
|
2014-01-14 12:03:00 +04:00
|
|
|
ret = parasite_fixup_vdso(parasite_ctl, pid, &vmas);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't fixup vdso VMAs (pid: %d)\n", pid);
|
|
|
|
goto err_cure;
|
|
|
|
}
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
ret = parasite_dump_misc_seized(parasite_ctl, &misc);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump misc (pid: %d)\n", pid);
|
|
|
|
goto err_cure;
|
|
|
|
}
|
|
|
|
|
2014-01-30 13:52:55 +04:00
|
|
|
ret = predump_task_files(pid);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Pre-dumping files failed (pid: %d)\n", pid);
|
|
|
|
goto err_cure;
|
|
|
|
}
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
parasite_ctl->pid.virt = item->pid.virt = misc.pid;
|
|
|
|
|
|
|
|
ret = parasite_dump_pages_seized(parasite_ctl, &vmas, ¶site_ctl->mem_pp);
|
|
|
|
if (ret)
|
|
|
|
goto err_cure;
|
|
|
|
|
2013-05-24 16:20:08 +04:00
|
|
|
if (parasite_cure_remote(parasite_ctl))
|
2013-05-14 12:00:40 +04:00
|
|
|
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
|
|
|
list_add_tail(¶site_ctl->pre_list, ctls);
|
|
|
|
err_free:
|
|
|
|
free_mappings(&vmas);
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
err_cure:
|
2013-05-24 16:20:08 +04:00
|
|
|
if (parasite_cure_seized(parasite_ctl))
|
2013-05-14 12:00:40 +04:00
|
|
|
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
|
|
|
goto err_free;
|
|
|
|
}
|
|
|
|
|
2012-08-02 16:25:52 +04:00
|
|
|
static int dump_one_task(struct pstree_item *item)
|
2012-01-22 20:13:59 +04:00
|
|
|
{
|
2012-06-22 00:38:00 +04:00
|
|
|
pid_t pid = item->pid.real;
|
2013-03-01 20:11:51 +04:00
|
|
|
struct vm_area_list vmas;
|
2011-10-21 12:14:45 +04:00
|
|
|
struct parasite_ctl *parasite_ctl;
|
2014-10-14 20:24:00 +04:00
|
|
|
int ret, exit_code = -1;
|
2012-01-27 21:35:59 +04:00
|
|
|
struct parasite_dump_misc misc;
|
2014-09-29 12:47:37 +04:00
|
|
|
struct cr_imgset *cr_imgset = NULL;
|
2014-04-07 23:32:00 +04:00
|
|
|
struct parasite_drain_fd *dfds = NULL;
|
2013-06-27 23:32:22 +04:00
|
|
|
struct proc_posix_timers_stat proc_args;
|
2012-03-28 17:36:00 +04:00
|
|
|
|
2013-10-02 13:27:32 +04:00
|
|
|
INIT_LIST_HEAD(&vmas.h);
|
|
|
|
vmas.nr = 0;
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info("========================================\n");
|
|
|
|
pr_info("Dumping task (pid: %d)\n", pid);
|
|
|
|
pr_info("========================================\n");
|
|
|
|
|
2012-09-07 19:17:18 +04:00
|
|
|
if (item->state == TASK_DEAD)
|
2013-10-01 11:17:28 +04:00
|
|
|
/*
|
|
|
|
* zombies are dumped separately in dump_zombies()
|
|
|
|
*/
|
2012-09-07 19:17:18 +04:00
|
|
|
return 0;
|
|
|
|
|
2013-04-12 13:00:05 -07:00
|
|
|
pr_info("Obtaining task stat ... ");
|
2012-02-17 01:39:36 +04:00
|
|
|
ret = parse_pid_stat(pid, &pps_buf);
|
2012-01-22 20:12:44 +04:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
ret = collect_mappings(pid, &vmas);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Collect mappings (pid: %d) failed with %d\n", pid, ret);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-08-27 13:10:00 +04:00
|
|
|
if (!shared_fdtable(item)) {
|
|
|
|
dfds = xmalloc(sizeof(*dfds));
|
|
|
|
if (!dfds)
|
|
|
|
goto err;
|
2014-04-07 23:32:00 +04:00
|
|
|
|
2014-08-27 13:10:00 +04:00
|
|
|
ret = collect_fds(pid, dfds);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Collect fds (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err;
|
|
|
|
}
|
2014-11-07 17:53:00 +04:00
|
|
|
|
|
|
|
parasite_ensure_args_size(drain_fds_size(dfds));
|
2012-02-29 16:06:48 +03:00
|
|
|
}
|
|
|
|
|
2013-06-27 23:32:22 +04:00
|
|
|
ret = parse_posix_timers(pid, &proc_args);
|
2014-01-14 09:33:19 +04:00
|
|
|
if (ret < 0) {
|
2013-06-27 23:32:22 +04:00
|
|
|
pr_err("Can't read posix timers file (pid: %d)\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-11-07 17:53:00 +04:00
|
|
|
parasite_ensure_args_size(posix_timers_dump_size(proc_args.timer_n));
|
|
|
|
|
2014-08-15 16:02:17 +03:00
|
|
|
ret = dump_task_signals(pid, item);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump %d signals failed %d\n", pid, ret);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-11-07 17:53:00 +04:00
|
|
|
parasite_ctl = parasite_infect_seized(pid, item, &vmas);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (!parasite_ctl) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Can't infect (pid: %d) with parasite\n", pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:22 +04:00
|
|
|
if (root_ns_mask & CLONE_NEWPID && root_item == item) {
|
2013-10-10 15:07:01 +04:00
|
|
|
int pfd;
|
|
|
|
|
|
|
|
pfd = parasite_get_proc_fd_seized(parasite_ctl);
|
|
|
|
if (pfd < 0) {
|
2012-09-07 19:16:36 +04:00
|
|
|
pr_err("Can't get proc fd (pid: %d)\n", pid);
|
2014-09-29 12:47:37 +04:00
|
|
|
goto err_cure_imgset;
|
2012-09-07 19:16:36 +04:00
|
|
|
}
|
2013-10-10 15:07:01 +04:00
|
|
|
|
|
|
|
if (install_service_fd(CR_PROC_FD_OFF, pfd) < 0)
|
2014-09-29 12:47:37 +04:00
|
|
|
goto err_cure_imgset;
|
2013-10-10 15:07:01 +04:00
|
|
|
|
|
|
|
close(pfd);
|
2012-09-07 19:16:36 +04:00
|
|
|
}
|
|
|
|
|
2013-05-24 01:42:18 +04:00
|
|
|
ret = parasite_fixup_vdso(parasite_ctl, pid, &vmas);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't fixup vdso VMAs (pid: %d)\n", pid);
|
2014-09-29 12:47:37 +04:00
|
|
|
goto err_cure_imgset;
|
2013-05-24 01:42:18 +04:00
|
|
|
}
|
|
|
|
|
2014-12-19 16:01:54 +03:00
|
|
|
ret = parasite_check_aios(parasite_ctl, &vmas); /* FIXME -- merge with above */
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Failed to check aio rings (pid: %d)\n", pid);
|
|
|
|
goto err_cure_imgset;
|
|
|
|
}
|
|
|
|
|
2012-06-19 15:53:00 +04:00
|
|
|
ret = parasite_dump_misc_seized(parasite_ctl, &misc);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump misc (pid: %d)\n", pid);
|
2014-09-29 12:47:37 +04:00
|
|
|
goto err_cure_imgset;
|
2012-06-19 15:53:00 +04:00
|
|
|
}
|
|
|
|
|
2013-04-08 17:59:29 +04:00
|
|
|
parasite_ctl->pid.virt = item->pid.virt = misc.pid;
|
2012-06-19 15:53:00 +04:00
|
|
|
item->sid = misc.sid;
|
|
|
|
item->pgid = misc.pgid;
|
|
|
|
|
2012-11-27 19:20:20 +04:00
|
|
|
pr_info("sid=%d pgid=%d pid=%d\n",
|
|
|
|
item->sid, item->pgid, item->pid.virt);
|
|
|
|
|
2013-04-01 20:11:13 +04:00
|
|
|
if (item->sid == 0) {
|
|
|
|
pr_err("A session leader of %d(%d) is outside of its pid namespace\n",
|
|
|
|
item->pid.real, item->pid.virt);
|
|
|
|
goto err_cure;
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
cr_imgset = cr_task_imgset_open(item->pid.virt, O_DUMP);
|
|
|
|
if (!cr_imgset)
|
2012-06-19 15:53:00 +04:00
|
|
|
goto err_cure;
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
ret = dump_task_ids(item, cr_imgset);
|
2013-01-11 18:16:25 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump ids (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err_cure;
|
|
|
|
}
|
|
|
|
|
2014-08-27 13:10:00 +04:00
|
|
|
if (dfds) {
|
2013-01-12 00:44:26 +04:00
|
|
|
ret = dump_task_files_seized(parasite_ctl, item, dfds);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump files (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err_cure;
|
|
|
|
}
|
2012-03-28 17:36:00 +04:00
|
|
|
}
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
ret = parasite_dump_pages_seized(parasite_ctl, &vmas, NULL);
|
2013-03-30 00:17:03 +04:00
|
|
|
if (ret)
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
ret = parasite_dump_sigacts_seized(parasite_ctl, cr_imgset);
|
2011-11-29 15:12:25 +03:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump sigactions (pid: %d) with parasite\n", pid);
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2011-11-29 15:12:25 +03:00
|
|
|
}
|
|
|
|
|
2014-04-15 21:58:49 +04:00
|
|
|
ret = parasite_dump_itimers_seized(parasite_ctl, item);
|
2012-01-24 16:45:19 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump itimers (pid: %d)\n", pid);
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2012-01-24 16:45:19 +04:00
|
|
|
}
|
|
|
|
|
2014-04-15 21:59:05 +04:00
|
|
|
ret = parasite_dump_posix_timers_seized(&proc_args, parasite_ctl, item);
|
2013-06-27 23:32:22 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump posix timers (pid: %d)\n", pid);
|
|
|
|
goto err_cure;
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
ret = dump_task_core_all(item, &pps_buf, &misc, cr_imgset);
|
2012-02-17 22:56:36 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump core (pid: %d) failed with %d\n", pid, ret);
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2012-02-17 22:56:36 +04:00
|
|
|
}
|
|
|
|
|
2014-10-31 12:14:22 +03:00
|
|
|
ret = dump_task_creds(parasite_ctl, cr_imgset);
|
2012-02-17 22:56:36 +04:00
|
|
|
if (ret) {
|
2014-10-14 16:31:08 +04:00
|
|
|
pr_err("Dump creds (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err;
|
2012-02-17 22:56:36 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 16:31:08 +04:00
|
|
|
ret = parasite_stop_daemon(parasite_ctl);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret) {
|
2014-10-14 16:31:08 +04:00
|
|
|
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = dump_task_threads(parasite_ctl, item);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump threads\n");
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2013-05-24 16:20:08 +04:00
|
|
|
ret = parasite_cure_seized(parasite_ctl);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret) {
|
2012-10-11 15:59:43 +04:00
|
|
|
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
ret = dump_task_mm(pid, &pps_buf, &misc, &vmas, cr_imgset);
|
2012-01-27 21:43:32 +04:00
|
|
|
if (ret) {
|
2012-10-11 15:59:43 +04:00
|
|
|
pr_err("Dump mappings (pid: %d) failed with %d\n", pid, ret);
|
2012-01-27 21:43:32 +04:00
|
|
|
goto err;
|
|
|
|
}
|
2012-04-09 13:41:05 +04:00
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
ret = dump_task_fs(pid, &misc, cr_imgset);
|
2012-04-09 13:41:05 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump fs (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
close_cr_imgset(&cr_imgset);
|
2014-10-14 20:24:00 +04:00
|
|
|
exit_code = 0;
|
2012-06-19 15:53:00 +04:00
|
|
|
err:
|
2012-02-17 01:39:36 +04:00
|
|
|
close_pid_proc();
|
2013-03-01 20:11:51 +04:00
|
|
|
free_mappings(&vmas);
|
2012-08-21 19:59:07 +04:00
|
|
|
xfree(dfds);
|
2014-10-14 20:24:00 +04:00
|
|
|
return exit_code;
|
2012-04-11 15:25:03 +04:00
|
|
|
|
|
|
|
err_cure:
|
2014-09-29 12:47:37 +04:00
|
|
|
close_cr_imgset(&cr_imgset);
|
|
|
|
err_cure_imgset:
|
2013-05-24 16:20:08 +04:00
|
|
|
parasite_cure_seized(parasite_ctl);
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
int cr_pre_dump_tasks(pid_t pid)
|
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
|
|
|
int ret = -1;
|
|
|
|
LIST_HEAD(ctls);
|
|
|
|
struct parasite_ctl *ctl, *n;
|
|
|
|
|
2014-01-28 22:35:42 +04:00
|
|
|
if (!opts.track_mem) {
|
|
|
|
pr_info("Enforcing memory tracking for pre-dump.\n");
|
|
|
|
opts.track_mem = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.final_state == TASK_DEAD) {
|
|
|
|
pr_info("Enforcing tasks run after pre-dump.\n");
|
|
|
|
opts.final_state = TASK_ALIVE;
|
|
|
|
}
|
|
|
|
|
2013-08-11 13:00:45 +04:00
|
|
|
if (init_stats(DUMP_STATS))
|
|
|
|
goto err;
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
if (kerndat_init())
|
|
|
|
goto err;
|
|
|
|
|
2014-01-30 14:00:42 +04:00
|
|
|
if (irmap_load_cache())
|
|
|
|
goto err;
|
|
|
|
|
2014-01-18 01:32:00 +04:00
|
|
|
if (cpu_init())
|
|
|
|
goto err;
|
|
|
|
|
2014-01-14 12:03:00 +04:00
|
|
|
if (vdso_init())
|
|
|
|
goto err;
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
if (connect_to_page_server())
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (collect_pstree(pid))
|
|
|
|
goto err;
|
|
|
|
|
2014-10-01 16:15:00 +04:00
|
|
|
if (collect_pstree_ids_predump())
|
2014-01-30 13:48:00 +04:00
|
|
|
goto err;
|
|
|
|
|
2014-10-01 16:15:00 +04:00
|
|
|
if (collect_namespaces(false) < 0)
|
2014-04-21 18:23:34 +04:00
|
|
|
goto err;
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
for_each_pstree_item(item)
|
|
|
|
if (pre_dump_one_task(item, &ctls))
|
|
|
|
goto err;
|
|
|
|
|
2014-09-30 23:57:00 +04:00
|
|
|
if (irmap_predump_prep())
|
|
|
|
goto err;
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
pstree_switch_state(root_item,
|
|
|
|
ret ? TASK_ALIVE : opts.final_state);
|
|
|
|
free_pstree(root_item);
|
|
|
|
|
|
|
|
timing_stop(TIME_FROZEN);
|
|
|
|
|
|
|
|
pr_info("Pre-dumping tasks' memory\n");
|
|
|
|
list_for_each_entry_safe(ctl, n, &ctls, pre_list) {
|
|
|
|
struct page_xfer xfer;
|
|
|
|
|
|
|
|
pr_info("\tPre-dumping %d\n", ctl->pid.virt);
|
|
|
|
timing_start(TIME_MEMWRITE);
|
|
|
|
ret = open_page_xfer(&xfer, CR_FD_PAGEMAP, ctl->pid.virt);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = page_xfer_dump_pages(&xfer, ctl->mem_pp, 0);
|
|
|
|
|
|
|
|
xfer.close(&xfer);
|
2014-01-24 15:47:45 +04:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
timing_stop(TIME_MEMWRITE);
|
|
|
|
|
|
|
|
destroy_page_pipe(ctl->mem_pp);
|
|
|
|
list_del(&ctl->pre_list);
|
|
|
|
parasite_cure_local(ctl);
|
|
|
|
}
|
|
|
|
|
2014-01-30 14:00:42 +04:00
|
|
|
if (irmap_predump_run())
|
|
|
|
ret = -1;
|
|
|
|
|
2013-10-10 22:56:33 +04:00
|
|
|
if (disconnect_from_page_server())
|
|
|
|
ret = -1;
|
|
|
|
|
2014-10-29 15:43:00 +04:00
|
|
|
if (bfd_flush_images())
|
|
|
|
ret = -1;
|
|
|
|
|
2013-05-14 12:00:40 +04:00
|
|
|
if (ret)
|
|
|
|
pr_err("Pre-dumping FAILED.\n");
|
|
|
|
else {
|
|
|
|
write_stats(DUMP_STATS);
|
|
|
|
pr_info("Pre-dumping finished successfully\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-05-08 16:00:53 +04:00
|
|
|
int cr_dump_tasks(pid_t pid)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
2013-07-22 19:35:28 +04:00
|
|
|
int post_dump_ret = 0;
|
2012-05-18 15:39:00 +04:00
|
|
|
int ret = -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2011-10-06 14:07:48 +04:00
|
|
|
pr_info("========================================\n");
|
2012-07-19 17:55:34 +04:00
|
|
|
pr_info("Dumping processes (pid: %d)\n", pid);
|
2011-10-06 14:07:48 +04:00
|
|
|
pr_info("========================================\n");
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2013-08-11 13:00:45 +04:00
|
|
|
if (init_stats(DUMP_STATS))
|
|
|
|
goto err;
|
|
|
|
|
2014-02-27 20:58:23 +04:00
|
|
|
if (cr_plugin_init(CR_PLUGIN_STAGE__DUMP))
|
2013-12-19 21:35:00 +04:00
|
|
|
goto err;
|
|
|
|
|
2013-04-15 13:02:09 +04:00
|
|
|
if (kerndat_init())
|
|
|
|
goto err;
|
|
|
|
|
2014-01-30 14:00:42 +04:00
|
|
|
if (irmap_load_cache())
|
|
|
|
goto err;
|
|
|
|
|
2012-12-21 17:35:36 +04:00
|
|
|
if (cpu_init())
|
|
|
|
goto err;
|
|
|
|
|
2013-05-24 01:42:13 +04:00
|
|
|
if (vdso_init())
|
|
|
|
goto err;
|
|
|
|
|
2014-07-10 17:00:28 +04:00
|
|
|
if (parse_cg_info())
|
|
|
|
goto err;
|
|
|
|
|
2012-07-19 17:37:25 +04:00
|
|
|
if (write_img_inventory())
|
|
|
|
goto err;
|
|
|
|
|
2014-12-17 16:48:00 +03:00
|
|
|
if (opts.cpu_cap & (CPU_CAP_CPU | CPU_CAP_INS)) {
|
cpuinfo: x86 -- Add dump and validation of cpuinfo image, v2
On Wed, Oct 01, 2014 at 04:57:40PM +0400, Pavel Emelyanov wrote:
> On 10/01/2014 01:07 AM, Cyrill Gorcunov wrote:
> > On Tue, Sep 30, 2014 at 09:18:53PM +0400, Cyrill Gorcunov wrote:
> >> If a user requested criu to dump cpuinfo image then we
> >> write one on dump and verify on restore. At the moment
> >> we require all cpu feature bits to match the destination
> >> cpu in a sake of simplicity, but in future we need deps
> >> engine which would filer out bits and test if cpu we're
> >> restoring on is more capable than one we were dumping at
> >> allowing to proceed restore procedure.
> >>
> >> Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
> >
> > Updated to new img format
Something like attached?
>From 59272a9514311e6736cddee08d5f88aa95d49189 Mon Sep 17 00:00:00 2001
From: Cyrill Gorcunov <gorcunov@openvz.org>
Date: Thu, 25 Sep 2014 16:04:10 +0400
Subject: [PATCH] cpuinfo: x86 -- Add dump and validation of cpuinfo image
If a user requested criu to dump cpuinfo image then we
write one on dump and verify on restore. At the moment
we require all cpu feature bits to match the destination
cpu in a sake of simplicity, but in future we need deps
engine which would filer out bits and test if cpu we're
restoring on is more capable than one we were dumping at
allowing to proceed restore procedure.
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-10-01 17:53:50 +04:00
|
|
|
if (cpu_dump_cpuinfo())
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2013-03-12 21:23:06 +04:00
|
|
|
if (connect_to_page_server())
|
|
|
|
goto err;
|
|
|
|
|
2013-09-28 06:28:33 +04:00
|
|
|
/*
|
|
|
|
* The collect_pstree will also stop (PTRACE_SEIZE) the tasks
|
|
|
|
* thus ensuring that they don't modify anything we collect
|
|
|
|
* afterwards.
|
|
|
|
*/
|
|
|
|
|
2013-05-08 16:00:53 +04:00
|
|
|
if (collect_pstree(pid))
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
|
2013-05-14 11:44:36 +04:00
|
|
|
if (collect_pstree_ids())
|
|
|
|
goto err;
|
|
|
|
|
2013-01-17 18:14:55 +04:00
|
|
|
if (network_lock())
|
|
|
|
goto err;
|
|
|
|
|
2013-05-08 16:00:53 +04:00
|
|
|
if (collect_file_locks())
|
2013-01-17 16:09:32 +08:00
|
|
|
goto err;
|
|
|
|
|
2014-10-01 16:15:00 +04:00
|
|
|
if (collect_namespaces(true) < 0)
|
2014-04-21 18:23:34 +04:00
|
|
|
goto err;
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
glob_imgset = cr_glob_imgset_open(O_DUMP);
|
|
|
|
if (!glob_imgset)
|
2012-03-26 17:38:59 +04:00
|
|
|
goto err;
|
|
|
|
|
2012-05-31 14:50:00 +04:00
|
|
|
for_each_pstree_item(item) {
|
2012-08-02 16:25:52 +04:00
|
|
|
if (dump_one_task(item))
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-08-03 23:31:04 +04:00
|
|
|
/* MNT namespaces are dumped after files to save remapped links */
|
|
|
|
if (dump_mnt_namespaces() < 0)
|
|
|
|
goto err;
|
|
|
|
|
locks: Don't dump locks in per-task manner (v3)
We have a problem with file locks (bug #2512) -- the /proc/locks
file shows the ID of lock creator, not the owner. Thus, if the
creator died, but holder is still alive, criu fails to dump the
lock held by latter task.
The proposal is to find who _might_ hold the lock by checking
for dev:inode pairs on lock vs file descriptors being dumped.
If the creator of the lock is still alive, then he will take
the priority.
One thing to note about flocks -- these belong to file entries,
not to tasks. Thus, when we meet one, we should check whether
the flock is really held by task's FD by trying to set yet
another one. In case of success -- lock really belongs to fd
we dump, in case it doesn't trylock should fail.
At the very end -- walk the list of locks and dump them all at
once, which is possible by merge of per-task file-locks images
into one global one.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2014-09-02 17:43:06 +04:00
|
|
|
if (dump_file_locks())
|
|
|
|
goto err;
|
|
|
|
|
2012-10-29 13:37:34 +04:00
|
|
|
if (dump_verify_tty_sids())
|
|
|
|
goto err;
|
|
|
|
|
2012-09-07 19:17:18 +04:00
|
|
|
if (dump_zombies())
|
|
|
|
goto err;
|
|
|
|
|
2012-06-19 15:53:00 +04:00
|
|
|
if (dump_pstree(root_item))
|
|
|
|
goto err;
|
|
|
|
|
2014-04-21 18:23:22 +04:00
|
|
|
if (root_ns_mask)
|
|
|
|
if (dump_namespaces(root_item, root_ns_mask) < 0)
|
2012-06-19 15:53:00 +04:00
|
|
|
goto err;
|
|
|
|
|
2014-05-08 16:42:40 +04:00
|
|
|
ret = dump_cgroups();
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2012-03-21 10:12:00 +04:00
|
|
|
ret = cr_dump_shmem();
|
2012-04-19 11:48:00 +04:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2012-04-26 14:20:22 +04:00
|
|
|
ret = fix_external_unix_sockets();
|
2012-04-19 11:48:00 +04:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2012-03-24 16:52:09 +04:00
|
|
|
|
2012-09-20 13:35:39 +04:00
|
|
|
ret = tty_verify_active_pairs();
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2012-03-24 16:52:09 +04:00
|
|
|
fd_id_show_tree();
|
2011-09-23 12:00:45 +04:00
|
|
|
err:
|
2013-08-09 15:39:37 +04:00
|
|
|
if (disconnect_from_page_server())
|
|
|
|
ret = -1;
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
close_cr_imgset(&glob_imgset);
|
2012-04-28 17:38:46 +04:00
|
|
|
|
2014-10-29 15:43:00 +04:00
|
|
|
if (bfd_flush_images())
|
|
|
|
ret = -1;
|
|
|
|
|
2014-02-27 20:58:23 +04:00
|
|
|
cr_plugin_fini(CR_PLUGIN_STAGE__DUMP, ret);
|
2013-12-19 21:35:00 +04:00
|
|
|
|
2013-07-22 19:35:28 +04:00
|
|
|
if (!ret) {
|
|
|
|
/*
|
|
|
|
* It might be a migration case, where we're asked
|
|
|
|
* to dump everything, then some script transfer
|
|
|
|
* image on a new node and we're supposed to kill
|
|
|
|
* dumpee because it continue running somewhere
|
|
|
|
* else.
|
|
|
|
*
|
|
|
|
* Thus ask user via script if we're to break
|
|
|
|
* checkpoint.
|
|
|
|
*/
|
2014-09-03 23:43:46 +04:00
|
|
|
post_dump_ret = run_scripts(ACT_POST_DUMP);
|
2013-07-22 19:35:28 +04:00
|
|
|
if (post_dump_ret) {
|
|
|
|
post_dump_ret = WEXITSTATUS(post_dump_ret);
|
|
|
|
pr_info("Post dump script passed with %d\n", post_dump_ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-28 17:38:46 +04:00
|
|
|
/*
|
2013-11-01 00:50:47 +04:00
|
|
|
* Dump is complete at this stage. To choose what
|
|
|
|
* to do next we need to consider the following
|
|
|
|
* scenarios
|
|
|
|
*
|
|
|
|
* - error happened during checkpoint: just clean up
|
|
|
|
* everything and continue execution of the dumpee;
|
|
|
|
*
|
|
|
|
* - dump successed but post-dump script returned
|
|
|
|
* some ret code: same as in previous scenario --
|
|
|
|
* just clean up everything and continue execution,
|
|
|
|
* we will return script ret code back to criu caller
|
|
|
|
* and it's up to a caller what to do with running instance
|
|
|
|
* of the dumpee -- either kill it, or continue running;
|
|
|
|
*
|
|
|
|
* - dump successed but -R option passed, pointing that
|
|
|
|
* we're asked to continue execution of the dumpee. It's
|
|
|
|
* assumed that a user will use post-dump script to keep
|
|
|
|
* consistency of the FS and other resources, we simply
|
|
|
|
* start rollback procedure and cleanup everyhting.
|
2012-04-28 17:38:46 +04:00
|
|
|
*/
|
2013-11-01 00:50:47 +04:00
|
|
|
if (ret || post_dump_ret || opts.final_state == TASK_ALIVE) {
|
2012-09-17 20:05:55 +04:00
|
|
|
network_unlock();
|
2013-11-01 00:50:48 +04:00
|
|
|
delete_link_remaps();
|
2013-11-01 00:50:47 +04:00
|
|
|
}
|
2012-05-31 14:50:00 +04:00
|
|
|
pstree_switch_state(root_item,
|
2013-07-22 19:35:28 +04:00
|
|
|
(ret || post_dump_ret) ?
|
|
|
|
TASK_ALIVE : opts.final_state);
|
2013-05-09 22:21:08 +04:00
|
|
|
timing_stop(TIME_FROZEN);
|
2012-05-31 14:50:00 +04:00
|
|
|
free_pstree(root_item);
|
2013-01-17 16:09:32 +08:00
|
|
|
free_file_locks();
|
2013-11-01 00:50:48 +04:00
|
|
|
free_link_remaps();
|
2014-08-19 22:31:07 -07:00
|
|
|
free_aufs_branches();
|
2014-11-07 13:56:34 +03:00
|
|
|
free_userns_maps();
|
2011-10-24 23:01:42 +04:00
|
|
|
|
2013-10-10 15:07:01 +04:00
|
|
|
close_service_fd(CR_PROC_FD_OFF);
|
2012-09-07 19:16:36 +04:00
|
|
|
|
2013-03-27 16:16:25 +04:00
|
|
|
if (ret) {
|
|
|
|
kill_inventory();
|
2013-03-27 16:06:26 +04:00
|
|
|
pr_err("Dumping FAILED.\n");
|
2013-05-09 22:16:56 +04:00
|
|
|
} else {
|
|
|
|
write_stats(DUMP_STATS);
|
2013-03-27 16:06:26 +04:00
|
|
|
pr_info("Dumping finished successfully\n");
|
2013-05-09 22:16:56 +04:00
|
|
|
}
|
2013-03-27 16:06:26 +04:00
|
|
|
|
2013-12-13 14:32:18 +04:00
|
|
|
return post_dump_ret ? : (ret != 0);
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|