Xref: utzoo comp.lang.c:19335 comp.unix.wizards:16910 Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!apple!ames!purdue!haven!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c,comp.unix.wizards Subject: Re: redirect stdin when using execl Message-ID: <18053@mimsy.UUCP> Date: 13 Jun 89 20:09:13 GMT References: <851@pcsbst.UUCP> <5587@goofy.megatest.UUCP> <2489@mit-caf.MIT.EDU> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 94 [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 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