Пример #1
0
int create_listen_socket(void) {
	listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);

	listen_port = (int) config_get_number("global", "port", LISTEN_PORT);
	if(listen_port < 1 || listen_port > 65535) {
		error("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
		listen_port = LISTEN_PORT;
	}
	else debug(D_OPTIONS, "Listen port set to %d.", listen_port);

	int ip = 0;
	char *ipv = config_get("global", "ip version", "any");
	if(!strcmp(ipv, "any") || !strcmp(ipv, "both") || !strcmp(ipv, "all")) ip = 0;
	else if(!strcmp(ipv, "ipv4") || !strcmp(ipv, "IPV4") || !strcmp(ipv, "IPv4") || !strcmp(ipv, "4")) ip = 4;
	else if(!strcmp(ipv, "ipv6") || !strcmp(ipv, "IPV6") || !strcmp(ipv, "IPv6") || !strcmp(ipv, "6")) ip = 6;
	else error("Cannot understand ip version '%s'. Assuming 'any'.", ipv);

	if(ip == 0 || ip == 6) listen_fd = create_listen_socket6(config_get("global", "bind socket to IP", "*"), listen_port, listen_backlog);
	if(listen_fd < 0) {
		listen_fd = create_listen_socket4(config_get("global", "bind socket to IP", "*"), listen_port, listen_backlog);
		// if(listen_fd >= 0 && ip != 4) info("Managed to open an IPv4 socket on port %d.", listen_port);
	}

	return listen_fd;
}
Пример #2
0
void read_cgroup_plugin_configuration() {
	cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every);

	cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
	cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
	cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
	cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);

	char filename[FILENAME_MAX + 1], *s;
	struct mountinfo *mi, *root = mountinfo_read();

	mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
	if(!mi) mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
	if(!mi) {
		error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
		s = "/sys/fs/cgroup/cpuacct";
	}
	else s = mi->mount_point;
	snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
	cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);

	mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
	if(!mi) mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
	if(!mi) {
		error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
		s = "/sys/fs/cgroup/blkio";
	}
	else s = mi->mount_point;
	snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
	cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);

	mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
	if(!mi) mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
	if(!mi) {
		error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
		s = "/sys/fs/cgroup/memory";
	}
	else s = mi->mount_point;
	snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
	cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);

	mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
	if(!mi) mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
	if(!mi) {
		error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
		s = "/sys/fs/cgroup/devices";
	}
	else s = mi->mount_point;
	snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
	cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);

	cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
	cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);

	cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime);

	mountinfo_free(root);
}
Пример #3
0
/*
 * Sets up a socket on which we listen for clients. Also creates the listening
 * thread.
 */
int client_interface_set_up() {
    char ourname[HOSTNAME_BUF_SIZE+1];
    struct sockaddr_in sa;
    struct hostent *hp;

    memset(&sa, 0, sizeof(struct sockaddr_in));
    gethostname(ourname, HOSTNAME_BUF_SIZE);

    hp = gethostbyname(ourname);
    if(hp == NULL) { // we apparently don't exist - what is this, the matrix?
        perror("Finding hostname");
        return -1;
    }

    sa.sin_family = hp->h_addrtype;
    sa.sin_port = htons(config_get_number("Client_Listen_Port"));

    // create socket
    if((client_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Creating listening socket");
        return 255;
    }

    // bind socket
    if(bind(client_socket, (const struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
        close(client_socket);
        perror("Binding listening socket");
        return 512;
    }

    listen(client_socket, (int) config_get_number("Client_Max_Queue"));

    // Set up the listening thread
    client_should_run = 1;

    int error = pthread_create(&client_service_thread, NULL, client_interface_listening_thread, NULL);

    if(error) {
        client_should_run = 0;
        close(client_socket);

        perror("Creating listening thread");

        return error | 0x8000;
    }

    return 0;
}
Пример #4
0
/*
 * This thread processes connections.
 */
void *client_interface_listening_thread(void *param) {
    int t; // holds the connection we establish with clients

    printf("Client listening thread active. Listening for clients on port %lli...\n"
           , config_get_number("Client_Listen_Port"));
    fflush(stdout);

    while(client_should_run == 1) {
        if((t = client_interface_get_connection()) < 0) {
            if(errno == EINTR) { // The socket might sometimes get EINTR
                continue;
            }

            perror("Accept client connection"); // die, we got an unknown error
        } else {
            pthread_t thread; // we don't really care about this thread later, it'll die eventually
            pthread_create(&thread, NULL, client_interface_connection_handler, (void *) &t);
        }
    }

    close(client_socket);

    client_has_quit = 1;

    pthread_exit(NULL);
}
Пример #5
0
static inline void tc_device_classes_cleanup(struct tc_device *d) {
    static int cleanup_every = 999;

    if(unlikely(cleanup_every > 0)) {
        cleanup_every = (int) config_get_number("plugin:tc", "cleanup unused classes every", 120);
        if(cleanup_every < 0) cleanup_every = -cleanup_every;
    }

    d->name_updated = 0;
    d->family_updated = 0;

    struct tc_class *c = d->classes;
    while(c) {
        if(unlikely(cleanup_every && c->unupdated >= cleanup_every)) {
            struct tc_class *nc = c->next;
            tc_class_free(d, c);
            c = nc;
        }
        else {
            c->updated = 0;
            c->name_updated = 0;

            c = c->next;
        }
    }
}
Пример #6
0
int sock_fd_init (void)
{
	static int	initialized = 0;

	if (fds)
		return (0);
#if defined (NUTTX_RTOS)
	fds = malloc (sizeof (struct pollfd) * FD_INC_SIZE);
	fcts = malloc (sizeof (RSDATAFCT) * FD_INC_SIZE);
	ud = malloc (sizeof (void *) * FD_INC_SIZE);
	names = malloc (sizeof (const char *) * FD_INC_SIZE);
#else
	fds = xmalloc (sizeof (struct pollfd) * FD_INC_SIZE);
	fcts = xmalloc (sizeof (RSDATAFCT) * FD_INC_SIZE);
	ud = xmalloc (sizeof (void *) * FD_INC_SIZE);
	names = xmalloc (sizeof (const char *) * FD_INC_SIZE);
#endif	
	if (!fds || !fcts || !ud || !names)
		return (1);

	atomic_set_w (num_fds, 0);
	atomic_set_w (max_fds, FD_INC_SIZE);

	if (!initialized) {
		lock_init_nr (sock_lock, "lock");
		fd_max_size = config_get_number (DC_IP_Sockets, FD_MAX_SIZE);
		initialized = 1;
	}
	return (0);
}
Пример #7
0
void web_server_threading_selection(void) {
	int threaded = config_get_boolean("global", "multi threaded web server", 1);

	int i;
	for(i = 0; static_threads[i].name ; i++) {
		if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
			static_threads[i].enabled = threaded?1:0;

		if(static_threads[i].start_routine == socket_listen_main_single_threaded)
			static_threads[i].enabled = threaded?0:1;
	}

	web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);

	web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);

#ifdef NETDATA_WITH_ZLIB
	web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);

	char *s = config_get("global", "web compression strategy", "default");
	if(!strcmp(s, "default"))
		web_gzip_strategy = Z_DEFAULT_STRATEGY;
	else if(!strcmp(s, "filtered"))
		web_gzip_strategy = Z_FILTERED;
	else if(!strcmp(s, "huffman only"))
		web_gzip_strategy = Z_HUFFMAN_ONLY;
	else if(!strcmp(s, "rle"))
		web_gzip_strategy = Z_RLE;
	else if(!strcmp(s, "fixed"))
		web_gzip_strategy = Z_FIXED;
	else {
		error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
		web_gzip_strategy = Z_DEFAULT_STRATEGY;
	}

	web_gzip_level = (int)config_get_number("global", "web compression level", 3);
	if(web_gzip_level < 1) {
		error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
		web_gzip_level = 1;
	}
	else if(web_gzip_level > 9) {
		error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
		web_gzip_level = 9;
	}
#endif /* NETDATA_WITH_ZLIB */
}
Пример #8
0
void web_server_config_options(void) {
    web_client_timeout = (int) config_get_number(CONFIG_SECTION_WEB, "disconnect idle clients after seconds", web_client_timeout);
    web_client_first_request_timeout = (int) config_get_number(CONFIG_SECTION_WEB, "timeout for first request", web_client_first_request_timeout);

    respect_web_browser_do_not_track_policy = config_get_boolean(CONFIG_SECTION_WEB, "respect do not track policy", respect_web_browser_do_not_track_policy);
    web_x_frame_options = config_get(CONFIG_SECTION_WEB, "x-frame-options response header", "");
    if(!*web_x_frame_options) web_x_frame_options = NULL;

    web_allow_connections_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow connections from", "localhost *"), NULL, SIMPLE_PATTERN_EXACT);
    web_allow_dashboard_from   = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow dashboard from", "localhost *"), NULL, SIMPLE_PATTERN_EXACT);
    web_allow_badges_from      = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow badges from", "*"), NULL, SIMPLE_PATTERN_EXACT);
    web_allow_registry_from    = simple_pattern_create(config_get(CONFIG_SECTION_REGISTRY, "allow from", "*"), NULL, SIMPLE_PATTERN_EXACT);
    web_allow_streaming_from   = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow streaming from", "*"), NULL, SIMPLE_PATTERN_EXACT);
    web_allow_netdataconf_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow netdata.conf from", "localhost fd* 10.* 192.168.* 172.16.* 172.17.* 172.18.* 172.19.* 172.20.* 172.21.* 172.22.* 172.23.* 172.24.* 172.25.* 172.26.* 172.27.* 172.28.* 172.29.* 172.30.* 172.31.*"), NULL, SIMPLE_PATTERN_EXACT);

#ifdef NETDATA_WITH_ZLIB
    web_enable_gzip = config_get_boolean(CONFIG_SECTION_WEB, "enable gzip compression", web_enable_gzip);

    char *s = config_get(CONFIG_SECTION_WEB, "gzip compression strategy", "default");
    if(!strcmp(s, "default"))
        web_gzip_strategy = Z_DEFAULT_STRATEGY;
    else if(!strcmp(s, "filtered"))
        web_gzip_strategy = Z_FILTERED;
    else if(!strcmp(s, "huffman only"))
        web_gzip_strategy = Z_HUFFMAN_ONLY;
    else if(!strcmp(s, "rle"))
        web_gzip_strategy = Z_RLE;
    else if(!strcmp(s, "fixed"))
        web_gzip_strategy = Z_FIXED;
    else {
        error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
        web_gzip_strategy = Z_DEFAULT_STRATEGY;
    }

    web_gzip_level = (int)config_get_number(CONFIG_SECTION_WEB, "gzip compression level", 3);
    if(web_gzip_level < 1) {
        error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
        web_gzip_level = 1;
    }
    else if(web_gzip_level > 9) {
        error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
        web_gzip_level = 9;
    }
#endif /* NETDATA_WITH_ZLIB */
}
Пример #9
0
void log_init(void) {
    char filename[FILENAME_MAX + 1];
    snprintfz(filename, FILENAME_MAX, "%s/debug.log", netdata_configured_log_dir);
    stdout_filename    = config_get(CONFIG_SECTION_GLOBAL, "debug log",  filename);

    snprintfz(filename, FILENAME_MAX, "%s/error.log", netdata_configured_log_dir);
    stderr_filename    = config_get(CONFIG_SECTION_GLOBAL, "error log",  filename);

    snprintfz(filename, FILENAME_MAX, "%s/access.log", netdata_configured_log_dir);
    stdaccess_filename = config_get(CONFIG_SECTION_GLOBAL, "access log", filename);

    error_log_throttle_period_backup =
    error_log_throttle_period = config_get_number(CONFIG_SECTION_GLOBAL, "errors flood protection period", error_log_throttle_period);
    error_log_errors_per_period = (unsigned long)config_get_number(CONFIG_SECTION_GLOBAL, "errors to trigger flood protection", (long long int)error_log_errors_per_period);

    setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get(CONFIG_SECTION_GLOBAL, "errors flood protection period"    , ""), 1);
    setenv("NETDATA_ERRORS_PER_PERIOD",      config_get(CONFIG_SECTION_GLOBAL, "errors to trigger flood protection", ""), 1);
}
Пример #10
0
void *cpuidlejitter_main(void *ptr) {
    struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;

    info("IDLEJITTER thread created with task id %d", gettid());

    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
        error("Cannot set pthread cancel type to DEFERRED.");

    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
        error("Cannot set pthread cancel state to ENABLE.");

    int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
    if(sleep_ms <= 0) {
        config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
        sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
    }

    RRDSET *st = rrdset_find_localhost("system.idlejitter");
    if(!st) {
        st = rrdset_create_localhost("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter"
                                     , "microseconds lost/s", 9999, localhost->rrd_update_every, RRDSET_TYPE_LINE);
        rrddim_add(st, "jitter", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
    }

    struct timeval before, after;
    unsigned long long counter;
    for(counter = 0; 1 ;counter++) {
        usec_t usec = 0, susec = 0;

        if(netdata_exit) break;

        while(susec < (localhost->rrd_update_every * USEC_PER_SEC)) {

            now_monotonic_timeval(&before);
            sleep_usec(sleep_ms * 1000);
            now_monotonic_timeval(&after);

            // calculate the time it took for a full loop
            usec = dt_usec(&after, &before);
            susec += usec;
        }
        usec -= (sleep_ms * 1000);

        if(counter) rrdset_next(st);
        rrddim_set(st, "jitter", usec);
        rrdset_done(st);
    }

    info("IDLEJITTER thread exiting");

    static_thread->enabled = 0;
    pthread_exit(NULL);
    return NULL;
}
Пример #11
0
int create_listen_sockets(void) {
	listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);

    if(config_exists("global", "bind socket to IP") && !config_exists("global", "bind to"))
        config_rename("global", "bind socket to IP", "bind to");

    if(config_exists("global", "port") && !config_exists("global", "default port"))
        config_rename("global", "port", "default port");

    listen_port = (int) config_get_number("global", "default port", LISTEN_PORT);
	if(listen_port < 1 || listen_port > 65535) {
		error("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
        listen_port = (int) config_set_number("global", "default port", LISTEN_PORT);
	}
	debug(D_OPTIONS, "Default listen port set to %d.", listen_port);

    char *s = config_get("global", "bind to", "*");
    while(*s) {
        char *e = s;

        // skip separators, moving both s(tart) and e(nd)
        while(isspace(*e) || *e == ',') s = ++e;

        // move e(nd) to the first separator
        while(*e && !isspace(*e) && *e != ',') e++;

        // is there anything?
        if(!*s || s == e) break;

        char buf[e - s + 1];
        strncpyz(buf, s, e - s);
        bind_to_one(buf, listen_port, listen_backlog);

        s = e;
    }

    if(!listen_fds_count)
        fatal("Cannot listen on any socket. Exiting...");

	return listen_fds_count;
}
Пример #12
0
int rrdpush_init() {
    default_rrdpush_enabled     = appconfig_get_boolean(&stream_config, CONFIG_SECTION_STREAM, "enabled", default_rrdpush_enabled);
    default_rrdpush_destination = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "destination", "");
    default_rrdpush_api_key     = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "api key", "");
    rrdhost_free_orphan_time    = config_get_number(CONFIG_SECTION_GLOBAL, "cleanup orphan hosts after seconds", rrdhost_free_orphan_time);

    if(default_rrdpush_enabled && (!default_rrdpush_destination || !*default_rrdpush_destination || !default_rrdpush_api_key || !*default_rrdpush_api_key)) {
        error("STREAM [send]: cannot enable sending thread - information is missing.");
        default_rrdpush_enabled = 0;
    }

    return default_rrdpush_enabled;
}
Пример #13
0
void *cpuidlejitter_main(void *ptr)
{
	if(ptr) { ; }

	info("CPU Idle Jitter thread created with task id %d", gettid());

	if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
		error("Cannot set pthread cancel type to DEFERRED.");

	if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
		error("Cannot set pthread cancel state to ENABLE.");

	int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
	if(sleep_ms <= 0) {
		config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
		sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
	}

	RRDSET *st = rrdset_find("system.idlejitter");
	if(!st) {
		st = rrdset_create("system", "idlejitter", NULL, "cpu", "CPU Idle Jitter", "microseconds lost/s", 9999, rrd_update_every, RRDSET_TYPE_LINE);
		rrddim_add(st, "jitter", NULL, 1, 1, RRDDIM_ABSOLUTE);
	}

	struct timeval before, after;
	unsigned long long counter;
	for(counter = 0; 1 ;counter++) {
		unsigned long long usec = 0, susec = 0;

		while(susec < (rrd_update_every * 1000000ULL)) {

			gettimeofday(&before, NULL);
			usleep(sleep_ms * 1000);
			gettimeofday(&after, NULL);

			// calculate the time it took for a full loop
			usec = usecdiff(&after, &before);
			susec += usec;
		}
		usec -= (sleep_ms * 1000);

		if(counter) rrdset_next(st);
		rrddim_set(st, "jitter", usec);
		rrdset_done(st);
	}

	return NULL;
}
Пример #14
0
static inline void tc_device_classes_cleanup(struct tc_device *d) {
	static int cleanup_every = 999;

	if(cleanup_every > 0) {
		cleanup_every = (int) -config_get_number("plugin:tc", "cleanup unused classes every", 60);
		if(cleanup_every > 0) cleanup_every = -cleanup_every;
		if(cleanup_every == 0) cleanup_every = -1;
	}

	struct tc_class *c = d->classes;
	while(c) {
		if(c->seen < cleanup_every) {
			struct tc_class *nc = c->next;
			tc_class_free(d, c);
			c = nc;
		}
		else c = c->next;

		if(c) {
			c->updated = 0;
			c->seen--;
		}
	}
}
Пример #15
0
int main(int argc, char **argv)
{
	int i;
	int config_loaded = 0;
	int dont_fork = 0;

	// global initialization
	get_HZ();

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

	// parse  the arguments
	for(i = 1; i < argc ; i++) {
		if(strcmp(argv[i], "-c") == 0 && (i+1) < argc) {
			if(load_config(argv[i+1], 1) != 1) {
				error("Cannot load configuration file %s.", argv[i+1]);
				exit(1);
			}
			else {
				debug(D_OPTIONS, "Configuration loaded from %s.", argv[i+1]);
				config_loaded = 1;
			}
			i++;
		}
		else if(strcmp(argv[i], "-df") == 0 && (i+1) < argc) { config_set("global", "debug flags",  argv[i+1]); debug_flags = strtoull(argv[i+1], NULL, 0); i++; }
		else if(strcmp(argv[i], "-p")  == 0 && (i+1) < argc) { config_set("global", "port",         argv[i+1]); i++; }
		else if(strcmp(argv[i], "-u")  == 0 && (i+1) < argc) { config_set("global", "run as user",  argv[i+1]); i++; }
		else if(strcmp(argv[i], "-l")  == 0 && (i+1) < argc) { config_set("global", "history",      argv[i+1]); i++; }
		else if(strcmp(argv[i], "-t")  == 0 && (i+1) < argc) { config_set("global", "update every", argv[i+1]); i++; }
		else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) { config_set("global", "host access prefix", argv[i+1]); i++; }
		else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) dont_fork = 1;
		else if(strcmp(argv[i], "--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 {
			fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
			fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-p LISTEN_PORT] [-df debug flags].\n\n", argv[0]);
			fprintf(stderr, "  -c CONFIG FILE the configuration file to load. Default: %s.\n", CONFIG_DIR "/" CONFIG_FILENAME);
			fprintf(stderr, "  -l LINES_TO_SAVE can be from 5 to %d lines in JSON data. Default: %d.\n", RRD_HISTORY_ENTRIES_MAX, RRD_DEFAULT_HISTORY_ENTRIES);
			fprintf(stderr, "  -t UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", UPDATE_EVERY_MAX, UPDATE_EVERY);
			fprintf(stderr, "  -p LISTEN_PORT can be from 1 to %d. Default: %d.\n", 65535, LISTEN_PORT);
			fprintf(stderr, "  -u USERNAME can be any system username to run as. Default: none.\n");
			fprintf(stderr, "  -ch path to access host /proc and /sys when running in a container. Default: empty.\n");
			fprintf(stderr, "  -nd or -nodeamon to disable forking in the background. Default: unset.\n");
			fprintf(stderr, "  -df FLAGS debug options. Default: 0x%08llx.\n", debug_flags);
			exit(1);
		}
	}

	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_LOG_DIR"    , config_get("global", "log directory"      , LOG_DIR)    , 1);
	setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "")         , 1);

	// avoid extended 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);

	// cd to /tmp to avoid any plugins writing files at random places
	if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");

	char *input_log_file = NULL;
	char *output_log_file = NULL;
	char *error_log_file = NULL;
	char *access_log_file = NULL;
	char *user = NULL;
	{
		char buffer[1024];

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

		sprintf(buffer, "0x%08llx", 0ULL);
		char *flags = config_get("global", "debug flags", buffer);
		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)
				info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
			prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
		}

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

#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);

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

		output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
		if(strcmp(output_log_file, "syslog") == 0) {
			output_log_syslog = 1;
			output_log_file = NULL;
		}
		else if(strcmp(output_log_file, "none") == 0) {
			output_log_syslog = 0;
			output_log_file = NULL;
		}
		else output_log_syslog = 0;

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

		error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
		if(strcmp(error_log_file, "syslog") == 0) {
			error_log_syslog = 1;
			error_log_file = NULL;
		}
		else if(strcmp(error_log_file, "none") == 0) {
			error_log_syslog = 0;
			error_log_file = NULL;
			// optimization - do not even generate debug log entries
		}
		else error_log_syslog = 0;

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

		access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
		if(strcmp(access_log_file, "syslog") == 0) {
			access_log_syslog = 1;
			access_log_file = NULL;
		}
		else if(strcmp(access_log_file, "none") == 0) {
			access_log_syslog = 0;
			access_log_file = NULL;
		}
		else access_log_syslog = 0;

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

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

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

		if(gethostname(buffer, HOSTNAME_MAX) == -1)
			error("WARNING: Cannot get machine hostname.");
		hostname = config_get("global", "hostname", buffer);
		debug(D_OPTIONS, "hostname set to '%s'", hostname);

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

		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) {
			fprintf(stderr, "Invalid save lines %d given. Defaulting to %d.\n", 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) {
			fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", 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[50];
			snprintf(buf, 50, "%d", rrd_update_every);
			setenv("NETDATA_UPDATE_EVERY", buf, 1);
		}

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

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

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

		prepare_rundir();
		user = config_get("global", "run as user"    , (getuid() == 0)?NETDATA_USER:"");
		web_files_uid();

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

		listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);

		listen_port = (int) config_get_number("global", "port", LISTEN_PORT);
		if(listen_port < 1 || listen_port > 65535) {
			fprintf(stderr, "Invalid listen port %d given. Defaulting to %d.\n", listen_port, LISTEN_PORT);
			listen_port = LISTEN_PORT;
		}
		else debug(D_OPTIONS, "Listen port set to %d.", listen_port);

		int ip = 0;
		char *ipv = config_get("global", "ip version", "any");
		if(!strcmp(ipv, "any") || !strcmp(ipv, "both") || !strcmp(ipv, "all")) ip = 0;
		else if(!strcmp(ipv, "ipv4") || !strcmp(ipv, "IPV4") || !strcmp(ipv, "IPv4") || !strcmp(ipv, "4")) ip = 4;
		else if(!strcmp(ipv, "ipv6") || !strcmp(ipv, "IPV6") || !strcmp(ipv, "IPv6") || !strcmp(ipv, "6")) ip = 6;
		else fprintf(stderr, "Cannot understand ip version '%s'. Assumming 'any'.", ipv);

		if(ip == 0 || ip == 6) listen_fd = create_listen_socket6(listen_port, listen_backlog);
		if(listen_fd < 0) {
			listen_fd = create_listen_socket4(listen_port, listen_backlog);
			if(listen_fd >= 0 && ip != 4) fprintf(stderr, "Managed to open an IPv4 socket on port %d.", listen_port);
		}

		if(listen_fd < 0) fatal("Cannot listen socket.");
	}

	// never become a problem
	if(nice(20) == -1) error("Cannot lower my CPU priority.");

	if(become_daemon(dont_fork, 0, user, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1) {
		fatal("Cannot demonize myself.");
		exit(1);
	}

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

		prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
	}

	if(output_log_syslog || error_log_syslog || access_log_syslog)
		openlog("netdata", LOG_PID, LOG_DAEMON);

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


	// catch all signals
	for (i = 1 ; i < 65 ;i++) {
		switch(i) {
			case SIGKILL: // not catchable
			case SIGSTOP: // not catchable
				break;

			case SIGSEGV:
			case SIGFPE:
			case SIGCHLD:
				signal(i, SIG_DFL);
				break;

			default:
				signal(i,  sig_handler);
				break;
		}
	}

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

		if(st->enabled) {
			st->thread = malloc(sizeof(pthread_t));
			if(!st->thread)
				fatal("Cannot allocate pthread_t memory");

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

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

	// for future use - the main thread
	while(1) {
		if(netdata_exit != 0) {
			netdata_exit++;

			if(netdata_exit > 5) {
				netdata_cleanup_and_exit(0);
				exit(0);
			}
		}
		sleep(2);
	}

	exit(0);
}
void *proc_diskspace_main(void *ptr) {
    struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;

    info("DISKSPACE thread created with task id %d", gettid());

    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
        error("DISKSPACE: Cannot set pthread cancel type to DEFERRED.");

    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
        error("DISKSPACE: Cannot set pthread cancel state to ENABLE.");

    int vdo_cpu_netdata = config_get_boolean("plugin:proc", "netdata server resources", 1);

    int update_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "update every", localhost->rrd_update_every);
    if(update_every < localhost->rrd_update_every)
        update_every = localhost->rrd_update_every;

    check_for_new_mountpoints_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "check for new mount points every", check_for_new_mountpoints_every);
    if(check_for_new_mountpoints_every < update_every)
        check_for_new_mountpoints_every = update_every;

    struct rusage thread;

    usec_t duration = 0;
    usec_t step = update_every * USEC_PER_SEC;
    heartbeat_t hb;
    heartbeat_init(&hb);
    for(;;) {
        duration = heartbeat_dt_usec(&hb);
        /* usec_t hb_dt = */ heartbeat_next(&hb, step);

        if(unlikely(netdata_exit)) break;


        // --------------------------------------------------------------------------
        // this is smart enough not to reload it every time

        mountinfo_reload(0);


        // --------------------------------------------------------------------------
        // disk space metrics

        struct mountinfo *mi;
        for(mi = disk_mountinfo_root; mi; mi = mi->next) {

            if(unlikely(mi->flags & (MOUNTINFO_IS_DUMMY | MOUNTINFO_IS_BIND)))
                continue;

            do_disk_space_stats(mi, update_every);
            if(unlikely(netdata_exit)) break;
        }

        if(unlikely(netdata_exit)) break;

        dictionary_get_all(dict_mountpoints, mount_point_cleanup, NULL);

        if(vdo_cpu_netdata) {
            static RRDSET *stcpu_thread = NULL, *st_duration = NULL;
            static RRDDIM *rd_user = NULL, *rd_system = NULL, *rd_duration = NULL;

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

            getrusage(RUSAGE_THREAD, &thread);

            if(!stcpu_thread) {
                stcpu_thread = rrdset_find_localhost("netdata.plugin_diskspace");
                if(!stcpu_thread)
                    stcpu_thread = rrdset_create_localhost(
                            "netdata"
                            , "plugin_diskspace"
                            , NULL
                            , "diskspace"
                            , NULL
                            , "NetData Disk Space Plugin CPU usage"
                            , "milliseconds/s"
                            , 132020
                            , update_every
                            , RRDSET_TYPE_STACKED
                    );

                rd_user   = rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
                rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
            }
            else
                rrdset_next(stcpu_thread);

            rrddim_set_by_pointer(stcpu_thread, rd_user, thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
            rrddim_set_by_pointer(stcpu_thread, rd_system, thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
            rrdset_done(stcpu_thread);

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

            if(!st_duration) {
                st_duration = rrdset_find_localhost("netdata.plugin_diskspace_dt");
                if(!st_duration)
                    st_duration = rrdset_create_localhost(
                            "netdata"
                            , "plugin_diskspace_dt"
                            , NULL
                            , "diskspace"
                            , NULL
                            , "NetData Disk Space Plugin Duration"
                            , "milliseconds/run"
                            , 132021
                            , update_every
                            , RRDSET_TYPE_AREA
                    );

                rd_duration = rrddim_add(st_duration, "duration", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
            }
            else
                rrdset_next(st_duration);

            rrddim_set_by_pointer(st_duration, rd_duration, duration);
            rrdset_done(st_duration);

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

            if(unlikely(netdata_exit)) break;
        }
    }

    info("DISKSPACE thread exiting");

    static_thread->enabled = 0;
    pthread_exit(NULL);
    return NULL;
}
Пример #17
0
RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm)
{
	char filename[FILENAME_MAX + 1];
	char fullfilename[FILENAME_MAX + 1];

	char varname[CONFIG_MAX_NAME + 1];
	RRDDIM *rd = NULL;
	unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));

	debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);

	rrdset_strncpyz_name(filename, id, FILENAME_MAX);
	snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
	if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1);
	if(rd) {
		struct timeval now;
		gettimeofday(&now, NULL);

		if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
			errno = 0;
			info("Initializing file %s.", fullfilename);
			bzero(rd, size);
		}
		else if(rd->memsize != size) {
			errno = 0;
			error("File %s does not have the desired size. Clearing it.", fullfilename);
			bzero(rd, size);
		}
		else if(rd->multiplier != multiplier) {
			errno = 0;
			error("File %s does not have the same multiplier. Clearing it.", fullfilename);
			bzero(rd, size);
		}
		else if(rd->divisor != divisor) {
			errno = 0;
			error("File %s does not have the same divisor. Clearing it.", fullfilename);
			bzero(rd, size);
		}
		else if(rd->algorithm != algorithm) {
			errno = 0;
			error("File %s does not have the same algorithm. Clearing it.", fullfilename);
			bzero(rd, size);
		}
		else if(rd->update_every != st->update_every) {
			errno = 0;
			error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
			bzero(rd, size);
		}
		else if(usecdiff(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
			errno = 0;
			error("File %s is too old. Clearing it.", fullfilename);
			bzero(rd, size);
		}
		else if(strcmp(rd->id, id) != 0) {
			errno = 0;
			error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
			// munmap(rd, size);
			// rd = NULL;
			bzero(rd, size);
		}
	}

	if(rd) {
		// we have a file mapped for rd
		rd->mapped = rrd_memory_mode;
		rd->flags = 0x00000000;
		rd->next = NULL;
		rd->name = NULL;
	}
	else {
		// if we didn't manage to get a mmap'd dimension, just create one

		rd = calloc(1, size);
		if(!rd) {
			fatal("Cannot allocate RRD_DIMENSION %s/%s.", st->id, id);
			return NULL;
		}

		rd->mapped = RRD_MEMORY_MODE_RAM;
	}
	rd->memsize = size;

	strcpy(rd->magic, RRDDIMENSION_MAGIC);
	strcpy(rd->cache_filename, fullfilename);
	strncpyz(rd->id, id, RRD_ID_LENGTH_MAX);
	rd->hash = simple_hash(rd->id);

	snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
	rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);

	snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
	rd->algorithm = rrddim_algorithm_id(config_get(st->id, varname, rrddim_algorithm_name(algorithm)));

	snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
	rd->multiplier = config_get_number(st->id, varname, multiplier);

	snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
	rd->divisor = config_get_number(st->id, varname, divisor);
	if(!rd->divisor) rd->divisor = 1;

	rd->entries = st->entries;
	rd->update_every = st->update_every;

	// prevent incremental calculation spikes
	rd->counter = 0;
	rd->updated = 0;
	rd->calculated_value = 0;
	rd->last_calculated_value = 0;
	rd->collected_value = 0;
	rd->last_collected_value = 0;
	rd->collected_volume = 0;
	rd->stored_volume = 0;
	rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
	rd->last_collected_time.tv_sec = 0;
	rd->last_collected_time.tv_usec = 0;

	// append this dimension
	pthread_rwlock_wrlock(&st->rwlock);
	if(!st->dimensions)
		st->dimensions = rd;
	else {
		RRDDIM *td = st->dimensions;
		for(; td->next; td = td->next) ;
		td->next = rd;
	}
	pthread_rwlock_unlock(&st->rwlock);

	rrddim_index_add(st, rd);

	return(rd);
}
Пример #18
0
/*
 * This function is called in a new thread to process a client that connects. It
 * handles the protocol and all that other fun stuff to translate messages the
 * clients send into a push notification we can send out.
 */
void *client_interface_connection_handler(void *connection) {
    if(connection == NULL) return NULL;

    int sock = *((int *) connection);

    printf("Client connected.\n");

    char *msg_buf = calloc(config_get_number("Client_Max_Msg_Size") + 1, sizeof(char));
    char *msg_buf_write_ptr = msg_buf;

    long bytes_read_total = 0;
    long bytes_read = 0;

    // read one byte at a time
    while((bytes_read = read(sock, msg_buf_write_ptr, 1)) > 0) {
        bytes_read_total += bytes_read;

        // check if we have \n
        if(strcmp(msg_buf_write_ptr, "\n") == 0) {
//            printf("Received \\n.\n");
            break;
        }

        msg_buf_write_ptr += bytes_read;

        // have we got MAX_CLIENT_MSG_SIZE bytes and no \n?
        if(bytes_read_total == config_get_number("Client_Max_Msg_Size")) {
            write(sock, "err", 3);
            printf(ANSI_COLOR_RED
                   "Client tried to write too much request data - potential"
                   "buffer overflow exploit attempt!\n" ANSI_RESET);

            write(sock, "overflow", 8);

            // Close socket
            free(msg_buf);
            close(sock);

            pthread_exit(NULL);
        }
    }

//    printf("Read %li bytes from client.\n%s\n", bytes_read_total, msg_buf);

    // try and parse JSON
    json_error_t json_error;
    json_t *parsed = json_loads(msg_buf, 0, &json_error);
    free(msg_buf);

    if(!parsed) {
        write(sock, "err", 3);
        printf("Error parsing JSON at line %i: %s\n", json_error.line, json_error.text);

        // free memory, close socket
        free(msg_buf);
        close(sock);

        pthread_exit(NULL);
    } else {
        if(!json_is_object(parsed)) {
            write(sock, "err_type", 8);
            printf("Expected JSON object, got %i", parsed->type);

            // free memory, close socket
            free(parsed);
            close(sock);

            pthread_exit(NULL);
        }
    }

    // allocate memory for the message
    push_msg *message = malloc(sizeof(push_msg));
    memset(message, 0x00, sizeof(push_msg));

    if(message == NULL) {
        write(sock, "nomem", 5);

        printf("Could not allocate push_msg object!\n");

        // free memory, close socket
        free(parsed);
        close(sock);

        pthread_exit(NULL);
    }

    message->text = copy_json_info(json_object_get(parsed, "text"));
    message->sound = copy_json_info(json_object_get(parsed, "sound"));
    message->badgeNumber = (int) json_integer_value(json_object_get(parsed, "badge"));
    message->custPayload = copy_json_info(json_object_get(parsed, "custom"));
    message->deviceID = copy_json_info(json_object_get(parsed, "key"));
    message->buttonTitle = copy_json_info(json_object_get(parsed, "btnTitle"));
    message->localized_template = copy_json_info(json_object_get(parsed, "localized_text"));
    message->localized_arguments.argument0 = copy_json_info(json_object_get(parsed, "localized_arg0"));
    message->localized_arguments.argument1 = copy_json_info(json_object_get(parsed, "localized_arg1"));
    message->localized_arguments.argument2 = copy_json_info(json_object_get(parsed, "localized_arg2"));
    message->localized_arguments.argument3 = copy_json_info(json_object_get(parsed, "localized_arg3"));

    write(sock, "ok", 2);

    // Free memory, close socket
    free(parsed);
    close(sock);

//    printf("Text: %s\nDevice: %s\n", message->text, message->deviceID);

    int error = msg_queue_insert(message); // shove message into queue
    if(error) {
        printf("Error adding to queue: %i\n\n", error);
    }

    fflush(stdout);

    pthread_exit(NULL);
}
Пример #19
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;
}
Пример #20
0
void *pluginsd_main(void *ptr)
{
	if(ptr) { ; }

	info("PLUGINS.D thread created with task id %d", gettid());

	if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
		error("Cannot set pthread cancel type to DEFERRED.");

	if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
		error("Cannot set pthread cancel state to ENABLE.");

	char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR);
	int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1);
	int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60);
	DIR *dir = NULL;
	struct dirent *file = NULL;
	struct plugind *cd;

	// enable the apps plugin by default
	// config_get_boolean("plugins", "apps", 1);

	if(scan_frequency < 1) scan_frequency = 1;

	while(likely(1)) {
		if(unlikely(netdata_exit)) break;

		dir = opendir(dir_name);
		if(unlikely(!dir)) {
			error("Cannot open directory '%s'.", dir_name);
			return NULL;
		}

		while(likely((file = readdir(dir)))) {
			if(unlikely(netdata_exit)) break;

			debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);

			if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;

			int len = (int) strlen(file->d_name);
			if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
			if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
				debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
				continue;
			}

			char pluginname[CONFIG_MAX_NAME + 1];
			snprintf(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
			int enabled = config_get_boolean("plugins", pluginname, automatic_run);

			if(unlikely(!enabled)) {
				debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
				continue;
			}

			// check if it runs already
			for(cd = pluginsd_root ; likely(cd) ; cd = cd->next) {
				if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
			}
			if(likely(cd && !cd->obsolete)) {
				debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
				continue;
			}

			// it is not running
			// allocate a new one, or use the obsolete one
			if(unlikely(!cd)) {
				cd = calloc(sizeof(struct plugind), 1);
				if(unlikely(!cd)) fatal("Cannot allocate memory for plugin.");

				snprintf(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);

				strncpy(cd->filename, file->d_name, FILENAME_MAX);
				snprintf(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename);

				cd->enabled = enabled;
				cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every);
				cd->started_t = time(NULL);

				char *def = "";
				snprintf(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));

				// link it
				if(likely(pluginsd_root)) cd->next = pluginsd_root;
				pluginsd_root = cd;
			}
			cd->obsolete = 0;

			if(unlikely(!cd->enabled)) continue;

			// spawn a new thread for it
			if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0)) {
				error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename);
				cd->obsolete = 1;
			}
			else if(unlikely(pthread_detach(cd->thread) != 0))
				error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename);
		}

		closedir(dir);
		sleep((unsigned int) scan_frequency);
	}

	return NULL;
}
Пример #21
0
void *proc_diskspace_main(void *ptr) {
    netdata_thread_cleanup_push(diskspace_main_cleanup, ptr);

    int vdo_cpu_netdata = config_get_boolean("plugin:proc", "netdata server resources", 1);

    cleanup_mount_points = config_get_boolean(CONFIG_SECTION_DISKSPACE, "remove charts of unmounted disks" , cleanup_mount_points);

    int update_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "update every", localhost->rrd_update_every);
    if(update_every < localhost->rrd_update_every)
        update_every = localhost->rrd_update_every;

    check_for_new_mountpoints_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "check for new mount points every", check_for_new_mountpoints_every);
    if(check_for_new_mountpoints_every < update_every)
        check_for_new_mountpoints_every = update_every;

    struct rusage thread;

    usec_t duration = 0;
    usec_t step = update_every * USEC_PER_SEC;
    heartbeat_t hb;
    heartbeat_init(&hb);
    while(!netdata_exit) {
        duration = heartbeat_dt_usec(&hb);
        /* usec_t hb_dt = */ heartbeat_next(&hb, step);

        if(unlikely(netdata_exit)) break;


        // --------------------------------------------------------------------------
        // this is smart enough not to reload it every time

        mountinfo_reload(0);


        // --------------------------------------------------------------------------
        // disk space metrics

        struct mountinfo *mi;
        for(mi = disk_mountinfo_root; mi; mi = mi->next) {

            if(unlikely(mi->flags & (MOUNTINFO_IS_DUMMY | MOUNTINFO_IS_BIND)))
                continue;

            do_disk_space_stats(mi, update_every);
            if(unlikely(netdata_exit)) break;
        }

        if(unlikely(netdata_exit)) break;

        if(dict_mountpoints)
            dictionary_get_all(dict_mountpoints, mount_point_cleanup, NULL);

        if(vdo_cpu_netdata) {
            static RRDSET *stcpu_thread = NULL, *st_duration = NULL;
            static RRDDIM *rd_user = NULL, *rd_system = NULL, *rd_duration = NULL;

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

            getrusage(RUSAGE_THREAD, &thread);

            if(unlikely(!stcpu_thread)) {
                stcpu_thread = rrdset_create_localhost(
                        "netdata"
                        , "plugin_diskspace"
                        , NULL
                        , "diskspace"
                        , NULL
                        , "NetData Disk Space Plugin CPU usage"
                        , "milliseconds/s"
                        , "diskspace"
                        , NULL
                        , 132020
                        , update_every
                        , RRDSET_TYPE_STACKED
                );

                rd_user   = rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
                rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
            }
            else
                rrdset_next(stcpu_thread);

            rrddim_set_by_pointer(stcpu_thread, rd_user, thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
            rrddim_set_by_pointer(stcpu_thread, rd_system, thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
            rrdset_done(stcpu_thread);

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

            if(unlikely(!st_duration)) {
                st_duration = rrdset_create_localhost(
                        "netdata"
                        , "plugin_diskspace_dt"
                        , NULL
                        , "diskspace"
                        , NULL
                        , "NetData Disk Space Plugin Duration"
                        , "milliseconds/run"
                        , "diskspace"
                        , NULL
                        , 132021
                        , update_every
                        , RRDSET_TYPE_AREA
                );

                rd_duration = rrddim_add(st_duration, "duration", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
            }
            else
                rrdset_next(st_duration);

            rrddim_set_by_pointer(st_duration, rd_duration, duration);
            rrdset_done(st_duration);

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

            if(unlikely(netdata_exit)) break;
        }
    }

    netdata_thread_cleanup_pop(1);
    return NULL;
}
Пример #22
0
RRDHOST *rrdhost_create(const char *hostname,
        const char *guid,
        const char *os,
        int update_every,
        long entries,
        RRD_MEMORY_MODE memory_mode,
        int health_enabled,
        int rrdpush_enabled,
        char *rrdpush_destination,
        char *rrdpush_api_key,
        int is_localhost
) {
    debug(D_RRDHOST, "Host '%s': adding with guid '%s'", hostname, guid);

    rrd_check_wrlock();

    RRDHOST *host = callocz(1, sizeof(RRDHOST));

    host->rrd_update_every    = (update_every > 0)?update_every:1;
    host->rrd_history_entries = align_entries_to_pagesize(memory_mode, entries);
    host->rrd_memory_mode     = memory_mode;
    host->health_enabled      = (memory_mode == RRD_MEMORY_MODE_NONE)? 0 : health_enabled;
    host->rrdpush_enabled     = (rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key);
    host->rrdpush_destination = (host->rrdpush_enabled)?strdupz(rrdpush_destination):NULL;
    host->rrdpush_api_key     = (host->rrdpush_enabled)?strdupz(rrdpush_api_key):NULL;

    host->rrdpush_pipe[0] = -1;
    host->rrdpush_pipe[1] = -1;
    host->rrdpush_socket  = -1;

    netdata_mutex_init(&host->rrdpush_mutex);
    netdata_rwlock_init(&host->rrdhost_rwlock);

    rrdhost_init_hostname(host, hostname);
    rrdhost_init_machine_guid(host, guid);
    rrdhost_init_os(host, os);

    avl_init_lock(&(host->rrdset_root_index),      rrdset_compare);
    avl_init_lock(&(host->rrdset_root_index_name), rrdset_compare_name);
    avl_init_lock(&(host->rrdfamily_root_index),   rrdfamily_compare);
    avl_init_lock(&(host->variables_root_index),   rrdvar_compare);

    if(config_get_boolean(CONFIG_SECTION_GLOBAL, "delete obsolete charts files", 1))
        rrdhost_flag_set(host, RRDHOST_DELETE_OBSOLETE_FILES);

    if(config_get_boolean(CONFIG_SECTION_GLOBAL, "delete orphan hosts files", 1) && !is_localhost)
        rrdhost_flag_set(host, RRDHOST_DELETE_ORPHAN_FILES);


    // ------------------------------------------------------------------------
    // initialize health variables

    host->health_log.next_log_id = 1;
    host->health_log.next_alarm_id = 1;
    host->health_log.max = 1000;
    host->health_log.next_log_id =
    host->health_log.next_alarm_id = (uint32_t)now_realtime_sec();

    long n = config_get_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", host->health_log.max);
    if(n < 10) {
        error("Host '%s': health configuration has invalid max log entries %ld. Using default %u", host->hostname, n, host->health_log.max);
        config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", (long)host->health_log.max);
    }
    else
        host->health_log.max = (unsigned int)n;

    netdata_rwlock_init(&host->health_log.alarm_log_rwlock);

    char filename[FILENAME_MAX + 1];

    if(is_localhost) {

        host->cache_dir  = strdupz(netdata_configured_cache_dir);
        host->varlib_dir = strdupz(netdata_configured_varlib_dir);

    }
    else {
        // this is not localhost - append our GUID to localhost path

        snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_cache_dir, host->machine_guid);
        host->cache_dir = strdupz(filename);

        if(host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
            int r = mkdir(host->cache_dir, 0775);
            if(r != 0 && errno != EEXIST)
                error("Host '%s': cannot create directory '%s'", host->hostname, host->cache_dir);
        }

        snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_varlib_dir, host->machine_guid);
        host->varlib_dir = strdupz(filename);

        if(host->health_enabled) {
            int r = mkdir(host->varlib_dir, 0775);
            if(r != 0 && errno != EEXIST)
                error("Host '%s': cannot create directory '%s'", host->hostname, host->varlib_dir);
       }

    }

    if(host->health_enabled) {
        snprintfz(filename, FILENAME_MAX, "%s/health", host->varlib_dir);
        int r = mkdir(filename, 0775);
        if(r != 0 && errno != EEXIST)
            error("Host '%s': cannot create directory '%s'", host->hostname, filename);
    }

    snprintfz(filename, FILENAME_MAX, "%s/health/health-log.db", host->varlib_dir);
    host->health_log_filename = strdupz(filename);

    snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_plugins_dir);
    host->health_default_exec = strdupz(config_get(CONFIG_SECTION_HEALTH, "script to execute on alarm", filename));
    host->health_default_recipient = strdup("root");


    // ------------------------------------------------------------------------
    // load health configuration

    if(host->health_enabled) {
        health_alarm_log_load(host);
        health_alarm_log_open(host);

        rrdhost_wrlock(host);
        health_readdir(host, health_config_dir());
        rrdhost_unlock(host);
    }


    // ------------------------------------------------------------------------
    // link it and add it to the index

    if(is_localhost) {
        host->next = localhost;
        localhost = host;
    }
    else {
        if(localhost) {
            host->next = localhost->next;
            localhost->next = host;
        }
        else localhost = host;
    }

    RRDHOST *t = rrdhost_index_add(host);

    if(t != host) {
        error("Host '%s': cannot add host with machine guid '%s' to index. It already exists as host '%s' with machine guid '%s'.", host->hostname, host->machine_guid, t->hostname, t->machine_guid);
        rrdhost_free(host);
        host = NULL;
    }
    else {
        info("Host '%s' with guid '%s' initialized"
                     ", os %s"
                     ", update every %d"
                     ", memory mode %s"
                     ", history entries %ld"
                     ", streaming %s"
                     " (to '%s' with api key '%s')"
                     ", health %s"
                     ", cache_dir '%s'"
                     ", varlib_dir '%s'"
                     ", health_log '%s'"
                     ", alarms default handler '%s'"
                     ", alarms default recipient '%s'"
             , host->hostname
             , host->machine_guid
             , host->os
             , host->rrd_update_every
             , rrd_memory_mode_name(host->rrd_memory_mode)
             , host->rrd_history_entries
             , host->rrdpush_enabled?"enabled":"disabled"
             , host->rrdpush_destination?host->rrdpush_destination:""
             , host->rrdpush_api_key?host->rrdpush_api_key:""
             , host->health_enabled?"enabled":"disabled"
             , host->cache_dir
             , host->varlib_dir
             , host->health_log_filename
             , host->health_default_exec
             , host->health_default_recipient
        );
    }

    rrd_hosts_available++;

    return host;
}
Пример #23
0
static void get_netdata_configured_variables() {
    backwards_compatible_config();

    // ------------------------------------------------------------------------
    // get the hostname

    char buf[HOSTNAME_MAX + 1];
    if(gethostname(buf, HOSTNAME_MAX) == -1)
        error("Cannot get machine hostname.");

    netdata_configured_hostname = config_get(CONFIG_SECTION_GLOBAL, "hostname", buf);
    debug(D_OPTIONS, "hostname set to '%s'", netdata_configured_hostname);

    // ------------------------------------------------------------------------
    // get default database size

    default_rrd_history_entries = (int) config_get_number(CONFIG_SECTION_GLOBAL, "history", align_entries_to_pagesize(default_rrd_memory_mode, RRD_DEFAULT_HISTORY_ENTRIES));

    long h = align_entries_to_pagesize(default_rrd_memory_mode, default_rrd_history_entries);
    if(h != default_rrd_history_entries) {
        config_set_number(CONFIG_SECTION_GLOBAL, "history", h);
        default_rrd_history_entries = (int)h;
    }

    if(default_rrd_history_entries < 5 || default_rrd_history_entries > RRD_HISTORY_ENTRIES_MAX) {
        error("Invalid history entries %d given. Defaulting to %d.", default_rrd_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
        default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
    }

    // ------------------------------------------------------------------------
    // get default database update frequency

    default_rrd_update_every = (int) config_get_number(CONFIG_SECTION_GLOBAL, "update every", UPDATE_EVERY);
    if(default_rrd_update_every < 1 || default_rrd_update_every > 600) {
        error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", default_rrd_update_every, UPDATE_EVERY_MAX);
        default_rrd_update_every = UPDATE_EVERY;
    }

    // ------------------------------------------------------------------------
    // get system paths

    netdata_configured_config_dir  = config_get(CONFIG_SECTION_GLOBAL, "config directory",    CONFIG_DIR);
    netdata_configured_log_dir     = config_get(CONFIG_SECTION_GLOBAL, "log directory",       LOG_DIR);
    netdata_configured_web_dir     = config_get(CONFIG_SECTION_GLOBAL, "web files directory", WEB_DIR);
    netdata_configured_cache_dir   = config_get(CONFIG_SECTION_GLOBAL, "cache directory",     CACHE_DIR);
    netdata_configured_varlib_dir  = config_get(CONFIG_SECTION_GLOBAL, "lib directory",       VARLIB_DIR);
    netdata_configured_home_dir    = config_get(CONFIG_SECTION_GLOBAL, "home directory",      CACHE_DIR);

    {
        char plugins_dirs[(FILENAME_MAX * 2) + 1];
        snprintfz(plugins_dirs, FILENAME_MAX * 2, "\"%s\" \"%s/custom-plugins.d\"", PLUGINS_DIR, CONFIG_DIR);
        netdata_configured_plugins_dir_base = strdupz(config_get(CONFIG_SECTION_GLOBAL, "plugins directory",  plugins_dirs));
        quoted_strings_splitter(netdata_configured_plugins_dir_base, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace);
        netdata_configured_plugins_dir = plugin_directories[0];
    }

    // ------------------------------------------------------------------------
    // get default memory mode for the database

    default_rrd_memory_mode = rrd_memory_mode_id(config_get(CONFIG_SECTION_GLOBAL, "memory mode", rrd_memory_mode_name(default_rrd_memory_mode)));

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

    netdata_configured_host_prefix = config_get(CONFIG_SECTION_GLOBAL, "host access prefix", "");
    verify_netdata_host_prefix();

    // --------------------------------------------------------------------
    // get KSM settings

#ifdef MADV_MERGEABLE
    enable_ksm = config_get_boolean(CONFIG_SECTION_GLOBAL, "memory deduplication (ksm)", enable_ksm);
#endif

    // --------------------------------------------------------------------
    // get various system parameters

    get_system_HZ();
    get_system_cpus();
    get_system_pid_max();
}
Пример #24
0
int registry_init(void) {
    char filename[FILENAME_MAX + 1];

    // registry enabled?
    if(web_server_mode != WEB_SERVER_MODE_NONE) {
        registry.enabled = config_get_boolean(CONFIG_SECTION_REGISTRY, "enabled", 0);
    }
    else {
        info("Registry is disabled - use the central netdata");
        config_set_boolean(CONFIG_SECTION_REGISTRY, "enabled", 0);
        registry.enabled = 0;
    }

    // pathnames
    snprintfz(filename, FILENAME_MAX, "%s/registry", netdata_configured_varlib_dir);
    registry.pathname = config_get(CONFIG_SECTION_REGISTRY, "registry db directory", filename);
    if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST)
        fatal("Cannot create directory '%s'.", registry.pathname);

    // filenames
    snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
    registry.machine_guid_filename = config_get(CONFIG_SECTION_REGISTRY, "netdata unique id file", filename);

    snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
    registry.db_filename = config_get(CONFIG_SECTION_REGISTRY, "registry db file", filename);

    snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
    registry.log_filename = config_get(CONFIG_SECTION_REGISTRY, "registry log file", filename);

    // configuration options
    registry.save_registry_every_entries = (unsigned long long)config_get_number(CONFIG_SECTION_REGISTRY, "registry save db every new entries", 1000000);
    registry.persons_expiration = config_get_number(CONFIG_SECTION_REGISTRY, "registry expire idle persons days", 365) * 86400;
    registry.registry_domain = config_get(CONFIG_SECTION_REGISTRY, "registry domain", "");
    registry.registry_to_announce = config_get(CONFIG_SECTION_REGISTRY, "registry to announce", "https://registry.my-netdata.io");
    registry.hostname = config_get(CONFIG_SECTION_REGISTRY, "registry hostname", netdata_configured_hostname);
    registry.verify_cookies_redirects = config_get_boolean(CONFIG_SECTION_REGISTRY, "verify browser cookies support", 1);

    setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1);
    setenv("NETDATA_REGISTRY_URL", registry.registry_to_announce, 1);

    registry.max_url_length = (size_t)config_get_number(CONFIG_SECTION_REGISTRY, "max URL length", 1024);
    if(registry.max_url_length < 10) {
        registry.max_url_length = 10;
        config_set_number(CONFIG_SECTION_REGISTRY, "max URL length", (long long)registry.max_url_length);
    }

    registry.max_name_length = (size_t)config_get_number(CONFIG_SECTION_REGISTRY, "max URL name length", 50);
    if(registry.max_name_length < 10) {
        registry.max_name_length = 10;
        config_set_number(CONFIG_SECTION_REGISTRY, "max URL name length", (long long)registry.max_name_length);
    }

    // initialize entries counters
    registry.persons_count = 0;
    registry.machines_count = 0;
    registry.usages_count = 0;
    registry.urls_count = 0;
    registry.persons_urls_count = 0;
    registry.machines_urls_count = 0;

    // initialize memory counters
    registry.persons_memory = 0;
    registry.machines_memory = 0;
    registry.urls_memory = 0;
    registry.persons_urls_memory = 0;
    registry.machines_urls_memory = 0;

    // initialize locks
    netdata_mutex_init(&registry.lock);

    // create dictionaries
    registry.persons = dictionary_create(DICTIONARY_FLAGS);
    registry.machines = dictionary_create(DICTIONARY_FLAGS);
    avl_init(&registry.registry_urls_root_index, registry_url_compare);

    // load the registry database
    if(registry.enabled) {
        registry_log_open();
        registry_db_load();
        registry_log_load();

        if(unlikely(registry_db_should_be_saved()))
            registry_db_save();
    }

    return 0;
}
Пример #25
0
int main(int argc, char **argv)
{
	int i;
	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.
	{
		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 '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 1.2.1_master\n");
					return 0;
					break;
				case 'W':
					{
						char* stacksize = "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, strlen(stacksize)) == 0) {
							optarg += strlen(stacksize);
							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);

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

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

	{
		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 *input_log_file = NULL;
	char *output_log_file = NULL;
	char *error_log_file = NULL;
	char *access_log_file = NULL;
	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)
				info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
			prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
		}

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

#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);

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

		output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
		if(strcmp(output_log_file, "syslog") == 0) {
			output_log_syslog = 1;
			output_log_file = NULL;
		}
		else if(strcmp(output_log_file, "none") == 0) {
			output_log_syslog = 0;
			output_log_file = NULL;
		}
		else output_log_syslog = 0;

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

		error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
		if(strcmp(error_log_file, "syslog") == 0) {
			error_log_syslog = 1;
			error_log_file = NULL;
		}
		else if(strcmp(error_log_file, "none") == 0) {
			error_log_syslog = 0;
			error_log_file = NULL;
			// optimization - do not even generate debug log entries
		}
		else error_log_syslog = 0;

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

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

		access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
		if(strcmp(access_log_file, "syslog") == 0) {
			access_log_syslog = 1;
			access_log_file = NULL;
		}
		else if(strcmp(access_log_file, "none") == 0) {
			access_log_syslog = 0;
			access_log_file = NULL;
		}
		else access_log_syslog = 0;

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

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

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

		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);
			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) {
			info("Invalid update timer %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 to quit savely
		struct sigaction sa;
		sigemptyset(&sa.sa_mask);
		sigaddset(&sa.sa_mask, SIGHUP);
		sigaddset(&sa.sa_mask, SIGINT);
		sigaddset(&sa.sa_mask, SIGTERM);
		sa.sa_handler = sig_handler_exit;
		sa.sa_flags = 0;
		if(sigaction(SIGHUP, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGHUP");
		}
		if(sigaction(SIGINT, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGINT");
		}
		if(sigaction(SIGTERM, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGTERM");
		}

		// save database on SIGUSR1
		sa.sa_handler = sig_handler_save;
		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.
		sa.sa_handler = SIG_IGN;
		if(sigaction(SIGPIPE, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGPIPE");
		}

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

		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 = config_get_number("global", "pthread stack size", 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();

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

		create_listen_sockets();
	}

	// never become a problem
	if(nice(20) == -1) error("Cannot lower my CPU priority.");

	if(become_daemon(dont_fork, 0, user, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1)
		fatal("Cannot demonize myself.");

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

	if(output_log_syslog || error_log_syslog || access_log_syslog)
		openlog("netdata", LOG_PID, LOG_DAEMON);

	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
			info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
	}

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

	registry_init();

	// ------------------------------------------------------------------------
	// 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 = malloc(sizeof(pthread_t));
			if(!st->thread)
				fatal("Cannot allocate pthread_t memory");

			info("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 info("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) {
			info("Exit main loop of netdata.");
			netdata_cleanup_and_exit(0);
			exit(0);
		}
	}
}
Пример #26
0
void *backends_main(void *ptr) {
    netdata_thread_cleanup_push(backends_main_cleanup, ptr);

    int default_port = 0;
    int sock = -1;
    BUFFER *b = buffer_create(1), *response = buffer_create(1);
    int (*backend_request_formatter)(BUFFER *, const char *, RRDHOST *, const char *, RRDSET *, RRDDIM *, time_t, time_t, uint32_t) = NULL;
    int (*backend_response_checker)(BUFFER *) = NULL;

    // ------------------------------------------------------------------------
    // collect configuration options

    struct timeval timeout = {
            .tv_sec = 0,
            .tv_usec = 0
    };
    int enabled             = config_get_boolean(CONFIG_SECTION_BACKEND, "enabled", 0);
    const char *source      = config_get(CONFIG_SECTION_BACKEND, "data source", "average");
    const char *type        = config_get(CONFIG_SECTION_BACKEND, "type", "graphite");
    const char *destination = config_get(CONFIG_SECTION_BACKEND, "destination", "localhost");
    backend_prefix          = config_get(CONFIG_SECTION_BACKEND, "prefix", "netdata");
    const char *hostname    = config_get(CONFIG_SECTION_BACKEND, "hostname", localhost->hostname);
    backend_update_every    = (int)config_get_number(CONFIG_SECTION_BACKEND, "update every", backend_update_every);
    int buffer_on_failures  = (int)config_get_number(CONFIG_SECTION_BACKEND, "buffer on failures", 10);
    long timeoutms          = config_get_number(CONFIG_SECTION_BACKEND, "timeout ms", backend_update_every * 2 * 1000);
    backend_send_names      = config_get_boolean(CONFIG_SECTION_BACKEND, "send names instead of ids", backend_send_names);

    charts_pattern = simple_pattern_create(config_get(CONFIG_SECTION_BACKEND, "send charts matching", "*"), NULL, SIMPLE_PATTERN_EXACT);
    hosts_pattern = simple_pattern_create(config_get(CONFIG_SECTION_BACKEND, "send hosts matching", "localhost *"), NULL, SIMPLE_PATTERN_EXACT);


    // ------------------------------------------------------------------------
    // validate configuration options
    // and prepare for sending data to our backend

    backend_options = backend_parse_data_source(source, backend_options);

    if(timeoutms < 1) {
        error("BACKEND: invalid timeout %ld ms given. Assuming %d ms.", timeoutms, backend_update_every * 2 * 1000);
        timeoutms = backend_update_every * 2 * 1000;
    }
    timeout.tv_sec  = (timeoutms * 1000) / 1000000;
    timeout.tv_usec = (timeoutms * 1000) % 1000000;

    if(!enabled || backend_update_every < 1)
        goto cleanup;

    // ------------------------------------------------------------------------
    // select the backend type

    if(!strcmp(type, "graphite") || !strcmp(type, "graphite:plaintext")) {

        default_port = 2003;
        backend_response_checker = process_graphite_response;

        if((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED)
            backend_request_formatter = format_dimension_collected_graphite_plaintext;
        else
            backend_request_formatter = format_dimension_stored_graphite_plaintext;

    }
    else if(!strcmp(type, "opentsdb") || !strcmp(type, "opentsdb:telnet")) {

        default_port = 4242;
        backend_response_checker = process_opentsdb_response;

        if((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED)
            backend_request_formatter = format_dimension_collected_opentsdb_telnet;
        else
            backend_request_formatter = format_dimension_stored_opentsdb_telnet;

    }
    else if (!strcmp(type, "json") || !strcmp(type, "json:plaintext")) {

        default_port = 5448;
        backend_response_checker = process_json_response;

        if ((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED)
            backend_request_formatter = format_dimension_collected_json_plaintext;
        else
            backend_request_formatter = format_dimension_stored_json_plaintext;

    }
    else {
        error("BACKEND: Unknown backend type '%s'", type);
        goto cleanup;
    }

    if(backend_request_formatter == NULL || backend_response_checker == NULL) {
        error("BACKEND: backend is misconfigured - disabling it.");
        goto cleanup;
    }


    // ------------------------------------------------------------------------
    // prepare the charts for monitoring the backend operation

    struct rusage thread;

    collected_number
            chart_buffered_metrics = 0,
            chart_lost_metrics = 0,
            chart_sent_metrics = 0,
            chart_buffered_bytes = 0,
            chart_received_bytes = 0,
            chart_sent_bytes = 0,
            chart_receptions = 0,
            chart_transmission_successes = 0,
            chart_transmission_failures = 0,
            chart_data_lost_events = 0,
            chart_lost_bytes = 0,
            chart_backend_reconnects = 0,
            chart_backend_latency = 0;

    RRDSET *chart_metrics = rrdset_create_localhost("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", "backends", NULL, 130600, backend_update_every, RRDSET_TYPE_LINE);
    rrddim_add(chart_metrics, "buffered", NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_metrics, "lost",     NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_metrics, "sent",     NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);

    RRDSET *chart_bytes = rrdset_create_localhost("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", "backends", NULL, 130610, backend_update_every, RRDSET_TYPE_AREA);
    rrddim_add(chart_bytes, "buffered", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_bytes, "lost",     NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_bytes, "sent",     NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_bytes, "received", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);

    RRDSET *chart_ops = rrdset_create_localhost("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", "backends", NULL, 130630, backend_update_every, RRDSET_TYPE_LINE);
    rrddim_add(chart_ops, "write",     NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_ops, "discard",   NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_ops, "reconnect", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_ops, "failure",   NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
    rrddim_add(chart_ops, "read",      NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);

    /*
     * this is misleading - we can only measure the time we need to send data
     * this time is not related to the time required for the data to travel to
     * the backend database and the time that server needed to process them
     *
     * issue #1432 and https://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html
     *
    RRDSET *chart_latency = rrdset_create_localhost("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", "backends", NULL, 130620, backend_update_every, RRDSET_TYPE_AREA);
    rrddim_add(chart_latency, "latency",   NULL,  1, 1000, RRD_ALGORITHM_ABSOLUTE);
    */

    RRDSET *chart_rusage = rrdset_create_localhost("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", "backends", NULL, 130630, backend_update_every, RRDSET_TYPE_STACKED);
    rrddim_add(chart_rusage, "user",   NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
    rrddim_add(chart_rusage, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);


    // ------------------------------------------------------------------------
    // prepare the backend main loop

    info("BACKEND: configured ('%s' on '%s' sending '%s' data, every %d seconds, as host '%s', with prefix '%s')", type, destination, source, backend_update_every, hostname, backend_prefix);

    usec_t step_ut = backend_update_every * USEC_PER_SEC;
    time_t after = now_realtime_sec();
    int failures = 0;
    heartbeat_t hb;
    heartbeat_init(&hb);

    while(!netdata_exit) {

        // ------------------------------------------------------------------------
        // Wait for the next iteration point.

        heartbeat_next(&hb, step_ut);
        time_t before = now_realtime_sec();
        debug(D_BACKEND, "BACKEND: preparing buffer for timeframe %lu to %lu", (unsigned long)after, (unsigned long)before);

        // ------------------------------------------------------------------------
        // add to the buffer the data we need to send to the backend

        netdata_thread_disable_cancelability();

        size_t count_hosts = 0;
        size_t count_charts_total = 0;
        size_t count_dims_total = 0;

        rrd_rdlock();
        RRDHOST *host;
        rrdhost_foreach_read(host) {
            if(unlikely(!rrdhost_flag_check(host, RRDHOST_FLAG_BACKEND_SEND|RRDHOST_FLAG_BACKEND_DONT_SEND))) {
                char *name = (host == localhost)?"localhost":host->hostname;
                if (!hosts_pattern || simple_pattern_matches(hosts_pattern, name)) {
                    rrdhost_flag_set(host, RRDHOST_FLAG_BACKEND_SEND);
                    info("enabled backend for host '%s'", name);
                }
                else {
                    rrdhost_flag_set(host, RRDHOST_FLAG_BACKEND_DONT_SEND);
                    info("disabled backend for host '%s'", name);
                }
            }

            if(unlikely(!rrdhost_flag_check(host, RRDHOST_FLAG_BACKEND_SEND)))
                continue;

            rrdhost_rdlock(host);

            count_hosts++;
            size_t count_charts = 0;
            size_t count_dims = 0;
            size_t count_dims_skipped = 0;

            const char *__hostname = (host == localhost)?hostname:host->hostname;

            RRDSET *st;
            rrdset_foreach_read(st, host) {
                if(likely(backends_can_send_rrdset(backend_options, st))) {
                    rrdset_rdlock(st);

                    count_charts++;

                    RRDDIM *rd;
                    rrddim_foreach_read(rd, st) {
                        if (likely(rd->last_collected_time.tv_sec >= after)) {
                            chart_buffered_metrics += backend_request_formatter(b, backend_prefix, host, __hostname, st, rd, after, before, backend_options);
                            count_dims++;
                        }
                        else {
                            debug(D_BACKEND, "BACKEND: not sending dimension '%s' of chart '%s' from host '%s', its last data collection (%lu) is not within our timeframe (%lu to %lu)", rd->id, st->id, __hostname, (unsigned long)rd->last_collected_time.tv_sec, (unsigned long)after, (unsigned long)before);
                            count_dims_skipped++;
                        }
                    }

                    rrdset_unlock(st);
                }
            }

            debug(D_BACKEND, "BACKEND: sending host '%s', metrics of %zu dimensions, of %zu charts. Skipped %zu dimensions.", __hostname, count_dims, count_charts, count_dims_skipped);
            count_charts_total += count_charts;
            count_dims_total += count_dims;

            rrdhost_unlock(host);
        }
        rrd_unlock();

        netdata_thread_enable_cancelability();

        debug(D_BACKEND, "BACKEND: buffer has %zu bytes, added metrics for %zu dimensions, of %zu charts, from %zu hosts", buffer_strlen(b), count_dims_total, count_charts_total, count_hosts);

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

        chart_buffered_bytes = (collected_number)buffer_strlen(b);

        // reset the monitoring chart counters
        chart_received_bytes =
        chart_sent_bytes =
        chart_sent_metrics =
        chart_lost_metrics =
        chart_transmission_successes =
        chart_transmission_failures =
        chart_data_lost_events =
        chart_lost_bytes =
        chart_backend_reconnects =
        chart_backend_latency = 0;

        if(unlikely(netdata_exit)) break;

        //fprintf(stderr, "\nBACKEND BEGIN:\n%s\nBACKEND END\n", buffer_tostring(b)); // FIXME
        //fprintf(stderr, "after = %lu, before = %lu\n", after, before);

        // prepare for the next iteration
        // to add incrementally data to buffer
        after = before;

        // ------------------------------------------------------------------------
        // if we are connected, receive a response, without blocking

        if(likely(sock != -1)) {
            errno = 0;

            // loop through to collect all data
            while(sock != -1 && errno != EWOULDBLOCK) {
                buffer_need_bytes(response, 4096);

                ssize_t r = recv(sock, &response->buffer[response->len], response->size - response->len, MSG_DONTWAIT);
                if(likely(r > 0)) {
                    // we received some data
                    response->len += r;
                    chart_received_bytes += r;
                    chart_receptions++;
                }
                else if(r == 0) {
                    error("BACKEND: '%s' closed the socket", destination);
                    close(sock);
                    sock = -1;
                }
                else {
                    // failed to receive data
                    if(errno != EAGAIN && errno != EWOULDBLOCK) {
                        error("BACKEND: cannot receive data from backend '%s'.", destination);
                    }
                }
            }

            // if we received data, process them
            if(buffer_strlen(response))
                backend_response_checker(response);
        }

        // ------------------------------------------------------------------------
        // if we are not connected, connect to a backend server

        if(unlikely(sock == -1)) {
            usec_t start_ut = now_monotonic_usec();
            size_t reconnects = 0;

            sock = connect_to_one_of(destination, default_port, &timeout, &reconnects, NULL, 0);

            chart_backend_reconnects += reconnects;
            chart_backend_latency += now_monotonic_usec() - start_ut;
        }

        if(unlikely(netdata_exit)) break;

        // ------------------------------------------------------------------------
        // if we are connected, send our buffer to the backend server

        if(likely(sock != -1)) {
            size_t len = buffer_strlen(b);
            usec_t start_ut = now_monotonic_usec();
            int flags = 0;
#ifdef MSG_NOSIGNAL
            flags += MSG_NOSIGNAL;
#endif

            ssize_t written = send(sock, buffer_tostring(b), len, flags);
            chart_backend_latency += now_monotonic_usec() - start_ut;
            if(written != -1 && (size_t)written == len) {
                // we sent the data successfully
                chart_transmission_successes++;
                chart_sent_bytes += written;
                chart_sent_metrics = chart_buffered_metrics;

                // reset the failures count
                failures = 0;

                // empty the buffer
                buffer_flush(b);
            }
            else {
                // oops! we couldn't send (all or some of the) data
                error("BACKEND: failed to write data to database backend '%s'. Willing to write %zu bytes, wrote %zd bytes. Will re-connect.", destination, len, written);
                chart_transmission_failures++;

                if(written != -1)
                    chart_sent_bytes += written;

                // increment the counter we check for data loss
                failures++;

                // close the socket - we will re-open it next time
                close(sock);
                sock = -1;
            }
        }
        else {
            error("BACKEND: failed to update database backend '%s'", destination);
            chart_transmission_failures++;

            // increment the counter we check for data loss
            failures++;
        }

        if(failures > buffer_on_failures) {
            // too bad! we are going to lose data
            chart_lost_bytes += buffer_strlen(b);
            error("BACKEND: reached %d backend failures. Flushing buffers to protect this host - this results in data loss on back-end server '%s'", failures, destination);
            buffer_flush(b);
            failures = 0;
            chart_data_lost_events++;
            chart_lost_metrics = chart_buffered_metrics;
        }

        if(unlikely(netdata_exit)) break;

        // ------------------------------------------------------------------------
        // update the monitoring charts

        if(likely(chart_ops->counter_done)) rrdset_next(chart_ops);
        rrddim_set(chart_ops, "read",         chart_receptions);
        rrddim_set(chart_ops, "write",        chart_transmission_successes);
        rrddim_set(chart_ops, "discard",      chart_data_lost_events);
        rrddim_set(chart_ops, "failure",      chart_transmission_failures);
        rrddim_set(chart_ops, "reconnect",    chart_backend_reconnects);
        rrdset_done(chart_ops);

        if(likely(chart_metrics->counter_done)) rrdset_next(chart_metrics);
        rrddim_set(chart_metrics, "buffered", chart_buffered_metrics);
        rrddim_set(chart_metrics, "lost",     chart_lost_metrics);
        rrddim_set(chart_metrics, "sent",     chart_sent_metrics);
        rrdset_done(chart_metrics);

        if(likely(chart_bytes->counter_done)) rrdset_next(chart_bytes);
        rrddim_set(chart_bytes, "buffered",   chart_buffered_bytes);
        rrddim_set(chart_bytes, "lost",       chart_lost_bytes);
        rrddim_set(chart_bytes, "sent",       chart_sent_bytes);
        rrddim_set(chart_bytes, "received",   chart_received_bytes);
        rrdset_done(chart_bytes);

        /*
        if(likely(chart_latency->counter_done)) rrdset_next(chart_latency);
        rrddim_set(chart_latency, "latency",  chart_backend_latency);
        rrdset_done(chart_latency);
        */

        getrusage(RUSAGE_THREAD, &thread);
        if(likely(chart_rusage->counter_done)) rrdset_next(chart_rusage);
        rrddim_set(chart_rusage, "user",   thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
        rrddim_set(chart_rusage, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
        rrdset_done(chart_rusage);

        if(likely(buffer_strlen(b) == 0))
            chart_buffered_metrics = 0;

        if(unlikely(netdata_exit)) break;
    }

cleanup:
    if(sock != -1)
        close(sock);

    buffer_free(b);
    buffer_free(response);

    netdata_thread_cleanup_pop(1);
    return NULL;
}
Пример #27
0
int main(int argc, char **argv)
{
	int i;
	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  the arguments
	for(i = 1; i < argc ; i++) {
		if(strcmp(argv[i], "-c") == 0 && (i+1) < argc) {
			if(load_config(argv[i+1], 1) != 1) {
				error("Cannot load configuration file %s.", argv[i+1]);
				exit(1);
			}
			else {
				debug(D_OPTIONS, "Configuration loaded from %s.", argv[i+1]);
				config_loaded = 1;
			}
			i++;
		}
		else if(strcmp(argv[i], "-df") == 0 && (i+1) < argc) { config_set("global", "debug flags",  argv[i+1]); debug_flags = strtoull(argv[i+1], NULL, 0); i++; }
		else if(strcmp(argv[i], "-p")  == 0 && (i+1) < argc) { config_set("global", "port",         argv[i+1]); i++; }
		else if(strcmp(argv[i], "-u")  == 0 && (i+1) < argc) { config_set("global", "run as user",  argv[i+1]); i++; }
		else if(strcmp(argv[i], "-l")  == 0 && (i+1) < argc) { config_set("global", "history",      argv[i+1]); i++; }
		else if(strcmp(argv[i], "-t")  == 0 && (i+1) < argc) { config_set("global", "update every", argv[i+1]); i++; }
		else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) { config_set("global", "host access prefix", argv[i+1]); i++; }
		else if(strcmp(argv[i], "-stacksize") == 0 && (i+1) < argc) { config_set("global", "pthread stack size", argv[i+1]); i++; }
		else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) dont_fork = 1;
		else if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
			i++;
			strncpy(pidfile, argv[i], FILENAME_MAX);
			pidfile[FILENAME_MAX] = '\0';
		}
		else if(strcmp(argv[i], "--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 {
			fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
			fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-p LISTEN_PORT] [-df debug flags].\n\n", argv[0]);
			fprintf(stderr, "  -c CONFIG FILE the configuration file to load. Default: %s.\n", CONFIG_DIR "/" CONFIG_FILENAME);
			fprintf(stderr, "  -l LINES_TO_SAVE can be from 5 to %d lines in JSON data. Default: %d.\n", RRD_HISTORY_ENTRIES_MAX, RRD_DEFAULT_HISTORY_ENTRIES);
			fprintf(stderr, "  -t UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", UPDATE_EVERY_MAX, UPDATE_EVERY);
			fprintf(stderr, "  -p LISTEN_PORT can be from 1 to %d. Default: %d.\n", 65535, LISTEN_PORT);
			fprintf(stderr, "  -u USERNAME can be any system username to run as. Default: none.\n");
			fprintf(stderr, "  -ch path to access host /proc and /sys when running in a container. Default: empty.\n");
			fprintf(stderr, "  -nd or -nodeamon to disable forking in the background. Default: unset.\n");
			fprintf(stderr, "  -df FLAGS debug options. Default: 0x%08llx.\n", debug_flags);
			fprintf(stderr, "  -stacksize BYTES to overwrite the pthread stack size.\n");
			fprintf(stderr, "  -pidfile FILENAME to save a pid while running.\n");
			exit(1);
		}
	}

	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_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);

	// avoid extended 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);

	// cd to /tmp to avoid any plugins writing files at random places
	if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");

	char *input_log_file = NULL;
	char *output_log_file = NULL;
	char *error_log_file = NULL;
	char *access_log_file = NULL;
	char *user = NULL;
	{
		char buffer[1024];

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

		sprintf(buffer, "0x%08llx", 0ULL);
		char *flags = config_get("global", "debug flags", buffer);
		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)
				info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
			prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
		}

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

#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);

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

		output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
		if(strcmp(output_log_file, "syslog") == 0) {
			output_log_syslog = 1;
			output_log_file = NULL;
		}
		else if(strcmp(output_log_file, "none") == 0) {
			output_log_syslog = 0;
			output_log_file = NULL;
		}
		else output_log_syslog = 0;

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

		error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
		if(strcmp(error_log_file, "syslog") == 0) {
			error_log_syslog = 1;
			error_log_file = NULL;
		}
		else if(strcmp(error_log_file, "none") == 0) {
			error_log_syslog = 0;
			error_log_file = NULL;
			// optimization - do not even generate debug log entries
		}
		else error_log_syslog = 0;

		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 = 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);

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

		access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
		if(strcmp(access_log_file, "syslog") == 0) {
			access_log_syslog = 1;
			access_log_file = NULL;
		}
		else if(strcmp(access_log_file, "none") == 0) {
			access_log_syslog = 0;
			access_log_file = NULL;
		}
		else access_log_syslog = 0;

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

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

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

		if(gethostname(buffer, HOSTNAME_MAX) == -1)
			error("WARNING: Cannot get machine hostname.");
		hostname = config_get("global", "hostname", buffer);
		debug(D_OPTIONS, "hostname set to '%s'", hostname);

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

		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);
			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) {
			info("Invalid update timer %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[50];
			snprintf(buf, 50, "%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 to quit savely
		struct sigaction sa;
		sigemptyset(&sa.sa_mask);
		sigaddset(&sa.sa_mask, SIGHUP);
		sigaddset(&sa.sa_mask, SIGINT);
		sigaddset(&sa.sa_mask, SIGTERM);
		sa.sa_handler = sig_handler;
		sa.sa_flags = 0;
		if(sigaction(SIGHUP, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGHUP");
		}
		if(sigaction(SIGINT, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGINT");
		}
		if(sigaction(SIGTERM, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGTERM");
		}
		// Ignore SIGPIPE completely.
		// INFO: If we add signals here we have to unblock them
		// at popen.c when running a external plugin.
		sa.sa_handler = SIG_IGN;
		if(sigaction(SIGPIPE, &sa, NULL) == -1) {
			error("Failed to change signal handler for SIGTERM");
		}

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

		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 = config_get_number("global", "pthread stack size", 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();

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

		listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);

		listen_port = (int) config_get_number("global", "port", LISTEN_PORT);
		if(listen_port < 1 || listen_port > 65535) {
			info("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
			listen_port = LISTEN_PORT;
		}
		else debug(D_OPTIONS, "Listen port set to %d.", listen_port);

		int ip = 0;
		char *ipv = config_get("global", "ip version", "any");
		if(!strcmp(ipv, "any") || !strcmp(ipv, "both") || !strcmp(ipv, "all")) ip = 0;
		else if(!strcmp(ipv, "ipv4") || !strcmp(ipv, "IPV4") || !strcmp(ipv, "IPv4") || !strcmp(ipv, "4")) ip = 4;
		else if(!strcmp(ipv, "ipv6") || !strcmp(ipv, "IPV6") || !strcmp(ipv, "IPv6") || !strcmp(ipv, "6")) ip = 6;
		else info("Cannot understand ip version '%s'. Assuming 'any'.", ipv);

		if(ip == 0 || ip == 6) listen_fd = create_listen_socket6(config_get("global", "bind socket to IP", "*"), listen_port, listen_backlog);
		if(listen_fd < 0) {
			listen_fd = create_listen_socket4(config_get("global", "bind socket to IP", "*"), listen_port, listen_backlog);
			if(listen_fd >= 0 && ip != 4) info("Managed to open an IPv4 socket on port %d.", listen_port);
		}

		if(listen_fd < 0) fatal("Cannot listen socket.");
	}

	// never become a problem
	if(nice(20) == -1) error("Cannot lower my CPU priority.");

	if(become_daemon(dont_fork, 0, user, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1) {
		fatal("Cannot demonize myself.");
		exit(1);
	}

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

		prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
	}

	if(output_log_syslog || error_log_syslog || access_log_syslog)
		openlog("netdata", LOG_PID, LOG_DAEMON);

	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
			info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
	}

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

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

		if(st->enabled) {
			st->thread = malloc(sizeof(pthread_t));
			if(!st->thread)
				fatal("Cannot allocate pthread_t memory");

			info("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 info("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) {
			info("Exit main loop of netdata.");
			netdata_cleanup_and_exit(0);
			exit(0);
		}
	}
}
Пример #28
0
RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char *family, const char *context, const char *title, const char *units, long priority, int update_every, int chart_type)
{
	if(!type || !type[0]) {
		fatal("Cannot create rrd stats without a type.");
		return NULL;
	}

	if(!id || !id[0]) {
		fatal("Cannot create rrd stats without an id.");
		return NULL;
	}

	char fullid[RRD_ID_LENGTH_MAX + 1];
	char fullfilename[FILENAME_MAX + 1];
	RRDSET *st = NULL;

	snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);

	st = rrdset_find(fullid);
	if(st) {
		error("Cannot create rrd stats for '%s', it already exists.", fullid);
		return st;
	}

	long entries = config_get_number(fullid, "history", rrd_default_history_entries);
	if(entries < 5) entries = config_set_number(fullid, "history", 5);
	if(entries > RRD_HISTORY_ENTRIES_MAX) entries = config_set_number(fullid, "history", RRD_HISTORY_ENTRIES_MAX);

	int enabled = config_get_boolean(fullid, "enabled", 1);
	if(!enabled) entries = 5;

	unsigned long size = sizeof(RRDSET);
	char *cache_dir = rrdset_cache_dir(fullid);

	debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);

	snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
	if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) st = (RRDSET *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 0);
	if(st) {
		if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
			errno = 0;
			info("Initializing file %s.", fullfilename);
			bzero(st, size);
		}
		else if(strcmp(st->id, fullid) != 0) {
			errno = 0;
			error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
			// munmap(st, size);
			// st = NULL;
			bzero(st, size);
		}
		else if(st->memsize != size || st->entries != entries) {
			errno = 0;
			error("File %s does not have the desired size. Clearing it.", fullfilename);
			bzero(st, size);
		}
		else if(st->update_every != update_every) {
			errno = 0;
			error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
			bzero(st, size);
		}
		else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) {
			errno = 0;
			error("File %s is too old. Clearing it.", fullfilename);
			bzero(st, size);
		}
	}

	if(st) {
		st->name = NULL;
		st->type = NULL;
		st->family = NULL;
		st->context = NULL;
		st->title = NULL;
		st->units = NULL;
		st->dimensions = NULL;
		st->next = NULL;
		st->mapped = rrd_memory_mode;
	}
	else {
		st = calloc(1, size);
		if(!st) {
			fatal("Cannot allocate memory for RRD_STATS %s.%s", type, id);
			return NULL;
		}
		st->mapped = RRD_MEMORY_MODE_RAM;
	}
	st->memsize = size;
	st->entries = entries;
	st->update_every = update_every;

	if(st->current_entry >= st->entries) st->current_entry = 0;

	strcpy(st->cache_filename, fullfilename);
	strcpy(st->magic, RRDSET_MAGIC);

	strcpy(st->id, fullid);
	st->hash = simple_hash(st->id);

	st->cache_dir = cache_dir;

	st->chart_type = rrdset_type_id(config_get(st->id, "chart type", rrdset_type_name(chart_type)));
	st->type       = config_get(st->id, "type", type);
	st->family     = config_get(st->id, "family", family?family:st->type);
	st->context    = config_get(st->id, "context", context?context:st->id);
	st->units      = config_get(st->id, "units", units?units:"");

	st->priority = config_get_number(st->id, "priority", priority);
	st->enabled = enabled;

	st->isdetail = 0;
	st->debug = 0;

	st->last_collected_time.tv_sec = 0;
	st->last_collected_time.tv_usec = 0;
	st->counter_done = 0;

	st->gap_when_lost_iterations_above = (int) (
			config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);

	avl_init_lock(&st->dimensions_index, rrddim_compare);

	pthread_rwlock_init(&st->rwlock, NULL);
	pthread_rwlock_wrlock(&rrdset_root_rwlock);

	if(name && *name) rrdset_set_name(st, name);
	else rrdset_set_name(st, id);

	{
		char varvalue[CONFIG_MAX_VALUE + 1];
		snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
		st->title = config_get(st->id, "title", varvalue);
	}

	st->next = rrdset_root;
	rrdset_root = st;

	rrdset_index_add(st);

	pthread_rwlock_unlock(&rrdset_root_rwlock);

	return(st);
}
Пример #29
0
int become_user(const char *username, int pid_fd) {
    int am_i_root = (getuid() == 0)?1:0;

    struct passwd *pw = getpwnam(username);
    if(!pw) {
        error("User %s is not present.", username);
        return -1;
    }

    uid_t uid = pw->pw_uid;
    gid_t gid = pw->pw_gid;

    create_needed_dir(netdata_configured_cache_dir, uid, gid);
    create_needed_dir(netdata_configured_varlib_dir, uid, gid);

    if(pidfile[0]) {
        if(chown(pidfile, uid, gid) == -1)
            error("Cannot chown '%s' to %u:%u", pidfile, (unsigned int)uid, (unsigned int)gid);
    }

    int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
    gid_t *supplementary_groups = NULL;
    if(ngroups > 0) {
        supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
        if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
            if(am_i_root)
                error("Cannot get supplementary groups of user '%s'.", username);

            ngroups = 0;
        }
    }

    chown_open_file(STDOUT_FILENO, uid, gid);
    chown_open_file(STDERR_FILENO, uid, gid);
    chown_open_file(stdaccess_fd, uid, gid);
    chown_open_file(pid_fd, uid, gid);

    if(supplementary_groups && ngroups > 0) {
        if(setgroups((size_t)ngroups, supplementary_groups) == -1) {
            if(am_i_root)
                error("Cannot set supplementary groups for user '%s'", username);
        }
        ngroups = 0;
    }

    if(supplementary_groups)
        freez(supplementary_groups);

#ifdef __APPLE__
    if(setregid(gid, gid) != 0) {
#else
    if(setresgid(gid, gid, gid) != 0) {
#endif /* __APPLE__ */
        error("Cannot switch to user's %s group (gid: %u).", username, gid);
        return -1;
    }

#ifdef __APPLE__
    if(setreuid(uid, uid) != 0) {
#else
    if(setresuid(uid, uid, uid) != 0) {
#endif /* __APPLE__ */
        error("Cannot switch to user %s (uid: %u).", username, uid);
        return -1;
    }

    if(setgid(gid) != 0) {
        error("Cannot switch to user's %s group (gid: %u).", username, gid);
        return -1;
    }
    if(setegid(gid) != 0) {
        error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
        return -1;
    }
    if(setuid(uid) != 0) {
        error("Cannot switch to user %s (uid: %u).", username, uid);
        return -1;
    }
    if(seteuid(uid) != 0) {
        error("Cannot effectively switch to user %s (uid: %u).", username, uid);
        return -1;
    }

    return(0);
}

#ifndef OOM_SCORE_ADJ_MAX
#define OOM_SCORE_ADJ_MAX 1000
#endif
#ifndef OOM_SCORE_ADJ_MIN
#define OOM_SCORE_ADJ_MIN -1000
#endif

static void oom_score_adj(void) {
    char buf[30 + 1];
    long long int old_score, wanted_score = OOM_SCORE_ADJ_MAX, final_score = 0;

    // read the existing score
    if(read_single_signed_number_file("/proc/self/oom_score_adj", &old_score)) {
        error("Out-Of-Memory (OOM) score setting is not supported on this system.");
        return;
    }

    if(old_score != 0)
        wanted_score = old_score;

    // check the environment
    char *s = getenv("OOMScoreAdjust");
    if(!s || !*s) {
        snprintfz(buf, 30, "%d", (int)wanted_score);
        s = buf;
    }

    // check netdata.conf configuration
    s = config_get(CONFIG_SECTION_GLOBAL, "OOM score", s);
    if(s && *s && (isdigit(*s) || *s == '-' || *s == '+'))
        wanted_score = atoll(s);
    else {
        info("Out-Of-Memory (OOM) score not changed due to non-numeric setting: '%s' (running with %d)", s, (int)old_score);
        return;
    }

    if(wanted_score < OOM_SCORE_ADJ_MIN) {
        error("Wanted Out-Of-Memory (OOM) score %d is too small. Using %d", (int)wanted_score, (int)OOM_SCORE_ADJ_MIN);
        wanted_score = OOM_SCORE_ADJ_MIN;
    }

    if(wanted_score > OOM_SCORE_ADJ_MAX) {
        error("Wanted Out-Of-Memory (OOM) score %d is too big. Using %d", (int)wanted_score, (int)OOM_SCORE_ADJ_MAX);
        wanted_score = OOM_SCORE_ADJ_MAX;
    }

    if(old_score == wanted_score) {
        info("Out-Of-Memory (OOM) score is already set to the wanted value %d", (int)old_score);
        return;
    }

    int written = 0;
    int fd = open("/proc/self/oom_score_adj", O_WRONLY);
    if(fd != -1) {
        snprintfz(buf, 30, "%d", (int)wanted_score);
        ssize_t len = strlen(buf);
        if(len > 0 && write(fd, buf, (size_t)len) == len) written = 1;
        close(fd);

        if(written) {
            if(read_single_signed_number_file("/proc/self/oom_score_adj", &final_score))
                error("Adjusted my Out-Of-Memory (OOM) score to %d, but cannot verify it.", (int)wanted_score);
            else if(final_score == wanted_score)
                info("Adjusted my Out-Of-Memory (OOM) score from %d to %d.", (int)old_score, (int)final_score);
            else
                error("Adjusted my Out-Of-Memory (OOM) score from %d to %d, but it has been set to %d.", (int)old_score, (int)wanted_score, (int)final_score);
        }
        else
            error("Failed to adjust my Out-Of-Memory (OOM) score to %d. Running with %d. (systemd systems may change it via netdata.service)", (int)wanted_score, (int)old_score);
    }
    else
        error("Failed to adjust my Out-Of-Memory (OOM) score. Cannot open /proc/self/oom_score_adj for writing.");
}

static void process_nice_level(void) {
#ifdef HAVE_NICE
    int nice_level = (int)config_get_number(CONFIG_SECTION_GLOBAL, "process nice level", 19);
    if(nice(nice_level) == -1) error("Cannot set netdata CPU nice level to %d.", nice_level);
    else debug(D_SYSTEM, "Set netdata nice level to %d.", nice_level);
#endif // HAVE_NICE
};
Пример #30
0
void *socket_listen_main(void *ptr)
{
	if(ptr) { ; }

	info("WEB SERVER thread created with task id %d", gettid());

	struct web_client *w;
	struct timeval tv;
	int retval;

	if(ptr) { ; }

	if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
		error("Cannot set pthread cancel type to DEFERRED.");

	if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
		error("Cannot set pthread cancel state to ENABLE.");

	web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
	web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);

	if(listen_fd < 0) fatal("LISTENER: Listen socket is not ready.");

	fd_set ifds, ofds, efds;
	int fdmax = listen_fd;

	FD_ZERO (&ifds);
	FD_ZERO (&ofds);
	FD_ZERO (&efds);

	for(;;) {
		tv.tv_sec = 0;
		tv.tv_usec = 200000;

		if(listen_fd >= 0) {
			FD_SET(listen_fd, &ifds);
			FD_SET(listen_fd, &efds);
		}

		// debug(D_WEB_CLIENT, "LISTENER: Waiting...");
		retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);

		if(retval == -1) {
			error("LISTENER: select() failed.");
			continue;
		}
		else if(retval) {
			// check for new incoming connections
			if(FD_ISSET(listen_fd, &ifds)) {
				w = web_client_create(listen_fd);
				if(unlikely(!w)) {
					// no need for error log - web_client_create already logged the error
					continue;
				}

				if(pthread_create(&w->thread, NULL, web_client_main, w) != 0) {
					error("%llu: failed to create new thread for web client.");
					w->obsolete = 1;
				}
				else if(pthread_detach(w->thread) != 0) {
					error("%llu: Cannot request detach of newly created web client thread.", w->id);
					w->obsolete = 1;
				}
			}
			else debug(D_WEB_CLIENT, "LISTENER: select() didn't do anything.");

		}
		//else {
		//	debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
		//}

		// cleanup unused clients
		for(w = web_clients; w ; w = w?w->next:NULL) {
			if(w->obsolete) {
				debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
				// pthread_join(w->thread,  NULL);
				w = web_client_free(w);
				log_allocations();
			}
		}
	}

	error("LISTENER: exit!");

	if(listen_fd >= 0) close(listen_fd);
	exit(2);

	return NULL;
}