From 5a528e2c6ff9735266fc2607c359e925b074bf2c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 12 May 2020 14:20:54 +0200 Subject: [PATCH] login: add support for directories in MOTD_FILE= The current standard is to use directories to make it easy for distributions to share resources. This patch also add /etc/motd.d and /run/motd.d to the default MOTD_FILE=. Addresses: https://github.com/karelzak/util-linux/issues/10341 Signed-off-by: Karel Zak --- include/pathnames.h | 2 +- login-utils/login.1 | 13 ++++-- login-utils/login.c | 96 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/include/pathnames.h b/include/pathnames.h index 3845d4c33..fac3a0783 100644 --- a/include/pathnames.h +++ b/include/pathnames.h @@ -41,7 +41,7 @@ #ifndef _PATH_MAILDIR # define _PATH_MAILDIR "/var/spool/mail" #endif -#define _PATH_MOTDFILE "/usr/share/misc/motd:/run/motd:/etc/motd" +#define _PATH_MOTDFILE "/usr/share/misc/motd:/run/motd:/run/motd.d:/etc/motd:/etc/motd.d" #ifndef _PATH_NOLOGIN # define _PATH_NOLOGIN "/etc/nologin" #endif diff --git a/login-utils/login.1 b/login-utils/login.1 index 29fe1b7a6..1e2d3cb28 100644 --- a/login-utils/login.1 +++ b/login-utils/login.1 @@ -151,9 +151,12 @@ configuration items are relevant for .B MOTD_FILE (string) .RS 4 -If defined, a ":" delimited list of "message of the day" files to be -displayed upon login. The default value is -.IR /etc\:/motd . +Sepecifies a ":" delimited list of "message of the day" files and directories +to be displayed upon login. If the specified path is a directory then displays +all files with .motd file extension in version-sort order from the directory. +.PP +The default value is +.IR "/usr/share/misc/motd:/run/motd:/run/motd.d:/etc/motd:/etc/motd.d" . If the .B MOTD_FILE item is empty or a quiet login is enabled, then the message of the day @@ -161,6 +164,10 @@ is not displayed. Note that the same functionality is also provided by .BR pam_motd (8) PAM module. +.PP +The directories in the +.B MOTD_FILE +are supported since version 2.36. .RE .PP .B LOGIN_PLAIN_PROMPT diff --git a/login-utils/login.c b/login-utils/login.c index 30940a6f8..0ea2fbb3f 100644 --- a/login-utils/login.c +++ b/login-utils/login.c @@ -97,6 +97,14 @@ #define TTYGRPNAME "tty" /* name of group to own ttys */ #define VCS_PATH_MAX 64 +#if defined(HAVE_SCANDIRAT) && defined(HAVE_OPENAT) +# include +# include "fileutils.h" +# define MOTDDIR_SUPPORT +# define MOTDDIR_EXT ".motd" +# define MOTDDIR_EXTSIZ (sizeof(MOTDDIR_EXT) - 1) +#endif + /* * Login control struct */ @@ -238,40 +246,98 @@ static const char *get_thishost(struct login_context *cxt, const char **domain) return cxt->thishost; } +#ifdef MOTDDIR_SUPPORT +static int motddir_filter(const struct dirent *d) +{ + size_t namesz; + +#ifdef _DIRENT_HAVE_D_TYPE + if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG && + d->d_type != DT_LNK) + return 0; +#endif + if (*d->d_name == '.') + return 0; + + namesz = strlen(d->d_name); + if (!namesz || namesz < MOTDDIR_EXTSIZ + 1 || + strcmp(d->d_name + (namesz - MOTDDIR_EXTSIZ), MOTDDIR_EXT) != 0) + return 0; + + return 1; /* accept */ +} + +static void motddir(const char *dirname) +{ + int dd, nfiles, i; + struct dirent **namelist = NULL; + + dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dd < 0) + return; + + nfiles = scandirat(dd, ".", &namelist, motddir_filter, versionsort); + if (nfiles <= 0) + goto done; + + for (i = 0; i < nfiles; i++) { + struct dirent *d = namelist[i]; + int fd; + + fd = openat(dd, d->d_name, O_RDONLY|O_CLOEXEC); + if (fd >= 0) { + struct stat st; + if (fstat(fd, &st) == 0 && st.st_size > 0) + sendfile(fileno(stdout), fd, NULL, st.st_size); + close(fd); + } + } + + for (i = 0; i < nfiles; i++) + free(namelist[i]); + free(namelist); +done: + close(dd); +} +#endif /* MOTDDIR_SUPPORT */ + /* * Output the /etc/motd file. * - * It determines the name of a login announcement file and outputs it to the + * It determines the name of a login announcement file/dir and outputs it to the * user's terminal at login time. The MOTD_FILE configuration option is a - * colon-delimited list of filenames. An empty MOTD_FILE option disables + * colon-delimited list of filenames or directories. An empty option disables * message-of-the-day printing completely. */ static void motd(void) { - char *motdlist, *motdfile; const char *mb; + char *file, *list; mb = getlogindefs_str("MOTD_FILE", _PATH_MOTDFILE); if (!mb || !*mb) return; - motdlist = xstrdup(mb); - - for (motdfile = strtok(motdlist, ":"); motdfile; - motdfile = strtok(NULL, ":")) { + list = xstrdup(mb); + for (file = strtok(list, ":"); file; file = strtok(NULL, ":")) { struct stat st; - int fd; - fd = open(motdfile, O_RDONLY, 0); - if (fd < 0) + if (stat(file, &st) < 0) continue; - if (!fstat(fd, &st) && st.st_size) - sendfile(fileno(stdout), fd, NULL, st.st_size); - close(fd); +#ifdef MOTDDIR_SUPPORT + if (S_ISDIR(st.st_mode)) + motddir(file); + else +#endif + if (S_ISREG(st.st_mode) && st.st_size > 0) { + int fd = open(file, O_RDONLY, 0); + if (fd >= 0) + sendfile(fileno(stdout), fd, NULL, st.st_size); + close(fd); + } } - - free(motdlist); + free(list); } /*