[comp.unix.wizards] How does Unix kernel find /bin/sh?

jc@minya.UUCP (John Chambers) (08/26/89)

Well, here I am again, with yet another puzzle.  It keeps coming to
my attention that there are still Unices about that don't understand
the "#!" notation.  You'd think by now that everyone would realize
what a Good Idea this is, and it'd have been installed everywhere.
I realized how slow things can be when last week I tried some of
my scripts for for first time on a brand-new, out-of-the-box Sys/V
release 3.something, and to my surprise, the ones that started with
lines like "#!/bin/awk" or "#!/bin/msh" (that's my menu shell) got
some funny Bourne-shell diagnostics (rather than the awk or msh
diagnostics that I expected ;-).

Well, I decided to try to solve this problem once and for all, in
the obvious way.  I wrote myself a shell that I call ish (for Indirect
SHell), and installed it in /bin, and also linked /bin/sh to /bin/bsh.
After a bit of testing, I did
	rm /bin/sh
	ln /bin/ish /bin/sh
and this should have solved the problem.  What ish does, of course,
is examines its command-line args, figures out what file the caller
intended to exec, and opens it for reading.  If it starts with "#!",
ish does the obvious thing.  It is, of course, quite paranoid about
getting things wrong, so if it gets at all puzzled about what it
should do, it just punts the job to /bin/bsh.  For true binary files,
of course, it never gets invoked, because the kernel knows how to
handle that case.

The problem is simple:  when I exec a script directly, the kernel
doesn't run /bin/sh, it runs /bin/bsh!  I can prove this easily,
since ish always writes a line to an audit trail showing when and
how it was called.  This code isn't conditional; there is no way
that ish can work without writing to the audit trail.  When I boot
the machine, the audit trail shows a whole lot of entries from all
the subprocesses started by /etc/*rc, and whenever anyone calls
system(), the audit trail gets a new line.  So basically the trick
works.

But there seems to be some sort of deal going between the Bourne
shell and the kernel, so that when bsh starts up, it tells the
kernel "Don't pay any attention to /bin/sh; I'm the real shell",
and the kernel believes it.  Does anyone know how this works?  I'd
like to see if my program can intercede and convince the kernel that
it's the shell (after all, it *is* /bin/sh).  

I'd like an RTFM response or two.  I've spent some time poring through
some FMs, and so far I haven't even found any admission that the Unix
kernel knows how to run scripts.  The descriptions of the exec*() calls
only talk about executable binary files, as does Bach's book.  I've
yet to see any mention of the "#!" notation in any document anywhere;
I've picked it up by word-of-mouth (including a bunch of flames in
several newsgroups about the idiots who don't use it correctly; such
flames seems to be the best way to learn how it works. :-)

Can someone explain what's going on, and why my imposter shell (hey, 
maybe that's what "ish" stands for |^) gets invoked correctly in every 
case except when a script is execed directly?

I am truly bemused by this one.

[A free copy of ish will be emailed (if possible;-) to the first five
individuals who correctly answer the question...]

-- 
#echo 'Opinions Copyright 1989 by John Chambers; for licensing information contact:'
echo '	John Chambers <{adelie,ima,mit-eddie}!minya!{jc,root}> (617/484-6393)'
echo ''
saying

cpcahil@virtech.UUCP (Conor P. Cahill) (08/27/89)

In article <5@minya.UUCP>, jc@minya.UUCP (John Chambers) writes:
> The problem is simple:  when I exec a script directly, the kernel
> doesn't run /bin/sh, it runs /bin/bsh! 

This is your problem.  The kernel doesn't interpret your command and
run the shell, the shell does.  The shell realizes that you wish to 
execute a sub shell and just forks without having to re-exec himself.  

> But there seems to be some sort of deal going between the Bourne
> shell and the kernel, so that when bsh starts up, it tells the
> kernel "Don't pay any attention to /bin/sh; I'm the real shell",
> and the kernel believes it.  Does anyone know how this works?  I'd
> like to see if my program can intercede and convince the kernel that
> it's the shell (after all, it *is* /bin/sh).  

Like I said, the kernel plays no part in this it is the shell itself.

> Can someone explain what's going on, and why my imposter shell (hey, 
> maybe that's what "ish" stands for |^) gets invoked correctly in every 
> case except when a script is execed directly?

If you want to bypass this you can change the exec to be 
an exec of "/bin/sh script" and you will get what you want, but not
the way that you want it.

-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

wolfgang@mgm.mit.edu (Wolfgang Rupprecht) (08/27/89)

In article <5@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
[ questions on replacing /bin/sh w. a dispatcher for the #!/bin/foo
first line in shell scripts ]
>The problem is simple:  when I exec a script directly, the kernel
>doesn't run /bin/sh, it runs /bin/bsh!  I can prove this easily [...]

Its *not* the kernel.  It is /bin/sh (called /bin/bsh in your system)
that is taking the short-cut.

The real /bin/sh doesn't need to exec a copy of /bin/sh.  It knows
that it is the "True" /bin/sh and simply forks passing the command 
file's ascii contents to the forked copy of itself.

I executing a non-builtin is done roughly like this:

	if (fork == 0)
	{
		execle(command_name, ...);
	    /* what, still here?  Must be a shell script, run it ourselves */
	        top_lev_shell_interpreter(fopen(command_name), ...);
		exit(...);
	}else
		wait(...);

-wolfgang
----
Wolfgang Rupprecht	ARPA:  wolfgang@mgm.mit.edu (IP 18.82.0.114)
TEL: (703) 768-2640	UUCP:  mit-eddie!mgm.mit.edu!wolfgang

debra@alice.UUCP (Paul De Bra) (08/28/89)

In article <5@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
>...
>But there seems to be some sort of deal going between the Bourne
>shell and the kernel, so that when bsh starts up, it tells the
>kernel "Don't pay any attention to /bin/sh; I'm the real shell",
>and the kernel believes it.  Does anyone know how this works?  I'd
>like to see if my program can intercede and convince the kernel that
>it's the shell (after all, it *is* /bin/sh).  
>...

Hmm, RTFM doesn't seem to help, and neither the Bach book indeed.
A possible explanation however is that if the Bourne shell wants to
execute it forks off a child which execs. If the exec fails the shell
thinks the file must be a shell script so it again forks off a child
which does not exec /bin/sh because it thinks it already IS /bin/sh.
It just tries to run the script.
So the kernel has nothing to do with it. The shell just doesn't exec
"itself" because it knows it is "itself".

Hope someone can confirm whether this is indeed what's happening?

Paul.
-- 
------------------------------------------------------
|debra@research.att.com   | uunet!research!debra     |
------------------------------------------------------

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/28/89)

In article <5@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
>the "#!" notation.  You'd think by now that everyone would realize
>what a Good Idea this is, and it'd have been installed everywhere.

I think it's slated for SVR4.0.  However, not everyone agrees that
it's a Good Thing.  It causes problems when porting scripts across
systems, because the various interpreter utilities may not have
identical pathnames across systems.  Also, some of us have relied
on System V NOT understanding the #! convention, and our scripts
will break when SVR4.0 arrives.

>The problem is simple:  when I exec a script directly, the kernel
>doesn't run /bin/sh, it runs /bin/bsh!

No, it's run under whatever shell you're already running.  The
shell forks a subshell to handle the script, after the direct
exec() system call fails.

>I've yet to see any mention of the "#!" notation in any document anywhere;

It's documented in the 4.3BSD PRM under EXECVE(2).

pc@cs.keele.ac.uk (Phil Cornes) (09/04/89)

From article <5@minya.UUCP>, by jc@minya.UUCP (John Chambers):
> Well, here I am again, with yet another puzzle.....
> 
> The problem is simple:  when I exec a script directly, the kernel
> doesn't run /bin/sh, it runs /bin/bsh.....
> 
The exec command is actually a shell built in command so your 'ish'
won't find it and will therefore execute /bin/bsh to sort it out.
> 
> I'd like an RTFM response or two.  I've spent some time poring through
> some FMs, and so far I haven't even found any admission that the Unix
> kernel knows how to run scripts. 
> 
The UNIX kernel *doesn't* know how to execute scripts, that is done by
the C library interface to the exec*() system calls by spawning a shell
to cope with the script.
>
> [A free copy of ish will be emailed (if possible;-) to the first five
> individuals who correctly answer the question...]
> 
Yes please.....?




Phil Cornes          I just called to say .....
-----------*
                     JANET: cdtpc@uk.ac.stafpol.cr83
                     Phone: +44 (0)785 53511 x6058
                     Smail: Staffordshire Polytechnic, Computing Department
                            Blackheath Lane, STAFFORD, ST18 0AD, ENGLAND.