root/src/noitd.c

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

expose version string - untested and unused for now, refs #115

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