2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-23 02:17:22 +00:00
criu/cr-show.c

545 lines
11 KiB
C
Raw Normal View History

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "asm/types.h"
#include "list.h"
#include "fdset.h"
#include "namespaces.h"
#include "compiler.h"
#include "cr_options.h"
#include "util.h"
Unix sockets initial support Currently it can only work with stream sockets, which have no skbs in queues (listening or established -- both work OK). The cpt part uses the sock_diag engine that was merged to Dave recently to collect sockets. Then it dumps sockets by checking the filesystem ID of a failed-to-open through /proc/pid/fd descriptors (sockets do not allow for such tricks with opens through proc) against SOCKFS_TYPE. The rst part is more tricky. Listen sockets are just restored, this is simple. Connected sockets are restored like this: 1. One end establishes a listening anon socket at the desired descriptor; 2. The other end just creates a socket at the desired descriptor; 3. All sockets, that are to be connect()-ed call connect. Unix sockets do not block connect() till the accept() time and thus we continue with... 4. ... all listening sockets call accept() and ... dup2 the new fd into the accepting end. There's a problem with this approach -- socket names are not preserved, but looking into our OpenVZ implementation I think this is OK for existing apps. What should be done next is: 1. Need to merge the file IDs patches in our tree and make Andrey to support files sharing. This will solve the sk = socket(); fork(); case. Currently it simply doesn't work :( 2. Need to add support for DGRAM sockets -- I wrote comment how to do it in the can_dump_unix_sk() 3. Need to add support for in-flight connections 4. Implement support for UDP sockets (quite simple) 5. Implement support for listening TCP sockets (also not very complex) 6. Implement support for connected TCP scokets (hard one, Tejun's patches are not very good for this from my POV) Cyrill, plz, apply this patch and put the above descriptions onto wiki docs (do we have the plans page yet?). Andrey, plz, take care of unix sockets tests in zdtm. Most likely it won't work till you do the shared files support for sockets. Signed-off-by: Pavel Emelyanov <xemul@parallels.com> Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2011-12-26 22:12:03 +04:00
#include "sockets.h"
#include "image.h"
#include "uts_ns.h"
#include "ipc_ns.h"
#include "pstree.h"
#include "cr-show.h"
#include "crtools.h"
#include "protobuf.h"
#include "protobuf/pstree.pb-c.h"
#include "protobuf/pipe-data.pb-c.h"
#include "protobuf/siginfo.pb-c.h"
#define DEF_PAGES_PER_LINE 6
static LIST_HEAD(pstree_list);
static void pipe_data_handler(int fd, void *obj)
{
PipeDataEntry *e = obj;
print_image_data(fd, e->bytes, opts.show_pages_content);
}
static int nice_width_for(unsigned long addr)
{
int ret = 3;
while (addr) {
addr >>= 4;
ret++;
}
return ret;
}
static inline void pr_xdigi(unsigned char *data, size_t len, int pos)
{
if (pos < len)
pr_msg("%02x ", data[pos]);
else
pr_msg(" ");
}
static inline void pr_xsym(unsigned char *data, size_t len, int pos)
{
char sym;
if (pos < len)
sym = data[pos];
else
sym = ' ';
pr_msg("%c", isprint(sym) ? sym : '.');
}
void print_data(unsigned long addr, unsigned char *data, size_t size)
{
int i, j, addr_len;
unsigned zero_line = 0;
addr_len = nice_width_for(addr + size);
for (i = 0; i < size; i += 16) {
if (*(u64 *)(data + i) == 0 && *(u64 *)(data + i + 8) == 0) {
if (zero_line == 0)
zero_line = 1;
else {
if (zero_line == 1) {
pr_msg("*\n");
zero_line = 2;
}
continue;
}
} else
zero_line = 0;
pr_msg("%#0*lx: ", addr_len, addr + i);
for (j = 0; j < 8; j++)
pr_xdigi(data, size, i + j);
pr_msg(" ");
for (j = 8; j < 16; j++)
pr_xdigi(data, size, i + j);
pr_msg(" |");
for (j = 0; j < 8; j++)
pr_xsym(data, size, i + j);
pr_msg(" ");
for (j = 8; j < 16; j++)
pr_xsym(data, size, i + j);
pr_msg("|\n");
}
}
void print_image_data(int fd, unsigned int length, int show)
{
void *data;
int ret;
if (!show) {
lseek(fd, length, SEEK_CUR);
return;
}
pr_msg("\n");
data = xmalloc(length);
if (!data)
return;
ret = read_img_buf(fd, (unsigned char *)data, length);
if (ret < 0) {
xfree(data);
return;
}
print_data(0, (unsigned char *)data, length);
xfree(data);
}
static void show_pagemaps(int fd, void *obj)
{
pb_show_plain_pretty(fd, PB_PAGEMAP, "nr_pages:%u");
}
void show_siginfo(int fd)
{
int ret;
pr_img_head(CR_FD_SIGNAL);
while (1) {
SiginfoEntry *sie;
siginfo_t *info;
ret = pb_read_one_eof(fd, &sie, PB_SIGINFO);
if (ret <= 0)
break;
info = (siginfo_t *) sie->siginfo.data;
pr_msg("signal: si_signo=%d si_code=%x\n",
info->si_signo, info->si_code);
siginfo_entry__free_unpacked(sie, NULL);
}
pr_img_tail(CR_FD_SIGNAL);
}
static int pstree_item_from_pb(PstreeEntry *e, struct pstree_item *item)
{
int i;
item->pid.virt = e->pid;
item->nr_threads = e->n_threads;
item->threads = xzalloc(sizeof(struct pid) * e->n_threads);
if (!item->threads)
return -1;
for (i = 0; i < item->nr_threads; i++)
item->threads[i].virt = e->threads[i];
return 0;
}
static void pstree_handler(int fd, void *obj)
{
PstreeEntry *e = obj;
struct pstree_item *item = NULL;
item = xzalloc(sizeof(struct pstree_item));
if (!item)
return;
if (pstree_item_from_pb(e, item)) {
xfree(item);
return;
}
list_add_tail(&item->sibling, &pstree_list);
}
void show_collect_pstree(int fd, int collect)
{
pb_show_plain_payload_pretty(fd, PB_PSTREE,
collect ? pstree_handler : NULL, "*:%d");
}
static inline char *task_state_str(int state)
{
switch (state) {
case TASK_ALIVE:
return "running/sleeping";
case TASK_DEAD:
return "zombie";
default:
return "UNKNOWN";
}
}
static void show_core_regs(UserX86RegsEntry *regs)
{
#define pr_regs4(s, n1, n2, n3, n4) \
pr_msg("\t%8s: 0x%-16"PRIx64" " \
"%8s: 0x%-16"PRIx64" " \
"%8s: 0x%-16"PRIx64" " \
"%8s: 0x%-16"PRIx64"\n", \
#n1, s->n1, \
#n2, s->n2, \
#n3, s->n3, \
#n4, s->n4)
#define pr_regs3(s, n1, n2, n3) \
pr_msg("\t%8s: 0x%-16"PRIx64" " \
"%8s: 0x%-16"PRIx64" " \
"%8s: 0x%-16"PRIx64"\n", \
#n1, s->n1, \
#n2, s->n2, \
#n3, s->n3)
pr_msg("\t---[ GP registers set ]---\n");
pr_regs4(regs, cs, ip, ds, es);
pr_regs4(regs, ss, sp, fs, gs);
pr_regs4(regs, di, si, dx, cx);
pr_regs4(regs, ax, r8, r9, r10);
pr_regs4(regs, r11, r12, r13, r14);
pr_regs3(regs, r15, bp, bx);
pr_regs4(regs, orig_ax, flags, fs_base, gs_base);
pr_msg("\n");
}
void show_thread_info(ThreadInfoX86 *thread_info)
{
if (!thread_info)
return;
pr_msg("\t---[ Thread info ]---\n");
pr_msg("\tclear_tid_addr: 0x%"PRIx64"\n", thread_info->clear_tid_addr);
pr_msg("\n");
show_core_regs(thread_info->gpregs);
}
static struct {
u32 magic;
u32 mask;
char *hint;
} magic_hints[] = {
{ .magic = 0x45311224, .mask = 0xffffffff, .hint = "ip route dump", },
{ .magic = 0x47361222, .mask = 0xffffffff, .hint = "ip ifaddr dump", },
{ .magic = 0x00008b1f, .mask = 0x0000ffff, .hint = "gzip file", },
{ },
};
static void try_hint_magic(u32 magic)
{
int i;
for (i = 0; magic_hints[i].hint != 0; i++)
if ((magic & magic_hints[i].mask) == magic_hints[i].magic)
pr_msg("This can be %s\n", magic_hints[i].hint);
}
#define SHOW_PLAIN(name) { name##_MAGIC, PB_##name, false, NULL, NULL, }
/* nothing special behind this -S, just to avoid heavy patching */
#define SHOW_PLAINS(name) { name##S_MAGIC, PB_##name, false, NULL, NULL, }
#define SHOW_VERT(name) { name##_MAGIC, PB_##name, true, NULL, NULL, }
static struct show_image_info show_infos[] = {
SHOW_VERT(INVENTORY),
SHOW_VERT(CORE),
SHOW_VERT(IDS),
SHOW_VERT(CREDS),
SHOW_VERT(UTSNS),
SHOW_VERT(IPC_VAR),
SHOW_VERT(FS),
SHOW_VERT(GHOST_FILE),
SHOW_VERT(MM),
SHOW_PLAINS(REG_FILE),
SHOW_PLAINS(NS_FILE),
SHOW_PLAIN(EVENTFD_FILE),
SHOW_PLAIN(EVENTPOLL_FILE),
SHOW_PLAIN(EVENTPOLL_TFD),
SHOW_PLAIN(SIGNALFD),
SHOW_PLAIN(INOTIFY_FILE),
SHOW_PLAIN(INOTIFY_WD),
SHOW_PLAIN(FANOTIFY_FILE),
SHOW_PLAIN(FANOTIFY_MARK),
SHOW_PLAINS(VMA),
SHOW_PLAINS(PIPE),
SHOW_PLAIN(FIFO),
SHOW_PLAIN(SIGACT),
SHOW_PLAIN(NETLINK_SK),
SHOW_PLAIN(REMAP_FPATH),
SHOW_PLAINS(MNT),
SHOW_PLAINS(TTY_FILE),
SHOW_PLAIN(TTY_INFO),
SHOW_PLAINS(FILE_LOCK),
SHOW_PLAIN(RLIMIT),
SHOW_PLAIN(TUNFILE),
SHOW_PLAINS(EXT_FILE),
{ TCP_STREAM_MAGIC, PB_TCP_STREAM, true, show_tcp_stream, "1:%u 2:%u 3:%u 4:%u 12:%u", },
{ STATS_MAGIC, PB_STATS, true, NULL, "1.1:%u 1.2:%u 1.3:%u 1.4:%u 1.5:%Lu 1.6:%Lu 1.7:%Lu", },
{ FDINFO_MAGIC, PB_FDINFO, false, NULL, "flags:%#o fd:%d", },
{ UNIXSK_MAGIC, PB_UNIX_SK, false, NULL, "1:%#x 2:%#x 3:%d 4:%d 5:%d 6:%d 7:%d 8:%#x 11:S", },
{ INETSK_MAGIC, PB_INET_SK, false, NULL, "1:%#x 2:%#x 3:%d 4:%d 5:%d 6:%d 7:%d 8:%d 9:%2x 11:A 12:A", },
{ PACKETSK_MAGIC, PB_PACKET_SOCK, false, NULL, "5:%d", },
{ ITIMERS_MAGIC, PB_ITIMER, false, NULL, "*:%Lu", },
{ POSIX_TIMERS_MAGIC, PB_POSIX_TIMER, false, NULL, "*:%d 5:%Lu 7:%Lu 8:%lu 9:%Lu 10:%Lu", },
{ NETDEV_MAGIC, PB_NETDEV, false, NULL, "2:%d", },
{ PAGEMAP_MAGIC, PB_PAGEMAP_HEAD, true, show_pagemaps, NULL, },
{ PIPES_DATA_MAGIC, PB_PIPE_DATA, false, pipe_data_handler, NULL, },
{ FIFO_DATA_MAGIC, PB_PIPE_DATA, false, pipe_data_handler, NULL, },
{ SK_QUEUES_MAGIC, PB_SK_QUEUES, false, sk_queue_data_handler, NULL, },
{ IPCNS_SHM_MAGIC, PB_IPC_SHM, false, ipc_shm_handler, NULL, },
{ IPCNS_SEM_MAGIC, PB_IPC_SEM, false, ipc_sem_handler, NULL, },
{ IPCNS_MSG_MAGIC, PB_IPCNS_MSG_ENT, false, ipc_msg_handler, NULL, },
{ }
};
static int cr_parse_file(void)
{
u32 magic;
int ret = -1, fd;
fd = open(opts.show_dump_file, O_RDONLY);
if (fd < 0) {
pr_perror("Can't open %s", opts.show_dump_file);
goto out;
}
if (read_img(fd, &magic) < 0)
goto out;
ret = cr_parse_fd(fd, magic);
out:
close_safe(&fd);
return ret;
}
int cr_parse_fd(int fd, u32 magic)
{
int ret = 0, i;
if (magic == PSTREE_MAGIC) {
show_collect_pstree(fd, 0);
goto out;
}
if (magic == SIGNAL_MAGIC || magic == PSIGNAL_MAGIC) {
show_siginfo(fd);
goto out;
}
for (i = 0; show_infos[i].magic; i++) {
struct show_image_info *si;
si = &show_infos[i];
if (si->magic != magic)
continue;
do_pb_show_plain(fd, si->pb_type, si->single,
si->payload, si->fmt);
goto out;
}
ret = -1;
pr_err("Unknown magic %#x in %s\n",
magic, opts.show_dump_file);
try_hint_magic(magic);
out:
return ret;
}
static int cr_show_pstree_item(struct pstree_item *item)
{
int ret = -1, i;
struct cr_fdset *cr_fdset = NULL;
TaskKobjIdsEntry *ids;
cr_fdset = cr_task_fdset_open(item->pid.virt, O_SHOW);
if (!cr_fdset)
goto out;
pr_msg("Task %d:\n", item->pid.virt);
pr_msg("----------------------------------------\n");
cr_parse_fd(fdset_fd(cr_fdset, CR_FD_CORE), CORE_MAGIC);
if (item->nr_threads > 1) {
int fd_th;
for (i = 0; i < item->nr_threads; i++) {
if (item->threads[i].virt == item->pid.virt)
continue;
fd_th = open_image(CR_FD_CORE, O_SHOW, item->threads[i].virt);
if (fd_th < 0)
goto outc;
pr_msg("Thread %d.%d:\n", item->pid.virt, item->threads[i].virt);
pr_msg("----------------------------------------\n");
cr_parse_fd(fd_th, CORE_MAGIC);
close_safe(&fd_th);
}
}
pr_msg("Resources for %d:\n", item->pid.virt);
pr_msg("----------------------------------------\n");
for (i = _CR_FD_TASK_FROM + 1; i < _CR_FD_TASK_TO; i++)
if ((i != CR_FD_CORE) && (i != CR_FD_IDS)) {
pr_msg("* ");
pr_msg(fdset_template[i].fmt, item->pid.virt);
pr_msg(":\n");
cr_parse_fd(fdset_fd(cr_fdset, i), fdset_template[i].magic);
}
if (pb_read_one(fdset_fd(cr_fdset, CR_FD_IDS), &ids, PB_IDS) > 0) {
i = open_image(CR_FD_FDINFO, O_SHOW, ids->files_id);
if (i >= 0) {
pr_msg("* ");
pr_msg(fdset_template[CR_FD_FDINFO].fmt, ids->files_id);
pr_msg(":\n");
cr_parse_fd(i, FDINFO_MAGIC);
close(i);
}
task_kobj_ids_entry__free_unpacked(ids, NULL);
}
pr_msg("---[ end of task %d ]---\n", item->pid.virt);
ret = 0;
outc:
close_cr_fdset(&cr_fdset);
out:
return ret;
}
static int cr_show_pid(int pid)
{
int fd, ret;
struct pstree_item item;
fd = open_image(CR_FD_PSTREE, O_SHOW);
if (fd < 0)
return -1;
while (1) {
PstreeEntry *pe;
ret = pb_read_one_eof(fd, &pe, PB_PSTREE);
if (ret <= 0){
close(fd);
return ret;
}
if (pe->pid == pid) {
pstree_item_from_pb(pe, &item);
pstree_entry__free_unpacked(pe, NULL);
break;
}
pstree_entry__free_unpacked(pe, NULL);
}
close(fd);
return cr_show_pstree_item(&item);
}
static int cr_show_all(void)
{
struct pstree_item *item = NULL, *tmp;
int ret = -1, fd, pid;
fd = open_image(CR_FD_PSTREE, O_SHOW);
if (fd < 0)
goto out;
show_collect_pstree(fd, 1);
close(fd);
fd = open_image(CR_FD_SK_QUEUES, O_SHOW);
if (fd < 0)
goto out;
close(fd);
pid = list_first_entry(&pstree_list, struct pstree_item, sibling)->pid.virt;
ret = try_show_namespaces(pid);
if (ret)
goto out;
list_for_each_entry(item, &pstree_list, sibling)
if (cr_show_pstree_item(item))
break;
out:
list_for_each_entry_safe(item, tmp, &pstree_list, sibling) {
list_del(&item->sibling);
xfree(item->threads);
xfree(item);
}
return ret;
}
int cr_show(int pid)
{
if (opts.show_dump_file)
return cr_parse_file();
if (pid)
return cr_show_pid(pid);
return cr_show_all();
}