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>
|
2012-01-27 21:35:59 +04:00
|
|
|
#include <parasite.h>
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
|
|
|
|
#include <sys/sendfile.h>
|
|
|
|
|
2011-11-28 16:33:54 +03:00
|
|
|
#include <linux/major.h>
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
#include "types.h"
|
|
|
|
#include "list.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"
|
|
|
|
#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"
|
2011-12-26 22:12:03 +04:00
|
|
|
#include "sockets.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-05-03 17:36:00 +04:00
|
|
|
#include "pipes.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-05-04 13:38:00 +04:00
|
|
|
#include "eventfd.h"
|
2012-05-04 13:38:00 +04:00
|
|
|
#include "eventpoll.h"
|
2012-05-04 13:38:00 +04:00
|
|
|
#include "inotify.h"
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
#ifndef CONFIG_X86_64
|
|
|
|
# error No x86-32 support yet
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static char big_buffer[PATH_MAX];
|
|
|
|
static char loc_buf[PAGE_SIZE];
|
|
|
|
|
2011-12-05 15:57:47 +04:00
|
|
|
void free_pstree(struct list_head *pstree_list)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
struct pstree_item *item, *p;
|
|
|
|
|
2011-10-21 11:46:19 +04:00
|
|
|
list_for_each_entry_safe(item, p, pstree_list, list) {
|
2011-09-23 12:00:45 +04:00
|
|
|
xfree(item->children);
|
2011-10-20 11:03:19 +04:00
|
|
|
xfree(item->threads);
|
2011-09-23 12:00:45 +04:00
|
|
|
xfree(item);
|
|
|
|
}
|
|
|
|
|
2011-10-21 11:46:19 +04:00
|
|
|
INIT_LIST_HEAD(pstree_list);
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2011-10-26 22:48:10 +04:00
|
|
|
void free_mappings(struct list_head *vma_area_list)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
struct vma_area *vma_area, *p;
|
|
|
|
|
2011-10-21 12:14:45 +04:00
|
|
|
list_for_each_entry_safe(vma_area, p, vma_area_list, list) {
|
2011-09-23 12:00:45 +04:00
|
|
|
if (vma_area->vm_file_fd > 0)
|
|
|
|
close(vma_area->vm_file_fd);
|
|
|
|
free(vma_area);
|
|
|
|
}
|
|
|
|
|
2011-10-21 12:14:45 +04:00
|
|
|
INIT_LIST_HEAD(vma_area_list);
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
static int collect_mappings(pid_t pid, struct list_head *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");
|
|
|
|
|
2012-05-10 21:07:00 +04:00
|
|
|
ret = parse_smaps(pid, vma_area_list, true);
|
2012-03-02 19:28:46 +04:00
|
|
|
if (ret < 0)
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
|
2011-10-21 12:14:45 +04:00
|
|
|
pr_info_vma_list(vma_area_list);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("----------------------------------------\n");
|
2012-03-02 19:28:46 +04:00
|
|
|
ret = 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-26 17:47:38 +04:00
|
|
|
struct cr_fdset *glob_fdset;
|
2012-03-25 20:24:53 +04:00
|
|
|
|
2012-03-28 17:36:00 +04:00
|
|
|
static int collect_fds(pid_t pid, int *fd, int *nr_fd)
|
|
|
|
{
|
|
|
|
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))) {
|
|
|
|
if (!strcmp(de->d_name, "."))
|
|
|
|
continue;
|
|
|
|
if (!strcmp(de->d_name, ".."))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (n > *nr_fd - 1)
|
|
|
|
return -ENOMEM;
|
|
|
|
fd[n++] = atoi(de->d_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
*nr_fd = n;
|
|
|
|
pr_info("Found %d file descriptors\n", n);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
|
|
|
closedir(fd_dir);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-13 17:54:36 +04:00
|
|
|
/*
|
|
|
|
* Ghost files are those not visible from the FS. Dumping them is
|
|
|
|
* nasty and the only way we have -- just carry its contents with
|
|
|
|
* us. Any brave soul to implement link unlinked file back?
|
|
|
|
*/
|
|
|
|
struct ghost_file {
|
|
|
|
u32 dev;
|
|
|
|
u32 ino;
|
|
|
|
u32 id;
|
|
|
|
struct list_head list;
|
|
|
|
};
|
|
|
|
|
|
|
|
static u32 ghost_file_ids = 1;
|
|
|
|
static LIST_HEAD(ghost_files);
|
|
|
|
/*
|
|
|
|
* This constant is selected without any calculations. Just do not
|
|
|
|
* want to pick up too big files with us in the image.
|
|
|
|
*/
|
|
|
|
#define MAX_GHOST_FILE_SIZE (1 * 1024 * 1024)
|
|
|
|
|
|
|
|
static int dump_ghost_file(int _fd, u32 id, const struct stat *st)
|
|
|
|
{
|
|
|
|
int img, fd;
|
|
|
|
struct ghost_file_entry gfe;
|
|
|
|
char lpath[32];
|
|
|
|
|
2012-04-24 15:20:24 +04:00
|
|
|
pr_info("Dumping ghost file contents (id %#x)\n", id);
|
2012-04-13 17:54:36 +04:00
|
|
|
|
|
|
|
img = open_image(CR_FD_GHOST_FILE, O_DUMP, id);
|
|
|
|
if (img < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reopen file locally since it may have no read
|
|
|
|
* permissions when drained
|
|
|
|
*/
|
|
|
|
snprintf(lpath, sizeof(lpath), "/proc/self/fd/%d", _fd);
|
|
|
|
fd = open(lpath, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't open ghost original file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfe.uid = st->st_uid;
|
|
|
|
gfe.gid = st->st_gid;
|
|
|
|
gfe.mode = st->st_mode;
|
|
|
|
|
|
|
|
if (write_img(img, &gfe))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (copy_file(fd, img, st->st_size))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
close(img);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_ghost_remap(char *path, const struct stat *st, int lfd, u32 id)
|
|
|
|
{
|
|
|
|
struct ghost_file *gf;
|
|
|
|
struct remap_file_path_entry rpe;
|
|
|
|
|
2012-04-24 15:20:24 +04:00
|
|
|
pr_info("Dumping ghost file for fd %d id %#x\n", lfd, id);
|
2012-04-13 17:54:36 +04:00
|
|
|
|
|
|
|
if (st->st_size > MAX_GHOST_FILE_SIZE) {
|
|
|
|
pr_err("Can't dump ghost file %s of %lu size\n",
|
|
|
|
path, st->st_size);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(gf, &ghost_files, list)
|
|
|
|
if ((gf->dev == st->st_dev) && (gf->ino == st->st_ino))
|
|
|
|
goto dump_entry;
|
|
|
|
|
|
|
|
gf = xmalloc(sizeof(*gf));
|
|
|
|
if (gf == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
gf->dev = st->st_dev;
|
|
|
|
gf->ino = st->st_ino;
|
|
|
|
gf->id = ghost_file_ids++;
|
|
|
|
list_add_tail(&gf->list, &ghost_files);
|
|
|
|
|
|
|
|
if (dump_ghost_file(lfd, gf->id, st))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dump_entry:
|
|
|
|
rpe.orig_id = id;
|
|
|
|
rpe.remap_id = gf->id | REMAP_GHOST;
|
|
|
|
|
|
|
|
return write_img(fdset_fd(glob_fdset, CR_FD_REMAP_FPATH), &rpe);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_path_remap(char *path, const struct stat *ost, int lfd, u32 id)
|
2012-04-07 00:32:03 +04:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct stat pst;
|
|
|
|
|
2012-04-13 17:54:36 +04:00
|
|
|
if (ost->st_nlink == 0)
|
|
|
|
/*
|
|
|
|
* Unpleasant, but easy case. File is completely invisible
|
|
|
|
* from the FS. Just dump its contents and that's it. But
|
|
|
|
* be careful whether anybody still has any of its hardlinks
|
|
|
|
* also open.
|
|
|
|
*/
|
|
|
|
return dump_ghost_remap(path, ost, lfd, id);
|
2012-04-07 00:32:03 +04:00
|
|
|
|
|
|
|
ret = stat(path, &pst);
|
|
|
|
if (ret < 0) {
|
2012-04-13 17:54:36 +04:00
|
|
|
/*
|
|
|
|
* FIXME linked file, but path is not accessible (unless any
|
|
|
|
* other error occurred). We can create a temporary link to it
|
|
|
|
* uning linkat with AT_EMPTY_PATH flag and remap it to this
|
|
|
|
* name.
|
|
|
|
*/
|
2012-04-07 00:32:03 +04:00
|
|
|
pr_perror("Can't stat path");
|
2012-04-13 17:54:36 +04:00
|
|
|
return -1;
|
2012-04-07 00:32:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((pst.st_ino != ost->st_ino) || (pst.st_dev != ost->st_dev)) {
|
2012-04-13 17:54:36 +04:00
|
|
|
/*
|
|
|
|
* FIXME linked file, but the name we see it by is reused
|
|
|
|
* by somebody else.
|
|
|
|
*/
|
2012-04-07 00:32:03 +04:00
|
|
|
pr_err("Unaccessible path opened %u:%u, need %u:%u\n",
|
|
|
|
(int)pst.st_dev, (int)pst.st_ino,
|
|
|
|
(int)ost->st_dev, (int)ost->st_ino);
|
2012-04-13 17:54:36 +04:00
|
|
|
return -1;
|
2012-04-07 00:32:03 +04:00
|
|
|
}
|
|
|
|
|
2012-04-13 17:54:36 +04:00
|
|
|
/*
|
|
|
|
* File is linked and visible by the name it is opened by
|
|
|
|
* this task. Go ahead and dump it.
|
|
|
|
*/
|
|
|
|
return 0;
|
2012-04-07 00:32:03 +04:00
|
|
|
}
|
|
|
|
|
2012-03-25 21:37:39 +04:00
|
|
|
static int dump_one_reg_file(int lfd, u32 id, const struct fd_parms *p)
|
|
|
|
{
|
|
|
|
char fd_str[128];
|
2012-03-26 17:47:38 +04:00
|
|
|
int len, rfd;
|
2012-03-25 21:37:39 +04:00
|
|
|
struct reg_file_entry rfe;
|
|
|
|
|
|
|
|
snprintf(fd_str, sizeof(fd_str), "/proc/self/fd/%d", lfd);
|
|
|
|
len = readlink(fd_str, big_buffer, sizeof(big_buffer) - 1);
|
|
|
|
if (len < 0) {
|
|
|
|
pr_perror("Can't readlink %s", fd_str);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
big_buffer[len] = '\0';
|
2012-04-10 17:49:39 +04:00
|
|
|
pr_info("Dumping path for %d fd via self %d [%s]\n",
|
|
|
|
p->fd, lfd, big_buffer);
|
2012-03-25 21:37:39 +04:00
|
|
|
|
2012-05-04 15:25:25 +04:00
|
|
|
if (check_path_remap(big_buffer, &p->stat, lfd, id))
|
2012-04-07 00:32:03 +04:00
|
|
|
return -1;
|
|
|
|
|
2012-03-25 21:37:39 +04:00
|
|
|
rfe.len = len;
|
|
|
|
rfe.flags = p->flags;
|
|
|
|
rfe.pos = p->pos;
|
|
|
|
rfe.id = id;
|
2012-04-11 15:53:00 +04:00
|
|
|
rfe.fown = p->fown;
|
2012-03-25 21:37:39 +04:00
|
|
|
|
2012-03-26 17:47:38 +04:00
|
|
|
rfd = fdset_fd(glob_fdset, CR_FD_REG_FILES);
|
|
|
|
|
|
|
|
if (write_img(rfd, &rfe))
|
2012-03-25 21:37:39 +04:00
|
|
|
return -1;
|
2012-03-26 17:47:38 +04:00
|
|
|
if (write_img_buf(rfd, big_buffer, len))
|
2012-03-25 21:37:39 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-04 15:34:55 +04:00
|
|
|
u32 make_gen_id(const struct fd_parms *p)
|
2012-05-04 15:25:25 +04:00
|
|
|
{
|
|
|
|
return MAKE_FD_GENID(p->stat.st_dev, p->stat.st_ino, p->pos);
|
|
|
|
}
|
|
|
|
|
2012-05-04 15:34:55 +04:00
|
|
|
int do_dump_gen_file(struct fd_parms *p, int lfd,
|
2012-05-04 15:25:25 +04:00
|
|
|
const struct fdtype_ops *ops, const struct cr_fdset *cr_fdset)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
struct fdinfo_entry e;
|
|
|
|
int ret = -1;
|
|
|
|
|
2012-05-04 15:25:25 +04:00
|
|
|
e.type = ops->type;
|
|
|
|
e.id = p->id = ops->make_gen_id(p);
|
2012-04-10 17:49:39 +04:00
|
|
|
e.fd = p->fd;
|
2012-04-10 18:36:59 +04:00
|
|
|
e.flags = p->fd_flags;
|
2012-02-28 18:27:28 +04:00
|
|
|
|
2012-03-24 22:02:10 +04:00
|
|
|
ret = fd_id_generate(p->pid, &e);
|
2012-03-25 21:37:39 +04:00
|
|
|
if (ret == 1) /* new ID generated */
|
2012-05-04 15:25:25 +04:00
|
|
|
ret = ops->dump(lfd, e.id, p);
|
2012-03-25 21:37:39 +04:00
|
|
|
|
2012-03-24 22:02:10 +04:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-04-13 19:44:00 +04:00
|
|
|
pr_info("fdinfo: type: 0x%2x flags: 0x%4x pos: 0x%8lx fd: %d\n",
|
2012-05-04 15:28:28 +04:00
|
|
|
ops->type, p->flags, p->pos, p->fd);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-26 17:43:29 +04:00
|
|
|
if (write_img(fdset_fd(cr_fdset, CR_FD_FDINFO), &e))
|
2012-01-22 20:20:40 +04:00
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-04 15:34:55 +04:00
|
|
|
static const struct fdtype_ops regfile_ops = {
|
|
|
|
.type = FDINFO_REG,
|
|
|
|
.make_gen_id = make_gen_id,
|
|
|
|
.dump = dump_one_reg_file,
|
|
|
|
};
|
|
|
|
|
2012-05-04 15:06:20 +04:00
|
|
|
static int dump_reg_file(struct fd_parms *p, int lfd,
|
2012-03-29 16:41:43 +04:00
|
|
|
const struct cr_fdset *cr_fdset)
|
|
|
|
{
|
2012-05-04 15:25:25 +04:00
|
|
|
return do_dump_gen_file(p, lfd, ®file_ops, cr_fdset);
|
2012-05-04 15:06:20 +04:00
|
|
|
}
|
2012-03-29 16:41:43 +04:00
|
|
|
|
2012-04-09 15:52:00 +04:00
|
|
|
static int dump_task_exe_link(pid_t pid, struct mm_entry *mm)
|
2011-12-07 17:11:00 +04:00
|
|
|
{
|
2012-02-07 19:20:17 +04:00
|
|
|
struct fd_parms params;
|
|
|
|
int fd, ret;
|
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
|
|
|
|
|
|
|
if (fstat(fd, ¶ms.stat) < 0) {
|
|
|
|
pr_perror("Can't fstat exe link");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
params.flags = 0;
|
|
|
|
params.pos = 0;
|
2012-04-11 15:53:00 +04:00
|
|
|
params.fown = (fown_t){ };
|
2012-04-09 15:52:00 +04:00
|
|
|
mm->exe_file_id = fd_id_generate_special();
|
|
|
|
|
|
|
|
ret = dump_one_reg_file(fd, mm->exe_file_id, ¶ms);
|
2012-03-29 16:40:50 +04:00
|
|
|
close(fd);
|
2012-02-07 19:32:11 +04:00
|
|
|
|
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
|
|
|
|
2012-04-10 18:36:59 +04:00
|
|
|
static int fill_fd_params(pid_t pid, int fd, int lfd, char fd_flags, struct fd_parms *p)
|
2012-03-01 20:56:21 +03:00
|
|
|
{
|
2012-04-11 15:53:00 +04:00
|
|
|
struct f_owner_ex owner_ex;
|
|
|
|
u32 v[2];
|
|
|
|
|
2012-03-29 16:38:30 +04:00
|
|
|
if (fstat(lfd, &p->stat) < 0) {
|
|
|
|
pr_perror("Can't stat fd %d\n", lfd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-10 17:49:39 +04:00
|
|
|
p->fd = fd;
|
2012-03-29 16:38:30 +04:00
|
|
|
p->pos = lseek(lfd, 0, SEEK_CUR);
|
|
|
|
p->flags = fcntl(lfd, F_GETFL);
|
|
|
|
p->pid = pid;
|
|
|
|
p->id = FD_ID_INVALID;
|
2012-04-10 18:36:59 +04:00
|
|
|
p->fd_flags = fd_flags;
|
2012-04-11 15:53:00 +04:00
|
|
|
p->fown = (fown_t){ };
|
2012-03-01 20:56:21 +03:00
|
|
|
|
2012-04-24 15:20:24 +04:00
|
|
|
pr_info("%d fdinfo %d: pos: 0x%16lx flags: %16o/%#x\n",
|
2012-04-10 18:36:59 +04:00
|
|
|
pid, fd, p->pos, p->flags, (int)fd_flags);
|
2012-03-29 16:38:30 +04:00
|
|
|
|
2012-04-11 15:53:00 +04:00
|
|
|
p->fown.signum = fcntl(lfd, F_GETSIG, 0);
|
|
|
|
if (p->fown.signum < 0) {
|
|
|
|
pr_perror("Can't get owner signum on %d\n", lfd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fcntl(lfd, F_GETOWN_EX, (long)&owner_ex)) {
|
|
|
|
pr_perror("Can't get owners on %d\n", lfd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Simple case -- nothing is changed.
|
|
|
|
*/
|
|
|
|
if (owner_ex.pid == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (fcntl(lfd, F_GETOWNER_UIDS, (long)&v)) {
|
|
|
|
pr_perror("Can't get owner uids on %d\n", lfd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->fown.uid = v[0];
|
|
|
|
p->fown.euid = v[1];
|
|
|
|
p->fown.pid_type= owner_ex.type;
|
|
|
|
p->fown.pid = owner_ex.pid;
|
|
|
|
|
2012-03-29 16:38:30 +04:00
|
|
|
return 0;
|
2012-03-01 20:56:21 +03:00
|
|
|
}
|
|
|
|
|
2012-03-29 16:43:12 +04:00
|
|
|
static int dump_unsupp_fd(const struct fd_parms *p)
|
|
|
|
{
|
2012-04-24 15:20:24 +04:00
|
|
|
pr_err("Can't dump file %d of that type [%#x]\n",
|
2012-04-10 17:49:39 +04:00
|
|
|
p->fd, p->stat.st_mode);
|
2012-03-29 16:43:12 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-10 18:17:26 +04:00
|
|
|
static int dump_chrdev(struct fd_parms *p, int lfd, const struct cr_fdset *set)
|
2012-03-29 16:43:12 +04:00
|
|
|
{
|
|
|
|
int maj;
|
|
|
|
|
|
|
|
maj = major(p->stat.st_rdev);
|
|
|
|
if (maj == MEM_MAJOR)
|
2012-05-04 15:06:20 +04:00
|
|
|
return dump_reg_file(p, lfd, set);
|
2012-03-29 16:43:12 +04:00
|
|
|
|
2012-04-10 17:49:39 +04:00
|
|
|
if (p->fd < 3 && (maj == TTY_MAJOR ||
|
2012-03-29 16:43:12 +04:00
|
|
|
maj == UNIX98_PTY_SLAVE_MAJOR)) {
|
2012-04-10 17:49:39 +04:00
|
|
|
pr_info("... Skipping tty ... %d\n", p->fd);
|
2012-03-29 16:43:12 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dump_unsupp_fd(p);
|
|
|
|
}
|
|
|
|
|
2012-05-04 15:06:20 +04:00
|
|
|
#ifndef PIPEFS_MAGIC
|
|
|
|
#define PIPEFS_MAGIC 0x50495045
|
|
|
|
#endif
|
|
|
|
|
2012-04-10 18:36:59 +04:00
|
|
|
static int dump_one_file(pid_t pid, int fd, int lfd, char fd_flags,
|
2012-03-29 16:44:15 +04:00
|
|
|
const struct cr_fdset *cr_fdset)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-03-01 20:56:21 +03:00
|
|
|
struct fd_parms p;
|
2012-05-04 13:38:00 +04:00
|
|
|
struct statfs statfs;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-04-10 18:36:59 +04:00
|
|
|
if (fill_fd_params(pid, fd, lfd, fd_flags, &p) < 0) {
|
2012-03-28 17:36:00 +04:00
|
|
|
pr_perror("Can't get stat on %d", fd);
|
2012-03-29 16:40:50 +04:00
|
|
|
return -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-03-29 16:38:30 +04:00
|
|
|
if (S_ISSOCK(p.stat.st_mode))
|
2012-03-29 16:44:15 +04:00
|
|
|
return dump_socket(&p, lfd, cr_fdset);
|
2012-03-28 17:36:00 +04:00
|
|
|
|
2012-03-29 16:43:12 +04:00
|
|
|
if (S_ISCHR(p.stat.st_mode))
|
2012-04-10 18:17:26 +04:00
|
|
|
return dump_chrdev(&p, lfd, cr_fdset);
|
2011-11-28 13:57:41 +03:00
|
|
|
|
2012-05-04 13:38:00 +04:00
|
|
|
if (fstatfs(lfd, &statfs)) {
|
|
|
|
pr_perror("Can't obtain statfs on fd %d\n", fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_anon_inode(&statfs)) {
|
|
|
|
if (is_eventfd_link(lfd))
|
|
|
|
return dump_eventfd(&p, lfd, cr_fdset);
|
2012-05-04 13:38:00 +04:00
|
|
|
else if (is_eventpoll_link(lfd))
|
|
|
|
return dump_eventpoll(&p, lfd, cr_fdset);
|
2012-05-04 13:38:00 +04:00
|
|
|
else if (is_inotify_link(lfd))
|
|
|
|
return dump_inotify(&p, lfd, cr_fdset);
|
2012-05-04 15:57:49 +04:00
|
|
|
else
|
|
|
|
return dump_unsupp_fd(&p);
|
2012-05-04 13:38:00 +04:00
|
|
|
}
|
|
|
|
|
2012-04-05 20:02:00 +04:00
|
|
|
if (S_ISREG(p.stat.st_mode) ||
|
2012-05-04 15:06:20 +04:00
|
|
|
S_ISDIR(p.stat.st_mode))
|
|
|
|
return dump_reg_file(&p, lfd, cr_fdset);
|
|
|
|
|
|
|
|
if (S_ISFIFO(p.stat.st_mode) && (statfs.f_type == PIPEFS_MAGIC))
|
|
|
|
return dump_pipe(&p, lfd, cr_fdset);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-29 16:43:12 +04:00
|
|
|
return dump_unsupp_fd(&p);
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-03-28 17:36:00 +04:00
|
|
|
static int dump_task_files_seized(struct parasite_ctl *ctl, const struct cr_fdset *cr_fdset,
|
2012-03-29 16:44:15 +04:00
|
|
|
int *fds, int nr_fds)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-03-28 17:36:00 +04:00
|
|
|
int *lfds;
|
2012-04-10 18:36:59 +04:00
|
|
|
char *flags;
|
2012-03-28 17:36:00 +04:00
|
|
|
int i, ret = -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
2012-03-28 17:36:00 +04:00
|
|
|
pr_info("Dumping opened files (pid: %d)\n", ctl->pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-03-28 17:36:00 +04:00
|
|
|
lfds = xmalloc(PARASITE_MAX_FDS * sizeof(int));
|
|
|
|
if (!lfds)
|
|
|
|
goto err;
|
|
|
|
|
2012-04-10 18:36:59 +04:00
|
|
|
flags = xmalloc(PARASITE_MAX_FDS * sizeof(char));
|
|
|
|
if (!flags)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
ret = parasite_drain_fds_seized(ctl, fds, lfds, nr_fds, flags);
|
2012-03-28 17:36:00 +04:00
|
|
|
if (ret)
|
2012-04-10 18:36:59 +04:00
|
|
|
goto err2;
|
2012-03-28 17:36:00 +04:00
|
|
|
|
|
|
|
for (i = 0; i < nr_fds; i++) {
|
2012-04-10 18:36:59 +04:00
|
|
|
ret = dump_one_file(ctl->pid, fds[i], lfds[i], flags[i], cr_fdset);
|
2012-03-29 16:40:50 +04:00
|
|
|
close(lfds[i]);
|
2012-03-28 17:36:00 +04:00
|
|
|
if (ret)
|
2012-04-10 18:36:59 +04:00
|
|
|
goto err2;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("----------------------------------------\n");
|
2012-04-10 18:36:59 +04:00
|
|
|
err2:
|
|
|
|
xfree(flags);
|
|
|
|
err1:
|
2012-03-28 17:36:00 +04:00
|
|
|
xfree(lfds);
|
2012-04-10 18:36:59 +04:00
|
|
|
err:
|
2012-03-28 17:36:00 +04:00
|
|
|
return ret;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-04-09 13:41:05 +04:00
|
|
|
static int dump_task_fs(pid_t pid, struct cr_fdset *fdset)
|
|
|
|
{
|
|
|
|
struct fd_parms p;
|
|
|
|
struct fs_entry fe;
|
|
|
|
int fd, ret;
|
|
|
|
|
|
|
|
fd = open_proc(pid, "cwd");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (fstat(fd, &p.stat) < 0) {
|
|
|
|
pr_perror("Can't stat cwd");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
p.flags = 0;
|
|
|
|
p.pos = 0;
|
2012-04-11 15:53:00 +04:00
|
|
|
p.fown = (fown_t){ };
|
2012-04-09 13:41:05 +04:00
|
|
|
fe.cwd_id = fd_id_generate_special();
|
|
|
|
|
|
|
|
ret = dump_one_reg_file(fd, fe.cwd_id, &p);
|
2012-04-09 13:52:42 +04:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
fd = open_proc(pid, "root");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (fstat(fd, &p.stat) < 0) {
|
|
|
|
pr_perror("Can't stat root");
|
|
|
|
return -1;
|
2012-04-09 13:41:05 +04:00
|
|
|
}
|
|
|
|
|
2012-04-09 13:52:42 +04:00
|
|
|
p.flags = 0;
|
|
|
|
p.pos = 0;
|
|
|
|
fe.root_id = fd_id_generate_special();
|
|
|
|
|
|
|
|
ret = dump_one_reg_file(fd, fe.root_id, &p);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
return write_img(fdset_fd(fdset, CR_FD_FS), &fe);
|
2012-04-09 13:41:05 +04:00
|
|
|
}
|
|
|
|
|
2012-03-21 11:33:54 +04:00
|
|
|
static int dump_filemap(pid_t pid, struct vma_entry *vma, int file_fd,
|
|
|
|
const struct cr_fdset *fdset)
|
|
|
|
{
|
2012-04-09 12:57:38 +04:00
|
|
|
struct fd_parms p;
|
2012-03-21 11:33:54 +04:00
|
|
|
|
2012-04-09 12:57:38 +04:00
|
|
|
if (fstat(file_fd, &p.stat) < 0) {
|
|
|
|
pr_perror("Can't stat file for vma");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-19 01:14:00 +04:00
|
|
|
p.pos = 0;
|
2012-04-11 15:53:00 +04:00
|
|
|
p.fown = (fown_t){ };
|
2012-03-21 11:33:54 +04:00
|
|
|
if ((vma->prot & PROT_WRITE) && vma_entry_is(vma, VMA_FILE_SHARED))
|
|
|
|
p.flags = O_RDWR;
|
|
|
|
else
|
|
|
|
p.flags = O_RDONLY;
|
2012-04-09 12:57:38 +04:00
|
|
|
vma->shmid = fd_id_generate_special();
|
2012-03-21 11:33:54 +04:00
|
|
|
|
2012-04-09 12:57:38 +04:00
|
|
|
return dump_one_reg_file(file_fd, vma->shmid, &p);
|
2012-03-21 11:33:54 +04:00
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int dump_task_mappings(pid_t pid, const struct list_head *vma_area_list,
|
|
|
|
const struct cr_fdset *cr_fdset)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
struct vma_area *vma_area;
|
2012-03-26 17:43:29 +04:00
|
|
|
int ret = -1, fd;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping mappings (pid: %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-03-26 17:43:29 +04:00
|
|
|
fd = fdset_fd(cr_fdset, CR_FD_VMAS);
|
|
|
|
|
2011-10-21 12:14:45 +04:00
|
|
|
list_for_each_entry(vma_area, vma_area_list, list) {
|
2011-09-23 12:00:45 +04:00
|
|
|
struct vma_entry *vma = &vma_area->vma;
|
|
|
|
|
|
|
|
pr_info_vma(vma_area);
|
|
|
|
|
2012-04-09 12:57:38 +04:00
|
|
|
if (!vma_entry_is(vma, VMA_AREA_REGULAR) ||
|
|
|
|
vma_entry_is(vma, VMA_AREA_SYSVIPC))
|
|
|
|
ret = 0;
|
|
|
|
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))
|
|
|
|
ret = dump_filemap(pid, vma, vma_area->vm_file_fd, cr_fdset);
|
2012-03-21 14:22:00 +04:00
|
|
|
else
|
|
|
|
ret = 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-04-09 12:57:38 +04:00
|
|
|
if (!ret)
|
|
|
|
ret = write_img(fd, vma);
|
2012-03-21 11:33:54 +04:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int dump_task_creds(pid_t pid, const struct parasite_dump_misc *misc,
|
|
|
|
const struct cr_fdset *fds)
|
2012-01-27 21:43:32 +04:00
|
|
|
{
|
|
|
|
int ret, i;
|
|
|
|
struct proc_status_creds cr;
|
|
|
|
struct creds_entry ce;
|
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping creds for %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
ret = parse_pid_status(pid, &cr);
|
2012-01-27 21:43:32 +04:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ce.uid = cr.uids[0];
|
|
|
|
ce.gid = cr.gids[0];
|
|
|
|
ce.euid = cr.uids[1];
|
|
|
|
ce.egid = cr.gids[1];
|
|
|
|
ce.suid = cr.uids[2];
|
|
|
|
ce.sgid = cr.gids[2];
|
|
|
|
ce.fsuid = cr.uids[3];
|
|
|
|
ce.fsgid = cr.gids[3];
|
|
|
|
|
|
|
|
BUILD_BUG_ON(CR_CAP_SIZE != PROC_CAP_SIZE);
|
|
|
|
|
|
|
|
for (i = 0; i < CR_CAP_SIZE; i++) {
|
|
|
|
ce.cap_inh[i] = cr.cap_inh[i];
|
|
|
|
ce.cap_prm[i] = cr.cap_prm[i];
|
|
|
|
ce.cap_eff[i] = cr.cap_eff[i];
|
|
|
|
ce.cap_bnd[i] = cr.cap_bnd[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
ce.secbits = misc->secbits;
|
|
|
|
|
2012-03-26 17:43:29 +04:00
|
|
|
ret = write_img(fdset_fd(fds, CR_FD_CREDS), &ce);
|
2012-01-27 21:43:32 +04:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
#define assign_reg(dst, src, e) dst.e = (__typeof__(dst.e))src.e
|
|
|
|
#define assign_array(dst, src, e) memcpy(&dst.e, &src.e, sizeof(dst.e))
|
|
|
|
|
2012-04-09 14:51:37 +04:00
|
|
|
static int get_task_auxv(pid_t pid, struct mm_entry *mm)
|
2012-01-23 15:22:00 +04:00
|
|
|
{
|
2012-04-09 14:51:37 +04:00
|
|
|
int fd, ret, i;
|
|
|
|
|
|
|
|
pr_info("Obtainting task auvx ... ");
|
2012-01-23 15:22:00 +04:00
|
|
|
|
2012-04-09 14:51:37 +04:00
|
|
|
fd = open_proc(pid, "auxv");
|
2012-02-17 01:39:35 +04:00
|
|
|
if (fd < 0)
|
2012-01-23 15:22:00 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0; i < AT_VECTOR_SIZE; i++) {
|
2012-04-09 14:51:37 +04:00
|
|
|
ret = read(fd, &mm->mm_saved_auxv[i],
|
|
|
|
sizeof(mm->mm_saved_auxv[0]));
|
2012-01-23 15:22:00 +04:00
|
|
|
if (ret == 0)
|
|
|
|
break;
|
2012-04-09 14:51:37 +04:00
|
|
|
else if (ret != sizeof(mm->mm_saved_auxv[0])) {
|
2012-01-23 15:22:00 +04:00
|
|
|
ret = -1;
|
2012-01-31 15:13:05 +04:00
|
|
|
pr_perror("Error readind %d's auxv[%d]",
|
2012-01-23 15:22:00 +04:00
|
|
|
pid, i);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
close_safe(&fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-09 14:51:37 +04:00
|
|
|
static int dump_task_mm(pid_t pid, const struct proc_pid_stat *stat,
|
|
|
|
const struct parasite_dump_misc *misc, const struct cr_fdset *fdset)
|
|
|
|
{
|
|
|
|
struct mm_entry mme;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (get_task_auxv(pid, &mme))
|
|
|
|
return -1;
|
2012-04-12 15:22:06 +04:00
|
|
|
pr_info("OK\n");
|
2012-04-09 14:51:37 +04:00
|
|
|
|
2012-04-09 15:52:00 +04:00
|
|
|
if (dump_task_exe_link(pid, &mme))
|
|
|
|
return -1;
|
|
|
|
|
2012-04-09 14:51:37 +04:00
|
|
|
return write_img(fdset_fd(fdset, CR_FD_MM), &mme);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
FILE *file = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2012-04-10 15:39:21 +04:00
|
|
|
pr_info("Obtainting personality ... ");
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
file = fopen_proc(pid, "personality");
|
2012-02-17 01:39:35 +04:00
|
|
|
if (!file)
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (!fgets(loc_buf, sizeof(loc_buf), file)) {
|
|
|
|
perror("Can't read task personality");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
*personality = atoi(loc_buf);
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
if (file)
|
|
|
|
fclose(file);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int get_task_regs(pid_t pid, struct core_entry *core, const struct parasite_ctl *ctl)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
user_fpregs_struct_t fpregs = {-1};
|
|
|
|
user_regs_struct_t regs = {-1};
|
2011-10-20 17:19:26 +04:00
|
|
|
int ret = -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-04-10 15:38:33 +04:00
|
|
|
pr_info("Dumping GP/FPU registers ... ");
|
|
|
|
|
2012-02-17 22:56:36 +04:00
|
|
|
if (ctl)
|
|
|
|
regs = ctl->regs_orig;
|
|
|
|
else {
|
|
|
|
if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) {
|
|
|
|
pr_err("Can't obtain GP registers for %d\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-02-15 18:36:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs)) {
|
|
|
|
pr_err("Can't obtain FPU registers for %d\n", pid);
|
|
|
|
goto err;
|
|
|
|
}
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2011-12-02 16:46:00 +04:00
|
|
|
/* Did we come from a system call? */
|
2012-02-10 18:34:09 +04:00
|
|
|
if ((int)regs.orig_ax >= 0) {
|
2011-12-02 16:46:00 +04:00
|
|
|
/* Restart the system call */
|
2012-02-10 18:34:09 +04:00
|
|
|
switch ((long)(int)regs.ax) {
|
2011-12-02 16:46:00 +04:00
|
|
|
case -ERESTARTNOHAND:
|
|
|
|
case -ERESTARTSYS:
|
|
|
|
case -ERESTARTNOINTR:
|
|
|
|
regs.ax = regs.orig_ax;
|
|
|
|
regs.ip -= 2;
|
|
|
|
break;
|
|
|
|
case -ERESTART_RESTARTBLOCK:
|
|
|
|
regs.ax = __NR_restart_syscall;
|
|
|
|
regs.ip -= 2;
|
|
|
|
break;
|
|
|
|
}
|
2012-02-10 18:34:09 +04:00
|
|
|
}
|
2011-12-02 16:46:00 +04:00
|
|
|
|
2012-03-17 01:02:55 +04:00
|
|
|
assign_reg(core->arch.gpregs, regs, r15);
|
|
|
|
assign_reg(core->arch.gpregs, regs, r14);
|
|
|
|
assign_reg(core->arch.gpregs, regs, r13);
|
|
|
|
assign_reg(core->arch.gpregs, regs, r12);
|
|
|
|
assign_reg(core->arch.gpregs, regs, bp);
|
|
|
|
assign_reg(core->arch.gpregs, regs, bx);
|
|
|
|
assign_reg(core->arch.gpregs, regs, r11);
|
|
|
|
assign_reg(core->arch.gpregs, regs, r10);
|
|
|
|
assign_reg(core->arch.gpregs, regs, r9);
|
|
|
|
assign_reg(core->arch.gpregs, regs, r8);
|
|
|
|
assign_reg(core->arch.gpregs, regs, ax);
|
|
|
|
assign_reg(core->arch.gpregs, regs, cx);
|
|
|
|
assign_reg(core->arch.gpregs, regs, dx);
|
|
|
|
assign_reg(core->arch.gpregs, regs, si);
|
|
|
|
assign_reg(core->arch.gpregs, regs, di);
|
|
|
|
assign_reg(core->arch.gpregs, regs, orig_ax);
|
|
|
|
assign_reg(core->arch.gpregs, regs, ip);
|
|
|
|
assign_reg(core->arch.gpregs, regs, cs);
|
|
|
|
assign_reg(core->arch.gpregs, regs, flags);
|
|
|
|
assign_reg(core->arch.gpregs, regs, sp);
|
|
|
|
assign_reg(core->arch.gpregs, regs, ss);
|
|
|
|
assign_reg(core->arch.gpregs, regs, fs_base);
|
|
|
|
assign_reg(core->arch.gpregs, regs, gs_base);
|
|
|
|
assign_reg(core->arch.gpregs, regs, ds);
|
|
|
|
assign_reg(core->arch.gpregs, regs, es);
|
|
|
|
assign_reg(core->arch.gpregs, regs, fs);
|
|
|
|
assign_reg(core->arch.gpregs, regs, gs);
|
|
|
|
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, cwd);
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, swd);
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, twd);
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, fop);
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, rip);
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, rdp);
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, mxcsr);
|
|
|
|
assign_reg(core->arch.fpregs, fpregs, mxcsr_mask);
|
2012-01-22 20:16:33 +04:00
|
|
|
|
|
|
|
assign_array(core->arch.fpregs, fpregs, st_space);
|
|
|
|
assign_array(core->arch.fpregs, fpregs, xmm_space);
|
|
|
|
assign_array(core->arch.fpregs, fpregs, padding);
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2011-10-20 17:19:26 +04:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-26 17:36:44 +04:00
|
|
|
static int dump_task_core(struct core_entry *core, int fd_core)
|
2012-01-22 20:21:35 +04:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_info("Dumping header ... ");
|
|
|
|
|
|
|
|
core->header.version = HEADER_VERSION;
|
|
|
|
core->header.arch = HEADER_ARCH_X86_64;
|
|
|
|
core->header.flags = 0;
|
|
|
|
|
2012-03-26 17:36:44 +04:00
|
|
|
return write_img(fd_core, core);
|
2012-01-22 20:21:35 +04:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
static int dump_task_kobj_ids(pid_t pid, struct core_entry *core)
|
|
|
|
{
|
|
|
|
int new;
|
|
|
|
struct kid_elem elem;
|
|
|
|
|
|
|
|
elem.pid = pid;
|
|
|
|
elem.idx = 0; /* really 0 for all */
|
|
|
|
elem.genid = 0; /* FIXME optimize */
|
|
|
|
|
|
|
|
new = 0;
|
|
|
|
core->ids.vm_id = kid_generate_gen(&vm_tree, &elem, &new);
|
|
|
|
if (!core->ids.vm_id || !new) {
|
|
|
|
pr_err("Can't make VM id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new = 0;
|
|
|
|
core->ids.fs_id = kid_generate_gen(&fs_tree, &elem, &new);
|
|
|
|
if (!core->ids.fs_id || !new) {
|
|
|
|
pr_err("Can't make FS id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new = 0;
|
|
|
|
core->ids.files_id = kid_generate_gen(&files_tree, &elem, &new);
|
|
|
|
if (!core->ids.files_id || !new) {
|
|
|
|
pr_err("Can't make FILES id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new = 0;
|
|
|
|
core->ids.sighand_id = kid_generate_gen(&sighand_tree, &elem, &new);
|
|
|
|
if (!core->ids.sighand_id || !new) {
|
|
|
|
pr_err("Can't make IO id for %d\n", pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int dump_task_core_all(pid_t pid, const struct proc_pid_stat *stat,
|
|
|
|
const struct parasite_dump_misc *misc, const struct parasite_ctl *ctl,
|
|
|
|
const struct cr_fdset *cr_fdset)
|
2011-10-20 17:19:26 +04:00
|
|
|
{
|
2012-04-10 15:41:16 +04:00
|
|
|
struct core_entry *core;
|
|
|
|
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-04-10 15:41:16 +04:00
|
|
|
core = xzalloc(sizeof(*core));
|
2011-10-20 17:19:26 +04:00
|
|
|
if (!core)
|
|
|
|
goto err;
|
|
|
|
|
2012-04-09 18:02:00 +04:00
|
|
|
ret = dump_task_kobj_ids(pid, core);
|
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
|
|
|
|
2012-04-10 15:41:16 +04:00
|
|
|
ret = dump_task_mm(pid, stat, misc, cr_fdset);
|
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
|
|
|
|
2012-02-17 22:56:36 +04:00
|
|
|
ret = get_task_regs(pid, core, ctl);
|
2011-10-20 17:19:26 +04:00
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
ret = get_task_personality(pid, &core->tc.personality);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
|
|
|
|
2012-01-22 20:16:33 +04:00
|
|
|
strncpy((char *)core->tc.comm, stat->comm, TASK_COMM_LEN);
|
|
|
|
core->tc.flags = stat->flags;
|
2012-02-17 22:56:36 +04:00
|
|
|
BUILD_BUG_ON(sizeof(core->tc.blk_sigset) != sizeof(k_rtsigset_t));
|
|
|
|
memcpy(&core->tc.blk_sigset, &misc->blocked, sizeof(k_rtsigset_t));
|
2011-10-01 13:24:34 +04:00
|
|
|
|
2012-01-22 20:24:04 +04:00
|
|
|
core->tc.task_state = TASK_ALIVE;
|
|
|
|
core->tc.exit_code = 0;
|
|
|
|
|
2012-03-26 17:43:29 +04:00
|
|
|
ret = dump_task_core(core, fdset_fd(cr_fdset, CR_FD_CORE));
|
2012-04-12 15:22:06 +04:00
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
|
|
|
pr_info("OK\n");
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
err_free:
|
|
|
|
free(core);
|
|
|
|
err:
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int parse_threads(const struct pstree_item *item, u32 **_t, int *_n)
|
2011-10-20 11:03:19 +04:00
|
|
|
{
|
|
|
|
struct dirent *de;
|
|
|
|
DIR *dir;
|
|
|
|
u32 *t = NULL;
|
2011-12-02 18:24:50 +04:00
|
|
|
int nr = 1;
|
2011-10-20 11:03:19 +04:00
|
|
|
|
2012-03-01 19:02:03 +04:00
|
|
|
dir = opendir_proc(item->pid, "task");
|
2012-02-17 01:39:35 +04:00
|
|
|
if (!dir)
|
2011-12-02 18:24:50 +04:00
|
|
|
return -1;
|
2011-10-20 11:03:19 +04:00
|
|
|
|
|
|
|
while ((de = readdir(dir))) {
|
2011-12-04 11:48:10 +04:00
|
|
|
u32 *tmp;
|
|
|
|
|
2011-10-20 11:03:19 +04:00
|
|
|
/* We expect numbers only here */
|
|
|
|
if (de->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
2011-12-04 11:48:10 +04:00
|
|
|
tmp = xrealloc(t, nr * sizeof(u32));
|
|
|
|
if (!tmp) {
|
|
|
|
xfree(t);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
t = tmp;
|
2011-12-02 18:24:50 +04:00
|
|
|
t[nr - 1] = atoi(de->d_name);
|
|
|
|
nr++;
|
2011-10-20 11:03:19 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
*_t = t;
|
|
|
|
*_n = nr - 1;
|
2011-12-25 17:09:37 +04:00
|
|
|
|
2011-12-02 18:24:50 +04:00
|
|
|
return 0;
|
2011-10-20 11:03:19 +04:00
|
|
|
}
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
static int get_threads(struct pstree_item *item)
|
|
|
|
{
|
|
|
|
return parse_threads(item, &item->threads, &item->nr_threads);
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int check_threads(const struct pstree_item *item)
|
2012-03-01 19:07:15 +04:00
|
|
|
{
|
|
|
|
u32 *t;
|
|
|
|
int nr, ret;
|
|
|
|
|
|
|
|
ret = parse_threads(item, &t, &nr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ((nr == item->nr_threads) && !memcmp(t, item->threads, nr));
|
|
|
|
xfree(t);
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
pr_info("Threads set has changed while suspending\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int parse_children(const struct pstree_item *item, u32 **_c, int *_n)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
char *tok;
|
2011-12-02 18:26:15 +04:00
|
|
|
u32 *ch = NULL;
|
2011-12-25 18:19:16 +04:00
|
|
|
int nr = 1, i;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2011-12-25 18:19:16 +04:00
|
|
|
for (i = 0; i < item->nr_threads; i++) {
|
2011-12-01 17:04:49 +04:00
|
|
|
|
2012-03-01 19:05:39 +04:00
|
|
|
file = fopen_proc(item->pid, "task/%d/children", item->threads[i]);
|
2012-02-17 01:39:35 +04:00
|
|
|
if (!file)
|
2011-12-25 18:19:16 +04:00
|
|
|
goto err;
|
2011-12-01 17:04:49 +04:00
|
|
|
|
2011-12-25 18:19:16 +04:00
|
|
|
if (!(fgets(loc_buf, sizeof(loc_buf), file)))
|
|
|
|
loc_buf[0] = 0;
|
2011-12-01 17:04:49 +04:00
|
|
|
|
2011-12-25 18:19:16 +04:00
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
tok = strtok(loc_buf, " \n");
|
|
|
|
while (tok) {
|
|
|
|
u32 *tmp = xrealloc(ch, nr * sizeof(u32));
|
|
|
|
if (!tmp)
|
|
|
|
goto err;
|
|
|
|
ch = tmp;
|
|
|
|
ch[nr - 1] = atoi(tok);
|
|
|
|
nr++;
|
|
|
|
tok = strtok(NULL, " \n");
|
2011-12-04 11:48:10 +04:00
|
|
|
}
|
2011-12-25 18:19:16 +04:00
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
*_c = ch;
|
|
|
|
*_n = nr - 1;
|
2011-12-25 18:19:16 +04:00
|
|
|
|
2011-12-02 18:26:15 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
2011-12-25 18:19:16 +04:00
|
|
|
xfree(ch);
|
2011-12-02 18:26:15 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
static int get_children(struct pstree_item *item)
|
|
|
|
{
|
|
|
|
return parse_children(item, &item->children, &item->nr_children);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
for (i = 0; i < item->nr_threads; i++)
|
|
|
|
unseize_task(item->threads[i], st); /* item->pid will be here */
|
|
|
|
}
|
|
|
|
|
2012-04-11 15:17:26 +04:00
|
|
|
static void pstree_switch_state(const struct list_head *list, int st)
|
2011-12-02 18:26:15 +04:00
|
|
|
{
|
2011-12-02 18:27:51 +04:00
|
|
|
struct pstree_item *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);
|
|
|
|
list_for_each_entry(item, list, list)
|
|
|
|
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;
|
|
|
|
return item ? item->pid : -1;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int seize_threads(const 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
|
|
|
{
|
|
|
|
int i = 0, ret;
|
|
|
|
|
|
|
|
if ((item->state == TASK_DEAD) && (item->nr_threads > 1)) {
|
|
|
|
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
|
|
|
|
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
|
|
|
for (i = 0; i < item->nr_threads; i++) {
|
|
|
|
if (item->pid == item->threads[i])
|
|
|
|
continue;
|
2012-02-01 17:03:51 +04:00
|
|
|
|
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_info("\tSeizing %d's %d thread\n", item->pid, item->threads[i]);
|
2012-04-11 22:10:09 +04:00
|
|
|
ret = seize_task(item->threads[i], item_ppid(item), 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
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
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) {
|
|
|
|
pr_err("Stopped threads not supported\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2011-10-20 11:03:19 +04:00
|
|
|
|
2011-12-02 18:27:51 +04:00
|
|
|
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
|
|
|
for (i--; i >= 0; i--) {
|
|
|
|
if (item->pid == item->threads[i])
|
|
|
|
continue;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-01 19:09:02 +04:00
|
|
|
unseize_task(item->threads[i], TASK_ALIVE);
|
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-01-19 19:22:59 +04:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-03-01 19:02:03 +04:00
|
|
|
static int collect_threads(struct pstree_item *item)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
ret = get_threads(item);
|
2012-03-01 19:02:03 +04:00
|
|
|
if (!ret)
|
|
|
|
ret = seize_threads(item);
|
2012-03-01 19:07:15 +04:00
|
|
|
if (!ret)
|
|
|
|
ret = check_threads(item);
|
2012-03-01 19:02:03 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-11 22:11:41 +04:00
|
|
|
/*
|
|
|
|
* Few words about sid and pgid handling.
|
|
|
|
*
|
|
|
|
* An axiom: session can only be changed on self, group -- on self or
|
|
|
|
* on one of self children.
|
|
|
|
*
|
|
|
|
* Conclusions:
|
|
|
|
* 1. both should better be saved in pstree.img so we can restore sid
|
|
|
|
* at the time we fork kids and pgid is just for harmony;
|
|
|
|
* 2. if we seized the parent these IDs* shouldn't change and it's safe
|
|
|
|
* to read and check them right at the seizing time.
|
|
|
|
*
|
|
|
|
* Easings:
|
|
|
|
* 1. with the existing setsid we can only reset sid to task's pid.
|
|
|
|
* Thus, if task escaped from its ancestors with sid we will not be
|
|
|
|
* able to easily restore this construction. Thus for now this is
|
|
|
|
* treated as "unsupported" (FIXME);
|
|
|
|
*
|
|
|
|
* 2. when task whose pid is equal to pgid/sid (i.e. leader) exits we
|
|
|
|
* lose the ability to restore the grp/session easily with the
|
|
|
|
* existing interfaces, thus we also treat this as temporarily
|
|
|
|
* unsupported (FIXME #2).
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int check_xids(struct list_head *list)
|
|
|
|
{
|
|
|
|
struct pstree_item *p, *tmp;
|
|
|
|
|
|
|
|
list_for_each_entry(p, list, list) {
|
|
|
|
if (p->parent == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Easing #1 and #2 for sids */
|
|
|
|
if ((p->sid != p->pid) && (p->sid != p->parent->sid)) {
|
|
|
|
pr_err("SID mismatch on %d (%d/%d)\n",
|
|
|
|
p->pid, p->sid, p->parent->sid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Easing #2 for pgids */
|
|
|
|
list_for_each_entry(tmp, list, list)
|
|
|
|
if (tmp->pid == p->pgid)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (&tmp->list == list) {
|
|
|
|
pr_err("PGIG mismatch on %d (%d)\n",
|
|
|
|
p->pid, p->pgid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-11 22:07:47 +04:00
|
|
|
static struct pstree_item *collect_task(pid_t pid, struct pstree_item *parent,
|
|
|
|
struct list_head *list)
|
2012-01-19 19:22:59 +04:00
|
|
|
{
|
2012-02-17 01:39:36 +04:00
|
|
|
int ret;
|
2012-01-19 19:22:59 +04:00
|
|
|
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
|
|
|
item = xzalloc(sizeof(*item));
|
|
|
|
if (!item)
|
|
|
|
goto err;
|
2012-01-19 19:22:59 +04:00
|
|
|
|
2012-04-11 22:07:47 +04:00
|
|
|
item->pid = pid;
|
|
|
|
item->parent = parent;
|
|
|
|
|
2012-04-11 22:10:09 +04:00
|
|
|
ret = seize_task(pid, item_ppid(item), &item->pgid, &item->sid);
|
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_free;
|
2012-01-19 19:22:59 +04:00
|
|
|
|
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_info("Seized task %d, state %d\n", pid, ret);
|
|
|
|
item->state = ret;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-01 19:02:03 +04:00
|
|
|
ret = collect_threads(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
|
|
|
if (ret < 0)
|
|
|
|
goto err_close;
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
ret = get_children(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
|
|
|
if (ret < 0)
|
|
|
|
goto err_close;
|
|
|
|
|
|
|
|
if ((item->state == TASK_DEAD) && (item->nr_children > 0)) {
|
|
|
|
pr_err("Zombie with children?! O_o Run, run, run!\n");
|
|
|
|
goto err_close;
|
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
close_pid_proc();
|
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
|
|
|
list_add_tail(&item->list, list);
|
|
|
|
pr_info("Collected %d in %d state\n", item->pid, item->state);
|
|
|
|
return item;
|
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-03-01 19:09:02 +04:00
|
|
|
unseize_task(pid, item->state);
|
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
|
|
|
err_free:
|
|
|
|
xfree(item->children);
|
|
|
|
xfree(item->threads);
|
|
|
|
xfree(item);
|
2011-09-23 12:00:45 +04:00
|
|
|
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
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
static int check_subtree(const struct pstree_item *item)
|
2012-03-01 19:07:15 +04:00
|
|
|
{
|
|
|
|
u32 *ch;
|
|
|
|
int nr, ret;
|
|
|
|
|
|
|
|
ret = parse_children(item, &ch, &nr);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ((nr == item->nr_children) && !memcmp(ch, item->children, nr));
|
|
|
|
xfree(ch);
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
pr_info("Children set has changed while suspending\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-11 22:07:47 +04:00
|
|
|
static int collect_subtree(pid_t pid, struct pstree_item *parent,
|
|
|
|
struct list_head *pstree_list, int leader_only)
|
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
|
|
|
{
|
|
|
|
struct pstree_item *item;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pr_info("Collecting tasks starting from %d\n", pid);
|
2012-04-11 22:07:47 +04:00
|
|
|
item = collect_task(pid, parent, pstree_list);
|
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 (item == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (leader_only)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < item->nr_children; i++)
|
2012-04-11 22:07:47 +04:00
|
|
|
if (collect_subtree(item->children[i], item, pstree_list, 0) < 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
|
|
|
return -1;
|
|
|
|
|
2012-03-01 19:07:15 +04:00
|
|
|
if (check_subtree(item))
|
|
|
|
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
|
|
|
return 0;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-03-21 14:12:00 +04:00
|
|
|
static int dump_pstree(pid_t pid, const struct list_head *pstree_list);
|
|
|
|
|
|
|
|
static int collect_dump_pstree(pid_t pid, struct list_head *pstree_list,
|
2012-03-06 14:20:00 +04:00
|
|
|
const struct cr_options *opts)
|
2012-03-01 19:04:31 +04:00
|
|
|
{
|
2012-03-01 19:11:29 +04:00
|
|
|
int ret, attempts = 5;
|
|
|
|
|
|
|
|
while (1) {
|
2012-03-01 19:12:35 +04:00
|
|
|
struct pstree_item *item;
|
|
|
|
|
2012-04-11 22:07:47 +04:00
|
|
|
ret = collect_subtree(pid, NULL, pstree_list, opts->leader_only);
|
2012-03-01 19:12:35 +04:00
|
|
|
if (ret == 0) {
|
|
|
|
/*
|
|
|
|
* Some tasks could have been reparented to
|
|
|
|
* namespaces' reaper. Check this.
|
|
|
|
*/
|
|
|
|
if (opts->namespaces_flags & CLONE_NEWPID) {
|
|
|
|
item = list_first_entry(pstree_list,
|
|
|
|
struct pstree_item, list);
|
|
|
|
BUG_ON(item->pid != 1);
|
|
|
|
|
|
|
|
if (check_subtree(item))
|
|
|
|
goto try_again;
|
|
|
|
}
|
|
|
|
|
2012-03-01 19:11:29 +04:00
|
|
|
break;
|
2012-03-01 19:12:35 +04:00
|
|
|
}
|
2012-03-01 19:11:29 +04:00
|
|
|
|
2012-03-01 19:13:34 +04:00
|
|
|
if (list_empty(pstree_list))
|
|
|
|
/*
|
|
|
|
* No items at all -- no need in re-scanning it again
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
|
2012-03-01 19:11:29 +04:00
|
|
|
/*
|
|
|
|
* Old tasks can die and new ones can appear while we
|
|
|
|
* try to seize the swarm. It's much simpler (and reliable)
|
|
|
|
* just to restart the collection from the beginning
|
|
|
|
* rather than trying to chase them.
|
|
|
|
*/
|
2012-03-01 19:12:35 +04:00
|
|
|
try_again:
|
2012-03-01 19:11:29 +04:00
|
|
|
if (attempts == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
attempts--;
|
|
|
|
pr_info("Trying to suspend tasks again\n");
|
|
|
|
|
|
|
|
while (!list_empty(pstree_list)) {
|
|
|
|
item = list_first_entry(pstree_list,
|
|
|
|
struct pstree_item, list);
|
|
|
|
list_del(&item->list);
|
|
|
|
|
|
|
|
unseize_task_and_threads(item, TASK_ALIVE);
|
|
|
|
|
|
|
|
xfree(item->children);
|
|
|
|
xfree(item->threads);
|
|
|
|
xfree(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-11 22:11:41 +04:00
|
|
|
if (!ret)
|
|
|
|
ret = check_xids(pstree_list);
|
|
|
|
|
2012-03-21 14:12:00 +04:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return dump_pstree(pid, pstree_list);
|
2012-03-01 19:04:31 +04:00
|
|
|
}
|
|
|
|
|
2012-03-21 14:12:00 +04:00
|
|
|
static int dump_pstree(pid_t pid, const struct list_head *pstree_list)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2012-03-06 14:20:00 +04:00
|
|
|
const struct pstree_item *item;
|
2011-09-23 12:00:45 +04:00
|
|
|
struct pstree_entry e;
|
|
|
|
unsigned long i;
|
|
|
|
int ret = -1;
|
2012-03-21 14:12:00 +04:00
|
|
|
int pstree_fd;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping pstree (pid: %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-03-27 15:20:38 +04:00
|
|
|
pstree_fd = open_image(CR_FD_PSTREE, O_DUMP);
|
2012-03-21 14:12:00 +04:00
|
|
|
if (pstree_fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2011-10-21 11:46:19 +04:00
|
|
|
list_for_each_entry(item, pstree_list, list) {
|
2011-09-23 12:00:45 +04:00
|
|
|
|
|
|
|
pr_info("Process: %d (%d children)\n",
|
|
|
|
item->pid, item->nr_children);
|
|
|
|
|
|
|
|
e.pid = item->pid;
|
2012-04-11 22:10:09 +04:00
|
|
|
e.pgid = item->pgid;
|
|
|
|
e.sid = item->sid;
|
2011-09-23 12:00:45 +04:00
|
|
|
e.nr_children = item->nr_children;
|
2011-10-20 17:19:26 +04:00
|
|
|
e.nr_threads = item->nr_threads;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-21 14:12:00 +04:00
|
|
|
if (write_img(pstree_fd, &e))
|
2012-01-22 20:20:40 +04:00
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-22 20:51:47 +04:00
|
|
|
if (write_img_buf(pstree_fd, item->children,
|
|
|
|
item->nr_children * sizeof(u32)))
|
|
|
|
goto err;
|
2011-10-20 17:19:26 +04:00
|
|
|
|
2012-03-22 20:51:47 +04:00
|
|
|
if (write_img_buf(pstree_fd, item->threads,
|
|
|
|
item->nr_threads * sizeof(u32)))
|
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
pr_info("----------------------------------------\n");
|
2012-03-21 14:12:00 +04:00
|
|
|
close(pstree_fd);
|
2011-09-23 12:00:45 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-26 17:36:44 +04:00
|
|
|
static int dump_task_thread(struct parasite_ctl *parasite_ctl, pid_t pid)
|
2011-10-20 17:19:26 +04:00
|
|
|
{
|
2012-03-26 17:36:44 +04:00
|
|
|
struct core_entry *core;
|
|
|
|
int ret = -1, fd_core;
|
2012-02-21 09:25:17 +03:00
|
|
|
unsigned int *taddr;
|
2011-10-20 17:19:26 +04:00
|
|
|
|
|
|
|
pr_info("\n");
|
|
|
|
pr_info("Dumping core for thread (pid: %d)\n", pid);
|
|
|
|
pr_info("----------------------------------------\n");
|
|
|
|
|
2012-03-26 17:36:44 +04:00
|
|
|
core = xzalloc(sizeof(*core));
|
2011-10-20 17:19:26 +04:00
|
|
|
if (!core)
|
|
|
|
goto err;
|
|
|
|
|
2012-02-17 22:56:36 +04:00
|
|
|
ret = get_task_regs(pid, core, NULL);
|
2011-10-20 17:19:26 +04:00
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
2012-02-21 09:25:17 +03:00
|
|
|
|
|
|
|
ret = parasite_dump_tid_addr_seized(parasite_ctl, pid, &taddr);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump tid address for pid %d", pid);
|
2012-03-26 17:36:44 +04:00
|
|
|
goto err_free;
|
2012-02-15 18:36:32 +04:00
|
|
|
}
|
2012-02-21 09:25:17 +03:00
|
|
|
|
|
|
|
pr_info("%d: tid_address=%p\n", pid, taddr);
|
|
|
|
core->clear_tid_address = (u64) taddr;
|
|
|
|
|
2011-10-20 17:19:26 +04:00
|
|
|
pr_info("OK\n");
|
|
|
|
|
2012-01-22 20:24:04 +04:00
|
|
|
core->tc.task_state = TASK_ALIVE;
|
|
|
|
core->tc.exit_code = 0;
|
|
|
|
|
2012-03-27 15:20:38 +04:00
|
|
|
fd_core = open_image(CR_FD_CORE, O_DUMP, pid);
|
2012-03-26 17:36:44 +04:00
|
|
|
if (fd_core < 0)
|
|
|
|
goto err_free;
|
|
|
|
|
|
|
|
ret = dump_task_core(core, fd_core);
|
2011-10-20 17:19:26 +04:00
|
|
|
|
2012-03-26 17:36:44 +04:00
|
|
|
close(fd_core);
|
2011-10-20 17:19:26 +04:00
|
|
|
err_free:
|
|
|
|
free(core);
|
|
|
|
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
|
|
|
{
|
|
|
|
struct core_entry *core;
|
2012-03-26 17:36:44 +04:00
|
|
|
int ret = -1, fd_core;
|
2012-01-22 20:28:30 +04:00
|
|
|
|
|
|
|
core = xzalloc(sizeof(*core));
|
|
|
|
if (core == NULL)
|
2012-03-26 17:36:44 +04:00
|
|
|
goto err;
|
2012-01-22 20:28:30 +04:00
|
|
|
|
|
|
|
core->tc.task_state = TASK_DEAD;
|
|
|
|
core->tc.exit_code = pps->exit_code;
|
|
|
|
|
2012-03-27 15:20:38 +04:00
|
|
|
fd_core = open_image(CR_FD_CORE, O_DUMP, item->pid);
|
2012-03-26 17:36:44 +04:00
|
|
|
if (fd_core < 0)
|
|
|
|
goto err_free;
|
|
|
|
|
|
|
|
ret = dump_task_core(core, fd_core);
|
|
|
|
close(fd_core);
|
|
|
|
err_free:
|
|
|
|
xfree(core);
|
|
|
|
err:
|
|
|
|
return ret;
|
2012-01-22 20:28:30 +04:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (item->nr_threads == 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < item->nr_threads; i++) {
|
|
|
|
/* Leader is already dumped */
|
|
|
|
if (item->pid == item->threads[i])
|
|
|
|
continue;
|
|
|
|
|
2012-03-26 17:36:44 +04:00
|
|
|
if (dump_task_thread(parasite_ctl, item->threads[i]))
|
|
|
|
return -1;
|
2012-01-22 20:13:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-26 17:40:18 +04:00
|
|
|
static int dump_one_task(const struct pstree_item *item)
|
2012-01-22 20:13:59 +04:00
|
|
|
{
|
|
|
|
pid_t pid = item->pid;
|
2011-10-21 12:14:45 +04:00
|
|
|
LIST_HEAD(vma_area_list);
|
|
|
|
struct parasite_ctl *parasite_ctl;
|
2012-01-12 23:50:45 +04:00
|
|
|
int ret = -1;
|
2012-01-27 21:35:59 +04:00
|
|
|
struct parasite_dump_misc misc;
|
2012-03-26 22:58:22 +04:00
|
|
|
struct cr_fdset *cr_fdset = NULL;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-28 17:36:00 +04:00
|
|
|
int nr_fds = PARASITE_MAX_FDS;
|
|
|
|
int *fds = NULL;
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
pr_info("========================================\n");
|
|
|
|
pr_info("Dumping task (pid: %d)\n", pid);
|
|
|
|
pr_info("========================================\n");
|
|
|
|
|
2012-03-28 17:36:00 +04:00
|
|
|
fds = xmalloc(nr_fds * sizeof(int));
|
|
|
|
if (!fds)
|
|
|
|
goto err_free;
|
|
|
|
|
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 (item->state == TASK_STOPPED) {
|
|
|
|
pr_err("Stopped tasks are not supported\n");
|
2012-02-17 01:39:32 +04:00
|
|
|
goto err_free;
|
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-01-22 20:12:44 +04:00
|
|
|
pr_info("Obtainting 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;
|
|
|
|
|
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 (item->state == TASK_DEAD)
|
2012-03-26 17:36:44 +04:00
|
|
|
return dump_one_zombie(item, &pps_buf);
|
2012-01-22 20:24:47 +04:00
|
|
|
|
2012-01-31 18:26:51 +04:00
|
|
|
ret = -1;
|
2012-03-26 17:45:08 +04:00
|
|
|
cr_fdset = cr_task_fdset_open(item->pid, O_DUMP);
|
2012-03-26 17:40:18 +04:00
|
|
|
if (!cr_fdset)
|
2012-01-22 20:15:11 +04:00
|
|
|
goto err;
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
ret = collect_mappings(pid, &vma_area_list);
|
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;
|
|
|
|
}
|
|
|
|
|
2012-03-28 17:36:00 +04:00
|
|
|
ret = collect_fds(pid, fds, &nr_fds);
|
2012-02-29 16:06:48 +03:00
|
|
|
if (ret) {
|
2012-03-28 17:36:00 +04:00
|
|
|
pr_err("Collect fds (pid: %d) failed with %d\n", pid, ret);
|
2012-02-29 16:06:48 +03:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
parasite_ctl = parasite_infect_seized(pid, &vma_area_list);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (!parasite_ctl) {
|
2012-04-07 00:22:00 +04:00
|
|
|
ret = -1;
|
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;
|
|
|
|
}
|
|
|
|
|
2012-03-29 16:44:15 +04:00
|
|
|
ret = dump_task_files_seized(parasite_ctl, cr_fdset, fds, nr_fds);
|
2012-03-28 17:36:00 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump files (pid: %d) failed with %d\n", pid, ret);
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2012-03-28 17:36:00 +04:00
|
|
|
}
|
|
|
|
|
2012-01-11 13:30:38 +04:00
|
|
|
ret = parasite_dump_pages_seized(parasite_ctl, &vma_area_list, cr_fdset);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Can't dump pages (pid: %d) with parasite\n", pid);
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2011-11-29 15:12:25 +03:00
|
|
|
ret = parasite_dump_sigacts_seized(parasite_ctl, cr_fdset);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-01-24 16:45:19 +04:00
|
|
|
ret = parasite_dump_itimers_seized(parasite_ctl, cr_fdset);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-01-27 21:35:59 +04:00
|
|
|
ret = parasite_dump_misc_seized(parasite_ctl, &misc);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump misc (pid: %d)\n", pid);
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2012-01-27 21:35:59 +04:00
|
|
|
}
|
|
|
|
|
2012-02-17 22:56:36 +04:00
|
|
|
ret = dump_task_core_all(pid, &pps_buf, &misc, parasite_ctl, cr_fdset);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-02-21 09:25:17 +03:00
|
|
|
ret = dump_task_threads(parasite_ctl, item);
|
2012-02-17 22:56:36 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't dump threads\n");
|
2012-04-11 15:25:03 +04:00
|
|
|
goto err_cure;
|
2012-02-17 22:56:36 +04:00
|
|
|
}
|
|
|
|
|
2012-02-15 18:00:50 +04:00
|
|
|
ret = parasite_cure_seized(parasite_ctl);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Can't cure (pid: %d) from parasite\n", pid);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2011-10-21 12:14:45 +04:00
|
|
|
ret = dump_task_mappings(pid, &vma_area_list, cr_fdset);
|
2011-09-23 12:00:45 +04:00
|
|
|
if (ret) {
|
2011-09-30 14:37:12 +04:00
|
|
|
pr_err("Dump mappings (pid: %d) failed with %d\n", pid, ret);
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
ret = dump_task_creds(pid, &misc, cr_fdset);
|
2012-01-27 21:43:32 +04:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump creds (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-04-09 13:41:05 +04:00
|
|
|
|
|
|
|
ret = dump_task_fs(pid, cr_fdset);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Dump fs (pid: %d) failed with %d\n", pid, ret);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
err:
|
2012-03-26 22:58:22 +04:00
|
|
|
close_cr_fdset(&cr_fdset);
|
2012-02-17 01:39:36 +04:00
|
|
|
close_pid_proc();
|
2012-02-17 01:39:32 +04:00
|
|
|
err_free:
|
2011-10-21 12:14:45 +04:00
|
|
|
free_mappings(&vma_area_list);
|
2012-03-28 17:36:00 +04:00
|
|
|
xfree(fds);
|
2011-09-23 12:00:45 +04:00
|
|
|
return ret;
|
2012-04-11 15:25:03 +04:00
|
|
|
|
|
|
|
err_cure:
|
|
|
|
parasite_cure_seized(parasite_ctl);
|
|
|
|
goto err;
|
2011-09-23 12:00:45 +04:00
|
|
|
}
|
|
|
|
|
2012-03-06 14:20:00 +04:00
|
|
|
int cr_dump_tasks(pid_t pid, const struct cr_options *opts)
|
2011-09-23 12:00:45 +04:00
|
|
|
{
|
2011-10-21 11:46:19 +04:00
|
|
|
LIST_HEAD(pstree_list);
|
2011-09-23 12:00:45 +04:00
|
|
|
struct pstree_item *item;
|
2012-02-17 01:39:35 +04:00
|
|
|
int i, ret = -1;
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2011-10-06 14:07:48 +04:00
|
|
|
pr_info("========================================\n");
|
2012-03-30 15:50:00 +04:00
|
|
|
pr_info("Dumping process %s(pid: %d)\n", !opts->leader_only ? "group " : "", pid);
|
2011-10-06 14:07:48 +04:00
|
|
|
pr_info("========================================\n");
|
2011-09-23 12:00:45 +04:00
|
|
|
|
2012-03-21 14:12:00 +04:00
|
|
|
if (collect_dump_pstree(pid, &pstree_list, opts))
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
|
2012-01-31 22:28:22 +04:00
|
|
|
if (opts->namespaces_flags) {
|
2012-03-13 18:11:00 +04:00
|
|
|
if (dump_namespaces(pid, opts->namespaces_flags) < 0)
|
2012-01-26 15:27:00 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-01-19 19:16:55 +04:00
|
|
|
/*
|
|
|
|
* Ignore collection errors by now since we may not want
|
|
|
|
* to dump the missed sockets. But later, when we will start
|
|
|
|
* dumping containers, we'll better fail here, rather than
|
|
|
|
* in the dump stage
|
|
|
|
*/
|
|
|
|
|
|
|
|
collect_sockets();
|
2011-12-26 22:12:03 +04:00
|
|
|
|
2012-03-26 17:47:38 +04:00
|
|
|
glob_fdset = cr_glob_fdset_open(O_DUMP);
|
|
|
|
if (!glob_fdset)
|
2012-03-26 17:38:59 +04:00
|
|
|
goto err;
|
|
|
|
|
2012-05-03 18:07:01 +04:00
|
|
|
if (init_shmem_dump())
|
2012-03-21 10:12:00 +04:00
|
|
|
goto err;
|
2012-05-03 17:36:00 +04:00
|
|
|
|
|
|
|
if (init_pipes_dump())
|
2012-04-05 20:02:00 +04:00
|
|
|
goto err;
|
2012-03-21 10:12:00 +04:00
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
list_for_each_entry(item, &pstree_list, list) {
|
2012-03-26 17:40:18 +04:00
|
|
|
if (dump_one_task(item))
|
2011-09-23 12:00:45 +04:00
|
|
|
goto err;
|
|
|
|
|
2011-10-04 01:50:19 +04:00
|
|
|
if (opts->leader_only)
|
2011-09-23 12:00:45 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
fd_id_show_tree();
|
2011-09-23 12:00:45 +04:00
|
|
|
err:
|
2012-05-03 18:07:01 +04:00
|
|
|
fini_shmem_dump();
|
2012-05-03 17:36:00 +04:00
|
|
|
fini_pipes_dump();
|
2012-03-26 17:47:38 +04:00
|
|
|
close_cr_fdset(&glob_fdset);
|
2012-04-28 17:38:46 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we've failed to do anything -- unlock all TCP sockets
|
|
|
|
* so that the connections can go on. But if we succeeded --
|
|
|
|
* don't, just close them silently.
|
|
|
|
*/
|
|
|
|
if (ret)
|
|
|
|
tcp_unlock_all();
|
2012-04-11 15:17:26 +04:00
|
|
|
pstree_switch_state(&pstree_list,
|
|
|
|
ret ? TASK_ALIVE : opts->final_state);
|
2011-10-21 11:46:19 +04:00
|
|
|
free_pstree(&pstree_list);
|
2011-10-24 23:01:42 +04:00
|
|
|
|
2011-09-23 12:00:45 +04:00
|
|
|
return ret;
|
|
|
|
}
|