]> arthur.barton.de Git - netdata.git/blob - src/main.c
code cleanup by replacing all memory allocation functions with ones that handle excep...
[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 *input_log_file = NULL;
424         char *output_log_file = NULL;
425         char *error_log_file = NULL;
426         char *access_log_file = NULL;
427         char *user = NULL;
428         {
429                 char *flags = config_get("global", "debug flags",  "0x00000000");
430                 setenv("NETDATA_DEBUG_FLAGS", flags, 1);
431
432                 debug_flags = strtoull(flags, NULL, 0);
433                 debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
434
435                 if(debug_flags != 0) {
436                         struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
437                         if(setrlimit(RLIMIT_CORE, &rl) != 0)
438                                 info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
439                         prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
440                 }
441
442                 // --------------------------------------------------------------------
443
444 #ifdef MADV_MERGEABLE
445                 enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
446 #else
447 #warning "Kernel memory deduplication (KSM) is not available"
448 #endif
449
450                 // --------------------------------------------------------------------
451
452
453                 global_host_prefix = config_get("global", "host access prefix", "");
454                 setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
455
456                 // --------------------------------------------------------------------
457
458                 output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
459                 if(strcmp(output_log_file, "syslog") == 0) {
460                         output_log_syslog = 1;
461                         output_log_file = NULL;
462                 }
463                 else if(strcmp(output_log_file, "none") == 0) {
464                         output_log_syslog = 0;
465                         output_log_file = NULL;
466                 }
467                 else output_log_syslog = 0;
468
469                 // --------------------------------------------------------------------
470
471                 error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
472                 if(strcmp(error_log_file, "syslog") == 0) {
473                         error_log_syslog = 1;
474                         error_log_file = NULL;
475                 }
476                 else if(strcmp(error_log_file, "none") == 0) {
477                         error_log_syslog = 0;
478                         error_log_file = NULL;
479                         // optimization - do not even generate debug log entries
480                 }
481                 else error_log_syslog = 0;
482
483                 error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
484                 setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period"    , ""), 1);
485
486                 error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
487                 setenv("NETDATA_ERRORS_PER_PERIOD"     , config_get("global", "errors to trigger flood protection", ""), 1);
488
489                 // --------------------------------------------------------------------
490
491                 access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
492                 if(strcmp(access_log_file, "syslog") == 0) {
493                         access_log_syslog = 1;
494                         access_log_file = NULL;
495                 }
496                 else if(strcmp(access_log_file, "none") == 0) {
497                         access_log_syslog = 0;
498                         access_log_file = NULL;
499                 }
500                 else access_log_syslog = 0;
501
502                 // --------------------------------------------------------------------
503
504                 rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
505
506                 // --------------------------------------------------------------------
507
508                 {
509                         char hostnamebuf[HOSTNAME_MAX + 1];
510                         if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
511                                 error("WARNING: Cannot get machine hostname.");
512                         hostname = config_get("global", "hostname", hostnamebuf);
513                         debug(D_OPTIONS, "hostname set to '%s'", hostname);
514                 }
515
516                 // --------------------------------------------------------------------
517
518                 rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
519                 if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
520                         info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
521                         rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
522                 }
523                 else {
524                         debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
525                 }
526
527                 // --------------------------------------------------------------------
528
529                 rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
530                 if(rrd_update_every < 1 || rrd_update_every > 600) {
531                         info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
532                         rrd_update_every = UPDATE_EVERY;
533                 }
534                 else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
535
536                 // let the plugins know the min update_every
537                 {
538                         char buf[16];
539                         snprintfz(buf, 15, "%d", rrd_update_every);
540                         setenv("NETDATA_UPDATE_EVERY", buf, 1);
541                 }
542
543                 // --------------------------------------------------------------------
544
545                 // block signals while initializing threads.
546                 // this causes the threads to block signals.
547                 sigset_t sigset;
548                 sigfillset(&sigset);
549
550                 if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
551                         error("Could not block signals for threads");
552                 }
553
554                 // Catch signals which we want to use to quit savely
555                 struct sigaction sa;
556                 sigemptyset(&sa.sa_mask);
557                 sigaddset(&sa.sa_mask, SIGHUP);
558                 sigaddset(&sa.sa_mask, SIGINT);
559                 sigaddset(&sa.sa_mask, SIGTERM);
560                 sa.sa_handler = sig_handler_exit;
561                 sa.sa_flags = 0;
562                 if(sigaction(SIGHUP, &sa, NULL) == -1) {
563                         error("Failed to change signal handler for SIGHUP");
564                 }
565                 if(sigaction(SIGINT, &sa, NULL) == -1) {
566                         error("Failed to change signal handler for SIGINT");
567                 }
568                 if(sigaction(SIGTERM, &sa, NULL) == -1) {
569                         error("Failed to change signal handler for SIGTERM");
570                 }
571
572                 // save database on SIGUSR1
573                 sa.sa_handler = sig_handler_save;
574                 if(sigaction(SIGUSR1, &sa, NULL) == -1) {
575                         error("Failed to change signal handler for SIGUSR1");
576                 }
577
578                 // Ignore SIGPIPE completely.
579                 // INFO: If we add signals here we have to unblock them
580                 // at popen.c when running a external plugin.
581                 sa.sa_handler = SIG_IGN;
582                 if(sigaction(SIGPIPE, &sa, NULL) == -1) {
583                         error("Failed to change signal handler for SIGPIPE");
584                 }
585
586                 // --------------------------------------------------------------------
587
588                 i = pthread_attr_init(&attr);
589                 if(i != 0)
590                         fatal("pthread_attr_init() failed with code %d.", i);
591
592                 i = pthread_attr_getstacksize(&attr, &stacksize);
593                 if(i != 0)
594                         fatal("pthread_attr_getstacksize() failed with code %d.", i);
595                 else
596                         debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
597
598                 wanted_stacksize = config_get_number("global", "pthread stack size", stacksize);
599
600                 // --------------------------------------------------------------------
601
602                 for (i = 0; static_threads[i].name != NULL ; i++) {
603                         struct netdata_static_thread *st = &static_threads[i];
604
605                         if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
606                         if(st->enabled && st->init_routine) st->init_routine();
607                 }
608
609                 // --------------------------------------------------------------------
610
611                 // get the user we should run
612                 // IMPORTANT: this is required before web_files_uid()
613                 user = config_get("global", "run as user"    , (getuid() == 0)?NETDATA_USER:"");
614
615                 // IMPORTANT: these have to run once, while single threaded
616                 web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
617                 web_files_gid();
618
619                 // --------------------------------------------------------------------
620
621                 create_listen_sockets();
622         }
623
624         // never become a problem
625         if(nice(20) == -1) error("Cannot lower my CPU priority.");
626
627         if(become_daemon(dont_fork, 0, user, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1)
628                 fatal("Cannot demonize myself.");
629
630 #ifdef NETDATA_INTERNAL_CHECKS
631         if(debug_flags != 0) {
632                 struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
633                 if(setrlimit(RLIMIT_CORE, &rl) != 0)
634                         info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
635                 prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
636         }
637 #endif /* NETDATA_INTERNAL_CHECKS */
638
639         if(output_log_syslog || error_log_syslog || access_log_syslog)
640                 openlog("netdata", LOG_PID, LOG_DAEMON);
641
642         info("NetData started on pid %d", getpid());
643
644
645         // ------------------------------------------------------------------------
646         // get default pthread stack size
647
648         if(stacksize < wanted_stacksize) {
649                 i = pthread_attr_setstacksize(&attr, wanted_stacksize);
650                 if(i != 0)
651                         fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
652                 else
653                         info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
654         }
655
656         // --------------------------------------------------------------------
657         // initialize the registry
658
659         registry_init();
660
661         // ------------------------------------------------------------------------
662         // spawn the threads
663
664         web_server_threading_selection();
665
666         for (i = 0; static_threads[i].name != NULL ; i++) {
667                 struct netdata_static_thread *st = &static_threads[i];
668
669                 if(st->enabled) {
670                         st->thread = mallocz(sizeof(pthread_t));
671
672                         info("Starting thread %s.", st->name);
673
674                         if(pthread_create(st->thread, &attr, st->start_routine, NULL))
675                                 error("failed to create new thread for %s.", st->name);
676
677                         else if(pthread_detach(*st->thread))
678                                 error("Cannot request detach of newly created %s thread.", st->name);
679                 }
680                 else info("Not starting thread %s.", st->name);
681         }
682
683         // ------------------------------------------------------------------------
684         // block signals while initializing threads.
685         sigset_t sigset;
686         sigfillset(&sigset);
687
688         if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
689                 error("Could not unblock signals for threads");
690         }
691
692         // Handle flags set in the signal handler.
693         while(1) {
694                 pause();
695                 if(netdata_exit) {
696                         info("Exit main loop of netdata.");
697                         netdata_cleanup_and_exit(0);
698                         exit(0);
699                 }
700         }
701 }