[comp.protocols.appletalk] Executing Unix commands from the Mac via AUFS

paul@lfcs.ed.ac.uk (Paul Anderson) (03/22/89)

Quite a lot of people seem to be interested in my patches to AUFS, so I have
decided to post them. Here they are - this is a single patch file which
patches some of the source files for AUFS and adds a couple of new ones ...

----------------------------------------------------------------------------
*** README.PROCESSES	Tue Mar 21 18:58:38 1989
--- ../aufsp/README.PROCESSES	Tue Mar 21 20:42:05 1989
***************
*** 0 ****
--- 1,52 ----
+ 
+ 		AUFS and running UNIX processes from the Mac
+ 		============================================
+ 
+ This file contains a set of patches for the AUFS distributed with CAP 5.0.
+ These patches allow a Macintosh application to fork one or more Unix processes
+ on the AUFS server and comminicate with the running processes via a pipe.
+ To start a process, the Macintosh simply opens a file whose name
+ starts with a "!". Instead of opening the file, the server attempts to run
+ the named file as a process. The Macintosh can communicate with the process
+ simply by reading and writing to the file handle returned by the open command.
+ Closing the file kills the process. Opening commands with different names
+ allows more than one process to be active simultaneously. This arrangement
+ requires no extra software on the Macintosh and is very easy to use from
+ programs like Hypercard.
+ 
+ The program has been tested on Sun 3s and 4s, running under SunOS 3.2 and 4.0.
+ Some changes may be needed for other UNIX versions. It was written as an
+ experiment rather than a serious tool and it probably has lots of other
+ problems as well as those listed below - I am not likely to be doing any more
+ work on it myself, but I would be glad to hear of any improvements and
+ suggestions.
+ 
+ Some random thoughts .....
+ 
+ The filename has to be supplied as a full Mac volume pathname, including a
+ full unix pathname :- Eg:  sun:!/bin/ls
+ Where sun is the AUFS volume name.
+ 
+ The processes run with the same uid as the server. This is an obvious security
+ problem! Even if the server does not run as root, anybody can log onto it
+ and run a process with the uid of the server owner. I guess this could be
+ fixed quite easily.
+ 
+ It is quite easy to get the server into a deadlocked state - this happens eg.
+ if the UNIX process and the Mac program both issue reads on the pipe at the
+ same time. In our Hypercard applications, we isolated the communication with
+ the UNIX processes into a synchronous routine in Hypertalk - I think it would
+ be quite hard to prevent this problem in the server itself. Deadlocks like this
+ can be broken by issuing a ^C to the server.
+ 
+ Because of this, we don't run the processes in our main AUFS server, but
+ start a separate server specially for the application. This is why the
+ supplied version has Process debugging turned on by default and invents itself
+ a new name based on the user (See aufs.c if you want to change this).
+ I would be interested in any better solutions to the problem.
+ 
+ 
+ Paul Anderson                       JANET: paul@uk.ac.ed.lfcs
+ LFCS, Dept. of Computer Science     UUCP:  ..!mcvax!ukc!lfcs!paul	
+ University of Edinburgh             ARPA:  paul%lfcs.ed.ac.uk@nss.cs.ucl.ac.uk
+ Edinburgh EH9 3JZ, UK.              Tel:   031-667-1081 Ext 2788
*** Makefile	Tue Mar 21 17:48:11 1989
--- ../aufsp/Makefile	Thu Mar 16 10:57:42 1989
***************
*** 49,60 ****
  #
  # End of configurable options
  #
! SRCS=afpos.c afpvols.c afpfile.c afpdir.c afpfork.c \
  	afpmisc.c afpserver.c aufsicon.c abmisc2.c \
  	afpdt.c afpdid.c afposenum.c  afpavl.c \
  	afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \
  	afpudb.c afposncs.c
! OBJS=afpos.o afpvols.o afpfile.o \
  	afpmisc.o afpserver.o aufsicon.o abmisc2.o \
  	afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \
  	afposfi.o afpgc.o afppasswd.o aufsv.o \
--- 49,60 ----
  #
  # End of configurable options
  #
! SRCS=afpos.c afproc.c afpvols.c afpfile.c afpdir.c afpfork.c \
  	afpmisc.c afpserver.c aufsicon.c abmisc2.c \
  	afpdt.c afpdid.c afposenum.c  afpavl.c \
  	afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \
  	afpudb.c afposncs.c
! OBJS=afpos.o afproc.o afpvols.o afpfile.o \
  	afpmisc.o afpserver.o aufsicon.o abmisc2.o \
  	afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \
  	afposfi.o afpgc.o afppasswd.o aufsv.o \
***************
*** 94,99 ****
--- 94,102 ----
  afpos.o:	afpos.c
  	${CC} ${OSDEFS} ${CFLAGS} -c afpos.c
  
+ afproc.o:	afproc.c
+ 	${CC} ${OSDEFS} ${CFLAGS} -c afproc.c
+ 
  afposncs.o:	afposncs.c
  	${CC} ${OSDEFS} ${CFLAGS} -c afposncs.c
  
***************
*** 105,110 ****
--- 108,117 ----
  
  # Dependencies
  afpos.o:        afpos.c         $I/netat/appletalk.h \
+ 				$I/netat/aberrors.h $I/netat/abqueue.h \
+ 				$I/netat/afp.h \
+ 				afpvols.h $I/netat/afpcmd.h 
+ afproc.o:        afproc.c       $I/netat/appletalk.h \
  				$I/netat/aberrors.h $I/netat/abqueue.h \
  				$I/netat/afp.h \
  				afpvols.h $I/netat/afpcmd.h 
*** afpmisc.c	Tue Mar 21 17:03:25 1989
--- ../aufsp/afpmisc.c	Thu Mar 16 10:57:36 1989
***************
*** 49,58 ****
    {DBG_DEBG|DBG_SRVR,"Server"},
    {DBG_DEBG|DBG_UNIX,"Unix"},
    {DBG_DEBG|DBG_VOLS,"Volume"},
    {DBG_DEBG,"debug"}
  };
  
! #define DBTABN 11		/* size of debug table */
  
  char *DBLevelOpts()
  {
--- 49,59 ----
    {DBG_DEBG|DBG_SRVR,"Server"},
    {DBG_DEBG|DBG_UNIX,"Unix"},
    {DBG_DEBG|DBG_VOLS,"Volume"},
+   {DBG_DEBG|DBG_PROC,"Process"},
    {DBG_DEBG,"debug"}
  };
  
! #define DBTABN 12		/* size of debug table */
  
  char *DBLevelOpts()
  {
*** afps.h	Tue Mar 21 17:48:25 1989
--- ../aufsp/afps.h	Thu Mar 16 10:57:38 1989
***************
*** 62,67 ****
--- 62,68 ----
  #define DBG_DEBG 00200		/* in debug */
  #define DBG_UNIX 00400		/* debug unix routines */
  #define DBG_ENUM 01000		/* debug directory enumerations */
+ #define DBG_PROC 02000		/* debug processes */
  
  #define DBG_ALL (DBG_FILE|DBG_FORK|DBG_SRVR|DBG_VOLS| \
  		 DBG_OSIN|DBG_DIRS|DBG_DESK|DBG_DEBG| \
***************
*** 78,83 ****
--- 79,85 ----
  #define DBDEB (afp_dbug & DBG_DEBG)
  #define DBUNX (afp_dbug & DBG_UNIX)
  #define DBENU (afp_dbug & DBG_ENUM)
+ #define DBPRO (afp_dbug & DBG_PROC)
  
  /* afpdid.c */
  
*** afpserver.c	Tue Mar 21 17:03:29 1989
--- ../aufsp/afpserver.c	Thu Mar 16 10:57:39 1989
***************
*** 221,227 ****
      printf("SrvrDispatch cmd=%s (%d 0x%x), len=%d\n",
  	   d->dsp_name,cmd,cmd,len);
  
- 
    d->dsp_cnt++;			/* increment counter */
  
    if (d->dsp_flg & DSPF_DMPIN)
--- 221,226 ----
*** aufs.c	Tue Mar 21 17:48:26 1989
--- ../aufsp/aufs.c	Tue Mar 21 20:42:11 1989
***************
*** 178,200 ****
    exit(1);
  }
  
  /*
!  * generate default name from host name
   *
  */
  char *
  afs_default_name()
  {
!   char hostname[255];
    static char afsname[255];
    char *ap;
  
!   gethostname(hostname,(int) sizeof(hostname));
!   if ((ap = index(hostname,'.')) != NULL)
!     *ap = '\0';				/* remove trailing domain name */
!   sprintf(afsname,"%s Aufs",hostname);
    return(afsname);
  }
  
  doargs(argc,argv)
  int argc;
--- 178,202 ----
    exit(1);
  }
  
+ /*PAUL*/
+ /* Test server is run as user process, so there may be several on the same
+ host - invent a default name from the user id */
  /*
!  * generate default name from user name
   *
  */
  char *
  afs_default_name()
  {
!   char username[255];
    static char afsname[255];
    char *ap;
  
!   cuserid(username);
!   sprintf(afsname,"AUFSP (%s)",username);
    return(afsname);
  }
+ /*END*/
  
  doargs(argc,argv)
  int argc;
***************
*** 203,208 ****
--- 205,215 ----
    int c;
    extern char *optarg;
    extern int optind;
+ 
+ /*PAUL*/
+ /* Debug processes by default */
+       (void)SetDBLevel("Process");
+ /*END*/
    
    while ((c = getopt(argc,argv,"a:d:D:n:N:t:sV:U:G:P:c:l:z:S:R:X:")) != EOF) {
      switch (c) {
***************
*** 301,306 ****
--- 308,314 ----
    int srvinfolen;
    import word this_net;
    import byte this_node;
+   import void Breakin();
    char *getenv(),*env;
    int pid;
    int mask;
***************
*** 479,484 ****
--- 487,493 ----
    if (!DBDEB)
  #endif
      signal(SIGCHLD, inferiordone);
+   signal(SIGINT, Breakin);	/* PAUL - ctrl/C shuts down gracefully ! */
  #ifndef NOSHUTDOWNCODE
    signal(SIGHUP, killnow);	/* kill -HUP -- Immediate shutdown */
    signal(SIGTERM, killin5);	/* kill -TERM -- Timed (5 min) shutdown */
*** afpos.c	Tue Mar 21 17:48:24 1989
--- ../aufsp/afpos.c	Thu Mar 16 10:57:36 1989
***************
*** 135,140 ****
--- 135,141 ----
  #include "afppasswd.h"			/* in case we are using privates */
  #include "afposncs.h"
  #include "afpgc.h"
+ #include "afproc.h"
  
  #ifdef MAXBSIZE
  # define IOBSIZE MAXBSIZE	/* set to max buf entry size by if there */
***************
*** 205,210 ****
--- 206,213 ----
  #endif
  
  import int errno;
+ import PROCESS *FindFd();
+ import PROCESS *FindProcess();
  
  /*
   * AFPOS functions
***************
*** 879,884 ****
--- 882,891 ----
  OSClose(fd)
  int fd;
  {
+ 
+ /*PAUL*/
+   if (FindFd(fd)!=NOPROC) return ProcClose(fd);
+ /*END*/ 
    return(unix_close(fd));		/* return OSErr */
  }
  
***************
*** 914,919 ****
--- 921,933 ----
    }
  #endif
  
+ /*PAUL*/
+   {
+     PROCESS *p;
+     if ((p=FindFd(fd))!=NOPROC) return( ProcRead( p, r, reqcnt, rl ) );
+   }
+ /*END*/
+ 
    if (offs != *fpos)
      lseek(fd,(off_t)offs,L_SET);
  
***************
*** 991,996 ****
--- 1005,1017 ----
    if (wlen == 0)			/* zero byte request? */
      return(noErr);			/*  yes... no work */
  
+ /*PAUL*/
+   {
+     PROCESS *p;
+     if ((p=FindFd(fd))!=NOPROC) return( ProcWrite( p, wbuf, wlen ) );
+   }
+ /*END*/
+ 
    if (flg == 0) {
      if (offs != *fpos)
        *fpos = lseek(fd,(off_t)offs,L_SET);
***************
*** 1125,1131 ****
    if (DBOSI)
      printf("OSFileDirInfo on %s\n",path);
  
!   if (stat(path,&buf) != 0) {		/* file exists? */
      if (idir)				/* was in directory tree? */
        Idrdirid(ipdir, idir);		/* invalidate the entry then */
      return(aeObjectNotFound);		/* no... */
--- 1146,1165 ----
    if (DBOSI)
      printf("OSFileDirInfo on %s\n",path);
  
! /*PAUL*/
!   if (*fn=='!') {
!     PROCESS *p;
!     if ( ((p=FindProcess(fn))==NOPROC) || (stat(p->cmnd,&buf)!=0) ){
!       if (idir)				/* was in directory tree? */
! 	Idrdirid(ipdir, idir);		/* invalidate the entry then */
!       return(aeObjectNotFound);		/* no... */
!     }
!     buf.st_mode |= 0777;
!   }
!   else
! /*END*/
! 
!   if (stat(path,&buf) != 0) {         /* file exists? */
      if (idir)				/* was in directory tree? */
        Idrdirid(ipdir, idir);		/* invalidate the entry then */
      return(aeObjectNotFound);		/* no... */
***************
*** 1285,1300 ****
    if (DBDIR)
      printf("OSFileInfo on %s bm=%x\n",fn,bm);
  
!   if (bm & FP_ATTR)			/* skip attr if not requested */
!     OSGetAttr(ipdir,fn,&fdp->fdp_attr);
  
!   if (bm & FP_FINFO)			/* skip finfo if not requested */
!     OSGetFNDR(ipdir,fn,fdp->fdp_finfo);
  
    fdp->fdp_parms.fp_parms.fp_fileno = buf->st_ino;
    fdp->fdp_parms.fp_parms.fp_dflen = buf->st_size;
    fdp->fdp_parms.fp_parms.fp_rflen = 0;
  
    if (bm & FP_RFLEN) {
      OSfname(rpath,ipdir,fn,F_RSRC);	/* convert to rsrc name */
      if (stat(rpath,&stb) != 0)		/* to figure size of resource fork */
--- 1319,1344 ----
    if (DBDIR)
      printf("OSFileInfo on %s bm=%x\n",fn,bm);
  
! /*PAUL*/
!   if (*fn!='!') {
! /*END*/
!     if (bm & FP_ATTR)			/* skip attr if not requested */
!       OSGetAttr(ipdir,fn,&fdp->fdp_attr);
  
!     if (bm & FP_FINFO)			/* skip finfo if not requested */
!       OSGetFNDR(ipdir,fn,fdp->fdp_finfo);
! /*PAUL*/
!   }
! /*END/
  
    fdp->fdp_parms.fp_parms.fp_fileno = buf->st_ino;
    fdp->fdp_parms.fp_parms.fp_dflen = buf->st_size;
    fdp->fdp_parms.fp_parms.fp_rflen = 0;
  
+ /*PAUL*/
+   if (*fn=='!') fdp->fdp_parms.fp_parms.fp_rflen = 0;
+   else
+ /*END*/
    if (bm & FP_RFLEN) {
      OSfname(rpath,ipdir,fn,F_RSRC);	/* convert to rsrc name */
      if (stat(rpath,&stb) != 0)		/* to figure size of resource fork */
***************
*** 2209,2215 ****
    if (DBOSI)
      printf("OSCreateFile: creating %s with %s\n",path,
  	   (delf) ? "OverWrite" : "No OverWrite");
!   
    err = unix_stat(pathstr(pdir),&stb);
    if (err != noErr)
      return(err);
--- 2253,2263 ----
    if (DBOSI)
      printf("OSCreateFile: creating %s with %s\n",path,
  	   (delf) ? "OverWrite" : "No OverWrite");
! 
! /*PAUL*/
!   if (*file=='!') return ProcCreateFile( file, delf );
! /*END*/
! 
    err = unix_stat(pathstr(pdir),&stb);
    if (err != noErr)
      return(err);
***************
*** 2287,2292 ****
--- 2335,2344 ----
  
    if (DBOSI) 
      printf("OSOpenFork: Opening %s for %s\n",path,ms);
+ 
+ /*PAUL*/
+   if (*file=='!') return( ProcOpen( file, mo, fhdl ) );
+ /*END*/
  
    return(unix_open(path,mo,fhdl));
  }
*** afproc.c	Tue Mar 21 19:06:30 1989
--- ../aufsp/afproc.c	Thu Mar 16 10:57:46 1989
***************
*** 0 ****
--- 1,453 ----
+ /* Extended routines for AUFS to allow communication with UNIX processes.
+    Paul Anderson 21/9/88
+ */
+ 
+ #include <stdio.h>
+ #include <sys/filio.h>
+ #include <sys/file.h>
+ #include <sys/signal.h>
+ #include <netat/appletalk.h>
+ #include <netat/afp.h>
+ #include <errno.h>
+ #include <netat/afpcmd.h>
+ #include <signal.h>
+ #include "afps.h"
+ #include "afpvols.h"
+ #include "afppasswd.h"
+ #include "afposncs.h"
+ #include "afpgc.h"
+ #include "afproc.h"
+ 
+ export void Breakin();
+ export PROCESS *FindFd();
+ export PROCESS *FindProcess();
+ export PROCESS *FindPid();
+ export OSErr ProcOpen();
+ export OSErr ProcClose();
+ export OSErr ProcRead();
+ export OSErr ProcWrite();
+ export OSErr ProcCreateFile();
+ export OSErr ProcDelete();
+ 
+ extern int errno;
+ extern void killnow();		/* Terminate server */
+ 
+ PROCESS *procList = NOPROC;	/* Linked list of process records */
+ private int interrupt = 0;	/* CTRL/C pressed flag */
+ private int status = WAITING;	/* Current server status */
+ 
+ 
+ export void Breakin()
+ {
+   /* If we have an IO deadlock, break it */
+   if (status!=WAITING) { interrupt = 1; return; }
+ 
+   /* Otherwise, kill all the subprocesses & exit */
+   if (DBPRO)
+     printf( "PROCESS: Interrupt - Terminating all processes\n" );
+   while (procList != NOPROC) ProcDelete( procList->cmnd );
+   if (DBPRO)
+     printf( "PROCESS: Exiting server\n" );
+   killnow();
+ }
+ 
+ 
+ private char *ConvertPath( path )
+ char *path;
+ {
+   char *buf = malloc( 1+strlen(path) ), *p=buf;
+ 
+   while (*path) {
+     if (*path=='!') { ++path; continue; }
+     if (!strncmp( path, ":2f", 3 )) { path+=3; *p++='/'; continue; }
+     *p++ = *path++;
+   }
+   *p='\0';
+ 
+   return buf;
+ }
+ 
+ 
+ export PROCESS
+ *FindProcess( path )
+ char *path;
+ {
+   PROCESS *p = procList;
+ 
+   path = ConvertPath( path );
+ 
+   while (p!=NOPROC) 
+     if (!strcmp( path, p->cmnd )) { free(path); return p; }
+   else p=p->nextProc;
+ 
+   free(path);
+   return (NOPROC);
+ }
+ 
+ 
+ export PROCESS
+ *FindFd( fd )
+ int fd;
+ {
+   PROCESS *p = procList;
+ 
+   while (p!=NOPROC) 
+     if ((p->status!=PROC_CLOSED) && (fd==p->fdi[1])) return p;
+   else p=p->nextProc;
+ 
+   return NOPROC;
+ }
+ 
+ 
+ export OSErr ProcDelete( path )
+ char *path;
+ {
+   PROCESS *s, *p = FindProcess( path );
+ 
+   if (DBPRO)
+     printf( "PROCESS: Deleting path: %s\n", path );
+ 
+   if (p==NOPROC) return (aeObjectNotFound);
+ 
+   if (p->status!=PROC_CLOSED) ProcShut(p->fdi[1]);
+ 
+   if (p==procList) procList=p->nextProc;
+   else {
+     for ( s=procList; s->nextProc!=p; s=s->nextProc ) ;
+     s->nextProc = p->nextProc;
+   }
+ 
+   if (DBPRO)
+     printf( "PROCESS: Deleted: [%08x] %s\n", (unsigned)p, p->cmnd );
+ 
+   free(p->cmnd); free(p);
+ 
+   return (noErr);
+ }
+ 
+ 
+ export OSErr
+ ProcCreateFile( path, delf )
+ char *path;
+ int delf;
+ {
+   PROCESS *p;
+ 
+   if (DBPRO)
+     printf( "PROCESS: Creating path: %s\n", path );
+ 
+   if ( FindProcess( path ) != NOPROC ) {
+     if (delf) (void)ProcDelete( path );
+     else return (aeObjectExists);
+   }
+ 
+   if ( ( (p=(PROCESS*)malloc( sizeof(*p) )) == NOPROC ) ) return (aeMiscErr);
+ 
+   if ( ( p->cmnd = ConvertPath(path) ) == (char*)0 ) {
+     free(p); return(aeMiscErr);
+   }
+ 
+   if ( access( p->cmnd, X_OK ) == -1 ) {
+     free(p); return(aeObjectNotFound);
+   }
+ 
+   if (DBPRO)
+     printf( "PROCESS: Created: [%08x] %s\n", (unsigned)p, p->cmnd );
+ 
+   p->status = PROC_CLOSED;
+ 
+   p->nextProc = procList;
+   procList = p;
+ 
+   return (noErr);
+ }
+ 
+ 
+ export OSErr
+ ProcOpen( path, mo, fhdl )
+ char *path;
+ int mo;
+ int *fhdl;
+ {
+   int block = 1;
+   PROCESS *p = FindProcess( path );
+ 
+   if (DBPRO)
+     printf( "PROCESS: Opening path: %s\n", path );
+ 
+   if (p==NOPROC) return(aeObjectNotFound);
+ 
+   if (p->status==PROC_CLOSED) {
+ 
+     if ( pipe(&(p->fdi[0])) == -1 ) {
+       return (aeMiscErr);
+     }
+ 
+     ioctl( p->fdi[1], FIONBIO, &block );
+ 
+     if ( pipe(&(p->fdo[0])) == -1 ) {
+       close(p->fdi[0]); close(p->fdo[1]);
+       return (aeMiscErr);
+     }
+ 
+     p->status = PROC_OPEN;
+   }
+ 
+   *fhdl = p->fdi[1];
+ 
+   if (DBPRO)
+     printf( "PROCESS: Opened: [%08x] %s in=(%d,%d) out=(%d,%d)\n",
+ 	   (unsigned)p, p->cmnd, p->fdi[0], p->fdi[1], p->fdo[0], p->fdo[1] );
+ 
+   return (noErr);
+ }
+ 
+ 
+ export PROCESS
+ *FindPid( pid )
+ int pid;
+ {
+   PROCESS *p = procList;
+ 
+   while (p!=NOPROC) 
+     if ((p->status==PROC_RUNNING) && (pid==p->pid)) return p;
+   else p=p->nextProc;
+ 
+   return NOPROC;
+ }
+ 
+ 
+ private void KillProcess(p)
+ PROCESS *p;
+ {
+   union wait status;
+   int timer;
+ 
+   if (DBPRO)
+     printf( "PROCESS: Terminating: [%08x] %s\n", (unsigned)p, p->cmnd );
+ 
+   /* Give it two seconds to terminate by itself */
+   for (timer=0; timer<4; ++timer) {
+     if ( wait4( p->pid, &(p->termStat), WNOHANG, NULL ) > 0 ) break;
+     abSleep( 2, TRUE );
+   }
+ 
+   /* Give it a hangup, then another two seconds to clean up */
+   if ( wait4( p->pid, &(p->termStat), WNOHANG, NULL ) == 0 ) {
+     if (DBPRO)
+       printf( "PROCESS: Hanging Up: [%08x] %s\n", (unsigned)p, p->cmnd );
+     kill( p->pid, SIGHUP );
+     for (timer=0; timer<4; ++timer) {
+       if ( wait4( p->pid, &(p->termStat), WNOHANG, NULL ) > 0 ) break;
+       abSleep( 2, TRUE );
+     }
+   }
+ 
+   /* If it still hasnt gone, kill it */
+   if ( wait4( p->pid, &(p->termStat), WNOHANG, NULL ) == 0 ) {
+     if (DBPRO)
+       printf( "PROCESS: Killing: [%08x] %s\n", (unsigned)p, p->cmnd );
+     kill( p->pid, SIGKILL );
+   }
+ 
+   /* Now wait for it */
+   while ( wait4( p->pid, &(p->termStat), WNOHANG, NULL ) == 0 )
+     abSleep( 2, TRUE );;
+   if (DBPRO)
+     printf( "PROCESS: Died: [%08x] %s status=%04x\n", 
+ 	   (unsigned)p, p->cmnd, status );
+ 
+   p->status=PROC_OPEN;
+   p->termStat = status;
+ }
+ 
+ 
+ export OSErr
+ ProcClose(fd)
+ int fd;
+ {
+ #ifdef NOCLOSE
+   return(noErr);
+ #else
+   return ProcShut(fd);
+ #endif
+ }
+ 
+ 
+ private OSErr
+ ProcShut(fd)
+ int fd;
+ {
+   PROCESS *p = FindFd(fd);
+ 
+   if (DBPRO)
+     printf( "PROCESS: Closing fd: %d\n", fd );
+ 
+   if ( (p==NOPROC) || (p->status==PROC_CLOSED) ) return (noErr);
+ 
+   close( p->fdi[1] ); close( p->fdo[0] );
+ 
+   if (p->status==PROC_RUNNING) KillProcess(p);
+   else { close(p->fdi[0]); close(p->fdo[1]); }
+ 
+   if (DBPRO)
+     printf( "PROCESS: Closed: [%08x] %s\n", (unsigned)p, p->cmnd );
+ 
+   p->status = PROC_CLOSED;
+ 
+   return (noErr);
+ }
+ 
+ 
+ private void StartProcess(p)
+ PROCESS *p;
+ {
+   if (DBPRO)
+     printf( "PROCESS: Starting: [%08x] %s\n", (unsigned)p, p->cmnd );
+ 
+   if (p->pid=fork()) {
+ 
+     close(p->fdi[0]);
+     close(p->fdo[1]);
+     p->status=PROC_RUNNING;
+     if (DBPRO)
+       printf( "PROCESS: Started: [%08x] %s pid=%d\n",
+ 	     (unsigned)p, p->cmnd, p->pid );
+     return;
+ 
+   } else {
+ 
+     dup2(p->fdi[0],0);
+     dup2(p->fdo[1],1);
+     close(p->fdi[0]);
+     close(p->fdo[0]);
+     close(p->fdi[1]);
+     close(p->fdo[1]);
+     signal( SIGINT, SIG_IGN );
+     execlp( p->cmnd, p->cmnd, (char*)0 );
+     exit(0x99);
+   }
+ }
+ 
+ 
+ export OSErr
+ ProcWrite( p, buf, len )
+ PROCESS *p;
+ char *buf;
+ int len;
+ {
+   int count;
+   char *s;
+ 
+   if (DBPRO)
+     printf( "PROCESS: Writing: [%08x] %s bytes=%d\n",
+ 	   (unsigned)p, p->cmnd, len );
+ 
+   if (p->status==PROC_CLOSED) return(aeEOFErr);
+   if (p->status==PROC_OPEN) StartProcess(p);
+ 
+   for ( count=len, s=buf; count>0; --count, ++s ) if (*s=='\r') *s='\n';
+ 
+   status = WRITING; interrupt = 0;
+ 
+   for (;;) {
+ 
+     count = write( p->fdi[1], buf, len );
+ 
+     if (DBPRO && (count>0))
+       printf( "PROCESS: Wrote: [%08x] %s bytes=%d\n",
+ 	     (unsigned)p, p->cmnd, count );
+ 
+     if (count==len) { status = WAITING; return (noErr); }
+     else if (count>=0) { len-=count; buf=&buf[count]; }
+     else if (errno!=EWOULDBLOCK) { status = WAITING; return (aeMiscErr); };
+ 
+     if ( wait4( p->pid, &(p->termStat), WNOHANG, NULL ) != 0 ) {
+       close( p->fdi[1] ); close( p->fdo[0] );
+       p->status=PROC_CLOSED;
+       status = WAITING;
+       return (aeMiscErr);
+     }
+ 
+     if (interrupt) {
+       interrupt=0;
+       status = WAITING;
+       if (DBPRO)
+ 	printf( "PROCESS: Write interrupted: [%08x] %s\n",
+ 	       (unsigned)p, p->cmnd );
+       return (aeMiscErr);
+     }
+ 
+     abSleep( 2, TRUE );
+   }
+ }
+ 
+ 
+ export OSErr
+ ProcRead( p, buf, len, rl )
+ PROCESS *p;
+ char *buf;
+ int len;
+ int *rl;
+ {
+   long count;
+ 
+   if (DBPRO)
+     printf( "PROCESS: Reading: [%08x] %s maxbytes=%d\n",
+ 	   (unsigned)p, p->cmnd, len );
+ 
+   if (p->status==PROC_CLOSED) return(aeEOFErr);
+   if (p->status==PROC_OPEN) StartProcess(p);
+ 
+   status = READING;
+   interrupt = 0;
+ 
+   for (;;) {
+ 
+     if ( ioctl( p->fdo[0], FIONREAD, &count ) == -1 ) {
+       ProcShut(p->fdi[1]);
+       status = WAITING;
+       return(aeEOFErr);
+     }
+ 
+     if (count>0) {
+ 
+       if (count>len) count=len;
+       count = read( p->fdo[0], buf, (int)count );
+       *rl = count;
+ 
+       while (count) { if (*buf=='\n') *buf='\r'; ++buf; --count; }
+ 
+       if (DBPRO)
+ 	printf( "PROCESS: Read: [%08x] %s bytes=%d\n",
+ 	       (unsigned)p, p->cmnd, *rl );
+ 
+       status = WAITING;
+ 
+       return (noErr);
+     }
+ 
+     if ( wait4( p->pid, &(p->termStat), WNOHANG, NULL ) != 0 ) {
+       close( p->fdi[1] ); close( p->fdo[0] );
+       p->status=PROC_CLOSED;
+       status = WAITING;
+       if (DBPRO)
+ 	printf( "PROCESS: Died while reading: [%08x] %s pid=%d\n",
+ 	       (unsigned)p, p->cmnd, p->pid );
+       return (aeEOFErr);
+     }
+ 
+     if (interrupt) {
+       interrupt=0;
+       *rl=0;
+       status = WAITING;
+       if (DBPRO)
+ 	printf( "PROCESS: Read interrupted: [%08x] %s\n",
+ 	       (unsigned)p, p->cmnd );
+       return (aeEOFErr);
+     }
+ 
+     abSleep( 2, TRUE );
+   }
+ }
+ 
*** afproc.h	Tue Mar 21 19:06:31 1989
--- ../aufsp/afproc.h	Thu Mar 16 10:57:47 1989
***************
*** 0 ****
--- 1,28 ----
+ #include <sys/wait.h>
+ 
+ /* Active process record */
+ 
+ typedef struct process {
+   int pid;			/* Process ID */
+   union wait termStat;		/* Termination status */
+   int fdi[2];			/* Input pipe to process */
+   int fdo[2];			/* Output pipe from process */
+   int status;			/* Process status */
+   char *cmnd;			/* Process command line */
+   struct process *nextProc;	/* Pointer to next process record */
+ } PROCESS;
+ 
+ #define NOPROC (PROCESS*)0	/* Null process pointer */
+ 
+ /* States in process record */
+ 
+ #define PROC_CLOSED 1		/* Process created */
+ #define PROC_OPEN 2		/* Pipes are open */
+ #define PROC_RUNNING 3		/* Process is running */
+ 
+ /* Server states */
+ 
+ #define WAITING 1		/* Server is not blocked */
+ #define READING 2		/* Server is blocked in a read */
+ #define WRITING 3		/* Server is blocked in a write */
+ 
-------------------------------------------------
END
Paul Anderson                       JANET: paul@uk.ac.ed.lfcs
LFCS, Dept. of Computer Science     UUCP:  ..!mcvax!ukc!lfcs!paul	
University of Edinburgh             ARPA:  paul%lfcs.ed.ac.uk@nss.cs.ucl.ac.uk
Edinburgh EH9 3JZ, UK.              Tel:   031-667-1081 Ext 2788