|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.