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