Permalink
Browse files

agetty: add support for /etc/issue.d

The /etc/issue file has been originally designed to inform users
about the system (version, name, etc.).

In last years is growing number of additional tools (containers,
maintenance tools and interfaces, ...) and many admins and downstream
maintainer want to add some tool specific hints to the issue file, but
it mess to share one file between more packages and/or scripts. The
solution is /etc/issue.d directory.

The directory is extension to the standard system /etc/issue. The
/etc/issue file has to exist, otherwise the directory will be ignored.
It means "rm /etc/issue" (or --onissue) is still the way how keep our
system silent independently on 3rd-party installed files in the
/etc/issue.d directory.

The content of the files in the directory are printed after content of
the /etc/issue. The files are printed in version-sort order and .issue
file extension is required (00-foo.issue 01-bar.issue ...).

The change is backwardly compatible.

Signed-off-by: Karel Zak <kzak@redhat.com>
  • Loading branch information...
karelzak committed Nov 7, 2017
1 parent e7f9744 commit 1fc82a1360305f696dc1be6105c9c56a9ea03f52
Showing with 171 additions and 30 deletions.
  1. +2 −0 include/pathnames.h
  2. +21 −8 term-utils/agetty.8
  3. +148 −22 term-utils/agetty.c
View
@@ -69,6 +69,8 @@
#endif
#define _PATH_ISSUE "/etc/issue"
#define _PATH_ISSUEDIR _PATH_ISSUE ".d"
#define _PATH_OS_RELEASE_ETC "/etc/os-release"
#define _PATH_OS_RELEASE_USR "/usr/lib/os-release"
#define _PATH_NUMLOCK_ON _PATH_RUNSTATEDIR "/numlock-on"
View
@@ -32,7 +32,7 @@ Optionally does not hang up when it is given an already opened line
.IP \(bu
Optionally does not display the contents of the \fI/etc/issue\fP file.
.IP \(bu
Optionally displays an alternative issue file instead of \fI/etc/issue\fP.
Optionally displays an alternative issue file or directory instead of \fI/etc/issue\fP or \fI/etc/issue.d\fP.
.IP \(bu
Optionally does not ask for a login name.
.IP \(bu
@@ -113,10 +113,12 @@ is added to the \fB/bin/login\fP command line.
.IP
See \fB\-\-login\-options\fR.
.TP
\-f, \-\-issue\-file \fIissue_file\fP
Display the contents of \fIissue_file\fP instead of \fI/etc/issue\fP.
This allows custom messages to be displayed on different terminals.
The \-\-noissue option will override this option.
\-f, \-\-issue\-file \fIfile|directory\fP
Display the contents of \fIfile\fP instead of \fI/etc/issue\fP. 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
\-\-noissue option will override this option.
.TP
\-h, \-\-flow\-control
Enable hardware (RTS/CTS) flow control. It is left up to the
@@ -337,9 +339,20 @@ Some programs use "\-\-" to indicate that the rest of the commandline should
not be interpreted as options. Use this feature if available by passing "\-\-"
before the username gets passed by \\u.
.SH ISSUE ESCAPES
The issue-file (\fI/etc/issue\fP, or the file set with the \fB\-\-issue\-file\fP option)
may contain certain escape codes to display the system name, date, time
.SH ISSUE FILES
The default issue file is \fI/etc/issue\fP. If the file exists then agetty also
checks for \fI/etc/issue.d\fP directory. The directory is optional extension to
the default issue file and content of the directory is printed after
\fI/etc/issue\fP content. If the \fI/etc/issue\fP does not exist than the
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.
The default path maybe overrided 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.
The issue files may contain certain escape codes to display the system name, date, time
etcetera. All escape codes consist of a backslash (\\) immediately
followed by one of the characters listed below.
View
@@ -35,6 +35,7 @@
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/utsname.h>
#include "strutils.h"
#include "all-io.h"
@@ -116,9 +117,9 @@
/*
* Things you may want to modify.
*
* If ISSUE is not defined, agetty will never display the contents of the
* /etc/issue file. You will not want to spit out large "issue" files at the
* wrong baud rate. Relevant for System V only.
* If ISSUE_SUPPORT is not defined, agetty will never display the contents of
* the /etc/issue file. You will not want to spit out large "issue" files at
* the wrong baud rate. Relevant for System V only.
*
* You may disagree with the default line-editing etc. characters defined
* below. Note, however, that DEL cannot be used for interrupt generation
@@ -127,8 +128,14 @@
/* Displayed before the login prompt. */
#ifdef SYSV_STYLE
# define ISSUE _PATH_ISSUE
# include <sys/utsname.h>
# define ISSUE_SUPPORT
# if defined(HAVE_SCANDIRAT) && defined(HAVE_OPENAT)
# include <dirent.h>
# include "fileutils.h"
# define ISSUEDIR_SUPPORT
# define ISSUEDIR_EXT ".issue"
# define ISSUEDIR_EXTSIZ (sizeof(ISSUEDIR_EXT) - 1)
# endif
#endif
/* Login prompt. */
@@ -169,7 +176,7 @@ struct options {
char *vcline; /* line of virtual console */
char *term; /* terminal type */
char *initstring; /* modem init string */
char *issue; /* alternative issue file */
char *issue; /* alternative issue file or directory */
char *erasechars; /* string with erase chars */
char *killchars; /* string with kill chars */
char *osrelease; /* /etc/os-release data */
@@ -188,7 +195,7 @@ enum {
};
#define F_PARSE (1<<0) /* process modem status messages */
#define F_ISSUE (1<<1) /* display /etc/issue */
#define F_ISSUE (1<<1) /* display /etc/issue or /etc/issue.d */
#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
#define F_INITSTRING (1<<4) /* initstring is set */
@@ -342,8 +349,7 @@ int main(int argc, char **argv)
struct options options = {
.flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
.login = _PATH_LOGIN, /* default login program */
.tty = "tty1", /* default tty line */
.issue = ISSUE /* default issue file */
.tty = "tty1" /* default tty line */
};
char *login_argv[LOGIN_ARGV_MAX + 1];
int login_argc = 0;
@@ -629,9 +635,12 @@ static void output_version(void)
#ifdef KDGKBLED
"hints",
#endif
#ifdef ISSUE
#ifdef ISSUE_SUPPORT
"issue",
#endif
#ifdef ISSUEDIR_SUPPORT
"issue.d",
#endif
#ifdef KDGKBMODE
"keyboard mode",
#endif
@@ -1657,18 +1666,117 @@ static int wait_for_term_input(int fd)
}
}
#endif /* AGETTY_RELOAD */
#ifdef ISSUEDIR_SUPPORT
static int issuedir_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 < ISSUEDIR_EXTSIZ + 1 ||
strcmp(d->d_name + (namesz - ISSUEDIR_EXTSIZ), ISSUEDIR_EXT))
return 0;
/* Accept this */
return 1;
}
static FILE *issuedir_next_file(int dd, struct dirent **namelist, int nfiles, int *n)
{
while (*n < nfiles) {
struct dirent *d = namelist[*n];
struct stat st;
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;
}
return NULL;
}
#endif /* ISSUEDIR_SUPPORT */
#ifndef ISSUE_SUPPORT
static void print_issue_file(struct options *op, struct termios *tp __attribute__((__unused__)))
{
if ((op->flags & F_NONL) == 0) {
/* Issue not in use, start with a new line. */
write_all(STDOUT_FILENO, "\r\n", 2);
}
}
#else /* ISSUE_SUPPORT */
static void print_issue_file(struct options *op, struct termios *tp)
{
#ifdef ISSUE
FILE *fd;
const char *filename, *dirname = NULL;
FILE *f = NULL;
#ifdef ISSUEDIR_SUPPORT
int dd = -1, nfiles = 0, i;
struct dirent **namelist = NULL;
#endif
if ((op->flags & F_NONL) == 0) {
/* Issue not in use, start with a new line. */
write_all(STDOUT_FILENO, "\r\n", 2);
}
#ifdef ISSUE
if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
if (!(op->flags & F_ISSUE))
return;
/*
* 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) {
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;
}
#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, oflag = tp->c_oflag; /* Save current setting. */
if ((op->flags & F_VCONSOLE) == 0) {
@@ -1677,12 +1785,23 @@ static void print_issue_file(struct options *op, struct termios *tp)
tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
}
while ((c = getc(fd)) != EOF) {
if (c == '\\')
output_special_char(getc(fd), op, tp, fd);
else
putchar(c);
}
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(getc(f), op, tp, f);
else
putchar(c);
}
fclose(f);
f = NULL;
} while (dirname);
fflush(stdout);
if ((op->flags & F_VCONSOLE) == 0) {
@@ -1691,10 +1810,17 @@ static void print_issue_file(struct options *op, struct termios *tp)
/* Wait till output is gone. */
tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
}
fclose(fd);
}
#endif /* ISSUE */
#ifdef ISSUEDIR_SUPPORT
for (i = 0; i < nfiles; i++)
free(namelist[i]);
free(namelist);
if (dd >= 0)
close(dd);
#endif
}
#endif /* ISSUE_SUPPORT */
/* Show login prompt, optionally preceded by /etc/issue contents. */
static void do_prompt(struct options *op, struct termios *tp)

6 comments on commit 1fc82a1

@thkukuk

This comment has been minimized.

Show comment
Hide comment
@thkukuk

thkukuk Mar 6, 2018

I only want to make aware that quite some distributions (CoreOS, openSUSE, ...) are already using tools using /etc/issue.d. We should make sure to not break this. Currently this breaks all existing solutions.

thkukuk replied Mar 6, 2018

I only want to make aware that quite some distributions (CoreOS, openSUSE, ...) are already using tools using /etc/issue.d. We should make sure to not break this. Currently this breaks all existing solutions.

@karelzak

This comment has been minimized.

Show comment
Hide comment
@karelzak

karelzak Mar 6, 2018

Owner

Hmm... they use *.issue files in the directory?

Anyway, we can add ./configure (e.g. --disable-agetty-issued) option to disable this feature.

Owner

karelzak replied Mar 6, 2018

Hmm... they use *.issue files in the directory?

Anyway, we can add ./configure (e.g. --disable-agetty-issued) option to disable this feature.

@thkukuk

This comment has been minimized.

Show comment
Hide comment
@thkukuk

thkukuk Mar 6, 2018

Ok, missed that the files have to end on .issue, so this should not become a problem. Else all implementations I'm aware of are using it in the same way as agetty, except that additional /usr/lib/issue.d and /run/issue.d are used.

thkukuk replied Mar 6, 2018

Ok, missed that the files have to end on .issue, so this should not become a problem. Else all implementations I'm aware of are using it in the same way as agetty, except that additional /usr/lib/issue.d and /run/issue.d are used.

@karelzak

This comment has been minimized.

Show comment
Hide comment
@karelzak

karelzak Mar 6, 2018

Owner

We have discussion about this on mailing list right now :-)

It's good idea to add /run and /usr/lib between the directories in the next version, but I'll probably prefer to use "agetty" in the path (/usr/lib/agetty/issued.d/ etc.).

The final goal is to use /run to store output from 3rd party generators, so it should be possible to adopt to the new agetty feature if you already have any solution, ... but it's too late for v2.32 where is support for /etc/issue.d only. Let's wait for the next v2.33.

Owner

karelzak replied Mar 6, 2018

We have discussion about this on mailing list right now :-)

It's good idea to add /run and /usr/lib between the directories in the next version, but I'll probably prefer to use "agetty" in the path (/usr/lib/agetty/issued.d/ etc.).

The final goal is to use /run to store output from 3rd party generators, so it should be possible to adopt to the new agetty feature if you already have any solution, ... but it's too late for v2.32 where is support for /etc/issue.d only. Let's wait for the next v2.33.

@thkukuk

This comment has been minimized.

Show comment
Hide comment
@thkukuk

thkukuk Mar 6, 2018

In https://github.com/thkukuk/issue-generator (which uses the same algorithm and semantic as CoreOS) the order is /usr/lib/issue.d (contains distribution stuff), which can be overwritten by /run/issue.d (dynamic generated by e.g. udev), which can be overwritten by the admin in /etc/issue.d.
If agetty could do the same, that would be really great, then we would not need issue-generator and the CoreOS implementation any longer, but have a drop in replacement without problems during update. I don't like the "agetty" in the path, this makes it tool depending.

thkukuk replied Mar 6, 2018

In https://github.com/thkukuk/issue-generator (which uses the same algorithm and semantic as CoreOS) the order is /usr/lib/issue.d (contains distribution stuff), which can be overwritten by /run/issue.d (dynamic generated by e.g. udev), which can be overwritten by the admin in /etc/issue.d.
If agetty could do the same, that would be really great, then we would not need issue-generator and the CoreOS implementation any longer, but have a drop in replacement without problems during update. I don't like the "agetty" in the path, this makes it tool depending.

@karelzak

This comment has been minimized.

Show comment
Hide comment
@karelzak

karelzak Mar 6, 2018

Owner

OK. Makes sense.

Owner

karelzak replied Mar 6, 2018

OK. Makes sense.

Please sign in to comment.