]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
agetty: handle systems without a shell or /bin/login
authorMatt Van Horn <455140+mvanhorn@users.noreply.github.com>
Mon, 22 Jun 2026 08:58:11 +0000 (01:58 -0700)
committerMatt Van Horn <455140+mvanhorn@users.noreply.github.com>
Mon, 22 Jun 2026 08:58:11 +0000 (01:58 -0700)
When the login program (default /bin/login, or the one set with
--login-program) is missing or not executable, logging in is impossible.
Previously agetty still displayed the issue file and prompted for a
username, producing a confusing dead end.

Before prompting, check whether the login program is executable with
access(X_OK). If it is not, print a short banner ("This system does not
permit logins." by default, configurable with --nologin-message) and
wait for the user to press Enter, then re-check so an administrator who
installs the login program at runtime can proceed without a reboot. The
normal prompt flow is unaffected when the login program exists.

Add --nologin-message to override the banner text and document the new
behavior in agetty.8.adoc.

Closes #4117

agetty-cmd/agetty.c
agetty-cmd/agetty.h
term-utils/agetty.8.adoc

index 333dba2fa6884503f8800ed65f21e56a2bc74664..65d1dde0c330361f5adaf79e652263250fd0c437 100644 (file)
@@ -46,7 +46,11 @@ static int inotify_fd = AGETTY_RELOAD_FDNONE;
 #define serial_tty_option(opt, flag)   \
        (((opt)->flags & (F_VCONSOLE|(flag))) == (flag))
 
+/* Default banner printed when the login program is not available. */
+#define DEFAULT_NOLOGIN_MESSAGE        N_("This system does not permit logins.")
+
 static int wait_for_term_input(struct agetty_issue *ie, int fd);
+static void wait_for_login_program(struct agetty_options *op);
 static void do_prompt(struct agetty_issue *ie, struct agetty_options *op, struct termios *tp);
 static char *get_logname(struct agetty_issue *ie, struct agetty_options *op,
                         struct termios *tp, struct chardata *cp);
@@ -157,6 +161,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_("     --long-hostname        show full qualified hostname\n"), out);
        fputs(_("     --erase-chars <string> additional backspace chars\n"), out);
        fputs(_("     --kill-chars <string>  additional kill chars\n"), out);
+       fputs(_("     --nologin-message <msg> message shown when login program is missing\n"), out);
        fputs(_("     --chdir <directory>    chdir before the login\n"), out);
        fputs(_("     --delay <number>       sleep seconds before prompt\n"), out);
        fputs(_("     --nice <number>        run login with this priority\n"), out);
@@ -186,6 +191,7 @@ static void parse_args(int argc, char **argv, struct agetty_options *op)
                RELOAD_OPTION,
                LIST_SPEEDS_OPTION,
                ISSUE_SHOW_OPTION,
+               NOLOGIN_MESSAGE_OPTION,
        };
        static const struct option longopts[] = {
                {  "8bits",          no_argument,        NULL,  '8'  },
@@ -224,6 +230,7 @@ static void parse_args(int argc, char **argv, struct agetty_options *op)
                {  "help",           no_argument,        NULL,  HELP_OPTION     },
                {  "erase-chars",    required_argument,  NULL,  ERASE_CHARS_OPTION },
                {  "kill-chars",     required_argument,  NULL,  KILL_CHARS_OPTION },
+               {  "nologin-message", required_argument, NULL,  NOLOGIN_MESSAGE_OPTION },
                { NULL, 0, NULL, 0 }
        };
 
@@ -339,6 +346,9 @@ static void parse_args(int argc, char **argv, struct agetty_options *op)
                case KILL_CHARS_OPTION:
                        op->killchars = optarg;
                        break;
+               case NOLOGIN_MESSAGE_OPTION:
+                       op->nologin_message = optarg;
+                       break;
                case RELOAD_OPTION:
                        agetty_reload();
                        exit(EXIT_SUCCESS);
@@ -541,6 +551,13 @@ int main(int argc, char **argv)
 
        INIT_CHARDATA(&chardata);
 
+       /*
+        * If the login program is missing or not executable there is no way to
+        * log in.  Show a banner and wait until it becomes available instead of
+        * prompting for a username that can never be used.
+        */
+       wait_for_login_program(&options);
+
        if (options.autolog) {
                debug("doing auto login\n");
                options.username = options.autolog;
@@ -620,6 +637,32 @@ int main(int argc, char **argv)
 }
 
 
+/*
+ * Block until the login program is executable.
+ *
+ * On systems without a shell or /bin/login there is no point in showing the
+ * issue file and prompting for a username, because the login program can never
+ * be executed.  Instead of running into a confusing dead end, print a short
+ * banner and wait for the user to press Enter, then re-check whether the login
+ * program has become available (for example because an administrator installed
+ * it at runtime).  As soon as the login program is executable we return and the
+ * normal prompt flow continues.
+ */
+static void wait_for_login_program(struct agetty_options *op)
+{
+       const char *message = op->nologin_message ?
+                               op->nologin_message : _(DEFAULT_NOLOGIN_MESSAGE);
+
+       while (access(op->login, X_OK) != 0) {
+               printf("%s\n", message);
+               fflush(stdout);
+
+               /* Wait for Enter, then re-check the login program. */
+               if (getc(stdin) == EOF)
+                       return;
+       }
+}
+
 static void do_prompt(struct agetty_issue *ie, struct agetty_options *op, struct termios *tp)
 {
 #ifdef AGETTY_RELOAD
index 00c824622d2a7ebc82235a79fb264e63947d13ce..7465a173b3ba694fd8c880e2af06be03efb5ef67 100644 (file)
@@ -78,6 +78,7 @@ struct agetty_options {
        char *username;                 /* login name, given to /bin/login */
        char *fakehost;                 /* fake hostname for ut_host */
        char *osrelease;                /* /etc/os-release data */
+       char *nologin_message;          /* message shown when login program is missing */
        unsigned int delay;             /* Sleep seconds before prompt */
        int nice;                       /* Run login with this priority */
        int numspeed;                   /* number of baud rates to try */
index fe7c3a57f73e88e75b34f0e954748b91b464f4d9..5f54821199f6d127c68fb09bc4edc88200747fa1 100644 (file)
@@ -32,6 +32,8 @@ agetty - alternative Linux getty
 
 This program does not use the _/etc/gettydefs_ (System V) or _/etc/gettytab_ (SunOS 4) files.
 
+If the login program (by default _/bin/login_, or the program given with *--login-program*) is missing or not executable, then logging in is impossible. In that case *agetty* does not display the issue file or prompt for a login name. Instead it prints a short message ("This system does not permit logins." by default, configurable with *--nologin-message*) and waits for the user to press Enter. On Enter it re-checks whether the login program has become available, for example because an administrator installed it at runtime, and either continues with the normal prompt or shows the message again.
+
 == ARGUMENTS
 
 _port_::
@@ -168,6 +170,9 @@ This option specifies additional characters that should be interpreted as a back
 *--kill-chars* _string_::
 This option specifies additional characters that should be interpreted as a kill ("ignore all previous characters") when the user types the login name. The default additional 'kill' has been '@', but since util-linux 2.23 no additional kill characters are enabled by default.
 
+*--nologin-message* _string_::
+Use _string_ as the message that is shown when the login program is missing or not executable, instead of the default "This system does not permit logins." See the *DESCRIPTION* above for details about this behavior.
+
 *--chdir* _directory_::
 Change directory before the login.