root/src/noitd.c

Revision 839ed4b45ead30b4c16f8196190d8e185d1ef116, 7.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

fixes #62

  • Property mode set to 100644
Line 
1 #include "noit_defines.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <sys/ioctl.h>
8 #include <fcntl.h>
9 #include <sys/mman.h>
10 #include <signal.h>
11 #ifdef HAVE_SYS_WAIT_H
12 #include <sys/wait.h>
13 #endif
14
15 #include "eventer/eventer.h"
16 #include "utils/noit_log.h"
17 #include "utils/noit_hash.h"
18 #include "utils/noit_security.h"
19 #include "noit_listener.h"
20 #include "noit_console.h"
21 #include "noit_jlog_listener.h"
22 #include "noit_module.h"
23 #include "noit_conf.h"
24 #include "noit_conf_checks.h"
25 #include "noit_filters.h"
26
27 #define APPNAME "noit"
28 #define CHILD_WATCHDOG_TIMEOUT 5 /*seconds*/
29
30 static char *config_file = ETC_DIR "/" APPNAME ".conf";
31 static const char *droptouser = NULL;
32 static const char *droptogroup = NULL;
33 static const char *chrootpath = NULL;
34 static int foreground = 0;
35 static int debug = 0;
36
37 #include "man/noitd.usage.h"
38 static void usage(const char *progname) {
39   printf("Usage for %s:\n", progname);
40 #ifdef NOITD_USAGE
41   write(STDOUT_FILENO, NOITD_USAGE, sizeof(NOITD_USAGE)-1);
42 #else
43   printf("\nError in usage, build problem.\n");
44 #endif
45   return;
46 }
47 void parse_clargs(int argc, char **argv) {
48   int c;
49   while((c = getopt(argc, argv, "hc:dDu:g:t:")) != EOF) {
50     switch(c) {
51       case 'h':
52         usage(argv[0]);
53         exit(1);
54         break;
55       case 'u':
56         droptouser = strdup(optarg);
57         break;
58       case 'g':
59         droptogroup = strdup(optarg);
60         break;
61       case 't':
62         chrootpath = strdup(optarg);
63         break;
64       case 'c':
65         config_file = strdup(optarg);
66         break;
67       case 'D':
68         foreground = 1;
69         break;
70       case 'd':
71         debug++;
72         break;
73       default:
74         break;
75     }
76   }
77 }
78
79 static
80 int configure_eventer() {
81   int rv = 0;
82   noit_hash_table *table;
83   table = noit_conf_get_hash(NULL, "/" APPNAME "/eventer/config");
84   if(table) {
85     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
86     const char *key, *value;
87     int klen;
88     while(noit_hash_next(table, &iter, &key, &klen, (void **)&value)) {
89       int subrv;
90       if((subrv = eventer_propset(key, value)) != 0)
91         rv = subrv;
92     }
93     noit_hash_destroy(table, free, free);
94     free(table);
95   }
96   return rv;
97 }
98
99 /* Watchdog stuff */
100 static int *lifeline = NULL;
101 static unsigned long last_tick_time() {
102   static struct timeval lastchange = { 0, 0 };
103   static int lastcheck = 0;
104   struct timeval now, diff;
105
106   gettimeofday(&now, NULL);
107   if(lastcheck != *lifeline) {
108     lastcheck = *lifeline;
109     memcpy(&lastchange, &now, sizeof(lastchange));
110   }
111   sub_timeval(now, lastchange, &diff);
112   return (unsigned long)diff.tv_sec;
113 }
114 static void it_ticks() {
115   (*lifeline)++;
116 }
117 static void setup_mmap() {
118   lifeline = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE,
119                   MAP_SHARED|MAP_ANON, -1, 0);
120   if(lifeline == (void *)-1) {
121     noitL(noit_error, "Failed to mmap anon for watchdog\n");
122     exit(-1);
123   }
124 }
125
126 static int watch_over_child(int (*func)()) {
127   int child_pid;
128   while(1) {
129     child_pid = fork();
130     if(child_pid == -1) {
131       noitL(noit_error, "fork failed: %s\n", strerror(errno));
132       exit(-1);
133     }
134     if(child_pid == 0) {
135       /* This sets up things so we start alive */
136       it_ticks();
137       /* run the program */
138       exit(func());
139     }
140     else {
141       int sig = -1, exit_val = -1;
142       while(1) {
143         unsigned long ltt;
144         int status, rv;
145         sleep(1); /* Just check child status every second */
146         rv = waitpid(child_pid, &status, WNOHANG);
147         if(rv == 0) {
148           /* Nothing */
149         }
150         else if (rv == child_pid) {
151           /* We died!... we need to relaunch, unless the status was a requested exit (2) */
152           sig = WTERMSIG(status);
153           exit_val = WEXITSTATUS(status);
154           if(sig == SIGINT || sig == SIGQUIT ||
155              (sig == 0 && (exit_val == 2 || exit_val < 0))) {
156             noitL(noit_error, "noitd shutdown acknowledged.\n");
157             exit(0);
158           }
159           break;
160         }
161         else {
162           noitL(noit_error, "Unexpected return from waitpid: %d\n", rv);
163           exit(-1);
164         }
165         /* Now check out timeout */
166         if((ltt = last_tick_time()) > CHILD_WATCHDOG_TIMEOUT) {
167           noitL(noit_error,
168                 "Watchdog timeout (%lu s)... terminating child\n",
169                 ltt);
170           kill(child_pid, SIGKILL);
171         }
172       }
173       noitL(noit_error, "noitd child died [%d/%d], restarting.\n", exit_val, sig);
174     }
175   }
176 }
177
178 static int watchdog_tick(eventer_t e, int mask, void *unused, struct timeval *now) {
179   it_ticks();
180   return 0;
181 }
182 static int child_main() {
183   eventer_t e;
184
185   /* Load our config...
186    * to ensure it is current w.r.t. to this child starting */
187   if(noit_conf_load(config_file) == -1) {
188     noitL(noit_error, "Cannot load config: '%s'\n", config_file);
189     exit(-1);
190   }
191
192   /* initialize the eventer */
193   if(eventer_init() == -1) {
194     noitL(noit_stderr, "Cannot initialize eventer\n");
195     exit(-1);
196   }
197
198   /* Setup our hearbeat */
199   e = eventer_alloc();
200   e->mask = EVENTER_RECURRENT;
201   e->callback = watchdog_tick;
202   eventer_add_recurrent(e);
203
204   /* Initialize all of our listeners */
205   noit_console_init();
206   noit_jlog_listener_init();
207
208   noit_module_init();
209
210   /* Drop privileges */
211   if(chrootpath && noit_security_chroot(chrootpath)) {
212     noitL(noit_stderr, "Failed to chroot(), exiting.\n");
213     exit(-1);
214   }
215   if(noit_security_usergroup(droptouser, droptogroup)) {
216     noitL(noit_stderr, "Failed to drop privileges, exiting.\n");
217     exit(-1);
218   }
219
220   /* Prepare for launch... */
221   noit_filters_init();
222   noit_poller_init();
223   noit_listener_init(APPNAME);
224
225   /* Write our log out, and setup a watchdog to write it out on change. */
226   noit_conf_write_log(NULL);
227   noit_conf_coalesce_changes(10); /* 10 seconds of no changes before we write */
228   noit_conf_watch_and_journal_watchdog(noit_conf_write_log, NULL);
229
230   eventer_loop();
231   return 0;
232 }
233
234 int main(int argc, char **argv) {
235   char conf_str[1024];
236
237   parse_clargs(argc, argv);
238
239   /* First initialize logging, so we can log errors */
240   noit_log_init();
241   noit_log_stream_add_stream(noit_debug, noit_stderr);
242   noit_log_stream_add_stream(noit_error, noit_stderr);
243
244   /* Next load the configs */
245   noit_conf_init(APPNAME);
246   noit_conf_checks_init(APPNAME);
247   if(noit_conf_load(config_file) == -1) {
248     fprintf(stderr, "Cannot load config: '%s'\n", config_file);
249   }
250
251   /* Reinitialize the logging system now that we have a config */
252   noit_conf_log_init(APPNAME);
253   if(debug)
254     noit_debug->enabled = 1;
255
256   /* Lastly, run through all other system inits */
257   if(!noit_conf_get_stringbuf(NULL, "/" APPNAME "/eventer/@implementation",
258                               conf_str, sizeof(conf_str))) {
259     noitL(noit_stderr, "Cannot find '%s' in configuration\n",
260           "/" APPNAME "/eventer/@implementation");
261     exit(-1);
262   }
263   if(eventer_choose(conf_str) == -1) {
264     noitL(noit_stderr, "Cannot choose eventer %s\n", conf_str);
265     exit(-1);
266   }
267   if(configure_eventer() != 0) {
268     noitL(noit_stderr, "Cannot configure eventer\n");
269     exit(-1);
270   }
271
272   setup_mmap();
273
274   chdir("/");
275   if(foreground) return child_main();
276
277   close(STDIN_FILENO);
278   close(STDOUT_FILENO);
279   close(STDERR_FILENO);
280   if(fork()) exit(0);
281   setsid();
282   if(fork()) exit(0);
283
284   return watch_over_child(child_main);
285 }
Note: See TracBrowser for help on using the browser.