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