Need a 2-way alternative to popen()
Tom Christiansen
tchrist at convex.COM
Tue Mar 6 23:28:09 AEST 1990
In <12430 at csli.Stanford.EDU>, mpf at csli.Stanford.EDU (Michael Frank) writes:
>Yeah, I was afraid it would be something like that. I just hoped
>there would be an easier way. Oh well, does anyone have example
>source code that does this kind of thing? Especially the hard parts?
Here is something I wrote to do this 5 years ago. It worked for me then,
but I've not used it since. It lints cleanly at least. :-) It uses
socketpair(2) and vfork(2). Use this way:
FILE *ip, *op;
if (process("some csh_cmd", &ip, &op) == -1) { error(); }
/* processing with fprintf(op, ...) and fscanf(ip, ...) */
process_close(ip);
hope this helps,
--tom
/* process.c
*
* written by tom christiansen on Wed May 22 15:02:19 CDT 1985 to open
* a socket pair and let the user read and write from both sides.
* return -1 on error, otherwise put the correct file pointers
* into *input and *output.
*
* CAVEAT UTILITOR:
* you will block forever if one of the sides of the
* pipes blocks because of too much in the buffer.
*/
#include <stdio.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/socket.h>
#define READ 0
#define WRITE 1
#define CHILD 0
#define PARENT 1
/*
* define QUIET if you don't want error messages
* to print out if something blows up due to test
* macros. this assumes you will perror() it yourself
* when the routine returns.
*/
#ifdef QUIET
# define announce(x) /* nothing at all */
#else
# define announce(x) perror(x)
#endif QUIET
/*
* first some macros to avoid lots of typing and ugly
* code.
*/
#define test0(x) \
if (!(x)) { \
announce("process: x"); \
return -1; \
}
#define test(x) \
if ( (x) < 0 ) { \
announce("process: x"); \
return -1; \
}
char FD_READ[] = "r";
char FD_WRITE[] = "w";
/*
* next a static array to hold the pid of
* the process we create so we can wait for
* it to die when we close up. there is enough
* room for all possible file descriptors (NOFILE)
* so this function may be called repeatedly by
* a program with no ill effects.
*/
static int pids[NOFILE];
/*****************************************************************
*
* process - a function to do what popen does, but to
* give you back file pointers for read as
* well as for write.
*
*****************************************************************/
int
process(cmd,input,output)
char *cmd;
FILE **input,**output;
{
int sock[2];
register pid;
/*
* use connected socket pair so we can do reads and
* writes on these things. if we used a pipe() call,
* it would be unidirectional and we would have to
* make two calls to get 4 file descriptors.
*/
test(socketpair(AF_UNIX,SOCK_STREAM,0,sock));
/*
* fork for the child command. don't bother doing
* a real fork, since we're just going to exec anyway,
* so borrow the parent's pages with a vfork.
*/
if((pid = vfork()) == CHILD) {
/*
* close old stdin and stdout to make room for socket
* descriptors.
*/
test(close(READ));
test(close(WRITE));
/*
* the kid will use the CHILD end. connect both his stdin
* and stdout to the CHILD socket, which is fine because
* unix domain sockets are bidirectional.
*/
test(dup2(sock[CHILD], WRITE));
test(dup2(sock[CHILD], READ));
/*
* don't need these anymore, and if we don't get rid of them, we
* won't be happy later on, because the process doesn't seem to die.
*/
test(close(sock[CHILD]));
test(close(sock[PARENT]));
/*
* now do the command. use the csh for tilda's sake.
*/
execl("/bin/csh", "csh", "-fc", cmd, 0);
perror("process kid: execl");
_exit(1);
}
/*
* -1 pid means we couldn't fork.
*/
if(pid == -1) {
perror("process: vfork");
(void) close(sock[CHILD]);
(void) close(sock[PARENT]);
return -1;
}
/*
* otherwise, we are the parent and healthy;
* remember the kid pid for a later close.
*/
pids[sock[PARENT]] = pid;
/*
* connect up the user's input and output file
* pointers to our end of the socket connection.
* give him one for read and one for write.
*/
test0(*input = fdopen(sock[PARENT],FD_READ));
test0(*output = fdopen(sock[PARENT],FD_WRITE));
test(close(sock[CHILD]));
return 0;
}
/****************************************************************
* close up the passed file and wait for the
* child to die.
***************************************************************/
#undef test
#define test(x) \
if ( (x) < 0 ) { \
announce("process_close: x"); \
return -1; \
}
/*
* don't need them both since they are the
* same thing
*/
int
process_close(input)
FILE *input;
{
register f,r, (*hstat)(), (*istat)(), (*qstat)();
int status;
f = fileno(input);
test(fclose(input));
/*
* don't need to close also output, as it is the same
*/
/*
* protect ourselves from unfriendly signals while
* waiting for child's death.
*/
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
hstat = signal(SIGHUP, SIG_IGN);
/*
* wait for the child to die, keeping track of status.
* we saved the kid pid in the pids[] array when we
* first did the process call.
*/
while((r = wait((union wait *)&status)) != pids[f] && r == -1)
;
if(r == -1)
status = -1;
/*
* restore old sig values.
*/
(void) signal(SIGINT, istat);
(void) signal(SIGQUIT, qstat);
(void) signal(SIGHUP, hstat);
return(status);
}
/* lint output:
process.c:
*/
--
Tom Christiansen {uunet,uiucdcs,sun}!convex!tchrist
Convex Computer Corporation tchrist at convex.COM
"EMACS belongs in <sys/errno.h>: Editor too big!"
More information about the Comp.unix.questions
mailing list