From 6bbfc7b1f1d1d966809b463791bc19c26e46cfca Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 20 Nov 2022 10:44:30 +0100 Subject: [PATCH] getty: extend issue parser with /etc/os-release support Add support for /etC/os-release, with fallback to /usr/lib/os-release, overriding some traditional modifiers with the os-release variant. The traditional values for these modifiers are taken from uname, which on Linux systems are pretty useless since they always return the kernel name and version instead of the distro/OS values. E.g., \s becomes PRETTY_NAME instead of 'Linux' and \v becomes the pretty VERSION, while \r becomes VERSION_ID. Signed-off-by: Joachim Wiberg --- src/getty.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 138 insertions(+), 15 deletions(-) diff --git a/src/getty.c b/src/getty.c index b47b6026..7e833aa4 100644 --- a/src/getty.c +++ b/src/getty.c @@ -34,6 +34,7 @@ #include #include /* Not included by default in musl libc */ #include +#include #include /* sysconf() */ #include "finit.h" @@ -48,9 +49,82 @@ #define LOGIN_NAME_MIN 64 #endif +struct osrel { + char name[32]; + char pretty[64]; + char id[32]; + char version[32]; + char version_id[32]; + char home_url[128]; + char doc_url[128]; + char bug_url[128]; + char support_url[128]; +}; + static long logname_len = 32; /* useradd(1) limit at 32 chars */ static int passenv = 0; /* Set /bin/login -p or not */ +/* + * Parse os-release + */ +static int osrel(struct osrel *rel) +{ + char codename[30]; + char *line; + FILE *fp; + + fp = fopen("/etc/os-release", "r"); + if (!fp) { + fp = fopen("/usr/lib/os-release", "r"); + if (!fp) + return -1; + } + + while ((line = fparseln(fp, NULL, NULL, NULL, 0))) { + char *val; + + if ((val = fgetval(line, "NAME", "="))) + strlcpy(rel->name, val, sizeof(rel->name)); + if ((val = fgetval(line, "PRETTY_NAME", "="))) + strlcpy(rel->pretty, val, sizeof(rel->pretty)); + if ((val = fgetval(line, "ID", "="))) + strlcpy(rel->id, val, sizeof(rel->id)); + if ((val = fgetval(line, "VERSION", "="))) + strlcpy(rel->version, val, sizeof(rel->version)); + if ((val = fgetval(line, "VERSION_ID", "="))) + strlcpy(rel->version_id, val, sizeof(rel->version_id)); + if ((val = fgetval(line, "VERSION_CODENAME", "="))) + strlcpy(codename, val, sizeof(codename)); + if ((val = fgetval(line, "HOME_URL", "="))) + strlcpy(rel->home_url, val, sizeof(rel->home_url)); + if ((val = fgetval(line, "DOCUMENTATION_URL", "="))) + strlcpy(rel->doc_url, val, sizeof(rel->doc_url)); + if ((val = fgetval(line, "SUPPORT_URL", "="))) + strlcpy(rel->support_url, val, sizeof(rel->support_url)); + if ((val = fgetval(line, "BUG_REPORT_URL", "="))) + strlcpy(rel->bug_url, val, sizeof(rel->bug_url)); + + if (val) + free(val); + free(line); + } + fclose(fp); + + /* + * Many distros don't set VERSION or VERSION_ID for their + * rolling, or unstable, branches. However, most set the + * version code name, so we use that as VERSION. + */ + if (!rel->version[0] && codename[0]) { + if (codename[0] == '(') + strlcpy(rel->version, codename, sizeof(rel->version)); + else + snprintf(rel->version, sizeof(rel->version), "(%s)", codename); + } + + return 0; +} + /* * Read one character from stdin. */ @@ -68,10 +142,19 @@ static int readch(void) /* * Parse and display a line from /etc/issue + * + * Includes Finit-specific extensions to display information from + * /etc/os-release instead of relying on legacy uname. Activated + * only if /etc/os-release or /usr/lib/os-release exist. + * + * https://www.unix.com/man-page/Linux/8/getty/ + * https://www.systutorials.com/docs/linux/man/8-mingetty/ */ -static void do_parse(char *line, struct utsname *uts, char *tty) +static void parseln(char *line, struct utsname *uts, struct osrel *rel, char *tty, int compat) { + char buf[32] = { 0 }; char *s, *s0; + time_t now; s0 = line; for (s = line; *s != 0; s++) { @@ -80,12 +163,32 @@ static void do_parse(char *line, struct utsname *uts, char *tty) dprint(STDOUT_FILENO, s0, s - s0); s0 = s + 2; switch (*++s) { + case 'B': + if (!compat) + dprint(STDOUT_FILENO, rel->bug_url, 0); + break; + case 'D': + if (!compat) + dprint(STDOUT_FILENO, rel->doc_url, 0); + break; + case 'H': + if (!compat) + dprint(STDOUT_FILENO, rel->home_url, 0); + break; + case 'I': + if (!compat) + dprint(STDOUT_FILENO, rel->id, 0); + break; case 'l': dprint(STDOUT_FILENO, tty, 0); break; case 'm': dprint(STDOUT_FILENO, uts->machine, 0); break; + case 'N': + if (!compat) + dprint(STDOUT_FILENO, rel->name, 0); + break; case 'n': dprint(STDOUT_FILENO, uts->nodename, 0); break; @@ -95,13 +198,31 @@ static void do_parse(char *line, struct utsname *uts, char *tty) break; #endif case 'r': - dprint(STDOUT_FILENO, uts->release, 0); + if (compat) + dprint(STDOUT_FILENO, uts->release, 0); + else + dprint(STDOUT_FILENO, rel->version_id, 0); + break; + case 'S': + if (!compat) + dprint(STDOUT_FILENO, rel->support_url, 0); break; case 's': - dprint(STDOUT_FILENO, uts->sysname, 0); + if (compat) + dprint(STDOUT_FILENO, uts->sysname, 0); + else + dprint(STDOUT_FILENO, rel->pretty, 0); + break; + case 't': + now = time(NULL); + ctime_r(&now, buf); + dprint(STDOUT_FILENO, chomp(buf), 0); break; case 'v': - dprint(STDOUT_FILENO, uts->version, 0); + if (compat) + dprint(STDOUT_FILENO, uts->version, 0); + else + dprint(STDOUT_FILENO, rel->version, 0); break; case 0: goto leave; @@ -119,28 +240,31 @@ leave: /* * Parse and display /etc/issue */ -static void do_issue(char *tty) +static void issue(char *tty) { - FILE *fp; char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n"; struct utsname uts; + struct osrel rel; + int compat; + FILE *fp; /* * Get data about this machine. */ uname(&uts); + compat = osrel(&rel); fp = fopen("/etc/issue", "r"); if (fp) { while (fgets(buf, sizeof(buf), fp)) - do_parse(buf, &uts, tty); + parseln(buf, &uts, &rel, tty, compat); fclose(fp); } else { - do_parse(buf, &uts, tty); + parseln(buf, &uts, &rel, tty, compat); } - do_parse("\\n login: ", &uts, tty); + parseln("\\n login: ", &uts, &rel, tty, compat); } /* @@ -157,7 +281,7 @@ static int get_logname(char *tty, char *name, size_t len) ch = ' '; *name = '\0'; while (ch != '\n') { - do_issue(tty); + issue(tty); np = name; while ((ch = readch()) != '\n') { @@ -178,11 +302,10 @@ static int get_logname(char *tty, char *name, size_t len) } /* - * Execute the login(1) command with the current - * username as its argument. It will reply to the - * calling user by typing "Password: "... + * Start login(1) with the current username as its argument. It replies + * to the calling user by typing "Password: " ... */ -static int do_login(char *name) +static int exec_login(char *name) { struct stat st; @@ -242,7 +365,7 @@ restart: if (term && term[0]) setenv("TERM", term, 1); - return do_login(name); + return exec_login(name); } static int usage(int rc)