prwopen: A popen-like routine using ptys

John Ioannidis ji at garfield.columbia.edu
Tue Mar 4 13:49:50 AEST 1986


[ Line eater, I dare yo@^&U. (mjam, slurp, gulp)

In article <641 at puff.UUCP>, tom at puff.wisc.edu (Thomas Scott Christiansen) writes:
> Does anyone have a nice package that allows you to say something
> like
> 	process("silly_game arg1 arg2",&input_fd,&output_fd,&error_fd)
> and start the process running not on a pipe (I have one of these) but
> rather on a pty so that the proc can 1) do ioctls 2) have line buffered
> output ?
> 

Well, here it is, after all.

This is my version of such a thing; it's a substitute for popen(3) when
any of the following bothers you:
* You want to be able to both read from and write to the child process
* You want the program executed in the child process to think it's talking
  to a terminal.

There are a couple of things to note: First, I supply it an array of FILE
pointers (`a la pipe(2)), which are subsequently defined for unbufferred I/O.
Second, I have hardwired into the code a maximum of 32 pty's and a maximum
of 20 available file descriptors. The code is pretty much straightforward,
and parts of it are shamelessly stolen from popen(3) and script(1). I guess
there is only one way of doing some things.

----------------- CUT HERE --------------
/* Sample program, it just calls ftp and exits */

#include <stdio.h>
main()
{
	FILE *foo[2];
	char buf[80];
	char pr, cu=0;
	prwopen( "ftp -n", foo );
	for(;;)
	{	
		pr=cu;
		cu=getc( foo[0] );
		putchar( cu );
		fflush( stdout );	
		if( pr=='>' && cu==' ' )
			break;
	}
	fprintf( foo[1], "open localhost\n" );
	fflush( foo[1] );
	for(;;)
	{	
		pr=cu;
		cu=getc( foo[0] );
		putchar( cu );
		fflush( stdout );	
		if( pr=='>' && cu==' ' )
			break;
	}
	fprintf( foo[1], "quit\n" );
	for(;;)
	{	
		pr=cu;
		cu=getc( foo[0] );
		if( cu==EOF )
			break;
		putchar( cu );
		fflush( stdout );	
		if( pr=='>' && cu==' ' )
			break;
	}
	printf( "prwclose returns %d\n", prwclose( foo ) );
}
	
/*
 * NAME
 * 	prwopen - modified popen(3) to work with pty's instead of 
 * 		  pipes, and also provide both read and write 
 * 		  capabilities to the child process.
 *
 * SYNOPSIS
 *	#include <stdio.h>
 *	
 * 	int prwopen( cmd, streams )
 *	char *cmd;
 *	FILE streams[2];
 *
 * AUTHOR (actually, merger!)
 *	John Ioannidis, ioannidis at cs.columbia.edu
 * 
 * SEE ALSO
 *	popen(3), after which this call is modelled.
 *
 * UNIX SOURCES USED
 * 	popen(3), script(1)
 *
 */
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sgtty.h>

static	int	pip_pid[ 20 ];

int
prwopen( cmd, streams )
char *cmd;
FILE *streams[];
{
	int pty, pid, j;
	struct stat stb;
	char c;
	static char *line = "/dev/ptyp0";

        for( c = 'p'; c <= 's'; c++ )
        {
                line[strlen("/dev/pty")] = c;
                line[strlen("/dev/ptyp")] = '0';
                if( stat( line, &stb ) < 0 )
                        break;
                for( j = 0; j < 16; j++ )
                {
                        line[strlen("/dev/ptyp")] = "0123456789abcdef"[j];
                        if( ( pty = open( line, 2 )) > 0 )
			goto opened;
                }
        }
        return( -1 );

opened:
	switch( pid = fork() )
	{
	case -1:
		return( -1 );
	
	case 0:
		{
			int t, tty;
			struct sgttyb bf;
			t=open( "/dev/tty", 2 );
			if( t >= 0 )
			{
				ioctl( t, TIOCNOTTY, (char *)0 );
				close( t );
			}
			line[strlen("/dev/")] = 't';
			tty = open( line, 2 );
			close( pty );
			ioctl( tty, TIOCGETP, &bf );
			bf.sg_flags &= ~ECHO;
			ioctl( tty, TIOCSETP, &bf );
			dup2( tty, 0 );
			dup2( tty, 1 );
			dup2( tty, 2 );
			close( tty );

			execl( "/bin/sh", "sh", "-c", cmd, 0 );
			_exit(1);
		}
	}
	
	pip_pid[pty]=pid;

	if( (streams[0] = fdopen( pty, "r" )) == NULL ||
	    (streams[1] = fdopen( pty, "w" )) == NULL )
	{
		return( -1 );
	}	
	setbuf( streams[0], NULL );
	setbuf( streams[1], NULL );
	return( 0 );
}

prwclose( streams )
FILE *streams[];
{
        register f, r, (*hstat)(), (*istat)(), (*qstat)();
        int status;

        f = fileno(streams[1]);
        fclose(streams[0]);
        fclose(streams[1]);
        istat = signal(SIGINT, SIG_IGN);
        qstat = signal(SIGQUIT, SIG_IGN);
        hstat = signal(SIGHUP, SIG_IGN);
        while((r = wait(&status)) != pip_pid[f] && r != -1)
                ;
        if(r == -1)
                status = -1;
        signal(SIGINT, istat);
        signal(SIGQUIT, qstat);
        signal(SIGHUP, hstat);
        return(status);
}

------- CUT HERE -----

I hope this will be useful. 


#include <appropriate_disclaimers>

VOICE: 	+1 212 280 5510			ARPA: ioannidis at cs.columbia.EDU
USnail:	John Ioannidis			      ji at garfield.columbia.EDU
	450 Computer Science
	Columbia University,		USENET: ...{seismo|topaz}!
	New York, NY 10027			   columbia!garfield!ji

			... It's all Greek to me!



More information about the Comp.unix.wizards mailing list