shell.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*
  2. Provides a functions for working with shell.
  3. Copyright (C) 2006-2025
  4. Free Software Foundation, Inc.
  5. Written by:
  6. Slava Zanko <slavazanko@gmail.com>, 2015.
  7. This file is part of the Midnight Commander.
  8. The Midnight Commander is free software: you can redistribute it
  9. and/or modify it under the terms of the GNU General Public License as
  10. published by the Free Software Foundation, either version 3 of the License,
  11. or (at your option) any later version.
  12. The Midnight Commander is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /** \file shell.c
  20. * \brief Source: provides a functions for working with shell.
  21. */
  22. #include <config.h>
  23. #include <pwd.h> /* for username in xterm title */
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include "global.h"
  28. #include "util.h"
  29. /*** global variables ****************************************************************************/
  30. /*** file scope macro definitions ****************************************************************/
  31. /*** file scope type declarations ****************************************************************/
  32. /*** forward declarations (file scope functions) *************************************************/
  33. /*** file scope variables ************************************************************************/
  34. static char rp_shell[PATH_MAX];
  35. /* --------------------------------------------------------------------------------------------- */
  36. /*** file scope functions ************************************************************************/
  37. /* --------------------------------------------------------------------------------------------- */
  38. /**
  39. * Get a system shell.
  40. *
  41. * @return newly allocated mc_shell_t object with shell name
  42. */
  43. static mc_shell_t *
  44. mc_shell_get_installed_in_system (void)
  45. {
  46. mc_shell_t *mc_shell;
  47. mc_shell = g_new0 (mc_shell_t, 1);
  48. /* 3rd choice: look for existing shells supported as MC subshells. */
  49. if (access ("/bin/bash", X_OK) == 0)
  50. mc_shell->path = g_strdup ("/bin/bash");
  51. else if (access ("/bin/zsh", X_OK) == 0)
  52. mc_shell->path = g_strdup ("/bin/zsh");
  53. else if (access ("/bin/oksh", X_OK) == 0)
  54. mc_shell->path = g_strdup ("/bin/oksh");
  55. else if (access ("/bin/ksh", X_OK) == 0)
  56. mc_shell->path = g_strdup ("/bin/ksh");
  57. else if (access ("/bin/ksh93", X_OK) == 0)
  58. mc_shell->path = g_strdup ("/bin/ksh93");
  59. else if (access ("/bin/ash", X_OK) == 0)
  60. mc_shell->path = g_strdup ("/bin/ash");
  61. else if (access ("/bin/dash", X_OK) == 0)
  62. mc_shell->path = g_strdup ("/bin/dash");
  63. else if (access ("/bin/busybox", X_OK) == 0)
  64. mc_shell->path = g_strdup ("/bin/busybox");
  65. else if (access ("/bin/tcsh", X_OK) == 0)
  66. mc_shell->path = g_strdup ("/bin/tcsh");
  67. else if (access ("/bin/csh", X_OK) == 0)
  68. mc_shell->path = g_strdup ("/bin/csh");
  69. else if (access ("/bin/mksh", X_OK) == 0)
  70. mc_shell->path = g_strdup ("/bin/mksh");
  71. /* No fish as fallback because it is so much different from other shells and
  72. * in a way exotic (even though user-friendly by name) that we should not
  73. * present it as a subshell without the user's explicit intention. We rather
  74. * will not use a subshell but just a command line.
  75. * else if (access("/bin/fish", X_OK) == 0)
  76. * mc_global.tty.shell = g_strdup ("/bin/fish");
  77. */
  78. else
  79. /* Fallback and last resort: system default shell */
  80. mc_shell->path = g_strdup ("/bin/sh");
  81. return mc_shell;
  82. }
  83. /* --------------------------------------------------------------------------------------------- */
  84. static char *
  85. mc_shell_get_name_env (void)
  86. {
  87. const char *shell_env;
  88. char *shell_name = NULL;
  89. shell_env = g_getenv ("SHELL");
  90. if ((shell_env == NULL) || (shell_env[0] == '\0'))
  91. {
  92. /* 2nd choice: user login shell */
  93. struct passwd *pwd;
  94. pwd = getpwuid (geteuid ());
  95. if (pwd != NULL)
  96. shell_name = g_strdup (pwd->pw_shell);
  97. }
  98. else
  99. /* 1st choice: SHELL environment variable */
  100. shell_name = g_strdup (shell_env);
  101. return shell_name;
  102. }
  103. /* --------------------------------------------------------------------------------------------- */
  104. static mc_shell_t *
  105. mc_shell_get_from_env (void)
  106. {
  107. mc_shell_t *mc_shell = NULL;
  108. char *shell_name;
  109. shell_name = mc_shell_get_name_env ();
  110. if (shell_name != NULL)
  111. {
  112. mc_shell = g_new0 (mc_shell_t, 1);
  113. mc_shell->path = shell_name;
  114. }
  115. return mc_shell;
  116. }
  117. /* --------------------------------------------------------------------------------------------- */
  118. static void
  119. mc_shell_recognize_real_path (mc_shell_t *mc_shell)
  120. {
  121. if (strstr (mc_shell->path, "/zsh") != NULL || strstr (mc_shell->real_path, "/zsh") != NULL
  122. || getenv ("ZSH_VERSION") != NULL)
  123. {
  124. /* Also detects ksh symlinked to zsh */
  125. mc_shell->type = SHELL_ZSH;
  126. mc_shell->name = "zsh";
  127. }
  128. else if (strstr (mc_shell->path, "/tcsh") != NULL
  129. || strstr (mc_shell->real_path, "/tcsh") != NULL)
  130. {
  131. /* Also detects csh symlinked to tcsh */
  132. mc_shell->type = SHELL_TCSH;
  133. mc_shell->name = "tcsh";
  134. }
  135. else if (strstr (mc_shell->path, "/csh") != NULL
  136. || strstr (mc_shell->real_path, "/csh") != NULL)
  137. {
  138. mc_shell->type = SHELL_TCSH;
  139. mc_shell->name = "csh";
  140. }
  141. else if (strstr (mc_shell->path, "/fish") != NULL
  142. || strstr (mc_shell->real_path, "/fish") != NULL)
  143. {
  144. mc_shell->type = SHELL_FISH;
  145. mc_shell->name = "fish";
  146. }
  147. else if (strstr (mc_shell->path, "/dash") != NULL
  148. || strstr (mc_shell->real_path, "/dash") != NULL)
  149. {
  150. /* Debian ash (also found if symlinked to by ash/sh) */
  151. mc_shell->type = SHELL_DASH;
  152. mc_shell->name = "dash";
  153. }
  154. else if (strstr (mc_shell->real_path, "/busybox") != NULL)
  155. {
  156. /* If shell is symlinked to busybox, assume it is an ash, even though theoretically
  157. * it could also be a hush (a mini shell for non-MMU systems deactivated by default).
  158. * For simplicity's sake we assume that busybox always contains an ash, not a hush.
  159. * On embedded platforms or on server systems, /bin/sh often points to busybox.
  160. * Sometimes even bash is symlinked to busybox (CONFIG_FEATURE_BASH_IS_ASH option),
  161. * so we need to check busybox symlinks *before* checking for the name "bash"
  162. * in order to avoid that case. */
  163. mc_shell->type = SHELL_ASH_BUSYBOX;
  164. mc_shell->name = mc_shell->path;
  165. }
  166. else if (strstr (mc_shell->path, "/ksh") != NULL
  167. || strstr (mc_shell->real_path, "/ksh") != NULL
  168. || strstr (mc_shell->path, "/ksh93") != NULL
  169. || strstr (mc_shell->real_path, "/ksh93") != NULL
  170. || strstr (mc_shell->path, "/oksh") != NULL
  171. || strstr (mc_shell->real_path, "/oksh") != NULL)
  172. {
  173. mc_shell->type = SHELL_KSH;
  174. mc_shell->name = "ksh";
  175. }
  176. else if (strstr (mc_shell->path, "/mksh") != NULL
  177. || strstr (mc_shell->real_path, "/mksh") != NULL)
  178. {
  179. mc_shell->type = SHELL_MKSH;
  180. mc_shell->name = "mksh";
  181. }
  182. else
  183. mc_shell->type = SHELL_NONE;
  184. }
  185. /* --------------------------------------------------------------------------------------------- */
  186. static void
  187. mc_shell_recognize_path (mc_shell_t *mc_shell)
  188. {
  189. /* If shell is not symlinked to busybox, it is safe to assume it is a real shell */
  190. if (strstr (mc_shell->path, "/bash") != NULL || getenv ("BASH_VERSION") != NULL)
  191. {
  192. mc_shell->type = SHELL_BASH;
  193. mc_shell->name = "bash";
  194. }
  195. else if (strstr (mc_shell->path, "/sh") != NULL)
  196. {
  197. mc_shell->type = SHELL_SH;
  198. mc_shell->name = "sh";
  199. }
  200. else if (strstr (mc_shell->path, "/ash") != NULL || getenv ("BB_ASH_VERSION") != NULL)
  201. {
  202. mc_shell->type = SHELL_ASH_BUSYBOX;
  203. mc_shell->name = "ash";
  204. }
  205. else if (strstr (mc_shell->path, "/ksh") != NULL
  206. || strstr (mc_shell->path, "/ksh93") != NULL
  207. || strstr (mc_shell->path, "/oksh") != NULL
  208. || (getenv ("KSH_VERSION") != NULL
  209. && strstr (getenv ("KSH_VERSION"), "PD KSH") != NULL))
  210. {
  211. mc_shell->type = SHELL_KSH;
  212. mc_shell->name = "ksh";
  213. }
  214. else if (strstr (mc_shell->path, "/mksh") != NULL
  215. || (getenv ("KSH_VERSION") != NULL
  216. && strstr (getenv ("KSH_VERSION"), "MIRBSD KSH") != NULL))
  217. {
  218. mc_shell->type = SHELL_MKSH;
  219. mc_shell->name = "mksh";
  220. }
  221. else
  222. mc_shell->type = SHELL_NONE;
  223. }
  224. /* --------------------------------------------------------------------------------------------- */
  225. /*** public functions ****************************************************************************/
  226. /* --------------------------------------------------------------------------------------------- */
  227. void
  228. mc_shell_init (void)
  229. {
  230. mc_shell_t *mc_shell;
  231. mc_shell = mc_shell_get_from_env ();
  232. if (mc_shell == NULL)
  233. mc_shell = mc_shell_get_installed_in_system ();
  234. mc_shell->real_path = mc_realpath (mc_shell->path, rp_shell);
  235. /* Find out what type of shell we have. Also consider real paths (resolved symlinks)
  236. * because e.g. csh might point to tcsh, ash to dash or busybox, sh to anything. */
  237. if (mc_shell->real_path != NULL)
  238. mc_shell_recognize_real_path (mc_shell);
  239. if (mc_shell->type == SHELL_NONE)
  240. mc_shell_recognize_path (mc_shell);
  241. if (mc_shell->type == SHELL_NONE)
  242. mc_global.tty.use_subshell = FALSE;
  243. mc_global.shell = mc_shell;
  244. }
  245. /* --------------------------------------------------------------------------------------------- */
  246. void
  247. mc_shell_deinit (void)
  248. {
  249. if (mc_global.shell != NULL)
  250. {
  251. g_free (mc_global.shell->path);
  252. MC_PTR_FREE (mc_global.shell);
  253. }
  254. }
  255. /* --------------------------------------------------------------------------------------------- */