tchrist@convex.COM (Tom Christiansen) (03/06/90)
In <12430@csli.Stanford.EDU>, mpf@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@convex.COM "EMACS belongs in <sys/errno.h>: Editor too big!"