diff --git a/Makefile b/Makefile index 712a7d6b6..dee0e617a 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,7 @@ OBJS += sockets.o OBJS += files.o OBJS += namespaces.o OBJS += uts_ns.o +OBJS += ipc_ns.o OBJS-BLOB += parasite.o SRCS-BLOB += $(patsubst %.o,%.c,$(OBJS-BLOB)) diff --git a/crtools.c b/crtools.c index 05b565976..dd67a4212 100644 --- a/crtools.c +++ b/crtools.c @@ -110,6 +110,12 @@ struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX] = { .fmt = FMT_FNAME_UTSNS, .magic = UTSNS_MAGIC, }, + + /* IPC namespace */ + [CR_FD_IPCNS] = { + .fmt = FMT_FNAME_IPCNS, + .magic = IPCNS_MAGIC, + }, }; static struct cr_fdset *alloc_cr_fdset(void) @@ -262,6 +268,8 @@ static int parse_ns_string(const char *ptr, unsigned int *flags) goto bad_ns; if (!strncmp(ptr, "uts", 3)) opts.namespaces_flags |= CLONE_NEWUTS; + else if (!strncmp(ptr, "ipc", 3)) + opts.namespaces_flags |= CLONE_NEWIPC; else goto bad_ns; ptr += 4; diff --git a/include/crtools.h b/include/crtools.h index 0a9f7efaf..3ec7dddda 100644 --- a/include/crtools.h +++ b/include/crtools.h @@ -37,6 +37,7 @@ enum { CR_FD_PSTREE, CR_FD_UTSNS, + CR_FD_IPCNS, CR_FD_MAX }; @@ -79,6 +80,7 @@ extern struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX]; #define FMT_FNAME_ITIMERS "itimers-%d.img" #define FMT_FNAME_CREDS "creds-%d.img" #define FMT_FNAME_UTSNS "utsns-%d.img" +#define FMT_FNAME_IPCNS "ipcns-%d.img" extern int get_image_path(char *path, int size, const char *fmt, int pid); @@ -109,7 +111,8 @@ struct cr_fdset { CR_FD_DESC_USE(CR_FD_ITIMERS) |\ 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_UTSNS) |\ + CR_FD_DESC_USE(CR_FD_IPCNS) ) #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 1b505ad88..b63e41fa1 100644 --- a/include/image.h +++ b/include/image.h @@ -21,6 +21,7 @@ #define ITIMERS_MAGIC 0x57464056 /* Kostroma */ #define UTSNS_MAGIC 0x54473203 /* Smolensk */ #define CREDS_MAGIC 0x54023547 /* Kozelsk */ +#define IPCNS_MAGIC 0x53115007 /* Samara */ #define PIPEFS_MAGIC 0x50495045 @@ -104,6 +105,22 @@ struct vma_entry { s64 fd; } __packed; +struct ipc_ns_entry { + u32 sem_ctls[4]; + u32 msg_ctlmax; + u32 msg_ctlmnb; + u32 msg_ctlmni; + u32 auto_msgmni; + u32 shm_ctlmax[2]; + u64 shm_ctlall[2]; + u32 shm_ctlmni; + u32 shm_rmid_forced; + u32 mq_queues_max; + u32 mq_msg_max; + u32 mq_msgsize_max; + u32 in_use[3]; +} __packed; + #define VMA_AREA_NONE (0 << 0) #define VMA_AREA_REGULAR (1 << 0) /* Dumpable area */ #define VMA_AREA_STACK (1 << 1) diff --git a/include/syscall.h b/include/syscall.h index a1f69c059..9fdb00fd5 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -353,6 +353,10 @@ static long sys_capset(struct cap_header *h, struct cap_data *d) #define CLONE_NEWUTS 0x04000000 #endif +#ifndef CLONE_NEWIPC +#define CLONE_NEWIPC 0x08000000 +#endif + #define setns sys_setns #else /* CONFIG_X86_64 */ diff --git a/ipc_ns.c b/ipc_ns.c new file mode 100644 index 000000000..1ed290a5b --- /dev/null +++ b/ipc_ns.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "crtools.h" +#include "syscall.h" +#include "namespaces.h" + +struct ipc_ns_data { + struct ipc_ns_entry entry; +}; + +#define IPC_SEM_IDS 0 +#define IPC_MSG_IDS 1 +#define IPC_SHM_IDS 2 + +static int collect_ipc_msg(void *data) +{ + struct msginfo info; + int ret; + int fd; + + ret = msgctl(0, MSG_INFO, (struct msqid_ds *)&info); + if (ret < 0) { + pr_err("msgctl failed with %d\n", errno); + return ret; + } + + if (ret) { + pr_err("IPC messages migration is not supported yet\n"); + return -EINVAL; + } + + return 0; +} + +static int collect_ipc_sem(void *data) +{ + int ret; + struct seminfo info; + + ret = semctl(0, 0, SEM_INFO, &info); + if (ret < 0) + pr_err("semctl failed with %d\n", errno); + + if (ret) { + pr_err("IPC semaphores migration is not supported yet\n"); + return -EINVAL; + } + + return 0; +} + +static int collect_ipc_shm(void *data) +{ + int fd; + int ret; + struct shmid_ds shmid; + + ret = shmctl(0, IPC_INFO, &shmid); + if (ret < 0) + pr_err("semctl failed with %d\n", errno); + + if (ret) { + pr_err("IPC shared memory migration is not supported yet\n"); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_X86_64 +static int read_ipc_sysctl_long(char *name, u64 *data, size_t size) +{ + int fd; + int ret; + char buf[32]; + + fd = open(name, O_RDONLY); + if (fd < 0) { + pr_err("Can't open %s\n", name); + return fd; + } + ret = read(fd, buf, 32); + if (ret < 0) { + pr_err("Can't read %s\n", name); + ret = -errno; + goto err; + } + *data = strtoull(buf, NULL, 10); +err: + close(fd); + return ret; +} +#endif + +static int read_ipc_sysctl(char *name, u32 *data, size_t size) +{ + int fd; + int ret; + char buf[32]; + + fd = open(name, O_RDONLY); + if (fd < 0) { + pr_err("Can't open %s\n", name); + return fd; + } + ret = read(fd, buf, 32); + if (ret < 0) { + pr_err("Can't read %s\n", name); + ret = -errno; + goto err; + } + *data = (u32)strtoul(buf, NULL, 10); +err: + close(fd); + return ret; +} + +static int read_ipc_sem(u32 sem[]) +{ + int fd; + int ret; + char buf[128], *ptr = buf; + char *name = "/proc/sys/kernel/sem"; + int i; + + fd = open(name, O_RDONLY); + if (fd < 0) { + pr_err("Can't open %s\n", name); + return fd; + } + ret = read(fd, buf, 128); + if (ret < 0) { + pr_err("Can't read %s\n", name); + ret = -errno; + goto err; + } + sem[0] = (u32)strtoul(ptr, &ptr, 10); ptr++; + sem[1] = (u32)strtoul(ptr, &ptr, 10); ptr++; + sem[2] = (u32)strtoul(ptr, &ptr, 10); ptr++; + sem[3] = (u32)strtoul(ptr, &ptr, 10); ptr++; +err: + close(fd); + return ret; +} + +static int collect_ipc_tun(struct ipc_ns_entry *entry) +{ + int ret; + + ret = read_ipc_sem(entry->sem_ctls); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/kernel/msgmax", + &entry->msg_ctlmax, sizeof(entry->msg_ctlmax)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/kernel/msgmnb", + &entry->msg_ctlmnb, sizeof(entry->msg_ctlmnb)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/kernel/msgmni", + &entry->msg_ctlmni, sizeof(entry->msg_ctlmni)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/kernel/auto_msgmni", + &entry->auto_msgmni, sizeof(entry->auto_msgmni)); + if (ret < 0) + goto err; +#ifdef CONFIG_X86_64 + ret = read_ipc_sysctl_long("/proc/sys/kernel/shmmax", + (u64 *)entry->shm_ctlmax, sizeof(entry->shm_ctlmax)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl_long("/proc/sys/kernel/shmall", + (u64 *)entry->shm_ctlall, sizeof(entry->shm_ctlall)); +#else + ret = read_ipc_sysctl("/proc/sys/kernel/shmmax", + entry->shm_ctlmax, sizeof(entry->shm_ctlmax)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/kernel/shmall", + entry->shm_ctlall, sizeof(entry->shm_ctlall)); +#endif + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/kernel/shmmni", + &entry->shm_ctlmni, sizeof(entry->shm_ctlmni)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/kernel/shm_rmid_forced", + &entry->shm_rmid_forced, sizeof(entry->shm_rmid_forced)); + if (ret < 0) + goto err; + + + ret = read_ipc_sysctl("/proc/sys/fs/mqueue/queues_max", + &entry->mq_queues_max, sizeof(entry->mq_queues_max)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/fs/mqueue/msg_max", + &entry->mq_msg_max, sizeof(entry->mq_msg_max)); + if (ret < 0) + goto err; + ret = read_ipc_sysctl("/proc/sys/fs/mqueue/msgsize_max", + &entry->mq_msgsize_max, sizeof(entry->mq_msgsize_max)); + if (ret < 0) + goto err; + + return 0; +err: + pr_err("Failed to dump ipc namespace tunables\n"); + return ret; +} + +static int collect_ipc_data(int ns_pid, struct ipc_ns_data *ipc) +{ + int fd, ret; + struct ipc_ns_entry *entry = &ipc->entry; + + ret = switch_ns(ns_pid, CLONE_NEWIPC, "ipc"); + if (ret < 0) + return ret; + + entry->in_use[IPC_MSG_IDS] = ret = collect_ipc_msg(NULL); + if (ret < 0) + return ret; + entry->in_use[IPC_SEM_IDS] = ret = collect_ipc_sem(NULL); + if (ret < 0) + return ret; + entry->in_use[IPC_SHM_IDS] = ret = collect_ipc_shm(NULL); + if (ret < 0) + return ret; + ret = collect_ipc_tun(entry); + if (ret < 0) + return ret; + + return 0; +} + +static int dump_ipc_data(int fd, struct ipc_ns_data *ipc) +{ + int err; + + err = write_img_buf(fd, &ipc->entry, sizeof(ipc->entry)); + if (err < 0) { + pr_err("Failed to write IPC namespace entry\n"); + return err; + } + return 0; +} + +int dump_ipc_ns(int ns_pid, struct cr_fdset *fdset) +{ + int fd, ret; + struct ipc_ns_data ipc; + + ret = collect_ipc_data(ns_pid, &ipc); + if (ret < 0) { + pr_err("Failed to collect IPC namespace data\n"); + return ret; + } + + ret = dump_ipc_data(fdset->fds[CR_FD_IPCNS], &ipc); + if (ret < 0) { + pr_err("Failed to write IPC namespace data\n"); + return ret; + } + return 0; +} diff --git a/namespaces.c b/namespaces.c index 583bd7a62..04d476f5d 100644 --- a/namespaces.c +++ b/namespaces.c @@ -4,6 +4,7 @@ #include "util.h" #include "syscall.h" #include "uts_ns.h" +#include "ipc_ns.h" int switch_ns(int pid, int type, char *ns) { @@ -35,8 +36,17 @@ static int do_dump_namespaces(int ns_pid, unsigned int ns_flags) if (fdset == NULL) return -1; - ret = dump_uts_ns(ns_pid, fdset); - + if (ns_flags & CLONE_NEWUTS) { + ret = dump_uts_ns(ns_pid, fdset); + if (ret < 0) + goto err; + } + if (ns_flags & CLONE_NEWIPC) { + ret = dump_ipc_ns(ns_pid, fdset); + if (ret < 0) + goto err; + } +err: close_cr_fdset(&fdset); return ret;