From: Dwight Engen Date: Wed, 24 Apr 2013 19:06:20 +0000 (-0400) Subject: Allow multiple monitor clients X-Git-Tag: lxc-2.1.1~2542^2~266 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=e51d4895129209cec1c15bda2322136a03ec94b2;p=mirror_lxc.git Allow multiple monitor clients This fixes a long standing issue that there could only be a single lxc-monitor per container. With this change, a new lxc-monitord daemon is spawned the first time lxc-monitor is called against the container and will accept connections from any subsequent lxc-monitor. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- diff --git a/.gitignore b/.gitignore index 905a2dc77..c614a7539 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ src/lxc/lxc-info src/lxc/lxc-init src/lxc/lxc-kill src/lxc/lxc-monitor +src/lxc/lxc-monitord src/lxc/lxc-netstat src/lxc/lxc-ps src/lxc/lxc-restart diff --git a/doc/lxc-monitor.sgml.in b/doc/lxc-monitor.sgml.in index f9760a5a8..eae6f8233 100644 --- a/doc/lxc-monitor.sgml.in +++ b/doc/lxc-monitor.sgml.in @@ -67,18 +67,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA &commonoptions; - - Bugs - - - Only one lxc-monitor can run at a time. Other - invocations will fail with the following error: - - - lxc-monitor: bind : Address already in use - - - Examples diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index ebeca4663..1fa0fa88a 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -150,6 +150,7 @@ bin_PROGRAMS = \ lxc-start \ lxc-execute \ lxc-monitor \ + lxc-monitord \ lxc-wait \ lxc-console \ lxc-freeze \ @@ -181,6 +182,7 @@ lxc_freeze_SOURCES = lxc_freeze.c lxc_info_SOURCES = lxc_info.c lxc_init_SOURCES = lxc_init.c lxc_monitor_SOURCES = lxc_monitor.c +lxc_monitord_SOURCES = lxc_monitord.c lxc_restart_SOURCES = lxc_restart.c lxc_start_SOURCES = lxc_start.c lxc_stop_SOURCES = lxc_stop.c diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index 643c44276..f6659f64e 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -241,7 +241,7 @@ Type to exit the console, \ goto out_mainloop_open; } - err = lxc_mainloop(&descr); + err = lxc_mainloop(&descr, -1); if (err) { ERROR("mainloop returned an error"); goto out_mainloop_open; @@ -255,7 +255,7 @@ out_mainloop_open: out: /* Restore previous terminal parameter */ tcsetattr(0, TCSAFLUSH, &oldtios); - + /* Return to line it is */ printf("\n"); diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c index 8c158698e..0ca829f67 100644 --- a/src/lxc/lxc_monitor.c +++ b/src/lxc/lxc_monitor.c @@ -87,6 +87,8 @@ int main(int argc, char *argv[]) return -1; } + lxc_monitord_spawn(my_args.lxcpath); + fd = lxc_monitor_open(my_args.lxcpath); if (fd < 0) return -1; diff --git a/src/lxc/lxc_monitord.c b/src/lxc/lxc_monitord.c new file mode 100644 index 000000000..3b5cf53b1 --- /dev/null +++ b/src/lxc/lxc_monitord.c @@ -0,0 +1,409 @@ +/* + * lxc: linux Container library + * + * Copyright © 2012 Oracle. + * + * Authors: + * Dwight Engen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define CLIENTFDS_CHUNK 64 + +lxc_log_define(lxc_monitord, lxc); + +static struct lxc_monitor mon; + +static void lxc_monitord_cleanup(void); + +/* + * Defines the structure to store the monitor information + * @lxcpath : the path being monitored + * @fifofd : the file descriptor for publishers (containers) to write state + * @listenfd : the file descriptor for subscribers (lxc-monitors) to connect + * @clientfds : accepted client file descriptors + * @clientfds_size : number of file descriptors clientfds can hold + * @clientfds_cnt : the count of valid fds in clientfds + * @descr : the lxc_mainloop state + */ +struct lxc_monitor { + const char *lxcpath; + int fifofd; + int listenfd; + int *clientfds; + int clientfds_size; + int clientfds_cnt; + struct lxc_epoll_descr descr; +}; + +static int lxc_monitord_fifo_create(struct lxc_monitor *mon) +{ + char fifo_path[PATH_MAX]; + int ret; + + ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor-fifo", mon->lxcpath); + if (ret < 0 || ret >= sizeof(fifo_path)) { + ERROR("lxcpath too long to monitor fifo"); + return -1; + } + + ret = mknod(fifo_path, S_IFIFO|S_IRUSR|S_IWUSR, 0); + if (ret < 0) { + INFO("monitor fifo %s exists, already running?", fifo_path); + return -1; + } + + mon->fifofd = open(fifo_path, O_RDWR); + if (mon->fifofd < 0) { + unlink(fifo_path); + ERROR("failed to open monitor fifo"); + return -1; + } + return 0; +} + +static int lxc_monitord_fifo_delete(struct lxc_monitor *mon) +{ + char fifo_path[PATH_MAX]; + int ret; + + ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor-fifo", mon->lxcpath); + if (ret < 0 || ret >= sizeof(fifo_path)) { + ERROR("lxcpath too long to monitor fifo"); + return -1; + } + unlink(fifo_path); + return 0; +} + +static void lxc_monitord_sockfd_remove(struct lxc_monitor *mon, int fd) { + int i; + + if (lxc_mainloop_del_handler(&mon->descr, fd)) + CRIT("fd:%d not found in mainloop", fd); + close(fd); + + for (i = 0; i < mon->clientfds_cnt; i++) { + if (mon->clientfds[i] == fd) + break; + } + if (i >= mon->clientfds_cnt) { + CRIT("fd:%d not found in clients array", fd); + lxc_monitord_cleanup(); + exit(EXIT_FAILURE); + } + + memmove(&mon->clientfds[i], &mon->clientfds[i+1], + (mon->clientfds_cnt - i - 1) * sizeof(mon->clientfds[0])); + mon->clientfds_cnt--; +} + +static int lxc_monitord_sock_handler(int fd, void *data, + struct lxc_epoll_descr *descr) +{ + struct lxc_monitor *mon = data; + + lxc_monitord_sockfd_remove(mon, fd); + return 0; +} + +static int lxc_monitord_sock_accept(int fd, void *data, + struct lxc_epoll_descr *descr) +{ + int ret,clientfd; + struct lxc_monitor *mon = data; + struct ucred cred; + socklen_t credsz = sizeof(cred); + + ret = -1; + clientfd = accept(fd, NULL, 0); + if (clientfd < 0) { + SYSERROR("failed to accept connection"); + goto out; + } + + if (fcntl(clientfd, F_SETFD, FD_CLOEXEC)) { + SYSERROR("failed to set close-on-exec on incoming connection"); + goto err1; + } + + if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED, &cred, &credsz)) + { + ERROR("failed to get credentials on socket"); + goto err1; + } + if (cred.uid && cred.uid != geteuid()) { + WARN("monitor denied for uid:%d", cred.uid); + ret = -EACCES; + goto err1; + } + + if (mon->clientfds_cnt + 1 > mon->clientfds_size) { + int *clientfds; + DEBUG("realloc space for %d clientfds", + mon->clientfds_size + CLIENTFDS_CHUNK); + clientfds = realloc(mon->clientfds, + (mon->clientfds_size + CLIENTFDS_CHUNK) * + sizeof(mon->clientfds[0])); + if (clientfds == NULL) { + ERROR("failed to realloc memory for clientfds"); + goto err1; + } + mon->clientfds = clientfds; + mon->clientfds_size += CLIENTFDS_CHUNK; + } + + ret = lxc_mainloop_add_handler(&mon->descr, clientfd, + lxc_monitord_sock_handler, mon); + if (ret) { + ERROR("failed to add socket handler"); + goto err1; + } + + mon->clientfds[mon->clientfds_cnt++] = clientfd; + INFO("accepted client fd:%d clients:%d", clientfd, mon->clientfds_cnt); + goto out; + +err1: + close(clientfd); +out: + return ret; +} + +static int lxc_monitord_sock_create(struct lxc_monitor *mon) +{ + struct sockaddr_un addr; + int fd; + + if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0) + return -1; + + fd = lxc_af_unix_open(addr.sun_path, SOCK_STREAM, O_TRUNC); + if (fd < 0) { + ERROR("failed to open unix socket : %s", strerror(errno)); + return -1; + } + + mon->listenfd = fd; + return 0; +} + +static int lxc_monitord_sock_delete(struct lxc_monitor *mon) +{ + struct sockaddr_un addr; + + if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0) + return -1; + if (addr.sun_path[0]) + unlink(addr.sun_path); + return 0; +} + +static int lxc_monitord_create(struct lxc_monitor *mon) +{ + int ret; + + ret = lxc_monitord_fifo_create(mon); + if (ret < 0) + return ret; + + ret = lxc_monitord_sock_create(mon); + return ret; +} + +static void lxc_monitord_delete(struct lxc_monitor *mon) +{ + int i; + + lxc_mainloop_del_handler(&mon->descr, mon->listenfd); + close(mon->listenfd); + lxc_monitord_sock_delete(mon); + + lxc_mainloop_del_handler(&mon->descr, mon->fifofd); + close(mon->fifofd); + lxc_monitord_fifo_delete(mon); + + for (i = 0; i < mon->clientfds_cnt; i++) { + lxc_mainloop_del_handler(&mon->descr, mon->clientfds[i]); + close(mon->clientfds[i]); + } + mon->clientfds_cnt = 0; +} + +static int lxc_monitord_fifo_handler(int fd, void *data, + struct lxc_epoll_descr *descr) +{ + int ret,i; + struct lxc_msg msglxc; + struct lxc_monitor *mon = data; + + ret = read(fd, &msglxc, sizeof(msglxc)); + if (ret != sizeof(msglxc)) { + SYSERROR("read fifo failed : %s", strerror(errno)); + return 1; + } + + for (i = 0; i < mon->clientfds_cnt; i++) { + DEBUG("writing client fd:%d", mon->clientfds[i]); + ret = write(mon->clientfds[i], &msglxc, sizeof(msglxc)); + if (ret < 0) { + ERROR("write failed to client sock:%d %d %s", + mon->clientfds[i], errno, strerror(errno)); + } + } + + return 0; +} + +static int lxc_monitord_mainloop_add(struct lxc_monitor *mon) +{ + int ret; + + ret = lxc_mainloop_add_handler(&mon->descr, mon->fifofd, + lxc_monitord_fifo_handler, mon); + if (ret < 0) { + ERROR("failed to add to mainloop monitor handler for fifo"); + return -1; + } + + ret = lxc_mainloop_add_handler(&mon->descr, mon->listenfd, + lxc_monitord_sock_accept, mon); + if (ret < 0) { + ERROR("failed to add to mainloop monitor handler for listen socket"); + return -1; + } + + return 0; +} + +static void lxc_monitord_cleanup(void) +{ + lxc_monitord_delete(&mon); +} + +static void lxc_monitord_sig_handler(int sig) +{ + INFO("caught signal %d", sig); + lxc_monitord_cleanup(); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + int ret,pipefd; + char *lxcpath = argv[1]; + char logpath[PATH_MAX]; + sigset_t mask; + + if (argc != 3) { + fprintf(stderr, + "Usage: lxc-monitord lxcpath sync-pipe-fd\n\n" + "NOTE: lxc-monitord is intended for use by lxc internally\n" + " and does not need to be run by hand\n\n"); + exit(EXIT_FAILURE); + } + + ret = snprintf(logpath, sizeof(logpath), "%s/lxc-monitord.log", + lxcpath); + if (ret < 0 || ret >= sizeof(logpath)) + return EXIT_FAILURE; + + ret = lxc_log_init(NULL, logpath, "NOTICE", "lxc-monitord", 0); + if (ret) + return ret; + + pipefd = atoi(argv[2]); + + if (sigfillset(&mask) || + sigdelset(&mask, SIGILL) || + sigdelset(&mask, SIGSEGV) || + sigdelset(&mask, SIGBUS) || + sigdelset(&mask, SIGTERM) || + sigprocmask(SIG_BLOCK, &mask, NULL)) { + SYSERROR("failed to set signal mask"); + return -1; + } + + signal(SIGILL, lxc_monitord_sig_handler); + signal(SIGSEGV, lxc_monitord_sig_handler); + signal(SIGBUS, lxc_monitord_sig_handler); + signal(SIGTERM, lxc_monitord_sig_handler); + + ret = EXIT_FAILURE; + memset(&mon, 0, sizeof(mon)); + mon.lxcpath = lxcpath; + if (lxc_mainloop_open(&mon.descr)) { + ERROR("failed to create mainloop"); + goto out; + } + + if (lxc_monitord_create(&mon)) { + goto out; + } + + /* sync with parent, we're ignoring the return from write + * because regardless if it works or not, the following + * close will sync us with the parent process. the + * if-empty-statement construct is to quiet the + * warn-unused-result warning. + */ + if (write(pipefd, "S", 1)) ; + close(pipefd); + + if (lxc_monitord_mainloop_add(&mon)) { + ERROR("failed to add mainloop handlers"); + goto out; + } + + NOTICE("monitoring lxcpath %s", mon.lxcpath); + for(;;) { + ret = lxc_mainloop(&mon.descr, 1000 * 30); + if (mon.clientfds_cnt <= 0) + { + NOTICE("no clients for 30 seconds, exiting"); + break; + } + } + + lxc_mainloop_close(&mon.descr); + lxc_monitord_cleanup(); + ret = EXIT_SUCCESS; + NOTICE("monitor exiting"); +out: + return ret; +} diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 1df6a9817..53765b04c 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -32,6 +32,7 @@ #include #include #include +#include lxc_log_define(lxc_container, lxc); @@ -370,6 +371,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv if (daemonize) { if (!lxc_container_get(c)) return false; + lxc_monitord_spawn(c->config_path); pid_t pid = fork(); if (pid < 0) { lxc_container_put(c); @@ -560,7 +562,7 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) } /* container is already created if we have a config and rootfs.path is accessible */ - if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) + if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) goto out; /* we're going to fork. but since we'll wait for our child, we @@ -826,7 +828,7 @@ static bool lxcapi_destroy(struct lxc_container *c) return false; /* container is already destroyed if we don't have a config and rootfs.path is not accessible */ - if (!lxcapi_is_defined(c) && (!c->lxc_conf || !c->lxc_conf->rootfs.path || access(c->lxc_conf->rootfs.path, F_OK) != 0)) + if (!lxcapi_is_defined(c) && (!c->lxc_conf || !c->lxc_conf->rootfs.path || access(c->lxc_conf->rootfs.path, F_OK) != 0)) return false; pid = fork(); diff --git a/src/lxc/mainloop.c b/src/lxc/mainloop.c index 975215db4..d9ab5d1af 100644 --- a/src/lxc/mainloop.c +++ b/src/lxc/mainloop.c @@ -38,7 +38,7 @@ struct mainloop_handler { #define MAX_EVENTS 10 -int lxc_mainloop(struct lxc_epoll_descr *descr) +int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms) { int i, nfds; struct mainloop_handler *handler; @@ -46,7 +46,7 @@ int lxc_mainloop(struct lxc_epoll_descr *descr) for (;;) { - nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, -1); + nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, timeout_ms); if (nfds < 0) { if (errno == EINTR) continue; @@ -64,6 +64,9 @@ int lxc_mainloop(struct lxc_epoll_descr *descr) return 0; } + if (nfds == 0 && timeout_ms != 0) + return 0; + if (lxc_list_empty(&descr->handlers)) return 0; } diff --git a/src/lxc/mainloop.h b/src/lxc/mainloop.h index 6b16242aa..ec875693a 100644 --- a/src/lxc/mainloop.h +++ b/src/lxc/mainloop.h @@ -21,6 +21,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _mainloop_h +#define _mainloop_h + #include "list.h" struct lxc_epoll_descr { @@ -31,7 +34,7 @@ struct lxc_epoll_descr { typedef int (*lxc_mainloop_callback_t)(int fd, void *data, struct lxc_epoll_descr *descr); -extern int lxc_mainloop(struct lxc_epoll_descr *descr); +extern int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms); extern int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd, lxc_mainloop_callback_t callback, @@ -42,3 +45,5 @@ extern int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd); extern int lxc_mainloop_open(struct lxc_epoll_descr *descr); extern int lxc_mainloop_close(struct lxc_epoll_descr *descr); + +#endif diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index afdaf67a2..e108eb764 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -5,6 +5,7 @@ * * Authors: * Daniel Lezcano + * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +21,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include #include #include @@ -30,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -40,37 +42,36 @@ #include #include #include +#include lxc_log_define(lxc_monitor, lxc); -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 -#endif - -static void lxc_monitor_send(struct lxc_msg *msg, const char *lxcpath) +/* routines used by monitor publishers (containers) */ +static void lxc_monitor_fifo_send(struct lxc_msg *msg, const char *lxcpath) { - int fd; - struct sockaddr_un addr = { .sun_family = AF_UNIX }; - char *offset = &addr.sun_path[1]; - size_t ret, len; - - /* - * addr.sun_path is only 108 bytes. - * should we take a hash of lxcpath? a subset of it? - */ - len = sizeof(addr.sun_path) - 1; - ret = snprintf(offset, len, "%s/lxc-monitor", lxcpath); - if (ret < 0 || ret >= len) { - ERROR("lxcpath too long to open monitor"); + int fd,ret; + char fifo_path[PATH_MAX]; + + BUILD_BUG_ON(sizeof(*msg) > PIPE_BUF); /* write not guaranteed atomic */ + ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor-fifo", lxcpath); + if (ret < 0 || ret >= sizeof(fifo_path)) { + ERROR("lxcpath too long to open monitor fifo"); return; } - fd = socket(PF_UNIX, SOCK_DGRAM, 0); - if (fd < 0) + fd = open(fifo_path, O_WRONLY); + if (fd < 0) { + /* it is normal for this open to fail when there is no monitor + * running, so we don't log it + */ return; + } - sendto(fd, msg, sizeof(*msg), 0, - (const struct sockaddr *)&addr, sizeof(addr)); + ret = write(fd, msg, sizeof(*msg)); + if (ret != sizeof(*msg)) { + SYSERROR("failed to write monitor fifo %s", fifo_path); + return; + } close(fd); } @@ -82,50 +83,74 @@ void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxc strncpy(msg.name, name, sizeof(msg.name)); msg.name[sizeof(msg.name) - 1] = 0; - lxc_monitor_send(&msg, lxcpath); + lxc_monitor_fifo_send(&msg, lxcpath); } -int lxc_monitor_open(const char *lxcpath) + +/* routines used by monitor subscribers (lxc-monitor) */ +int lxc_monitor_close(int fd) { - struct sockaddr_un addr = { .sun_family = AF_UNIX }; - char *offset = &addr.sun_path[1]; - int fd; - size_t ret, len; - - /* - * addr.sun_path is only 108 bytes. - * should we take a hash of lxcpath? a subset of it? + return close(fd); +} + +int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) { + size_t len; + int ret; + char *sockname = &addr->sun_path[0]; // 1 for abstract + + /* addr.sun_path is only 108 bytes. + * should we take a hash of lxcpath? a subset of it? ftok()? we need + * to make sure it is unique. */ - len = sizeof(addr.sun_path) - 1; - ret = snprintf(offset, len, "%s/lxc-monitor", lxcpath); + memset(addr, 0, sizeof(*addr)); + addr->sun_family = AF_UNIX; + len = sizeof(addr->sun_path) - 1; + ret = snprintf(sockname, len, "%s/monitor-sock", lxcpath); if (ret < 0 || ret >= len) { - ERROR("lxcpath too long to open monitor"); + ERROR("lxcpath too long for unix socket"); return -1; } + return 0; +} - fd = socket(PF_UNIX, SOCK_DGRAM, 0); +int lxc_monitor_open(const char *lxcpath) +{ + struct sockaddr_un addr; + int fd,ret; + int retry,backoff_ms[] = {10, 50, 100}; + + if (lxc_monitor_sock_name(lxcpath, &addr) < 0) + return -1; + + fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { ERROR("socket : %s", strerror(errno)); return -1; } - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) { - ERROR("bind : %s", strerror(errno)); - close(fd); - return -1; + for (retry = 0; retry < sizeof(backoff_ms)/sizeof(backoff_ms[0]); retry++) { + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret == 0 || errno != ECONNREFUSED) + break; + ERROR("connect : backing off %d", backoff_ms[retry]); + usleep(backoff_ms[retry] * 1000); } + if (ret < 0) { + ERROR("connect : %s", strerror(errno)); + goto err1; + } return fd; +err1: + close(fd); + return ret; } -/* timeout of 0 means return immediately; -1 means wait forever */ -int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) +int lxc_monitor_read_timeout(int fd, struct lxc_msg *msglxc, int timeout) { - struct sockaddr_un from; - socklen_t len = sizeof(from); - int ret; fd_set rfds; struct timeval tv; + int ret; if (timeout != -1) { FD_ZERO(&rfds); @@ -141,13 +166,12 @@ int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) return -2; // timed out } - ret = recvfrom(fd, msg, sizeof(*msg), 0, - (struct sockaddr *)&from, &len); - if (ret < 0) { - SYSERROR("failed to receive state"); + ret = recv(fd, msglxc, sizeof(*msglxc), 0); + if (ret <= 0) { + SYSERROR("client failed to recv (monitord died?) %s", + strerror(errno)); return -1; } - return ret; } @@ -156,7 +180,73 @@ int lxc_monitor_read(int fd, struct lxc_msg *msg) return lxc_monitor_read_timeout(fd, msg, -1); } -int lxc_monitor_close(int fd) + + +/* used to spawn a monitord either on startup of a daemon container, or when + * lxc-monitor starts + */ +int lxc_monitord_spawn(const char *lxcpath) { - return close(fd); + pid_t pid1,pid2; + int pipefd[2]; + char pipefd_str[11]; + + char * const args[] = { + "/usr/bin/lxc-monitord", + (char *)lxcpath, + pipefd_str, + NULL, + }; + + /* double fork to avoid zombies when monitord exits */ + pid1 = fork(); + if (pid1 < 0) { + SYSERROR("failed to fork"); + return -1; + } + + if (pid1) { + waitpid(pid1, NULL, 0); + return 0; + } + + if (pipe(pipefd) < 0) { + SYSERROR("failed to create pipe"); + exit(EXIT_FAILURE); + } + + pid2 = fork(); + if (pid2 < 0) { + SYSERROR("failed to fork"); + exit(EXIT_FAILURE); + } + if (pid2) { + char c; + /* wait for daemon to create socket */ + close(pipefd[1]); + /* sync with child, we're ignoring the return from read + * because regardless if it works or not, either way we've + * synced with the child process. the if-empty-statement + * construct is to quiet the warn-unused-result warning. + */ + if (read(pipefd[0], &c, 1)) ; + close(pipefd[0]); + exit(EXIT_SUCCESS); + } + + umask(0); + if (setsid() < 0) { + SYSERROR("failed to setsid"); + exit(EXIT_FAILURE); + } + close(0); + close(1); + close(2); + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + open("/dev/null", O_RDWR); + close(pipefd[0]); + sprintf(pipefd_str, "%d", pipefd[1]); + execvp(args[0], args); + exit(EXIT_FAILURE); } diff --git a/src/lxc/monitor.h b/src/lxc/monitor.h index 8bef4c784..cd59ee841 100644 --- a/src/lxc/monitor.h +++ b/src/lxc/monitor.h @@ -24,6 +24,9 @@ #define __monitor_h #include +#include + +#include typedef enum { lxc_msg_state, @@ -32,11 +35,14 @@ typedef enum { struct lxc_msg { lxc_msg_type_t type; - char name[MAXPATHLEN]; + char name[NAME_MAX+1]; int value; }; -void lxc_monitor_send_state(const char *name, lxc_state_t state, +extern int lxc_monitor_open(const char *lxcpath); +extern int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr); +extern void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxcpath); +extern int lxc_monitord_spawn(const char *lxcpath); #endif diff --git a/src/lxc/start.c b/src/lxc/start.c index 0a0cc40f7..fd96d4f60 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -390,7 +390,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler) #endif } - return lxc_mainloop(&descr); + return lxc_mainloop(&descr, -1); out_mainloop_open: lxc_mainloop_close(&descr); @@ -808,7 +808,7 @@ int lxc_spawn(struct lxc_handler *handler) /* TODO - pass lxc.cgroup.dir (or user's pam cgroup) in for first argument */ if ((handler->cgroup = lxc_cgroup_path_create(NULL, name)) == NULL) goto out_delete_net; - + if (lxc_cgroup_enter(handler->cgroup, handler->pid) < 0) goto out_delete_net; diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 8954503a3..8e6a7489c 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -32,4 +32,30 @@ extern int mkdir_p(const char *dir, mode_t mode); */ extern const char *default_lxc_path(void); +/** + * BUILD_BUG_ON - break compile if a condition is true. + * @condition: the condition which the compiler should know is false. + * + * If you have some code which relies on certain constants being equal, or + * other compile-time-evaluated condition, you should use BUILD_BUG_ON to + * detect if someone changes it. + * + * The implementation uses gcc's reluctance to create a negative array, but + * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments + * to inline functions). So as a fallback we use the optimizer; if it can't + * prove the condition is false, it will cause a link error on the undefined + * "__build_bug_on_failed". This error message can be harder to track down + * though, hence the two different methods. + */ +#ifndef __OPTIMIZE__ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#else +extern int __build_bug_on_failed; +#define BUILD_BUG_ON(condition) \ + do { \ + ((void)sizeof(char[1 - 2*!!(condition)])); \ + if (condition) __build_bug_on_failed = 1; \ + } while(0) +#endif + #endif