root/src/noitd.c

Revision 781897bd0a05bbae82234e01395da26f6542a4ae, 9.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

Make SIGHUP perform reload, fixes #124

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007-2009, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 #include "noit_defines.h"
33
34 #include <assert.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <sys/ioctl.h>
40 #include <fcntl.h>
41 #include <sys/mman.h>
42 #include <signal.h>
43 #ifdef HAVE_SYS_WAIT_H
44 #include <sys/wait.h>
45 #endif
46
47 #include "eventer/eventer.h"
48 #include "utils/noit_log.h"
49 #include "utils/noit_hash.h"
50 #include "utils/noit_security.h"
51 #include "noit_listener.h"
52 #include "noit_console.h"
53 #include "noit_jlog_listener.h"
54 #include "noit_livestream_listener.h"
55 #include "noit_capabilities_listener.h"
56 #include "noit_module.h"
57 #include "noit_conf.h"
58 #include "noit_conf_checks.h"
59 #include "noit_filters.h"
60
61 #define APPNAME "noit"
62 #define CHILD_WATCHDOG_TIMEOUT 5 /*seconds*/
63
64 static char *config_file = ETC_DIR "/" APPNAME ".conf";
65 static const char *droptouser = NULL;
66 static const char *droptogroup = NULL;
67 static const char *chrootpath = NULL;
68 static int foreground = 0;
69 static int debug = 0;
70
71 #include "man/noitd.usage.h"
72 static void usage(const char *progname) {
73   printf("Usage for %s:\n", progname);
74 #ifdef NOITD_USAGE
75   assert(write(STDOUT_FILENO,
76                NOITD_USAGE,
77                sizeof(NOITD_USAGE)-1) == sizeof(NOITD_USAGE)-1);
78 #else
79   printf("\nError in usage, build problem.\n");
80 #endif
81   return;
82 }
83 void parse_clargs(int argc, char **argv) {
84   int c;
85   while((c = getopt(argc, argv, "hc:dDu:g:t:")) != EOF) {
86     switch(c) {
87       case 'h':
88         usage(argv[0]);
89         exit(1);
90         break;
91       case 'u':
92         droptouser = strdup(optarg);
93         break;
94       case 'g':
95         droptogroup = strdup(optarg);
96         break;
97       case 't':
98         chrootpath = strdup(optarg);
99         break;
100       case 'c':
101         config_file = strdup(optarg);
102         break;
103       case 'D':
104         foreground = 1;
105         break;
106       case 'd':
107         debug++;
108         break;
109       default:
110         break;
111     }
112   }
113 }
114
115 static
116 int configure_eventer() {
117   int rv = 0;
118   noit_hash_table *table;
119   table = noit_conf_get_hash(NULL, "/" APPNAME "/eventer/config");
120   if(table) {
121     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
122     const char *key, *value;
123     int klen;
124     while(noit_hash_next_str(table, &iter, &key, &klen, &value)) {
125       int subrv;
126       if((subrv = eventer_propset(key, value)) != 0)
127         rv = subrv;
128     }
129     noit_hash_destroy(table, free, free);
130     free(table);
131   }
132   return rv;
133 }
134
135 /* Watchdog stuff */
136 static int *lifeline = NULL;
137 static unsigned long last_tick_time() {
138   static struct timeval lastchange = { 0, 0 };
139   static int lastcheck = 0;
140   struct timeval now, diff;
141
142   gettimeofday(&now, NULL);
143   if(lastcheck != *lifeline) {
144     lastcheck = *lifeline;
145     memcpy(&lastchange, &now, sizeof(lastchange));
146   }
147   sub_timeval(now, lastchange, &diff);
148   return (unsigned long)diff.tv_sec;
149 }
150 static void it_ticks() {
151   (*lifeline)++;
152 }
153 static void setup_mmap() {
154   lifeline = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE,
155                   MAP_SHARED|MAP_ANON, -1, 0);
156   if(lifeline == (void *)-1) {
157     noitL(noit_error, "Failed to mmap anon for watchdog\n");
158     exit(-1);
159   }
160 }
161
162 static int watch_over_child(int (*func)()) {
163   int child_pid;
164   while(1) {
165     child_pid = fork();
166     if(child_pid == -1) {
167       noitL(noit_error, "fork failed: %s\n", strerror(errno));
168       exit(-1);
169     }
170     if(child_pid == 0) {
171       /* This sets up things so we start alive */
172       it_ticks();
173       /* run the program */
174       exit(func());
175     }
176     else {
177       int sig = -1, exit_val = -1;
178       signal(SIGHUP, SIG_IGN);
179       while(1) {
180         unsigned long ltt;
181         int status, rv;
182         sleep(1); /* Just check child status every second */
183         rv = waitpid(child_pid, &status, WNOHANG);
184         if(rv == 0) {
185           /* Nothing */
186         }
187         else if (rv == child_pid) {
188           /* We died!... we need to relaunch, unless the status was a requested exit (2) */
189           sig = WTERMSIG(status);
190           exit_val = WEXITSTATUS(status);
191           if(sig == SIGINT || sig == SIGQUIT ||
192              (sig == 0 && (exit_val == 2 || exit_val < 0))) {
193             noitL(noit_error, "noitd shutdown acknowledged.\n");
194             exit(0);
195           }
196           break;
197         }
198         else {
199           noitL(noit_error, "Unexpected return from waitpid: %d\n", rv);
200           exit(-1);
201         }
202         /* Now check out timeout */
203         if((ltt = last_tick_time()) > CHILD_WATCHDOG_TIMEOUT) {
204           noitL(noit_error,
205                 "Watchdog timeout (%lu s)... terminating child\n",
206                 ltt);
207           kill(child_pid, SIGKILL);
208         }
209       }
210       noitL(noit_error, "noitd child died [%d/%d], restarting.\n", exit_val, sig);
211     }
212   }
213 }
214
215 static int __reload_needed = 0;
216 static void request_conf_reload(int sig) {
217   if(sig == SIGHUP) {
218     __reload_needed = 1;
219   }
220 }
221 static int watchdog_tick(eventer_t e, int mask, void *unused, struct timeval *now) {
222   it_ticks();
223   if(__reload_needed) {
224     noitL(noit_error, "SIGHUP received, performing reload\n");
225     if(noit_conf_load(config_file) == -1) {
226       noitL(noit_error, "Cannot load config: '%s'\n", config_file);
227       exit(-1);
228     }
229     __reload_needed = 0;
230   }
231   return 0;
232 }
233 static int child_main() {
234   eventer_t e;
235
236   /* Load our config...
237    * to ensure it is current w.r.t. to this child starting */
238   if(noit_conf_load(config_file) == -1) {
239     noitL(noit_error, "Cannot load config: '%s'\n", config_file);
240     exit(-1);
241   }
242
243   signal(SIGHUP, request_conf_reload);
244
245   /* initialize the eventer */
246   if(eventer_init() == -1) {
247     noitL(noit_stderr, "Cannot initialize eventer\n");
248     exit(-1);
249   }
250
251   /* Setup our hearbeat */
252   e = eventer_alloc();
253   e->mask = EVENTER_RECURRENT;
254   e->callback = watchdog_tick;
255   eventer_add_recurrent(e);
256
257   /* Initialize all of our listeners */
258   noit_console_init(APPNAME);
259   noit_capabilities_listener_init();
260   noit_jlog_listener_init();
261   noit_livestream_listener_init();
262
263   noit_module_init();
264
265   /* Drop privileges */
266   if(chrootpath && noit_security_chroot(chrootpath)) {
267     noitL(noit_stderr, "Failed to chroot(), exiting.\n");
268     exit(-1);
269   }
270   if(noit_security_usergroup(droptouser, droptogroup)) {
271     noitL(noit_stderr, "Failed to drop privileges, exiting.\n");
272     exit(-1);
273   }
274
275   /* Prepare for launch... */
276   noit_filters_init();
277   noit_poller_init();
278   noit_listener_init(APPNAME);
279
280   /* Write our log out, and setup a watchdog to write it out on change. */
281   noit_conf_write_log(NULL);
282   noit_conf_coalesce_changes(10); /* 10 seconds of no changes before we write */
283   noit_conf_watch_and_journal_watchdog(noit_conf_write_log, NULL);
284
285   eventer_loop();
286   return 0;
287 }
288
289 int main(int argc, char **argv) {
290   char conf_str[1024];
291
292   parse_clargs(argc, argv);
293
294   /* First initialize logging, so we can log errors */
295   noit_log_init();
296   noit_log_stream_add_stream(noit_debug, noit_stderr);
297   noit_log_stream_add_stream(noit_error, noit_stderr);
298
299   /* Next load the configs */
300   noit_conf_init(APPNAME);
301   noit_conf_checks_init(APPNAME);
302   if(noit_conf_load(config_file) == -1) {
303     fprintf(stderr, "Cannot load config: '%s'\n", config_file);
304   }
305
306   /* Reinitialize the logging system now that we have a config */
307   noit_conf_log_init(APPNAME);
308   if(debug)
309     noit_debug->enabled = 1;
310
311   /* Lastly, run through all other system inits */
312   if(!noit_conf_get_stringbuf(NULL, "/" APPNAME "/eventer/@implementation",
313                               conf_str, sizeof(conf_str))) {
314     noitL(noit_stderr, "Cannot find '%s' in configuration\n",
315           "/" APPNAME "/eventer/@implementation");
316     exit(-1);
317   }
318   if(eventer_choose(conf_str) == -1) {
319     noitL(noit_stderr, "Cannot choose eventer %s\n", conf_str);
320     exit(-1);
321   }
322   if(configure_eventer() != 0) {
323     noitL(noit_stderr, "Cannot configure eventer\n");
324     exit(-1);
325   }
326
327   setup_mmap();
328
329   if(chdir("/") != 0) {
330     noitL(noit_stderr, "Failed chdir(\"/\"): %s\n", strerror(errno));
331     exit(-1);
332   }
333   if(foreground) return child_main();
334
335   close(STDIN_FILENO);
336   close(STDOUT_FILENO);
337   close(STDERR_FILENO);
338   if(fork()) exit(0);
339   setsid();
340   if(fork()) exit(0);
341
342   return watch_over_child(child_main);
343 }
Note: See TracBrowser for help on using the browser.