]> git.proxmox.com Git - mirror_lxc.git/commitdiff
Allow multiple monitor clients
authorDwight Engen <dwight.engen@oracle.com>
Wed, 24 Apr 2013 19:06:20 +0000 (15:06 -0400)
committerStéphane Graber <stgraber@ubuntu.com>
Wed, 24 Apr 2013 23:38:26 +0000 (01:38 +0200)
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 <dwight.engen@oracle.com>
Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
13 files changed:
.gitignore
doc/lxc-monitor.sgml.in
src/lxc/Makefile.am
src/lxc/lxc_console.c
src/lxc/lxc_monitor.c
src/lxc/lxc_monitord.c [new file with mode: 0644]
src/lxc/lxccontainer.c
src/lxc/mainloop.c
src/lxc/mainloop.h
src/lxc/monitor.c
src/lxc/monitor.h
src/lxc/start.c
src/lxc/utils.h

index 905a2dc773ecaf41f1aed2c6348c876b5a1fb9e8..c614a7539696cbc885f1ee88885862ec66f05a72 100644 (file)
@@ -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
index f9760a5a84abc8083fddcbc68900544b01a40922..eae6f8233d839a9eb2c3bfdade32f94d6af807d6 100644 (file)
@@ -67,18 +67,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
   &commonoptions;
 
-  <refsect1>
-    <title>Bugs</title>
-
-    <para>
-      Only one <command>lxc-monitor</command> can run at a time. Other
-      invocations will fail with the following error:
-    </para>
-    <para>
-      lxc-monitor: bind : Address already in use
-    </para>
-
-  </refsect1>
   <refsect1>
     <title>Examples</title>
     <variablelist>
index ebeca46633d04d0e80b52b2b1ed60b43a80462bc..1fa0fa88af09cd48576bb74dc80f8c45fa93cc1e 100644 (file)
@@ -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
index 643c442767c14c20a7828407bb348fcf062e073c..f6659f64e8460fa48a89ffd1faccf942cfbf7221 100644 (file)
@@ -241,7 +241,7 @@ Type <Ctrl+%1$c q> 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");
 
index 8c158698e3f2bf8072619980a26372e83ad618e1..0ca829f6775f96971d29e40dfb19b76b746513f6 100644 (file)
@@ -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 (file)
index 0000000..3b5cf53
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2012 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen@oracle.com>
+ *
+ * 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 <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <lxc/af_unix.h>
+#include <lxc/log.h>
+#include <lxc/mainloop.h>
+#include <lxc/monitor.h>
+#include <lxc/utils.h>
+
+#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;
+}
index 1df6a9817383bf89713a9782855a653869c51e5f..53765b04cd64c00821f75a2315916a86469ce949 100644 (file)
@@ -32,6 +32,7 @@
 #include <sys/wait.h>
 #include <errno.h>
 #include <lxc/utils.h>
+#include <lxc/monitor.h>
 
 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();
index 975215db44423df721821667e05404afc43a76e4..d9ab5d1af3caaefabcc487911b7744f10b0c82af 100644 (file)
@@ -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;
        }
index 6b16242aa7ede7cd117eb286447f8ae08437d148..ec875693afcee1f97a1696ccc48d9a184194ab6b 100644 (file)
@@ -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
index afdaf67a2481e8a01372d3a287e3fa787ecf286c..e108eb764b3d7c56761c312d454623a4be30a228 100644 (file)
@@ -5,6 +5,7 @@
  *
  * Authors:
  * Daniel Lezcano <daniel.lezcano at free.fr>
+ * Dwight Engen <dwight.engen@oracle.com>
  *
  * 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 <stdio.h>
 #include <errno.h>
 #include <unistd.h>
@@ -30,7 +32,7 @@
 #include <sys/stat.h>
 #include <sys/param.h>
 #include <sys/socket.h>
-#include <sys/un.h>
+#include <sys/wait.h>
 #include <netinet/in.h>
 #include <net/if.h>
 
 #include <lxc/log.h>
 #include <lxc/state.h>
 #include <lxc/monitor.h>
+#include <lxc/utils.h>
 
 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);
 }
index 8bef4c784b757a2ecb87e1a4a203e180e9e1d622..cd59ee841414408c7cb8e7507e74469138139fb2 100644 (file)
@@ -24,6 +24,9 @@
 #define __monitor_h
 
 #include <sys/param.h>
+#include <sys/un.h>
+
+#include <lxc/conf.h>
 
 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
index 0a0cc40f752557986d211ac8abf08b2b1b9b288e..fd96d4f609ed1183bb12f27fe3396042a47d638c 100644 (file)
@@ -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;
 
index 8954503a3d1ba5acb1c87a8c2a012b825e0c361b..8e6a7489c17cd7f661bef46fc63ad9825df08f86 100644 (file)
@@ -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