[comp.lang.c] how do I exec

llj@kps.UUCP (Leif Ljung /DP) (06/27/90)

I have a program that I want to do a general exec(2) sometimes
executing a binary program, sometimes a shell-script preferably
using PATH.
Say I have the program `prog' - if this is a script I add the
'#! /bin/sh' at the top. Can I exec(2) that? No.

The only way I have been able to exec the script is by executing
a one-liner containing  `#! /bin/sh /path/prog.  '

But this is not what I want. Not only is the first line limited in 
length - 32 chars according to exec(2) - it also requires a secondary 
file that I do not want, I just want to feed the script itself into 
/bin/sh using the exec(). Is there a way to do this?

The system is a Pyramid running BSD4.3.
Any clues?

--leif
---------------
Leif Ljung, Kuwait Petroleum, Stockholm, SWEDEN
(llj@kps.se||uunet!mcsun!sunic!kps!llj)

cookson@helios.mitre.org (Cookson) (06/28/90)

In article <661@kps.UUCP> llj@kps.se (Leif Ljung /DP) writes:
>I have a program that I want to do a general exec(2) sometimes
>executing a binary program, sometimes a shell-script preferably
>using PATH.
>Say I have the program `prog' - if this is a script I add the
>'#! /bin/sh' at the top. Can I exec(2) that? No.
>
>The only way I have been able to exec the script is by executing
>a one-liner containing  `#! /bin/sh /path/prog.  '
>
>But this is not what I want. Not only is the first line limited in 
>length - 32 chars according to exec(2) - it also requires a secondary 
>file that I do not want, I just want to feed the script itself into 
>/bin/sh using the exec(). Is there a way to do this?
>
Well, I'm not sure if this is what you want,  but I'll give it a try.
If I uderstand the question, you want a 'C' program to be able to
exec.  The file that it exec's can be either an executeable, or
a shell script, and you want to be able to specify just the filename
to be execed, and have exec look it up in the PATH environment
variable.  Yes??  Ok if that is the case, then just do this:

execlp("file_to_be_execed", "file_to_be_execed", (a list of arguments, if
any, separated by commas), (char *) 0);

That should do the trick.

Hope this helps,
Dean


% Dean Cookson				  $ Anyone can be taught to sculpt    %
% dcookson@mitre.org			    $ Michaelangleo would have had to %
% {devax,et al..}!linus!mbunix!helios!cookson $ have been taught how not to.  %
% Disclaimer: My opinions are my own, and       $ The same is true of great   %
%       are of questionable sanity                $ programmers               %

moliver@pyrshadow (Mike Oliver) (06/28/90)

This really isn't (an answer to) a C question; followups have been
redirected to comp.unix.questions.

In article <661@kps.UUCP> llj@kps.se (Leif Ljung /DP) writes:
>I have a program that I want to do a general exec(2) sometimes
>executing a binary program, sometimes a shell-script preferably
>using PATH.
>Say I have the program `prog' - if this is a script I add the
>'#! /bin/sh' at the top. Can I exec(2) that? No.

I don't have a problem doing this.  I don't know how you're managing
the PATH variable, but if you provide the coorect relative path of the
target file you can exec() it regardless of whether it's a program or
an interpreter script.

There's a tiny demo program on the end of this post - you might want to
compare it against your code.  My favourite way to get this wrong is to
forget that *argv[0] in the exec() call should be the name of the
executable, usually the same as the path that you supply as the first
parameter to exec().

If you're still stuck after that, mail me your code and I'll see if I
can find what might be causing the problem.

>The system is a Pyramid running BSD4.3.

Meaning that it's running OSx and you're working in the ucb universe ?
If you're building in the att universe you should be aware that the
ability to exec() a script is configurable, and it defaults to being
disabled.  Don't ask me why, I don't know.  This hinges on the universe
that your program was built under, not on the universe in which it
runs.  Check your makefile to make sure the build isn't running under
att.  If that's the case you'll get an "Exec format error" when you try
to exec a script.

Demo program and sample script are below, hit "n" if you've had enough
already.

 --- 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ---

/*	This program will exec() its first argument, passing any
*	additional arguments to the exec()'d program.
*
*	Try "a.out /bin/ls -l" for starters.  Then try the script
*	below, which will print "hello world" and then print its
*	arguments.
*/

#include <stdio.h>
#include <sys/wait.h>

main( argc, argv )
int	argc;
char	**argv;
{
	extern	char **environ;

	union	wait waitstatus;
	char	*cmdpath;

	if ( argc < 2 )
	{
		/*	Not enough info on the command line.
		*/

		fprintf( stderr, "%s: %s <command> [args ...]\n",
						argv[0], argv[0] );
		exit( -1 );
	}

	switch ( fork() )
	{
		case 0:		/* This is the child */
			cmdpath = *(++argv);
			(void) execve( cmdpath, argv, environ );
			perror( "child ... execve() failed" );
			break;
		case -1:	/* This is the parent, and the fork failed */
			perror( argv[0] );
			break;
		default:	/* This is the parent, the fork() worked */
			fprintf( stdout, "parent ... deceased PID is %d\n", 
							wait( &waitstatus ) );
			fprintf( stdout, "       ... status is %d\n", 
							waitstatus.w_status );
			break;
	}
}

 --- 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ---

#!	/bin/sh
# Simple script, just says "hello world" and then prints its arguments.

echo "hello world"
echo $*

 --- 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ---

Cheers, Mike.

moliver@pyramid.com
{allegra,decwrl,hplabs,munnari,sun,utai,uunet}!pyramid!moliver

cpcahil@virtech.uucp (Conor P. Cahill) (06/28/90)

In article <661@kps.UUCP> llj@kps.se (Leif Ljung /DP) writes:
>I have a program that I want to do a general exec(2) sometimes
>executing a binary program, sometimes a shell-script preferably
>using PATH.
>Say I have the program `prog' - if this is a script I add the
>'#! /bin/sh' at the top. Can I exec(2) that? No.

The correct way to do this (one that will work on systems that understand
the "#!" and those that don't) is as follows:

	place "#!/bin/sh" as the first line for the program.

	in the code that you wish to exec the program do the following:


		execvp("prog",argv);  /* argv is setup accordingly */

		/*
		 * if we get here, prog was not executed by the kernel,
		 * so this system doesn't understand "#!" and we must call
		 * the shell ourselves
		 */

		arg_str[0] = '\0';	
		for(i=0; argv[i] != NULL; i++)
			strcat(arg_str,argv[i]);

		sh_argv[0] = "sh";
		sh_argv[1] = "-c";
		sh_argv[2] = arg_str;
		sh_argv[3] = (char *)0;
		execvp("sh",sh_argv);

-c is used so sh will use PATH to find the program.

NOTE: I did not compile or test the above code, your milage may vary.


-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

leo@ehviea.ine.philips.nl (Leo de Wit) (06/28/90)

In article <661@kps.UUCP> llj@kps.se (Leif Ljung /DP) writes:
|I have a program that I want to do a general exec(2) sometimes
|executing a binary program, sometimes a shell-script preferably
|using PATH.
|Say I have the program `prog' - if this is a script I add the
|'#! /bin/sh' at the top. Can I exec(2) that? No.

Yes you can.

|
|The only way I have been able to exec the script is by executing
|a one-liner containing  `#! /bin/sh /path/prog.  '
|
|But this is not what I want. Not only is the first line limited in 
|length - 32 chars according to exec(2) - it also requires a secondary 
|file that I do not want, I just want to feed the script itself into 
|/bin/sh using the exec(). Is there a way to do this?
|
|The system is a Pyramid running BSD4.3.
|Any clues?

Yes, assuming you got the semantics of the exec(2) functions/syscalls
correct, you probably forgot to make the script executable. If not,
show us what your exec() call looks like.

Another hint: put a perror("This is what went wrong"); after the exec()
call. 9 out of 10 times you will see the problem immediately.

    Leo.

caxwgk@pkinbg.UUCP (Wolfgang G. Kuehnel) (06/28/90)

From article <661@kps.UUCP>, by llj@kps.UUCP (Leif Ljung /DP):
> I have a program that I want to do a general exec(2) sometimes
> executing a binary program, sometimes a shell-script preferably
> using PATH.
> Say I have the program `prog' - if this is a script I add the
> '#! /bin/sh' at the top. Can I exec(2) that? No.
> The system is a Pyramid running BSD4.3.
> Any clues?

I remember that we've done this using execve() after forking a program.
The program fork()ed and the child process execve()ed a /bin/sh-script.
We put '#! /bin/sh' at the top line and that worked.
We had to make sure that the arguments to execve() were correct:
*path had to be a full path *containing the file name at the end*
**argv[0] had to be the filename (again)
**envp could be point to NULL
The script had to be executeable for the effective uid of the forked
child process. We were working under SunOs 4.0.3, which should make
no difference to BSD4.3 in this respect.

Greetings
	Wolfgang Kuehnel
	Philips Kommunikations Industrie AG
	Nuernberg / West Germany

martin@mwtech.UUCP (Martin Weitzel) (06/29/90)

In article <1990Jun27.225015.1956@virtech.uucp> cpcahil@virtech.UUCP (Conor P. Cahill) writes:
>In article <661@kps.UUCP> llj@kps.se (Leif Ljung /DP) writes:
>>I have a program that I want to do a general exec(2) sometimes
>>executing a binary program, sometimes a shell-script preferably
>>using PATH.
>>Say I have the program `prog' - if this is a script I add the
>>'#! /bin/sh' at the top. Can I exec(2) that? No.
>
>The correct way to do this (one that will work on systems that understand
>the "#!" and those that don't) is as follows:

Though I usually appreciate Conors experience and good advice, I must
object this time:

>	place "#!/bin/sh" as the first line for the program.

Well, there are some good arguments in favor for a first line
that starts with a colon (:) in shell scripts. At least this
helps in some (IMHO brain damaged) C shells that consider every
text file which starts with a hash (#) to be a C-shell script.
(I've had some discussions in the past, if it was "legal" for
the bourne shell to add #-comments, after the C-shell "invented"
them and obviously claimed exclusive rights to use them ...)

>	in the code that you wish to exec the program do the following:
>
>		execvp("prog",argv);  /* argv is setup accordingly */
>
>		/*
>		 * if we get here, prog was not executed by the kernel,
>		 * so this system doesn't understand "#!" and we must call
>		 * the shell ourselves
>		 */

Wrong: At least in SysV(%) for which I can check this out right now,
the facts are that execvp() executes a shell if the given file
name turns out to be a text file with appropriate x-bits set.

THIS DOESN'T DEPEND ON THE FIRST LINE OF THE TEXT-FILE!

Behavior differs with "execv()", which will not only do *no* path
search, but also never execute a text file, regardless of what the
first line contains (I'm aware the latter was not what Conor claimed).

%: If memory serves, behaviour was the same in SysIII-derived XENIX.

>		arg_str[0] = '\0';	
>		for(i=0; argv[i] != NULL; i++)
>			strcat(arg_str,argv[i]);

WRONG (forgets to embed blanks or some other IFS-seperator) and
DANGEROUS - at least you should count the length of your arguments
and allocate space for arg_str dynamically. (I hope that it was
not meant this way: As I exec anyway, I may leave total desaster
behind me :-).)

>
>		sh_argv[0] = "sh";
>		sh_argv[1] = "-c";
>		sh_argv[2] = arg_str;
>		sh_argv[3] = (char *)0;
>		execvp("sh",sh_argv);
>
>-c is used so sh will use PATH to find the program.

Yes, but this is only *one* of the effects of "-c". Anotherone is that
the shell fully re-interprets the arguments. If this is desirable or
not will generally depend on the problem, but as the method here was
used as substitute for exec-ing a script from the kernel, the behaviour
will differ (Again IMHO; I have no such kernel here to check this, but
I would be surprised if the kernel had not only mechanisms to exec
the program given in the first line, but also to interpret args.)

>NOTE: I did not compile or test the above code, your milage may vary.

*If* we do so many things manually here, why not open the file,
read the first line and exec a given program in case of #!prog
in the way UCB-derivates do it in the kernel?

Any volunteers to write (and compile and test!!) such code?
You should call it something like "ucb_exec" and make the same
functions available that are normaly provided by exec. This
should not be more than one day work for any halfway experienced
C-programmer.
-- 
Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83