diff options
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 19 | ||||
| -rw-r--r-- | coreupdown.c | 610 | ||||
| -rw-r--r-- | coreupdownd.1 | 83 | ||||
| -rw-r--r-- | coreupdownd.conf.5 | 57 | 
5 files changed, 724 insertions, 46 deletions
| @@ -16,3 +16,4 @@  /coreup  /coredown  /coreupdownd +*.conf @@ -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"; +		argv[0] = *(char **)(void *)&"coreupdownd"; -	if (argc && !strcmp(argv[0], "--")) { -		argv++; -		argc--; -	} +#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; -	/* TODO configurations should be configurable */ +	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; +	} + +	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) | 
