Hostinfo day 4: Implement command processing code.
[virt-hostinfo.git] / hostinfod / main.c
index 17da054..e9ca8ae 100644 (file)
@@ -27,6 +27,7 @@
 #include <dirent.h>
 #include <time.h>
 #include <sys/types.h>
+#include <sys/time.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -50,6 +51,7 @@ 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;
@@ -95,6 +97,14 @@ usage (void)
          DEFAULT_CONF_FILE);
 }
 
+void
+initialize (void)
+{
+  apr_initialize ();
+  apr_pool_create (&pool, NULL);
+  init_syslog ();
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -110,13 +120,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);
 
@@ -168,7 +181,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");
@@ -228,6 +241,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");
@@ -369,7 +384,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;
 
@@ -490,7 +505,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);
@@ -514,6 +529,22 @@ guest_force_close (struct guest_description *hval)
   guest_removed (hval);
 }
 
+/* Difference between two timespec structures (r = a - b) */
+static struct timespec *
+diff_timespec (struct timespec *r,
+              const struct timespec *a, const struct timespec *b)
+{
+  if (a->tv_nsec - b->tv_nsec < 0) {
+    r->tv_sec = a->tv_sec - b->tv_sec - 1;
+    r->tv_nsec = 1000000000 + a->tv_nsec - b->tv_nsec;
+  } else {
+    r->tv_sec = a->tv_sec - b->tv_sec;
+    r->tv_nsec = a->tv_nsec - b->tv_nsec;
+  }
+
+  return r;
+}
+
 /* This is called when there is some event from the guest, eg.
  * connection finished, read, write or closed.
  */
@@ -524,9 +555,16 @@ guest_event (const apr_pollfd_t *pollfd, void *hvalv)
   int err, max, r, extra;
   socklen_t len;
   char *p;
-  time_t now;
-
-  time (&now);
+  struct timespec now;
+
+#ifdef HAVE_CLOCK_GETTIME
+  clock_gettime (CLOCK_MONOTONIC, &now);
+#else
+  struct timeval tv;
+  gettimeofday (&tv, NULL);
+  now.tv_sec = tv.tv_sec;
+  now.tv_nsec = tv.tv_usec * 1000;
+#endif
 
   /* If the guest keeps doing bad stuff, eventually lose patience with it. */
   if (hval->penalty >= 100) {
@@ -537,9 +575,15 @@ guest_event (const apr_pollfd_t *pollfd, void *hvalv)
   }
 
   /* Decrement the penalty once a minute, so the guest can recover. */
-  if (hval->penalty > 0 && now - hval->last_penalty_decr >= 60) {
-    hval->penalty--;
-    hval->last_penalty_decr = now;
+  if (hval->penalty > 0) {
+    struct timespec diff;
+
+    diff_timespec (&diff, &now, &hval->last_penalty_decr);
+
+    if (diff.tv_sec >= 60) {
+      hval->penalty--;
+      hval->last_penalty_decr = now;
+    }
   }
 
   switch (hval->state) {
@@ -621,7 +665,7 @@ guest_event (const apr_pollfd_t *pollfd, void *hvalv)
 
     *(p+1) = '\0';
 
-    execute_command (now, hval, hval->request);
+    execute_command (&now, hval, hval->request);
 
     hval->request_posn = 0;
     break;
@@ -662,16 +706,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);
+  }
+}