From 1c222c0991ab816f0c8dca32407801ce2525e885 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sun, 25 Feb 2024 11:19:34 +0100 Subject: Add coreupdownd: and configurations and daemonisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 1 + Makefile | 19 +- coreupdown.c | 612 +++++++++++++++++++++++++++++++++++++++++++++++++---- coreupdownd.1 | 83 +++++++- coreupdownd.conf.5 | 57 +++++ 5 files changed, 725 insertions(+), 47 deletions(-) create mode 100644 coreupdownd.conf.5 diff --git a/.gitignore b/.gitignore index a7d0aa5..9978821 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /coreup /coredown /coreupdownd +*.conf diff --git a/Makefile b/Makefile index 63eedf7..89ddfb5 100644 --- a/Makefile +++ b/Makefile @@ -4,14 +4,14 @@ CONFIGFILE = config.mk include $(CONFIGFILE) CPPFLAGS_CONFS =\ - -D'COREUP_THRESHOLD_CPU=$(COREUP_THRESHOLD_CPU)'\ - -D'COREUP_THRESHOLD_TIME=$(COREUP_THRESHOLD_TIME)'\ - -D'COREUP_COOLDOWN=$(COREUP_COOLDOWN)'\ - -D'COREDOWN_THRESHOLD_CPU=$(COREDOWN_THRESHOLD_CPU)'\ - -D'COREDOWN_THRESHOLD_TIME=$(COREDOWN_THRESHOLD_TIME)'\ - -D'COREDOWN_COOLDOWN=$(COREDOWN_COOLDOWN)'\ - -D'DAEMON_INTERVAL_SEC=$(DAEMON_INTERVAL_SEC)'\ - -D'DAEMON_INTERVAL_NSEC=$(DAEMON_INTERVAL_NSEC)'\ + -D'COREUP_THRESHOLD_CPU=$(COREUP_THRESHOLD_CPU)U'\ + -D'COREUP_THRESHOLD_TIME=$(COREUP_THRESHOLD_TIME)U'\ + -D'COREUP_COOLDOWN=$(COREUP_COOLDOWN)U'\ + -D'COREDOWN_THRESHOLD_CPU=$(COREDOWN_THRESHOLD_CPU)U'\ + -D'COREDOWN_THRESHOLD_TIME=$(COREDOWN_THRESHOLD_TIME)U'\ + -D'COREDOWN_COOLDOWN=$(COREDOWN_COOLDOWN)U'\ + -D'DAEMON_INTERVAL_SEC=$(DAEMON_INTERVAL_SEC)U'\ + -D'DAEMON_INTERVAL_NSEC=$(DAEMON_INTERVAL_NSEC)UL'\ -D'DAEMON_PATH="$(PREFIX)/bin/coreupdownd"' OBJ =\ @@ -31,6 +31,7 @@ coreupdown: $(OBJ) install: coreupdown mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man5" test -f "$(DESTDIR)$(PREFIX)/bin/coreup" || test ! -e "$(DESTDIR)$(PREFIX)/bin/coreup" test -f "$(DESTDIR)$(PREFIX)/bin/coredown" || test ! -e "$(DESTDIR)$(PREFIX)/bin/coredown" test -f "$(DESTDIR)$(PREFIX)/bin/coreupdownd" || test ! -e "$(DESTDIR)$(PREFIX)/bin/coreupdownd" @@ -38,6 +39,7 @@ install: coreupdown ln -sf -- coreup "$(DESTDIR)$(PREFIX)/bin/coredown" ln -sf -- coreup "$(DESTDIR)$(PREFIX)/bin/coreupdownd" cp -- coreup.1 coredown.1 coreupdownd.1 "$(DESTDIR)$(MANPREFIX)/man1" + cp -- coreupdownd.conf.5 "$(DESTDIR)$(MANPREFIX)/man5" postinstall: chown '0:0' "$(DESTDIR)$(PREFIX)/bin/coreup" @@ -50,6 +52,7 @@ uninstall: -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/coreup.1" -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/coredown.1" -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/coreupdownd.1" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man5/coreupdownd.conf.5" clean: -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda diff --git a/coreupdown.c b/coreupdown.c index ab3d4e9..500839f 100644 --- a/coreupdown.c +++ b/coreupdown.c @@ -1,4 +1,7 @@ /* See LICENSE file for copyright and license details. */ +#include +#include +#include #include #include #include @@ -11,7 +14,9 @@ #include #include -static const char *argv0; +#include + +char *argv0; static volatile sig_atomic_t caught_sighup = 0; #define coreup() coreupdown(ULONG_MAX) @@ -22,7 +27,7 @@ coreupdown(unsigned long int count) { char path[sizeof("/sys/devices/system/cpu/cpu/online") + 3 * sizeof(count)]; unsigned long int i; - int fd, ret = 0; + int fd, have_success = 0, have_failure = 0; for (i = 1; i; i++) { sprintf(path, "/sys/devices/system/cpu/cpu%zu/online", i); @@ -33,13 +38,39 @@ coreupdown(unsigned long int count) fprintf(stderr, "%s: open %s O_WRONLY\n", argv0, path); return -1; } + write_again: if (write(fd, i < count ? "1\n" : "0\n", 2) < 0) { + if (errno == EINTR) + goto write_again; fprintf(stderr, "%s: write %s\n", argv0, path); - ret = -1; + have_failure = 1; + } else { + have_success = 1; } close(fd); } - return ret; + return -(have_failure && !have_success); +} + +static void +oneshot_usage(const char *command) +{ + fprintf(stderr, "usage: %s [count]\n", command); +} + +static void +daemon_usage(const char *command) +{ + fprintf(stderr, "usage: %s " + "[-C coreup-cooldown-time] " + "[-c coredown-cooldown-time] " + "[-i check-interval] " + "[-s configuration-file] " + "[-T coreup-cpu-usage-time-consistency-threshold] " + "[-t coredown-cpu-usage-time-consistency-threshold] " + "[-U coreup-cpu-usage-threshold] " + "[-u coredown-cpu-usage-threshold] " + "[-ef]\n", command); } static int @@ -52,7 +83,7 @@ oneshot_main(int argc, char **argv, unsigned long int count) if (argc > 1) { usage: - fprintf(stderr, "usage: %s [count]\n", argv0); + oneshot_usage(argv0); return 1; } @@ -235,18 +266,429 @@ sighup_handler(int signo) caught_sighup = 1; } +static int +init_daemon(char **cwd_out) +{ + int sig; + sigset_t sigmask; + char *cwd = NULL; + size_t cwd_size = 0; + + for (;;) { + cwd = realloc(cwd, cwd_size += 512); + if (cwd) { + if (getcwd(cwd, cwd_size)) + break; + if (errno == ERANGE) + continue; + } + fprintf(stderr, "%s: getcwd: %s\n", argv0, strerror(errno)); + return -1; + } + *cwd_out = cwd; + + umask(0); + if (chdir("/")) + fprintf(stderr, "%s: chdir /: %s\n", argv0, strerror(errno)); + if (sigemptyset(&sigmask)) + fprintf(stderr, "%s: sigemptyset: %s\n", argv0, strerror(errno)); + else if (sigprocmask(SIG_SETMASK, &sigmask, NULL)) + fprintf(stderr, "%s: sigprocmask SIG_SETMASK {}: %s\n", argv0, strerror(errno)); + for (sig = 1; sig < _NSIG; sig++) + if (signal(sig, SIG_DFL) == SIG_ERR && errno != EINVAL) + fprintf(stderr, "%s: signal %i SIG_DFL: %s\n", argv0, sig, strerror(errno)); + if (close_range(3, UINT_MAX, 0)) + fprintf(stderr, "%s: close_range 3 0: %s\n", argv0, strerror(errno)); + + return 0; +} + +static void +writefile(const char *path, const char *data, size_t len) +{ + int fd; + size_t off; + ssize_t r; + + fd = open(path, O_CREAT | O_WRONLY, 0644); + if (fd < 0) { + fprintf(stderr, "%s: open %s O_CREAT|O_WRONLY, 0644: %s\n", argv0, path, strerror(errno)); + exit(1); + } + + while (flock(fd, LOCK_EX)) { + if (errno != EINTR) { + fprintf(stderr, "%s: flock %s LOCK_EX: %s\n", argv0, path, strerror(errno)); + exit(1); + } + } + + if (ftruncate(fd, 0)) { + fprintf(stderr, "%s: ftruncate %s 0: %s\n", argv0, path, strerror(errno)); + exit(1); + } + + for (off = 0; off < len; off++) { + r = write(fd, &data[off], len - off); + if (r < 0) { + if (errno == EINTR) + continue; + write_error: + fprintf(stderr, "%s: write %s: %s\n", argv0, path, strerror(errno)); + exit(1); + } + off += (size_t)r; + } + + if (close(fd)) + goto write_error; +} + +static void +daemonise(int keep_stderr) +{ + pid_t pid; + char buf[3 * sizeof(uintmax_t) + 2]; + int len, pipe_rw[2], fd; + ssize_t r; + + if (pipe(pipe_rw)) { + fprintf(stderr, "%s: pipe: %s\n", argv0, strerror(errno)); + exit(1); + } + + pid = fork(); + switch (pid) { + case -1: + fork_failure: + fprintf(stderr, "%s: fork: %s\n", argv0, strerror(errno)); + _exit(1); + + case 0: + close(pipe_rw[0]); + if (pipe_rw[0] <= STDERR_FILENO) { + fd = fcntl(pipe_rw[0], F_DUPFD, STDERR_FILENO + 1); + if (fd <= STDERR_FILENO) { + fprintf(stderr, "%s: fcntl DUPFD: %s\n", argv0, strerror(errno)); + exit(1); + } + close(pipe_rw[0]); + pipe_rw[0] = fd; + } + + if (setsid() == -1) { + fprintf(stderr, "%s: setsid: %s\n", argv0, strerror(errno)); + exit(1); + } + + pid = fork(); + switch (pid) { + case -1: + goto fork_failure; + case 0: + pid = getpid(); + len = snprintf(buf, sizeof(buf), "%ju\n", (uintmax_t)pid); + if (len < 2) + abort(); + writefile("/run/coreupdown.pid", buf, (size_t)len); + + if (write(pipe_rw[1], &(char){0}, 1) <= 0 || close(pipe_rw[1])) { + fprintf(stderr, "%s: write : %s\n", argv0, strerror(errno)); + exit(1); + } + + if (!keep_stderr) { + close(STDERR_FILENO); + fd = open("/dev/null", O_WRONLY); + if (fd >= 0 && fd != STDERR_FILENO) { + dup2(fd, STDERR_FILENO); + close(fd); + } + } + break; + default: + _exit(1); + } + break; + + default: + close(pipe_rw[1]); + r = read(pipe_rw[0], buf, 1); + if (!r) + _exit(1); + if (r < 0) { + fprintf(stderr, "%s: read : %s\n", argv0, strerror(errno)); + _exit(1); + } + _exit(0); + } +} + +static int +parse_nanotime(const char *s, unsigned int *sec, unsigned long int *nsec) +{ + unsigned long int tmp; + int digits; + char *end; + + if (!isdigit(*s)) + goto invalid; + tmp = strtoul(s, &end, 10); + s = end; + if (errno) + goto invalid; +#if UINT_MAX < ULONG_MAX + if (tmp > UINT_MAX) + goto invalid; +#endif + + *sec = (unsigned int)tmp; + *nsec = 0; + + if (!*s) + goto done; + if (*s != '.') + goto invalid; + s++; + + for (digits = 0; digits < 9 && *s; digits++, s++) { + if (!isdigit(*s)) + goto invalid; + *nsec *= 10; + *nsec += (*s & 15); + } + for (; digits < 9; digits++) + *nsec *= 10; + if (!*s) + goto done; + if (!isdigit(*s)) + goto invalid; + + if (*s >= '5') { + if (*nsec < 999999999UL) { + *nsec += 1; + } else if (*sec < UINT_MAX) { + *sec += 1; + *nsec = 0; + } + } + for (; *s; s++) + if (!isdigit(*s)) + goto invalid; + +done: + return 0; +invalid: + return -1; +} + +#if defined(__GNUC__) +__attribute__((pure)) +#endif +static char * +rstrip(char *s) +{ + char *ret = s; + while (*s) { + while (*s && !isspace(*s) && *s != '#') + s++; + ret = s; + while (isspace(*s)) + s++; + if (*s == '#') + break; + } + return ret; +} + +static void +load_configurations(const char *file, int optional, + unsigned int *coreup_threshold_cpu, unsigned int *coredown_threshold_cpu, + unsigned int *coreup_threshold_time, unsigned int *coredown_threshold_time, + unsigned int *coreup_cooldown, unsigned int *coredown_cooldown, + unsigned int *daemon_interval_sec, unsigned long int *daemon_interval_nsec) +{ + unsigned int *uintp; + unsigned long int tmp; + int fd; + FILE *f; + ssize_t len; + char *line = NULL, *s, *p, pc; + size_t size = 0, n; + + fd = open(file, O_RDONLY); + if (fd < 0) { + if (optional && errno == ENOENT) + return; + fprintf(stderr, "%s: open %s O_RDONLY: %s\n", argv0, file, strerror(errno)); + exit(1); + } + + f = fdopen(fd, "r"); + if (!f) { + fprintf(stderr, "%s: fdopen %s r: %s\n", argv0, file, strerror(errno)); + exit(1); + } + +#define OPTION(OPT) (!strncmp(s, OPT, n = sizeof(OPT) - 1) && (isspace(s[n]) || s[n] == '=')) + + while ((len = getline(&line, &size, f)) != -1) { + if (len && line[len - 1] == '\n') + line[--len] = 0; + s = line; + while (isspace(*s)) + s++; + if (!*s || *s == '#') + continue; + + if (OPTION("coreup-cpu-usage-threshold")) + uintp = coreup_threshold_cpu; + else if (OPTION("coredown-cpu-usage-threshold")) + uintp = coredown_threshold_cpu; + else if (OPTION("coreup-cpu-usage-time-consistency-threshold")) + uintp = coreup_threshold_time; + else if (OPTION("coredown-cpu-usage-time-consistency-threshold")) + uintp = coredown_threshold_time; + else if (OPTION("coreup-cooldown-time")) + uintp = coreup_cooldown; + else if (OPTION("coredown-cooldown-time")) + uintp = coredown_cooldown; + else + goto not_uint; + + s = &s[n]; + while (isspace(*s)) + s++; + if (*s != '=') + goto invalid; + s++; + while (isspace(*s)) + s++; + p = rstrip(s); + pc = *p; + *p = 0; + + if (!isdigit(*s)) + goto invalid_restore; + errno = 0; + tmp = strtoul(s, &s, 10); + if (*s || errno) + goto invalid_restore; +#if UINT_MAX < ULONG_MAX + if (tmp > UINT_MAX) + goto invalid_restore; +#endif + if (uintp) + *uintp = (unsigned int)tmp; + + continue; + not_uint: + + if (!OPTION("check-interval")) + goto invalid; + + s = &s[n]; + while (isspace(*s)) + s++; + if (*s != '=') + goto invalid; + s++; + while (isspace(*s)) + s++; + p = rstrip(s); + pc = *p; + *p = 0; + + if (daemon_interval_sec && daemon_interval_nsec) { + if (parse_nanotime(s, daemon_interval_sec, daemon_interval_nsec)) { + invalid_restore: + *p = pc; + goto invalid; + } + } + + continue; + + invalid: + fprintf(stderr, "%s: invalid line in %s: %s\n", argv0, file, line); + } + +#undef OPTION + + if (fclose(f)) { + fprintf(stderr, "%s: fclose %s: %s\n", argv0, file, strerror(errno)); + exit(1); + } + + free(line); +} + +static char * +pathjoin(const char *a, const char *b) +{ + const char *p; + char *r; + size_t n; + int need_slash; + p = strrchr(a, '/'); + if (!p) + abort(); + need_slash = p[1] ? 1 : 0; + n = strlen(a) + (size_t)need_slash + strlen(b) + 1; + r = malloc(n); + if (!r) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + return NULL; + } + stpcpy(stpcpy(stpcpy(r, a), need_slash ? "/" : ""), b); + return r; +} + +static char * +memjoin(const char *a, size_t an, const char *b, size_t bn) +{ + size_t n = an + bn; + char *r = malloc(n); + if (!r) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + return NULL; + } + memcpy(r, a, an); + memcpy(&r[an], b, bn); + return r; +} + static int daemon_main(int argc, char **argv, int orig_argc, char **orig_argv, int reexeced) { uintmax_t total, idle; - size_t cpus; - int up_time = 0, down_time = 0, cooldown = 0, err; + size_t cpus, offset; + unsigned int up_time = 0, down_time = 0, cooldown = 0; + int err, foreground = 0, keep_stderr = 0, argnum, different; struct timespec interval, rem; - uintmax_t coreup_threshold_cpu, coredown_threshold_cpu; - int coreup_threshold_time, coredown_threshold_time; - int coreup_cooldown, coredown_cooldown; - time_t daemon_interval_sec; - long int daemon_interval_nsec; + uintmax_t coreup_threshold_cpu_ju, coredown_threshold_cpu_ju; + unsigned int coreup_threshold_cpu, coredown_threshold_cpu; + unsigned int coreup_threshold_time, coredown_threshold_time; + unsigned int coreup_cooldown, coredown_cooldown; + unsigned int daemon_interval_sec, *uintp; + unsigned long int daemon_interval_nsec, tmp; + int have_coreup_threshold_cpu = 0, have_coredown_threshold_cpu = 0; + int have_coreup_threshold_time = 0, have_coredown_threshold_time = 0; + int have_coreup_cooldown = 0, have_coredown_cooldown = 0; + int have_daemon_interval = 0; + char *arg, *cwd = NULL, *conffile = NULL; + char **proper_orig_argv; + + if (!reexeced && init_daemon(&cwd)) + return -1; + + proper_orig_argv = calloc((size_t)(orig_argc + 1), sizeof(*orig_argv)); + if (!proper_orig_argv) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + return 1; + } + memcpy(proper_orig_argv, orig_argv, (size_t)(orig_argc + 1) * sizeof(*orig_argv)); + orig_argv = proper_orig_argv; restart: coreup_threshold_cpu = COREUP_THRESHOLD_CPU; @@ -259,40 +701,137 @@ restart: daemon_interval_nsec = DAEMON_INTERVAL_NSEC; if (reexeced) - argv[0] = "coreupdownd"; - - if (argc && !strcmp(argv[0], "--")) { - argv++; - argc--; - } + argv[0] = *(char **)(void *)&"coreupdownd"; + +#define usage() goto usage + SUBARGBEGIN { + case 'U': uintp = &coreup_threshold_cpu; have_coreup_threshold_cpu = 1; goto uint; + case 'u': uintp = &coredown_threshold_cpu; have_coredown_threshold_cpu = 1; goto uint; + case 'T': uintp = &coreup_threshold_time; have_coreup_threshold_time = 1; goto uint; + case 't': uintp = &coredown_threshold_time; have_coredown_threshold_time = 1; goto uint; + case 'C': uintp = &coreup_cooldown; have_coreup_cooldown = 1; goto uint; + case 'c': uintp = &coredown_cooldown; have_coredown_cooldown = 1; goto uint; + + case 'i': + arg = ARGNULL(); + if (!arg) + goto usage; + if (parse_nanotime(arg, &daemon_interval_sec, &daemon_interval_nsec)) + goto invalid; + have_daemon_interval = 1; + break; + + case 'e': + keep_stderr = 1; + break; + + case 'f': + foreground = 1; + break; + + case 's': + conffile = ARGNULL(); + if (!conffile || !*conffile) + goto usage; + if (*conffile == '/') + break; + offset = (size_t)(conffile - LFLAG()); + different = (conffile < LFLAG() || offset > strlen(LFLAG())); + for (argnum = 0; argnum < orig_argc; argnum++) + if (LFLAG() == orig_argv[argnum]) + break; + if (argnum == orig_argc) + abort(); + conffile = pathjoin(cwd, conffile); + if (different) + orig_argv[argnum + 1] = conffile; + else + orig_argv[argnum] = memjoin(LFLAG(), offset, conffile, strlen(conffile) + 1); + break; + + default: + goto usage; + + uint: + arg = ARGNULL(); + if (!arg) + goto usage; + errno = 0; + if (!isdigit(*arg)) + goto invalid; + tmp = strtoul(arg, &arg, 10); + if (*arg || errno) { + invalid: + fprintf(stderr, "%s: invalid argument for option -%c\n", argv0, FLAG()); + return 1; + } +#if UINT_MAX < ULONG_MAX + if (tmp > UINT_MAX) + goto invalid; +#endif + *uintp = (unsigned int)tmp; + break; + } ARGEND; +#undef usage if (argc > 0) { - fprintf(stderr, "usage: %s\n", argv0); + usage: + daemon_usage(argv0); return 1; } - if (!reexeced) { - /* TODO add proper daemonisation */ + load_configurations(conffile ? conffile : "/etc/coreupdownd.conf", !conffile, + have_coreup_threshold_cpu ? NULL : &coreup_threshold_cpu, + have_coredown_threshold_cpu ? NULL : &coredown_threshold_cpu, + have_coreup_threshold_time ? NULL : &coreup_threshold_time, + have_coredown_threshold_time ? NULL : &coredown_threshold_time, + have_coreup_cooldown ? NULL : &coreup_cooldown, + have_coredown_cooldown ? NULL : &coredown_cooldown, + have_daemon_interval ? NULL : &daemon_interval_sec, + have_daemon_interval ? NULL : &daemon_interval_nsec); + + if (coreup_threshold_cpu > 100) { + fprintf(stderr, "%s: CPU usage threshold for coreup was above 100, setting to 100\n", argv0); + coreup_threshold_cpu = 100; + } else if (coreup_threshold_cpu == 0) { + fprintf(stderr, "%s: CPU usage threshold for coreup was 0, setting to 1\n", argv0); + coreup_threshold_cpu = 1; + } + if (coredown_threshold_cpu > 99) { + fprintf(stderr, "%s: CPU usage threshold for coreup was above 99, setting to 99\n", argv0); + coredown_threshold_cpu = 99; + } + if (!daemon_interval_sec && !daemon_interval_nsec) + daemon_interval_nsec = 1; + + interval.tv_sec = (time_t)daemon_interval_sec; + interval.tv_nsec = (long int)daemon_interval_nsec; + coreup_threshold_cpu_ju = (uintmax_t)coreup_threshold_cpu; + coredown_threshold_cpu_ju = (uintmax_t)coredown_threshold_cpu; + if (coreup_threshold_cpu_ju <= coredown_threshold_cpu_ju) { + fprintf(stderr, "%s: coreup cpu usage threshold must be above " + "coredown cpu usage threshold\n", argv0); + return 1; } - /* TODO configurations should be configurable */ + if (!reexeced && !foreground) + daemonise(keep_stderr); if (signal(SIGHUP, sighup_handler) == SIG_ERR) fprintf(stderr, "%s: signal SIGHUP : %s\n", argv0, strerror(errno)); - interval.tv_sec = daemon_interval_sec; - interval.tv_nsec = daemon_interval_nsec; - if (getusage(&total, &idle, &cpus)) return 1; - close(STDIN_FILENO); - close(STDOUT_FILENO); + if (!reexeced) { + close(STDIN_FILENO); + close(STDOUT_FILENO); + } for (;;) { if (caught_sighup) { caught_sighup = 0; - orig_argv[0] = "coreupdownd "; + orig_argv[0] = *(char **)(void *)&"coreupdownd "; execv(DAEMON_PATH, orig_argv); fprintf(stderr, "%s: execv %s: %s\n", argv0, DAEMON_PATH, strerror(errno)); reexeced = 1; @@ -317,18 +856,20 @@ restart: continue; } - if (cpus == 1 && 100 * (total - idle) > coreup_threshold_cpu * total) { + if (cpus == 1 && 100 * (total - idle) >= coreup_threshold_cpu_ju * total) { down_time = 0; if (up_time++ >= coreup_threshold_time) { cooldown = coreup_cooldown; - coreup(); + if (coreup()) + return 1; up_time = 0; } - } else if (cpus > 1 && 100 * (total - idle) < coredown_threshold_cpu * total * cpus) { + } else if (cpus > 1 && 100 * (total - idle) <= coredown_threshold_cpu_ju * total * cpus) { up_time = 0; if (down_time++ >= coredown_threshold_time) { cooldown = coredown_cooldown; - coredown(); + if (coredown()) + return 1; down_time = 0; } } else { @@ -358,9 +899,8 @@ main(int argc, char **argv) return daemon_main(argc, argv, argc + 1, &argv[-1], 1); } - fprintf(stderr, "usage: coreup [count]\n" - "usage: coredown [count]\n" - "usage: coreupdownd\n"); - + oneshot_usage("coreup"); + oneshot_usage("coredown"); + daemon_usage("coreupdownd"); return 1; } diff --git a/coreupdownd.1 b/coreupdownd.1 index 9dde00b..23d7487 100644 --- a/coreupdownd.1 +++ b/coreupdownd.1 @@ -1,8 +1,17 @@ .TH COREUPDOWND 1 COREUPDOWN .SH MAME -coreupdownd - Dynamically enable and disable CPU' to only use one during low CPU usage +coreupdownd - Dynamically enable and disable CPU's to only use one during low CPU usage .SH SYNPOSIS coreupdownd +.RI "[-C " coreup-cooldown-time ] +.RI "[-c " coredown-cooldown-time ] +.RI "[-i " check-interval ] +.RI "[-s " configuration-file ] +.RI "[-T " coreup-cpu-usage-time-consistency-threshold ] +.RI "[-t " coredown-cpu-usage-time-consistency-threshold ] +.RI "[-U " coreup-cpu-usage-threshold ] +.RI "[-u " coredown-cpu-usage-threshold ] +[-ef] .SH DESCRIPTION The .B coreupdownd @@ -19,7 +28,67 @@ that have input performance issues (keyboard input lag and keys getting stuck) when more than one CPU core is online. .SH OPTIONS -No options are supported. +The +.B coreupdownd +daemon shall conform to the Base Definitions volume of +POSIX.1-2017, Section 12.2, Utility Syntax Guidelines. +.PP +The following options are supported: +.TP +.BI "-C " coreup-cooldown-time +The amount of time, measured in (integer) multiples of the +.IR check-interval , +before the daemon may disable CPU's after enabling CPU's. +During this time, time is not counted toward +.IR coredown-cpu-usage-threshold . +.TP +.BI "-c " coredown-cooldown-time +The amount of time, measured in (integer) multiples of the +.IR check-interval , +before the daemon may enable CPU's after disabling CPU's. +During this time, time is not counted toward +.IR coreup-cpu-usage-threshold . +.TP +.B -e +Do not close standard error at end of initialisation. +.TP +.B -f +Do not daemonise. (The standard input and standard +output will still be closed when the process has +completed it's initialisation.) + +Implies +.BR -e . +.TP +.BI "-i " check-interval +The interval, in seconds (need not be integer), with +which the daemon periodically checks the CPU usage. +.TP +.BI "-s " configuration-file +Configuration file to read instead of +.BR /etc/coreupdownd.conf . +.TP +.BI "-T " coreup-cpu-usage-time-consistency-threshold +The amount of time, measure in (integer) multiples of the +.I check-interval +the CPU usage must have stayed at or above +.I coreup-cpu-usage-threshold +before the daemon may enable all CPU's. +.TP +.BI "-t " coredown-cpu-usage-time-consistency-threshold +The amount of time, measured in (integer) multiples of the +.IR check-interval , +the CPU usage must have stayed at or below +.I coredown-cpu-usage-threshold +before the daemon may disable all CPU's except the main core . +.TP +.BI "-U " coreup-cpu-usage-threshold +The required CPU usage (integer lower bound) before the +daemon may enable all CPU's. +.TP +.BI "-u " coredown-cpu-usage-threshold +The required CPU usage (integer upper bound) before the +daemon may disable all CPU's except the main core. .SH OPERANDS No operands are supported. .SH STDIN @@ -34,7 +103,14 @@ None. No environment variables affects the execution of .BR coreupdownd . .SH ASYNCHRONOUS EVENTS -Default. +If the +.B coreupdownd +utility traps the following signals: +.TP +.B SIGHUP +Causes the daemon reload it's configurations, and +attempt to update to the newest installed version +of the daemon. .SH STDOUT The .B coreupdownd @@ -70,5 +146,6 @@ None. .SH FUTURE DIRECTIONS None. .SH SEE ALSO +.BR coreupdownd.conf (5), .BR coreup (1), .BR coredown (1) diff --git a/coreupdownd.conf.5 b/coreupdownd.conf.5 new file mode 100644 index 0000000..3e8759c --- /dev/null +++ b/coreupdownd.conf.5 @@ -0,0 +1,57 @@ +.TH COREUPDOWND 5 COREUPDOWN +.SH MAME +coreupdownd.conf - Configurations for coreupdownd(1) +.SH SYNPOSIS +/etc/coreupdownd.conf +.SH DESCRIPTION +The +.B coreupdownd.conf +configuration files is a plain line based +configuration file for the +.BR coreupdownd (1) +daemon. +.PP +Any hash +.RB ( # ) +character starts comment that end of the +end of the cline. +Comments and otherwise blank lines are ignored. +Apart from this, each line must be a configuration +name, followed by an equals sign +.RB ( = ), +followed by the value for the configuration. +Spaces may be added around the equals sign and +around the line. +.PP +The following configurations are recognised, +refer to the OPTIONS section in +.BR coreupdownd (1) +for interpretation of them: +.PP +.TP +* +coreup-cpu-usage-threshold +.TP +* +coredown-cpu-usage-threshold +.TP +* +coreup-cpu-usage-time-consistency-threshold +.TP +* +coredown-cpu-usage-time-consistency-threshold +.TP +* +coreup-cooldown-time +.TP +* +coredown-cooldown-time +.TP +* +check-interval +.SH NOTES +None. +.SH SEE ALSO +.BR coreupdownd (1), +.BR coreup (1), +.BR coredown (1) -- cgit v1.2.3-70-g09d2