[PATCH 1/3] lib: introduce ul_default_shell() for consistent shell resolution

Alessandro Ratti <[email protected]> Sun, 23 Nov 2025 16:32:44 +0100
Newsgroups org.kernel.vger.util-linux
Message-ID <[email protected]>
Add a new ul_default_shell() function to provide consistent shell
resolution across util-linux tools. The function follows a priority
order: $SHELL environment variable, user's shell from passwd database,
and finally _PATH_BSHELL as fallback.

The function supports flags to control its behavior:
- UL_SHELL_NOENV: skip $SHELL environment variable check
- UL_SHELL_NOPWD: skip passwd database lookup

This addresses the issue where tools like script(1) would default to
/bin/sh without respecting the user's configured shell, potentially
causing data loss.

Addresses: https://github.com/util-linux/util-linux/issues/3865
Suggested-by: Karel Zak <[email protected]>
Suggested-by: Thomas Weißschuh <[email protected]>
Signed-off-by: Alessandro Ratti <[email protected]>
---
 include/shells.h  |  6 ++++++
 lib/Makemodule.am |  1 +
 lib/shells.c      | 26 ++++++++++++++++++++++++++
 3 files changed, 33 insertions(+)

diff --git a/include/shells.h b/include/shells.h
index c770a13ba..eca2b4187 100644
--- a/include/shells.h
+++ b/include/shells.h
@@ -4,7 +4,13 @@
 #ifndef UTIL_LINUX_SHELLS_H
 #define UTIL_LINUX_SHELLS_H
 
+#define UL_SHELL_NOENV  (1 << 0)
+#define UL_SHELL_NOPWD  (1 << 1)
+
 extern void print_shells(FILE *out, const char *format);
 extern int is_known_shell(const char *shell_name);
 
+struct passwd;
+const char *ul_default_shell(int flags, const struct passwd *pw);
+
 #endif /* UTIL_LINUX_SHELLS_H */
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index a9da57734..1d598faa2 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -49,6 +49,7 @@ libcommon_la_SOURCES = \
 if LINUX
 libcommon_la_SOURCES += \
 	lib/linux_version.c \
+	lib/shells.c \
 	lib/loopdev.c
 endif
 
diff --git a/lib/shells.c b/lib/shells.c
index 13f293c5e..ef2aecd0f 100644
--- a/lib/shells.c
+++ b/lib/shells.c
@@ -1,6 +1,11 @@
 /*
  * SPDX-License-Identifier: GPL-2.0-or-later
  */
+#include <sys/types.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <unistd.h>
 #include <sys/syslog.h>
 #if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
 #include <libeconf.h>
@@ -116,3 +121,24 @@ extern int is_known_shell(const char *shell_name)
 #endif
 	return ret;
 }
+
+const char *ul_default_shell(int flags, const struct passwd *pw)
+{
+	const char *shell = NULL;
+
+	if (!(flags & UL_SHELL_NOENV)) {
+		shell = getenv("SHELL");
+		if (shell && *shell)
+			return shell;
+	}
+	if (!(flags & UL_SHELL_NOPWD)) {
+		if (!pw)
+			pw = getpwuid(getuid());
+		if (pw)
+			shell = pw->pw_shell;
+		if (shell && *shell)
+			return shell;
+	}
+
+	return _PATH_BSHELL;
+}
-- 
2.51.1