agetty: add support for /run/issue and /usr/lib/issue

Addresses: https://github.com/karelzak/util-linux/issues/828
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2019-11-12 10:10:02 +01:00
parent c2409b55a0
commit 456bcbca6b
3 changed files with 135 additions and 90 deletions

View File

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

View File

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

View File

@ -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;
int dd, nfiles, i;
struct dirent **namelist = NULL;
dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (dd < 0)
return 1;
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;
(*n)++;
if (fstatat(dd, d->d_name, &st, 0) ||
!S_ISREG(st.st_mode))
continue;
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 <path>.
* 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;
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;
}
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;
}
i = 0;
#endif
if (filename)
f = fopen(filename, "r");
if (f || dirname) {
int c;
ie->do_tcsetattr = 1;
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);
if ((op->flags & F_VCONSOLE) == 0)
ie->do_tcrestore = 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;
}
#ifdef ISSUEDIR_SUPPORT
for (i = 0; i < nfiles; i++)
free(namelist[i]);
free(namelist);
if (dd >= 0)
close(dd);
#endif
/* 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);
done:
#ifdef AGETTY_RELOAD
if (netlink_groups != 0)
open_netlink();
#endif
fclose(ie->output);
if (ie->output)
fclose(ie->output);
}
#endif /* ISSUE_SUPPORT */