The Machine Perception Toolbox

[Introduction]- [News]- [Download]- [Screenshots]- [Manual (pdf)]- [Forums]- [API Reference]- [Repository ]

 

Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

execunix.c

Go to the documentation of this file.
00001 /*
00002  * Copyright 1993, 1995 Christopher Seiwald.
00003  *
00004  * This file is part of Jam - see jam.c for Copyright information.
00005  */
00006 
00007 # include "jam.h"
00008 # include "lists.h"
00009 # include "execcmd.h"
00010 # include <errno.h>
00011 
00012 #if defined(sun) || defined(__sun)
00013 #include <unistd.h> // need to include unistd.h on sun for the vfork prototype
00014 #include <wait.h>
00015 #endif
00016 
00017 # ifdef USE_EXECUNIX
00018 
00019 # ifdef NO_VFORK
00020 # define vfork() fork()
00021 # endif
00022 
00023 # if defined( OS_NT ) || defined( OS_OS2 )
00024 
00025 # define USE_EXECNT
00026 
00027 # include <process.h>
00028 
00029 # if !defined( __BORLANDC__ ) && !defined( OS_OS2 )
00030 # define wait my_wait
00031 static int my_wait( int *status );
00032 # endif
00033 
00034 # endif
00035 
00036 /*
00037  * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS
00038  *
00039  * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
00040  * The default is:
00041  *
00042  *      /bin/sh -c %            [ on UNIX/AmigaOS ]
00043  *      cmd.exe /c %            [ on OS2/WinNT ]
00044  *
00045  * Each word must be an individual element in a jam variable value.
00046  *
00047  * In $(JAMSHELL), % expands to the command string and ! expands to 
00048  * the slot number (starting at 1) for multiprocess (-j) invocations.
00049  * If $(JAMSHELL) doesn't include a %, it is tacked on as the last
00050  * argument.
00051  *
00052  * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work!
00053  *
00054  * External routines:
00055  *      execcmd() - launch an async command execution
00056  *      execwait() - wait and drive at most one execution completion
00057  *
00058  * Internal routines:
00059  *      onintr() - bump intr to note command interruption
00060  *
00061  * 04/08/94 (seiwald) - Coherent/386 support added.
00062  * 05/04/94 (seiwald) - async multiprocess interface
00063  * 01/22/95 (seiwald) - $(JAMSHELL) support
00064  * 06/02/97 (gsar)    - full async multiprocess support for Win32
00065  */
00066 
00067 static int intr = 0;
00068 static int cmdsrunning = 0;
00069 static void (*istat)( int );
00070 
00071 static struct
00072 {
00073         int     pid; /* on win32, a real process handle */
00074         void    (*func)( void *closure, int status );
00075         void    *closure;
00076 
00077 # ifdef USE_EXECNT
00078         char    *tempfile;
00079 # endif
00080 
00081 } cmdtab[ MAXJOBS ] = {{0}};
00082 
00083 /*
00084  * onintr() - bump intr to note command interruption
00085  */
00086 
00087 void
00088 onintr( int disp )
00089 {
00090         intr++;
00091         printf( "...interrupted\n" );
00092 }
00093 
00094 /*
00095  * execcmd() - launch an async command execution
00096  */
00097 
00098 void
00099 execcmd( 
00100         char *string,
00101         void (*func)( void *closure, int status ),
00102         void *closure,
00103         LIST *shell )
00104 {
00105         int pid;
00106         int slot;
00107         char *argv[ MAXARGC + 1 ];      /* +1 for NULL */
00108 
00109 # ifdef USE_EXECNT
00110         char *p;
00111 # endif
00112 
00113         /* Find a slot in the running commands table for this one. */
00114 
00115         for( slot = 0; slot < MAXJOBS; slot++ )
00116             if( !cmdtab[ slot ].pid )
00117                 break;
00118 
00119         if( slot == MAXJOBS )
00120         {
00121             printf( "no slots for child!\n" );
00122             exit( EXITBAD );
00123         }
00124 
00125 # ifdef USE_EXECNT
00126         if( !cmdtab[ slot ].tempfile )
00127         {
00128             char *tempdir;
00129 
00130             if( !( tempdir = getenv( "TEMP" ) ) &&
00131                 !( tempdir = getenv( "TMP" ) ) )
00132                     tempdir = "\\temp";
00133 
00134             cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 );
00135 
00136             sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat", 
00137                                 tempdir, slot );
00138         }
00139 
00140         /* Trim leading, ending white space */
00141 
00142         while( isspace( *string ) )
00143                 ++string;
00144 
00145         p = strchr( string, '\n' );
00146 
00147         while( p && isspace( *p ) )
00148                 ++p;
00149 
00150         /* If multi line, or too long, or JAMSHELL is set, write to bat file. */
00151         /* Otherwise, exec directly. */
00152         /* Frankly, if it is a single long line I don't think the */
00153         /* command interpreter will do any better -- it will fail. */
00154 
00155         if( p && *p || strlen( string ) > MAXLINE || shell )
00156         {
00157             FILE *f;
00158 
00159             /* Write command to bat file. */
00160 
00161             f = fopen( cmdtab[ slot ].tempfile, "w" );
00162             fputs( string, f );
00163             fclose( f );
00164 
00165             string = cmdtab[ slot ].tempfile;
00166         }
00167 # endif
00168 
00169         /* Forumulate argv */
00170         /* If shell was defined, be prepared for % and ! subs. */
00171         /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */
00172 
00173         if( shell )
00174         {
00175             int i;
00176             char jobno[4];
00177             int gotpercent = 0;
00178 
00179             sprintf( jobno, "%d", slot + 1 );
00180 
00181             for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
00182             {
00183                 switch( shell->string[0] )
00184                 {
00185                 case '%':       argv[i] = string; gotpercent++; break;
00186                 case '!':       argv[i] = jobno; break;
00187                 default:        argv[i] = shell->string;
00188                 }
00189                 if( DEBUG_EXECCMD )
00190                     printf( "argv[%d] = '%s'\n", i, argv[i] );
00191             }
00192 
00193             if( !gotpercent )
00194                 argv[i++] = string;
00195 
00196             argv[i] = 0;
00197         }
00198         else
00199         {
00200 # ifdef USE_EXECNT
00201             argv[0] = "cmd.exe";
00202             argv[1] = "/Q/C";           /* anything more is non-portable */
00203 # else
00204             argv[0] = "/bin/sh";
00205             argv[1] = "-c";
00206 # endif
00207             argv[2] = string;
00208             argv[3] = 0;
00209         }
00210 
00211         /* Catch interrupts whenever commands are running. */
00212 
00213         if( !cmdsrunning++ )
00214             istat = signal( SIGINT, onintr );
00215 
00216         /* Start the command */
00217 
00218 # ifdef USE_EXECNT
00219         if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
00220         {
00221             perror( "spawn" );
00222             exit( EXITBAD );
00223         }
00224 # else
00225         if ((pid = vfork()) == 0) 
00226         {
00227                 execvp( argv[0], argv );
00228                 _exit(127);
00229         }
00230 
00231         if( pid == -1 )
00232         {
00233             perror( "vfork" );
00234             exit( EXITBAD );
00235         }
00236 # endif
00237         /* Save the operation for execwait() to find. */
00238 
00239         cmdtab[ slot ].pid = pid;
00240         cmdtab[ slot ].func = func;
00241         cmdtab[ slot ].closure = closure;
00242 
00243         /* Wait until we're under the limit of concurrent commands. */
00244         /* Don't trust globs.jobs alone. */
00245 
00246         while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
00247             if( !execwait() )
00248                 break;
00249 }
00250 
00251 /*
00252  * execwait() - wait and drive at most one execution completion
00253  */
00254 
00255 int
00256 execwait()
00257 {
00258         int i;
00259         int status, w;
00260         int rstat;
00261 
00262         /* Handle naive make1() which doesn't know if cmds are running. */
00263 
00264         if( !cmdsrunning )
00265             return 0;
00266 
00267         /* Pick up process pid and status */
00268     
00269         while( ( w = wait( &status ) ) == -1 && errno == EINTR )
00270                 ;
00271 
00272         if( w == -1 )
00273         {
00274             printf( "child process(es) lost!\n" );
00275             perror("wait");
00276             exit( EXITBAD );
00277         }
00278 
00279         /* Find the process in the cmdtab. */
00280 
00281         for( i = 0; i < MAXJOBS; i++ )
00282             if( w == cmdtab[ i ].pid )
00283                 break;
00284 
00285         if( i == MAXJOBS )
00286         {
00287             printf( "waif child found!\n" );
00288             exit( EXITBAD );
00289         }
00290 
00291         /* Drive the completion */
00292 
00293         if( !--cmdsrunning )
00294             signal( SIGINT, istat );
00295 
00296         if( intr )
00297             rstat = EXEC_CMD_INTR;
00298         else if( w == -1 || status != 0 )
00299             rstat = EXEC_CMD_FAIL;
00300         else
00301             rstat = EXEC_CMD_OK;
00302 
00303         cmdtab[ i ].pid = 0;
00304 
00305         (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
00306 
00307         return 1;
00308 }
00309 
00310 # if defined( OS_NT ) && !defined( __BORLANDC__ )
00311 
00312 # define WIN32_LEAN_AND_MEAN
00313 
00314 # include <windows.h>           /* do the ugly deed */
00315 
00316 static int
00317 my_wait( int *status )
00318 {
00319         int i, num_active = 0;
00320         DWORD exitcode, waitcode;
00321         static HANDLE *active_handles = 0;
00322 
00323         if (!active_handles)
00324             active_handles = (HANDLE *)malloc(globs.jobs * sizeof(HANDLE) );
00325 
00326         /* first see if any non-waited-for processes are dead,
00327          * and return if so.
00328          */
00329         for ( i = 0; i < globs.jobs; i++ ) {
00330             if ( cmdtab[i].pid ) {
00331                 if ( GetExitCodeProcess((HANDLE)cmdtab[i].pid, &exitcode) ) {
00332                     if ( exitcode == STILL_ACTIVE )
00333                         active_handles[num_active++] = (HANDLE)cmdtab[i].pid;
00334                     else {
00335                         CloseHandle((HANDLE)cmdtab[i].pid);
00336                         *status = (int)((exitcode & 0xff) << 8);
00337                         return cmdtab[i].pid;
00338                     }
00339                 }
00340                 else
00341                     goto FAILED;
00342             }
00343         }
00344 
00345         /* if a child exists, wait for it to die */
00346         if ( !num_active ) {
00347             errno = ECHILD;
00348             return -1;
00349         }
00350         waitcode = WaitForMultipleObjects( num_active,
00351                                            active_handles,
00352                                            FALSE,
00353                                            INFINITE );
00354         if ( waitcode != WAIT_FAILED ) {
00355             if ( waitcode >= WAIT_ABANDONED_0
00356                 && waitcode < WAIT_ABANDONED_0 + num_active )
00357                 i = waitcode - WAIT_ABANDONED_0;
00358             else
00359                 i = waitcode - WAIT_OBJECT_0;
00360             if ( GetExitCodeProcess(active_handles[i], &exitcode) ) {
00361                 CloseHandle(active_handles[i]);
00362                 *status = (int)((exitcode & 0xff) << 8);
00363                 return (int)active_handles[i];
00364             }
00365         }
00366 
00367 FAILED:
00368         errno = GetLastError();
00369         return -1;
00370     
00371 }
00372 
00373 # endif /* NT && !__BORLANDC__ */
00374 
00375 # endif /* USE_EXECUNIX */

Generated on Mon Nov 8 17:07:32 2004 for MPT by  doxygen 1.3.9.1