[comp.unix.wizards] exec

martin@mqcomp.oz (Martin Foord) (04/20/89)

Everytime I seem to exec() a shell script that is executable I have problems
(execepting if I use execlp() or execvp()).
For example, the program (where /bin/true is an executable shell script) :

% cat foo.c
#include <stdio.h>

char *arg="true";

main()
{
	char *args[2];

	args[1]=arg;
	args[2]=NULL;
	execv("/bin/true",args);
	printf("Can't execute process\n");
}
% foo
Can't execute process

	Why is this ? Is it because /bin/true is a shell script because
othere executable non-shell scripts work fine.

maf.

maart@cs.vu.nl (Maarten Litmaath) (04/22/89)

martin@mqcomp.oz (Martin Foord) writes:
\Everytime I seem to exec() a shell script that is executable I have problems
\(execepting if I use execlp() or execvp()).

The shell script doesn't start with the magic number #! or your kernel
doesn't recognize it, hence you get ENOEXEC ("Exec format error"), as you
would have known, had you put a call to perror() in your C program.
On the other hand, after having received an exec format error EXECVP()
decides the file must be a shell script, and executes /bin/sh with the file
as first argument. Hence execvp() works.

\...
\	execv("/bin/true",args);
\	printf("Can't execute process\n");

Thanks a lot for that *VERY* helpful error message! :-(
Instead do:

	perror("/bin/true");

If you are a Real Programmer, add:

	*(char *) -1 = 0xDEAD;

and read the hexdump with breakfast! :-)
-- 
 "If it isn't aesthetically pleasing, |Maarten Litmaath @ VU Amsterdam:
  it's probably wrong." (jim@bilpin). |maart@cs.vu.nl, mcvax!botter!maart

ugkamins@sunybcs.uucp (John Kaminski) (04/22/89)

In article <647@mqcomp.oz> martin@mqcomp.oz (Martin Foord) writes:
>
>Everytime I seem to exec() a shell script that is executable I have problems
>(execepting if I use execlp() or execvp()).
>For example, the program (where /bin/true is an executable shell script) :
[ short example of a C program that calls execv() deleted ]
>	Why is this ? Is it because /bin/true is a shell script because
>othere executable non-shell scripts work fine.
>
>maf.

Bingo.  When exec() is invoked, it expects to find a "magic number" in the
beginning of the file somewhere that identifies to the kernel that this is
something that can be loaded into core and be executed directly by the
processor.  When shell scripts are invoked, the shell must be exec'ed some-
how to interpret the text in the file.  I believe that the one exception
on most systems is the case where the file length is zero, whereby the
process just returns the true exit status.  I notice that true on a SysV
system is length zero.

jiii@visdc.UUCP (John E Van Deusen III) (04/23/89)

To top off this discussion, please note that an explanation of the
special characteristics of execlp and execvp in duplicating the actions
of the shell are to be found somewhat obscurely in the exec(2) manual
entry.

If you do not want to use either of those variants, you have to do the
following, (I have used execl for clarity):

	execl("/bin/sh", "sh", "-c", "/bin/true", 0);

--
John E Van Deusen III, PO Box 9283, Boise, ID  83707, (208) 343-1865

uunet!visdc!jiii

ka@june.cs.washington.edu (Kenneth Almquist) (04/23/89)

jiii@visdc.UUCP (John E Van Deusen III) writes:
> If you do not want to use [execlp or execvp], you have to do the
> following, (I have used execl for clarity):
>
> 	execl("/bin/sh", "sh", "-c", "/bin/true", 0);

More generally, you can write:

	execl(program, (char *)0);
	if (errno == ENOEXEC) {		/* it's a shell procedure */
		execl("/bin/sh", "sh", program, (char *)0);
		perror("/bin/sh");
	} else {
		perror(program);
	}
	exit(2);

Note that if you write "0" in place of "(char *)0" this code will no
longer be machine independent
				Kenneth Almquist

guy@auspex.auspex.com (Guy Harris) (04/23/89)

>Bingo.  When exec() is invoked, it expects to find a "magic number" in the
>beginning of the file somewhere that identifies to the kernel that this is
>something that can be loaded into core and be executed directly by the
>processor.

Correct.  Some more modern UNIX systems do, however, have a magic
number, namely "#!", that tells the system "the rest of this line is a
pathname of an interpreter program, such as a shell, and an
optional argument; run *that* program and give it the name of *this*
file as an argument, after the optional argument if it's present."  This
allows you to "exec" shell scripts, "awk" scripts, Makefiles, "ftp"
scripts, etc. (and, if you're feeling *REALLY* perverse, edit files by
typing their name as a command...).

>When shell scripts are invoked, the shell must be exec'ed somehow
>to interpret the text in the file.

Yup.  On systems with "#!", this can be done by using it; the pathname
of the shell is in the "#!" line (e.g., "#! /bin/sh").  On systems
without "#!", this is generally done by having the code that calls
"exec" recognize the ENOEXEC error (which says "well, you had all the
right permissions to run this file, but it doesn't have a magic number)
and then "exec" the shell, handing it the name of the script as an
argument.

>I believe that the one exception on most systems is the case where the
>file length is zero, whereby the process just returns the true exit
>status.

Nope.  No UNIX system I know of makes such an exception; the reason a
zero-length file can be executed by the shell is that the shell uses the
ENOEXEC trick listed above.

>I notice that true on a SysV system is length zero.

Not in later versions; it's filled up with a humongo copyright notice
that looks *REALLY SILLY*, given that the entire file consists of its
copyright notice!

jiii@visdc.UUCP (John E Van Deusen III) (04/25/89)

In article <7954@june.cs.washington.edu> (Kenneth Almquist) writes:
> (John E Van Deusen III) writes:
>> If you do not want to use [execlp or execvp], you have to ...
>>
>> 	execl("/bin/sh", "sh", "-c", "/bin/true", 0);
>
> More generally, you can write:
>
>	execl(program, (char *)0);
>	if (errno == ENOEXEC) {		/* it's a shell procedure */
>		execl("/bin/sh", "sh", program, (char *)0);
>		perror("/bin/sh");
>	} else {
>		perror(program);
>	}
>	exit(2);

With respect to the construct I used, (#1), Mr. Almquist's code, (#2),
is NOT more "general"; assuming, of course, that program is substituted
for "/bin/true" and (char *)0 for 0.

Construct #2 will fail, that is exit(2), if program is not an absolute
or relative pathname and is not a file in the current directory.
Construct #1 uses the shell to resolve pathname(s) and can handle
situations where program is not even a file name; for instance
"date | cut -d: -f2".  Construct #2 will correctly handle the "special"
case where program is a command name, not a path name; it is intended
for the command to be executed from the current directory; and that is
NOT the way PATH is set up.
--
John E Van Deusen III, PO Box 9283, Boise, ID  83707, (208) 343-1865

uunet!visdc!jiii

ka@june.cs.washington.edu (Kenneth Almquist) (04/26/89)

jiii@visdc.UUCP (John E Van Deusen III) writes:
> [The sample exec code in <7954@june.cs.washington.edu>] will fail, that
> is exit(2), if program is not an absolute or relative pathname and is
> not a file in the current directory.

That was intentional:  the initial problem was to do the equivalent of
an "exec" on a shell procedure.  If you want a path search in addition,
then you can use execlp or execvp.
					Kenneth Almquist

mhoffman@infocenter.UUCP (Mike Hoffman) (04/28/89)

in article <2351@solo11.cs.vu.nl>, maart@cs.vu.nl (Maarten Litmaath) says:
> 
> martin@mqcomp.oz (Martin Foord) writes:
> \Everytime I seem to exec() a shell script that is executable I have problems
> \(execepting if I use execlp() or execvp()).
> 
> The shell script doesn't start with the magic number #! or your kernel
> doesn't recognize it, hence you get ENOEXEC ("Exec format error"), as you
> ...
> \...
> \	execv("/bin/true",args);
> \	printf("Can't execute process\n");
> 

How about:

	args[0] = "sh";
	args[1] = "/bin/true";
	args[2] = NULL;
	execv("/bin/sh",args);

for files that do not begin with #! and are perhaps write-protected? Maybe
the shell script is owned by root but the applications programmer doesn't
have su priveleges.

(Shame on the sysadm for writing shell scripts that don't start with #! :-)

Michael J. Hoffman                                    Voice: (407)255-8116
Manufacturing Engineering                               FAX: (407)255-8186
Encore Computer Corporation                           Email: mhoffman
                                                     USnail: 100 N. Babcock St.
UUCP: {uunet,codas!novavax,sun,pur-ee}!gould!mhoffman        Melbourne, Fl 32935
	"Curiouser and Curiouser" -- Alice