]> arthur.barton.de Git - netdata.git/blob - src/main.c
proper log file management; re-opening logs on SIGHUP; updated logrotate; updated...
[netdata.git] / src / main.c
1 #include "common.h"
2
3 extern void *cgroups_main(void *ptr);
4
5 void netdata_cleanup_and_exit(int ret) {
6         netdata_exit = 1;
7
8         error_log_limit_unlimited();
9
10         info("Called: netdata_cleanup_and_exit()");
11 #ifdef NETDATA_INTERNAL_CHECKS
12     rrdset_free_all();
13 #else
14         rrdset_save_all();
15 #endif
16         // kill_childs();
17
18         if(pidfile[0]) {
19                 if(unlink(pidfile) != 0)
20                         error("Cannot unlink pidfile '%s'.", pidfile);
21         }
22
23         info("NetData exiting. Bye bye...");
24         exit(ret);
25 }
26
27 struct netdata_static_thread {
28         char *name;
29
30         char *config_section;
31         char *config_name;
32
33         int enabled;
34
35         pthread_t *thread;
36
37         void (*init_routine) (void);
38         void *(*start_routine) (void *);
39 } static_threads[] = {
40 #ifdef INTERNAL_PLUGIN_NFACCT
41 // nfacct requires root access
42         // so, we build it as an external plugin with setuid to root
43         {"nfacct",              "plugins",  "nfacct",     1, NULL, NULL, nfacct_main},
44 #endif
45
46         {"tc",                 "plugins",   "tc",         1, NULL, NULL, tc_main},
47         {"idlejitter",         "plugins",   "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
48         {"proc",               "plugins",   "proc",       1, NULL, NULL, proc_main},
49         {"cgroups",            "plugins",   "cgroups",    1, NULL, NULL, cgroups_main},
50         {"plugins.d",           NULL,       NULL,         1, NULL, NULL, pluginsd_main},
51         {"check",               "plugins",  "checks",     0, NULL, NULL, checks_main},
52         {"web",                 NULL,       NULL,         1, NULL, NULL, socket_listen_main_multi_threaded},
53         {"web-single-threaded", NULL,       NULL,         0, NULL, NULL, socket_listen_main_single_threaded},
54         {NULL,                  NULL,       NULL,         0, NULL, NULL, NULL}
55 };
56
57 void web_server_threading_selection(void) {
58         int threaded = config_get_boolean("global", "multi threaded web server", 1);
59
60         int i;
61         for(i = 0; static_threads[i].name ; i++) {
62                 if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
63                         static_threads[i].enabled = threaded?1:0;
64
65                 if(static_threads[i].start_routine == socket_listen_main_single_threaded)
66                         static_threads[i].enabled = threaded?0:1;
67         }
68
69         web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
70
71         web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
72
73 #ifdef NETDATA_WITH_ZLIB
74         web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
75
76         char *s = config_get("global", "web compression strategy", "default");
77         if(!strcmp(s, "default"))
78                 web_gzip_strategy = Z_DEFAULT_STRATEGY;
79         else if(!strcmp(s, "filtered"))
80                 web_gzip_strategy = Z_FILTERED;
81         else if(!strcmp(s, "huffman only"))
82                 web_gzip_strategy = Z_HUFFMAN_ONLY;
83         else if(!strcmp(s, "rle"))
84                 web_gzip_strategy = Z_RLE;
85         else if(!strcmp(s, "fixed"))
86                 web_gzip_strategy = Z_FIXED;
87         else {
88                 error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
89                 web_gzip_strategy = Z_DEFAULT_STRATEGY;
90         }
91
92         web_gzip_level = (int)config_get_number("global", "web compression level", 3);
93         if(web_gzip_level < 1) {
94                 error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
95                 web_gzip_level = 1;
96         }
97         else if(web_gzip_level > 9) {
98                 error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
99                 web_gzip_level = 9;
100         }
101 #endif /* NETDATA_WITH_ZLIB */
102 }
103
104
105 int killpid(pid_t pid, int sig)
106 {
107         int ret = -1;
108         debug(D_EXIT, "Request to kill pid %d", pid);
109
110         errno = 0;
111         if(kill(pid, 0) == -1) {
112                 switch(errno) {
113                         case ESRCH:
114                                 error("Request to kill pid %d, but it is not running.", pid);
115                                 break;
116
117                         case EPERM:
118                                 error("Request to kill pid %d, but I do not have enough permissions.", pid);
119                                 break;
120
121                         default:
122                                 error("Request to kill pid %d, but I received an error.", pid);
123                                 break;
124                 }
125         }
126         else {
127                 errno = 0;
128                 ret = kill(pid, sig);
129                 if(ret == -1) {
130                         switch(errno) {
131                                 case ESRCH:
132                                         error("Cannot kill pid %d, but it is not running.", pid);
133                                         break;
134
135                                 case EPERM:
136                                         error("Cannot kill pid %d, but I do not have enough permissions.", pid);
137                                         break;
138
139                                 default:
140                                         error("Cannot kill pid %d, but I received an error.", pid);
141                                         break;
142                         }
143                 }
144         }
145
146         return ret;
147 }
148
149 void kill_childs()
150 {
151         siginfo_t info;
152
153         struct web_client *w;
154         for(w = web_clients; w ; w = w->next) {
155                 debug(D_EXIT, "Stopping web client %s", w->client_ip);
156                 pthread_cancel(w->thread);
157                 pthread_join(w->thread, NULL);
158         }
159
160         int i;
161         for (i = 0; static_threads[i].name != NULL ; i++) {
162                 if(static_threads[i].thread) {
163                         debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
164                         pthread_cancel(*static_threads[i].thread);
165                         pthread_join(*static_threads[i].thread, NULL);
166                         static_threads[i].thread = NULL;
167                 }
168         }
169
170         if(tc_child_pid) {
171                 info("Killing tc-qos-helper procees");
172                 if(killpid(tc_child_pid, SIGTERM) != -1)
173                         waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
174         }
175         tc_child_pid = 0;
176
177         struct plugind *cd;
178         for(cd = pluginsd_root ; cd ; cd = cd->next) {
179                 debug(D_EXIT, "Stopping %s plugin thread", cd->id);
180                 pthread_cancel(cd->thread);
181                 pthread_join(cd->thread, NULL);
182
183                 if(cd->pid && !cd->obsolete) {
184                         debug(D_EXIT, "killing %s plugin process", cd->id);
185                         if(killpid(cd->pid, SIGTERM) != -1)
186                                 waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
187                 }
188         }
189
190         // if, for any reason there is any child exited
191         // catch it here
192         waitid(P_PID, 0, &info, WEXITED|WNOHANG);
193
194         debug(D_EXIT, "All threads/childs stopped.");
195 }
196
197 struct option_def options[] = {
198         // opt description                                                       arg name                     default value
199         {'c', "Load alternate configuration file",                               "config_file",                          CONFIG_DIR "/" CONFIG_FILENAME},
200         {'D', "Disable fork into background",                                    NULL,                                   NULL},
201         {'h', "Display help message",                                            NULL,                                   NULL},
202         {'P', "File to save a pid while running",                                "FILE",                                 NULL},
203         {'i', "The IP address to listen to.",                                    "address",                              "All addresses"},
204         {'p', "Port to listen. Can be from 1 to 65535.",                         "port_number",                          "19999"},
205         {'s', "Path to access host /proc and /sys when running in a container.", "PATH",                                 NULL},
206         {'t', "The frequency in seconds, for data collection. \
207 Same as 'update every' config file option.",                                 "seconds",                              "1"},
208         {'u', "System username to run as.",                                      "username",                             "netdata"},
209         {'v', "Version of the program",                                          NULL,                                   NULL},
210         {'W', "vendor options.",                                                 "stacksize=<size>|unittest|debug_flag", NULL},
211 };
212
213 void help(int exitcode) {
214         FILE *stream;
215         if(exitcode == 0)
216                 stream = stdout;
217         else
218                 stream = stderr;
219
220         int num_opts = sizeof(options) / sizeof(struct option_def);
221         int i;
222         int max_len_arg = 0;
223
224         // Compute maximum argument length
225         for( i = 0; i < num_opts; i++ ) {
226                 if(options[i].arg_name) {
227                         int len_arg = strlen(options[i].arg_name);
228                         if(len_arg > max_len_arg) max_len_arg = len_arg;
229                 }
230         }
231
232         fprintf(stream, "SYNOPSIS: netdata [options]\n");
233         fprintf(stream, "\n");
234         fprintf(stream, "Options:\n");
235
236         // Output options description.
237         for( i = 0; i < num_opts; i++ ) {
238                 fprintf(stream, "  -%c %-*s  %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description);
239                 if(options[i].default_value) {
240                         fprintf(stream, " Default: %s\n", options[i].default_value);
241                 } else {
242                         fprintf(stream, "\n");
243                 }
244         }
245
246         fflush(stream);
247         exit(exitcode);
248 }
249
250 // TODO: Remove this function with the nix major release.
251 void remove_option(int opt_index, int *argc, char **argv) {
252         int i = opt_index;
253         // remove the options.
254         do {
255                 *argc = *argc - 1;
256                 for(i = opt_index; i < *argc; i++) {
257                         argv[i] = argv[i+1];
258                 }
259                 i = opt_index;
260         } while(argv[i][0] != '-' && opt_index >= *argc);
261 }
262
263
264 int main(int argc, char **argv)
265 {
266         int i;
267         int config_loaded = 0;
268         int dont_fork = 0;
269         size_t wanted_stacksize = 0, stacksize = 0;
270         pthread_attr_t attr;
271
272         // global initialization
273         get_HZ();
274
275         // set the name for logging
276         program_name = "netdata";
277
278         // parse command line.
279
280         // parse depercated options
281         // TODO: Remove this block with the next major release.
282         {
283                 i = 1;
284                 while(i < argc) {
285                         if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
286                                 strncpyz(pidfile, argv[i+1], FILENAME_MAX);
287                                 fprintf(stderr, "%s: deprecated option -- %s -- please use -P instead.\n", argv[0], argv[i]);
288                                 remove_option(i, &argc, argv);
289                         }
290                         else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
291                                 dont_fork = 1;
292                                 fprintf(stderr, "%s: deprecated option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
293                                 remove_option(i, &argc, argv);
294                         }
295                         else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
296                                 config_set("global", "host access prefix", argv[i+1]);
297                                 fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
298                                 remove_option(i, &argc, argv);
299                         }
300                         else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
301                                 config_set("global", "history", argv[i+1]);
302                                 fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
303                                 remove_option(i, &argc, argv);
304                         }
305                         else i++;
306                 }
307         }
308
309         // parse options
310         {
311                 int num_opts = sizeof(options) / sizeof(struct option_def);
312                 char optstring[(num_opts * 2) + 1];
313
314                 int string_i = 0;
315                 for( i = 0; i < num_opts; i++ ) {
316                         optstring[string_i] = options[i].val;
317                         string_i++;
318                         if(options[i].arg_name) {
319                                 optstring[string_i] = ':';
320                                 string_i++;
321                         }
322                 }
323
324                 int opt;
325                 while( (opt = getopt(argc, argv, optstring)) != -1 ) {
326                         switch(opt) {
327                                 case 'c':
328                                         if(load_config(optarg, 1) != 1) {
329                                                 error("Cannot load configuration file %s.", optarg);
330                                                 exit(1);
331                                         }
332                                         else {
333                                                 debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
334                                                 config_loaded = 1;
335                                         }
336                                         break;
337                                 case 'D':
338                                         dont_fork = 1;
339                                         break;
340                                 case 'h':
341                                         help(0);
342                                         break;
343                                 case 'i':
344                                         config_set("global", "bind to", optarg);
345                                         break;
346                                 case 'P':
347                                         strncpy(pidfile, optarg, FILENAME_MAX);
348                                         pidfile[FILENAME_MAX] = '\0';
349                                         break;
350                                 case 'p':
351                                         config_set("global", "default port", optarg);
352                                         break;
353                                 case 's':
354                                         config_set("global", "host access prefix", optarg);
355                                         break;
356                                 case 't':
357                                         config_set("global", "update every", optarg);
358                                         break;
359                                 case 'u':
360                                         config_set("global", "run as user", optarg);
361                                         break;
362                                 case 'v':
363                                         // TODO: Outsource version to makefile which can compute version from git.
364                                         printf("netdata 1.2.1_master\n");
365                                         return 0;
366                                         break;
367                                 case 'W':
368                                         {
369                                                 char* stacksize = "stacksize=";
370                                                 char* debug_flags_string = "debug_flags=";
371                                                 if(strcmp(optarg, "unittest") == 0) {
372                                                         rrd_update_every = 1;
373                                                         if(run_all_mockup_tests()) exit(1);
374                                                         if(unit_test_storage()) exit(1);
375                                                         fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
376                                                         exit(0);
377                                                 } else if(strncmp(optarg, stacksize, strlen(stacksize)) == 0) {
378                                                         optarg += strlen(stacksize);
379                                                         config_set("global", "pthread stack size", optarg);
380                                                 } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
381                                                         optarg += strlen(debug_flags_string);
382                                                         config_set("global", "debug flags",  optarg);
383                                                         debug_flags = strtoull(optarg, NULL, 0);
384                                                 }
385                                         }
386                                         break;
387                                 default: /* ? */
388                                         help(1);
389                                         break;
390                         }
391                 }
392         }
393
394         if(!config_loaded) load_config(NULL, 0);
395
396         // prepare configuration environment variables for the plugins
397         setenv("NETDATA_CONFIG_DIR" , config_get("global", "config directory"   , CONFIG_DIR) , 1);
398         setenv("NETDATA_PLUGINS_DIR", config_get("global", "plugins directory"  , PLUGINS_DIR), 1);
399         setenv("NETDATA_WEB_DIR"    , config_get("global", "web files directory", WEB_DIR)    , 1);
400         setenv("NETDATA_CACHE_DIR"  , config_get("global", "cache directory"    , CACHE_DIR)  , 1);
401         setenv("NETDATA_LIB_DIR"    , config_get("global", "lib directory"      , VARLIB_DIR) , 1);
402         setenv("NETDATA_LOG_DIR"    , config_get("global", "log directory"      , LOG_DIR)    , 1);
403         setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "")         , 1);
404         setenv("HOME"               , config_get("global", "home directory"     , CACHE_DIR)  , 1);
405
406         // disable buffering for python plugins
407         setenv("PYTHONUNBUFFERED", "1", 1);
408
409         // avoid flood calls to stat(/etc/localtime)
410         // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
411         setenv("TZ", ":/etc/localtime", 0);
412
413         {
414                 char path[1024 + 1], *p = getenv("PATH");
415                 if(!p) p = "/bin:/usr/bin";
416                 snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
417                 setenv("PATH", config_get("plugins", "PATH environment variable", path), 1);
418         }
419
420         // cd to /tmp to avoid any plugins writing files at random places
421         if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
422
423         char *user = NULL;
424         {
425                 char *flags = config_get("global", "debug flags",  "0x00000000");
426                 setenv("NETDATA_DEBUG_FLAGS", flags, 1);
427
428                 debug_flags = strtoull(flags, NULL, 0);
429                 debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
430
431                 if(debug_flags != 0) {
432                         struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
433                         if(setrlimit(RLIMIT_CORE, &rl) != 0)
434                                 info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
435                         prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
436                 }
437
438                 // --------------------------------------------------------------------
439
440 #ifdef MADV_MERGEABLE
441                 enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
442 #else
443 #warning "Kernel memory deduplication (KSM) is not available"
444 #endif
445
446                 // --------------------------------------------------------------------
447
448                 global_host_prefix = config_get("global", "host access prefix", "");
449                 setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
450
451                 // --------------------------------------------------------------------
452
453                 stdout_filename    = config_get("global", "debug log",  LOG_DIR "/debug.log");
454                 stderr_filename    = config_get("global", "error log",  LOG_DIR "/error.log");
455                 stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
456
457                 error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
458                 setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period"    , ""), 1);
459
460                 error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
461                 setenv("NETDATA_ERRORS_PER_PERIOD"     , config_get("global", "errors to trigger flood protection", ""), 1);
462
463                 // --------------------------------------------------------------------
464
465                 rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
466
467                 // --------------------------------------------------------------------
468
469                 {
470                         char hostnamebuf[HOSTNAME_MAX + 1];
471                         if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
472                                 error("WARNING: Cannot get machine hostname.");
473                         hostname = config_get("global", "hostname", hostnamebuf);
474                         debug(D_OPTIONS, "hostname set to '%s'", hostname);
475                 }
476
477                 // --------------------------------------------------------------------
478
479                 rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
480                 if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
481                         info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
482                         rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
483                 }
484                 else {
485                         debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
486                 }
487
488                 // --------------------------------------------------------------------
489
490                 rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
491                 if(rrd_update_every < 1 || rrd_update_every > 600) {
492                         info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
493                         rrd_update_every = UPDATE_EVERY;
494                 }
495                 else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
496
497                 // let the plugins know the min update_every
498                 {
499                         char buf[16];
500                         snprintfz(buf, 15, "%d", rrd_update_every);
501                         setenv("NETDATA_UPDATE_EVERY", buf, 1);
502                 }
503
504                 // --------------------------------------------------------------------
505
506                 // block signals while initializing threads.
507                 // this causes the threads to block signals.
508                 sigset_t sigset;
509                 sigfillset(&sigset);
510
511                 if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
512                         error("Could not block signals for threads");
513                 }
514
515                 // Catch signals which we want to use
516                 struct sigaction sa;
517                 sigemptyset(&sa.sa_mask);
518                 sigaddset(&sa.sa_mask, SIGINT);
519                 sigaddset(&sa.sa_mask, SIGTERM);
520                 sa.sa_handler = sig_handler_exit;
521                 sa.sa_flags = 0;
522                 if(sigaction(SIGINT, &sa, NULL) == -1) {
523                         error("Failed to change signal handler for SIGINT");
524                 }
525                 if(sigaction(SIGTERM, &sa, NULL) == -1) {
526                         error("Failed to change signal handler for SIGTERM");
527                 }
528
529         sigemptyset(&sa.sa_mask);
530         sigaddset(&sa.sa_mask, SIGHUP);
531         sa.sa_handler = sig_handler_logrotate;
532         sa.sa_flags = 0;
533         if(sigaction(SIGHUP, &sa, NULL) == -1) {
534             error("Failed to change signal handler for SIGHUP");
535         }
536
537                 // save database on SIGUSR1
538         sigemptyset(&sa.sa_mask);
539         sigaddset(&sa.sa_mask, SIGUSR1);
540                 sa.sa_handler = sig_handler_save;
541                 if(sigaction(SIGUSR1, &sa, NULL) == -1) {
542                         error("Failed to change signal handler for SIGUSR1");
543                 }
544
545                 // Ignore SIGPIPE completely.
546                 // INFO: If we add signals here we have to unblock them
547                 // at popen.c when running a external plugin.
548         sigemptyset(&sa.sa_mask);
549         sigaddset(&sa.sa_mask, SIGPIPE);
550                 sa.sa_handler = SIG_IGN;
551                 if(sigaction(SIGPIPE, &sa, NULL) == -1) {
552                         error("Failed to change signal handler for SIGPIPE");
553                 }
554
555                 // --------------------------------------------------------------------
556
557                 i = pthread_attr_init(&attr);
558                 if(i != 0)
559                         fatal("pthread_attr_init() failed with code %d.", i);
560
561                 i = pthread_attr_getstacksize(&attr, &stacksize);
562                 if(i != 0)
563                         fatal("pthread_attr_getstacksize() failed with code %d.", i);
564                 else
565                         debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
566
567                 wanted_stacksize = config_get_number("global", "pthread stack size", stacksize);
568
569                 // --------------------------------------------------------------------
570
571                 for (i = 0; static_threads[i].name != NULL ; i++) {
572                         struct netdata_static_thread *st = &static_threads[i];
573
574                         if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
575                         if(st->enabled && st->init_routine) st->init_routine();
576                 }
577
578                 // --------------------------------------------------------------------
579
580                 // get the user we should run
581                 // IMPORTANT: this is required before web_files_uid()
582                 user = config_get("global", "run as user"    , (getuid() == 0)?NETDATA_USER:"");
583
584                 // IMPORTANT: these have to run once, while single threaded
585                 web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
586                 web_files_gid();
587
588                 // --------------------------------------------------------------------
589
590                 create_listen_sockets();
591         }
592
593     // initialize the log files
594     open_all_log_files();
595
596     // become daemon, switch user, create pid file, set process attributes
597     if(become_daemon(dont_fork, user) == -1)
598                 fatal("Cannot demonize myself.");
599
600 #ifdef NETDATA_INTERNAL_CHECKS
601         if(debug_flags != 0) {
602                 struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
603                 if(setrlimit(RLIMIT_CORE, &rl) != 0)
604                         info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
605                 prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
606         }
607 #endif /* NETDATA_INTERNAL_CHECKS */
608
609         info("NetData started on pid %d", getpid());
610
611
612         // ------------------------------------------------------------------------
613         // get default pthread stack size
614
615         if(stacksize < wanted_stacksize) {
616                 i = pthread_attr_setstacksize(&attr, wanted_stacksize);
617                 if(i != 0)
618                         fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
619                 else
620                         info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
621         }
622
623         // --------------------------------------------------------------------
624         // initialize the registry
625
626         registry_init();
627
628         // ------------------------------------------------------------------------
629         // spawn the threads
630
631         web_server_threading_selection();
632
633         for (i = 0; static_threads[i].name != NULL ; i++) {
634                 struct netdata_static_thread *st = &static_threads[i];
635
636                 if(st->enabled) {
637                         st->thread = mallocz(sizeof(pthread_t));
638
639                         info("Starting thread %s.", st->name);
640
641                         if(pthread_create(st->thread, &attr, st->start_routine, NULL))
642                                 error("failed to create new thread for %s.", st->name);
643
644                         else if(pthread_detach(*st->thread))
645                                 error("Cannot request detach of newly created %s thread.", st->name);
646                 }
647                 else info("Not starting thread %s.", st->name);
648         }
649
650         // ------------------------------------------------------------------------
651         // block signals while initializing threads.
652         sigset_t sigset;
653         sigfillset(&sigset);
654
655         if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
656                 error("Could not unblock signals for threads");
657         }
658
659         // Handle flags set in the signal handler.
660         while(1) {
661                 pause();
662                 if(netdata_exit) {
663                         info("Exit main loop of netdata.");
664                         netdata_cleanup_and_exit(0);
665                         exit(0);
666                 }
667         }
668 }