From 456bcbca6b55fbed33d9f86e69a51abd0e1b8f0b Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 12 Nov 2019 10:10:02 +0100 Subject: [PATCH] agetty: add support for /run/issue and /usr/lib/issue Addresses: https://github.com/karelzak/util-linux/issues/828 Signed-off-by: Karel Zak --- include/pathnames.h | 7 +- term-utils/agetty.8 | 17 +++- term-utils/agetty.c | 197 ++++++++++++++++++++++++++------------------ 3 files changed, 133 insertions(+), 88 deletions(-) diff --git a/include/pathnames.h b/include/pathnames.h index 2e1f19355c..ae09803698 100644 --- a/include/pathnames.h +++ b/include/pathnames.h @@ -68,8 +68,11 @@ # define _PATH_BTMP "/var/log/btmp" #endif -#define _PATH_ISSUE "/etc/issue" -#define _PATH_ISSUEDIR _PATH_ISSUE ".d" +#define _PATH_ISSUE_FILENAME "issue" +#define _PATH_ISSUE_DIRNAME _PATH_ISSUE_FILENAME ".d" + +#define _PATH_ISSUE "/etc/" _PATH_ISSUE_FILENAME +#define _PATH_ISSUEDIR "/etc/" _PATH_ISSUE_DIRNAME #define _PATH_OS_RELEASE_ETC "/etc/os-release" #define _PATH_OS_RELEASE_USR "/usr/lib/os-release" diff --git a/term-utils/agetty.8 b/term-utils/agetty.8 index 1ef9387697..eb6cb33410 100644 --- a/term-utils/agetty.8 +++ b/term-utils/agetty.8 @@ -118,7 +118,7 @@ is added to the \fB/bin/login\fP command line. See \fB\-\-login\-options\fR. .TP \-f, \-\-issue\-file \fIfile|directory\fP -Display the contents of \fIfile\fP instead of \fI/etc/issue\fP. If the +Display the contents of \fIfile\fP instead of \fI/etc/issue\fP (or other). If the specified path is a \fIdirectory\fP then displays all files with .issue file extension in version-sort order from the directory. This allows custom messages to be displayed on different terminals. The @@ -340,7 +340,7 @@ for a leading "\-" and makes sure the logname gets passed as one parameter on how the login binary parses the command line that might not be sufficient. Check that the used login program cannot be abused this way. .PP -Some programs use "\-\-" to indicate that the rest of the commandline should +Some programs use "\-\-" to indicate that the rest of the command line should not be interpreted as options. Use this feature if available by passing "\-\-" before the username gets passed by \\u. @@ -353,9 +353,18 @@ directory is ignored. All files with .issue extension from the directory are printed in version-sort order. The directory allow to maintain 3rd-party messages independently on the primary system \fI/etc/issue\fP file. +Since version 2.35 additional locations for issue file and directory are +supported. If the default \fI/etc/issue\fP does not exist than agetty checks +for \fI/run/issue\fP and \fI/run/issue.d\fP, thereafter for +\fI/usr/lib/issue\fP and \fI/usr/lib/issue.d\fP. The directory /etc is +expected for host specific configuration, /run is expected for generated stuff +and /usr/lib for static distribution maintained configuration. + The default path maybe overridden by \fB\-\-issue\-file\fP option. In this case -specified path has to be file or directory and the default \fI/etc/issue\fP as -well as \fI/etc/issue.d\fP are ignored. +specified path has to be file or directory and all the default issue file and +directory locations are ignored. + +The issue file feature is possible to completely disable by \fB\-\-noissue\fP option. The issue files may contain certain escape codes to display the system name, date, time etcetera. All escape codes consist of a backslash (\\) immediately diff --git a/term-utils/agetty.c b/term-utils/agetty.c index 038152d56f..f1e5b32929 100644 --- a/term-utils/agetty.c +++ b/term-utils/agetty.c @@ -1729,26 +1729,53 @@ static int issuedir_filter(const struct dirent *d) return 1; } -static FILE *issuedir_next_file(int dd, struct dirent **namelist, int nfiles, int *n) + +static int issuefile_read_stream(struct issue *ie, FILE *f, struct options *op, struct termios *tp); + +/* returns: 0 on success, 1 cannot open, <0 on error + */ +static int issuedir_read(struct issue *ie, const char *dirname, + struct options *op, struct termios *tp) { - while (*n < nfiles) { - struct dirent *d = namelist[*n]; - struct stat st; - FILE *f; + int dd, nfiles, i; + struct dirent **namelist = NULL; - (*n)++; + dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dd < 0) + return 1; - if (fstatat(dd, d->d_name, &st, 0) || - !S_ISREG(st.st_mode)) - continue; + nfiles = scandirat(dd, ".", &namelist, issuedir_filter, versionsort); + if (nfiles <= 0) + goto done; + + ie->do_tcsetattr = 1; + + for (i = 0; i < nfiles; i++) { + struct dirent *d = namelist[i]; + FILE *f; f = fopen_at(dd, d->d_name, O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); - if (f) - return f; + if (f) { + issuefile_read_stream(ie, f, op, tp); + fclose(f); + } } - return NULL; + + for (i = 0; i < nfiles; i++) + free(namelist[i]); + free(namelist); +done: + close(dd); + return 0; } +#else /* !ISSUEDIR_SUPPORT */ +static int issuedir_read(struct issue *ie __attribute__((__unused__)), + const char *dirname __attribute__((__unused__)), + struct options *op __attribute__((__unused__)), + struct termios *tp __attribute__((__unused__))) +{ +} #endif /* ISSUEDIR_SUPPORT */ #ifndef ISSUE_SUPPORT @@ -1769,6 +1796,44 @@ static void eval_issue_file(struct issue *ie __attribute__((__unused__)), } #else /* ISSUE_SUPPORT */ +static int issuefile_read_stream( + struct issue *ie, FILE *f, + struct options *op, struct termios *tp) +{ + struct stat st; + int c; + + if (fstat(fileno(f), &st) || !S_ISREG(st.st_mode)) + return 1; + + if (!ie->output) + ie->output = open_memstream(&ie->mem, &ie->mem_sz); + + while ((c = getc(f)) != EOF) { + if (c == '\\') + output_special_char(ie, getc(f), op, tp, f); + else + putc(c, ie->output); + } + + fclose(f); + return 0; +} + +static int issuefile_read( + struct issue *ie, const char *filename, + struct options *op, struct termios *tp) +{ + FILE *f = fopen(filename, "r" UL_CLOEXECSTR); + int rc = 1; + + if (f) + rc = issuefile_read_stream(ie, f, op, tp); + fclose(f); + return rc; +} + + #ifdef AGETTY_RELOAD static int issue_is_changed(struct issue *ie) { @@ -1829,97 +1894,65 @@ static void eval_issue_file(struct issue *ie, struct options *op, struct termios *tp) { - const char *filename, *dirname = NULL; - FILE *f = NULL; -#ifdef ISSUEDIR_SUPPORT - int dd = -1, nfiles = 0, i; - struct dirent **namelist = NULL; -#endif + int has_file = 0; + #ifdef AGETTY_RELOAD netlink_groups = 0; #endif - if (!(op->flags & F_ISSUE)) - return; + goto done; /* * The custom issue file or directory specified by: agetty -f . * Note that nothing is printed if the file/dir does not exist. */ - filename = op->issue; - if (filename) { + if (op->issue) { struct stat st; - if (stat(filename, &st) < 0) - return; - if (S_ISDIR(st.st_mode)) { - dirname = filename; - filename = NULL; - } - } else { - /* The default /etc/issue and optional /etc/issue.d directory - * as extension to the file. The /etc/issue.d directory is - * ignored if there is no /etc/issue file. The file may be - * empty or symlink. - */ - if (access(_PATH_ISSUE, F_OK|R_OK) != 0) - return; - filename = _PATH_ISSUE; - dirname = _PATH_ISSUEDIR; - } - - ie->output = open_memstream(&ie->mem, &ie->mem_sz); -#ifdef ISSUEDIR_SUPPORT - if (dirname) { - dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (dd >= 0) - nfiles = scandirat(dd, ".", &namelist, issuedir_filter, versionsort); - if (nfiles <= 0) - dirname = NULL; + if (stat(op->issue, &st) < 0) + goto done; + if (S_ISDIR(st.st_mode)) + issuedir_read(ie, op->issue, op, tp); + else + issuefile_read(ie, op->issue, op, tp); + goto done; } - i = 0; -#endif - if (filename) - f = fopen(filename, "r"); - if (f || dirname) { - int c; - ie->do_tcsetattr = 1; + /* The default /etc/issue and optional /etc/issue.d directory as + * extension to the file. The /etc/issue.d directory is ignored if + * there is no /etc/issue file. The file may be empty or symlink. + */ + if (access(_PATH_ISSUE, F_OK|R_OK) == 0) { + issuefile_read(ie, _PATH_ISSUE, op, tp); + issuedir_read(ie, _PATH_ISSUEDIR, op, tp); + goto done; + } - do { -#ifdef ISSUEDIR_SUPPORT - if (!f && i < nfiles) - f = issuedir_next_file(dd, namelist, nfiles, &i); -#endif - if (!f) - break; - while ((c = getc(f)) != EOF) { - if (c == '\\') - output_special_char(ie, getc(f), op, tp, f); - else - putc(c, ie->output); - } - fclose(f); - f = NULL; - } while (dirname); + /* Fallback @runstatedir (usually /run) -- the file is not required to + * read the dir. + */ + if (issuefile_read(ie, _PATH_RUNSTATEDIR "/" _PATH_ISSUE_FILENAME, op, tp) == 0) + has_file++; + if (issuedir_read(ie, _PATH_RUNSTATEDIR "/" _PATH_ISSUE_DIRNAME, op, tp) == 0) + has_file++; + if (has_file) + goto done; + + /* Fallback @sysconfstaticdir (usually /usr/lib) -- the file is not + * required to read the dir + */ + issuefile_read(ie, _PATH_SYSCONFSTATICDIR "/" _PATH_ISSUE_FILENAME, op, tp); + issuedir_read(ie, _PATH_SYSCONFSTATICDIR "/" _PATH_ISSUE_DIRNAME, op, tp); - if ((op->flags & F_VCONSOLE) == 0) - ie->do_tcrestore = 1; - } +done: -#ifdef ISSUEDIR_SUPPORT - for (i = 0; i < nfiles; i++) - free(namelist[i]); - free(namelist); - if (dd >= 0) - close(dd); -#endif #ifdef AGETTY_RELOAD if (netlink_groups != 0) open_netlink(); #endif - fclose(ie->output); + if (ie->output) + fclose(ie->output); } #endif /* ISSUE_SUPPORT */ -- 2.39.5