From 3d886be2c66992f885e46ed970e22bb7fb343b64 Mon Sep 17 00:00:00 2001 From: Kinsbursky Stanislav Date: Thu, 9 Feb 2012 12:09:41 +0300 Subject: [PATCH] IPC: dump shared memory Signed-off-by: Stanislav Kinsbursky Acked-by: Pavel Emelyanov Signed-off-by: Cyrill Gorcunov --- crtools.c | 6 +++ include/crtools.h | 5 +- include/image.h | 17 +++++++ ipc_ns.c | 118 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 135 insertions(+), 11 deletions(-) diff --git a/crtools.c b/crtools.c index 6f6c25488..c43eeabcc 100644 --- a/crtools.c +++ b/crtools.c @@ -116,6 +116,12 @@ struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX] = { .fmt = FMT_FNAME_IPCNS_VAR, .magic = IPCNS_VAR_MAGIC, }, + + /* IPC namespace shared memory */ + [CR_FD_IPCNS_SHM] = { + .fmt = FMT_FNAME_IPCNS_SHM, + .magic = IPCNS_SHM_MAGIC, + }, }; static struct cr_fdset *alloc_cr_fdset(void) diff --git a/include/crtools.h b/include/crtools.h index 868ab3889..cf7a46b8a 100644 --- a/include/crtools.h +++ b/include/crtools.h @@ -38,6 +38,7 @@ enum { CR_FD_PSTREE, CR_FD_UTSNS, CR_FD_IPCNS_VAR, + CR_FD_IPCNS_SHM, CR_FD_MAX }; @@ -80,6 +81,7 @@ extern struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX]; #define FMT_FNAME_CREDS "creds-%d.img" #define FMT_FNAME_UTSNS "utsns-%d.img" #define FMT_FNAME_IPCNS_VAR "ipcns-var-%d.img" +#define FMT_FNAME_IPCNS_SHM "ipcns-shm-%d.img" extern int get_image_path(char *path, int size, const char *fmt, int pid); @@ -111,7 +113,8 @@ struct cr_fdset { CR_FD_DESC_USE(CR_FD_CREDS) ) #define CR_FD_DESC_NS (\ CR_FD_DESC_USE(CR_FD_UTSNS) |\ - CR_FD_DESC_USE(CR_FD_IPCNS_VAR) ) + CR_FD_DESC_USE(CR_FD_IPCNS_VAR) |\ + CR_FD_DESC_USE(CR_FD_IPCNS_SHM) ) #define CR_FD_DESC_NONE (0) int cr_dump_tasks(pid_t pid, struct cr_options *opts); diff --git a/include/image.h b/include/image.h index 84712a95c..01a3b86f5 100644 --- a/include/image.h +++ b/include/image.h @@ -22,6 +22,7 @@ #define UTSNS_MAGIC 0x54473203 /* Smolensk */ #define CREDS_MAGIC 0x54023547 /* Kozelsk */ #define IPCNS_VAR_MAGIC 0x53115007 /* Samara */ +#define IPCNS_SHM_MAGIC 0x46283044 /* Odessa */ #define PIPEFS_MAGIC 0x50495045 @@ -125,6 +126,22 @@ struct ipc_var_entry { u32 mq_msgsize_max; } __packed; +struct ipc_seg { + u32 key; + u32 uid; + u32 gid; + u32 cuid; + u32 cgid; + u32 mode; + u32 id; + u8 pad[4]; +} __packed; + +struct ipc_shm_entry { + struct ipc_seg seg; + u64 size; +} __packed; + #define VMA_AREA_NONE (0 << 0) #define VMA_AREA_REGULAR (1 << 0) /* Dumpable area */ #define VMA_AREA_STACK (1 << 1) diff --git a/ipc_ns.c b/ipc_ns.c index de51715b9..0ad2a6760 100644 --- a/ipc_ns.c +++ b/ipc_ns.c @@ -13,6 +13,36 @@ #include "namespaces.h" #include "sysctl.h" +static void print_ipc_seg(const struct ipc_seg *seg) +{ + pr_info("id: %-10d key: 0x%08x ", seg->id, seg->key); + pr_info("uid: %-10d gid: %-10d ", seg->uid, seg->gid); + pr_info("cuid: %-10d cgid: %-10d ", seg->cuid, seg->cgid); + pr_info("mode: %-10o ", seg->mode); +} + +static void fill_ipc_seg(int id, struct ipc_seg *seg, const struct ipc_perm *ipcp) +{ +#if defined (__GLIBC__) && __GLIBC__ >= 2 +#define KEY __key +#else +#define KEY key +#endif + seg->id = id; + seg->key = ipcp->KEY; + seg->uid = ipcp->uid; + seg->gid = ipcp->gid; + seg->cuid = ipcp->cuid; + seg->cgid = ipcp->cgid; + seg->mode = ipcp->mode; +} + +static void print_ipc_shm(const struct ipc_shm_entry *shm) +{ + print_ipc_seg(&shm->seg); + pr_info("size: %-10lu\n", shm->size); +} + static int ipc_sysctl_req(struct ipc_var_entry *e, int op) { struct sysctl_req req[] = { @@ -71,21 +101,89 @@ static int dump_ipc_sem(void *data) return 0; } -static int dump_ipc_shm(void *data) +/* + * TODO: Function below should be later improved to locate and dump only dirty + * pages via updated sys_mincore(). + */ +static int dump_ipc_shm_pages(int fd, const struct ipc_shm_entry *shm) { - int fd; + void *data; int ret; - struct shmid_ds shmid; - ret = shmctl(0, IPC_INFO, &shmid); - if (ret < 0) - pr_perror("semctl failed"); + data = shmat(shm->seg.id, NULL, SHM_RDONLY); + if (data == (void *)-1) { + pr_perror("Failed to attach IPC shared memory"); + return -errno; + } + ret = write_img_buf(fd, data, round_up(shm->size, sizeof(u32))); + if (ret < 0) { + pr_err("Failed to write IPC shared memory data\n"); + return ret; + } + if (shmdt(data)) { + pr_perror("Failed to detach IPC shared memory"); + return -errno; + } + return 0; +} - if (ret) { - pr_err("IPC shared memory migration is not supported yet\n"); - return -EINVAL; +static int dump_ipc_shm_seg(int fd, int id, const struct shmid_ds *ds) +{ + struct ipc_shm_entry shm; + int ret; + + fill_ipc_seg(id, &shm.seg, &ds->shm_perm); + shm.size = ds->shm_segsz; + print_ipc_shm(&shm); + + ret = write_img(fd, &shm); + if (ret < 0) { + pr_err("Failed to write IPC shared memory segment\n"); + return ret; + } + return dump_ipc_shm_pages(fd, &shm); +} + +static int dump_ipc_shm(int fd) +{ + int i, maxid, slot; + struct shm_info info; + + maxid = shmctl(0, SHM_INFO, (void *)&info); + if (maxid < 0) { + pr_perror("shmctl(SHM_INFO) failed"); + return -errno; } + pr_info("IPC shared memory segments: %d\n", info.used_ids); + for (i = 0, slot = 0; i <= maxid; i++) { + struct shmid_ds ds; + int id, ret; + + id = shmctl(i, SHM_STAT, &ds); + if (id < 0) { + if (errno == EINVAL) + continue; + pr_perror("Failed to get stats for IPC shared memory"); + break; + } + + if (ds.shm_nattch != 0) { + pr_err("Migration of attached IPC shared memory " + "segments is not supported yet\n"); + return -EFAULT; + } + + ret = dump_ipc_shm_seg(fd, id, &ds); + if (ret < 0) + return ret; + slot++; + } + if (slot != info.used_ids) { + pr_err("Failed to collect %d (only %d succeeded)\n", + info.used_ids, slot); + return -EFAULT; + } return 0; } @@ -115,7 +213,7 @@ static int dump_ipc_data(const struct cr_fdset *fdset) ret = dump_ipc_var(fdset->fds[CR_FD_IPCNS_VAR]); if (ret < 0) return ret; - ret = dump_ipc_shm(0); + ret = dump_ipc_shm(fdset->fds[CR_FD_IPCNS_SHM]); if (ret < 0) return ret; ret = dump_ipc_msg(0);