[comp.unix.wizards] Redirect Output in the Middle of a Program??

liuqing@cvl.umd.edu (Larry Liuqing Huang) (04/26/88)

Now more people are dialing into a system via telephone lines. It is
not unusual that the connection died out in the middle of a big C
program which you hate to start running from the beginning again.

Is it possible for the C program or Shell to detect the line problem?

If so, is it possible to redirect all standard output and error messages
to a designated file from the POINT where the problem is detected and 
continue running?

Any hints highly appreciated.

liuqing@cvl.umd.edu

root@didsgn.UUCP (didsgn) (04/26/88)

In article <2841@cvl.umd.edu>, liuqing@cvl.umd.edu (Larry Liuqing Huang) writes:
> Now more people are dialing into a system via telephone lines. It is
> not unusual that the connection died out in the middle of a big C
> program which you hate to start running from the beginning again.
> 
> Is it possible for the C program or Shell to detect the line problem?
> 
> If so, is it possible to redirect all standard output and error messages
> to a designated file from the POINT where the problem is detected and 
> continue running?
> 
> Any hints highly appreciated.

There is a way....If your system can pass you a SIGHUP signal telling you that the controlling terminal of your process has 'hung up' then you can handle() the signal (see: signal() etc..). At this point and in the 'handling' subroutine you can fopen() one or more files and replace stdout, and stderr.  WATCH OUT: as in many unix systems stdXXX are defined as &iob[X], you will need to to copy the FILE structure associated to your newly opened files to &iob[X].  Once this is done, you can keep on with your p





rogram execution.

Now this can be fairly easy for stderr and stdout but tricky for stdin as you cannot really tell when the event occurs and where is your input stream at...But
this is another story.

Here is a sort of an illustration of what I tried to explain (I am french so my english constructions are not perfect):

void myhandlerofsighup();

{
static	FILE	*emergencyout,*emergencyerr;

if ( (emergencyout = fopen(EMERGENCYOUT,"a")) == (FILE *)0 )
	{
	/* THIS IS REALLY BAD */
	.
	.
	}
if ( (emergencyerr = fopen(EMERGENCYERR,"a")) == (FILE *)0 )
	{
	/* THIS IS REALLY REALLY BAD */
	.
	.
	}

	(void) memcpy(stdout,emergencyout,sizeof(FILE));
	(void) memcpy(stderr,emergencyerr,sizeof(FILE));
	
	Go on ....
}

I am not finished...
What you could do is write a small process that does this work for any process that is passed as and argv[] and fexeced().

Have fun...
jlc

Jean-Luc Chatelain
404 447 0274

...!gatech!rebel!didsgn!jlc

bak@csd-v.UUCP (Bruce) (04/27/88)

In article <2841@cvl.umd.edu> liuqing@cvl.UUCP (Larry Liuqing Huang) writes:
>
>Is it possible for the C program or Shell to detect the line problem?

The following program (redir) will accomplish what you want.
It is called with the command you wish executed as arguments.
Say you want to call:

$ cmd arg_1 ... arg_n

then you execute the command:

$ redir cmd arg_1 ... arg_n

Included is a sample command (output) which produces plenty of output.
To demo redir build both executables via

$ make redir
$ make output

Then issue the following commands:

$ redir output
762

$ kill -1 1037              # Simulates a hangup and causes output redirection
                            # to file "newout".  Note that during this command
                            # output is streaming to the screen and you will
                            # not be able to see the command being typed.

$ ps                        # get the proc id of the output process.

   PID TTY      TIME COMMAND
    52 cons3    0:33 ksh
   762 cons3    3:07 redir [output]
   763 cons3    0:26 [output]
   773 cons3    0:01 ps

$ kill -15 763              # Simulate software termination of the output
                            # process.  This will cause redir to exit also.

$ head newdir               # print the first few lines of newdir (which will
                            # be pretty big by now).  Note that output should
                            # begin where the screen output left off.


The details of starting this program up on terminal lines and doing similar
redirection of standard error should be fairly straight forward.

The principles used here are explained very lucidly in Rochkind's
admirable book ADVANCED UNIX PROGRAMMING.

The following is redir.c:

#--- cut here ---#--- cut here ---#--- cut here ---#--- cut here ---*
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>


main( argc, argv, envp )
int argc ; char **argv, **envp;
    {
    int i, redir();
    int fpipe[2];
    char c;

    /*
    **      Set signal handler to redirect output on receipt of
    **      SIGHUP.
    */
    signal( SIGHUP, redir );

    /*
    **      Open a pipe for the child to write its standard output to.
    */      And the parent to read it's standard input from.

    if( pipe( fpipe ) == -1 )
        {
        perror( "redir: pipe" );
        exit( 1 );
        }

    switch( fork() )
        {
        case 0:                             /* child process */
        /*
        **      Now exec the child with args.
        */
        close( 1 );
        dup( fpipe[1] );            /* prepare pipe as child's stdout */
        close( fpipe[0] );
        close( fpipe[1] );
        execve( argv[1], &argv[1], envp );
        exit( 1 );

        default:                             /* parent process */
        close( 0 );
        dup( fpipe[0] );            /* prepare pipe as parent's stdin */
        close( fpipe[0] );
        close( fpipe[1] );

        /*
        **      Read child's standard output and write it to parent's
        **      standard output.  Note that initially this is the terminal
        **      but upon receipt of a hangup it will become the file newout
        **      ( see the signal handler below).
        */
        while( read( 0, &c, 1 ) ) write( 1, &c, 1 );

        /*
        **      Exit when the pipe is closed by the child.
        */
        exit( 0 );
        }
    }


int redir()                 /* Handle receipt of SIGHUP */
    {
    int fout;

    /*
    **      Create the output file newout.
    */
    if( ( fout = open( "newout", O_WRONLY | O_CREAT, 0666 ) ) < 0 )
        {
        perror( "redir: open" );
        exit( 1 );
        }

    /*
    **      Close stdout and dup the file just opened.
    **      Upon return from this signal handler, the new
    **      file will become the redirected stdoutput file.
    */
    close( 1 );
    if( dup( fout ) == -1 )
        {
        perror( "redir: dup" );
        exit( 2 );
        }
    }
#--- cut here ---#--- cut here ---#--- cut here ---#--- cut here ---*

This is output.c:
#--- cut here ---#--- cut here ---#--- cut here ---#--- cut here ---*

#include <stdio.h>

main()              /* Sample application to produce lots of output */
    {
    int i=0;
    while( 1 ) fprintf( stdout, "%d\n", i++ );
    }
-- 
  Bruce Kern                                 |  uunet!swlabs!csd-v!bak  
  Computer Systems Design                    |  1-203-270-0399          
  29 High Rock Rd., Sandy Hook, Ct. 06482    |  This space for rent.    

karish@denali.UUCP (karish) (04/28/88)

In article <2841@cvl.umd.edu> liuqing@cvl.UUCP (Larry Liuqing Huang) writes:
>Now more people are dialing into a system via telephone lines. It is
>not unusual that the connection died out in the middle of a big C
>program which you hate to start running from the beginning again.
>
>Is it possible for the C program or Shell to detect the line problem?
>
>If so, is it possible to redirect all standard output and error messages
>to a designated file from the POINT where the problem is detected and 
>continue running?
>
>Any hints highly appreciated.
>
>liuqing@cvl.umd.edu

If the modem on the computer end hangs up when this happens, there is a
way to do what you wish.  The tty driver should send a SIGHUP to the
login process group when the modem hangs up.  The user's program can
catch the SIGHUP, using a signal handler assigned by the signal()
function.  This handler can use fopen() to open outfiles, and
freopen() to reassign the output streams.

It would probably save a lot of programming effort to teach the
users to run their jobs in the background.   This prevents the process
from being killed when the connection dies.

You might also wish to point out the 'nice' and 'tee -f' commands.

Chuck

chris@mimsy.UUCP (Chris Torek) (04/28/88)

In article <62@denali.UUCP> karish@denali.UUCP (karish) writes:
>... The user's program can catch the SIGHUP, using a signal handler
>assigned by the signal() function.  This handler can use fopen() to
>open outfiles, and freopen() to reassign the output streams.

This is quite dangerous.  The proposed technique is likely to work
until you need it to do so.  A SIGHUP can occur while all sorts of
internal states are inconsistent; opening a new stream, or reopening an
existing one, is liable to result in a core dump or lost or duplicated
output.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

csch@tmpmbx.UUCP (Clemens Schrimpe) (04/28/88)

Larry Liuqing Huang (liuqing@cvl.UUCP) writes:
{} Now more people are dialing into a system via telephone lines. It is
{} not unusual that the connection died out in the middle of a big C
{} program which you hate to start running from the beginning again.
{} 
{} Is it possible for the C program or Shell to detect the line problem?
Sure! The system will generate a SIGHUP (Hangup) signal to all processes,
which have the "hung-up" tty as their controlling tty.
[this will work ONLY, if you are using a tty, which supports modem-control
 signals (mainly DTR & DCD) and this modem-control is also enabled 
 (~CLOCAL, HUPCL and open with ~O_NDELAY)]

{} If so, is it possible to redirect all standard output and error messages
{} to a designated file from the POINT where the problem is detected and 
{} continue running?
Sure (normally :-)

Simply catch the SIGHUP, close 0, reopen it on /dev/null and
close 1 & 2 and reopen both on the file you want, then return from
the signal-routine. I havn't tried this, but it should work.

{} Any hints highly appreciated.
Ya' welcome ... ]:-}

Clemens Schrimpe

UUCP:	csch@tmpmbx	=	{pyramid!tub!unido}!tmpmbx!csch
BITNET:	csch@db0tui6	csch@tub.BITNET
PHONE:	+49-30-332 40 15
FAX:	+49-30-361 40 93
TELEX:	186672 net d

mac@esl.UUCP (Mike McNamara) (04/29/88)

	The discussion concerned writing large programs that  "noticed" the
     user logged off, and changed their output device from the terminal to
     a file, automagicaly.

	I'd be more interested in a way I could type
     % make
     interesting lines from make....
     ^Z
     % bg > make.out

 	     Which, if you didn't catch my drift, allows me to stop a job, 
     and restart it with it's stdout redirected to a file.

      Any one done this?
-- 
 Michael Mc Namara                 
 ESL Incorporated                 
 ARPA: mac%esl@lll-lcc.ARPA    

bob@cloud9.UUCP (Bob Toxen) (04/30/88)

In article <11257@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
> In article <62@denali.UUCP> karish@denali.UUCP writes:
> >... The user's program can catch the SIGHUP.  This handler can use
> >fopen() to open outfiles, and freopen() to reassign the output streams.
> This is quite dangerous.
> In-Real-Life: Chris Torek uunet!mimsy!chris
(Edited to satisfy inews.)

The safe way is to do a close and then an open on the desired file
descriptors.  This will work even while executing stdio code.
System calls are internally consistent (except in some non-native
UNIXes).  If you want to be ultra-safe, have your interrupt ignore
signals before doing this and then restore them afterwards.
-- 

Bob Toxen	{ucbvax!ihnp4,harvard,cloud9!es}!anvil!cavu!bob
Stratus Computer, Marlboro, MA
Pilot to Copilot: What's a mountain goat doing way up here in a cloud bank?

amos@taux01.UUCP (Amos Shapir) (05/01/88)

I usually do
	make >& make.out & tail -f make.out
(This is csh; with sh, do make 2>&1 > make.out)

Thus, the make is always in the background, but its output can be followed
on the screen, stopped, restarted etc. Even if the line goes down, the
output will remain. (This can be put in a command file or alias)
-- 
	Amos Shapir			(My other cpu is a NS32532)
National Semiconductor (Israel)
6 Maskit st. P.O.B. 3007, Herzlia 46104, Israel  Tel. +972 52 522261
amos%taux01@nsc.com  34 48 E / 32 10 N

allbery@ncoast.UUCP (Brandon Allbery) (05/05/88)

As quoted from <2841@cvl.umd.edu> by liuqing@cvl.umd.edu (Larry Liuqing Huang):
+---------------
| Now more people are dialing into a system via telephone lines. It is
| not unusual that the connection died out in the middle of a big C
| program which you hate to start running from the beginning again.
| Is it possible for the C program or Shell to detect the line problem?
+---------------

From the time the program starts, the shell has no control over the program's
I/O.  You would have to patch stdio to detect SIGHUP on an fd attached to the
controlling terminal.  (Ecch!)

I use the following scripts instead.  (4.xBSDers will, of course, want to
change the names....)

----------------------------------- cut here -----------------------------------
: bg - run program in the background (essentially nohup)
case $# in
0)	echo "Usage: bg command [args]" >&2
	exit 1
esac
> ./do.out
{
	[ "$TERM" = vt100 ] && echo '\033[1q\c'
	trap '' 3 2 1
	{
		eval nice -20 "$@"
	} > ./do.out 2>&1
	[ "$TERM" = vt100 ] && echo '\033[q\c'
	if [ $? -ne 0 ]; then
		echo '\007\c'
		[ "$TERM" = vt100 ] && echo '\033[4q\c'
		sleep 1
	fi
	echo '\007\c'
	sleep 1
	echo '\007\c'
} &
----------------------------------- cut here -----------------------------------

----------------------------------- cut here -----------------------------------
: fg - run command in background, catching output which is displayed
case "$#" in
0)	echo "usage: fg command [args]" >&2
	exit 1
	;;
esac
> ./do.out
{
	SHELL=/bin/sh export SHELL
	case "$TERM" in
	vt1??)	echo '\033[q\033[1q\c' ;;
	esac
	trap '' 3 2 1
	eval nice -20 "sh -c '$@' > ./do.out 2>&1 < /dev/null"
	rc=$?
	case "$TERM" in
	vt1??)	echo '\033[q\c' ;;
	esac
	case $rc in
	0)	;;
	*)	echo '\007\c'
		case "$TERM" in
		vt100)	echo '\033[4q\c' ;;
		vt1??)	echo '\033[2q\c' ;;
		esac
#		sleep 1
	esac
	echo '\007\c'
	sleep 1
	echo '\007\c'
	kill -2 $$ 2> /dev/null
} &
exec tail -f ./do.out
----------------------------------- cut here -----------------------------------
-- 
	      Brandon S. Allbery, moderator of comp.sources.misc
	{well!hoptoad,uunet!marque,cbosgd,sun!mandrill}!ncoast!allbery
Delphi: ALLBERY						     MCI Mail: BALLBERY

james@cantuar.UUCP (J. Collier) (05/09/88)

Mike McNamara (mac@esl.UUCP) writes:
>       I'd be more interested in a way I could type
>     % make
>     interesting lines from make....
>     ^Z
>     % bg > make.out
[....]
>      Any one done this?

   Robert Biddle (robert@cantuar.uucp) and I discussed a scheme for this 
with Robert Elz (kre@munnari.oz) last year. We didn't follow it up for 
several reasons.

   After toying with various signal+IPC schemes (this is a 4.3BSD site), 
we decided that it would be preferable to avoid compiling the handling 
mechanism into every program. We proposed a new system call:
                dup3(localfd, remotefd, pid)
which would replace the file descriptor 'remotefd' in the process 'pid'
with the descriptor 'localfd' from the calling process - perhaps sending
a warning signal at the same time (c.f. SIGWINCH - urk).

   Although this looked clean enough at first sight, we acknowledged
a few potential problems:

 1) Security - it would probably be insufficient just to compare the 
    effective uid's; there may be r/e/uid-swapping programs which
    rely on the fact that a given file descriptor refers to a known file.

 2) Device types - there are plenty of programs which modify their I/O
    behaviour on the basis of isatty(fileno(stdout)) etc. The most 
    seriously (fatally?) affected interactive applications would 
    probably be unlikely targets for re-direction, but new features which
    can cause core dumps on hitherto healthy programs are undesirable.
    Stdio buffering assumptions would also cause unnatural behaviour in 
    many programs.

 3) The system would have to assume that (fileno(stdin) == 0) etc. This
    is not guaranteed.

 4) Implementing it would involve something like ptrace()'s hideous
    machinations to access the target process's u area.

 5) Yet another context dependent, multiple-occurrence-significant signal.

   Robert Elz's advice was "do it right, or not at all". He also pointed
out that ptrace()'s methods were neither philosophically sound nor 
suitable for contemplation as a Sunday afternoon hack. This seemed reason
enough to can that system, so I then looked at putting switchable 
pipelines into a shell using pseudo-terminals. On reflection I concluded 
that the idea was grotesque.
                                             
   Nevertheless, there would be many uses for such a system. Apart from 
the two already mentioned, one advantage would be the ability to move a 
process group to a different terminal or window - although the latter 
can be built into the window manager. Quite often I wish there was some 
method of doing this for input as well - e.g. to provide a prologue for 
an interactive program. ("cat foo - | bar" is not as useful as it looks 
for reason (2) above; in frustration I have at times used pseudo-terminals 
or TIOCSTI to achieve the desired effect, but these are ugly solutions).

   I think some fairly radical alterations would be needed to implement
this facility cleanly. On its own it wouldn't justify these alterations,
but the main underlying problems are becoming painful in other respects 
and may be due for attention anyway. Another subject, another posting...

>-- 
> Michael Mc Namara   ESL Incorporated  ARPA: mac%esl@lll-lcc.ARPA
>   
-------------------------
James Collier              Internet(ish):  james@cantuar.uucp
Computer Science Dept.,    UUCP:           {watmath,munnari,mcvax}!cantuar!james
University of Canterbury,  Spearnet/Janet: j.collier@nz.ac.canty (unreliable)
Christchurch, New Zealand. Office: +64 3 482 009 x8356  Home: +64 3 554 025