]> arthur.barton.de Git - netdata.git/blobdiff - src/main.c
ensure the database is always saved on exit
[netdata.git] / src / main.c
index 3a3d1907fee3d1ab7d0390a413747991357401f9..c4869218f76fec08685058321d23f61f8c633d73 100644 (file)
@@ -7,36 +7,30 @@ void netdata_cleanup_and_exit(int ret) {
 
     error_log_limit_unlimited();
 
-    info("Called: netdata_cleanup_and_exit()");
-#ifdef NETDATA_INTERNAL_CHECKS
-    rrdset_free_all();
-#else
+    debug(D_EXIT, "Called: netdata_cleanup_and_exit()");
+
+    // save the database
     rrdset_save_all();
-#endif
-    // kill_childs();
 
+    // unlink the pid
     if(pidfile[0]) {
         if(unlink(pidfile) != 0)
             error("Cannot unlink pidfile '%s'.", pidfile);
     }
 
-    info("NetData exiting. Bye bye...");
-    exit(ret);
-}
-
-struct netdata_static_thread {
-    char *name;
-
-    char *config_section;
-    char *config_name;
+#ifdef NETDATA_INTERNAL_CHECKS
+    // kill all childs
+    kill_childs();
 
-    int enabled;
+    // free all memory
+    rrdset_free_all();
+#endif
 
-    pthread_t *thread;
+    info("netdata exiting. Bye bye...");
+    exit(ret);
+}
 
-    void (*init_routine) (void);
-    void *(*start_routine) (void *);
-} static_threads[] = {
+struct netdata_static_thread static_threads[] = {
 #ifdef INTERNAL_PLUGIN_NFACCT
 // nfacct requires root access
     // so, we build it as an external plugin with setuid to root
@@ -45,10 +39,18 @@ struct netdata_static_thread {
 
     {"tc",                 "plugins",   "tc",         1, NULL, NULL, tc_main},
     {"idlejitter",         "plugins",   "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
+#if defined(__FreeBSD__)
+    {"freebsd",            "plugins",   "freebsd",    1, NULL, NULL, freebsd_main},
+#elif defined(__APPLE__)
+    {"macos",              "plugins",   "macos",      1, NULL, NULL, macos_main},
+#else
     {"proc",               "plugins",   "proc",       1, NULL, NULL, proc_main},
+#endif /* __FreeBSD__, __APPLE__*/
     {"cgroups",            "plugins",   "cgroups",    1, NULL, NULL, cgroups_main},
+    {"check",              "plugins",   "checks",     0, NULL, NULL, checks_main},
+    {"backends",            NULL,       NULL,         1, NULL, NULL, backends_main},
+    {"health",              NULL,       NULL,         1, NULL, NULL, health_main},
     {"plugins.d",           NULL,       NULL,         1, NULL, NULL, pluginsd_main},
-    {"check",               "plugins",  "checks",     0, NULL, NULL, checks_main},
     {"web",                 NULL,       NULL,         1, NULL, NULL, socket_listen_main_multi_threaded},
     {"web-single-threaded", NULL,       NULL,         0, NULL, NULL, socket_listen_main_single_threaded},
     {NULL,                  NULL,       NULL,         0, NULL, NULL, NULL}
@@ -148,27 +150,32 @@ int killpid(pid_t pid, int sig)
 
 void kill_childs()
 {
+    error_log_limit_unlimited();
+
     siginfo_t info;
 
     struct web_client *w;
     for(w = web_clients; w ; w = w->next) {
-        debug(D_EXIT, "Stopping web client %s", w->client_ip);
+        info("Stopping web client %s", w->client_ip);
         pthread_cancel(w->thread);
-        pthread_join(w->thread, NULL);
+        // it is detached
+        // pthread_join(w->thread, NULL);
     }
 
     int i;
     for (i = 0; static_threads[i].name != NULL ; i++) {
-        if(static_threads[i].thread) {
-            debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
+        if(static_threads[i].enabled && static_threads[i].thread) {
+            info("Stopping %s thread", static_threads[i].name);
             pthread_cancel(*static_threads[i].thread);
-            pthread_join(*static_threads[i].thread, NULL);
+            // it is detached
+            // pthread_join(*static_threads[i].thread, NULL);
+
             static_threads[i].thread = NULL;
         }
     }
 
     if(tc_child_pid) {
-        info("Killing tc-qos-helper procees");
+        info("Killing tc-qos-helper process %d", tc_child_pid);
         if(killpid(tc_child_pid, SIGTERM) != -1)
             waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
     }
@@ -176,22 +183,33 @@ void kill_childs()
 
     struct plugind *cd;
     for(cd = pluginsd_root ; cd ; cd = cd->next) {
-        debug(D_EXIT, "Stopping %s plugin thread", cd->id);
-        pthread_cancel(cd->thread);
-        pthread_join(cd->thread, NULL);
-
-        if(cd->pid && !cd->obsolete) {
-            debug(D_EXIT, "killing %s plugin process", cd->id);
-            if(killpid(cd->pid, SIGTERM) != -1)
-                waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
+        if(cd->enabled && !cd->obsolete) {
+            if(cd->thread != (pthread_t)NULL) {
+                info("Stopping %s plugin thread", cd->id);
+                pthread_cancel(cd->thread);
+                // they are detached
+                // pthread_join(cd->thread, NULL);
+                cd->thread = (pthread_t)NULL;
+            }
+
+            if(cd->pid) {
+                info("killing %s plugin child process pid %d", cd->id, cd->pid);
+                if(killpid(cd->pid, SIGTERM) != -1)
+                    waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
+
+                cd->pid = 0;
+            }
+
+            cd->obsolete = 1;
         }
     }
 
     // if, for any reason there is any child exited
     // catch it here
+    info("Cleaning up an other children");
     waitid(P_PID, 0, &info, WEXITED|WNOHANG);
 
-    debug(D_EXIT, "All threads/childs stopped.");
+    info("All threads/childs stopped.");
 }
 
 struct option_def options[] = {
@@ -208,7 +226,7 @@ struct option_def options[] = {
 Same as 'update every' config file option.",                                 "seconds",                              "1"},
     {'u', "System username to run as.",                                      "username",                             "netdata"},
     {'v', "Version of the program",                                          NULL,                                   NULL},
-    {'W', "vendor options.",                                                 "stacksize=<size>|unittest|debug_flag", NULL},
+    {'W', "vendor options.",                                                 "stacksize=N|unittest|debug_flags=N",   NULL},
 };
 
 void help(int exitcode) {
@@ -261,23 +279,30 @@ void remove_option(int opt_index, int *argc, char **argv) {
     } while(argv[i][0] != '-' && opt_index >= *argc);
 }
 
+static const char *verify_required_directory(const char *dir) {
+    if(chdir(dir) == -1)
+        fatal("Cannot cd to directory '%s'", dir);
+
+    DIR *d = opendir(dir);
+    if(!d)
+        fatal("Cannot examine the contents of directory '%s'", dir);
+    closedir(d);
+
+    return dir;
+}
 
 int main(int argc, char **argv)
 {
+    char *hostname = "localhost";
     int i, check_config = 0;
     int config_loaded = 0;
     int dont_fork = 0;
     size_t wanted_stacksize = 0, stacksize = 0;
     pthread_attr_t attr;
 
-    // global initialization
-    get_HZ();
-
     // set the name for logging
     program_name = "netdata";
 
-    // parse command line.
-
     // parse depercated options
     // TODO: Remove this block with the next major release.
     {
@@ -366,7 +391,7 @@ int main(int argc, char **argv)
                     break;
                 case 'v':
                     // TODO: Outsource version to makefile which can compute version from git.
-                    printf("netdata 1.2.1_master\n");
+                    printf("netdata %s\n", VERSION);
                     return 0;
                 case 'W':
                     {
@@ -395,35 +420,52 @@ int main(int argc, char **argv)
         }
     }
 
-    if(!config_loaded) load_config(NULL, 0);
+    if(!config_loaded)
+        load_config(NULL, 0);
 
-    // prepare configuration environment variables for the plugins
-    setenv("NETDATA_CONFIG_DIR" , config_get("global", "config directory"   , CONFIG_DIR) , 1);
-    setenv("NETDATA_PLUGINS_DIR", config_get("global", "plugins directory"  , PLUGINS_DIR), 1);
-    setenv("NETDATA_WEB_DIR"    , config_get("global", "web files directory", WEB_DIR)    , 1);
-    setenv("NETDATA_CACHE_DIR"  , config_get("global", "cache directory"    , CACHE_DIR)  , 1);
-    setenv("NETDATA_LIB_DIR"    , config_get("global", "lib directory"      , VARLIB_DIR) , 1);
-    setenv("NETDATA_LOG_DIR"    , config_get("global", "log directory"      , LOG_DIR)    , 1);
-    setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "")         , 1);
-    setenv("HOME"               , config_get("global", "home directory"     , CACHE_DIR)  , 1);
+    {
+        char *pmax = config_get("global", "glibc malloc arena max for plugins", "1");
+        if(pmax && *pmax)
+            setenv("MALLOC_ARENA_MAX", pmax, 1);
+
+#if defined(HAVE_C_MALLOPT)
+        int i = config_get_number("global", "glibc malloc arena max for netdata", 1);
+        if(i > 0)
+            mallopt(M_ARENA_MAX, 1);
+#endif
 
-    // disable buffering for python plugins
-    setenv("PYTHONUNBUFFERED", "1", 1);
+        char *config_dir = config_get("global", "config directory", CONFIG_DIR);
 
-    // avoid flood calls to stat(/etc/localtime)
-    // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
-    setenv("TZ", ":/etc/localtime", 0);
+        // prepare configuration environment variables for the plugins
+        setenv("NETDATA_CONFIG_DIR" , verify_required_directory(config_dir) , 1);
+        setenv("NETDATA_PLUGINS_DIR", verify_required_directory(config_get("global", "plugins directory"  , PLUGINS_DIR)), 1);
+        setenv("NETDATA_WEB_DIR"    , verify_required_directory(config_get("global", "web files directory", WEB_DIR))    , 1);
+        setenv("NETDATA_CACHE_DIR"  , verify_required_directory(config_get("global", "cache directory"    , CACHE_DIR))  , 1);
+        setenv("NETDATA_LIB_DIR"    , verify_required_directory(config_get("global", "lib directory"      , VARLIB_DIR)) , 1);
+        setenv("NETDATA_LOG_DIR"    , verify_required_directory(config_get("global", "log directory"      , LOG_DIR))    , 1);
+
+        setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "")         , 1);
+        setenv("HOME"               , config_get("global", "home directory"     , CACHE_DIR)  , 1);
+
+        // disable buffering for python plugins
+        setenv("PYTHONUNBUFFERED", "1", 1);
+
+        // avoid flood calls to stat(/etc/localtime)
+        // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
+        setenv("TZ", ":/etc/localtime", 0);
+
+        // work while we are cd into config_dir
+        // to allow the plugins refer to their config
+        // files using relative filenames
+        if(chdir(config_dir) == -1)
+            fatal("Cannot cd to '%s'", config_dir);
 
-    {
         char path[1024 + 1], *p = getenv("PATH");
         if(!p) p = "/bin:/usr/bin";
         snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
         setenv("PATH", config_get("plugins", "PATH environment variable", path), 1);
     }
 
-    // cd to /tmp to avoid any plugins writing files at random places
-    if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
-
     char *user = NULL;
     {
         char *flags = config_get("global", "debug flags",  "0x00000000");
@@ -435,8 +477,11 @@ int main(int argc, char **argv)
         if(debug_flags != 0) {
             struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
             if(setrlimit(RLIMIT_CORE, &rl) != 0)
-                info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+                error("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+
+#if !(defined(__FreeBSD__) || defined(__APPLE__))
             prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+#endif /* __FreeBSD__ || __APPLE__*/
         }
 
         // --------------------------------------------------------------------
@@ -452,13 +497,18 @@ int main(int argc, char **argv)
         global_host_prefix = config_get("global", "host access prefix", "");
         setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
 
+        get_system_HZ();
+        get_system_cpus();
+        get_system_pid_max();
+        
         // --------------------------------------------------------------------
 
         stdout_filename    = config_get("global", "debug log",  LOG_DIR "/debug.log");
         stderr_filename    = config_get("global", "error log",  LOG_DIR "/error.log");
         stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
 
-        error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
+        error_log_throttle_period_backup =
+            error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
         setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period"    , ""), 1);
 
         error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
@@ -469,6 +519,7 @@ int main(int argc, char **argv)
             error_log_throttle_period = 0;
             error_log_errors_per_period = 0;
         }
+        error_log_limit_unlimited();
 
         // --------------------------------------------------------------------
 
@@ -482,13 +533,14 @@ int main(int argc, char **argv)
                 error("WARNING: Cannot get machine hostname.");
             hostname = config_get("global", "hostname", hostnamebuf);
             debug(D_OPTIONS, "hostname set to '%s'", hostname);
+            setenv("NETDATA_HOSTNAME", hostname, 1);
         }
 
         // --------------------------------------------------------------------
 
         rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
         if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
-            info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
+            error("Invalid history entries %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
             rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
         }
         else {
@@ -499,7 +551,7 @@ int main(int argc, char **argv)
 
         rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
         if(rrd_update_every < 1 || rrd_update_every > 600) {
-            info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
+            error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
             rrd_update_every = UPDATE_EVERY;
         }
         else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
@@ -517,50 +569,45 @@ int main(int argc, char **argv)
         // this causes the threads to block signals.
         sigset_t sigset;
         sigfillset(&sigset);
-
-        if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
+        if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1)
             error("Could not block signals for threads");
-        }
 
         // Catch signals which we want to use
         struct sigaction sa;
-        sigemptyset(&sa.sa_mask);
-        sigaddset(&sa.sa_mask, SIGINT);
-        sigaddset(&sa.sa_mask, SIGTERM);
-        sa.sa_handler = sig_handler_exit;
         sa.sa_flags = 0;
-        if(sigaction(SIGINT, &sa, NULL) == -1) {
+
+        // ingore all signals while we run in a signal handler
+        sigfillset(&sa.sa_mask);
+
+        // INFO: If we add signals here we have to unblock them
+        // at popen.c when running a external plugin.
+
+        // Ignore SIGPIPE completely.
+        sa.sa_handler = SIG_IGN;
+        if(sigaction(SIGPIPE, &sa, NULL) == -1)
+            error("Failed to change signal handler for SIGPIPE");
+
+        sa.sa_handler = sig_handler_exit;
+        if(sigaction(SIGINT, &sa, NULL) == -1)
             error("Failed to change signal handler for SIGINT");
-        }
-        if(sigaction(SIGTERM, &sa, NULL) == -1) {
+
+        sa.sa_handler = sig_handler_exit;
+        if(sigaction(SIGTERM, &sa, NULL) == -1)
             error("Failed to change signal handler for SIGTERM");
-        }
 
-        sigemptyset(&sa.sa_mask);
-        sigaddset(&sa.sa_mask, SIGHUP);
         sa.sa_handler = sig_handler_logrotate;
-        sa.sa_flags = 0;
-        if(sigaction(SIGHUP, &sa, NULL) == -1) {
+        if(sigaction(SIGHUP, &sa, NULL) == -1)
             error("Failed to change signal handler for SIGHUP");
-        }
 
         // save database on SIGUSR1
-        sigemptyset(&sa.sa_mask);
-        sigaddset(&sa.sa_mask, SIGUSR1);
         sa.sa_handler = sig_handler_save;
-        if(sigaction(SIGUSR1, &sa, NULL) == -1) {
+        if(sigaction(SIGUSR1, &sa, NULL) == -1)
             error("Failed to change signal handler for SIGUSR1");
-        }
 
-        // Ignore SIGPIPE completely.
-        // INFO: If we add signals here we have to unblock them
-        // at popen.c when running a external plugin.
-        sigemptyset(&sa.sa_mask);
-        sigaddset(&sa.sa_mask, SIGPIPE);
-        sa.sa_handler = SIG_IGN;
-        if(sigaction(SIGPIPE, &sa, NULL) == -1) {
-            error("Failed to change signal handler for SIGPIPE");
-        }
+        // reload health configuration on SIGUSR2
+        sa.sa_handler = sig_handler_reload_health;
+        if(sigaction(SIGUSR2, &sa, NULL) == -1)
+            error("Failed to change signal handler for SIGUSR2");
 
         // --------------------------------------------------------------------
 
@@ -608,17 +655,18 @@ int main(int argc, char **argv)
     if(debug_flags != 0) {
         struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
         if(setrlimit(RLIMIT_CORE, &rl) != 0)
-            info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+            error("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+#if !(defined(__FreeBSD__) || defined(__APPLE__))
         prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+#endif /* __FreeBSD__ || __APPLE__*/
     }
 #endif /* NETDATA_INTERNAL_CHECKS */
 
     // fork, switch user, create pid file, set process priority
     if(become_daemon(dont_fork, user) == -1)
-        fatal("Cannot demonize myself.");
-
-    info("NetData started on pid %d", getpid());
+        fatal("Cannot daemonize myself.");
 
+    info("netdata started on pid %d.", getpid());
 
     // ------------------------------------------------------------------------
     // get default pthread stack size
@@ -628,9 +676,14 @@ int main(int argc, char **argv)
         if(i != 0)
             fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
         else
-            info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
+            debug(D_SYSTEM, "Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
     }
 
+    // ------------------------------------------------------------------------
+    // initialize rrd host
+
+    rrdhost_init(hostname);
+
     // ------------------------------------------------------------------------
     // initialize the registry
 
@@ -644,6 +697,11 @@ int main(int argc, char **argv)
     if(check_config)
         exit(1);
 
+    // ------------------------------------------------------------------------
+    // enable log flood protection
+
+    error_log_limit_reset();
+
     // ------------------------------------------------------------------------
     // spawn the threads
 
@@ -655,17 +713,19 @@ int main(int argc, char **argv)
         if(st->enabled) {
             st->thread = mallocz(sizeof(pthread_t));
 
-            info("Starting thread %s.", st->name);
+            debug(D_SYSTEM, "Starting thread %s.", st->name);
 
-            if(pthread_create(st->thread, &attr, st->start_routine, NULL))
+            if(pthread_create(st->thread, &attr, st->start_routine, st))
                 error("failed to create new thread for %s.", st->name);
 
             else if(pthread_detach(*st->thread))
                 error("Cannot request detach of newly created %s thread.", st->name);
         }
-        else info("Not starting thread %s.", st->name);
+        else debug(D_SYSTEM, "Not starting thread %s.", st->name);
     }
 
+    info("netdata initialization completed. Enjoy real-time performance monitoring!");
+
     // ------------------------------------------------------------------------
     // block signals while initializing threads.
     sigset_t sigset;
@@ -679,7 +739,7 @@ int main(int argc, char **argv)
     while(1) {
         pause();
         if(netdata_exit) {
-            info("Exit main loop of netdata.");
+            debug(D_EXIT, "Exit main loop of netdata.");
             netdata_cleanup_and_exit(0);
             exit(0);
         }