Commit 405985e9 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov

Add sysctl handling engine

Since we need to operate with sysctls pretty heavy,
better to add some common engine for all handlers.

Based-on-patch-from: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Acked-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 73b93182
...@@ -34,6 +34,7 @@ OBJS += cr-dump.o ...@@ -34,6 +34,7 @@ OBJS += cr-dump.o
OBJS += cr-show.o OBJS += cr-show.o
OBJS += util.o OBJS += util.o
OBJS += util-net.o OBJS += util-net.o
OBJS += sysctl.o
OBJS += ptrace.o OBJS += ptrace.o
OBJS += log.o OBJS += log.o
OBJS += libnetlink.o OBJS += libnetlink.o
......
...@@ -111,8 +111,8 @@ struct ipc_ns_entry { ...@@ -111,8 +111,8 @@ struct ipc_ns_entry {
u32 msg_ctlmnb; u32 msg_ctlmnb;
u32 msg_ctlmni; u32 msg_ctlmni;
u32 auto_msgmni; u32 auto_msgmni;
u32 shm_ctlmax[2]; u64 shm_ctlmax;
u64 shm_ctlall[2]; u64 shm_ctlall;
u32 shm_ctlmni; u32 shm_ctlmni;
u32 shm_rmid_forced; u32 shm_rmid_forced;
u32 mq_queues_max; u32 mq_queues_max;
......
#ifndef CR_SYSCTL_H_
#define CR_SYSCTL_H_
struct sysctl_req {
char *name;
void *arg;
int type;
};
extern int sysctl_op(struct sysctl_req *req, int op);
enum {
CTL_READ,
CTL_WRITE,
CTL_PRINT,
};
#define CTL_SHIFT 4 /* Up to 16 types */
#define CTL_U32 1 /* Single u32 */
#define CTL_U64 2 /* Single u64 */
#define __CTL_U32A 3 /* Array of u32 */
#define __CTL_U64A 4 /* Array of u64 */
#define __CTL_STR 5 /* String */
#define CTL_U32A(n) (__CTL_U32A | ((n) << CTL_SHIFT))
#define CTL_U64A(n) (__CTL_U64A | ((n) << CTL_SHIFT))
#define CTL_STR(len) (__CTL_STR | ((len) << CTL_SHIFT))
#define CTL_LEN(t) ((t) >> CTL_SHIFT)
#define CTL_TYPE(t) ((t) & ((1 << CTL_SHIFT) - 1))
#endif /* CR_SYSCTL_H_ */
...@@ -6,10 +6,12 @@ ...@@ -6,10 +6,12 @@
#include <sys/msg.h> #include <sys/msg.h>
#include <sys/sem.h> #include <sys/sem.h>
#include <sys/shm.h> #include <sys/shm.h>
#include "util.h" #include "util.h"
#include "crtools.h" #include "crtools.h"
#include "syscall.h" #include "syscall.h"
#include "namespaces.h" #include "namespaces.h"
#include "sysctl.h"
struct ipc_ns_data { struct ipc_ns_data {
struct ipc_ns_entry entry; struct ipc_ns_entry entry;
...@@ -19,6 +21,27 @@ struct ipc_ns_data { ...@@ -19,6 +21,27 @@ struct ipc_ns_data {
#define IPC_MSG_IDS 1 #define IPC_MSG_IDS 1
#define IPC_SHM_IDS 2 #define IPC_SHM_IDS 2
static int ipc_sysctl_req(struct ipc_ns_entry *e, int op)
{
struct sysctl_req req[] = {
{ "kernel/sem", e->sem_ctls, CTL_U32A(4) },
{ "kernel/msgmax", &e->msg_ctlmax, CTL_U32 },
{ "kernel/msgmnb", &e->msg_ctlmnb, CTL_U32 },
{ "kernel/msgmni", &e->msg_ctlmni, CTL_U32 },
{ "kernel/auto_msgmni", &e->auto_msgmni, CTL_U32 },
{ "kernel/shmmax", &e->shm_ctlmax, CTL_U64 },
{ "kernel/shmall", &e->shm_ctlall, CTL_U64 },
{ "kernel/shmmni", &e->shm_ctlmni, CTL_U32 },
{ "kernel/shm_rmid_forced", &e->shm_rmid_forced, CTL_U32 },
{ "fs/mqueue/queues_max", &e->mq_queues_max, CTL_U32 },
{ "fs/mqueue/msg_max", &e->mq_msg_max, CTL_U32 },
{ "fs/mqueue/msgsize_max", &e->mq_msgsize_max, CTL_U32 },
{ },
};
return sysctl_op(req, op);
}
static int collect_ipc_msg(void *data) static int collect_ipc_msg(void *data)
{ {
struct msginfo info; struct msginfo info;
...@@ -74,149 +97,9 @@ static int collect_ipc_shm(void *data) ...@@ -74,149 +97,9 @@ static int collect_ipc_shm(void *data)
return 0; return 0;
} }
#ifdef CONFIG_X86_64 static int collect_ipc_tun(struct ipc_ns_entry *e)
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_perror("Can't open %s", name);
return fd;
}
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
pr_perror("Can't read %s", 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_perror("Can't open %s", name);
return fd;
}
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
pr_perror("Can't read %s", 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; return ipc_sysctl_req(e, CTL_READ);
int ret;
char buf[128], *ptr = buf;
char *name = "/proc/sys/kernel/sem";
int i;
fd = open(name, O_RDONLY);
if (fd < 0) {
pr_perror("Can't open %s", name);
return fd;
}
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
pr_perror("Can't read %s", 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) static int collect_ipc_data(int ns_pid, struct ipc_ns_data *ipc)
...@@ -277,20 +160,7 @@ int dump_ipc_ns(int ns_pid, struct cr_fdset *fdset) ...@@ -277,20 +160,7 @@ int dump_ipc_ns(int ns_pid, struct cr_fdset *fdset)
static void show_ipc_entry(struct ipc_ns_entry *entry) static void show_ipc_entry(struct ipc_ns_entry *entry)
{ {
pr_info("/proc/sys/kernel/sem : %d\t%d\t%d\t%d\n", ipc_sysctl_req(entry, CTL_PRINT);
entry->sem_ctls[0], entry->sem_ctls[1],
entry->sem_ctls[2], entry->sem_ctls[3]);
pr_info("/proc/sys/kernel/msgmax : %d\n", entry->msg_ctlmax);
pr_info("/proc/sys/kernel/msgmnb : %d\n", entry->msg_ctlmnb);
pr_info("/proc/sys/kernel/msgmni : %d\n", entry->msg_ctlmni);
pr_info("/proc/sys/kernel/auto_msgmni : %d\n", entry->auto_msgmni);
pr_info("/proc/sys/kernel/shmmax : %ld\n", *(u64 *)entry->shm_ctlmax);
pr_info("/proc/sys/kernel/shmall : %ld\n", *(u64 *)entry->shm_ctlall);
pr_info("/proc/sys/kernel/shmmni : %d\n", entry->shm_ctlmni);
pr_info("/proc/sys/kernel/shm_rmid_forced : %d\n", entry->shm_rmid_forced);
pr_info("/proc/sys/fs/mqueue/queues_max : %d\n", entry->mq_queues_max);
pr_info("/proc/sys/fs/mqueue/msg_max : %d\n", entry->mq_msg_max);
pr_info("/proc/sys/fs/mqueue/msgsize_max : %d\n", entry->mq_msgsize_max);
} }
static void show_ipc_data(int fd) static void show_ipc_data(int fd)
...@@ -311,126 +181,9 @@ void show_ipc_ns(int fd) ...@@ -311,126 +181,9 @@ void show_ipc_ns(int fd)
pr_img_tail(CR_FD_IPCNS); pr_img_tail(CR_FD_IPCNS);
} }
#ifdef CONFIG_X86_64 static int prepare_ipc_tun(struct ipc_ns_entry *e)
static int write_ipc_sysctl_long(char *name, u64 *data)
{
int fd;
int ret;
char buf[32];
fd = open(name, O_WRONLY);
if (fd < 0) {
pr_perror("Can't open %s", name);
return fd;
}
sprintf(buf, "%ld\n", *(long *)data);
ret = write(fd, buf, 32);
if (ret < 0) {
pr_perror("Can't write %s", name);
ret = -errno;
}
close(fd);
return ret;
}
#endif
static int write_ipc_sysctl(char *name, u32 *data)
{
int fd;
int ret;
char buf[32];
fd = open(name, O_WRONLY);
if (fd < 0) {
pr_perror("Can't open %s", name);
return fd;
}
sprintf(buf, "%d\n", *(int *)data);
ret = write(fd, buf, 32);
if (ret < 0) {
pr_perror("Can't write %s", name);
ret = -errno;
}
close(fd);
return ret;
}
static int write_ipc_sem(u32 sem[])
{ {
int fd; return ipc_sysctl_req(e, CTL_WRITE);
int ret;
char buf[128];
char *name = "/proc/sys/kernel/sem";
fd = open(name, O_WRONLY);
if (fd < 0) {
pr_perror("Can't open %s", name);
return fd;
}
sprintf(buf, "%d %d %d %d\n", sem[0], sem[1], sem[2], sem[3]);
ret = write(fd, buf, 128);
if (ret < 0) {
pr_perror("Can't write %s", name);
ret = -errno;
}
close(fd);
return ret;
}
static int prepare_ipc_tun(struct ipc_ns_entry *entry)
{
int ret;
ret = write_ipc_sem(entry->sem_ctls);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/kernel/msgmax", &entry->msg_ctlmax);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/kernel/msgmnb", &entry->msg_ctlmnb);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/kernel/msgmni", &entry->msg_ctlmni);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/kernel/auto_msgmni", &entry->auto_msgmni);
if (ret < 0)
goto err;
#ifdef CONFIG_X86_64
ret = write_ipc_sysctl_long("/proc/sys/kernel/shmmax", (u64 *)entry->shm_ctlmax);
if (ret < 0)
goto err;
ret = write_ipc_sysctl_long("/proc/sys/kernel/shmall", (u64 *)entry->shm_ctlall);
#else
ret = write_ipc_sysctl("/proc/sys/kernel/shmmax", entry->shm_ctlmax);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/kernel/shmall", entry->shm_ctlall);
#endif
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/kernel/shmmni", &entry->shm_ctlmni);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/kernel/shm_rmid_forced", &entry->shm_rmid_forced);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/fs/mqueue/queues_max", &entry->mq_queues_max);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/fs/mqueue/msg_max", &entry->mq_msg_max);
if (ret < 0)
goto err;
ret = write_ipc_sysctl("/proc/sys/fs/mqueue/msgsize_max", &entry->mq_msgsize_max);
if (ret < 0)
goto err;
return 0;
err:
pr_err("Failed to restore ipc namespace tunables\n");
return ret;
} }
static int prepare_ipc_data(int fd) static int prepare_ipc_data(int fd)
......
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include "types.h"
#include "sysctl.h"
#include "util.h"
#define __SYSCTL_OP(__ret, __fd, __req, __type, __nr, __op) \
do { \
if (__op == CTL_READ) \
__ret = sysctl_read_##__type(__fd, __req, \
(__type *)(__req)->arg, \
__nr); \
else if (__op == CTL_WRITE) \
__ret = sysctl_write_##__type(__fd, __req, \
(__type *)(__req)->arg, \
__nr); \
else if (__op == CTL_PRINT) \
__ret = sysctl_print_##__type(__fd, __req, \
(__type *)(__req)->arg, \
__nr); \
else \
__ret = -1; \
} while (0)
#define GEN_SYSCTL_READ_FUNC(__type, __conv) \
static int sysctl_read_##__type(int fd, \
struct sysctl_req *req, \
__type *arg, \
int nr) \
{ \
char buf[1024] = {0}; \
int i, ret = -1; \
char *p = buf; \
\
ret = read(fd, buf, sizeof(buf)); \
if (ret < 0) { \
pr_perror("Can't read %s", req->name); \
ret = -1; \
goto err; \
} \
\
for (i = 0; i < nr && p < buf + sizeof(buf); p++, i++) \
((__type *)arg)[i] = __conv(p, &p, 10); \
\
if (i != nr) { \
pr_err("Not enough params for %s (%d != %d)\n", \
req->name, i, nr); \
goto err; \
} \
\
ret = 0; \
\
err: \
return ret; \
}
#define GEN_SYSCTL_WRITE_FUNC(__type, __fmt) \
static int sysctl_write_##__type(int fd, \
struct sysctl_req *req, \
__type *arg, \
int nr) \
{ \
char buf[1024]; \
int i, ret = -1; \
int off = 0; \
\
for (i = 0; i < nr && off < sizeof(buf) - 2; i++) { \
snprintf(&buf[off], sizeof(buf) - off, __fmt, arg[i]); \
off += strlen(&buf[off]); \
} \
\
if (i != nr) { \
pr_err("Not enough space for %s (%d != %d)\n", \
req->name, i, nr); \
goto err; \
} \
\
/* trailing spaces in format */ \
while (off > 0 && isspace(buf[off - 1])) \
off--; \
buf[off + 0] = '\n'; \
buf[off + 1] = '\0'; \
ret = write(fd, buf, off + 2); \
if (ret < 0) { \
pr_perror("Can't write %s", req->name); \
ret = -1; \
goto err; \
} \
\
ret = 0; \
err: \
return ret; \
}
#define GEN_SYSCTL_PRINT_FUNC(__type, __fmt) \
static int sysctl_print_##__type(int fd, \
struct sysctl_req *req, \
__type *arg, \
int nr) \
{ \
int i; \
pr_info("sysctl: <%s> = <", req->name); \
for (i = 0; i < nr; i++) \
pr_info(__fmt, arg[i]); \
pr_info(">\n"); \
\
return 0; \
}
GEN_SYSCTL_READ_FUNC(u32, strtoul);
GEN_SYSCTL_READ_FUNC(u64, strtoull);
GEN_SYSCTL_WRITE_FUNC(u32, "%u ");
GEN_SYSCTL_WRITE_FUNC(u64, "%lu ");
GEN_SYSCTL_PRINT_FUNC(u32, "%u ");
GEN_SYSCTL_PRINT_FUNC(u64, "%lu ");
GEN_SYSCTL_PRINT_FUNC(char, "%c");
static int
sysctl_write_char(int fd, struct sysctl_req *req, char *arg, int nr)
{
pr_debug("%s nr %d\n", req->name, nr);
if (dprintf(fd, "%s\n", arg) < 0)
return -1;
return 0;
}
static int
sysctl_read_char(int fd, struct sysctl_req *req, char *arg, int nr)
{
int ret = -1;
pr_debug("%s nr %d\n", req->name, nr);
ret = read(fd, arg, nr);
if (ret < 0) {
pr_perror("Can't read %s", req->name);
goto err;
}
ret = 0;
err:
return ret;
}
static int __sysctl_op(int dir, struct sysctl_req *req, int op)
{
int fd = -1;
int ret = -1;
int nr = 1;
if (dir > 0) {
int flags;
if (op == CTL_READ)
flags = O_RDONLY;
else
flags = O_WRONLY;
fd = openat(dir, req->name, flags);
if (fd < 0) {
pr_perror("Can't open sysctl %s", req->name);
return -1;
}
}
switch (CTL_TYPE(req->type)) {
case __CTL_U32A:
nr = CTL_LEN(req->type);
case CTL_U32:
__SYSCTL_OP(ret, fd, req, u32, nr, op);
break;
case __CTL_U64A:
nr = CTL_LEN(req->type);
case CTL_U64:
__SYSCTL_OP(ret, fd, req, u64, nr, op);
break;
case __CTL_STR:
nr = CTL_LEN(req->type);
__SYSCTL_OP(ret, fd, req, char, nr, op);
break;
}
if (fd > 0)
close(fd);
return ret;
}
int sysctl_op(struct sysctl_req *req, int op)
{
int ret = 0;
int dir = -1;
if (op != CTL_PRINT) {
dir = open("/proc/sys", O_RDONLY);
if (dir < 0) {
pr_perror("Can't open sysctl dir");
return -1;
}
}
while (req->name) {
ret = __sysctl_op(dir, req, op);
if (ret < 0)
break;
req++;
}
if (dir > 0)
close(dir);
return ret;
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "crtools.h" #include "crtools.h"
#include "syscall.h" #include "syscall.h"
#include "namespaces.h" #include "namespaces.h"
#include "sysctl.h"
static int dump_uts_string(int fd, char *str) static int dump_uts_string(int fd, char *str)
{ {
...@@ -44,62 +45,55 @@ int dump_uts_ns(int ns_pid, struct cr_fdset *fdset) ...@@ -44,62 +45,55 @@ int dump_uts_ns(int ns_pid, struct cr_fdset *fdset)
return ret; return ret;
} }
static int prepare_uts_str(int fd, char *n) static int read_uts_str(int fd, char *n, int size)
{ {
int ret; int ret;
u32 len; u32 len;
char str[65], path[128];
ret = read_img(fd, &len); ret = read_img(fd, &len);
if (ret > 0) { if (ret < 0)
if (len >= sizeof(str)) { return -1;
pr_err("Corrupted %s\n", n);
return -1;
}
ret = read_img_buf(fd, str, len);
if (ret < 0)
return -1;
str[len] = '\0';
snprintf(path, sizeof(path),
"/proc/sys/kernel/%s", n);
fd = open(path, O_WRONLY);
if (fd < 0) {
pr_perror("Can't open %s", path);
return -1;
}
pr_info("Restoring %s to [%s]\n", n, str);
ret = write(fd, str, len);
close(fd);
if (ret != len) {
pr_perror("Can't write %s to %s",
str, path);
return -1;
}
ret = 0; if (len >= size) {
pr_err("Corrupted %s\n", n);
return -1;
} }
return ret; ret = read_img_buf(fd, n, len);
if (ret < 0)
return -1;
n[len] = '\0';
return 0;
} }
int prepare_utsns(int pid) int prepare_utsns(int pid)
{ {
int fd, ret; int fd, ret;
u32 len; u32 len;
char hostname[65];
char domainname[65];
struct sysctl_req req[] = {
{ "kernel/hostname", hostname, CTL_STR(sizeof(hostname)) },
{ "kernel/domainname", domainname, CTL_STR(sizeof(hostname)) },
{ },
};
fd = open_image_ro(CR_FD_UTSNS, pid); fd = open_image_ro(CR_FD_UTSNS, pid);
if (fd < 0) if (fd < 0)
return -1; return -1;
ret = prepare_uts_str(fd, "hostname"); ret = read_uts_str(fd, hostname, sizeof(hostname));
if (!ret) if (ret < 0)
ret = prepare_uts_str(fd, "domainname"); goto out;
ret = read_uts_str(fd, domainname, sizeof(domainname));
if (ret < 0)
goto out;
ret = sysctl_op(req, CTL_WRITE);
out:
close(fd); close(fd);
return ret; return ret;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment