How does Unix kernel find /bin/sh?
John Chambers
jc at minya.UUCP
Sun Aug 27 01:03:27 AEST 1989
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
More information about the Comp.unix.wizards
mailing list