[comp.lang.c] redirect stdin when using execl

ron@sc50.UUCP ( Ron Winnacott ) (06/06/89)

Hello net.

Can anyone tell me how to redirect stdin when I use execl to 
start a new program. The problem I am haveing is this, I am writing
a C program that forks then execl's a new program, But I need to 
use a redirect "<" in the execl call.

execl("/bin/mail","mail","ron","<","/tmp/tfile",NULL);

The fork works fine, and mail starts and has a PPID of 1, but it
just sits there waiting for the letter and EOF to come from stdin.

If you can help me please email to !uunet!attcan!telly!sc50!ron
Thanks ron winacott.

-- 
Ron Winacott.  Unisys Canada, Support center.
               phone- (416) 495-4585
               uucp - uunet!attcan!telly!sc50!ron
*** All comments/statements made are MINE and MINE alone. ***

pc@cs.keele.ac.uk (Phil Cornes) (06/08/89)

From article <414@sc50.UUCP>, by ron@sc50.UUCP ( Ron Winnacott ):
> Hello net.
> 
> Can anyone tell me how to redirect stdin when I use execl to 
> start a new program. The problem I am haveing is this, I am writing
> a C program that forks then execl's a new program, But I need to 
> use a redirect "<" in the execl call.
> 
> execl("/bin/mail","mail","ron","<","/tmp/tfile",NULL);
> 

The  exec() system call does not know anything about I/O redirection, that is
all dealt with by the shell. If you want to redirect the input of mail as above
then you will have to do it yourself in between your fork() call and your
execl() call, something like this:

    if (fork())
    {
        /* parent code */
    }
    else
    {
        close(0);                      /* close child input */
        open("/tmp/tfile",O_RDONLY);   /* open() takes the first available
                                           file descriptor - here 0 - so any
                                          subsequent stdin reads will come
                                          from the file /tmp/tfile */
        execl("/bin/mail","mail","ron",NULL);    /* open file descriptors are
                                                     preserved across exec() */
    }

torsten@pcsbst.UUCP (torsten) (06/09/89)

In article <414@sc50.UUCP> ron@sc50.UUCP ( Ron Winnacott ) writes:
>Hello net.
>
>Can anyone tell me how to redirect stdin when I use execl to 
>start a new program. The problem I am haveing is this, I am writing
>a C program that forks then execl's a new program, But I need to 
>use a redirect "<" in the execl call.
>
>execl("/bin/mail","mail","ron","<","/tmp/tfile",NULL);

	I think this should do it:
	execl("/bin/sh","sh","/bin/mail ron < /tmp/tfile",NULL);

	Or you have to redirekt your stdin after fork and befor
	execl to /tmp/tfile.

	Hope that helps,
			Torsten.
---
Name    : Torsten Homeyer
Company : PCS GmbH, Pfaelzer-Wald-Str. 36, 8000 Munich W-Germany.
UUCP    : ..[pyramid ;uunet!unido]!pcsbst!tho  (PYRAMID PREFERRED!!)
DOMAIN  : tho@pcsbst.pcs.[ COM From rest of world; DE From Europe ]

mpl@cbnewsl.ATT.COM (michael.p.lindner) (06/10/89)

In article <414@sc50.UUCP>, ron@sc50.UUCP ( Ron Winnacott ) writes:
> Hello net.
> execl("/bin/mail","mail","ron","<","/tmp/tfile",NULL);
> Ron Winacott.  Unisys Canada, Support center.

The problem is that the "<" meaning redirection is a feature of the shell,
not the operating system execl() call.  You can call system, described in
section 3 of your programmer's guide, instead, such as:

	char	buffer[32];	/* make sure it's big enough */
	char	*ron = "ron";	/* or whoever */
	char	*tempfile = "/tmp/tfile"; /* or whatever */

	sprintf(buffer, "/bin/mail %s < %s", ron, tempfile);
	if (system(buffer) == 0)
		/* yay! I sent mail! */ ;
	else
		/* boo!  It failed! */ ;

Mike Lindner
attunix!mpl
AT&T
190 River Rd.
Summit, NJ 17901

djones@megatest.UUCP (Dave Jones) (06/10/89)

> In article <414@sc50.UUCP> ron@sc50.UUCP ( Ron Winnacott ) writes:
>>Hello net.
>>
>>Can anyone tell me how to redirect stdin when I use execl to 
>>start a new program. The problem I am haveing is this, I am writing
>>a C program that forks then execl's a new program, But I need to 
>>use a redirect "<" in the execl call.
>>

Under BSD Unix, you use dup2. 

(All you folks who want only to see complete, production quality,
 QA-tested code, wine-soaked and sugar-cured, shut your eyes quick!)



It goes something like this:

if((pid = fork()) == 0)
   { 
         /* This the the spawned process. */
         int file_descriptor = open("foo/bar", "r");

         if(file_descriptor == -1)
	   {
	     perror("foo/bar");
	     _exit(-1);
	   }
	 else
	   { 
	      dup2(file_descriptor,0); /* make it the standard input */
              close(file_descriptor);

	      /* Here, close any of the parent process's file-descriptors that
               * will not be used by this process..
               */

	      /* and now... */
              execl("prog", "arg0", /* etc */ (char*)0);

	      /* It's an error if we get to here.... */
              /* etc.. */
	      _exit(-1);
	   }
    }
else
    { if(pid != -1)
         { /* provide for "reaping" the process. */
           /* See man page for "wait" */
         }
      else
         { /* Could not execl the program... */
         }
    }

vlcek@mit-caf.MIT.EDU (Jim Vlcek) (06/11/89)

In article <5587@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>> In article <414@sc50.UUCP> ron@sc50.UUCP ( Ron Winnacott ) writes:
>>>Can anyone tell me how to redirect stdin when I use execl to 
>>>start a new program.
>
>Under BSD Unix, you use dup2. 

plus a bit of code showing how to fork(), dup2(), and then execl() to
get the desired effect.

I think a much better way, under BSD, is to use freopen() to attach
stdin to the redirected file.  Such redirection is, in fact, precisely
the intended usage of this function call.  Is freopen() a Berklism, or
do you have that in Sys V as well?  I'm almost completely unfamiliar
with the latter.

The original example, passing the string "<" and the redirected file,
was so innocently misguided I really got a laugh out of it.
execl()'ing "/bin/sh" to parse the command, or using system(), on the
other hand, were somewhat less amusing.

Jim Vlcek  (vlcek@caf.mit.edu  uunet!mit-caf!vlcek)

guy@auspex.auspex.com (Guy Harris) (06/13/89)

(Followups redirected to "comp.unix.questions", which was the place
where the original question should probably have appeared.)

>I think a much better way, under BSD, is to use freopen() to attach
>stdin to the redirected file.  Such redirection is, in fact, precisely
>the intended usage of this function call.

When using "freopen()" in this case, the implementations on all the UNIX
systems with which I'm familiar it will do:

	1) an "fclose" of "stdin", which closes the standard input
	   ("standard input" is file descriptor 0, to which "stdin"
	   happens to refer; it's not "stdin" itself) by virtue of doing
	   a "close" on "stdin"s file descriptor (as indicated, FD0,
	   barring some unlikely train of events);

	2) an "open" of the new file, which, if file descriptor 0 was
	   recently closed, should yield 0 as the new file descriptor.

The net result will, in fact, make FD0 point to the new file. 
"freopen()" also does some fiddling with standard I/O information for
"stdin", which is not necessary here (since the "execl" will, on most if
not all UNIX/POSIX implementations, cause the process's data space to be
discarded, and thus discard all the standard I/O information for that
process).

So "freopen()" is basically a convenient wrapper for the underlying
operations the program should do anyway.

>Is freopen() a Berklism,

No, it's been in UNIX for quite a while - V7 at least.

>The original example, passing the string "<" and the redirected file,
>was so innocently misguided I really got a laugh out of it.
>execl()'ing "/bin/sh" to parse the command, or using system(), on the
>other hand, were somewhat less amusing.

I'd certainly say they were less amusing, since they actually form a
fairly convenient wrapper for these kinds of operations, and would
seriously consider using them for this sort of thing.  If the guy wanted
to redirect the input of "mail" to a pipe from some other program, for
example, using "system" (or an "execl()" of "/bin/sh" with the "-c"
flag, which is what "system()" does after it does the "fork()") is a lot
more convenient than constructing the pipeline yourself.

chris@mimsy.UUCP (Chris Torek) (06/14/89)

[Astounding, an article which actually belongs in both a C newsgroup
and a Unix newsgroup.]

In article <2489@mit-caf.MIT.EDU> vlcek@mit-caf.MIT.EDU (Jim Vlcek) writes:
>... [someone gave] a bit of code showing how to fork(), dup2(), and then
>execl() to get the desired effect.
>
>I think a much better way, under BSD, is to use freopen() to attach
>stdin to the redirected file.  Such redirection is, in fact, precisely
>the intended usage of this function call.  Is freopen() a Berklism, or
>do you have that in Sys V as well?

freopen() is a standard <stdio.h> function and appears in the pANS;
most existing implementations should already provide it:

	FILE *freopen(const char *name, const char *type, FILE *stream)

replaces the current instance of `stream' (whatever it may be connected
to) with one connected to the given file `name', opened according to
`type'.  The type argument has the same format as for fopen ("r", "w",
"a", "rb", "wb", "r+", and so forth).  If the named file cannot be
connected to the given stream, the current connection is closed and
freopen() returns (FILE *)NULL; otherwise, the return value is equal
to `stream'.

[End C-specific topic; on to Unix-specific topic.]

This is not really a better way to redirect input after a fork(),
because freopen() makes no promises as to how it goes about its job.
In particular, it does *not* guarantee that fileno(stream) will be
the same after the operation as it was before.  There are, however,
a number of Unix utility programs whose source assume that it will,
so it probably will; but relying on this is a bad idea.

I found out about the various naughty programs when I changed freopen()
to open the new file before closing the old.  In particular, this is
necessary to make

	freopen("/dev/stdin", "r", stdin)

work.  I had to add code to dup2() the new descriptor over the old one
(after my first change, the above line caused fileno(stdin) to be 3).
This does, however, change the behaviour from the original, in which

	f1 = fopen("something", "r");
	... error checking deleted ...
	f2 = fopen("somethingelse", "r");
	...
	fclose(f1);
	freopen("yetanother", "r", f2);

caused fileno(f2) to decrease, e.g., from 4 to 3.  (This is not as
bad as a few programs which have code like

	fileno(fp) = 2;

Yow!)

Anyway, freopen() will probably work, but makes no guarantees, and
does more work than necessary.  In addition, if you use vfork() rather
than fork(), freopen() will clobber data in the parent process.
Stick with dup2().  But use it correctly (note in particular the
line marked with an arrow):

	/* initial error checking occurs in parent process */
	newfd = open(newfile, 0);
	if (newfd < 0)
		... error ...

	switch (pid = fork()) {

	case -1:
		... error ...

	case 0:
		/* child */
->		if (newfd != 0) {
			if (dup2(newfd, 0) < 0)
				... error ...
			if (close(newfd))
				... error ...
		}
		/* else it was already fd 0 */
		execl(pathname, argv0, argv1, argv2, ..., (char *)NULL);
		... error ...
	}
	/* parent */
	(void) close(newfd); /* if it fails, what would we do differently? */
	while ((w = wait(&status)) != pid)
		if (w == -1 && errno != EINTR)
			... error ...
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

mhoffman@infocenter.UUCP (Mike Hoffman) (06/15/89)

in article <352.nlhp3@oracle.nl>, bengsig@oracle.nl (Bjorn Engsig) says:
| In article <3709@tank.uchicago.edu> matt@oddjob.uchicago.edu (Matt Crawford) writes:
|>In article <414@sc50.UUCP>, ron@sc50 ( Ron Winnacott ) writes:
|>) Can anyone tell me how to redirect stdin when I use execl ...
|>) execl("/bin/mail","mail","ron","<","/tmp/tfile",NULL);
|>
|>So help me, if I see even ONE answer to this question posted in
|>comp.unix.WIZARDS, I'll scream!
| Hey come on, there is no need to flame this in this way.  

| Maybe you should ask the administrator of the coming monthly posting to add
| a few short examples of how to use fork and exec.  

Then, in article <851@pcsbst.UUCP>, torsten@pcsbst.UUCP (torsten) says:
| 	I think this should do it:
| 	execl("/bin/sh","sh","/bin/mail ron < /tmp/tfile",NULL);

I think Bjorn may be right. . .let's hope we don't see any more of this :-)
____
Michael J. Hoffman      
Manufacturing Engineering 
Encore Computer Corporation
UUCP: {uunet,codas!novavax,sun,pur-ee}!gould!mhoffman

"My opinions are my own and are not to be employed with those of my confuser."

john@frog.UUCP (John Woods) (07/22/89)

In article <851@pcsbst.UUCP>, torsten@pcsbst.UUCP (torsten) writes:
> In article <414@sc50.UUCP> ron@sc50.UUCP ( Ron Winnacott ) writes:
> >Hello net.
> >execl("/bin/mail","mail","ron","<","/tmp/tfile",NULL);
> 	I think this should do it:
> 	execl("/bin/sh","sh","/bin/mail ron < /tmp/tfile",NULL);

Or, you might try something general-purpose like:

fexecve(sin, sout, serr, av, ev)
int sin, sout, serr;
char **av, *ev;
{
	dup2(sin,  0);			/* note: dup2(0, 0) is a no-op */
	dup2(sout, 1);
	dup2(serr, 2);
	if (sin > 2)  close(sin);	/* delete extra file handles */
	if (sout > 2) close(sout);
	if (serr > 2) close(serr);
	return execve(av, ev);
}

Two provisos: System V users will want to use fcntl(, F_DUPFD, ) instead of
dup2() (and note that it is a little tricky to do that correctly), and this
doesn't quite work if you do silly things like

	fexecve(1, 2, 0, argvec, envp);

though it can be made to work if need be.
(Left to the student as an exercise :-)


-- 
John Woods, Charles River Data Systems, Framingham MA, (508) 626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu
    People...How you gonna FIGURE 'em?
    Don't bother, S.L.--Just stand back and enjoy the EVOLUTIONARY PROCESS...

djones@megatest.UUCP (Dave Jones) (07/23/89)

From article <2489@mit-caf.MIT.EDU>, by vlcek@mit-caf.MIT.EDU (Jim Vlcek):
> In article <5587@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>>> In article <414@sc50.UUCP> ron@sc50.UUCP ( Ron Winnacott ) writes:
>>>>Can anyone tell me how to redirect stdin when I use execl to 
>>>>start a new program.
>>
>>Under BSD Unix, you use dup2. 
> 
> plus a bit of code showing how to fork(), dup2(), and then execl() to
> get the desired effect.
> 
> I think a much better way, under BSD, is to use freopen() to attach
> stdin to the redirected file.


Maybe. But that wasn't the question. The question was how to redirect stdin when
using execl, not how to rebind stdin after the execl.

Anyway, using freopen is only possible if you have access to the source code
of all the programs which will be execled, are already responsible for their
content, and can easily modify and dependably distribute the modified version
to all sites which will use the execling program.  It may be much much easier to
redirect it using dup2 on the execling side -- I presume that's what the
various shells do.


             -- Dave

guy@auspex.auspex.com (Guy Harris) (08/03/89)

>Two provisos: System V users will want to use fcntl(, F_DUPFD, ) instead of
>dup2()

Although:

	1) S5R3 has "dup2()"

and

	2) 4.[23]BSD has "fcntl(F_DUPFD)"

so if you're willing to rule out older systems you can avoid polluting
your code with "#ifdef"s.