root/src/noitd.c

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

fixes #31

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