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