int make1( TARGET *t ) { memset( (char *)counts, 0, sizeof( *counts ) ); #ifdef OPT_SERIAL_OUTPUT_EXT exec_init(); #endif /* Recursively make the target and its dependents */ make1a( t, (TARGET *)0 ); /* Wait for any outstanding commands to finish running. */ while( execwait() ) ; #ifdef OPT_SERIAL_OUTPUT_EXT exec_done(); #endif /* Talk about it */ if( DEBUG_MAKE && counts->failed ) printf( "*** failed updating %d target(s)...\n", counts->failed ); if( DEBUG_MAKE && counts->skipped ) printf( "*** skipped %d target(s)...\n", counts->skipped ); if( DEBUG_MAKE && counts->made ) printf( "*** updated %d target(s)...\n", counts->made ); return counts->total != counts->made; }
int make1( TARGET *t ) { state *pState; memset( (char *)counts, 0, sizeof( *counts ) ); /* Recursively make the target and its dependents */ push_state(&state_stack, t, NULL, T_STATE_MAKE1A); do { while((pState = current_state(&state_stack)) != NULL) { if (intr) pop_state(&state_stack); switch(pState->curstate) { case T_STATE_MAKE1A: make1a(pState); break; case T_STATE_MAKE1ATAIL: make1atail(pState); break; case T_STATE_MAKE1B: make1b(pState); break; case T_STATE_MAKE1C: make1c(pState); break; case T_STATE_MAKE1D: make1d(pState); break; default: break; } } /* Wait for any outstanding commands to finish running. */ } while( execwait() ); clear_state_freelist(); /* Talk about it */ if( counts->failed ) printf( "...failed updating %d target%s...\n", counts->failed, counts->failed > 1 ? "s" : "" ); if( DEBUG_MAKE && counts->skipped ) printf( "...skipped %d target%s...\n", counts->skipped, counts->skipped > 1 ? "s" : "" ); if( DEBUG_MAKE && counts->made ) printf( "...updated %d target%s...\n", counts->made, counts->made > 1 ? "s" : "" ); return counts->total != counts->made; }
LIST * builtin_shell( PARSE *parse, LOL *args, int *jmp ) { LIST *l = lol_get( args, 0 ); LIST *shell = var_get( "JAMSHELL" ); /* shell is per-target */ LIST *output = L0; exec_init(); for( ; l; l = list_next( l ) ) { execcmd( l->string, shell_done, &output, shell, 1 ); execwait(); } exec_done(); return output; }
void execcmd( char *string, void (*func)( void *closure, int status ), void *closure, LIST *shell ) { int pid; int slot; char *argv[ MAXARGC + 1 ]; /* +1 for NULL */ # ifdef USE_EXECNT char *p; # endif /* Find a slot in the running commands table for this one. */ for( slot = 0; slot < MAXJOBS; slot++ ) if( !cmdtab[ slot ].pid ) break; if( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } # ifdef USE_EXECNT if( !cmdtab[ slot ].tempfile ) { char *tempdir; if( !( tempdir = getenv( "TEMP" ) ) && !( tempdir = getenv( "TMP" ) ) ) tempdir = "\\temp"; cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 ); sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat", tempdir, slot ); } /* Trim leading, ending white space */ while( isspace( *string ) ) ++string; p = strchr( string, '\n' ); while( p && isspace( *p ) ) ++p; /* If multi line, or too long, or JAMSHELL is set, write to bat file. */ /* Otherwise, exec directly. */ /* Frankly, if it is a single long line I don't think the */ /* command interpreter will do any better -- it will fail. */ if( p && *p || strlen( string ) > MAXLINE || shell ) { FILE *f; /* Write command to bat file. */ f = fopen( cmdtab[ slot ].tempfile, "w" ); fputs( string, f ); fclose( f ); string = cmdtab[ slot ].tempfile; } # endif /* Forumulate argv */ /* If shell was defined, be prepared for % and ! subs. */ /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */ if( shell ) { int i; char jobno[4]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) ) { switch( shell->string[0] ) { case '%': argv[i] = string; gotpercent++; break; case '!': argv[i] = jobno; break; default: argv[i] = shell->string; } if( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[i] ); } if( !gotpercent ) argv[i++] = string; argv[i] = 0; } else { # ifdef USE_EXECNT argv[0] = "cmd.exe"; argv[1] = "/Q/C"; /* anything more is non-portable */ # else argv[0] = "/bin/sh"; argv[1] = "-c"; # endif argv[2] = string; argv[3] = 0; } /* Catch interrupts whenever commands are running. */ if( !cmdsrunning++ ) istat = signal( SIGINT, onintr ); /* Start the command */ # ifdef USE_EXECNT if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 ) { perror( "spawn" ); exit( EXITBAD ); } # else if ((pid = vfork()) == 0) { execvp( argv[0], argv ); _exit(127); } if( pid == -1 ) { perror( "vfork" ); exit( EXITBAD ); } # endif /* Save the operation for execwait() to find. */ cmdtab[ slot ].pid = pid; cmdtab[ slot ].func = func; cmdtab[ slot ].closure = closure; /* Wait until we're under the limit of concurrent commands. */ /* Don't trust globs.jobs alone. */ while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs ) if( !execwait() ) break; }
void execcmd( char *string, void (*func)( void *closure, int status, timing_info*, char *, char * ), void *closure, LIST *shell, char *action, char *target ) { static int initialized = 0; int out[2], err[2]; int slot, len; char *argv[ MAXARGC + 1 ]; /* +1 for NULL */ /* Find a slot in the running commands table for this one. */ for( slot = 0; slot < MAXJOBS; slot++ ) if( !cmdtab[ slot ].pid ) break; if( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } /* Forumulate argv */ /* If shell was defined, be prepared for % and ! subs. */ /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */ if( shell ) { int i; char jobno[4]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) ) { switch( shell->string[0] ) { case '%': argv[i] = string; gotpercent++; break; case '!': argv[i] = jobno; break; default: argv[i] = shell->string; } if( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[i] ); } if( !gotpercent ) argv[i++] = string; argv[i] = 0; } else { argv[0] = "/bin/sh"; argv[1] = "-c"; argv[2] = string; argv[3] = 0; } /* increment jobs running */ ++cmdsrunning; /* save off actual command string */ cmdtab[ slot ].command = BJAM_MALLOC_ATOMIC(strlen(string)+1); strcpy(cmdtab[slot].command, string); /* initialize only once */ if ( ! initialized ) { times(&old_time); initialized = 1; } /* create pipe from child to parent */ if (pipe(out) < 0) exit(EXITBAD); fcntl(out[0], F_SETFL, O_NONBLOCK); fcntl(out[1], F_SETFL, O_NONBLOCK); if (pipe(err) < 0) exit(EXITBAD); fcntl(err[0], F_SETFL, O_NONBLOCK); fcntl(err[1], F_SETFL, O_NONBLOCK); /* Start the command */ cmdtab[ slot ].start_dt = time(0); if (0 < globs.timeout) { /* * handle hung processes by manually tracking elapsed * time and signal process when time limit expires */ struct tms buf; cmdtab[ slot ].start_time = times(&buf); /* make a global, only do this once */ if (tps == 0) tps = sysconf(_SC_CLK_TCK); } if ((cmdtab[slot].pid = vfork()) == 0) { int pid = getpid(); close(out[0]); close(err[0]); dup2(out[1], STDOUT_FILENO); if (globs.pipe_action == 0) { dup2(out[1], STDERR_FILENO); close(err[1]); } else dup2(err[1], STDERR_FILENO); /* Make this process a process group leader * so that when we kill it, all child * processes of this process are terminated * as well. * * we use killpg(pid, SIGKILL) to kill the * process group leader and all its children. */ if (0 < globs.timeout) { struct rlimit r_limit; r_limit.rlim_cur = globs.timeout; r_limit.rlim_max = globs.timeout; setrlimit(RLIMIT_CPU, &r_limit); } setpgid(pid,pid); execvp( argv[0], argv ); perror( "execvp" ); _exit(127); } else if( cmdtab[slot].pid == -1 ) { perror( "vfork" ); exit( EXITBAD ); } setpgid(cmdtab[slot].pid, cmdtab[slot].pid); /* close write end of pipes */ close(out[1]); close(err[1]); /* child writes stdout to out[1], parent reads from out[0] */ cmdtab[slot].fd[OUT] = out[0]; cmdtab[slot].stream[OUT] = fdopen(cmdtab[slot].fd[OUT], "rb"); if (cmdtab[slot].stream[OUT] == NULL) { perror( "fdopen" ); exit( EXITBAD ); } /* child writes stderr to err[1], parent reads from err[0] */ if (globs.pipe_action == 0) { close(err[0]); } else { cmdtab[slot].fd[ERR] = err[0]; cmdtab[slot].stream[ERR] = fdopen(cmdtab[slot].fd[ERR], "rb"); if (cmdtab[slot].stream[ERR] == NULL) { perror( "fdopen" ); exit( EXITBAD ); } } /* ensure enough room for rule and target name */ if (action && target) { len = strlen(action) + 1; if (cmdtab[slot].action_length < len) { BJAM_FREE(cmdtab[ slot ].action); cmdtab[ slot ].action = BJAM_MALLOC_ATOMIC(len); cmdtab[ slot ].action_length = len; } strcpy(cmdtab[ slot ].action, action); len = strlen(target) + 1; if (cmdtab[slot].target_length < len) { BJAM_FREE(cmdtab[ slot ].target); cmdtab[ slot ].target = BJAM_MALLOC_ATOMIC(len); cmdtab[ slot ].target_length = len; } strcpy(cmdtab[ slot ].target, target); } else { BJAM_FREE(cmdtab[ slot ].action); BJAM_FREE(cmdtab[ slot ].target); cmdtab[ slot ].action = 0; cmdtab[ slot ].target = 0; cmdtab[ slot ].action_length = 0; cmdtab[ slot ].target_length = 0; } /* Save the operation for execwait() to find. */ cmdtab[ slot ].func = func; cmdtab[ slot ].closure = closure; /* Wait until we're under the limit of concurrent commands. */ /* Don't trust globs.jobs alone. */ while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs ) if( !execwait() ) break; }
void execcmd( char *string, void (*func)( void *closure, int status, timing_info* ), void *closure, LIST *shell ) { int pid; int slot; int raw_cmd = 0 ; char *argv_static[ MAXARGC + 1 ]; /* +1 for NULL */ char **argv = argv_static; char *p; /* Check to see if we need to hack around the line-length limitation. */ /* Look for a JAMSHELL setting of "%", indicating that the command * should be invoked directly */ if ( shell && !strcmp(shell->string,"%") && !list_next(shell) ) { raw_cmd = 1; shell = 0; } if ( !is_win95_defined ) set_is_win95(); /* Find a slot in the running commands table for this one. */ if ( is_win95 ) { /* only synchronous spans are supported on Windows 95/98 */ slot = 0; } else { for( slot = 0; slot < MAXJOBS; slot++ ) if( !cmdtab[ slot ].pid ) break; } if( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } if( !cmdtab[ slot ].tempfile ) { const char *tempdir = path_tmpdir(); DWORD procID = GetCurrentProcessId(); /* SVA - allocate 64 other just to be safe */ cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 64 ); if ( DEBUG_PROFILE ) profile_memory( strlen( tempdir ) + 64 ); sprintf( cmdtab[ slot ].tempfile, "%s\\jam%d-%02d.bat", tempdir, procID, slot ); } /* Trim leading, ending white space */ while( isspace( *string ) ) ++string; /* Write to .BAT file unless the line would be too long and it * meets the other spawnability criteria. */ if( raw_cmd && can_spawn( string ) >= MAXLINE ) { if( DEBUG_EXECCMD ) printf("Executing raw command directly\n"); } else { FILE *f = 0; int tries = 0; raw_cmd = 0; /* Write command to bat file. For some reason this open can fails intermitently. But doing some retries works. Most likely this is due to a previously existing file of the same name that happens to be opened by an active virus scanner. Pointed out, and fix by Bronek Kozicki. */ for (; !f && tries < 4; ++tries) { f = fopen( cmdtab[ slot ].tempfile, "w" ); if ( !f && tries < 4 ) Sleep( 250 ); } if (!f) { printf( "failed to write command file!\n" ); exit( EXITBAD ); } fputs( string, f ); fclose( f ); string = cmdtab[ slot ].tempfile; if( DEBUG_EXECCMD ) { if (shell) printf("using user-specified shell: %s", shell->string); else printf("Executing through .bat file\n"); } } /* Forumulate argv */ /* If shell was defined, be prepared for % and ! subs. */ /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */ if( shell ) { int i; char jobno[4]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) ) { switch( shell->string[0] ) { case '%': argv[i] = string; gotpercent++; break; case '!': argv[i] = jobno; break; default: argv[i] = shell->string; } if( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[i] ); } if( !gotpercent ) argv[i++] = string; argv[i] = 0; } else if (raw_cmd) { argv = string_to_args(string); } else { /* don't worry, this is ignored on Win95/98, see later.. */ argv[0] = "cmd.exe"; argv[1] = "/Q/C"; /* anything more is non-portable */ argv[2] = string; argv[3] = 0; } /* Catch interrupts whenever commands are running. */ if( !cmdsrunning++ ) istat = signal( SIGINT, onintr ); /* Start the command */ /* on Win95, we only do a synchronous call */ if ( is_win95 ) { static const char* hard_coded[] = { "del", "erase", "copy", "mkdir", "rmdir", "cls", "dir", "ren", "rename", "move", 0 }; const char** keyword; int len, spawn = 1; int result; timing_info time = {0,0}; for ( keyword = hard_coded; keyword[0]; keyword++ ) { len = strlen( keyword[0] ); if ( strnicmp( string, keyword[0], len ) == 0 && !isalnum(string[len]) ) { /* this is one of the hard coded symbols, use 'system' to run */ /* them.. except for "del"/"erase" */ if ( keyword - hard_coded < 2 ) result = process_del( string ); else result = system( string ); spawn = 0; break; } } if (spawn) { char** args; /* convert the string into an array of arguments */ /* we need to take care of double quotes !! */ args = string_to_args( string ); if ( args ) { #if 0 char** arg; fprintf( stderr, "%s: ", args[0] ); arg = args+1; while ( arg[0] ) { fprintf( stderr, " {%s}", arg[0] ); arg++; } fprintf( stderr, "\n" ); #endif result = spawnvp( P_WAIT, args[0], args ); record_times(result, &time); free_argv( args ); } else result = 1; } func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK, &time ); return; } if( DEBUG_EXECCMD ) { char **argp = argv; printf("Executing command"); while(*argp != 0) { printf(" [%s]", *argp); argp++; } printf("\n"); } /* the rest is for Windows NT only */ /* spawn doesn't like quotes around the command name */ if ( argv[0][0] == '"') { int l = strlen(argv[0]); /* Clobber any closing quote, shortening the string by one * element */ if (argv[0][l-1] == '"') argv[0][l-1] = '\0'; /* Move everything *including* the original terminating zero * back one place in memory, covering up the opening quote */ memmove(argv[0],argv[0]+1,l); } if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 ) { perror( "spawn" ); exit( EXITBAD ); } /* Save the operation for execwait() to find. */ cmdtab[ slot ].pid = pid; cmdtab[ slot ].func = func; cmdtab[ slot ].closure = closure; /* Wait until we're under the limit of concurrent commands. */ /* Don't trust globs.jobs alone. */ while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs ) if( !execwait() ) break; if (argv != argv_static) { free_argv(argv); } }
void execcmd( char *string, void (*func)( void *closure, int status ), void *closure, LIST *shell ) { int pid; int slot; int raw_cmd = 0 ; char *argv_static[ MAXARGC + 1 ]; /* +1 for NULL */ char **argv = argv_static; char *p; /* Check to see if we need to hack around the line-length limitation. */ /* Look for a JAMSHELL setting of "%", indicating that the command * should be invoked directly */ if ( shell && !strcmp(shell->string,"%") && !list_next(shell) ) { raw_cmd = 1; shell = 0; } if ( !is_win95_defined ) set_is_win95(); /* Find a slot in the running commands table for this one. */ if ( is_win95 ) { /* only synchronous spans are supported on Windows 95/98 */ slot = 0; } else { for( slot = 0; slot < MAXJOBS; slot++ ) if( !cmdtab[ slot ].pid ) break; } if( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } if( !cmdtab[ slot ].tempfile ) { const char *tempdir; DWORD procID; tempdir = getTempDir(); // SVA - allocate 64 other just to be safe cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 64 ); procID = GetCurrentProcessId(); sprintf( cmdtab[ slot ].tempfile, "%s\\jam%d-%02d.bat", tempdir, procID, slot ); } /* Trim leading, ending white space */ while( isspace( *string ) ) ++string; /* If multi line, or too long, or JAMSHELL is set, write to bat file. */ /* Otherwise, exec directly. */ /* Frankly, if it is a single long line I don't think the */ /* command interpreter will do any better -- it will fail. */ if( shell || !raw_cmd // && use_bat_file( string ) ) { FILE *f; /* Write command to bat file. */ f = fopen( cmdtab[ slot ].tempfile, "w" ); fputs( string, f ); fclose( f ); string = cmdtab[ slot ].tempfile; if( DEBUG_EXECCMD ) { if (shell) printf("using user-specified shell: %s", shell->string); else printf("Executing through .bat file\n"); } } else if( DEBUG_EXECCMD ) { printf("Executing raw command directly\n"); } /* Forumulate argv */ /* If shell was defined, be prepared for % and ! subs. */ /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */ if( shell ) { int i; char jobno[4]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) ) { switch( shell->string[0] ) { case '%': argv[i] = string; gotpercent++; break; case '!': argv[i] = jobno; break; default: argv[i] = shell->string; } if( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[i] ); } if( !gotpercent ) argv[i++] = string; argv[i] = 0; } else if (raw_cmd) { int ignored; argv = string_to_args(string, &ignored); } else { /* don't worry, this is ignored on Win95/98, see later.. */ argv[0] = "cmd.exe"; argv[1] = "/Q/C"; /* anything more is non-portable */ argv[2] = string; argv[3] = 0; } /* Catch interrupts whenever commands are running. */ if( !cmdsrunning++ ) istat = signal( SIGINT, onintr ); /* Start the command */ /* on Win95, we only do a synchronous call */ if ( is_win95 ) { static const char* hard_coded[] = { "del", "erase", "copy", "mkdir", "rmdir", "cls", "dir", "ren", "rename", "move", 0 }; const char** keyword; int len, spawn = 1; int result; for ( keyword = hard_coded; keyword[0]; keyword++ ) { len = strlen( keyword[0] ); if ( strnicmp( string, keyword[0], len ) == 0 && !isalnum(string[len]) ) { /* this is one of the hard coded symbols, use 'system' to run */ /* them.. except for "del"/"erase" */ if ( keyword - hard_coded < 2 ) result = process_del( string ); else result = system( string ); spawn = 0; break; } } if (spawn) { char** args; int num_args; /* convert the string into an array of arguments */ /* we need to take care of double quotes !! */ args = string_to_args( string, &num_args ); if ( args ) { #if 0 char** arg; fprintf( stderr, "%s: ", args[0] ); arg = args+1; while ( arg[0] ) { fprintf( stderr, " {%s}", arg[0] ); arg++; } fprintf( stderr, "\n" ); #endif result = spawnvp( P_WAIT, args[0], args ); free_args( args ); } else result = 1; } func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK ); return; } if( DEBUG_EXECCMD ) { char **argp = argv; printf("Executing command"); while(*argp != 0) { printf(" [%s]", *argp); argp++; } printf("\n"); } /* the rest is for Windows NT only */ /* spawn doesn't like quotes aroudn the command name */ if ( argv[0][0] == '"') { int l = strlen(argv[0]); if (argv[0][l-1] == '"') argv[0][l-1] = '\0'; strcpy(argv[0],argv[0]+1); } if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 ) { perror( "spawn" ); exit( EXITBAD ); } /* Save the operation for execwait() to find. */ cmdtab[ slot ].pid = pid; cmdtab[ slot ].func = func; cmdtab[ slot ].closure = closure; /* Wait until we're under the limit of concurrent commands. */ /* Don't trust globs.jobs alone. */ while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs ) if( !execwait() ) break; if (argv != argv_static) { free_args(argv); } }