aboutsummaryrefslogtreecommitdiffstats
path: root/coreupdown.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2024-02-24 19:58:22 +0100
committerMattias Andrée <maandree@kth.se>2024-02-24 19:58:22 +0100
commit39ca33807b936a46bbd491ab5ecff817a6a28144 (patch)
treedec4c1bfc788fa6efa242fd6398e9c4f080b6999 /coreupdown.c
parentFirst commit (diff)
downloadcoreupdown-39ca33807b936a46bbd491ab5ecff817a6a28144.tar.gz
coreupdown-39ca33807b936a46bbd491ab5ecff817a6a28144.tar.bz2
coreupdown-39ca33807b936a46bbd491ab5ecff817a6a28144.tar.xz
Add coreupdownd
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'coreupdown.c')
-rw-r--r--coreupdown.c313
1 files changed, 280 insertions, 33 deletions
diff --git a/coreupdown.c b/coreupdown.c
index 1de5007..403bdf6 100644
--- a/coreupdown.c
+++ b/coreupdown.c
@@ -3,37 +3,46 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
-int
-main(int argc, char *argv[])
+static const char *argv0;
+
+#define coreup() coreupdown(ULONG_MAX)
+#define coredown() coreupdown(1)
+
+static int
+coreupdown(unsigned long int count)
{
- unsigned long i, count;
- const char *command, *argv0;
- char path[sizeof("/sys/devices/system/cpu/cpu/online") + 3 * sizeof(i)];
+ char path[sizeof("/sys/devices/system/cpu/cpu/online") + 3 * sizeof(count)];
+ unsigned long int i;
int fd, ret = 0;
- if (argc == 0) {
- wrong_command:
- fprintf(stderr, "usage: coreup [count]\nusage: coredown [count]\n");
- return 1;
+ for (i = 1; i; i++) {
+ sprintf(path, "/sys/devices/system/cpu/cpu%zu/online", i);
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ break;
+ fprintf(stderr, "%s: open %s O_WRONLY\n", argv0, path);
+ return -1;
+ }
+ if (write(fd, i < count ? "1\n" : "0\n", 2) < 0) {
+ fprintf(stderr, "%s: write %s\n", argv0, path);
+ ret = -1;
+ }
+ close(fd);
}
+ return ret;
+}
- argv0 = *argv++;
- argc--;
- command = strrchr(argv0, '/');
- command = command ? &command[1] : argv0;
-
- if (!strcmp(command, "coreup"))
- count = ULONG_MAX;
- else if (!strcmp(command, "coredown"))
- count = 1;
- else
- goto wrong_command;
-
+static int
+oneshot_main(int argc, char **argv, unsigned long int count)
+{
if (argc && !strcmp(argv[0], "--")) {
argv++;
argc--;
@@ -45,7 +54,7 @@ main(int argc, char *argv[])
return 1;
}
- if (argc >= 1) {
+ if (argc) {
char *end;
errno = 0;
if (!isdigit(**argv))
@@ -57,21 +66,259 @@ main(int argc, char *argv[])
argc--;
}
- for (i = 1; i; i++) {
- sprintf(path, "/sys/devices/system/cpu/cpu%zu/online", i);
- fd = open(path, O_WRONLY);
- if (fd < 0) {
- if (errno == ENOENT)
+ return -coreupdown(count);
+}
+
+static int
+getusage(uintmax_t *total_out, uintmax_t *idle_out, size_t *cpus_out)
+{
+#define IDLE_FIELD 3
+
+ static uintmax_t last_total = 0, last_idle = 0;
+ uintmax_t cur_total, cur_idle;
+ char buf[512];
+ int fd;
+ size_t off, len, field;
+ ssize_t r;
+ int line_incomplete = 0, beginning_of_line = 1;
+ uintmax_t amount[] = {0, 0}, value;
+
+ *cpus_out = 0;
+
+ fd = open("/proc/stat", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: open /proc/stat O_RDONLY\n", argv0);
+ return -1;
+ }
+
+ len = 0;
+ for (;;) {
+ r = read(fd, &buf[len], sizeof(buf) - len);
+ if (!r) {
+ fprintf(stderr, "%s: /proc/stat did not contain \"cpu\" line\n", argv0);
+ close(fd);
+ return -1;
+ } else if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s: read /proc/stat: %s\n", argv0, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ len += (size_t)r;
+
+ if (line_incomplete)
+ goto skip_line;
+
+ for (off = 0; off < len;) {
+ if (len - off < 4) {
+ memmove(&buf[0], &buf[off], len -= off);
break;
- fprintf(stderr, "%s: open %s O_WRONLY\n", argv0, path);
- return 1;
+ } else if (!strncmp(&buf[off], "cpu", 3)) {
+ if (isspace(buf[off + 3]))
+ goto cpu_line_found;
+ else if (isdigit(buf[off + 3]))
+ *cpus_out += 1;
+ }
+
+ skip_line:
+ while (off < len && buf[off] != '\n')
+ off++;
+ if (off == len) {
+ line_incomplete = 0;
+ break;
+ } else {
+ off++;
+ line_incomplete = 1;
+ }
}
- if (write(fd, i < count ? "1\n" : "0\n", 2) < 0) {
- fprintf(stderr, "%s: write %s\n", argv0, path);
- ret = 1;
+ }
+cpu_line_found:
+
+ off += 4;
+ field = 0;
+ value = 0;
+ for (;;) {
+ if (off == len) {
+ off = len = 0;
+ r = read(fd, buf, sizeof(buf));
+ if (!r) {
+ goto line_done;
+ } else if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s: read /proc/stat: %s\n", argv0, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ len = (size_t)r;
}
+ while (off < len && isspace(buf[off]))
+ off++;
+ if (off == len)
+ continue;
+ while (off < len) {
+ if (buf[off] == '\n') {
+ amount[field++ == IDLE_FIELD] += value;
+ off++;
+ goto line_done;
+ } else if (isspace(buf[off])) {
+ amount[field++ == IDLE_FIELD] += value;
+ value = 0;
+ off++;
+ break;
+ } else {
+ value *= 10;
+ value += buf[off] & 15;
+ off++;
+ }
+ }
+ }
+line_done:
+
+ for (;;) {
+ if (off == len) {
+ off = len = 0;
+ read_more:
+ r = read(fd, &buf[len], sizeof(buf) - len);
+ if (!r) {
+ break;
+ } else if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s: read /proc/stat: %s\n", argv0, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ len = (size_t)r;
+ }
+ for (; off < len; off++) {
+ if (beginning_of_line) {
+ if (len - off < 5) {
+ memmove(&buf[0], &buf[off], len -= off);
+ goto read_more;
+ }
+ if (!strncmp(&buf[off], "cpu", 3) && isdigit(buf[off + 3]))
+ *cpus_out += 1;
+ beginning_of_line = 0;
+ }
+ if (buf[off] == '\n')
+ beginning_of_line = 1;
+ }
+ }
+
+ close(fd);
+
+ if (field < IDLE_FIELD) {
+ fprintf(stderr, "%s: /proc/stat did not contain CPU idle time\n", argv0);
close(fd);
+ return -1;
}
- return ret;
+ cur_total = amount[0] + amount[1];
+ cur_idle = amount[1];
+ *total_out = cur_total - last_total;
+ *idle_out = cur_idle - last_idle;
+ last_total = cur_total;
+ last_idle = cur_idle;
+ return 0;
+
+#undef IDLE_FIELD
+}
+
+static int
+daemon_main(int argc, char **argv)
+{
+ uintmax_t total, idle;
+ size_t cpus;
+ int up_time = 0, down_time = 0, cooldown = 0;
+ struct timespec interval, rem;
+
+ if (argc && !strcmp(argv[0], "--")) {
+ argv++;
+ argc--;
+ }
+
+ if (argc > 0) {
+ fprintf(stderr, "usage: %s\n", argv0);
+ return 1;
+ }
+
+ /* TODO configurations should be configurable */
+
+ /* TODO add SIGHUP support */
+
+ /* TODO add proper daemonisation */
+
+ 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);
+
+ for (;;) {
+ if (clock_nanosleep(CLOCK_REALTIME /* = monotonic */, 0, &interval, &rem)) {
+ do {
+ if (errno != EINTR) {
+ fprintf(stderr, "%s: clock_nanosleep CLOCK_REALTIME: %s\n",
+ argv0, strerror(errno));
+ return 1;
+ }
+ } while (clock_nanosleep(CLOCK_REALTIME, 0, &rem, &rem));
+ }
+ sleep(1);
+ if (getusage(&total, &idle, &cpus))
+ return 1;
+ if (cooldown) {
+ cooldown--;
+ continue;
+ }
+
+ if (cpus == 1 && 100 * (total - idle) > COREUP_THRESHOLD_CPU * total) {
+ down_time = 0;
+ if (up_time++ >= COREUP_THRESHOLD_TIME) {
+ cooldown = COREUP_COOLDOWN;
+ coreup();
+ up_time = 0;
+ }
+ } else if (cpus > 1 && 100 * (total - idle) < COREDOWN_THRESHOLD_CPU * total * cpus) {
+ up_time = 0;
+ if (down_time++ >= COREDOWN_THRESHOLD_TIME) {
+ cooldown = COREDOWN_COOLDOWN;
+ coredown();
+ down_time = 0;
+ }
+ } else {
+ up_time = 0;
+ down_time = 0;
+ }
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *command;
+
+ if (argc--) {
+ command = strrchr(argv0 = *argv++, '/');
+ command = command ? &command[1] : argv0;
+ if (!strcmp(command, "coreup"))
+ return oneshot_main(argc, argv, ULONG_MAX);
+ if (!strcmp(command, "coredown"))
+ return oneshot_main(argc, argv, 1);
+ if (!strcmp(command, "coreupdownd"))
+ return daemon_main(argc, argv);
+ }
+
+ fprintf(stderr, "usage: coreup [count]\n"
+ "usage: coredown [count]\n"
+ "usage: coreupdownd\n");
+
+ return 1;
}