home *** CD-ROM | disk | FTP | other *** search
- From: tchrist@convex.COM (Tom Christiansen)
- Newsgroups: comp.unix.questions,alt.sources
- Subject: Re: Need a 2-way alternative to popen()
- Message-ID: <100399@convex.convex.com>
- Date: 6 Mar 90 13:28:09 GMT
-
- 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!"
-