aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile19
-rw-r--r--coreupdown.c612
-rw-r--r--coreupdownd.183
-rw-r--r--coreupdownd.conf.557
5 files changed, 725 insertions, 47 deletions
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 <linux/close_range.h>
+#include <sys/file.h>
+#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -11,7 +14,9 @@
#include <time.h>
#include <unistd.h>
-static const char *argv0;
+#include <libsimple-arg.h>
+
+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;
}
@@ -236,17 +267,428 @@ sighup_handler(int signo)
}
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 <max> 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 <pipe>: %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 <pipe>: %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 <function>: %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)