[comp.unix.questions] Redirecting stdout, and then getting it back

nelson@berlioz.nsc.com (Taed Nelson) (02/14/91)

We have a "shell" program which executes programs from within it using the
  system()/spawn() call.  We wanted to trap these errors, so we freopen()ed
  stdout and stderr to files that we could parse/show to the user from within
  the "shell".  This part works fine.

We also want to provide a "subshell" capability to get out to csh or DOS, but
  clearly we want stdout and stderr to go back to the screen.  Unfortunately,
  freopen() closed the original screen file, so we can't get output to go
  there.

We tried "storing" the FILE struct prior to the freopen(), but that didn't
  work, we assume because the FILE struct is just a buffer and not a real
  descriptor of the file.

So we need a way to fopen_again(FILE*) or something of the same sort.  Or
  can we get at the real file descriptors, or keep freopen() from closing
  the original?

Help!

(Thanks!)

jik@athena.mit.edu (Jonathan I. Kamens) (02/15/91)

  It sounds to me like your problem is that you are replacing stdout and
stderr too early.  If you are running programs in a subprocess and you want
the stdout and stderr of those programs to go into a file (or into a pipe from
which you can read), then you don't want to close stdout and stderr in the
parent before you fork() to start the sub-process; you want to do it after
you've fork()ed but before you exec() the sub-process.

  Now, I realize that if you're using system() right now to start the
sub-processes, then you don't have control over this, because system() deals
with the fork() and exec() stuff.  This means that you're going to have to
change the way you're doing things.

  If you want stdout and stderr to go to a file, then you can do that by
simply doing a fork(), then doing the freopen() calls in the child the same
way you're doing them in the parent right now, then doing the system() as
you're doing now.  Since the freopen() calls are in a subprocess, the parent
keeps its stdout and stderr.  If you do this, don't forget to install a
SIGCHLD handler to wait for your children (see the article I posted about that
yesterday).

  Alternatively, you can rewrite system() to do the freopen() calls itself,
after its fork() but before its exec().

  Pipes are a bit more complicated.  You would need to create the pipe in the
parent, then do the fork(), then replace fileno(stdout) and fileno(stderr)
with the write side of the pipes in the child and close the read sides; in the
parent, you close the write sides and read from the read sides to get the
output of the subprocess.  Then, you do the system() in the child.

  Of course, there is a much simpler answer to all of this -- you could
freopen() /dev/tty on top of stdout and stderr after the subprocess finishes. 
But this is sloppy programming and I wouldn't recommend it (I'm just
mentioning it for completeness).

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

mike (02/15/91)

In an article, berlioz.nsc.com!nelson (Taed Nelson) writes:
|
|We have a "shell" program which executes programs from within it using the
|  system()/spawn() call.  We wanted to trap these errors, so we freopen()ed
|  stdout and stderr to files that we could parse/show to the user from within
|  the "shell".  This part works fine.
|
|We also want to provide a "subshell" capability to get out to csh or DOS, but
|  clearly we want stdout and stderr to go back to the screen.  Unfortunately,
|  freopen() closed the original screen file, so we can't get output to go
|  there.

How about using /dev/tty, such as:

	freopen("/dev/tty","r",stdin);
	freopen("/dev/tty","w",stdout);
	freopen("/dev/tty","w",stderr);

|(Thanks!)

You're welcome.
-- 
Michael Stefanik                       | Opinions stated are not even my own.
Systems Engineer, Briareus Corporation | UUCP: ...!uunet!bria!mike
-------------------------------------------------------------------------------
technoignorami (tek'no-ig'no-ram`i) a group of individuals that are constantly
found to be saying things like "Well, it works on my DOS machine ..."

gwyn@smoke.brl.mil (Doug Gwyn) (02/15/91)

In article <1991Feb14.035930.20173@berlioz.nsc.com> nelson@berlioz.nsc.com (Taed Nelson) writes:
>We have a "shell" program which executes programs from within it using the
>  system()/spawn() call.  We wanted to trap these errors, so we freopen()ed
>  stdout and stderr to files that we could parse/show to the user from within
>  the "shell".  This part works fine.

You were lucky.  The UNIX convention is that file descriptors 0, 1, and 2
are the standard I/O/error hooks.  While in a given stdio-using program
stdin, stdout, and stderr will start out assigned to those file
descriptors, there is no guarantee that the same file descriptors will
be in use for those stdio streams after the freopen()s.

To keep the original file descriptors from being lost by the freopen()s,
you can save the value of dup(fileno(stdio_stream_here)), then later use
dup2() to move them to the original values 0,1,2.

nelson@berlioz.nsc.com (Taed Nelson) (02/15/91)

In article <15202@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>In article <1991Feb14.035930.20173@berlioz.nsc.com> nelson@berlioz.nsc.com (Taed Nelson) writes:
>>We have a "shell" program which executes programs from within it using the
>>  system()/spawn() call.  We wanted to trap these errors, so we freopen()ed
>
>You were lucky.  The UNIX convention is that file descriptors 0, 1, and 2
>are the standard I/O/error hooks.  While in a given stdio-using program

Actually, we weren't "lucky" -- the man page for freopen() says that this
  is a common use of the command.

Thanks for the replies.  What I didn't make clear enough is that we operate
  under both DOS and Unix;  I thought the REAL solution would be applicable
  to both.  Clearly, I forgot about fork() and did not think of using it
  due to the DOS environment.

Our final solution, which works, is to do another freopen() before going
  to the shell which redirects stdout and stderr back to "CON".  I was
  really happy that that little tidbit was not documented in the manual
  (actually, pissed off is a more appropriate adjective).  I wonder how one
  would actually open a file named "CON"?