📄 execunix.c
字号:
/* * Copyright 1993, 1995 Christopher Seiwald. * Copyright 2007 Noel Belcourt. * * This file is part of Jam - see jam.c for Copyright information. */# include "jam.h"# include "lists.h"# include "execcmd.h"# include "output.h"# include <errno.h># include <signal.h># include <stdio.h># include <time.h># include <unistd.h> /* needed for vfork(), _exit() prototypes */# include <sys/resource.h># include <sys/times.h># include <sys/wait.h>#if defined(sun) || defined(__sun) || defined(linux)#include <wait.h>#endif# ifdef USE_EXECUNIX# include <sys/times.h># ifdef NO_VFORK# define vfork() fork()# endif/* * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS * * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). * The default is: * * /bin/sh -c % [ on UNIX/AmigaOS ] * cmd.exe /c % [ on OS2/WinNT ] * * Each word must be an individual element in a jam variable value. * * In $(JAMSHELL), % expands to the command string and ! expands to * the slot number (starting at 1) for multiprocess (-j) invocations. * If $(JAMSHELL) doesn't include a %, it is tacked on as the last * argument. * * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work! * * External routines: * execcmd() - launch an async command execution * execwait() - wait and drive at most one execution completion * * Internal routines: * onintr() - bump intr to note command interruption * * 04/08/94 (seiwald) - Coherent/386 support added. * 05/04/94 (seiwald) - async multiprocess interface * 01/22/95 (seiwald) - $(JAMSHELL) support * 06/02/97 (gsar) - full async multiprocess support for Win32 */static clock_t tps = 0;static struct timeval tv;static int select_timeout = 0;static int intr = 0;static int cmdsrunning = 0;static struct tms old_time;#define OUT 0#define ERR 1static struct{ int pid; /* on win32, a real process handle */ int fd[2]; /* file descriptors for stdout and stderr */ FILE *stream[2]; /* child's stdout (0) and stderr (1) file stream */ clock_t start_time; /* start time of child process */ int exit_reason; /* termination status */ int action_length; /* length of action string */ int target_length; /* length of target string */ char *action; /* buffer to hold action and target invoked */ char *target; /* buffer to hold action and target invoked */ char *command; /* buffer to hold command being invoked */ char *buffer[2]; /* buffer to hold stdout and stderr, if any */ void (*func)( void *closure, int status, timing_info*, char *, char * ); void *closure; time_t start_dt; /* start of command timestamp */} cmdtab[ MAXJOBS ] = {{0}};/* * onintr() - bump intr to note command interruption */voidonintr( int disp ){ intr++; printf( "...interrupted\n" );}/* * execcmd() - launch an async command execution */voidexeccmd( 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) { 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(cmdtab[slot].pid, cmdtab[slot].pid); execvp( argv[0], argv ); _exit(127); } else if( cmdtab[slot].pid == -1 ) { perror( "vfork" ); exit( EXITBAD ); } /* 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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -