root/src/noitd.c

Revision 66c613e1604818b3d967f3e69ae30abd04be5a8b, 7.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

integrate command line docs as usage, refs #21

  • 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   }
190
191   /* initialize the eventer */
192   if(eventer_init() == -1) {
193     noitL(noit_stderr, "Cannot initialize eventer\n");
194     exit(-1);
195   }
196
197   /* Setup our hearbeat */
198   e = eventer_alloc();
199   e->mask = EVENTER_RECURRENT;
200   e->callback = watchdog_tick;
201   eventer_add_recurrent(e);
202
203   /* Initialize all of our listeners */
204   noit_console_init();
205   noit_jlog_listener_init();
206
207   noit_module_init();
208
209   /* Drop privileges */
210   if(chrootpath && noit_security_chroot(chrootpath)) {
211     noitL(noit_stderr, "Failed to chroot(), exiting.\n");
212     exit(-1);
213   }
214   if(noit_security_usergroup(droptouser, droptogroup)) {
215     noitL(noit_stderr, "Failed to drop privileges, exiting.\n");
216     exit(-1);
217   }
218
219   /* Prepare for launch... */
220   noit_filters_init();
221   noit_poller_init();
222   noit_listener_init(APPNAME);
223
224   /* Write our log out, and setup a watchdog to write it out on change. */
225   noit_conf_write_log(NULL);
226   noit_conf_coalesce_changes(10); /* 10 seconds of no changes before we write */
227   noit_conf_watch_and_journal_watchdog(noit_conf_write_log, NULL);
228
229   eventer_loop();
230   return 0;
231 }
232
233 int main(int argc, char **argv) {
234   char conf_str[1024];
235
236   parse_clargs(argc, argv);
237
238   /* First initialize logging, so we can log errors */
239   noit_log_init();
240   noit_log_stream_add_stream(noit_debug, noit_stderr);
241   noit_log_stream_add_stream(noit_error, noit_stderr);
242
243   /* Next load the configs */
244   noit_conf_init(APPNAME);
245   noit_conf_checks_init(APPNAME);
246   if(noit_conf_load(config_file) == -1) {
247     fprintf(stderr, "Cannot load config: '%s'\n", config_file);
248   }
249
250   /* Reinitialize the logging system now that we have a config */
251   noit_conf_log_init(APPNAME);
252   if(debug)
253     noit_debug->enabled = 1;
254
255   /* Lastly, run through all other system inits */
256   if(!noit_conf_get_stringbuf(NULL, "/" APPNAME "/eventer/@implementation",
257                               conf_str, sizeof(conf_str))) {
258     noitL(noit_stderr, "Cannot find '%s' in configuration\n",
259           "/" APPNAME "/eventer/@implementation");
260     exit(-1);
261   }
262   if(eventer_choose(conf_str) == -1) {
263     noitL(noit_stderr, "Cannot choose eventer %s\n", conf_str);
264     exit(-1);
265   }
266   if(configure_eventer() != 0) {
267     noitL(noit_stderr, "Cannot configure eventer\n");
268     exit(-1);
269   }
270
271   setup_mmap();
272
273   chdir("/");
274   if(foreground) return child_main();
275
276   close(STDIN_FILENO);
277   close(STDOUT_FILENO);
278   close(STDERR_FILENO);
279   if(fork()) exit(0);
280   setsid();
281   if(fork()) exit(0);
282
283   return watch_over_child(child_main);
284 }
Note: See TracBrowser for help on using the browser.