]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
login: using an avaiable shell while logging in.
authorWanBingjiang <wanbingjiang@webray.com.cn>
Thu, 8 May 2025 02:14:35 +0000 (10:14 +0800)
committerWanBingjiang <wanbingjiang@webray.com.cn>
Thu, 8 May 2025 02:40:56 +0000 (10:40 +0800)
login-utils/login.c

index 321f9d6ce321bf5a04fd965cc6a38b718bee3083..f54c97df172d96d789138cd06ba53459a92eed92 100644 (file)
@@ -1416,10 +1416,76 @@ static void initialize(int argc, char **argv, struct login_context *cxt)
                ul_close_all_fds(STDERR_FILENO + 1, ~0U);
 }
 
+static bool build_shell_argv(char *shell, char **child_argv)
+{
+       bool script;
+       int child_argc = 0;
+
+       /* if the shell field has a space: treat it like a shell script */
+       script = strchr(shell, ' ') != NULL;
+
+       if (script) {
+               char *buff;
+
+               xasprintf(&buff, "exec %s", shell);
+               child_argv[child_argc++] = "/bin/sh";
+               child_argv[child_argc++] = "-sh";
+               child_argv[child_argc++] = "-c";
+               child_argv[child_argc++] = buff;
+       } else {
+               char *buff, *p;
+
+               xasprintf(&buff, "-%.*s", PATH_MAX,
+                               ((p = strrchr(shell, '/')) ?
+                               p + 1 : shell));
+               child_argv[child_argc++] = shell;
+               child_argv[child_argc++] = buff;
+       }
+
+       child_argv[child_argc++] = NULL;
+
+       return script;
+}
+
+static void login_shell_fallback(char **child_argv)
+{
+#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
+       size_t size = 0;
+       econf_err error;
+       char **keys = NULL;
+       econf_file *key_file = open_etc_shells();
+
+       if (!key_file)
+               return;
+
+       error = econf_getKeys(key_file, NULL, &size, &keys);
+       if (error) {
+               econf_free(key_file);
+               errx(EXIT_FAILURE,
+                       _("Cannot evaluate entries in shells files: %s"),
+                       econf_errString(error));
+       }
+
+       for (size_t i = 0; i < size; i++) {
+               build_shell_argv(keys[i], child_argv);
+               execvp(child_argv[0], child_argv + 1);
+       }
+       econf_free(keys);
+       econf_free(key_file);
+#else
+       char *s;
+       setusershell();
+       while ((s = getusershell())) {
+               build_shell_argv(s, child_argv);
+               execvp(child_argv[0], child_argv + 1);
+       }
+       endusershell();
+#endif
+}
+
 int main(int argc, char **argv)
 {
        char *child_argv[10];
-       int child_argc = 0;
        struct passwd *pwd;
        struct login_context cxt = {
                .tty_mode = TTY_MODE,             /* tty chmod() */
@@ -1555,38 +1621,21 @@ int main(int argc, char **argv)
                printf(_("Logging in with home = \"/\".\n"));
        }
 
-       /* if the shell field has a space: treat it like a shell script */
-       script = strchr(pwd->pw_shell, ' ') != NULL;
-
-       if (script) {
-               char *buff;
-
-               xasprintf(&buff, "exec %s", pwd->pw_shell);
-               child_argv[child_argc++] = "/bin/sh";
-               child_argv[child_argc++] = "-sh";
-               child_argv[child_argc++] = "-c";
-               child_argv[child_argc++] = buff;
-       } else {
-               char *buff, *p;
-
-               xasprintf(&buff, "-%.*s", PATH_MAX,
-                         ((p = strrchr(pwd->pw_shell, '/')) ?
-                         p + 1 : pwd->pw_shell));
-               child_argv[child_argc++] = pwd->pw_shell;
-               child_argv[child_argc++] = buff;
-       }
-
-       child_argv[child_argc++] = NULL;
-
        /* http://www.linux-pam.org/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_end */
        (void) pam_end(cxt.pamh, PAM_SUCCESS|PAM_DATA_SILENT);
 
+       script = build_shell_argv(pwd->pw_shell, child_argv);
        execvp(child_argv[0], child_argv + 1);
 
-       if (script)
-               warn(_("couldn't exec shell script"));
-       else
-               warn(_("no shell"));
+       if (getlogindefs_bool("LOGIN_SHELL_FALLBACK", 0)) {
+               login_shell_fallback(child_argv);
+               warn(_("no available shell"));
+       } else {
+               if (script)
+                       warn(_("couldn't exec shell script"));
+               else
+                       warn(_("no shell"));
+       }
 
        exit(EXIT_SUCCESS);
 }