home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / UNIX / Subprocess / Subprocess.m < prev    next >
Text File  |  1992-06-01  |  7KB  |  319 lines

  1. /*
  2.     Subprocess.m    (v10)
  3.     by Charles L. Oei
  4.     pty support by Joe Freeman
  5.     Subprocess Example, Release 2.0
  6.     NeXT Computer, Inc. 
  7.  
  8.     You may freely copy, distribute and reuse the code in this example.
  9.     NeXT disclaims any warranty of any kind, expressed or implied, as to
  10.     its fitness for any particular use.
  11. */
  12.  
  13. #import "Subprocess.h"
  14. // #import <sgtty.h>    // needed to compile under Release 1.0
  15. #import <appkit/nextstd.h>
  16. #import <appkit/Application.h>
  17. #import <appkit/Panel.h>
  18.  
  19. #define    PTY_TEMPLATE "/dev/pty??"
  20. #define    PTY_LENGTH 11
  21.  
  22. static void showError();
  23.  
  24.  
  25. /*==========================================================
  26.  *
  27.  * Private Instance Methods
  28.  *
  29.  *==============================================&Bs========*/
  30.  
  31. @interface Subprocess(Private)
  32. - childDidExit;
  33. - fdHandler:(int)theFd;
  34. @end
  35.  
  36. @implementation Subprocess(Private)
  37.  
  38. - childDidExit
  39.     // cleanup after a child process exits
  40. {
  41.     if (childPid)
  42.     {
  43.     DPSRemoveFD(fromChild);
  44.     close(fromChild);
  45.     fclose(fpToChild);
  46.     childPid=0;    // specify that child is dead
  47.     if (delegate && [delegate respondsTo:@selector(subprocessDone)])
  48.         [delegate perform:@selector(subprocessDone)];
  49.     }
  50.     return self;
  51. }
  52.  
  53. - fdHandler:(int)theFd
  54.     // DPS handler for output from subprocess
  55. {
  56.     if (((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) < 0) ||
  57.       (!bufferCount))
  58.     {
  59.     [self childDidExit];
  60.     return self;
  61.     }
  62.     outputBuffer[bufferCount] = '\0';
  63.     if (delegate && [delegate respondsTo:@selector(subprocessOutput:)])
  64.     [delegate perform:@selector(subprocessOutput:)
  65.           with:(void *)&outputBuffer];
  66.     return self;
  67. }
  68.  
  69. @end
  70.  
  71.  
  72. /*==========================================================
  73.  *
  74.  * Private Utility Routines
  75.  *
  76.  *==========================================================*/
  77.  
  78. static void
  79. showError (const char *errorString, id theDelegate)
  80.     // ensure errors never get dropped on the floor
  81. {
  82.     if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
  83.     [theDelegate
  84.         perform:@selector(subprocessError:)
  85.         with:(void *)errorString];
  86.     else if (NXApp)    // no delegate, but we're running w/in an App
  87.     NXRunAlertPanel(0, errorString, 0, 0, 0);
  88.     else
  89.     perror(errorString);
  90. }
  91.  
  92. static void
  93. fdHandler (int theFd, id self)
  94.     // DPS handler for output from subprocess
  95. {
  96.     [self fdHandler:theFd];
  97. }
  98.  
  99. static void
  100. getptys (int *master, int *slave)
  101.     // attempt to setup the ptys
  102. {
  103.     char device[PTY_LENGTH];
  104.     char *block, *num;
  105.     char *blockLoc; // specifies the location of block for the device string
  106.     char *numLoc; // specifies the pty name with the digit ptyxD
  107.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  108.     
  109.     struct sgttyb setp =
  110.     {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  111.     struct tchars setc =
  112.     {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  113.     struct ltchars sltc =
  114.     {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  115.     int    lset =
  116.     (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  117.     int    setd = NTTYDISC;
  118.     
  119.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  120.     blockL&Bt &device[ strlen("/dev/pty") ];
  121.     numLoc = &device[ strlen("/dev/pty?") ];
  122.     msLoc = &device[ strlen("/dev/") ];
  123.     for (block = "pqrs"; *block; block++)
  124.     {
  125.     *blockLoc = *block;
  126.     for (num = "0123456789abcdef"; *num; num++)
  127.     {
  128.         *numLoc = *num;
  129.         *master = open(device, O_RDWR);
  130.         if (*master >= 0)
  131.         {
  132.         *msLoc = 't';
  133.         *slave = open(device, O_RDWR);
  134.         if (*slave >= 0)
  135.         {
  136.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  137.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  138.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  139.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  140.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  141.             return;
  142.         }
  143.         }
  144.     } /* hunting through a bank of ptys */
  145.     } /* hunting through blocks of ptys in all the right places */
  146.     *master = -1;
  147.     *slave = -1;
  148. }
  149.  
  150.  
  151. @implementation Subprocess
  152.  
  153. /*==========================================================
  154.  *
  155.  * Public Instance Methods
  156.  *
  157.  *==========================================================*/
  158.  
  159. - init:(const char *)subprocessString
  160.     // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
  161. {
  162.     return
  163.     [self
  164.         init:subprocessString
  165.         withDelegate:nil
  166.         andPtySupport:NO
  167.         andStdErr:YES];
  168. }
  169.  
  170. - init:(const char *)subprocessString
  171.     withDelegate:theDelegate
  172.     andPtySupport:(BOOL)wantsPty
  173.     andStdErr:(BOOL)wantsStdErr
  174.     // initializes an instance of Subprocess and corresponding UNIX process
  175. {
  176.     int pipeTo[2];        // for non-Pty support
  177.     int pipeFrom[2];
  178.     int    tty, numFds, fd;    // for temporary use
  179.     int processGroup;
  180.     int pidChild;        // needed because childPid does not exist
  181.                 // until Subprocess is instantiated
  182.  
  183.     if (wantsPty)
  184.     {
  185.         tty = open("/dev/tty", O_RDWR);
  186.     getptys(&masterPty,&slavePty);
  187.     if (masterPty <= 0 || slavePty <= 0)
  188.     {
  189.         showError("Error grabbing ptys for subprocess.", theDelegate);
  190.         return self;
  191.     }
  192.     // remove the controlling tty if launched from a shell,
  193.     // but not Workspace;
  194.     // so that we have job control over the parent application in shell
  195.     // and so that subprocesses can be restarted in Workspace
  196.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
  197.     {
  198.         ioctl(tty, TIOCNOTTY, 0);
  199.         close(tty);
  200.     }
  201.     }
  202.     else
  203.     {
  204.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
  205.     {
  206.         showError("Error starting UNIX pipes to subpro&C.", theDelegate);
  207.         return self;
  208.     }
  209.     }
  210.     
  211.     switch (pidChild = vfork())
  212.     {
  213.     case -1:    // error
  214.     showError("Error starting UNIX vfork of subprocess.", theDelegate);
  215.     return self;
  216.  
  217.     case 0:    // child
  218.     if (wantsPty)
  219.     {
  220.         dup2(slavePty, 0);
  221.         dup2(slavePty, 1);
  222.         if (wantsStdErr)
  223.         dup2(slavePty, 2);
  224.     }
  225.     else
  226.     {
  227.         dup2(pipeTo[0], 0);
  228.         dup2(pipeFrom[1], 1);
  229.         if (wantsStdErr)
  230.         dup2(pipeFrom[1], 2);
  231.     }
  232.     
  233.     numFds = getdtablesize();
  234.     for (fd=3; fd<numFds; fd++)
  235.         close(fd);
  236.  
  237.     processGroup = getpid();
  238.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  239.     setpgrp (0, processGroup);
  240.     
  241.     // we exec a /bin/sh so that cmds are easier to specify for the user
  242.     execl("/bin/sh", "sh", "-c", subprocessString, 0);
  243.     perror("vfork (child)"); // should never gets here tho
  244.     exit(1);
  245.  
  246.     default:    // parent
  247.     [self setDelegate:theDelegate];
  248.     childPid = pidChild;
  249.  
  250.     if (wantsPty)
  251.     {
  252.         close(slavePty);
  253.         
  254.         fpToChild = fdopen(masterPty, "w");
  255.         fromChild = masterPty;
  256.     }
  257.     else
  258.     {
  259.         close(pipeTo[0]);
  260.         close(pipeFrom[1]);
  261.     
  262.         fpToChild = fdopen(pipeTo[1], "w");
  263.         fromChild = pipeFrom[0];
  264.     }
  265.  
  266.     setbuf(fpToChild, NULL);
  267.     DPSAddFD(
  268.         fromChild,
  269.         (DPSFDProc)fdHandler,
  270.         (id)self,
  271.         NX_MODALRESPTHRESHOLD+1);
  272.     return self;
  273.     }
  274. }
  275.  
  276. - send:(const char *)string withNewline:(BOOL)wantNewline
  277. {
  278.     fputs(string, fpToChild);
  279.     if (wantNewline)
  280.         fputc('\n', fpToChild);
  281.     return self;
  282. }
  283.  
  284. - send:(const char *)string
  285. {
  286.     [self send:string withNewline:YES];
  287.     return self;
  288. }
  289.  
  290. - terminateInput
  291.     // effectively sends an EOF to the child process stdin
  292. {
  293.     fclose(fpToChild);
  294.     return self;
  295. }
  296.  
  297. - terminate:sender
  298. {
  299.     if (childPid)
  300.     {
  301.     kill(childPid+1, SIGTERM);
  302.     [self childDidExit];
  303.     }
  304.     return self;
  305. }
  306.  
  307. - setDelegate:anObject
  308. {
  309.     delegate = anObject;
  310.     return self;
  311. }
  312.  
  313. - delegate
  314. {
  315.     return delegate;
  316. }
  317.  
  318. @end
  319.