예제 #1
0
void netdata_cleanup_and_exit(int ret) {
    // enabling this, is wrong
    // because the threads will be cancelled while cleaning up
    // netdata_exit = 1;

    error_log_limit_unlimited();
    info("EXIT: netdata prepares to exit with code %d...", ret);

    // cleanup/save the database and exit
    info("EXIT: cleaning up the database...");
    rrdhost_cleanup_all();

    if(!ret) {
        // exit cleanly

        // stop everything
        info("EXIT: stopping master threads...");
        cancel_main_threads();

        // free the database
        info("EXIT: freeing database memory...");
        rrdhost_free_all();
    }

    // unlink the pid
    if(pidfile[0]) {
        info("EXIT: removing netdata PID file '%s'...", pidfile);
        if(unlink(pidfile) != 0)
            error("EXIT: cannot unlink pidfile '%s'.", pidfile);
    }

    info("EXIT: all done - netdata is now exiting - bye bye...");
    exit(ret);
}
예제 #2
0
void cancel_main_threads() {
    error_log_limit_unlimited();

    int i, found = 0, max = 5 * USEC_PER_SEC, step = 100000;
    for (i = 0; static_threads[i].name != NULL ; i++) {
        if(static_threads[i].enabled == NETDATA_MAIN_THREAD_RUNNING) {
            info("EXIT: Stopping master thread: %s", static_threads[i].name);
            netdata_thread_cancel(*static_threads[i].thread);
            found++;
        }
    }

    while(found && max > 0) {
        max -= step;
        info("Waiting %d threads to finish...", found);
        sleep_usec(step);
        found = 0;
        for (i = 0; static_threads[i].name != NULL ; i++) {
            if (static_threads[i].enabled != NETDATA_MAIN_THREAD_EXITED)
                found++;
        }
    }

    if(found) {
        for (i = 0; static_threads[i].name != NULL ; i++) {
            if (static_threads[i].enabled != NETDATA_MAIN_THREAD_EXITED)
                error("Master thread %s takes too long to exit. Giving up...", static_threads[i].name);
        }
    }
    else
        info("All threads finished.");
}
예제 #3
0
파일: daemon.c 프로젝트: yacloud-io/netdata
void sig_handler_exit(int signo)
{
    if(signo) {
        error_log_limit_unlimited();
        error("Received signal %d. Exiting...", signo);
        netdata_exit = 1;
    }
}
예제 #4
0
파일: main.c 프로젝트: 4224657/netdata
void netdata_cleanup_and_exit(int ret) {
	netdata_exit = 1;

	error_log_limit_unlimited();

	info("Called: netdata_cleanup_and_exit()");
	rrdset_save_all();
	// kill_childs();

	if(pidfile[0]) {
		if(unlink(pidfile) != 0)
			error("Cannot unlink pidfile '%s'.", pidfile);
	}

	info("NetData exiting. Bye bye...");
	exit(ret);
}
예제 #5
0
파일: main.c 프로젝트: bmaggard/netdata
void netdata_cleanup_and_exit(int ret) {
    netdata_exit = 1;

    error_log_limit_unlimited();

    debug(D_EXIT, "Called: netdata_cleanup_and_exit()");
#ifdef NETDATA_INTERNAL_CHECKS
    rrdset_free_all();
#else
    rrdset_save_all();
#endif
    // kill_childs();

    if(pidfile[0]) {
        if(unlink(pidfile) != 0)
            error("Cannot unlink pidfile '%s'.", pidfile);
    }

    info("NetData exiting. Bye bye...");
    exit(ret);
}
예제 #6
0
int main(int argc, char **argv) {
    int i;
    int config_loaded = 0;
    int dont_fork = 0;
    size_t default_stacksize;

    // 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(CONFIG_SECTION_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(CONFIG_SECTION_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(option_definitions) / sizeof(struct option_def);
        char optstring[(num_opts * 2) + 1];

        int string_i = 0;
        for( i = 0; i < num_opts; i++ ) {
            optstring[string_i] = option_definitions[i].val;
            string_i++;
            if(option_definitions[i].arg_name) {
                optstring[string_i] = ':';
                string_i++;
            }
        }
        // terminate optstring
        optstring[string_i] ='\0';
        optstring[(num_opts *2)] ='\0';

        int opt;
        while( (opt = getopt(argc, argv, optstring)) != -1 ) {
            switch(opt) {
                case 'c':
                    if(config_load(optarg, 1) != 1) {
                        error("Cannot load configuration file %s.", optarg);
                        return 1;
                    }
                    else {
                        debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
                        config_loaded = 1;
                    }
                    break;
                case 'D':
                    dont_fork = 1;
                    break;
                case 'h':
                    return help(0);
                case 'i':
                    config_set(CONFIG_SECTION_WEB, "bind to", optarg);
                    break;
                case 'P':
                    strncpy(pidfile, optarg, FILENAME_MAX);
                    pidfile[FILENAME_MAX] = '\0';
                    break;
                case 'p':
                    config_set(CONFIG_SECTION_GLOBAL, "default port", optarg);
                    break;
                case 's':
                    config_set(CONFIG_SECTION_GLOBAL, "host access prefix", optarg);
                    break;
                case 't':
                    config_set(CONFIG_SECTION_GLOBAL, "update every", optarg);
                    break;
                case 'u':
                    config_set(CONFIG_SECTION_GLOBAL, "run as user", optarg);
                    break;
                case 'v':
                case 'V':
                    printf("%s %s\n", program_name, program_version);
                    return 0;
                case 'W':
                    {
                        char* stacksize_string = "stacksize=";
                        char* debug_flags_string = "debug_flags=";

                        if(strcmp(optarg, "unittest") == 0) {
                            if(unit_test_buffer()) return 1;
                            if(unit_test_str2ld()) return 1;
                            //default_rrd_update_every = 1;
                            //default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
                            //if(!config_loaded) config_load(NULL, 0);
                            get_netdata_configured_variables();
                            default_rrd_update_every = 1;
                            default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
                            default_health_enabled = 0;
                            rrd_init("unittest");
                            default_rrdpush_enabled = 0;
                            if(run_all_mockup_tests()) return 1;
                            if(unit_test_storage()) return 1;
                            fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
                            return 0;
                        }
                        else if(strcmp(optarg, "simple-pattern") == 0) {
                            if(optind + 2 > argc) {
                                fprintf(stderr, "%s", "\nUSAGE: -W simple-pattern 'pattern' 'string'\n\n"
                                        " Checks if 'pattern' matches the given 'string'.\n"
                                        " - 'pattern' can be one or more space separated words.\n"
                                        " - each 'word' can contain one or more asterisks.\n"
                                        " - words starting with '!' give negative matches.\n"
                                        " - words are processed left to right\n"
                                        "\n"
                                        "Examples:\n"
                                        "\n"
                                        " > match all veth interfaces, except veth0:\n"
                                        "\n"
                                        "   -W simple-pattern '!veth0 veth*' 'veth12'\n"
                                        "\n"
                                        "\n"
                                        " > match all *.ext files directly in /path/:\n"
                                        "   (this will not match *.ext files in a subdir of /path/)\n"
                                        "\n"
                                        "   -W simple-pattern '!/path/*/*.ext /path/*.ext' '/path/test.ext'\n"
                                        "\n"
                                );
                                return 1;
                            }

                            const char *heystack = argv[optind];
                            const char *needle = argv[optind + 1];
                            size_t len = strlen(needle) + 1;
                            char wildcarded[len];

                            SIMPLE_PATTERN *p = simple_pattern_create(heystack, NULL, SIMPLE_PATTERN_EXACT);
                            int ret = simple_pattern_matches_extract(p, needle, wildcarded, len);
                            simple_pattern_free(p);

                            if(ret) {
                                fprintf(stdout, "RESULT: MATCHED - pattern '%s' matches '%s', wildcarded '%s'\n", heystack, needle, wildcarded);
                                return 0;
                            }
                            else {
                                fprintf(stdout, "RESULT: NOT MATCHED - pattern '%s' does not match '%s', wildcarded '%s'\n", heystack, needle, wildcarded);
                                return 1;
                            }
                        }
                        else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) {
                            optarg += strlen(stacksize_string);
                            config_set(CONFIG_SECTION_GLOBAL, "pthread stack size", optarg);
                        }
                        else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
                            optarg += strlen(debug_flags_string);
                            config_set(CONFIG_SECTION_GLOBAL, "debug flags",  optarg);
                            debug_flags = strtoull(optarg, NULL, 0);
                        }
                        else if(strcmp(optarg, "set") == 0) {
                            if(optind + 3 > argc) {
                                fprintf(stderr, "%s", "\nUSAGE: -W set 'section' 'key' 'value'\n\n"
                                        " Overwrites settings of netdata.conf.\n"
                                        "\n"
                                        " These options interact with: -c netdata.conf\n"
                                        " If -c netdata.conf is given on the command line,\n"
                                        " before -W set... the user may overwrite command\n"
                                        " line parameters at netdata.conf\n"
                                        " If -c netdata.conf is given after (or missing)\n"
                                        " -W set... the user cannot overwrite the command line\n"
                                        " parameters."
                                        "\n"
                                );
                                return 1;
                            }
                            const char *section = argv[optind];
                            const char *key = argv[optind + 1];
                            const char *value = argv[optind + 2];
                            optind += 3;

                            // set this one as the default
                            // only if it is not already set in the config file
                            // so the caller can use -c netdata.conf before or
                            // after this parameter to prevent or allow overwriting
                            // variables at netdata.conf
                            config_set_default(section, key,  value);

                            // fprintf(stderr, "SET section '%s', key '%s', value '%s'\n", section, key, value);
                        }
                        else if(strcmp(optarg, "get") == 0) {
                            if(optind + 3 > argc) {
                                fprintf(stderr, "%s", "\nUSAGE: -W get 'section' 'key' 'value'\n\n"
                                        " Prints settings of netdata.conf.\n"
                                        "\n"
                                        " These options interact with: -c netdata.conf\n"
                                        " -c netdata.conf has to be given before -W get.\n"
                                        "\n"
                                );
                                return 1;
                            }

                            if(!config_loaded) {
                                fprintf(stderr, "warning: no configuration file has been loaded. Use -c CONFIG_FILE, before -W get. Using default config.\n");
                                config_load(NULL, 0);
                            }

                            backwards_compatible_config();
                            get_netdata_configured_variables();

                            const char *section = argv[optind];
                            const char *key = argv[optind + 1];
                            const char *def = argv[optind + 2];
                            const char *value = config_get(section, key, def);
                            printf("%s\n", value);
                            return 0;
                        }
                        else {
                            fprintf(stderr, "Unknown -W parameter '%s'\n", optarg);
                            return help(1);
                        }
                    }
                    break;

                default: /* ? */
                    fprintf(stderr, "Unknown parameter '%c'\n", opt);
                    return help(1);
            }
        }
    }

#ifdef _SC_OPEN_MAX
    // close all open file descriptors, except the standard ones
    // the caller may have left open files (lxc-attach has this issue)
    {
        int fd;
        for(fd = (int) (sysconf(_SC_OPEN_MAX) - 1); fd > 2; fd--)
            if(fd_is_valid(fd)) close(fd);
    }
#endif

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

    // ------------------------------------------------------------------------
    // initialize netdata
    {
        char *pmax = config_get(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for plugins", "1");
        if(pmax && *pmax)
            setenv("MALLOC_ARENA_MAX", pmax, 1);

#if defined(HAVE_C_MALLOPT)
        i = (int)config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", 1);
        if(i > 0)
            mallopt(M_ARENA_MAX, 1);
#endif

        // prepare configuration environment variables for the plugins

        get_netdata_configured_variables();
        set_global_environment();

        // work while we are cd into config_dir
        // to allow the plugins refer to their config
        // files using relative filenames
        if(chdir(netdata_configured_config_dir) == -1)
            fatal("Cannot cd to '%s'", netdata_configured_config_dir);
    }

    char *user = NULL;

    {
        // --------------------------------------------------------------------
        // get the debugging flags from the configuration file

        char *flags = config_get(CONFIG_SECTION_GLOBAL, "debug flags",  "0x0000000000000000");
        setenv("NETDATA_DEBUG_FLAGS", flags, 1);

        debug_flags = strtoull(flags, NULL, 0);
        debug(D_OPTIONS, "Debug flags set to '0x%" PRIX64 "'.", 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...");

#ifdef HAVE_SYS_PRCTL_H
            prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif
        }


        // --------------------------------------------------------------------
        // get log filenames and settings

        log_init();
        error_log_limit_unlimited();


        // --------------------------------------------------------------------
        // load stream.conf
        {
            char filename[FILENAME_MAX + 1];
            snprintfz(filename, FILENAME_MAX, "%s/stream.conf", netdata_configured_config_dir);
            appconfig_load(&stream_config, filename, 0);
        }


        // --------------------------------------------------------------------
        // setup process signals

        // block signals while initializing threads.
        // this causes the threads to block signals.
        signals_block();

        // setup the signals we want to use
        signals_init();

        // setup threads configs
        default_stacksize = netdata_threads_init();


        // --------------------------------------------------------------------
        // check which threads are enabled and initialize them

        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()
        if(getuid() == 0) {
            user = config_get(CONFIG_SECTION_GLOBAL, "run as user", NETDATA_USER);
        }
        else {
            struct passwd *passwd = getpwuid(getuid());
            user = config_get(CONFIG_SECTION_GLOBAL, "run as user", (passwd && passwd->pw_name)?passwd->pw_name:"");
        }

        // --------------------------------------------------------------------
        // create the listening sockets

        web_client_api_v1_init();
        web_server_threading_selection();

        if(web_server_mode != WEB_SERVER_MODE_NONE)
            api_listen_sockets_setup();
    }

    // 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...");
#ifdef HAVE_SYS_PRCTL_H
        prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif
    }
#endif /* NETDATA_INTERNAL_CHECKS */

    // get the max file limit
    if(getrlimit(RLIMIT_NOFILE, &rlimit_nofile) != 0)
        error("getrlimit(RLIMIT_NOFILE) failed");
    else
        info("resources control: allowed file descriptors: soft = %zu, max = %zu", rlimit_nofile.rlim_cur, rlimit_nofile.rlim_max);

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

    // IMPORTANT: these have to run once, while single threaded
    // but after we have switched user
    web_files_uid();
    web_files_gid();

    netdata_threads_init_after_fork((size_t)config_get_number(CONFIG_SECTION_GLOBAL, "pthread stack size", (long)default_stacksize));

    // ------------------------------------------------------------------------
    // initialize rrd, registry, health, rrdpush, etc.

    rrd_init(netdata_configured_hostname);


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

    error_log_limit_reset();


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

    web_server_config_options();

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

        if(st->enabled) {
            st->thread = mallocz(sizeof(netdata_thread_t));
            debug(D_SYSTEM, "Starting thread %s.", st->name);
            netdata_thread_create(st->thread, st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, st);
        }
        else debug(D_SYSTEM, "Not starting thread %s.", st->name);
    }

    info("netdata initialization completed. Enjoy real-time performance monitoring!");


    // ------------------------------------------------------------------------
    // unblock signals

    signals_unblock();

    // ------------------------------------------------------------------------
    // Handle signals

    signals_handle();

    // should never reach this point
    // but we need it for rpmlint #2752
    return 1;
}
예제 #7
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);
        }
    }
}
예제 #8
0
void signals_handle(void) {
    while(1) {

        // pause()  causes  the calling process (or thread) to sleep until a signal
        // is delivered that either terminates the process or causes the invocation
        // of a signal-catching function.
        if(pause() == -1 && errno == EINTR) {

            // loop once, but keep looping while signals are coming in
            // this is needed because a few operations may take some time
            // so we need to check for new signals before pausing again
            int found = 1;
            while(found) {
                found = 0;

                // execute the actions of the signals
                int i;
                for (i = 0; signals_waiting[i].action != NETDATA_SIGNAL_END_OF_LIST; i++) {
                    if (signals_waiting[i].count) {
                        found = 1;
                        signals_waiting[i].count = 0;
                        const char *name = signals_waiting[i].name;

                        switch (signals_waiting[i].action) {
                            case NETDATA_SIGNAL_RELOAD_HEALTH:
                                error_log_limit_unlimited();
                                info("SIGNAL: Received %s. Reloading HEALTH configuration...", name);
                                health_reload();
                                error_log_limit_reset();
                                break;

                            case NETDATA_SIGNAL_SAVE_DATABASE:
                                error_log_limit_unlimited();
                                info("SIGNAL: Received %s. Saving databases...", name);
                                rrdhost_save_all();
                                info("Databases saved.");
                                error_log_limit_reset();
                                break;

                            case NETDATA_SIGNAL_LOG_ROTATE:
                                error_log_limit_unlimited();
                                info("SIGNAL: Received %s. Reopening all log files...", name);
                                reopen_all_log_files();
                                error_log_limit_reset();
                                break;

                            case NETDATA_SIGNAL_EXIT_CLEANLY:
                                info("SIGNAL: Received %s. Cleaning up to exit...", name);
                                netdata_cleanup_and_exit(0);
                                exit(0);

                            case NETDATA_SIGNAL_FATAL:
                                fatal("SIGNAL: Received %s. netdata now exits.", name);

                            default:
                                info("SIGNAL: Received %s. No signal handler configured. Ignoring it.", name);
                                break;
                        }
                    }
                }
            }
        }
        else
            error("SIGNAL: pause() returned but it was not interrupted by a signal.");
    }
}