This is Process.m in view mode; [Download] [Up]
/* * Process -- manage i/o with simple subprocesses. * Considerably tweaked relative of /NextDeveloper/Examples/Subprocess. * M. J. Hawley * Copyright (c) MIT Media Laboratory * mike@media-lab.mit.edu */ #import "Process.h" @interface Process(Private) - childDidExit; - fdHandler:(int)theFd; @end static void showError (const char *s, id delegate){ // ensure errors never get lost if (delegate && [delegate respondsTo:@selector(processError:)]) [delegate perform:@selector(processError:) with:(void *)s]; else if (NXApp) // no delegate, but we're running w/in an App NXRunAlertPanel(0, s, 0, 0, 0); else perror(s); } static void fdHandler (int fd, id self) { // DPS handler for output from process [self fdHandler:fd]; } static void getptys(int *master, int *slave){ // attempt to setup the ptys #define PTY_TEMPLATE "/dev/pty??" #define PTY_LENGTH 11 char device[PTY_LENGTH]; char *block, *num; char *blockLoc; // specifies the location of block for the device string char *numLoc; // specifies the pty name with the digit ptyxD char *msLoc; // specifies the master (ptyxx) or slave (ttyxx) struct sgttyb setp = {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)}; struct tchars setc = {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK}; struct ltchars sltc = {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT}; int lset = (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ); int setd = NTTYDISC; strcpy(device, PTY_TEMPLATE); // string constants are not writable blockLoc = &device[ strlen("/dev/pty") ]; numLoc = &device[ strlen("/dev/pty?") ]; msLoc = &device[ strlen("/dev/") ]; for (block = "pqrs"; *block; block++){ *blockLoc = *block; for (num = "0123456789abcdef"; *num; num++) { *numLoc = *num; *master = open(device, O_RDWR); if (*master >= 0) { *msLoc = 't'; *slave = open(device, O_RDWR); if (*slave >= 0) { (void) ioctl(*slave, TIOCSETP, (char *)&setp); (void) ioctl(*slave, TIOCSETC, (char *)&setc); (void) ioctl(*slave, TIOCSETD, (char *)&setd); (void) ioctl(*slave, TIOCSLTC, (char *)&sltc); (void) ioctl(*slave, TIOCLSET, (char *)&lset); return; } } } /* hunting through a bank of ptys */ } /* hunting through blocks of ptys in all the right places */ *master = -1; *slave = -1; } static int iwait(fd, timeout) long fd; unsigned long timeout; /* in seconds */ /* * Wait until 'fd' is ready for reading, or 'timeout'. * Return '>=0' when 'fd' is readable, '0' if timeout, '-1' on error. * Example: 'iwait(f,0)' polls a file descriptor * without blocking and returns true if it's readable; * e.g., 'iwait(0,0)' is true when standard input is contains something. */ { struct timeval t; int readfd = 1<<fd; t.tv_sec = timeout, t.tv_usec = 0; return (int)select(sizeof(int)*8, (fd_set *)&readfd, (fd_set *)0, (fd_set *)0, &t); } @implementation Process(Private) - childDidExit { // cleanup after a child process exits if (childPid) { if (from) DPSRemoveFD(from); close(from); if (fpTo) fclose(fpTo); if (fpFrom) fclose(fpFrom); fpTo = fpFrom = (FILE *)0; childPid=0; // specify that child is dead if (delegate && [delegate respondsTo:@selector(processDone)]) [delegate perform:@selector(processDone)]; } return self; } - fdHandler:(int)fd { // DPS handler for output from process if (iwait(fd,1)<=0) return self; if (((bufferCount=read(fd,buffer,BUFSIZE-1))<0)||(!bufferCount)) return [self childDidExit]; buffer[bufferCount] = '\0'; if (delegate && [delegate respondsTo:action]) [delegate perform:action with:(void *)&buffer]; return self; } @end @implementation Process + new:(char *)process delegate:del { self = [Process alloc]; [self init:process delegate:del andPty:YES andStderr:YES]; return self; } + new:(char *)process delegate:del andPty:(BOOL)wantsPty andStderr:(BOOL)wantsStderr { self = [Process alloc]; [self init:process delegate:del andPty:wantsPty andStderr:wantsStderr]; return self; } - init:(char *)process delegate:del { return [self init:process delegate:del andPty:NO andStderr:NO]; } - init:(char *)process delegate:del andPty:(BOOL)pty andStderr:(BOOL)err { // initializes an instance of process and corresponding UNIX process int pipeTo[2]; int pipeFrom[2]; int tty, numFds, fd; // for temporary use int processGroup; int pidChild; // needed because childPid does not exist // until process is instantiated [self setAction:@selector(processOutput:)]; if (pty){ tty = open("/dev/tty", O_RDWR); getptys(&masterPty,&slavePty); if (masterPty <= 0 || slavePty <= 0) { showError("Error grabbing ptys for subprocess.", del); return self; } // remove the controlling tty if launched from a shell, // but not Workspace; // so that we have job control over the parent application in shell // and so that subprocesses can be restarted in Workspace if ((tty<0) && ((tty = open("/dev/tty", 2))>=0)) { ioctl(tty, TIOCNOTTY, 0); close(tty); } } else if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0){ showError("Error starting UNIX pipes to process.", del); return self; } switch (pidChild = vfork()){ case -1: // error showError("Error starting UNIX vfork of process.", del); return self; case 0: // child if (pty) { dup2(slavePty, 0); dup2(slavePty, 1); if (err) dup2(slavePty, 2); } else { dup2(pipeTo[0], 0); dup2(pipeFrom[1], 1); if (err) dup2(pipeFrom[1], 2); } numFds = getdtablesize(); for (fd=3; fd<numFds; fd++) close(fd); processGroup = getpid(); ioctl(0, TIOCSPGRP, (char *)&processGroup); setpgrp (0, processGroup); // execl(process, 0); execl("/bin/sh", "sh", "-c", process, 0); perror("vfork (child)"); // should never gets here tho exit(1); default: // parent [self setDelegate:del]; childPid = pidChild; if (pty){ close(slavePty); fpTo = fdopen(masterPty, "w"); from = masterPty; fpFrom = fdopen(masterPty, "r"); } else { close(pipeTo[0]); close(pipeFrom[1]); fpTo = fdopen(pipeTo[1], "w"); from = pipeFrom[0]; fpFrom = fdopen(pipeFrom[0], "r"); } setbuf(fpTo, NULL); setbuf(fpFrom, NULL); DPSAddFD(from,(DPSFDProc)fdHandler,(id)self,NX_MODALRESPTHRESHOLD+1); // printf("added %d, %d, %d [%d %d]\n",from, self, delegate, pipeTo[1], pipeFrom[0]); return self; } } - puts:(char *)s { fputs(s, fpTo); return self; } static void _fgets(s,n,f) char *s; int n; FILE *f; { int fd = fileno(f); while (iwait(fd,3)>0){ if (read(fd,s,1) != 1){ *s = '\0'; return; } if (*s=='\n' || --n <= 0){ *++s = '\0'; return ; } if (*s != '\r') ++s; } *++s = '\0'; } - gets:(char *)s :(int)n { *s = '\0'; _fgets(s,n,fpFrom); return self; } - terminate:sender{ if (childPid){ kill(childPid+1, SIGTERM); [self childDidExit]; } return self; } - setDelegate:anObject { delegate = anObject; return self; } - delegate { return delegate; } - setAction:(SEL)theAction { action = theAction; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.