Annotation of Examples/UNIX/Subprocess/Subprocess.m, revision 1.1.1.1

1.1       root        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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.