diff options
author | Mattias Andrée <maandree@kth.se> | 2024-02-24 19:58:22 +0100 |
---|---|---|
committer | Mattias Andrée <maandree@kth.se> | 2024-02-24 19:58:22 +0100 |
commit | 39ca33807b936a46bbd491ab5ecff817a6a28144 (patch) | |
tree | dec4c1bfc788fa6efa242fd6398e9c4f080b6999 /coreupdown.c | |
parent | First commit (diff) | |
download | coreupdown-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.c | 313 |
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; } |