[comp.unix.programmer] How to provide Shell Escape from a C program?

rudrak@saphir.cl.bull.fr (Rudrakshala Purushotham) (11/19/90)

I want to provide shell escape feature from a C program. But I am having 
some problems with the following code:

	shell_escape (command);
	char *command;
	{
		...
		char *args [MAX_ARGS];

		args [0] = "/bin/sh";
		args [1] = "-c";

		for (i = 2; i < MAX_ARGS && (s = strtok (command, " \t")); i++)
			args [i] = strsave (s);
		
		args [i] = NULL;

		if (fork () > 0)	{
			execv ("/bin/sh", args);
			perror ("execv");
			_exit (1);
		}

		wait (&status);
		...

I am using C shell and System V. I tried `/bin/ls -l -R' as input to
shell_escape (), my .login file gets executed here and /bin/ls is executed
(without -l -R) options. 

Can some body help me with this problem? 

Thanks

-- Purushotham 
-- 
R. Purushotham                          Email: rudrak@saphir.cl.bull.fr
F7 1 D5 BULL SA                         Tel Off: 34627000 ext 3928
78340 Les Clayes sous Bois  FRANCE      Tel Res: 34604752
=======================================================================

cs@cci632.UUCP (Craig Schmackpfeffer) (11/20/90)

In article <349@clbull.cl.bull.fr> rudrak@saphir.cl.bull.fr (Rudrakshala Purushotham) writes:
>I want to provide shell escape feature from a C program. But I am having 
>some problems with the following code:
>	shell_escape (command);
			      ^
			      |
>	char *command;
>	{
>		char *args [MAX_ARGS];
>
>		args [0] = "/bin/sh";
>		args [1] = "-c";
>
>		for (i = 2; i < MAX_ARGS && (s = strtok (command, " \t")); i++)
>			args [i] = strsave (s);
>		
>		args [i] = NULL;
>
>		if (fork () > 0)	{
			    ^^^
			    |||
>			execv ("/bin/sh", args);
>			perror ("execv");
>			_exit (1);
>		}
>
>		wait (&status);
>
>I am using C shell and System V. I tried `/bin/ls -l -R' as input to
>shell_escape (), my .login file gets executed here and /bin/ls is executed
>(without -l -R) options. 

>-- Purushotham 


My first question would be "why re-invent the wheel?".  You could use the
system() call and not worry about any of the parsing and fork/exec'ing.
In fact, it your function cannot work properly because you are having the
parent process do the exec instead of the child.  

You only get the output of ls (with no -l -R) because the shell wants to 
parse the command itself.

Here's how the function could look:

	shell_escape (command)
	char *command;
	{
		char *args [MAX_ARGS];

		args [0] = "/bin/sh";
		args [1] = "-c";
		args [2] = command;
		args [3] = NULL;

		switch (fork())  {
		    case 0:		/* child */
				execv("/bin/sh", args);
		    case -1:		/* fork error or exec fallthrough */
				perror("fork/exec");
				_exit(1);
		    default:		/* parent */
				wait(&status);
		}
	}

		--  or better yet  --

	system(command);

Craig

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Me:          Craig Schmackpfeffer   (another damn yankee)
Disclaimer:  Disclaim'er? I don't even know her!
Address:     ccird1!cs@cci632.UUCP             			Go Bills!
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

weimer@ssd.kodak.com (Gary Weimer) (11/20/90)

In article <349@clbull.cl.bull.fr> rudrak@saphir.cl.bull.fr (Rudrakshala Purushotham) writes:
>I want to provide shell escape feature from a C program. But I am having 
>some problems with the following code:
>
>		for (i = 2; i < MAX_ARGS && (s = strtok (command, " \t")); i++)
>			args [i] = strsave (s);
>
>I am using C shell and System V. I tried `/bin/ls -l -R' as input to
>shell_escape (), my .login file gets executed here and /bin/ls is executed
>(without -l -R) options. 
>
>Can some body help me with this problem? 

From the man page for strtok:

     strtok() considers the string s1 to consist of a sequence of
     zero  or  more text tokens separated by spans of one or more
     characters from the separator string  s2.   The  first  call
     (with  pointer  s1 specified) returns a pointer to the first
     character of the first token, and will have written  a  null
     character  into s1 immediately following the returned token.
     The function keeps track  of  its  position  in  the  string
     between separate calls, so that subsequent calls (which must
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     be made with the first argument a NULL  pointer)  will  work
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     through  the string s1 immediately following that token.  In
     this way subsequent calls will work through  the  string  s1
     until no tokens remain.  The separator string s2 may be dif-
     ferent from call to call.  When no token remains  in  s1,  a
     NULL pointer is returned.

norm@oglvee.UUCP (Norman Joseph) (11/21/90)

In <349@clbull.cl.bull.fr> rudrak@saphir.cl.bull.fr (Rudrakshala Purushotham)
writes:

>I want to provide shell escape feature from a C program. But I am having 
>some problems with the following code:

You are doing more work than you have to.  Your "shell_escape( command )"
has already been written for you.  It is called "system()".  Look for it
in your manual.  As for your code:

>	char *args [MAX_ARGS];
>	args [0] = "/bin/sh";
>	args [1] = "-c";

>	for (i = 2; i < MAX_ARGS && (s = strtok (command, " \t")); i++)
>		args [i] = strsave (s);

>	args [i] = NULL;

To properly use strtok() you only pass it the string you want to parse
once.  To return successive tokens from the string, the first argument
should be a null pointer in all successive calls.  The idiom goes
something like this:

	s = strtok( command, " \t" );
	args[2] = strsave( s );
	for ( i = 3;  i < MAX_ARGS && ( s = strtok( NULL, " \t" ));  i++ )
		args[i] = strsave( s );
	args[i] = NULL;
-- 
Norm Joseph - (amanue!oglvee!norm)           cgh!amanue!oglvee!norm@dsi.com, or
  Oglevee Computer Systems, Inc.             ditka!oglvee!norm@daver.bungi.com
                                   ---<*>---
      "Shucking Usenet oysters in pursuit of a pearl."  --  Bill Kennedy

md@sco.COM (Michael Davidson) (11/22/90)

In article <349@clbull.cl.bull.fr> rudrak@saphir.cl.bull.fr (Rudrakshala Purushotham) writes:
>I want to provide shell escape feature from a C program. But I am having 
>some problems with the following code:
>
>	shell_escape (command);
>	char *command;
>	{
>		...
>		char *args [MAX_ARGS];
>
>		args [0] = "/bin/sh";
>		args [1] = "-c";
>
>		for (i = 2; i < MAX_ARGS && (s = strtok (command, " \t")); i++)
>			args [i] = strsave (s);
>		
>		args [i] = NULL;
>
>		if (fork () > 0)	{
>			execv ("/bin/sh", args);
>			perror ("execv");
>			_exit (1);
>		}
>
>		wait (&status);
>		...

Well, I don't know exactly what is going wrong, but I have a few comments ...

First, why not just use "system()"?
There *are* sometimes good reasons for NOT using system(), but I don't
see any evidence in your code that you are trying to do anything clever
that wouldn't work just fine with system().

One comment on the code itself - do you *really* intend that the
parent process should be the one that exec's the shell?

Something like this would be more appropriate:

		if ((pid = fork()) == 0) {	/* child */
			execv("/bin/sh", args);
			perror("execv");
			_exit(1);
		} else if (pid > 0) {		/* parent */
			int	(*sigint)();
			int	r;

			sigint = signal(SIGINT, SIG_IGN);

			while ((r = wait(&status)) != -1)
				if (r == pid)
					break;

			signal(SIGINT, sigint);
		} else
			perror("fork");

mday@iconsys.icon.com (Matt Day) (11/22/90)

Why don't you use the system() routine rather than reinventing the wheel with
a routine that essentially does the same thing?  system() is more portable
to non-UNIX systems and it makes your source code smaller and clearer.
-- 
- Matt Day, Sanyo/Icon, mday@iconsys.icon.com || uunet!iconsys!mday

guy@auspex.auspex.com (Guy Harris) (11/24/90)

>>Can some body help me with this problem? 
>
>From the man page for strtok:

That's certainly *A* problem with his code, but far from the most
fundamental one, and not the one that's screwing him up.  The problem
that's screwing him up, as others have noted, is that the "-c" flag to
the shell takes *one* argument, which is a character string containing
the command to be executed, in its entirety; his use of "strtok()" to
try to carve the command up into tokens, and pass each token as an
individual argument to the shell, is incorrect - and would still be
incorrect even if he were correctly calling "strtok()" with a NULL
pointer as the first argument in all but the first call.