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; }
/* Open LDAP and Notifier connection. * @return 0 on success, 1 on error. */ static int do_connection(univention_ldap_parameters_t *lp) { LDAPMessage *res; int rc; struct timeval timeout = { .tv_sec = 10, .tv_usec = 0, }; if (univention_ldap_open(lp) != LDAP_SUCCESS) goto fail; if (notifier_client_new(NULL, lp->host, 1) != 0) goto fail; /* check if we are connected to an OpenLDAP */ rc = ldap_search_ext_s(lp->ld, lp->base, LDAP_SCOPE_BASE, "objectClass=univentionBase", NULL, 0, NULL, NULL, &timeout, 0, &res); ldap_msgfree(res); switch (rc) { case LDAP_SUCCESS: return 0; case LDAP_NO_SUCH_OBJECT: univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, "Failed to find \"(objectClass=univentionBase)\" on LDAP server %s:%d", lp->host, lp->port); break; default: univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, "Failed to search for \"(objectClass=univentionBase)\" on LDAP server %s:%d with message %s", lp->host, lp->port, ldap_err2string(rc)); break; } fail: notifier_client_destroy(NULL); if (lp->ld) ldap_unbind_ext(lp->ld, NULL, NULL); lp->ld = NULL; return 1; } int main(int argc, char* argv[]) { univention_ldap_parameters_t *lp; univention_ldap_parameters_t *lp_local; char *server_role; #ifdef WITH_KRB5 univention_krb5_parameters_t *kp = NULL; bool do_kinit = false; #else void *kp = NULL ; #endif int debugging = 0; bool from_scratch = false; bool foreground = false; bool initialize_only = false; bool write_transaction_file = false; int rv; NotifierID id = -1; #ifndef WITH_DB42 NotifierID old_id = -1; #else CacheMasterEntry master_entry; #endif struct stat stbuf; char *f = NULL; univention_debug_init("stderr", 1, 1); if ((lp = univention_ldap_new()) == NULL) exit(1); lp->authmethod = LDAP_AUTH_SASL; if ((lp_local = univention_ldap_new()) == NULL) exit(1); #if WITH_KRB5 if ((kp=univention_krb5_new()) == NULL) exit(1); #endif /* parse arguments */ for (;;) { int c; c = getopt(argc, argv, "d:FH:h:p:b:D:w:y:xZY:U:R:Km:Bc:giol:"); if (c < 0) break; switch (c) { case 'd': debugging=atoi(optarg); break; case 'F': foreground = true; break; case 'H': lp->uri=strdup(optarg); break; case 'h': lp->host=strdup(optarg); break; case 'p': lp->port=atoi(optarg); break; case 'b': lp->base=strdup(optarg); break; case 'm': if ((module_dirs = realloc(module_dirs, (module_dir_count+2)*sizeof(char*))) == NULL) { return 1; } module_dirs[module_dir_count] = strdup(optarg); module_dirs[module_dir_count+1] = NULL; module_dir_count++; break; case 'c': cache_dir=strdup(optarg); break; case 'l': ldap_dir = strdup(optarg); if (asprintf(&transaction_file, "%s/listener/listener", ldap_dir) < 0) abort(); break; case 'D': lp->binddn=strdup(optarg); break; case 'w': lp->bindpw=strdup(optarg); #ifdef WITH_KRB5 kp->password=strdup(optarg); #endif /* remove password from process list */ memset(optarg, 'X', strlen(optarg)); break; case 'Z': lp->start_tls++; break; case 'x': lp->authmethod = LDAP_AUTH_SIMPLE; break; case 'y': lp->bindpw=read_pwd_from_file(optarg); #ifdef WITH_KRB5 kp->password=strdup(lp->bindpw); #endif break; case 'Y': lp->sasl_mech=strdup(optarg); break; case 'U': asprintf(&lp->sasl_authzid, "u:%s", optarg); /* kp->username=strdup(optarg); */ case 'R': lp->sasl_realm=strdup(optarg); #ifdef WITH_KRB5 kp->realm=strdup(optarg); #endif break; #ifdef WITH_KRB5 case 'K': do_kinit = true; break; #endif case 'g': from_scratch = true; break; case 'i': initialize_only = true; from_scratch = true; foreground = true; break; case 'o': write_transaction_file = true; break; case 'B': backup_notifier = 1; break; default: usage(); exit(1); } } if (asprintf(&f, "%s/bad_cache", cache_dir) < 0) abort(); if (stat(f, &stbuf) == 0) { exit(3); } free(f); univention_debug_set_level(UV_DEBUG_LISTENER, debugging); univention_debug_set_level(UV_DEBUG_LDAP, debugging); univention_debug_set_level(UV_DEBUG_KERBEROS, debugging); snprintf(pidfile, PATH_MAX, "%s/pid", cache_dir); signals_init(); if (!foreground && daemonize() != 0) exit(EXIT_FAILURE); drop_privileges(); if (from_scratch) purge_cache(cache_dir); prepare_cache(cache_dir); /* choose server to connect to */ if (lp->host == NULL && lp->uri == NULL) { select_server(lp); univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, "no server given, choosing one by myself (%s)", lp->host); } #ifdef WITH_KRB5 if (!do_kinit) kp = NULL; if (kp != NULL && univention_krb5_init(kp) != 0) { univention_debug(UV_DEBUG_KERBEROS, UV_DEBUG_ERROR, "kinit failed"); exit(1); } #endif while (do_connection(lp) != 0) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "can not connect to ldap server %s:%d", lp->host, lp->port); if (suspend_connect()) { if (initialize_only) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, "can not connect to any ldap server, exit"); exit(1); } univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "can not connect to any ldap server, retrying in 30 seconds"); sleep(30); } select_server(lp); univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "chosen server: %s:%d", lp->host, lp->port); } univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, "connection okay to host %s:%d", lp->host, lp->port); /* connect to local LDAP server */ server_role = univention_config_get_string("server/role"); if ( server_role != NULL ) { if (!strcmp(server_role, "domaincontroller_backup") || !strcmp(server_role, "domaincontroller_slave")) { // if not master lp_local->host = strdup("localhost"); // or fqdn e.g. from univention_config_get_string("ldap/server/name"); lp_local->base = strdup(lp->base); lp_local->binddn = strdup(lp->binddn); lp_local->bindpw = strdup(lp->bindpw); } free(server_role); } /* XXX: we shouldn't block all signals for so long */ signals_block(); cache_init(); handlers_init(); /* pass data to handlers */ if (lp->base != NULL) handlers_set_data_all("basedn", lp->base); if (lp->binddn != NULL) handlers_set_data_all("binddn", lp->binddn); if (lp->bindpw != NULL) handlers_set_data_all("bindpw", lp->bindpw); if (lp->host != NULL) handlers_set_data_all("ldapserver", lp->host); convert_cookie(); if (notifier_get_id_s(NULL, &id) != 0) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, "failed to receive current ID"); return 1; } if (initialize_only) { INIT_ONLY=1; } /* update schema */ if ((rv=change_update_schema(lp)) != LDAP_SUCCESS) return rv; /* do initial import of entries */ if ((rv=change_new_modules(lp)) != LDAP_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, "change_new_modules: %s", ldap_err2string(rv)); return rv; } signals_unblock(); /* if no ID is set, assume the database has just been initialized */ #ifdef WITH_DB42 if ((rv=cache_get_master_entry(&master_entry)) == DB_NOTFOUND) { master_entry.id = id; if ((rv=cache_update_master_entry(&master_entry, NULL)) != 0) exit(1); } else if (rv != 0) exit(1); #else cache_get_int("notifier_id", &old_id, -1); if ((long)old_id == -1) { cache_set_int("notifier_id", id); } #endif if (!initialize_only) { rv = notifier_listen(lp, kp, write_transaction_file, lp_local); } if (rv != 0) univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, "listener: %d", rv); univention_ldap_close(lp); univention_ldap_close(lp_local); exit_handler(0); }