#include #include #include #include #include #include #include "version.h" #include "process.h" #include "rrdtool.h" #include "parser.h" #include "debug.h" #include "config.h" #include "built_in_parsers.h" #include "plugin_manager.h" struct user_options { int max_jobs; int daemonize; char *config_file; char *pid_file; }; int read_args(int argc, char *argv[], struct user_options *opts) { int option_index = 0, c; static struct option long_options[] = { { .val = 'j', .has_arg = 1, .name = "jobs", }, { .val = 'c', .has_arg = 1, .name = "config", }, { .val = 'l', .has_arg = 1, .name = "log-file", }, { .val = 'p', .has_arg = 1, .name = "pid-file", }, { .val = 'd', .name = "daemon", }, { .val = 'v', .name = "verbose", }, { .val = 'q', .name = "quiet", }, { }, }; char short_options[] = "j:c:l:p:dvq"; while (1) { c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case 'j': opts->max_jobs = atoi(optarg); break; case 'c': opts->config_file = optarg; break; case 'l': open_log_file(optarg); break; case 'v': trace_level++; break; case 'q': trace_level--; break; case 'd': opts->daemonize = 1; break; case 'p': opts->pid_file = optarg; break; case '?': return -1; } } while (optind < argc) { opts->config_file = argv[optind]; optind++; } return 0; } static int is_already_running(const char *pidfile) { struct stat s; char pidstr[16], procpath[256] = { 0 }; int fd, ret, pid; fd = open(pidfile, O_RDONLY); if (fd < 0) { /* No pid, no running instance */ return 0; } ret = read(fd, pidstr, sizeof(pidstr) - 1); if (ret < 0) { pr_err("Failed to read pid file: %m\n"); close(fd); return ret; } close(fd); pidstr[ret] = 0; ret = sscanf(pidstr, "%d", &pid); /* Corrupted pid data? Maybe leftover from prev boot or something */ if (ret != 1) return 0; snprintf(procpath, sizeof(procpath) - 1, "/proc/%d/", pid); ret = stat(procpath, &s); if (ret < 0) { pr_info("Ignoring stale pid %d from pid file %s\n", pid, pidfile); return 0; } pr_err("Process %d is already running as stated in pid file %s\n", pid, pidfile); return 1; } static int write_pidfile(const char *pidfile) { char pidstr[16]; int fd, ret; fd = open(pidfile, O_WRONLY | O_CREAT, 0644); if (fd < 0) { pr_err("Failed to open pidfile %s for writing: %m\n", pidfile); return -1; } ret = snprintf(pidstr, sizeof(pidstr), "%d", getpid()); ret = write(fd, pidstr, ret); if (ret < 0) pr_err("Failed to write to pidfile %s: %m\n", pidfile); close(fd); return ret; } int main(int argc, char *argv[]) { struct user_options opts; struct rrd_database *db, **db_list = NULL; int sleeptime; int ret = 0; pr_info("%s Version %s starting\n", argv[0], RRDD_VERSION); bzero(&opts, sizeof(opts)); if (read_args(argc, argv, &opts) < 0) return -1; if (opts.daemonize) { pr_info("Setting itself as a daemon\n"); ret = daemon(1, 0); if (ret < 0) { pr_err("Failed to daemonize: %m\n"); return -1; } } if (opts.pid_file) { if (is_already_running(opts.pid_file)) return -1; if (write_pidfile(opts.pid_file) < 0) return -1; } init_plugin_manager(argv[0]); register_built_in_parsers(); register_network_parser(); if (!opts.config_file) { pr_err("No database config file given. Nothing to do\n"); return 1; } db_list = populate_database(opts.config_file); if (ret || !db_list) return 1; if (rrdtool_create_missing_databases(db_list)) return 1; if (init_jobcontrol(opts.max_jobs)) return -1; while (1) { const char *db_name; char timestr[128]; time_t t; /* * Update all databases parallel in one shot */ while ((db = get_outdated_db(db_list))) rrdtool_update_data(db); sleeptime = get_next_update(db_list, &db_name); if (sleeptime >= 0) { t = time(0) + sleeptime; strftime(timestr, sizeof(timestr), "%T", localtime(&t)); pr_info("Next scheduled event \"%s\" at %s, in %d seconds\n", db_name, timestr, sleeptime); } else pr_info("All jobs active, sleeping until event arrives\n"); poll_job_requests(sleeptime); } return 0; }