From a37037ded4b190578510f210709abce8e8fd8fa4 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 10 Jan 2022 21:21:35 +0100 Subject: [PATCH] Fix #188: support without cgroups v2 support This patch adds support for detecting if the Linux kernel Fint runs on has support for cgroups v2. The probe is done very early, in runlevel S, when attempting to mount /sys/fs/cgroup. Meaning, the log message may be lost on systems that do not run sysklogd[1]. Here's the relevant logs from a system running myLinux on a kernel that has cgroups v2 support disabled: Jan 10 20:19:40 anarchy finit[1]: myLinux 2021.11-696-g8fddaf36ed, entering runlevel S Jan 10 20:19:40 anarchy finit[1]: Kernel does not support cgroups v2, disabling. Jan 10 20:19:41 anarchy finit[1]: myLinux 2021.11-696-g8fddaf36ed, entering runlevel 2 All code paths that attempt to set or query kernel cgroups config has been short circuited when this happens. However, this does not apply to any cgroup related config in the Finit .conf files -- which may of course be omitted. Yet, if configuration directives exist, they will be parsed and may cause error. Also, the initctl tool checks if /sys/fs/cgroup is mounted at runtime and disables all status output and commands that rely on the feature. Signed-off-by: Joachim Wiberg --- src/cgroup.c | 31 ++++++++++++- src/cgutil.c | 4 ++ src/cgutil.h | 2 + src/initctl.c | 124 +++++++++++++++++++++++++++++--------------------- 4 files changed, 108 insertions(+), 53 deletions(-) diff --git a/src/cgroup.c b/src/cgroup.c index a15bf93c..1c0cb20c 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -56,6 +56,7 @@ static char controllers[256]; static struct iwatch iw_cgroup; static uev_t cgw; +static int avail; static void cgset(const char *path, char *ctrl, char *prop) @@ -164,6 +165,9 @@ static int cgroup_leaf_init(char *group, char *name, int pid, const char *cfg) int cgroup_user(char *name, int pid) { + if (!avail) + return 0; + return cgroup_leaf_init("user", name, pid, NULL); } @@ -171,6 +175,9 @@ int cgroup_service(char *name, int pid, struct cgroup *cg) { char *group = "system"; + if (!avail) + return 0; + if (cg && cg->name[0]) { char path[256]; @@ -307,6 +314,9 @@ void cgroup_mark_all(void) { struct cg *cg; + if (!avail) + return; + TAILQ_FOREACH(cg, &cgroups, link) { if (cg->is_protected) continue; @@ -323,6 +333,9 @@ void cgroup_cleanup(void) struct cg *cg, *tmp; char path[256]; + if (!avail) + return; + TAILQ_FOREACH_SAFE(cg, &cgroups, link, tmp) { if (cg->active) continue; @@ -339,6 +352,9 @@ int cgroup_add(char *name, char *cfg, int is_protected) { struct cg *cg; + if (!avail) + return 0; + if (!name) return -1; if (!cfg) @@ -383,6 +399,9 @@ int cgroup_del(char *dir) struct cg *cg; char path[256]; + if (!avail) + return 0; + TAILQ_FOREACH(cg, &cgroups, link) { snprintf(path, sizeof(path), FINIT_CGPATH "/%s", cg->name); if (strcmp(path, dir)) @@ -414,6 +433,9 @@ void cgroup_config(void) { struct cg *cg; + if (!avail) + return; + TAILQ_FOREACH(cg, &cgroups, link) { char path[256]; int leaf = 0; @@ -442,14 +464,21 @@ void cgroup_init(uev_ctx_t *ctx) int fd; if (mount("none", FINIT_CGPATH, "cgroup2", opts, NULL)) { - _pe("Failed mounting cgroup v2"); + if (errno == ENOENT) + logit(LOG_NOTICE, "Kernel does not support cgroups v2, disabling."); + else + _pe("Failed mounting cgroup v2, disabling."); + avail = 0; return; } + avail = 1; /* Find available controllers */ fp = fopen(FINIT_CGPATH "/cgroup.controllers", "r"); if (!fp) { _pe("Failed opening %s", FINIT_CGPATH "/cgroup.controllers"); + umount(FINIT_CGPATH); + avail = 0; return; } diff --git a/src/cgutil.c b/src/cgutil.c index f294ec8a..45845f5f 100644 --- a/src/cgutil.c +++ b/src/cgutil.c @@ -53,6 +53,10 @@ uint64_t total_ram; /* From sysinfo() */ struct cg dummy; /* empty result "NULL" */ struct cg *list; +int cgroup_avail(void) +{ + return fismnt(FINIT_CGPATH); +} char *pid_cmdline(int pid, char *buf, size_t len) { diff --git a/src/cgutil.h b/src/cgutil.h index 032e948d..083aacb9 100644 --- a/src/cgutil.h +++ b/src/cgutil.h @@ -52,6 +52,8 @@ struct cg { } cg_cpu; }; +int cgroup_avail(void); + char *pid_cmdline (int pid, char *buf, size_t len); char *pid_comm (int pid, char *buf, size_t len); char *pid_cgroup (int pid, char *buf, size_t len); diff --git a/src/initctl.c b/src/initctl.c index e1605330..3e5a72ce 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -51,6 +51,7 @@ struct cmd { char *cmd; struct cmd *ctx; int (*cb)(char *arg); + int *cond; }; int icreate = 0; @@ -62,6 +63,8 @@ int verbose = 0; int plain = 0; int quiet = 0; int runlevel = 0; +int cgrp = 0; + int iw, pw; extern int reboot_main(int argc, char *argv[]); @@ -711,11 +714,7 @@ static int show_status(char *arg) while (arg && arg[0]) { long now = jiffies(); char uptm[42] = "N/A"; - char grbuf[128]; - char path[256]; - struct cg *cg; int exact = 0; - char *group; for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) { svc_ident(svc, ident, sizeof(ident)); @@ -735,10 +734,6 @@ static int show_status(char *arg) if (quiet) return svc->state != SVC_RUNNING_STATE; - group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf)); - snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group); - cg = cg_conf(path); - printf(" Status : %s\n", status(svc, 1)); printf(" Identity : %s\n", svc_ident(svc, ident, sizeof(ident))); printf("Description : %s\n", svc->desc); @@ -752,7 +747,16 @@ static int show_status(char *arg) printf(" Group : %s\n", svc->group); printf(" Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm); printf(" Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels)); - if (svc->pid > 1) { + if (cgrp && svc->pid > 1) { + char grbuf[128]; + char path[256]; + struct cg *cg; + char *group; + + group = pid_cgroup(svc->pid, grbuf, sizeof(grbuf)); + snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, group); + cg = cg_conf(path); + printf(" Memory : %s\n", memsz(cgroup_memory(group), uptm, sizeof(uptm))); printf(" CGroup : %s cpu %s [%s, %s] mem [%s, %s]\n", group, cg->cg_cpu.set, cg->cg_cpu.weight, cg->cg_cpu.max, @@ -925,11 +929,14 @@ static int usage(int rc) " restart [:ID] Restart (stop/start) service by name\n" " ident [NAME] Show matching identities for NAME, or all\n" " status [:ID] Show service status, by name\n" - " status Show status of services, default command\n" - "\n" - " cgroup List cgroup config overview\n" - " ps List processes based on cgroups\n" - " top Show top-like listing based on cgroups\n" + " status Show status of services, default command\n"); + if (cgrp) + fprintf(stderr, + "\n" + " cgroup List cgroup config overview\n" + " ps List processes based on cgroups\n" + " top Show top-like listing based on cgroups\n"); + fprintf(stderr, "\n" " runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n" " reboot Reboot system\n" @@ -962,11 +969,22 @@ static int do_help(char *arg) return usage(0); } +static int cmd_cond(struct cmd *cmd) +{ + if (!cmd || !cmd->cond) + return 1; + + return *cmd->cond; +} + static int cmd_parse(int argc, char *argv[], struct cmd *command) { int i, j; for (i = 0; argc > 0 && command[i].cmd; i++) { + if (!cmd_cond(&command[i])) + continue; + if (!string_match(command[i].cmd, argv[0])) continue; @@ -991,7 +1009,6 @@ static int cmd_parse(int argc, char *argv[], struct cmd *command) return command[0].cb(NULL); /* default cmd */ } - int main(int argc, char *argv[]) { struct option long_options[] = { @@ -1008,58 +1025,61 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0 } }; struct cmd cond[] = { - { "status", NULL, do_cond_show }, /* default cmd */ - { "dump", NULL, do_cond_dump }, - { "set", NULL, do_cond_set }, - { "clr", NULL, do_cond_clr }, - { "clear", NULL, do_cond_clr }, - { NULL, NULL, NULL } + { "status", NULL, do_cond_show, NULL }, /* default cmd */ + { "dump", NULL, do_cond_dump, NULL }, + { "set", NULL, do_cond_set, NULL }, + { "clr", NULL, do_cond_clr, NULL }, + { "clear", NULL, do_cond_clr, NULL }, + { NULL, NULL, NULL, NULL } }; struct cmd command[] = { - { "status", NULL, show_status }, /* default cmd */ - { "ident", NULL, show_ident }, + { "status", NULL, show_status, NULL }, /* default cmd */ + { "ident", NULL, show_ident, NULL }, - { "debug", NULL, toggle_debug }, - { "devel", NULL, do_devel }, - { "help", NULL, do_help }, - { "version", NULL, show_version }, + { "debug", NULL, toggle_debug, NULL }, + { "devel", NULL, do_devel, NULL }, + { "help", NULL, do_help, NULL }, + { "version", NULL, show_version, NULL }, - { "list", NULL, serv_list }, - { "ls", NULL, serv_list }, - { "enable", NULL, serv_enable }, - { "disable", NULL, serv_disable }, - { "touch", NULL, serv_touch }, - { "show", NULL, serv_show }, - { "edit", NULL, serv_edit }, - { "create", NULL, serv_creat }, - { "delete", NULL, serv_delete }, - { "reload", NULL, do_reload }, + { "list", NULL, serv_list, NULL }, + { "ls", NULL, serv_list, NULL }, + { "enable", NULL, serv_enable, NULL }, + { "disable", NULL, serv_disable, NULL }, + { "touch", NULL, serv_touch, NULL }, + { "show", NULL, serv_show, NULL }, + { "edit", NULL, serv_edit, NULL }, + { "create", NULL, serv_creat, NULL }, + { "delete", NULL, serv_delete, NULL }, + { "reload", NULL, do_reload, NULL }, - { "cond", cond, NULL }, + { "cond", cond, NULL, NULL }, - { "log", NULL, do_log }, - { "start", NULL, do_start }, - { "stop", NULL, do_stop }, - { "restart", NULL, do_restart }, + { "log", NULL, do_log, NULL }, + { "start", NULL, do_start, NULL }, + { "stop", NULL, do_stop, NULL }, + { "restart", NULL, do_restart, NULL }, - { "cgroup", NULL, show_cgroup }, - { "ps", NULL, show_cgps }, - { "top", NULL, show_cgtop }, + { "cgroup", NULL, show_cgroup, &cgrp }, + { "ps", NULL, show_cgps, &cgrp }, + { "top", NULL, show_cgtop, &cgrp }, - { "runlevel", NULL, do_runlevel }, - { "reboot", NULL, do_reboot }, - { "halt", NULL, do_halt }, - { "poweroff", NULL, do_poweroff }, - { "suspend", NULL, do_suspend }, + { "runlevel", NULL, do_runlevel, NULL }, + { "reboot", NULL, do_reboot, NULL }, + { "halt", NULL, do_halt, NULL }, + { "poweroff", NULL, do_poweroff, NULL }, + { "suspend", NULL, do_suspend, NULL }, - { "utmp", NULL, do_utmp }, - { NULL, NULL, NULL } + { "utmp", NULL, do_utmp, NULL }, + { NULL, NULL, NULL, NULL } }; int interactive = 1, c; if (transform(progname(argv[0]))) return reboot_main(argc, argv); + /* Enable some functionality if kernel supports cgroups v2 */ + cgrp = cgroup_avail(); + while ((c = getopt_long(argc, argv, "1bcdfh?pqtv", long_options, NULL)) != EOF) { switch(c) { case '1':