home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 0 / 0993 < prev    next >
Internet Message Format  |  1990-12-28  |  6KB

  1. From: tchrist@convex.COM (Tom Christiansen)
  2. Newsgroups: comp.unix.questions,alt.sources
  3. Subject: Re: Need a 2-way alternative to popen()
  4. Message-ID: <100399@convex.convex.com>
  5. Date: 6 Mar 90 13:28:09 GMT
  6.  
  7. In <12430@csli.Stanford.EDU>, mpf@csli.Stanford.EDU (Michael Frank) writes:
  8.  
  9. >Yeah, I was afraid it would be something like that.  I just hoped
  10. >there would be an easier way.  Oh well, does anyone have example
  11. >source code that does this kind of thing?  Especially the hard parts?
  12.  
  13. Here is something I wrote to do this 5 years ago.  It worked for me then,
  14. but I've not used it since.  It lints cleanly at least. :-)  It uses 
  15. socketpair(2) and vfork(2).  Use this way:
  16.  
  17.     FILE *ip, *op;
  18.     if (process("some csh_cmd", &ip, &op) == -1) { error(); }
  19.     /* processing with fprintf(op, ...) and fscanf(ip, ...) */
  20.     process_close(ip);
  21.  
  22. hope this helps,
  23.  
  24. --tom
  25.  
  26.  
  27. /* process.c 
  28.  *
  29.  *  written by tom christiansen on Wed May 22 15:02:19 CDT 1985 to open
  30.  *  a socket pair and let the user read and write from both sides.
  31.  *  return -1 on error, otherwise put the correct file pointers
  32.  *  into *input and *output.  
  33.  *
  34.  *  CAVEAT UTILITOR:
  35.  *      you will block forever if one of the sides of the
  36.  *      pipes blocks because of too much in the buffer.  
  37.  */
  38.  
  39. #include <stdio.h>
  40. #include <signal.h>
  41. #include <sys/param.h>
  42. #include <sys/wait.h>
  43. #include <sys/socket.h>
  44.  
  45. #define READ    0
  46. #define WRITE   1
  47. #define CHILD   0
  48. #define PARENT  1
  49.  
  50. /*
  51.  *  define QUIET if you don't want error messages
  52.  *  to print out if something blows up due to test
  53.  *  macros.  this assumes you will perror() it yourself
  54.  *  when the routine returns.
  55.  */
  56. #ifdef QUIET
  57. #   define announce(x)  /* nothing at all */
  58. #else
  59. #   define announce(x) perror(x)
  60. #endif QUIET
  61.  
  62. /*
  63.  *  first some macros to avoid lots of typing and ugly
  64.  *  code.
  65.  */
  66. #define test0(x)    \
  67.     if (!(x)) { \
  68.         announce("process: x"); \
  69.         return -1;  \
  70.     }
  71.  
  72. #define test(x) \
  73.     if ( (x) < 0 ) {    \
  74.         announce("process: x"); \
  75.         return -1;  \
  76.     }
  77.  
  78. char FD_READ[] = "r";
  79. char FD_WRITE[] = "w";
  80.  
  81. /*
  82.  *  next a static array to hold the pid of 
  83.  *  the process we create so we can wait for
  84.  *  it to die when we close up.  there is enough
  85.  *  room for all possible file descriptors (NOFILE)
  86.  *  so this function may be called repeatedly by
  87.  *  a program with no ill effects.
  88.  */
  89. static  int pids[NOFILE];
  90.  
  91. /*****************************************************************
  92.  *
  93.  *  process - a function to do what popen does, but to 
  94.  *            give you back file pointers for read as
  95.  *            well as for write.
  96.  *
  97.  *****************************************************************/
  98.  
  99. int
  100. process(cmd,input,output)
  101.     char    *cmd;
  102.     FILE    **input,**output;
  103. {
  104.     int sock[2];
  105.     register  pid;
  106.  
  107.  
  108. /*
  109.  *  use connected socket pair so we can do reads and 
  110.  *  writes on these things.  if we used a pipe() call,
  111.  *  it would be unidirectional and we would have to 
  112.  *  make two calls to get 4 file descriptors.
  113.  */
  114.     test(socketpair(AF_UNIX,SOCK_STREAM,0,sock));
  115.  
  116. /*
  117.  *  fork for the child command.  don't bother doing
  118.  *  a real fork, since we're just going to exec anyway,
  119.  *  so borrow the parent's pages with a vfork.
  120.  */
  121.     if((pid = vfork()) == CHILD) { 
  122.  
  123. /*
  124.  *  close old stdin and stdout to make room for socket
  125.  *  descriptors.
  126.  */
  127.         test(close(READ));  
  128.         test(close(WRITE));
  129.  
  130. /*  
  131.  *  the kid will use the CHILD end.  connect both his stdin
  132.  *  and stdout to the CHILD socket, which is fine because 
  133.  *  unix domain sockets are bidirectional.
  134.  */
  135.         test(dup2(sock[CHILD], WRITE));
  136.         test(dup2(sock[CHILD], READ));
  137.  
  138. /*
  139.  *  don't need these anymore, and if we don't get rid of them, we 
  140.  *  won't be happy later on, because the process doesn't seem to die.
  141.  */
  142.         test(close(sock[CHILD]));
  143.         test(close(sock[PARENT]));
  144. /*
  145.  *  now do the command.  use the csh for tilda's sake.
  146.  */
  147.         execl("/bin/csh", "csh", "-fc", cmd, 0);
  148.         perror("process kid: execl");
  149.         _exit(1);
  150.     }
  151.  
  152. /*
  153.  *  -1 pid means we couldn't fork.
  154.  */
  155.     if(pid == -1) {
  156.         perror("process: vfork");
  157.         (void) close(sock[CHILD]);
  158.         (void) close(sock[PARENT]);
  159.         return -1;
  160.     }
  161.  
  162. /*
  163.  *  otherwise, we are the parent and healthy;
  164.  *  remember the kid pid for a later close.
  165.  */
  166.     pids[sock[PARENT]] = pid;
  167.  
  168. /*
  169.  *  connect up the user's input and output file
  170.  *  pointers to our end of the socket connection.
  171.  *  give him one for read and one for write.
  172.  */
  173.     test0(*input = fdopen(sock[PARENT],FD_READ));
  174.     test0(*output = fdopen(sock[PARENT],FD_WRITE));
  175.  
  176.     test(close(sock[CHILD]));
  177.  
  178.     return 0;
  179. }
  180.  
  181.  
  182. /****************************************************************
  183.  *  close up the passed file and wait for the 
  184.  *  child to die.
  185.  ***************************************************************/
  186.  
  187. #undef test
  188. #define test(x) \
  189.     if ( (x) < 0 ) {    \
  190.         announce("process_close: x");   \
  191.         return -1;  \
  192.     }
  193.  
  194. /*
  195.  *  don't need them both since they are the 
  196.  *  same thing
  197.  */
  198. int
  199. process_close(input)
  200. FILE *input;
  201. {
  202.     register f,r, (*hstat)(), (*istat)(), (*qstat)();
  203.     int status;
  204.  
  205.     f = fileno(input);
  206.     test(fclose(input)); 
  207. /*
  208.  *  don't need to close also output, as it is the same 
  209.  */
  210.  
  211. /*
  212.  *  protect ourselves from unfriendly signals while
  213.  *  waiting for child's death.
  214.  */
  215.     istat = signal(SIGINT, SIG_IGN);
  216.     qstat = signal(SIGQUIT, SIG_IGN);
  217.     hstat = signal(SIGHUP, SIG_IGN);
  218.  
  219. /*
  220.  *  wait for the child to die, keeping track of status.
  221.  *  we saved the kid pid in the pids[] array when we 
  222.  *  first did the process call.
  223.  */
  224.     while((r = wait((union wait *)&status)) != pids[f] && r == -1)
  225.         ;
  226.     if(r == -1)
  227.         status = -1;
  228.  
  229. /*  
  230.  *  restore old sig values.
  231.  */
  232.     (void) signal(SIGINT, istat);
  233.     (void) signal(SIGQUIT, qstat);
  234.     (void) signal(SIGHUP, hstat);
  235.  
  236.     return(status);
  237.  
  238. }
  239.  
  240.  
  241. /* lint output:
  242. process.c:
  243. */
  244. --
  245.  
  246.     Tom Christiansen                       {uunet,uiucdcs,sun}!convex!tchrist 
  247.     Convex Computer Corporation                            tchrist@convex.COM
  248.          "EMACS belongs in <sys/errno.h>: Editor too big!"
  249.