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 <kzak@redhat.com>
This commit is contained in:
Karel Zak 2020-05-12 14:20:54 +02:00
parent 1f861935a7
commit 5a528e2c6f
3 changed files with 92 additions and 19 deletions

View File

@ -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

View File

@ -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

View File

@ -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 <dirent.h>
# 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);
}
/*