예제 #1
0
void set_global_environment() {
    {
        char b[16];
        snprintfz(b, 15, "%d", default_rrd_update_every);
        setenv("NETDATA_UPDATE_EVERY", b, 1);
    }

    setenv("NETDATA_HOSTNAME"   , netdata_configured_hostname, 1);
    setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_config_dir),  1);
    setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_plugins_dir), 1);
    setenv("NETDATA_WEB_DIR"    , verify_required_directory(netdata_configured_web_dir),     1);
    setenv("NETDATA_CACHE_DIR"  , verify_required_directory(netdata_configured_cache_dir),   1);
    setenv("NETDATA_LIB_DIR"    , verify_required_directory(netdata_configured_varlib_dir),  1);
    setenv("NETDATA_LOG_DIR"    , verify_required_directory(netdata_configured_log_dir),     1);
    setenv("HOME"               , verify_required_directory(netdata_configured_home_dir),    1);
    setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);

    get_system_timezone();

    // set the path we need
    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(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);

    // python options
    p = getenv("PYTHONPATH");
    if(!p) p = "";
    setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);

    // disable buffering for python plugins
    setenv("PYTHONUNBUFFERED", "1", 1);

    // switch to standard locale for plugins
    setenv("LC_ALL", "C", 1);
}
예제 #2
0
파일: main.c 프로젝트: bmaggard/netdata
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;

    // set the name for logging
    program_name = "netdata";

    // parse depercated options
    // TODO: Remove this block with the next major release.
    {
        i = 1;
        while(i < argc) {
            if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
                strncpyz(pidfile, argv[i+1], FILENAME_MAX);
                fprintf(stderr, "%s: deprecated option -- %s -- please use -P instead.\n", argv[0], argv[i]);
                remove_option(i, &argc, argv);
            }
            else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
                dont_fork = 1;
                fprintf(stderr, "%s: deprecated option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
                remove_option(i, &argc, argv);
            }
            else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
                config_set("global", "host access prefix", argv[i+1]);
                fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
                remove_option(i, &argc, argv);
            }
            else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
                config_set("global", "history", argv[i+1]);
                fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
                remove_option(i, &argc, argv);
            }
            else i++;
        }
    }

    // parse options
    {
        int num_opts = sizeof(options) / sizeof(struct option_def);
        char optstring[(num_opts * 2) + 1];

        int string_i = 0;
        for( i = 0; i < num_opts; i++ ) {
            optstring[string_i] = options[i].val;
            string_i++;
            if(options[i].arg_name) {
                optstring[string_i] = ':';
                string_i++;
            }
        }

        int opt;
        while( (opt = getopt(argc, argv, optstring)) != -1 ) {
            switch(opt) {
                case 'c':
                    if(load_config(optarg, 1) != 1) {
                        error("Cannot load configuration file %s.", optarg);
                        exit(1);
                    }
                    else {
                        debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
                        config_loaded = 1;
                    }
                    break;
                case 'D':
                    dont_fork = 1;
                    break;
                case 'h':
                    help(0);
                    break;
                case 'i':
                    config_set("global", "bind to", optarg);
                    break;
                case 'k':
                    dont_fork = 1;
                    check_config = 1;
                    break;
                case 'P':
                    strncpy(pidfile, optarg, FILENAME_MAX);
                    pidfile[FILENAME_MAX] = '\0';
                    break;
                case 'p':
                    config_set("global", "default port", optarg);
                    break;
                case 's':
                    config_set("global", "host access prefix", optarg);
                    break;
                case 't':
                    config_set("global", "update every", optarg);
                    break;
                case 'u':
                    config_set("global", "run as user", optarg);
                    break;
                case 'v':
                    // TODO: Outsource version to makefile which can compute version from git.
                    printf("netdata %s\n", VERSION);
                    return 0;
                case 'W':
                    {
                        char* stacksize_string = "stacksize=";
                        char* debug_flags_string = "debug_flags=";
                        if(strcmp(optarg, "unittest") == 0) {
                            rrd_update_every = 1;
                            if(run_all_mockup_tests()) exit(1);
                            if(unit_test_storage()) exit(1);
                            fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
                            exit(0);
                        } else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) {
                            optarg += strlen(stacksize_string);
                            config_set("global", "pthread stack size", optarg);
                        } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
                            optarg += strlen(debug_flags_string);
                            config_set("global", "debug flags",  optarg);
                            debug_flags = strtoull(optarg, NULL, 0);
                        }
                    }
                    break;
                default: /* ? */
                    help(1);
                    break;
            }
        }
    }

    if(!config_loaded)
        load_config(NULL, 0);

    {
        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

        char *config_dir = config_get("global", "config directory", CONFIG_DIR);

        // 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);
    }

    char *user = NULL;
    {
        char *flags = config_get("global", "debug flags",  "0x00000000");
        setenv("NETDATA_DEBUG_FLAGS", flags, 1);

        debug_flags = strtoull(flags, NULL, 0);
        debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);

        if(debug_flags != 0) {
            struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
            if(setrlimit(RLIMIT_CORE, &rl) != 0)
                error("Cannot request unlimited core dumps for debugging... Proceeding anyway...");

#ifndef __FreeBSD__
            prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif /* __FreeBSD__ */
        }

        // --------------------------------------------------------------------

#ifdef MADV_MERGEABLE
        enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
#else
#warning "Kernel memory deduplication (KSM) is not available"
#endif

        // --------------------------------------------------------------------

        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_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);
        setenv("NETDATA_ERRORS_PER_PERIOD"     , config_get("global", "errors to trigger flood protection", ""), 1);

        if(check_config) {
            stdout_filename = stderr_filename = stdaccess_filename = "system";
            error_log_throttle_period = 0;
            error_log_errors_per_period = 0;
        }
        error_log_limit_unlimited();

        // --------------------------------------------------------------------

        rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));

        // --------------------------------------------------------------------

        {
            char hostnamebuf[HOSTNAME_MAX + 1];
            if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
                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) {
            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 {
            debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
        }

        // --------------------------------------------------------------------

        rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
        if(rrd_update_every < 1 || rrd_update_every > 600) {
            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);

        // let the plugins know the min update_every
        {
            char buf[16];
            snprintfz(buf, 15, "%d", rrd_update_every);
            setenv("NETDATA_UPDATE_EVERY", buf, 1);
        }

        // --------------------------------------------------------------------

        // block signals while initializing threads.
        // this causes the threads to block signals.
        sigset_t sigset;
        sigfillset(&sigset);
        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;
        sa.sa_flags = 0;

        // 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");

        sa.sa_handler = sig_handler_exit;
        if(sigaction(SIGTERM, &sa, NULL) == -1)
            error("Failed to change signal handler for SIGTERM");

        sa.sa_handler = sig_handler_logrotate;
        if(sigaction(SIGHUP, &sa, NULL) == -1)
            error("Failed to change signal handler for SIGHUP");

        // save database on SIGUSR1
        sa.sa_handler = sig_handler_save;
        if(sigaction(SIGUSR1, &sa, NULL) == -1)
            error("Failed to change signal handler for SIGUSR1");

        // 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");

        // --------------------------------------------------------------------

        i = pthread_attr_init(&attr);
        if(i != 0)
            fatal("pthread_attr_init() failed with code %d.", i);

        i = pthread_attr_getstacksize(&attr, &stacksize);
        if(i != 0)
            fatal("pthread_attr_getstacksize() failed with code %d.", i);
        else
            debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);

        wanted_stacksize = (size_t)config_get_number("global", "pthread stack size", (long)stacksize);

        // --------------------------------------------------------------------

        for (i = 0; static_threads[i].name != NULL ; i++) {
            struct netdata_static_thread *st = &static_threads[i];

            if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
            if(st->enabled && st->init_routine) st->init_routine();
        }

        // --------------------------------------------------------------------

        // get the user we should run
        // IMPORTANT: this is required before web_files_uid()
        user = config_get("global", "run as user"    , (getuid() == 0)?NETDATA_USER:"");

        // IMPORTANT: these have to run once, while single threaded
        web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
        web_files_gid();

        // --------------------------------------------------------------------

        if(!check_config)
            create_listen_sockets();
    }

    // initialize the log files
    open_all_log_files();

#ifdef NETDATA_INTERNAL_CHECKS
    if(debug_flags != 0) {
        struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
        if(setrlimit(RLIMIT_CORE, &rl) != 0)
            error("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
#ifndef __FreeBSD__
        prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif /* __FreeBSD__ */
    }
#endif /* NETDATA_INTERNAL_CHECKS */

    // fork, switch user, create pid file, set process priority
    if(become_daemon(dont_fork, user) == -1)
        fatal("Cannot daemonize myself.");

    info("NetData started on pid %d", getpid());


    // ------------------------------------------------------------------------
    // get default pthread stack size

    if(stacksize < wanted_stacksize) {
        i = pthread_attr_setstacksize(&attr, wanted_stacksize);
        if(i != 0)
            fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
        else
            debug(D_SYSTEM, "Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
    }

    // ------------------------------------------------------------------------
    // initialize rrd host

    rrdhost_init(hostname);

    // ------------------------------------------------------------------------
    // initialize the registry

    registry_init();

    // ------------------------------------------------------------------------
    // initialize health monitoring

    health_init();

    if(check_config)
        exit(1);

    // ------------------------------------------------------------------------
    // enable log flood protection

    error_log_limit_reset();

    // ------------------------------------------------------------------------
    // spawn the threads

    web_server_threading_selection();

    for (i = 0; static_threads[i].name != NULL ; i++) {
        struct netdata_static_thread *st = &static_threads[i];

        if(st->enabled) {
            st->thread = mallocz(sizeof(pthread_t));

            debug(D_SYSTEM, "Starting thread %s.", st->name);

            if(pthread_create(st->thread, &attr, st->start_routine, NULL))
                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 debug(D_SYSTEM, "Not starting thread %s.", st->name);
    }

    // ------------------------------------------------------------------------
    // block signals while initializing threads.
    sigset_t sigset;
    sigfillset(&sigset);

    if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
        error("Could not unblock signals for threads");
    }

    // Handle flags set in the signal handler.
    while(1) {
        pause();
        if(netdata_exit) {
            debug(D_EXIT, "Exit main loop of netdata.");
            netdata_cleanup_and_exit(0);
            exit(0);
        }
    }
}