[comp.unix.wizards] Weird things in csh

maart@cs.vu.nl (Maarten Litmaath) (02/25/88)

Has anyone noticed the following oddities?
1)
	% a.out < a.out
	a.out: Text file busy.
	%

Why shouldn't a process be able to read its text file?
2)
	% cat ~/.cshrc
	echo echo hello
	% cp /bin/echo .
	% ./echo > echo
	hello: Command not found.
	% cat echo
	echo hello
	hello
	%

What kinda weird thing is going on here?
Shouldn't there be an error message "Text file busy."
in this case?
-- 
"Brothers & sisters, praise the Lord, |Maarten Litmaath @ Free U Amsterdam:
send your money, Rolls Royce > Ford." |maart@cs.vu.nl, mcvax!botter!ark!maart

sjoerd@cs.vu.nl (Sjoerd Mullender) (02/25/88)

In article <1193@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>Has anyone noticed the following oddities?
>1)
>	% a.out < a.out
>	a.out: Text file busy.
>	%
>
If you are debugging a.out then this could happen.  Especially if you have set
breakpoints.

>Why shouldn't a process be able to read its text file?
It can!

>2)
>	% cat ~/.cshrc
>	echo echo hello
>	% cp /bin/echo .
>	% ./echo > echo
>	hello: Command not found.
>	% cat echo
>	echo hello
>	hello
>	%
When executing the command "./echo > echo" the shell will first create the
file echo.  It then tries to exec ./echo.  The kernel refuses (it's not an
object anymore, it an empty file) and the shell will start interpreting the
script.  The shell will fork off a new shell to do the work.  This new shell
will first execute ~/.cshrc which contains the command "echo echo hello".
So the string "echo hello" is echoed into the output file (echo).  Then the
shell really starts executing the script.  It now contians the string
"echo hello", so that command is executed.  The result (the string "hello")
is appended to the output file (that file was never closed or re-created).
After that the next line of the script is "hello" and the shell is not able
to find the command.  Hence the error message.

>
>What kinda weird thing is going on here?
>Shouldn't there be an error message "Text file busy."
>in this case?
Nothing that cannot be explained.

Why was this cross-posted to comp.unix.wizards?
-- 
Sjoerd Mullender
sjoerd@cs.vu.nl
When this doesn't work try sending via seismo.css.gov or mcvax.uucp.

ugfailau@sunybcs.uucp (Fai Lau) (02/27/88)

In article <1193@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>Has anyone noticed the following oddities?
>1)
>	% a.out < a.out
>	a.out: Text file busy.
>	%
>
	This is not as bad as this,

	cat file | grep john > file

	There will be nothing withsoever in file "file". UNIX is kinds
strange when you put the same file name at both "ends" of a pipeline or
redirect. Aviod doing it or sooner or later you'll find out some files
you're working on would be strangely deleted against your wish. Speaking
from experience here.

>Why shouldn't a process be able to read its text file?
>2)
>	% cat ~/.cshrc
>	echo echo hello
>	% cp /bin/echo .
>	% ./echo > echo
>	hello: Command not found.
>	% cat echo
>	echo hello
>	hello
>	%
>
	Find the source code echo.c, compile it, put the executable in you
directory, and the problem would go away. The only explanation
I can give for the phenomonen is that for some reason csh wants to
parse .cshrc, and somehow thinks hello is a command. I'm using
tcsh in /usr/local/bin and I got the error message "/bin/tcsh: not
found" until I restarted with csh did I got the behavior you
described.

Fai Lau
SUNY at Buffalo (The Arctic Wonderland)
UU: ..{rutgers,ames}!sunybcs!ugfailau
BI: ugfailau@sunybcs INT: ugfailau@joey.cs.buffalo.EDU

pdb@sei.cmu.edu (Patrick Barron) (02/27/88)

In article <1193@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>Has anyone noticed the following oddities?
>1)
>	% a.out < a.out
>	a.out: Text file busy.
>	%
>Why shouldn't a process be able to read its text file?

   I can't reproduce this under Ultrix 1.2.

>2)
>	% cat ~/.cshrc
>	echo echo hello
>	% cp /bin/echo .
>	% ./echo > echo
>	hello: Command not found.
>	% cat echo
>	echo hello
>	hello
>	%
>
>What kinda weird thing is going on here?
>Shouldn't there be an error message "Text file busy."
>in this case?

This is actually pretty simple.  When you redirect output to "echo",
the file gets overwritten *before* "./echo" is executed.  So, it ends
up trying to execute an empty file.  Since it doesn't have a valid
magic number, and the execute bits are set (copying /bin/echo left
them set, and overwriting the file didn't change that), it's assumed
to be a shell script.  Your .cshrc file gets executed, echoing
"echo hello" using the C-shell's built-in echo.  That goes into
the file "./echo", and the shell then starts to execute commands from
"./echo".  (Why isn't the Bourne shell used to do this?  The file does
not start with "#!/bin/csh")  That file now contains "echo hello", so
the shell's "echo" echoes "hello".  Remember, standard output is still
the "./echo" file, so that gets tacked on to the end.  The shell then
reads the next command, which is "hello", and it complains "Command not
found."

--Pat.

ka@june.cs.washington.edu (Kenneth Almquist) (02/29/88)

> % a.out < a.out
> a.out: Text file busy.
> %

The basic problem that the "text file busy" error was designed to solve
is this.  When several copies of the same program are run concurrently,
UNIX will only create a text segment for the first copy.  Subsequent
invocations of the program will reuse this text segment rather than
creating a new one.  The problem with this approach is that if a program
is modified, the system may continue to reuse the old version of the
text segment rather than using the new version.  This problem was avoided
by making the following actions illegal:

	1)  Opening a file that is being executed.
	2)  Executing an open file.
	3)  Deleting the last link to a file that is being executed.

So when you say "a.out < a.out", the shell opens a.out and then tries
to execute it.  The attempt to execute it fails due to rule 2.  You
are presumably running some version of 2 BSD; both 4 BSD and System V
have relaxed rules 1 and 2 to allow files that are being executed to
be opened for reading and vice versa.

My view is that these rules should be eliminated all together.  They are
inconvenient, since they make program installation harder.  They are
inconsistent with the rest of the design of the UNIX file system calls,
which permits any operation to be performed at any time (even operations
like deleting other people's current working directories).  It is a
bit simpler to enforce these rules than to design things so that they
are unnecessary, but given the growth of the kernel in recent years it
is hard to justify taking some of the complexity of supporting shared
text out of the kernel and pushing it onto the users.
				Kenneth Almquist
				ka@june.cs.washington.edu

maart@cs.vu.nl (Maarten Litmaath) (03/01/88)

In article <4320@june.cs.washington.edu> ka@june.cs.washington.edu (Kenneth Almquist) writes:
\> % a.out < a.out
\> a.out: Text file busy.
\> %
\
\...  You
\are presumably running some version of 2 BSD; both 4 BSD and System V
\have relaxed rules 1 and 2 to allow files that are being executed to
\be opened for reading and vice versa.

Our version is 4.1BSD!
-- 
Which of Santa Claus and God          |Maarten Litmaath @ Free U Amsterdam:
             is more likely to exist? |maart@cs.vu.nl, mcvax!botter!ark!maart

rcodi@yabbie.rmit.oz (Ian Donaldson) (03/02/88)

In article <1193@ark.cs.vu.nl>, maart@cs.vu.nl (Maarten Litmaath) writes:
> 	% a.out < a.out
> 	a.out: Text file busy.

You didn't say which system.

Yep, its a kernel bug in some pre 4.[23]bsd systems.  Exec processing
only checks for open files rather than files open for writing when
determining if the file is busy.

Its also a -big- security bug in such systems that leave most system binaries
publicly readable.  The workaround is to -not- make such binaries publicly
readable.  Simple.

ie: 

chmod o-r `file /bin/* /usr/bin/* /usr/ucb/* |egrep "demand|pure" |cut -d: -f1`

(this won't work right if your binaries are OMAGIC types, in which case you
will probably need to do it by hand, since grepping for the word "executable"
might discover shellscripts too)
 
> Why shouldn't a process be able to read its text file?
> 2)
> 	% cat ~/.cshrc
> 	echo echo hello
> 	% cp /bin/echo .
> 	% ./echo > echo
> 	hello: Command not found.
> 	% cat echo
> 	echo hello
> 	hello
> 	%
> 
> What kinda weird thing is going on here?

You are paving the road before you tread on it.

I'll elaborate:

> 	% cp /bin/echo .
	
	Creates a binary "./echo".  Fine.

> 	% ./echo > echo

	First clobbers "./echo" then csh tries to execute it with stdout
	sent to it.  execv() fails because the kernel sees an empty file, 
	so csh tries to execute it as a script.

	Since a new csh is started up to execute it, it reads your ".cshrc"
	and executes that first.  The first line says "echo echo hello"
	and since "echo" is a csh builtin, it echo's the words "echo hello"
	to stdout, which gets written onto "./echo".  Csh then finishes
	executing your ".cshrc" and then executes "./echo", seeing
	the words "echo hello".  Again it is a builtin, so the word
	"hello" gets written to stdout ("./echo").  Csh then reads
	the next line of "./echo" which is "hello".  Obviously not
	a known command so it complains to stdout (the terminal).
	It then runs out of things to do and stops.

	A great puzzle!
	
> Shouldn't there be an error message "Text file busy."
> in this case?

Nope.  You clobbered "./echo" before excuting it.  There is no text
there anymore.

Ian D

maart@cs.vu.nl (Maarten Litmaath) (03/05/88)

In article <716@yabbie.rmit.oz> rcodi@yabbie.rmit.oz (Ian Donaldson) writes:
\In article <1193@ark.cs.vu.nl>, maart@cs.vu.nl (Maarten Litmaath) writes:
\> 	% a.out < a.out
\> 	a.out: Text file busy.
\
\You didn't say which system.

4.1BSD!

\Yep, its a kernel bug in some pre 4.[23]bsd systems.  Exec processing
\only checks for open files rather than files open for writing when
\determining if the file is busy.
\
\Its also a -big- security bug in such systems that leave most system binaries
\publicly readable.  The workaround is to -not- make such binaries publicly
\readable.  Simple.

You're completely right.

\...
\> 	% ./echo > echo
\
\	First clobbers "./echo" then csh tries to execute it with stdout
\	sent to it.  execv() fails because the kernel sees an empty file, 
\	so csh tries to execute it as a script.
\
\	Since a new csh is started up to execute it, it reads your ".cshrc"
\	and executes that first.

Normally a Bourne shell is started up (default for shell scripts), but
- as Ronald Khoo mentioned in a reply I got - if the file size is zero,
csh appears to execute the shell mentioned in the internal variable
"shell":

	% : > gnome
	% chmod 755 gnome
	% set shell=hobgoblin
	% gnome
	hobgoblin: No such file or directory
	% echo '' > gnome
	% ls -l gnome
	-rwxr-xr-x 1 3754          1 Mar  5 15:23 gnome
	% gnome
	%

\	A great puzzle!

Thanks! :-)
-- 
Which of Santa Claus and God          |Maarten Litmaath @ Free U Amsterdam:
             is more likely to exist? |maart@cs.vu.nl, mcvax!botter!ark!maart