Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

My environment is Ubuntu 12.04 LTS, and the sudo version is 1.8.3p1.

First I login as a normal user:

$ whoami
fin

$ cat /etc/passwd | grep -i "root\|fin"
root:x:0:0:root:/root:/bin/bash
fin:x:1000:1000:This is a normal user:/home/fin:/bin/bash

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Mar 30  2012 /bin/sh -> dash

$ ls -l /bin/bash
-rwxr-xr-x 1 root root 920788 Apr  3  2012 /bin/bash

$ echo $SHELL
/bin/bash

$ ps | grep "$$" | awk '{ print $4 }'
bash

$ ls -l ./test.sh
-rwxr-xr-x 1 fin fin 37 Sep 27 16:46 test.sh

$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'

$ ./test.sh
bash

$ sudo ./test.sh
sh

I suppose the last output should also be bash because /etc/passwd shows that root uses bash, am I missing any points about sudo?

share|improve this question
add comment

3 Answers

up vote 11 down vote accepted

It uses _PATH_BSHELL like execvp() which on Linux is defined as /bin/sh in /usr/include/paths.h. That should be the same as when executed with env or find -exec for instance.

It should certainly not use the user's login shell. The fact that you're seeing bash above is because it's bash (the shell you enter that command line in) that tries to execute it and when it gets a ENOEXEC error code from execve it decides to interpret it with itself instead (in sh compatibility mode).

share|improve this answer
    
Thanks for your reply, so it depends on sudo implementation to decide whether to use sh or parse /etc/passwd when it receieves ENOEXEC error? –  layka Sep 27 '13 at 14:11
    
No, sudo will not use the user's login shell unless you tell it to by telling it you want to pass it a command line to interpret by a shell (with -s) as opposed to a command to execute. If a command to execute, it will behave like execvp, that is like the system is meant to execute commands. –  Stephane Chazelas Sep 27 '13 at 14:19
    
@Stephane Chazelas: I think it doesn't mean system to run command. sudo always setup its execution enviroment prior the call to execve(). If you don't pass -s, sudo will use _PATH_BSHELL. –  Gnouc Sep 27 '13 at 14:31
    
@Gnouc, Yes, sudo will use _PATH_BSHELL like the system does (in execvp(), env, find, vi most shells...), it just happen to implement its own version of execvp(). It may also use _PATH_BSHELL with -s if executing $SHELL returns ENOEXEC. See my comment to your answer for more details. –  Stephane Chazelas Sep 27 '13 at 17:43
add comment

Because you don't use -s option, so sudo will use _PATH_BSHELL (which is define in /usr/include/paths.h on Ubuntu 12.04 LTS) to set $SHELL it run. Looking in to sudo source code:

/* Stash user's shell for use with the -s flag; don't pass to plugin. */
    if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
    ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
    }

If you use -s option, sudo will use your $SHELL instead of _PATH_BSHELL:

$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'

$ ./test.sh
bash

$ sudo -s ./test.sh
bash
share|improve this answer
1  
Note that -s doesn't mean the script will be interpreted by $SHELL, it just means that (assuming you've been authorised to use $SHELL as the target user), the command is started as $SHELL -c ./test.sh. Shells like bash, yash or ksh93 may choose to interpret the she-bang-less scripts with a copy of themselves, others would not and would call the system's sh instead. –  Stephane Chazelas Sep 27 '13 at 11:05
    
In other words, it's not $SHELL instead of _PATH_BSHELL, it's exec($SHELL, "-c", "./test.sh") unconditionally instead of if (exec("./test.sh") == ENOEXEC) exec(_PATH_BSHELL, "./test.sh") (pseudo-code). –  Stephane Chazelas Sep 27 '13 at 12:21
add comment

The kernel can only run binary executable images. So how do scripts get run? After all, I can type my_script_without_shebang at a shell prompt and I don't get an ENOEXEC error. Script execution is done not by the kernel, but by the shell. The exec code in the shell usually looks something like:

/* try to run the program */
execl(program, basename(program), (char *)0);

/* the exec failed -- maybe it is a shell script without shebang? */
if (errno == ENOEXEC)
    execl ("/bin/sh", "sh", "-c", program, (char *)0);

You can verify that with tracing a dummy shell script without shebang:

cat > /tmp/foo.sh <<EOF
echo
EOF

chmod u+x /tmp/foo.sh

strace /tmp/foo.sh 2>&1 | grep exec
execve("/tmp/foo.sh", ["/tmp/foo.sh"], [/* 28 vars */]) = -1 ENOEXEC (Exec format error)

So then, the execution proceed as Stephane described - default shell is used (in the above code snippet is hard-coded). This nice UNIX FAQ can answer more.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.