[alt.sources] redirecting standard i/o from an exec'ed programme

tchrist@convex.COM (Tom Christiansen) (08/17/90)

Hmm.. I think I posted this before, but it may have been five years ago.
This is for systems with socketpair(), and you should read and consider
the caveat section very carefully.  Also, contains non-ANSI cpp abuse,
if that bugs you; what can I say?  I was young and foolish. :-)

--tom


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	process.c
# This archive created: Thu Aug 16 13:17:01 1990
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'process.c'" '(4801 characters)'
if test -f 'process.c'
then
	echo shar: "will not over-write existing file 'process.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'process.c'
	X/* process.c 
	X *
	X *	written by tom christiansen on Wed May 22 15:02:19 CDT 1985 to open
	X *	a socket pair and let the user read and write from both sides.
	X *	return -1 on error, otherwise put the correct file pointers
	X *	into *input and *output.  
	X *
	X *	CAVEAT UTILITOR:
	X *		you will block forever if one of the sides of the
	X *		pipes blocks because of too much in the buffer.  
	X */
	X 
	X#include <stdio.h>
	X#include <signal.h>
	X#include <sys/param.h>
	X#include <sys/wait.h>
	X#include <sys/socket.h>
	X
	X#define	READ	0
	X#define	WRITE	1
	X#define CHILD 	0
	X#define PARENT	1
	X
	X/*
	X *	define QUIET if you don't want error messages
	X *	to print out if something blows up due to test
	X *	macros.  this assumes you will perror() it yourself
	X *	when the routine returns.
	X */
	X#ifdef QUIET
	X#	define announce(x)  /* nothing at all */
	X#else
	X#	define announce(x) perror(x)
	X#endif QUIET
	X
	X/*
	X *	first some macros to avoid lots of typing and ugly
	X *	code.
	X */
	X#define test0(x)	\
	X	if (!(x)) {	\
	X		announce("process: x");	\
	X		return -1;	\
	X	}
	X
	X#define test(x)	\
	X	if ( (x) < 0 ) {	\
	X		announce("process: x");	\
	X		return -1;	\
	X	}
	X
	Xchar FD_READ[] = "r";
	Xchar FD_WRITE[] = "w";
	X
	X/*
	X *	first a static array to hold the pid of 
	X *	the process we create so we can wait for
	X *	it to die when we close up.  there is enough
	X *	room for all possible file descriptors (NOFILE)
	X *	so this function may be called repeatedly by
	X *	a program with no ill effects.
	X */
	Xstatic	int	pids[NOFILE];
	X
	X/*****************************************************************
	X *
	X *	process - a function to do what popen does, but to 
	X *			  give you back file pointers for read as
	X *			  well as for write.
	X *
	X *****************************************************************/
	X
	Xint
	Xprocess(cmd,input,output)
	X	char	*cmd;
	X	FILE	**input,**output;
	X{
	X	int sock[2];
	X	register  pid;
	X
	X
	X/*
	X *	use connected socket pair so we can do reads and 
	X *	writes on these things.  if we used a pipe() call,
	X *	it would be unidirectional and we would have to 
	X *	make two calls to get 4 file descriptors.
	X */
	X	test(socketpair(AF_UNIX,SOCK_STREAM,0,sock));
	X
	X/*
	X * 	fork for the child command.  don't bother doing
	X *	a real fork, since we're just going to exec anyway,
	X *	so borrow the parent's pages with a vfork.
	X */
	X	if((pid = vfork()) == CHILD) { 
	X
	X/*
	X *	close old stdin and stdout to make room for socket
	X *	descriptors.
	X */
	X		test(close(READ));	
	X		test(close(WRITE));
	X
	X/*	
	X *	the kid will use the CHILD end.  connect both his stdin
	X *	and stdout to the CHILD socket, which is fine because 
	X *	unix domain sockets are bidirectional.
	X */
	X		test(dup2(sock[CHILD], WRITE));
	X		test(dup2(sock[CHILD], READ));
	X
	X/*
	X *	don't need these anymore, and if we don't get rid of them, we 
	X *	won't be happy later on, because the process doesn't seem to die.
	X */
	X		test(close(sock[CHILD]));
	X		test(close(sock[PARENT]));
	X/*
	X *	now do the command.  use the csh for tilda's sake.
	X */
	X		execl("/bin/csh", "csh", "-fc", cmd, 0);
	X		perror("process kid: execl");
	X		_exit(1);
	X	}
	X
	X/*
	X *	-1 pid means we couldn't fork.
	X */
	X	if(pid == -1) {
	X		perror("process: vfork");
	X		(void) close(sock[CHILD]);
	X		(void) close(sock[PARENT]);
	X		return -1;
	X	}
	X
	X/*
	X *	otherwise, we are the parent and healthy;
	X *	remember the kid pid for a later close.
	X */
	X	pids[sock[PARENT]] = pid;
	X
	X/*
	X *	connect up the user's input and output file
	X *	pointers to our end of the socket connection.
	X *	give him one for read and one for write.
	X */
	X	test0(*input = fdopen(sock[PARENT],FD_READ));
	X	test0(*output = fdopen(sock[PARENT],FD_WRITE));
	X
	X	test(close(sock[CHILD]));
	X
	X	return 0;
	X}
	X
	X
	X/****************************************************************
	X *	close up the passed file and wait for the 
	X *	child to die.
	X ***************************************************************/
	X
	X#undef test
	X#define test(x)	\
	X	if ( (x) < 0 ) {	\
	X		announce("process_close: x");	\
	X		return -1;	\
	X	}
	X
	X/*
	X *	don't need them both since they are the 
	X * 	same thing
	X */
	Xint
	Xprocess_close(input)
	XFILE *input;
	X{
	X	register f,r, (*hstat)(), (*istat)(), (*qstat)();
	X	int status;
	X
	X	f = fileno(input);
	X	test(fclose(input)); 
	X/*
	X *	don't need to close also output, as it is the same 
	X */
	X
	X/*
	X *	protect ourselves from unfriendly signals while
	X *	waiting for child's death.
	X */
	X	istat = signal(SIGINT, SIG_IGN);
	X	qstat = signal(SIGQUIT, SIG_IGN);
	X	hstat = signal(SIGHUP, SIG_IGN);
	X
	X/*
	X *	wait for the child to die, keeping track of status.
	X *	we saved the kid pid in the pids[] array when we 
	X *	first did the process call.
	X */
	X	while((r = wait((union wait *)&status)) != pids[f] && r == -1)
	X		;
	X	if(r == -1)
	X		status = -1;
	X
	X/*	
	X *	restore old sig values.
	X */
	X	(void) signal(SIGINT, istat);
	X	(void) signal(SIGQUIT, qstat);
	X	(void) signal(SIGHUP, hstat);
	X
	X	return(status);
	X
	X}
	X
	X
	X/* lint output:
	Xprocess.c:
	X*/
	X
	X/*
	X *	yes, folks, unlike the kernel, this file lints clean!
	X */
SHAR_EOF
if test 4801 -ne "`wc -c < 'process.c'`"
then
	echo shar: "error transmitting 'process.c'" '(should have been 4801 characters)'
fi
chmod 644 'process.c'
fi
exit 0
#	End of shell archive
--
Tom Christiansen                       {uunet,uiucdcs,sun}!convex!tchrist 
Convex Computer Corporation                            tchrist@convex.COM
  "UNIX was never designed to keep people from doing stupid things,
   because that policy would also keep them from doing clever things." [gwyn]