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.