Hostinfo day 2: Implement 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 <unistd.h>
26
27 #include <apr_general.h>
28 #include <apr_network_io.h>
29 #include <apr_getopt.h>
30 #include <apr_strings.h>
31 #include <apr_thread_proc.h>
32 #include <apr_poll.h>
33 #include <apr_portable.h>
34
35 #include "hostinfod.h"
36
37 static void main_loop (void);
38 static void set_reread_socket_dir (const apr_pollfd_t *);
39 static void do_reread_socket_dir (void);
40
41 typedef void (*poll_callback) (const apr_pollfd_t *);
42
43 const char *conf_file = DEFAULT_CONF_FILE;
44 char *socket_dir = NULL;
45 char *guests_file = NULL;
46
47 int verbose = 0;
48 int verbose_set_on_cmdline = 0;
49 int foreground = 0;
50 int foreground_set_on_cmdline = 0;
51
52 int messages_to_stderr = 1;
53
54 static int reread_socket_dir = 1;
55 static int quit = 0;
56
57 apr_pool_t *pool = NULL;
58 static apr_pollset_t *set;
59
60 static void
61 usage (void)
62 {
63   printf ("hostinfod (virt-hostinfo daemon)\n"
64           "Copyright (C) 2009 Red Hat Inc.\n"
65           "\n"
66           "Usage:\n"
67           "  hostinfod [--options]\n"
68           "\n"
69           "Options:\n"
70           "  --help           Display full usage\n"
71           "  -c file | --config file\n"
72           "                   Configuration file (default: %s)\n"
73           "  -f | --foreground\n"
74           "                   Run in the foreground (don't fork)\n"
75           "  -v               Enable verbose messages (sent to syslog, and to\n"
76           "                     stderr if -d option is given)\n",
77           DEFAULT_CONF_FILE);
78 }
79
80 int
81 main (int argc, char *argv[])
82 {
83   static const apr_getopt_option_t options[] = {
84     { "config", 'c', TRUE, "configuration file" },
85     { "foreground", 'f', FALSE, "run in foreground (don't fork)" },
86     { "verbose", 'v', FALSE, "enable verbose messages" },
87     { "help", '?', FALSE, "display help" },
88     { NULL, 0, 0, NULL },
89   };
90   apr_status_t r;
91   apr_getopt_t *opt;
92   int c;
93   const char *optarg;
94
95   apr_initialize ();
96   apr_pool_create (&pool, NULL);
97
98   apr_getopt_init (&opt, pool, argc, argv);
99
100   init_syslog ();
101
102   socket_dir = apr_pstrdup (pool, DEFAULT_SOCKET_DIR);
103   guests_file = apr_pstrdup (pool, DEFAULT_GUESTS_FILE);
104
105   /* Command line. */
106   while ((r = apr_getopt_long (opt, options, &c, &optarg)) == APR_SUCCESS) {
107     switch (c) {
108     case 'c':
109       conf_file = optarg;
110       /* If the user is specifying this on the command line, then
111        * it should exist.  They may have typo'd the name.
112        */
113       if (access (conf_file, R_OK) == -1) {
114         perrorf ("%s", conf_file);
115         exit (1);
116       }
117       break;
118     case 'f':
119       foreground = 1;
120       foreground_set_on_cmdline = 1;
121       break;
122     case 'v':
123       verbose = 1;
124       verbose_set_on_cmdline = 1;
125       break;
126     case '?':
127       usage ();
128       exit (0);
129     default:
130       abort ();
131     }
132   }
133   if (r != APR_EOF) {
134     fprintf (stderr, "%s: unknown command line option\n", argv[0]);
135     exit (1);
136   }
137
138   /* Read the config file. */
139   read_main_conf_file ();
140
141   /* Monitor the socket directory. */
142   monitor_socket_dir ();
143
144   /* Create the initial pollset, just containing inotify socket. */
145   r = apr_pollset_create (&set, 1024 /* ? */, pool, 0);
146   if (r != APR_SUCCESS) {
147     paprerror (r, "apr_pollset_create");
148     exit (1);
149   }
150   apr_socket_t *tsock;
151   r = apr_os_sock_put (&tsock, &sockets_inotify_fd, pool);
152   if (r != APR_SUCCESS) {
153     paprerror (r, "apr_os_sock_put");
154     exit (1);
155   }
156   apr_pollfd_t *tpollfd = apr_palloc (pool, sizeof *tpollfd);
157   tpollfd->p = pool;
158   tpollfd->desc_type = APR_POLL_SOCKET;
159   tpollfd->reqevents = APR_POLLIN;
160   tpollfd->rtnevents = 0;
161   tpollfd->desc.s = tsock;
162   tpollfd->client_data = set_reread_socket_dir;
163   r = apr_pollset_add (set, tpollfd);
164   if (r != APR_SUCCESS) {
165     paprerror (r, "apr_pollset_add");
166     exit (1);
167   }
168
169   /* Daemonize. */
170   chdir ("/");
171   if (!foreground) {
172     apr_proc_detach (1);
173
174     /* After we detach from the terminal, all further messages
175      * should just go to syslog.
176      */
177     messages_to_stderr = 0;
178   }
179
180   message (PACKAGE_STRING);
181   main_loop ();
182
183   apr_terminate ();
184   return 0;
185 }
186
187 static void
188 main_loop (void)
189 {
190   apr_status_t r;
191   apr_int32_t numdescs;
192   const apr_pollfd_t *descs;
193   int i;
194
195   while (!quit) {
196     /* A socket has appeared or disappeared from the socket directory. */
197     if (reread_socket_dir) {
198       do_reread_socket_dir ();
199       reread_socket_dir = 0;
200     }
201
202     /* Poll. */
203     r = apr_pollset_poll (set, 0, &numdescs, &descs);
204     if (r != APR_SUCCESS) {
205       paprerror (r, "apr_pollset_poll");
206       exit (1);
207     }
208
209     /* Perform the callbacks. */
210     for (i = 0; i < numdescs; ++i) {
211       poll_callback callback;
212
213       callback = descs[i].client_data;
214       callback (&descs[i]);
215     }
216   }
217 }
218
219 static void
220 set_reread_socket_dir (const apr_pollfd_t *_)
221 {
222   reread_socket_dir = 1;
223 }
224
225 static void
226 do_reread_socket_dir (void)
227 {
228   char buf[256];
229
230   debug ("reading socket directory");
231
232   /* Discard anything which appears on the inotify socket.  We will
233    * reread the whole directory each time.
234    */
235   while (read (sockets_inotify_fd, buf, sizeof buf) > 0)
236     ;
237
238
239
240
241
242
243
244
245
246
247
248 }