home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer) / NeXT_Developer-3.3.iso / NextDeveloper / Examples / UNIX / Subprocess / Subprocess.m < prev    next >
Encoding:
Text File  |  1994-04-21  |  7.3 KB  |  324 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.  *==========================================================*/
  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.     blockLoc = &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.         } else {
  143.             // close the master and reset the device
  144.             // name so that the master opens it properly
  145.             *msLoc = 'p';
  146.             close(*master);
  147.         }
  148.         }
  149.     } /* hunting through a bank of ptys */
  150.     } /* hunting through blocks of ptys in all the right places */
  151.     *master = -1;
  152.     *slave = -1;
  153. }
  154.  
  155.  
  156. @implementation Subprocess
  157.  
  158. /*==========================================================
  159.  *
  160.  * Public Instance Methods
  161.  *
  162.  *==========================================================*/
  163.  
  164. - init:(const char *)subprocessString
  165.     // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
  166. {
  167.     return
  168.     [self
  169.         init:subprocessString
  170.         withDelegate:nil
  171.         andPtySupport:NO
  172.         andStdErr:YES];
  173. }
  174.  
  175. - init:(const char *)subprocessString
  176.     withDelegate:theDelegate
  177.     andPtySupport:(BOOL)wantsPty
  178.     andStdErr:(BOOL)wantsStdErr
  179.     // initializes an instance of Subprocess and corresponding UNIX process
  180. {
  181.     int pipeTo[2];        // for non-Pty support
  182.     int pipeFrom[2];
  183.     int    tty, numFds, fd;    // for temporary use
  184.     int processGroup;
  185.     int pidChild;        // needed because childPid does not exist
  186.                 // until Subprocess is instantiated
  187.  
  188.     if (wantsPty)
  189.     {
  190.         tty = open("/dev/tty", O_RDWR);
  191.     getptys(&masterPty,&slavePty);
  192.     if (masterPty <= 0 || slavePty <= 0)
  193.     {
  194.         showError("Error grabbing ptys for subprocess.", theDelegate);
  195.         return self;
  196.     }
  197.     // remove the controlling tty if launched from a shell,
  198.     // but not Workspace;
  199.     // so that we have job control over the parent application in shell
  200.     // and so that subprocesses can be restarted in Workspace
  201.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
  202.     {
  203.         ioctl(tty, TIOCNOTTY, 0);
  204.         close(tty);
  205.     }
  206.     }
  207.     else
  208.     {
  209.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
  210.     {
  211.         showError("Error starting UNIX pipes to subprocess.", theDelegate);
  212.         return self;
  213.     }
  214.     }
  215.     
  216.     switch (pidChild = vfork())
  217.     {
  218.     case -1:    // error
  219.     showError("Error starting UNIX vfork of subprocess.", theDelegate);
  220.     return self;
  221.  
  222.     case 0:    // child
  223.     if (wantsPty)
  224.     {
  225.         dup2(slavePty, 0);
  226.         dup2(slavePty, 1);
  227.         if (wantsStdErr)
  228.         dup2(slavePty, 2);
  229.     }
  230.     else
  231.     {
  232.         dup2(pipeTo[0], 0);
  233.         dup2(pipeFrom[1], 1);
  234.         if (wantsStdErr)
  235.         dup2(pipeFrom[1], 2);
  236.     }
  237.     
  238.     numFds = getdtablesize();
  239.     for (fd=3; fd<numFds; fd++)
  240.         close(fd);
  241.  
  242.     processGroup = getpid();
  243.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  244.     setpgrp (0, processGroup);
  245.     
  246.     // we exec a /bin/sh so that cmds are easier to specify for the user
  247.     execl("/bin/sh", "sh", "-c", subprocessString, 0);
  248.     perror("vfork (child)"); // should never gets here tho
  249.     exit(1);
  250.  
  251.     default:    // parent
  252.     [self setDelegate:theDelegate];
  253.     childPid = pidChild;
  254.  
  255.     if (wantsPty)
  256.     {
  257.         close(slavePty);
  258.         
  259.         fpToChild = fdopen(masterPty, "w");
  260.         fromChild = masterPty;
  261.     }
  262.     else
  263.     {
  264.         close(pipeTo[0]);
  265.         close(pipeFrom[1]);
  266.     
  267.         fpToChild = fdopen(pipeTo[1], "w");
  268.         fromChild = pipeFrom[0];
  269.     }
  270.  
  271.     setbuf(fpToChild, NULL);
  272.     DPSAddFD(
  273.         fromChild,
  274.         (DPSFDProc)fdHandler,
  275.         (id)self,
  276.         NX_MODALRESPTHRESHOLD+1);
  277.     return self;
  278.     }
  279. }
  280.  
  281. - send:(const char *)string withNewline:(BOOL)wantNewline
  282. {
  283.     fputs(string, fpToChild);
  284.     if (wantNewline)
  285.         fputc('\n', fpToChild);
  286.     return self;
  287. }
  288.  
  289. - send:(const char *)string
  290. {
  291.     [self send:string withNewline:YES];
  292.     return self;
  293. }
  294.  
  295. - terminateInput
  296.     // effectively sends an EOF to the child process stdin
  297. {
  298.     fclose(fpToChild);
  299.     return self;
  300. }
  301.  
  302. - terminate:sender
  303. {
  304.     if (childPid)
  305.     {
  306.     kill(childPid+1, SIGTERM);
  307.     [self childDidExit];
  308.     }
  309.     return self;
  310. }
  311.  
  312. - setDelegate:anObject
  313. {
  314.     delegate = anObject;
  315.     return self;
  316. }
  317.  
  318. - delegate
  319. {
  320.     return delegate;
  321. }
  322.  
  323. @end
  324.