7c9601ae8267e524cad48dcd651a4eda167a5052
[virt-hostinfo.git] / hostinfod / main.c
1 /* virt-hostinfo
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <unistd.h>
27 #include <dirent.h>
28 #include <time.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34
35 #define UNIX_PATH_MAX 108
36
37 #include <apr_general.h>
38 #include <apr_network_io.h>
39 #include <apr_getopt.h>
40 #include <apr_strings.h>
41 #include <apr_thread_proc.h>
42 #include <apr_poll.h>
43 #include <apr_hash.h>
44 #include <apr_portable.h>
45
46 #include "hostinfod.h"
47
48 static void main_loop (void);
49 static void set_reread_socket_dir (const apr_pollfd_t *, void *);
50 static void do_reread_socket_dir (void);
51 static struct guest_description *guest_added (const char *sock_path, const char *name);
52 static void guest_removed (struct guest_description *);
53 static void guest_event (const apr_pollfd_t *, void *);
54 static void modify_pollfd_reqevents (struct guest_description *, apr_int16_t);
55
56 const char *conf_file = DEFAULT_CONF_FILE;
57 char *socket_dir = NULL;
58 char *guests_file = NULL;
59
60 char *libvirt_uri = NULL;
61 int libvirt_uri_set_on_cmdline = 0;
62
63 int verbose = 0;
64 int verbose_set_on_cmdline = 0;
65 int foreground = 0;
66 int foreground_set_on_cmdline = 0;
67
68 int messages_to_stderr = 1;
69
70 static int reread_socket_dir = 1;
71 static int quit = 0;
72
73 apr_pool_t *pool = NULL;
74 static apr_pollset_t *set = NULL;
75
76 static apr_hash_t *guests = NULL; /* Hash "driver-name" -> guest_description */
77
78 typedef void (*poll_callback) (const apr_pollfd_t *, void *data);
79 struct callback_data {
80   poll_callback callback;
81   void *data;
82 };
83
84 static void
85 usage (void)
86 {
87   printf ("hostinfod (virt-hostinfo daemon)\n"
88           "Copyright (C) 2009 Red Hat Inc.\n"
89           "\n"
90           "Usage:\n"
91           "  hostinfod [--options]\n"
92           "\n"
93           "Options:\n"
94           "  --help           Display full usage\n"
95           "  -c file | --config file\n"
96           "                   Configuration file (default: %s)\n"
97           "  -C uri | --connect uri\n"
98           "                   Set libvirt connection URI (default: NULL)\n"
99           "  -f | --foreground\n"
100           "                   Run in the foreground (don't fork)\n"
101           "  -v               Enable verbose messages (sent to syslog)\n",
102           DEFAULT_CONF_FILE);
103 }
104
105 void
106 initialize (void)
107 {
108   apr_initialize ();
109   apr_pool_create (&pool, NULL);
110   init_syslog ();
111 }
112
113 int
114 main (int argc, char *argv[])
115 {
116   static const apr_getopt_option_t options[] = {
117     { "config", 'c', TRUE, "configuration file" },
118     { "connect", 'C', TRUE, "libvirt connection URI" },
119     { "foreground", 'f', FALSE, "run in foreground (don't fork)" },
120     { "verbose", 'v', FALSE, "enable verbose messages" },
121     { "help", '?', FALSE, "display help" },
122     { NULL, 0, 0, NULL },
123   };
124   apr_status_t r;
125   apr_getopt_t *opt;
126   int c;
127   const char *optarg;
128
129   /* REGISTER_COMMAND macro should have caused this to be
130    * initialized.  If it's not, then something is badly wrong ...
131    */
132   if (!pool) {
133     error ("internal error: daemon not initialized - no commands registered");
134     exit (1);
135   }
136
137   apr_getopt_init (&opt, pool, argc, argv);
138
139   socket_dir = apr_pstrdup (pool, DEFAULT_SOCKET_DIR);
140   guests_file = apr_pstrdup (pool, DEFAULT_GUESTS_FILE);
141
142   /* Command line. */
143   while ((r = apr_getopt_long (opt, options, &c, &optarg)) == APR_SUCCESS) {
144     switch (c) {
145     case 'c':
146       conf_file = optarg;
147       /* If the user is specifying this on the command line, then
148        * it should exist.  They may have typo'd the name.
149        */
150       if (access (conf_file, R_OK) == -1) {
151         perrorf ("%s", conf_file);
152         exit (1);
153       }
154       break;
155     case 'C':
156       libvirt_uri = optarg;
157       libvirt_uri_set_on_cmdline = 1;
158       break;
159     case 'f':
160       foreground = 1;
161       foreground_set_on_cmdline = 1;
162       break;
163     case 'v':
164       verbose = 1;
165       verbose_set_on_cmdline = 1;
166       break;
167     case '?':
168       usage ();
169       exit (0);
170     default:
171       abort ();
172     }
173   }
174   if (r != APR_EOF) {
175     fprintf (stderr, "%s: unknown command line option\n", argv[0]);
176     exit (1);
177   }
178
179   /* Read the config file. */
180   read_main_conf_file ();
181
182   /* Connect to libvirt. */
183   init_libvirt ();
184
185   /* Monitor the socket directory. */
186   monitor_socket_dir ();
187
188   /* Create the guests hash. */
189   guests = apr_hash_make (pool);
190
191   /* Create the initial pollset, just containing inotify socket. */
192   r = apr_pollset_create (&set, 1024 /* ? */, pool, 0);
193   if (r != APR_SUCCESS) {
194     paprerror (r, "apr_pollset_create");
195     exit (1);
196   }
197   apr_socket_t *tsock = NULL;
198   r = apr_os_sock_put (&tsock, &sockets_inotify_fd, pool);
199   if (r != APR_SUCCESS) {
200     paprerror (r, "apr_os_sock_put");
201     exit (1);
202   }
203   apr_pollfd_t *tpollfd = apr_palloc (pool, sizeof *tpollfd);
204   tpollfd->p = pool;
205   tpollfd->desc_type = APR_POLL_SOCKET;
206   tpollfd->reqevents = APR_POLLIN;
207   tpollfd->rtnevents = 0;
208   tpollfd->desc.s = tsock;
209
210   struct callback_data *callback_data =
211     apr_palloc (pool, sizeof *callback_data);
212   callback_data->callback = set_reread_socket_dir;
213   callback_data->data = NULL;
214   tpollfd->client_data = callback_data;
215
216   r = apr_pollset_add (set, tpollfd);
217   if (r != APR_SUCCESS) {
218     paprerror (r, "apr_pollset_add");
219     exit (1);
220   }
221
222   /* Daemonize. */
223   chdir ("/");
224   if (!foreground) {
225     apr_proc_detach (1);
226
227     /* After we detach from the terminal, all further messages
228      * should just go to syslog.
229      */
230     messages_to_stderr = 0;
231   }
232
233   message ("%s started", PACKAGE_STRING);
234   main_loop ();
235   message ("%s exiting", PACKAGE_STRING);
236
237   apr_terminate ();
238   return 0;
239 }
240
241 static void
242 main_loop (void)
243 {
244   apr_status_t r;
245   apr_int32_t numdescs;
246   const apr_pollfd_t *descs;
247   int i;
248
249   while (!quit) {
250     /* A socket has appeared or disappeared from the socket directory. */
251     if (reread_socket_dir) {
252       do_reread_socket_dir ();
253       reread_socket_dir = 0;
254     }
255
256     /* Poll. */
257     numdescs = 0;
258     descs = NULL;
259     r = apr_pollset_poll (set, -1, &numdescs, &descs);
260     if (r != APR_SUCCESS) {
261       paprerror (r, "apr_pollset_poll");
262       exit (1);
263     }
264
265     /* Perform the callbacks. */
266     for (i = 0; i < numdescs; ++i) {
267       struct callback_data *callback_data;
268
269       callback_data = descs[i].client_data;
270       callback_data->callback (&descs[i], callback_data->data);
271     }
272   }
273 }
274
275 static void
276 set_reread_socket_dir (const apr_pollfd_t *ignored1, void *ignored2)
277 {
278   reread_socket_dir = 1;
279 }
280
281 static void
282 do_reread_socket_dir (void)
283 {
284   static int count = 0;
285   int added = 0, removed = 0;
286   char buf[PATH_MAX];
287   int r;
288   DIR *dir;
289   struct dirent *d;
290   struct stat statbuf;
291   struct guest_description *hval;
292   apr_hash_index_t *hi;
293
294   count++;
295   debug ("reading socket directory (counter = %d)", count);
296
297   /* Discard anything which appears on the inotify socket.  We will
298    * reread the whole directory each time.
299    */
300   do {
301     r = read (sockets_inotify_fd, buf, sizeof buf);
302     if (r == -1) {
303       if (errno != EAGAIN && errno != EWOULDBLOCK) {
304         perrorf ("inotify socket: read");
305         exit (1);
306       }
307     }
308   } while (r > 0);
309
310   dir = opendir (socket_dir);
311   if (dir == NULL) {
312     perrorf ("%s: failed to open socket directory", socket_dir);
313     exit (1);
314   }
315
316   while (errno = 0, (d = readdir (dir)) != NULL) {
317     /* We expect the name to be "<driver>-<name>" (where <driver>
318      * is the libvirt driver name, and <name> is the name of the
319      * domain).  Skip any dot-entries and anything that doesn't have
320      * this form.
321      */
322     if (d->d_name[0] == '.')
323       continue;
324     if (strlen (d->d_name) < 3 || strchr (&d->d_name[1], '-') == NULL)
325       continue;
326
327     /* It must be a Unix domain socket - skip anything else. */
328     snprintf (buf, sizeof buf, "%s/%s", socket_dir, d->d_name);
329     if (stat (buf, &statbuf) == -1) {
330       perrorf ("stat: %s", buf);
331       continue;
332     }
333     if (!S_ISSOCK (statbuf.st_mode))
334       continue;
335
336     /* See if we have an entry matching this already. */
337     hval = (struct guest_description *)
338       apr_hash_get (guests, d->d_name, APR_HASH_KEY_STRING);
339     if (!hval) {
340       hval = guest_added (buf, d->d_name);
341       if (!hval)
342         continue;
343
344       /* NB.  It's not well documented, but the hash table
345        * implementation DOES NOT copy the key internally.  Therefore
346        * we have to use hval->name (ie. our copy) as the key, NOT
347        * d->d_name, even though they are the same string.
348        */
349       apr_hash_set (guests, hval->name, APR_HASH_KEY_STRING, hval);
350       added++;
351     }
352
353     hval->counter = count;
354   }
355   if (errno != 0) {
356     perrorf ("%s: error reading socket directory", socket_dir);
357     exit (1);
358   }
359
360   if (closedir (dir) == -1) {
361     perrorf ("%s: error closing socket directory", socket_dir);
362     exit (1);
363   }
364
365   /* Iterate over the hash and look for any guests which have
366    * gone away.  The guest_description.counter field won't have
367    * been updated.
368    */
369   for (hi = apr_hash_first (pool, guests); hi; hi = apr_hash_next (hi)) {
370     apr_hash_this(hi, NULL, NULL, (void **) &hval);
371     if (hval->counter != count) {
372       /* This hash table implementation allows you to delete the
373        * current entry safely.
374        */
375       apr_hash_set (guests, hval->name, APR_HASH_KEY_STRING, NULL);
376
377       /* guest_removed frees hval but does not unregister it from the
378        * hash.
379        */
380       guest_removed (hval);
381       removed++;
382     }
383   }
384
385   debug ("finished reading socket directory, added %d, removed %d, guests %d",
386          added, removed, apr_hash_count (guests));
387 }
388
389 /* This is called whenever we detect that a guest socket has been
390  * created in the socket directory.
391  */
392 static struct guest_description *
393 guest_added (const char *sock_path, const char *name)
394 {
395   struct guest_description *hval = NULL;
396   int sock;
397   int r;
398   unsigned retries = 0, tns;
399   enum guest_state state;
400   apr_pool_t *guest_pool = NULL;
401   struct sockaddr_un addr;
402   struct timespec ts;
403
404   sock = socket (AF_UNIX, SOCK_STREAM, 0);
405   if (sock == -1) {
406     perrorf ("socket");
407     return NULL;
408   }
409
410   if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1) {
411     perrorf ("fcntl: O_NONBLOCK");
412     close (sock);
413     return NULL;
414   }
415   if (fcntl (sock, F_SETFD, FD_CLOEXEC) == -1) {
416     perrorf ("fcntl: FD_CLOEXEC");
417     close (sock);
418     return NULL;
419   }
420
421   addr.sun_family = AF_UNIX;
422   strncpy (addr.sun_path, sock_path, UNIX_PATH_MAX);
423   addr.sun_path[UNIX_PATH_MAX-1] = '\0';
424
425  again:
426   r = connect (sock, (struct sockaddr *) &addr, sizeof addr);
427   if (r == -1) {
428     /* Nasty race condition: The moment the listener binds the socket,
429      * we see it in the directory and can try to connect to it.
430      * However the listener might not have called listen(2) yet, which
431      * means if we are faster than the other end, we will get
432      * ECONNREFUSED.  If this happens, sleep a bit and try again a few
433      * times.
434      */
435     if (errno == ECONNREFUSED) {
436       if (retries <= 10) {
437         tns = 1 << retries;
438         ts.tv_sec = tns / 1000000000;
439         ts.tv_nsec = tns % 1000000000;
440         nanosleep (&ts, NULL);
441         retries++;
442         goto again;
443       }
444     }
445
446     if (errno != EINPROGRESS) {
447       /* Dead socket - cull these dead sockets from the directory. */
448       perrorf ("connect: %s", sock_path);
449       close (sock);
450       unlink (sock_path);
451       return NULL;
452     }
453     state = guest_state_connecting;
454   }
455   else
456     state = guest_state_request;
457
458   /* Create a pool which can be used for allocations
459    * during the lifetime of this guest connection.
460    */
461   apr_pool_create (&guest_pool, pool);
462
463   hval = apr_pcalloc (guest_pool, sizeof *hval);
464   hval->pool = guest_pool;
465
466   /* Create the remaining hash fields. */
467   hval->state = state;
468   hval->name = apr_pstrdup (hval->pool, name);
469   hval->sock_path = apr_pstrdup (hval->pool, sock_path);
470   hval->sock = sock;
471   hval->request_max = 4096;
472   hval->request = apr_palloc (hval->pool, hval->request_max);
473   hval->lasttime = apr_hash_make (hval->pool);
474
475   /* Convert Unix fd into APR socket type. */
476   r = apr_os_sock_put (&hval->aprsock, &sock, hval->pool);
477   if (r != APR_SUCCESS) {
478     paprerror (r, "apr_os_sock_put: %s", sock_path);
479     exit (1);
480   }
481
482   /* Register the socket in the pollset. */
483   hval->pollfd.p = hval->pool;
484   hval->pollfd.desc_type = APR_POLL_SOCKET;
485   if (hval->state == guest_state_connecting)
486     hval->pollfd.reqevents = APR_POLLOUT;
487   else
488     hval->pollfd.reqevents = APR_POLLIN;
489   hval->pollfd.rtnevents = 0;
490   hval->pollfd.desc.s = hval->aprsock;
491
492   struct callback_data *callback_data =
493     apr_palloc (hval->pool, sizeof *callback_data);
494   callback_data->callback = guest_event;
495   callback_data->data = hval;
496   hval->pollfd.client_data = callback_data;
497
498   r = apr_pollset_add (set, &hval->pollfd);
499   if (r != APR_SUCCESS) {
500     paprerror (r, "apr_pollset_add: %s", sock_path);
501     exit (1);
502   }
503
504   message ("new guest added: %s", hval->name);
505   return hval;
506 }
507
508 /* This is called whenever we detect that a guest socket has been
509  * removed from the socket directory.  The guest_description parameter
510  * is freed after this call and must not be used again.
511  */
512 static void
513 guest_removed (struct guest_description *hval)
514 {
515   apr_status_t r;
516
517   message ("guest removed: %s", hval->name);
518
519   /* Unregister the socket from the pollset. */
520   r = apr_pollset_remove (set, &hval->pollfd);
521   if (r != APR_SUCCESS)
522     paprerror (r, "%s: apr_pollset_remove", hval->name);
523
524   if (close (hval->sock) == -1)
525     pwarningf ("close: %s", hval->sock_path);
526
527   /* This also frees hval and all related data. */
528   apr_pool_destroy (hval->pool);
529 }
530
531 /* Forcibly remove a guest, removing the socket from the
532  * socket directory and cleaning up any resources used in
533  * the daemon.  The guest_description parameter is freed
534  * after this call and must not be used again.
535  */
536 static void
537 guest_force_close (struct guest_description *hval)
538 {
539   debug ("forcibly closing guest: %s", hval->name);
540
541   apr_hash_set (guests, hval->name, APR_HASH_KEY_STRING, NULL);
542   unlink (hval->sock_path);
543   guest_removed (hval);
544 }
545
546 /* Difference between two timespec structures (r = a - b) */
547 struct timespec *
548 diff_timespec (struct timespec *r,
549                const struct timespec *a, const struct timespec *b)
550 {
551   if (a->tv_nsec - b->tv_nsec < 0) {
552     r->tv_sec = a->tv_sec - b->tv_sec - 1;
553     r->tv_nsec = 1000000000 + a->tv_nsec - b->tv_nsec;
554   } else {
555     r->tv_sec = a->tv_sec - b->tv_sec;
556     r->tv_nsec = a->tv_nsec - b->tv_nsec;
557   }
558
559   return r;
560 }
561
562 /* This is called when there is some event from the guest, eg.
563  * connection finished, read, write or closed.
564  */
565 static void
566 guest_event (const apr_pollfd_t *pollfd, void *hvalv)
567 {
568   struct guest_description *hval = hvalv;
569   int err, max, r, extra;
570   socklen_t len;
571   char *p;
572   struct timespec now;
573
574 #ifdef HAVE_CLOCK_GETTIME
575   clock_gettime (CLOCK_MONOTONIC, &now);
576 #else
577   struct timeval tv;
578   gettimeofday (&tv, NULL);
579   now.tv_sec = tv.tv_sec;
580   now.tv_nsec = tv.tv_usec * 1000;
581 #endif
582
583   /* If the guest keeps doing bad stuff, eventually lose patience with it. */
584   if (hval->penalty >= 100) {
585     error ("%s: guest did too much bad stuff, so we stopped talking to it",
586            hval->name);
587     guest_force_close (hval);
588     return;
589   }
590
591   /* Decrement the penalty once a minute, so the guest can recover. */
592   if (hval->penalty > 0) {
593     struct timespec diff;
594
595     diff_timespec (&diff, &now, &hval->last_penalty_decr);
596
597     if (diff.tv_sec >= 60) {
598       hval->penalty--;
599       hval->last_penalty_decr = now;
600     }
601   }
602
603   switch (hval->state) {
604   case guest_state_connecting:
605     /* Once we get a write event, we know the socket has
606      * connected, or there is an error.
607      */
608     err = 0;
609     len = sizeof err;
610     getsockopt (hval->sock, SOL_SOCKET, SO_ERROR, &err, &len);
611     if (err == 0)
612       hval->state = guest_state_request;
613     else {
614       errno = err;
615       perrorf ("connect: %s", hval->sock_path);
616       guest_force_close (hval);
617       return;
618     }
619     break;
620
621   case guest_state_request:
622     /* Reading the guest's request, a single line terminated by \r?\n */
623     max = hval->request_max - hval->request_posn;
624     if (max <= 0) {             /* Request too long w/o termination. */
625       hval->penalty++;
626       hval->request_posn = 0;
627       break;
628     }
629     r = read (hval->sock, &hval->request[hval->request_posn], max);
630     if (r == 0) {               /* Socket closed. */
631       guest_force_close (hval);
632       return;
633     }
634     if (r == -1) {
635       if (errno != EAGAIN && errno != EWOULDBLOCK) {
636         perrorf ("read: %s", hval->sock_path);
637         guest_force_close (hval);
638         return;
639       }
640       break;
641     }
642
643     hval->request_posn += r;
644
645     /* Have we got a terminating \n character in the buffer yet?  Note
646      * the buffer is not NUL-terminated which is why we use memchr.
647      */
648   again:
649     p = memchr (hval->request, '\n', hval->request_posn);
650     if (p == NULL)
651       break;
652
653     /* Is there more after the \n char?  Normal guests shouldn't do
654      * this, but it can be an attempt to reestablish synchronization.
655      * It's documented that we throw away all but the last command sent,
656      * so let's do that.
657      */
658     extra = &hval->request[hval->request_posn]-(p+1);
659     if (extra > 0) {
660       hval->penalty++;
661       memmove (hval->request, p+1, extra);
662       hval->request_posn = extra;
663       goto again;
664     }
665
666     /* Looks like we've got ourselves a command.  Remove trailing
667      * \r?\n char(s) and NUL-terminate the command string.
668      */
669     assert (*p == '\n');
670     assert (hval->request_posn >= 1);
671     assert (p == &hval->request[hval->request_posn-1]);
672     hval->request_posn--;
673     p--;
674
675     if (hval->request_posn > 0 && *p == '\r') {
676       hval->request_posn--;
677       p--;
678     }
679
680     *(p+1) = '\0';
681
682     execute_command (&now, hval, hval->request);
683
684     hval->request_posn = 0;
685     break;
686
687   case guest_state_reply:
688     /* Keep writing out the reply buffer until we've sent
689      * the whole thing.
690      */
691     max = hval->reply_size - hval->reply_posn;
692     if (max <= 0) {
693       hval->state = guest_state_request;
694       break;
695     }
696
697     r = write (hval->sock, &hval->reply[hval->reply_posn], max);
698     if (r == -1) {
699       if (errno != EAGAIN && errno != EWOULDBLOCK) {
700         perrorf ("write: %s", hval->sock_path);
701         guest_force_close (hval);
702         return;
703       }
704       break;
705     }
706
707     hval->reply_posn += r;
708     if (hval->reply_posn >= hval->reply_size)
709       hval->state = guest_state_request;
710
711     break;
712
713   case guest_state_dead:
714     /* We shouldn't get an event here. */
715     hval->penalty++;
716   }
717
718   /* Depending on the (new) state we want to set the
719    * events that we would like poll to give us next time.
720    */
721   switch (hval->state) {
722   case guest_state_connecting:
723     modify_pollfd_reqevents (hval, APR_POLLOUT);
724     break;
725   case guest_state_request:
726     modify_pollfd_reqevents (hval, APR_POLLIN);
727     break;
728   case guest_state_reply:
729     modify_pollfd_reqevents (hval, APR_POLLOUT);
730     break;
731   case guest_state_dead:
732     modify_pollfd_reqevents (hval, 0);
733     break;
734   }
735 }
736
737 /* It turns out you can't just update the pollfd->reqevents
738  * field.  Instead you have to remove the pollfd and reregister
739  * it in the pollset.
740  */
741 static void
742 modify_pollfd_reqevents (struct guest_description *hval,
743                          apr_int16_t new_reqevents)
744 {
745   apr_status_t r;
746
747   if (hval->pollfd.reqevents != new_reqevents) {
748     r = apr_pollset_remove (set, &hval->pollfd);
749     if (r != APR_SUCCESS) {
750       paprerror (r, "%s: apr_pollset_remove", hval->name);
751       return;
752     }
753
754     hval->pollfd.reqevents = new_reqevents;
755     r = apr_pollset_add (set, &hval->pollfd);
756     if (r != APR_SUCCESS)
757       paprerror (r, "%s: apr_pollset_add", hval->name);
758   }
759 }