home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Connectivity / GateKeeper-2.1 / Subprocess.m < prev    next >
Encoding:
Text File  |  1997-03-04  |  11.8 KB  |  400 lines

  1. //************************************************************************
  2. //
  3. //    Subprocess.m  
  4. //        
  5. //        Launch and monitor UNIX subprocesses. 
  6. // 
  7. //            by    Felipe A. Rodriguez        
  8. //
  9. //    The base for this file was derived from:
  10. //    
  11. //            Subprocess.m    (v10)
  12. //            by Charles L. Oei
  13. //            pty support by Joe Freeman
  14. //            Subprocess Example, Release 2.0
  15. //            NeXT Computer, Inc. 
  16. //
  17. //    This code is supplied "as is" the author makes no warranty as to its 
  18. //    suitability for any purpose.  This code is free and may be distributed 
  19. //    in accordance with the terms of the:
  20. //        
  21. //            GNU GENERAL PUBLIC LICENSE
  22. //            Version 2, June 1991
  23. //            copyright (C) 1989, 1991 Free Software Foundation, Inc.
  24. //             675 Mass Ave, Cambridge, MA 02139, USA
  25. //
  26. //************************************************************************
  27.  
  28. #import "GKdefs.h"
  29. #import "Subprocess.h"
  30. #import "Coordinator.h"
  31.  
  32. #import <appkit/nextstd.h>
  33. #import <appkit/Application.h>
  34. #import <appkit/Panel.h>
  35. #import <ansi/string.h>
  36. #import <dpsclient/dpsNeXT.h>
  37. #import <defaults/defaults.h>
  38.  
  39. #define    PTY_TEMPLATE "/dev/pty??"
  40. #define    PTY_LENGTH 11
  41.  
  42. static void showError();
  43.  
  44. //************************************************************************
  45. //
  46. // Private Instance Methods
  47. //
  48. //************************************************************************
  49.  
  50. @interface Subprocess(Private)
  51.  
  52. - childDidExit;
  53. - fdHandler:(int)theFd;
  54.  
  55. @end
  56. @implementation Subprocess(Private)
  57.  
  58. - childDidExit
  59.     // cleanup after a child process exits
  60. {
  61.     if (childPid)
  62.         {
  63.         DPSRemoveFD(fromChild);
  64.         close(fromChild);
  65.         fclose(fpToChild);
  66.         childPid=0;                // specify that child is dead
  67.         if (delegate && [delegate respondsTo:@selector(subprocessDone)])
  68.             [delegate perform:@selector(subprocessDone)];
  69.         }
  70.         
  71.     return self;
  72. }
  73. //************************************************************************
  74. //
  75. //        Called when output from UNIX subprocess is available    
  76. //
  77. //************************************************************************
  78.  
  79. - fdHandler:(int)theFd
  80.     // DPS handler for output from subprocess
  81. {
  82.     if (((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) < 0) ||     
  83.             (!bufferCount))
  84.         {
  85.         [self childDidExit];
  86.         return self;
  87.         }
  88.     outputBuffer[bufferCount] = '\0';
  89.     [delegate subprocessOutput:(void *)&outputBuffer];
  90.             
  91.     return self;
  92. }
  93.  
  94. @end
  95.  
  96. //************************************************************************
  97. //
  98. //        Private Utility Routines
  99. //
  100. //************************************************************************
  101.  
  102. static void showError (const char *errorString, id theDelegate)
  103.     // ensure errors never get dropped on the floor
  104. {
  105.     if(theDelegate && [theDelegate respondsTo:@selector(showAlert:)])
  106.         [theDelegate perform:@selector(showAlert:)
  107.             with:(void *)errorString];
  108.     else if(NXApp)        // no delegate, but we're running w/in an App
  109.             NXRunAlertPanel(0, errorString, 0, 0, 0);
  110.          else
  111.             perror(errorString);                 //output errstr on stderr
  112. }
  113. //*****************************************************************************
  114. //
  115. //        DPS registered function which recieves output from UNIX subprocess
  116. //
  117. //        void DPSAddFD(int fd, DPSFDProc handler, void *userData, int priority)
  118. //      fdhandler registered to read file descriptor fromChild
  119. //
  120. //*****************************************************************************
  121.  
  122. static void fdHandler(int theFd, id self)
  123. {
  124.     [self fdHandler:theFd];
  125. }
  126. //*****************************************************************************
  127. //
  128. //        attempt to setup the pty pair
  129. //
  130. //*****************************************************************************
  131.  
  132. static void getptys (int *master, int *slave)
  133. {
  134.     char device[PTY_LENGTH];
  135.     char *block, *num;
  136.     char *blockLoc; // specifies the location of block for the device string
  137.     char *numLoc;     // specifies the pty name with the digit ptyxD
  138.     char *msLoc;    // specifies the master (ptyxx) or slave (ttyxx)
  139.     struct sgttyb setp = {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  140.     struct tchars setc = {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  141.     struct ltchars sltc = {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  142.     int    lset = (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  143.     int    setd = NTTYDISC;        /* new tty discipline */
  144.     
  145.     strcpy(device, PTY_TEMPLATE);     // string constants are not writable
  146.     blockLoc = &device[ strlen("/dev/pty") ];
  147.     numLoc = &device[ strlen("/dev/pty?") ];
  148.     msLoc = &device[ strlen("/dev/") ];
  149.     for (block = "pqrs"; *block; block++)    
  150.         {                                    
  151.         *blockLoc = *block;
  152.         for (num = "0123456789abcdef"; *num; num++)
  153.             {
  154.             *numLoc = *num;
  155.             *master = open(device, O_RDWR);    //Open device assign fd to master
  156.             if (*master >= 0)                //If master 0 or >, open succesful
  157.                 {
  158.                 *msLoc = 't';
  159.                 *slave = open(device, O_RDWR);
  160.                 if (*slave >= 0)
  161.                     {
  162.                         /* set parameters -- stty */
  163.                     (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  164.                         /* set special characters */
  165.                     (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  166.                         /* set line discipline */
  167.                     (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  168.                         /* set local special chars */
  169.                     (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  170.                         /* set entire local mode word */
  171.                     (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  172.                     
  173.                     return;
  174.                     }
  175.                 }
  176.             } /* hunting through a bank of ptys */
  177.         } /* hunting through blocks of ptys in all the right places */
  178.         
  179.     *master = -1;        //If we get here we were unable to open 
  180.     *slave = -1;        //master and/or slave pty's 
  181. }
  182.  
  183. @implementation Subprocess
  184.  
  185. //************************ Public Instance Methods ****************************
  186. //*****************************************************************************
  187. //
  188. //         initializes an instance of Subprocess and corresponding UNIX process
  189. //
  190. //*****************************************************************************
  191.  
  192. - init:(const char *)subprocessString withDelegate:theDelegate
  193.             andStdErr:(BOOL)wantsStdErr
  194. {
  195. static char logFileName[MAXPATHLEN + 1];        // syslogd logging file
  196. int tty, numFds, fd;                            // for temporary use
  197. int processGroup;
  198. int pidChild;        // childPid does not exist until Subprocess is instantiated
  199.  
  200.                     // reset syslogd daemon 
  201.     if(theDelegate && [theDelegate respondsTo:@selector(syslogdReset)])
  202.         [theDelegate perform:@selector(syslogdReset)];    
  203.                                     // /dev/tty kernel synm for controlling tty
  204.     tty = open("/dev/tty", O_RDWR);            // open the controlling tty 
  205.     getptys(&masterPty,&slavePty);            // find and open psuedo terms
  206.     strcpy(logFileName, NXGetDefaultValue([NXApp appName], "locFIFO"));
  207.     if (masterPty <= 0 || slavePty <= 0)
  208.         {
  209.         showError("Error grabbing ptys for subprocess.", theDelegate);
  210.         return self;
  211.         }
  212.             // remove the controlling tty if launched from a shell,
  213.             // but not Workspace; so that we have job control over
  214.             // the parent application in shell and so that subprocesses 
  215.             // can be restarted in Workspace
  216.     if((tty<0) && ((tty = open("/dev/tty", 2)) >= 0))
  217.         {
  218.         ioctl(tty, TIOCNOTTY, 0);
  219.         close(tty);
  220.         }
  221.     switch (pidChild = fork())                // fork to create child process
  222.             {                                    // which will monitor pppd
  223.         case -1:    // error
  224.         
  225.             showError("Error starting UNIX vfork of subprocess.", theDelegate);
  226.             return self;
  227.             
  228.         case 0:      // child -- Vfork returns 0 in the child's context
  229.         
  230.             dup2(slavePty, 0);        // dup slavePty to be stdin
  231.             dup2(slavePty, 1);        // dup slavePty to be stdout
  232.             if(wantsStdErr)
  233.                 dup2(slavePty, 2);    // dup slavePty to be stderr
  234.             numFds = getdtablesize();
  235.             for(fd=3; fd < numFds; fd++)        // Child should close all open
  236.                 close(fd);                        // fd's beyond our basic 3
  237.             processGroup = getpid();            // return current pid
  238.                 // think -- tty was opened by parent so set pgrp of tty
  239.                 //    to that of ourselves
  240.             ioctl(0, TIOCSPGRP, (char *)&processGroup);        
  241.             setpgrp (0, processGroup);  // make curr process process grp leader
  242.  
  243.             if(strcmp(NXGetDefaultValue([NXApp appName],"useFIFO"), 
  244.                                                         "YES") == 0)
  245.                 { 
  246.                 if((log = open(logFileName, O_RDONLY)) < 0)        // open FIFO
  247.                     perror("Unable to open FIFO special file.");
  248.                 }
  249.             else
  250.                 {                                            // open log file
  251.                 if(!(fp = fopen(logFileName, "r")))
  252.                     perror("Unable to open log file.");
  253.                 else
  254.                     fseek(fp, 0, SEEK_END);
  255.                 }
  256.  
  257.             switch (vfork())                    // fork to exec pppd, parent    
  258.                 {                                // will monitor pppd
  259.                 case -1:    // error
  260.                 
  261.                     showError("Error during vfork to exec pppd", theDelegate);
  262.                     return self;
  263.                     
  264.                 case 0:      // child -- Vfork returns 0 in the child's context
  265.                             
  266.                     execl("/bin/sh", "sh", "-c", subprocessString, (char *)0);
  267.                     perror("error exec'ing pppd"); 
  268.                     exit(1);
  269.         
  270.                 default:    // parent --  vfork returns pid of the child          
  271.                         
  272.                     if(strcmp(NXGetDefaultValue([NXApp appName],"useFIFO"), 
  273.                                                                 "YES") == 0) 
  274.                         [self readFromFIFO];    // use a FIFO as IPC w/pppd
  275.                     else
  276.                         [self readFromFile];    // use a File as IPC w/pppd
  277.                 }        
  278.             perror("vfork 1st (child)"); 
  279.             exit(1);
  280.             
  281.         default:    // parent --  vfork returns pid of the child in the         
  282.                     //                parent's context
  283.             delegate = theDelegate;
  284.             childPid = pidChild;
  285.             close(slavePty);
  286.             fpToChild = fdopen(masterPty, "w");    //return ptr to masterPty
  287.             fromChild = masterPty;
  288.             setbuf(fpToChild, NULL);    // no buffering
  289.                 // Registers fdhandler to be called when their is activity with 
  290.                 // file descriptor fromChild
  291.                 // void DPSAddFD(int fd, DPSFDProc handler, void *userData, 
  292.                 // int priority)    
  293.             DPSAddFD(fromChild, (DPSFDProc)fdHandler, (id)self,     
  294.                     NX_BASETHRESHOLD+1);
  295.  
  296.             return self;
  297.         }
  298. }
  299. //*****************************************************************************
  300. //
  301. //         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.
  302. //
  303. //*****************************************************************************
  304.  
  305. - terminate:sender
  306. {
  307. FILE *ff; 
  308. int pid;
  309. int prgrp;
  310. char lockPath[32] = {"rm usr/spool/uucp/LCK/LCK.."};    
  311.                     
  312.     if((ff = fopen( "/etc/ppp/ppp0.pid", "r")) == NULL)
  313.         {
  314.         if(childPid)
  315.             {                                // get the child's process group
  316.             prgrp =  getpgrp(childPid);        // kill the child's group with
  317.             killpg(prgrp, SIGKILL);            // extreme prejudice
  318.             strcpy(Path, lockPath);        
  319.             strcat(Path, NXGetDefaultValue([NXApp appName], MODEMPORT));                
  320.             system(Path);                    // remove uucp lock from port
  321.             }
  322.         }
  323.     else            // attempt to do a clean hit on a properly running pppd 
  324.         {
  325.         if( fscanf( ff, "%d", &pid)<1) 
  326.             perror("Unable to read pid from ppp0.pid\n");
  327.         fclose( ff);
  328.         if( kill( pid, SIGINT)==-1) 
  329.             perror("killing pppd");
  330.         if (childPid)
  331.             kill(childPid, SIGTERM);
  332.         }
  333.     [self childDidExit];
  334.     
  335.     return self;
  336. }
  337. //*****************************************************************************
  338. //
  339. //         read pppd output written by syslogd to a FIFO 
  340. //
  341. //*****************************************************************************
  342.  
  343. - readFromFIFO
  344. {
  345. static int nread;
  346. static char buff[BUFFERSIZE]; 
  347.  
  348.     for(;;)        // reads log and writes to pty/user 
  349.         {
  350.         if((nread = read(log, buff, BUFFERSIZE)) < 0)
  351.             {
  352.             perror("Error reading FIFO.");
  353.             break;
  354.             }
  355.         if(write(1, buff, nread) != nread)
  356.             perror("Error writing to Slave Pty.");
  357.         }
  358.  
  359.     return self;
  360. }
  361. //*****************************************************************************
  362. //
  363. //         read pppd output written by syslogd to an ordinary file 
  364. //
  365. //*****************************************************************************
  366.  
  367. - readFromFile
  368. {
  369. register int ch;
  370. struct timeval second;
  371. fd_set zero;
  372.  
  373.     FD_ZERO(&zero);
  374.     second.tv_sec = 1;
  375.     second.tv_usec = 0;
  376.  
  377.     for(;;)        // reads log and writes to pty/user 
  378.         {
  379.         while ((ch = getc(fp)) != EOF)
  380.             if (putchar(ch) == EOF)
  381.                 perror("Error writing to Slave Pty.");
  382.         if (ferror(fp)) 
  383.             {
  384.             perror("Error reading FIFO.");
  385.             break;
  386.             }
  387.         (void)fflush(stdout);
  388.                 /* Sleep(3) is eight system calls.  Do it fast. */
  389.         if (select(0, &zero, &zero, &zero, &second) == -1)
  390.             perror("select: %s");
  391.         clearerr(fp);
  392.         }
  393.  
  394.     return self;
  395. }
  396.  
  397.  
  398. @end
  399.  
  400.