Re: How to add subshell support for ash
- From: Alexander Kriegisch <kriegaex freetz org>
- To: mc-devel gnome org
- Subject: Re: How to add subshell support for ash
- Date: Sun, 04 Mar 2012 15:19:52 +0100
Okay guys, I guess I have a working integration for ash subshell support
(tested with BusyBox ash/sh on mipsel) now. I am offering it to you
developers for inspection and adaption, so it may soon become a part of
the mainstream MC code base. I am aware of a few shortcomings and have
mentioned them as TODO items in code comments.
The first file 040-ash_as_subshell.patch contains the functional part of
the patch, while 040-ash_as_subshell_additional.patch contains some
minor additions to documentation and test code which are not necessary
to run mc + ash. Feel free and encouraged to criticise and improve the
code - as I said, I am not a C programmer and rather got my results by
continuous testing with strace as a debugging tool because I do not know
how to handle gdb.
What I have so far:
- Ash mode will be used if the login shell is /bin/sh (on my
platform it is safe to assume this, for general use this needs to
be adapted to really recognise if /bin/sh is an ash or dash. I know
how to do this via shell script, but not cleanly via C, sorry.
- Ash as a subshell currently displays a fixed PS1 prompt like
"user@host:/my/path/$ " ("user@host:/my/path/# " for root). It
should be easy enough to change this, it just served my purpose
like this. It is just a detail.
- Chdir via subshell "cd" and via MC panel works as expected.
- Analogous to Bash, it is possible to use an init file
~/.local/share/mc/ashrc (with auto-fallback to ~/.profile). This is
not even implemented upstream for Zsh and Tcsh at the moment.
- Plese refer to code comments for how and why I implemented the
precmd via PS1 with two-fold indirection. This was necessary to
enable the start of a sub-subshell (user command "ash" from
subshell) without the sub-shell freezing (kill -STOP) or throwing
an error. This is the trickiest part and I am somewhat proud I got
it resolved with my rudimentary knowledge because it was a pain in
the a**. Maybe there is a much simpler way.
- I added some minimal documentation for Ash subshell mode to the
help file, but only in English (needs translation), also extending
Bash mode by mentioning ~/.bashrc (upstream just mentions
~/.local/share/mc/ashrc).
Remark: In order to get ENV into the environment for the init file, I
had to uncomment "g_free (putenv_str)". Initially I had used g_free
because Bash mode also uses it for INPUTRC. I wonder if the g_free also
needs to be removed in that case. Some MC professional might want to
check this.
I hope you are interested in this feature even though until now nobody
has answered my earlier inquiries. I want to thank Harald (ralda) from
the BusyxBox list though for sharing his thoughts and insights with me.
e.g. he gave me the important hint with (not to use) g_free.
Kind regards
--
Alexander Kriegisch
Alexander Kriegisch, 03.03.2012 18:39:
> Never mind, I am one step further:
>
>> case ASH:
>> /* ash does not support precmd, we need to read cwd from the prompt */
>> g_snprintf (precmd, sizeof (precmd),
>> "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]);
>> break;
>
> This works for BusyBox ash (tested on mipsel platform). The command will
> be contained in the shell editing history, but for me this is acceptable.
>
> The next thing I want to do is set ENV so as to enable loading my shell
> profile without having to use a login shell. This would be the
> equivalent to Bash's "-rcfile" parameter. I did this:
>
>> case ASH:
>> /* Do we have a custom init file ~/.local/share/mc/ashrc? */
>> init_file = mc_config_get_full_path ("ashrc");
>>
>> /* Otherwise use ~/.profile */
>> if (access (init_file, R_OK) == -1)
>> {
>> g_free (init_file);
>> init_file = g_strdup (".profile");
>> }
>>
>> /* Put init file to ENV variable used by ash */
>> char *putenv_str = g_strconcat ("ENV=", init_file, NULL);
>> putenv (putenv_str);
>> g_free (putenv_str);
>>
>> break;
>
> And later this:
>
>> /* TODO for upstream patch: Execute correct ash/dash/busybox shell (not necessary for Freetz) */
>> case ASH:
>> execl (shell, "sh", (char *) NULL);
>> break;
>
> The problem is that ENV seems to be unset in my subshell's environment,
> i.e. that ~ /.profile (whiche exists) is not evaluated. Do I need to
> somehow export the ENV variable to make it available to the subshell?
> BTW, I am compiling against uClibc 0.9.29, if this is relevant.
>
>
> Alexander Kriegisch, 03.03.2012 14:41:
>> I am on an embedded environment. Bash is available as a separate
>> package, but huge in comparison with the built-in ash of BusyBox.
>>
>> Disclaimer before I continue: I am *not* a C programmer. I can read
>> a bit of C, but not develop anything meaningful other than copy,
>> paste and modify existing code. So bear with me anyway, if you
>> please.
>>
>> I looked into src/subshell.c and tried to figure out what happens
>> there. I have started to add ash support, but MC seems to depend on
>> PROMPT_COMMAND/precmd/fish_prompt, i.e. on a function or alias which
>> is executed each time just before a prompt is printed. Ash does not
>> have anything like that, AFAIK. If I understand correctly, using
>> those pre-commands are MC's way of determining the cwd. I wonder why
>> you do not just use something like pwd or $PWD, but OTOH I do not
>> understand the difficulties of corresponding with the subshell.
>>
>> Anyway, in my case it would be absolutely fine to set a fixed value
>> of PS1='\w\$ ' for the subshell and read the cwd from the prompt. In
>> any case it would be better than not having the option of using ash
>> as a subshell at all.
>>
>> Could anyone maybe provide me with a code snippet or a patch which
>> shows me how to extract the cwd from the prompt assuming that PS1 has
>> the value '\w\$ '? (BTW, '\$' will be evaluated to '$' or '#' in
>> ash, depending on whether you are root or not.)
>>
>> Thank you so much. If I succeed in adding ash as a subshell, I will
>> provide a (raw) patch for you to inspect and maybe refine and
>> include upstream.
--- src/subshell.c 2012-03-02 13:55:52.018954847 +0100
+++ src/subshell.c 2012-03-04 05:10:53.750406898 +0100
@@ -126,6 +126,7 @@
static enum
{
BASH,
+ ASH,
TCSH,
ZSH,
FISH
@@ -269,8 +270,10 @@
switch (subshell_type)
{
case BASH:
+ /* Do we have a custom init file ~/.local/share/mc/bashrc? */
init_file = mc_config_get_full_path ("bashrc");
+ /* Otherwise use ~/.bashrc */
if (access (init_file, R_OK) == -1)
{
g_free (init_file);
@@ -294,6 +297,24 @@
break;
+ case ASH:
+ /* Do we have a custom init file ~/.local/share/mc/ashrc? */
+ init_file = mc_config_get_full_path ("ashrc");
+
+ /* Otherwise use ~/.profile */
+ if (access (init_file, R_OK) == -1)
+ {
+ g_free (init_file);
+ init_file = g_strdup (".profile");
+ }
+
+ /* Put init file to ENV variable used by ash */
+ char *putenv_str = g_strconcat ("ENV=", init_file, NULL);
+ putenv (putenv_str);
+ // Do not use "g_free (putenv_str)", otherwise ENV will be undefined!
+
+ break;
+
/* TODO: Find a way to pass initfile to TCSH and ZSH */
case TCSH:
case ZSH:
@@ -332,6 +353,11 @@
execl (shell, "bash", "-rcfile", init_file, (char *) NULL);
break;
+ /* TODO for upstream patch: Execute correct ash/dash/busybox shell (not necessary for Freetz) */
+ case ASH:
+ execl (shell, "sh", (char *) NULL);
+ break;
+
case TCSH:
execl (shell, "tcsh", (char *) NULL);
break;
@@ -796,6 +822,9 @@
subshell_type = BASH;
else if (strstr (shell, "/fish"))
subshell_type = FISH;
+ /* TODO for upstream patch: Check if "sh" really points to ash/dash/busybox (not necessary for Freetz) */
+ else if (strstr (shell, "/ash") || strstr (shell, "/dash") || strstr (shell, "/sh"))
+ subshell_type = ASH;
else
{
mc_global.tty.use_subshell = FALSE;
@@ -846,7 +875,7 @@
return;
}
}
- else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe))
+ else /* subshell_type is BASH, ASH or ZSH */ if (pipe (subshell_pipe))
{
perror (__FILE__ ": couldn't create pipe");
mc_global.tty.use_subshell = FALSE;
@@ -883,6 +912,24 @@
" PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]);
break;
+ case ASH:
+ /* Ash needs a somewhat complicated precmd emulation via PS1.
+ BUF_SMALL (defined in lib/global.h) is the length limit for precmd. */
+ g_snprintf (precmd, sizeof (precmd),
+
+ // A: This leads to a stopped subshell (=frozen mc) if user calls "sh" command
+ //"PS1='$(pwd>&%d; kill -STOP $$)\\\\u@\\\\h:\\\\w\\\\$ '\n",
+
+ // B: This leads to "sh: precmd: not found" in sub-subshell if user calls "sh" command
+ //"precmd(){ pwd>&%d;kill -STOP $$; }; PS1='$(precmd)\\\\u@\\\\h:\\\\w\\\\$ '\n",
+
+ // C: This works if user calls "sh" command because in sub-subshell
+ // PRECMD is unfedined, thus evaluated to empty string - no damage done
+ "precmd(){ pwd>&%d;kill -STOP $$; }; PRECMD=precmd; PS1='$(eval $PRECMD)\\\\u@\\\\h:\\\\w\\\\$ '\n",
+
+ subshell_pipe[WRITE]);
+ break;
+
case ZSH:
g_snprintf (precmd, sizeof (precmd),
" precmd(){ pwd>&%d;kill -STOP $$ }\n", subshell_pipe[WRITE]);
@@ -1103,6 +1150,13 @@
quote_cmd_start = "(printf \"%b\" '";
quote_cmd_end = "')";
}
+ /* TODO: When BusyBox printf is fixed, get rid of this "else if", see
+ http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */
+ else if (subshell_type == ASH)
+ {
+ quote_cmd_start = "\"`echo -en '";
+ quote_cmd_end = "'`\"";
+ }
else
{
quote_cmd_start = "\"`printf \"%b\" '";
--- lib/mcconfig/paths.c 2012-03-04 04:28:07.000000000 +0100
+++ lib/mcconfig/paths.c 2012-03-04 04:28:43.000000000 +0100
@@ -82,6 +82,7 @@
/* data */
{ "skins", &mc_data_str, MC_SKINS_SUBDIR},
{ "fish", &mc_data_str, FISH_PREFIX},
+ { "ashrc", &mc_data_str, "ashrc"},
{ "bashrc", &mc_data_str, "bashrc"},
{ "inputrc", &mc_data_str, "inputrc"},
{ "extfs.d", &mc_data_str, MC_EXTFS_DIR},
--- tests/lib/mcconfig/user_configs_path.c 2012-03-04 04:27:47.000000000 +0100
+++ tests/lib/mcconfig/user_configs_path.c 2012-03-04 05:33:48.418447747 +0100
@@ -96,6 +96,7 @@
path_fail_unless (CONF_DATA, MC_SKINS_SUBDIR);
path_fail_unless (CONF_DATA, FISH_PREFIX);
+ path_fail_unless (CONF_DATA, "ashrc");
path_fail_unless (CONF_DATA, "bashrc");
path_fail_unless (CONF_DATA, "inputrc");
path_fail_unless (CONF_DATA, MC_EXTFS_DIR);
--- doc/man/mc.1.in 2012-03-04 05:18:35.970419532 +0100
+++ doc/man/mc.1.in 2012-03-04 05:35:58.262451703 +0100
@@ -2408,7 +2408,7 @@
.\"NODE " The subshell support"
.SH " The subshell support"
The subshell support is a compile time option, that works with the
-shells: bash, tcsh and zsh.
+shells: bash, ash, tcsh and zsh.
.PP
When the subshell code is activated the Midnight Commander will
spawn a concurrent copy of your shell (the one defined in the
@@ -2423,8 +2423,10 @@
If you are using
.B bash
you can specify startup
-commands for the subshell in your ~/.local/share/mc/bashrc file and
+commands for the subshell in your ~/.local/share/mc/bashrc file (fallback ~/.bashrc) and
special keyboard maps in the ~/.local/share/mc/inputrc file.
+.B ash
+users may specify startup commands in ~/.local/share/mc/ashrc (fallback ~/.profile).
.B tcsh
users may specify startup commands in the ~/.local/share/mc/tcshrc file.
.PP
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]