Annotation of Examples/UNIX/Subprocess/Subprocess.m, revision 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.