Closed ale5000-git closed 1 month ago
The su
applet now has a -s
option which behaves much like that of util-linux.
Some features of su
only work if the built-in shell is used:
-N
option, which keeps the console open when the shell is finished.Prerelease binaries are available (PRE-5446 or above).
Thanks, with full path it seems to work fine but it would be nice that it also works simply as su -s 'bash'
.
Having:
$ which -a bash
bash
C:/Program Files/Git/usr/bin/bash.exe
Could it just skip the internal one and use the one found on PATH?
Edit: This would also allow su -s cmd
and su -s powershell
.
Some features of
su
only work if the built-in shell is used:
- The
-N
option, which keeps the console open when the shell is finished.- The ability to retain the current working directory when the busybox-w32 binary is in certain 'system' directories.
- The friendly message in the console title bar.
These were expected.
it would be nice that it also works simply as
su -s 'bash'
No, because bash
is a busybox applet.
But you can disable the "bash" applet using BB_OVERRIDE_APPLETS=bash
or make it prefer bash
at $PATH over the applet using BB_OVERRIDE_APPLETS=";bash"
.
Currently it doesn't search in $PATH at all and just fail.
Also in this specific case we aren't using busybox completely but only in the middle so having to set a busybox specific env var and then unset it later is a bit unpleasant.
To note also that using the -s
parameter automatically exclude busybox specific behaviours so it make sense.
Currently it doesn't search in $PATH at all and just fail.
Correct. That's what util-linux su
does too: it calls execv(3)
on the supplied shell option argument, so there's no PATH
search.
There is a slight difference: busybox-w32 su
performs the check on the shell path before calling ShellExecuteEx()
, so in case of error there's no UAC prompt. util-linux su
calls execv(3)
(and so detects any error) after asking for the password.
This only affects an error case and the busybox-w32 behaviour can be considered less bothersome.
If you want to run PowerShell with elevated privileges you can do su -s $(which powershell)
. (Though perhaps with more error checking.)
The drop
applet now has a -s
option too. PRE-5447 or above.
If you want to run PowerShell with elevated privileges you can do
su -s $(which powershell)
. (Though perhaps with more error checking.)
Just as quick test I have tried:
su -s "$(which powershell)" -c 'dir c:/; sleep 5'
and it works perfectly.
Instead this:
su -s "$(which cmd)" -c 'dir c:\ && sleep 5'
just open cmd as interactive and no command is executed.
su
assumes that the shell takes a -c
option for the commands to be executed.
But cmd.exe
doesn't: it expects a /c
option.
(That's one reason why drop
has a separate cdrop
alias to run cmd.exe
, so it can use the correct /c
option.)
I admit it isn't nice but isn't possible to check if the -s
option end in /cmd.exe
and then behave differently (since the use of cmd.exe is quite common on Windows)?
Sure, I've already got the code that does it. Give me a few minutes to push it through the system...
Thanks.
OK. PRE-5448 has special treatment when cmd.exe
is used as the 'shell' with su -c
.
I'm not sure what is wrong because the window last only one instant but this fail:
su -W -s "$(which cmd)" -c 'dir c:\'; echo $?
The syntax of cmd.exe
is not like a Unix shell:
&
, not ;
$?
is just a string with no particular meaningThe ; echo $?
is outside the cmd.exe.
The command for cmd.exe was just dir c:\
.
This is the same:
su -W -s "$(which cmd)" -c 'dir c:\'
echo $? # This one is still in busybox
Oops. I misread the command.
I've no idea why the exit code is 1.
There is probably an error message but I cannot see it due to window disappearance.
If you change the code (on your pc) to use /k
instead of /c
on cmd.exe you can probably see the error message.
Using /k
shows the error is "The file name, directory name or volume label syntax is incorrect".
I can make the problem go away using this command:
su -W -s "$(which cmd)" -c 'dir c:\ '
It's something to do with quoting things so the shell, the intermediate process that runs cmd.exe
and cmd.exe
are all happy.
It's something to do with quoting things so the shell
I didn't look closely into it, but the quoting rules for cmd.exe are not the same as other apps. Specifically, if one wants to run foo ARG...
where quote_arg
would correctly encode the arguments when running foo
directly from busybox, then it's not guaranteed to create a correct line which cmd.exe would invoke so that foo
sees the original arguments.
That's because cmd.exe interprets env vars (using %), also inside strings, interpret unquoted pipe or redirections, and other factors.
So foo still needs to see what quote_arg
produces, but now it needs to be encoded in such a way that it's what cmd.exe will produce.
Building a line for cmd.exe requires a different encoding, and especially because cmd.exe commands interet arguments differently than what CommandLineToArgvW
does. I'd say the most compatible method would be to not try to interpret or quote it at all somehow. I.e. just build the line content, either from one user arg, or space-combine the args like eval
or echo
do, and then use this line with CreateProcess without trying to quote anything in it, in the hope that the user knows how to quote it themselves for cmd.exe.
At the very least quoting args would break the cmd.exe echo, because this echo simply dumps everything till the end of the line, and if that happens to include quotes, then they'll be visible at the output as well. For instance (in busybox sh):
$ cmd /c echo 'foo bar'
"foo bar"
Or this which quote_arg
doesn't quote:
$ cmd /c echo 123 \> 456
$ cat 456
123
You simply can't win by trying to be smart with cmd.exe line, so the best way is to be the dumbest possible.
If you're not familiar with this page, it's worth reading https://www.daviddeley.com/autohotkey/parameters/parameters.htm It's fascinating, amazing, and depressing at the same time.
Wasn't there a similar issue in drop
that was fixed?
You simply can't win by trying to be smart with cmd.exe line, so the best way is to be the dumbest possible.
There might be one way to be smart about it in busybox-w32:
cmd '/c echo foo bar baz'
.%
, "
etc. This would encode our foo ARG...
so that when passed as a line for cmd.exe, then foo will see the same args which sh
did.This works. It looks weird, but it works:
su -W -s $(which cmd) -c dir root 'c:\'
I have little inclination for adding code to support cmd.exe
's peculiarities.
Is it possible to share the parsing code of the common parts between su/drop
by delegate them to a normal or inline function?
So the commands can be tested on drop
that it is much easier since it doesn't need a new window.
Excluding the additionl parameters and the additional uses of su
they are almost identical:
su -s SHELL -c CMD_STRING -- root ARG0 ARG...
drop -s SHELL -c CMD_STRING -- ARG0 ARG...
I didn't like the idea of introducing a dependency between su
and drop
.
Instead I've added a -t
(test mode) option to su
. This uses the same mechanism to start the new shell, but without elevated privileges so it can run in the same console window.
All other options and arguments are accepted as before, though they may behave slightly differently in the new circumstances.
I forgot to upload the prereleases. They're there now, PRE-5449 or above is the one you want.
Thank you very much. The new option is very good and seems to work perfectly and also everything else seems to works as intended.
If possible I would like just two more things before closing this ticket: 1) Mention in the help that -W is implied inside -t, example:
-t Test mode, no elevation (-W is implied)
2) Add this cmd.exe trick also to drop: https://github.com/rmyorston/busybox-w32/commit/e5244175baa5c94726670a6aeb8645428306011a (cdrop is nice when you can't provide parameters but for other things I think it is nice to have a single drop for all shells)
@ale5000-git Both your requests are addressed in the latest prereleases (PRE-5451 or above).
Now it seems everything is working as intended, thanks for all the work.
As a bonus I'll post the functions I use to elevate/drop admin rights and re-execute the initialization script:
sume()
{
local _fix_pwd
if test "${PLATFORM:?}" != 'win'; then
ui_warning 'sume not supported!!!'
return 1
fi
! is_root || return 0
if test "${IS_BUSYBOX:?}" = 'true'; then
# shellcheck disable=SC2016 # Ignore: Expressions don't expand in single quotes
su -c "${MAIN_DIR:?}"'/cmdline.bat "${@}"' -- root "${0-}" "${@}"
elif test -n "${BB_CMD?}" && test -n "${SHELL_CMD?}"; then
_fix_pwd="cd '${PWD:?}'"
# shellcheck disable=SC2016 # Ignore: Expressions don't expand in single quotes
"${BB_CMD:?}" su -s "${SHELL_CMD:?}" -c "${_fix_pwd:?}; ${MAIN_DIR:?}"'/cmdline.sh "${@}"' -- root "${0-}" "${@}"
else
ui_warning 'sume failed!!!'
return 125
fi
}
dropme()
{
if test "${PLATFORM:?}" != 'win'; then
ui_warning 'dropme not supported!!!'
return 1
fi
is_root || return 0
if test "${IS_BUSYBOX:?}" = 'true'; then
# shellcheck disable=SC2016 # Ignore: Expressions don't expand in single quotes
drop -c "${MAIN_DIR:?}"'/cmdline.sh "${@}"' -- "${0-}" "${@}"
elif test -n "${BB_CMD?}" && test -n "${SHELL_CMD?}"; then
# shellcheck disable=SC2016 # Ignore: Expressions don't expand in single quotes
"${BB_CMD:?}" drop -s "${SHELL_CMD:?}" -c "${MAIN_DIR:?}"'/cmdline.sh "${@}"' -- "${0-}" "${@}"
else
ui_warning 'dropme failed!!!'
return 125
fi
}
@rmyorston
While experimenting I have just typed a wrong command:
su -t -s "$(cmd.exe)"
and I wonder why prompt disappears and ctrl+c
do nothing.
The command is waiting for cmd.exe
to finish so it can return its output. When it's in that state ps
from a different console shows:
2368 424 rmy 0:00 4:56 sh -l
2668 2368 rmy 0:00 0:07 sh --fs 00000188
1516 2668 rmy 0:00 0:07 cmd.exe
Killing process 1516 results in this in the hung console:
~ $ su -t -s "$(cmd.exe)"
su: Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\Users\rmy>: Not found
~ $
@rmyorston
Is it supposed to be that ctrl-c
do nothing?
Is it supposed to be that ctrl-c do nothing?
It would be good if ctrl-c worked, but in this case it doesn't.
The problem is with the command substitution, $(cmd.exe)
. It isn't specific to su
.
When a shell command includes a command substitution the parent shell waits in a non-interruptible state for it to finish. If the user presses ctrl-c it's the child process that receives it. So:
~ $ echo $(sleep 100)
^C
~ $
The sleep
was interrupted, not the parent shell. Now:
~ $ echo $(sh)
~ $
The output of sh
is being intercepted for use by the parent so, recalling #442, it's a non-interactive shell and it can be interrupted. (I don't know where the '^C' went.)
Also from #442 we know that we get different behaviour if we force the shell to be interactive. Here I use a different prompt to distinguish between the shells:
~ $ echo $(PS1='# ' sh -i)
# echo yo
# exit
yo
~ $
I can interact with the child shell, and when I exit from it the parent echoes its output.
Suppose I try interrupting the child shell by pressing 'ctrl-c' three times:
~ $ echo $(PS1='# ' sh -i)
#
#
#
# exit
^C^C^C
~ $
The interrupts are received by the child shell, but it ignores them. I had to exit from the shell by hand.
Now let's try cmd.exe
:
~ $ echo $(cmd)
echo yo
exit
Microsoft Windows [Version 6.3.9600] (c) 2013 Microsoft Corporation. All rights reserved. C:\Users\rmy>yo C:\Users\rmy>
~ $
Again, I can interact with the child process. When I exit from it the parent echoes its output (including the copyright message and prompts, which evidently are written to stdout
).
Now try interrupting cmd.exe
:
~ $ echo $(cmd)
exit
Microsoft Windows [Version 6.3.9600] (c) 2013 Microsoft Corporation. All rights reserved. C:\Users\rmy> C:\Users\rmy> C:\Users\rmy> C:\Users\rmy>
~ $
Again, it doesn't work and I have to exit by hand.
Similar thing with upstream BusyBox:
$ busybox sh
~ $ echo $(sh -i)
sh-5.2$ echo yo
sh-5.2$ ^C
sh-5.2$ ^C
sh-5.2$ ^C
sh-5.2$ exit
exit
yo
~ $
In summary: an interactive process that doesn't respond to ctrl-c by exiting can't be interrupted in a command substitution. You either need to interact with it to shut it down or kill it.
Hi, since
bash
for Windows doesn't offerdrop
andsu
I would like to use busybox for these from inside real bash. Now the problem is that the command to preserve parameters with spaces became hellish complicated and error prone. Also if I just run bash it use the bash alias of busybox.From real bash I want to do:
bash => drop => bash => my_script.sh
Sample command:
Is it possible to add support for the
-s
parameter as in the realsu
on Linux to bothsu
anddrop
of busybox for Windows?For the -s parameter it should ignore (or at least unprefer) the internal alias: sh ash bash since if one want to use busybox it won't specify the parameter.
This would reduce the complexity of the command a lot.