summaryrefslogtreecommitdiffstats
path: root/term-utils
diff options
context:
space:
mode:
authorKarel Zak2017-11-07 11:32:32 +0100
committerKarel Zak2017-11-07 11:32:32 +0100
commit1fc82a1360305f696dc1be6105c9c56a9ea03f52 (patch)
tree035990932961deeed632b647594797da3325c2e7 /term-utils
parentagetty: remove unused flag (diff)
downloadkernel-qcow2-util-linux-1fc82a1360305f696dc1be6105c9c56a9ea03f52.tar.gz
kernel-qcow2-util-linux-1fc82a1360305f696dc1be6105c9c56a9ea03f52.tar.xz
kernel-qcow2-util-linux-1fc82a1360305f696dc1be6105c9c56a9ea03f52.zip
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>
Diffstat (limited to 'term-utils')
-rw-r--r--term-utils/agetty.829
-rw-r--r--term-utils/agetty.c170
2 files changed, 169 insertions, 30 deletions
diff --git a/term-utils/agetty.8 b/term-utils/agetty.8
index f21634494..96d94bb48 100644
--- a/term-utils/agetty.8
+++ b/term-utils/agetty.8
@@ -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.
diff --git a/term-utils/agetty.c b/term-utils/agetty.c
index 4fc5abc24..9763fcd30 100644
--- a/term-utils/agetty.c
+++ b/term-utils/agetty.c
@@ -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)