]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
agetty: add support for /run/issue and /usr/lib/issue
authorKarel Zak <kzak@redhat.com>
Tue, 12 Nov 2019 09:10:02 +0000 (10:10 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 12 Nov 2019 09:26:35 +0000 (10:26 +0100)
Addresses: https://github.com/karelzak/util-linux/issues/828
Signed-off-by: Karel Zak <kzak@redhat.com>
include/pathnames.h
term-utils/agetty.8
term-utils/agetty.c

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