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