Hostinfo day 6: RPM packaging, initscripts, hostinfo-status, hostinfo-test.
[virt-hostinfo.git] / hostinfod / main.c
index fa835bb..b27338e 100644 (file)
@@ -51,11 +51,15 @@ static void do_reread_socket_dir (void);
 static struct guest_description *guest_added (const char *sock_path, const char *name);
 static void guest_removed (struct guest_description *);
 static void guest_event (const apr_pollfd_t *, void *);
+static void modify_pollfd_reqevents (struct guest_description *, apr_int16_t);
 
 const char *conf_file = DEFAULT_CONF_FILE;
 char *socket_dir = NULL;
 char *guests_file = NULL;
 
+char *libvirt_uri = NULL;
+int libvirt_uri_set_on_cmdline = 0;
+
 int verbose = 0;
 int verbose_set_on_cmdline = 0;
 int foreground = 0;
@@ -90,17 +94,28 @@ usage (void)
          "  --help           Display full usage\n"
          "  -c file | --config file\n"
          "                   Configuration file (default: %s)\n"
+         "  -C uri | --connect uri\n"
+         "                   Set libvirt connection URI (default: NULL)\n"
          "  -f | --foreground\n"
          "                   Run in the foreground (don't fork)\n"
          "  -v               Enable verbose messages (sent to syslog)\n",
          DEFAULT_CONF_FILE);
 }
 
+void
+initialize (void)
+{
+  apr_initialize ();
+  apr_pool_create (&pool, NULL);
+  init_syslog ();
+}
+
 int
 main (int argc, char *argv[])
 {
   static const apr_getopt_option_t options[] = {
     { "config", 'c', TRUE, "configuration file" },
+    { "connect", 'C', TRUE, "libvirt connection URI" },
     { "foreground", 'f', FALSE, "run in foreground (don't fork)" },
     { "verbose", 'v', FALSE, "enable verbose messages" },
     { "help", '?', FALSE, "display help" },
@@ -111,13 +126,16 @@ main (int argc, char *argv[])
   int c;
   const char *optarg;
 
-  apr_initialize ();
-  apr_pool_create (&pool, NULL);
+  /* REGISTER_COMMAND macro should have caused this to be
+   * initialized.  If it's not, then something is badly wrong ...
+   */
+  if (!pool) {
+    error ("internal error: daemon not initialized - no commands registered");
+    exit (1);
+  }
 
   apr_getopt_init (&opt, pool, argc, argv);
 
-  init_syslog ();
-
   socket_dir = apr_pstrdup (pool, DEFAULT_SOCKET_DIR);
   guests_file = apr_pstrdup (pool, DEFAULT_GUESTS_FILE);
 
@@ -134,6 +152,10 @@ main (int argc, char *argv[])
        exit (1);
       }
       break;
+    case 'C':
+      libvirt_uri = optarg;
+      libvirt_uri_set_on_cmdline = 1;
+      break;
     case 'f':
       foreground = 1;
       foreground_set_on_cmdline = 1;
@@ -157,6 +179,9 @@ main (int argc, char *argv[])
   /* Read the config file. */
   read_main_conf_file ();
 
+  /* Connect to libvirt. */
+  init_libvirt ();
+
   /* Monitor the socket directory. */
   monitor_socket_dir ();
 
@@ -169,7 +194,7 @@ main (int argc, char *argv[])
     paprerror (r, "apr_pollset_create");
     exit (1);
   }
-  apr_socket_t *tsock;
+  apr_socket_t *tsock = NULL;
   r = apr_os_sock_put (&tsock, &sockets_inotify_fd, pool);
   if (r != APR_SUCCESS) {
     paprerror (r, "apr_os_sock_put");
@@ -229,6 +254,8 @@ main_loop (void)
     }
 
     /* Poll. */
+    numdescs = 0;
+    descs = NULL;
     r = apr_pollset_poll (set, -1, &numdescs, &descs);
     if (r != APR_SUCCESS) {
       paprerror (r, "apr_pollset_poll");
@@ -340,7 +367,12 @@ do_reread_socket_dir (void)
    * been updated.
    */
   for (hi = apr_hash_first (pool, guests); hi; hi = apr_hash_next (hi)) {
-    apr_hash_this(hi, NULL, NULL, (void **) &hval);
+    /* On RHEL 5 this gives:
+     * dereferencing type-punned pointer will break strict-aliasing rules
+     * XXX
+     */
+    apr_hash_this (hi, NULL, NULL, (void **) &hval);
+
     if (hval->counter != count) {
       /* This hash table implementation allows you to delete the
        * current entry safely.
@@ -370,7 +402,7 @@ guest_added (const char *sock_path, const char *name)
   int r;
   unsigned retries = 0, tns;
   enum guest_state state;
-  apr_pool_t *guest_pool;
+  apr_pool_t *guest_pool = NULL;
   struct sockaddr_un addr;
   struct timespec ts;
 
@@ -443,6 +475,7 @@ guest_added (const char *sock_path, const char *name)
   hval->sock = sock;
   hval->request_max = 4096;
   hval->request = apr_palloc (hval->pool, hval->request_max);
+  hval->lasttime = apr_hash_make (hval->pool);
 
   /* Convert Unix fd into APR socket type. */
   r = apr_os_sock_put (&hval->aprsock, &sock, hval->pool);
@@ -491,7 +524,7 @@ guest_removed (struct guest_description *hval)
   /* Unregister the socket from the pollset. */
   r = apr_pollset_remove (set, &hval->pollfd);
   if (r != APR_SUCCESS)
-    paprerror (r, "apr_pollset_remove for %s", hval->name);
+    paprerror (r, "%s: apr_pollset_remove", hval->name);
 
   if (close (hval->sock) == -1)
     pwarningf ("close: %s", hval->sock_path);
@@ -516,7 +549,7 @@ guest_force_close (struct guest_description *hval)
 }
 
 /* Difference between two timespec structures (r = a - b) */
-static struct timespec *
+struct timespec *
 diff_timespec (struct timespec *r,
               const struct timespec *a, const struct timespec *b)
 {
@@ -692,16 +725,40 @@ guest_event (const apr_pollfd_t *pollfd, void *hvalv)
    */
   switch (hval->state) {
   case guest_state_connecting:
-    hval->pollfd.reqevents = APR_POLLOUT;
+    modify_pollfd_reqevents (hval, APR_POLLOUT);
     break;
   case guest_state_request:
-    hval->pollfd.reqevents = APR_POLLIN;
+    modify_pollfd_reqevents (hval, APR_POLLIN);
     break;
   case guest_state_reply:
-    hval->pollfd.reqevents = APR_POLLOUT;
+    modify_pollfd_reqevents (hval, APR_POLLOUT);
     break;
   case guest_state_dead:
-    hval->pollfd.reqevents = 0;
+    modify_pollfd_reqevents (hval, 0);
     break;
   }
 }
+
+/* It turns out you can't just update the pollfd->reqevents
+ * field.  Instead you have to remove the pollfd and reregister
+ * it in the pollset.
+ */
+static void
+modify_pollfd_reqevents (struct guest_description *hval,
+                        apr_int16_t new_reqevents)
+{
+  apr_status_t r;
+
+  if (hval->pollfd.reqevents != new_reqevents) {
+    r = apr_pollset_remove (set, &hval->pollfd);
+    if (r != APR_SUCCESS) {
+      paprerror (r, "%s: apr_pollset_remove", hval->name);
+      return;
+    }
+
+    hval->pollfd.reqevents = new_reqevents;
+    r = apr_pollset_add (set, &hval->pollfd);
+    if (r != APR_SUCCESS)
+      paprerror (r, "%s: apr_pollset_add", hval->name);
+  }
+}