This is Subprocess.m in view mode; [Download] [Up]
//************************************************************************ // // Subprocess.m // // Launch and monitor UNIX subprocesses. // // by Felipe A. Rodriguez // // The base for this file was derived from: // // Subprocess.m (v10) // by Charles L. Oei // pty support by Joe Freeman // Subprocess Example, Release 2.0 // NeXT Computer, Inc. // // This code is supplied "as is" the author makes no warranty as to its // suitability for any purpose. This code is free and may be distributed // in accordance with the terms of the: // // GNU GENERAL PUBLIC LICENSE // Version 2, June 1991 // copyright (C) 1989, 1991 Free Software Foundation, Inc. // 675 Mass Ave, Cambridge, MA 02139, USA // //************************************************************************ #import "GKdefs.h" #import "Subprocess.h" #import "Coordinator.h" #import <appkit/nextstd.h> #import <appkit/Application.h> #import <appkit/Panel.h> #import <ansi/string.h> #import <dpsclient/dpsNeXT.h> #import <defaults/defaults.h> #define PTY_TEMPLATE "/dev/pty??" #define PTY_LENGTH 11 static void showError(); //************************************************************************ // // Private Instance Methods // //************************************************************************ @interface Subprocess(Private) - childDidExit; - fdHandler:(int)theFd; @end @implementation Subprocess(Private) - childDidExit // cleanup after a child process exits { if (childPid) { DPSRemoveFD(fromChild); close(fromChild); fclose(fpToChild); childPid=0; // specify that child is dead if (delegate && [delegate respondsTo:@selector(subprocessDone)]) [delegate perform:@selector(subprocessDone)]; } return self; } //************************************************************************ // // Called when output from UNIX subprocess is available // //************************************************************************ - fdHandler:(int)theFd // DPS handler for output from subprocess { if (((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) < 0) || (!bufferCount)) { [self childDidExit]; return self; } outputBuffer[bufferCount] = '\0'; [delegate subprocessOutput:(void *)&outputBuffer]; return self; } @end //************************************************************************ // // Private Utility Routines // //************************************************************************ static void showError (const char *errorString, id theDelegate) // ensure errors never get dropped on the floor { if(theDelegate && [theDelegate respondsTo:@selector(showAlert:)]) [theDelegate perform:@selector(showAlert:) with:(void *)errorString]; else if(NXApp) // no delegate, but we're running w/in an App NXRunAlertPanel(0, errorString, 0, 0, 0); else perror(errorString); //output errstr on stderr } //***************************************************************************** // // DPS registered function which recieves output from UNIX subprocess // // void DPSAddFD(int fd, DPSFDProc handler, void *userData, int priority) // fdhandler registered to read file descriptor fromChild // //***************************************************************************** static void fdHandler(int theFd, id self) { [self fdHandler:theFd]; } //***************************************************************************** // // attempt to setup the pty pair // //***************************************************************************** static void getptys (int *master, int *slave) { 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; /* new tty discipline */ 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); //Open device assign fd to master if (*master >= 0) //If master 0 or >, open succesful { *msLoc = 't'; *slave = open(device, O_RDWR); if (*slave >= 0) { /* set parameters -- stty */ (void) ioctl(*slave, TIOCSETP, (char *)&setp); /* set special characters */ (void) ioctl(*slave, TIOCSETC, (char *)&setc); /* set line discipline */ (void) ioctl(*slave, TIOCSETD, (char *)&setd); /* set local special chars */ (void) ioctl(*slave, TIOCSLTC, (char *)&sltc); /* set entire local mode word */ (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; //If we get here we were unable to open *slave = -1; //master and/or slave pty's } @implementation Subprocess //************************ Public Instance Methods **************************** //***************************************************************************** // // initializes an instance of Subprocess and corresponding UNIX process // //***************************************************************************** - init:(const char *)subprocessString withDelegate:theDelegate andStdErr:(BOOL)wantsStdErr { static char logFileName[MAXPATHLEN + 1]; // syslogd logging file int tty, numFds, fd; // for temporary use int processGroup; int pidChild; // childPid does not exist until Subprocess is instantiated // reset syslogd daemon if(theDelegate && [theDelegate respondsTo:@selector(syslogdReset)]) [theDelegate perform:@selector(syslogdReset)]; // /dev/tty kernel synm for controlling tty tty = open("/dev/tty", O_RDWR); // open the controlling tty getptys(&masterPty,&slavePty); // find and open psuedo terms strcpy(logFileName, NXGetDefaultValue([NXApp appName], "locFIFO")); if (masterPty <= 0 || slavePty <= 0) { showError("Error grabbing ptys for subprocess.", theDelegate); 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); } switch (pidChild = fork()) // fork to create child process { // which will monitor pppd case -1: // error showError("Error starting UNIX vfork of subprocess.", theDelegate); return self; case 0: // child -- Vfork returns 0 in the child's context dup2(slavePty, 0); // dup slavePty to be stdin dup2(slavePty, 1); // dup slavePty to be stdout if(wantsStdErr) dup2(slavePty, 2); // dup slavePty to be stderr numFds = getdtablesize(); for(fd=3; fd < numFds; fd++) // Child should close all open close(fd); // fd's beyond our basic 3 processGroup = getpid(); // return current pid // think -- tty was opened by parent so set pgrp of tty // to that of ourselves ioctl(0, TIOCSPGRP, (char *)&processGroup); setpgrp (0, processGroup); // make curr process process grp leader if(strcmp(NXGetDefaultValue([NXApp appName],"useFIFO"), "YES") == 0) { if((log = open(logFileName, O_RDONLY)) < 0) // open FIFO perror("Unable to open FIFO special file."); } else { // open log file if(!(fp = fopen(logFileName, "r"))) perror("Unable to open log file."); else fseek(fp, 0, SEEK_END); } switch (vfork()) // fork to exec pppd, parent { // will monitor pppd case -1: // error showError("Error during vfork to exec pppd", theDelegate); return self; case 0: // child -- Vfork returns 0 in the child's context execl("/bin/sh", "sh", "-c", subprocessString, (char *)0); perror("error exec'ing pppd"); exit(1); default: // parent -- vfork returns pid of the child if(strcmp(NXGetDefaultValue([NXApp appName],"useFIFO"), "YES") == 0) [self readFromFIFO]; // use a FIFO as IPC w/pppd else [self readFromFile]; // use a File as IPC w/pppd } perror("vfork 1st (child)"); exit(1); default: // parent -- vfork returns pid of the child in the // parent's context delegate = theDelegate; childPid = pidChild; close(slavePty); fpToChild = fdopen(masterPty, "w"); //return ptr to masterPty fromChild = masterPty; setbuf(fpToChild, NULL); // no buffering // Registers fdhandler to be called when their is activity with // file descriptor fromChild // void DPSAddFD(int fd, DPSFDProc handler, void *userData, // int priority) DPSAddFD(fromChild, (DPSFDProc)fdHandler, (id)self, NX_BASETHRESHOLD+1); return self; } } //***************************************************************************** // // Attempt to kill our child as well as pppd and its subprocesses at any // stage in their respective lifetimes. This is trickier than it sounds. // //***************************************************************************** - terminate:sender { FILE *ff; int pid; int prgrp; char lockPath[32] = {"rm usr/spool/uucp/LCK/LCK.."}; if((ff = fopen( "/etc/ppp/ppp0.pid", "r")) == NULL) { if(childPid) { // get the child's process group prgrp = getpgrp(childPid); // kill the child's group with killpg(prgrp, SIGKILL); // extreme prejudice strcpy(Path, lockPath); strcat(Path, NXGetDefaultValue([NXApp appName], MODEMPORT)); system(Path); // remove uucp lock from port } } else // attempt to do a clean hit on a properly running pppd { if( fscanf( ff, "%d", &pid)<1) perror("Unable to read pid from ppp0.pid\n"); fclose( ff); if( kill( pid, SIGINT)==-1) perror("killing pppd"); if (childPid) kill(childPid, SIGTERM); } [self childDidExit]; return self; } //***************************************************************************** // // read pppd output written by syslogd to a FIFO // //***************************************************************************** - readFromFIFO { static int nread; static char buff[BUFFERSIZE]; for(;;) // reads log and writes to pty/user { if((nread = read(log, buff, BUFFERSIZE)) < 0) { perror("Error reading FIFO."); break; } if(write(1, buff, nread) != nread) perror("Error writing to Slave Pty."); } return self; } //***************************************************************************** // // read pppd output written by syslogd to an ordinary file // //***************************************************************************** - readFromFile { register int ch; struct timeval second; fd_set zero; FD_ZERO(&zero); second.tv_sec = 1; second.tv_usec = 0; for(;;) // reads log and writes to pty/user { while ((ch = getc(fp)) != EOF) if (putchar(ch) == EOF) perror("Error writing to Slave Pty."); if (ferror(fp)) { perror("Error reading FIFO."); break; } (void)fflush(stdout); /* Sleep(3) is eight system calls. Do it fast. */ if (select(0, &zero, &zero, &zero, &second) == -1) perror("select: %s"); clearerr(fp); } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.