added valid install target to Jamfile
[k8jam.git] / execunix.c
blobd9b634a9c7315e29914785061aa5d7a494ad2506
1 /*
2 * Copyright 1993, 1995 Christopher Seiwald.
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
7 /*
8 * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS
10 * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
11 * The default is:
13 * /bin/sh -c % [ on UNIX/AmigaOS ]
14 * cmd.exe /c % [ on OS2/WinNT ]
16 * Each word must be an individual element in a jam variable value.
18 * In $(JAMSHELL), % expands to the command string and ! expands to
19 * the slot number (starting at 1) for multiprocess (-j) invocations.
20 * If $(JAMSHELL) doesn't include a %, it is tacked on as the last
21 * argument.
23 * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work!
25 * External routines:
26 * execcmd() - launch an async command execution
27 * execwait() - wait and drive at most one execution completion
29 * Internal routines:
30 * onintr() - bump intr to note command interruption
32 * 04/08/94 (seiwald) - Coherent/386 support added.
33 * 05/04/94 (seiwald) - async multiprocess interface
34 * 01/22/95 (seiwald) - $(JAMSHELL) support
35 * 06/02/97 (gsar) - full async multiprocess support for Win32
36 * 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
37 * 11/04/02 (seiwald) - const-ing for string literals
38 * 12/27/02 (seiwald) - grist .bat file with pid for system uniqueness
40 # include "jam.h"
41 # include "lists.h"
42 # include "execcmd.h"
43 # include <errno.h>
45 # ifdef USE_EXECUNIX
47 # ifdef OS_OS2
48 # define USE_EXECNT
49 # include <process.h>
50 # endif
52 # ifdef OS_NT
53 # define USE_EXECNT
54 # include <process.h>
55 # define WIN32_LEAN_AND_MEAN
56 # include <windows.h> /* do the ugly deed */
57 # define USE_MYWAIT
58 # if !defined( __BORLANDC__ )
59 # define wait my_wait
60 static int my_wait (int *status);
61 # endif
62 # endif
65 //extern int execvp (const char *file, const char *argv[]);
67 static int intr = 0;
68 static int cmdsrunning = 0;
69 static void (*istat) (int);
71 static struct {
72 int pid; /* on win32, a real process handle */
73 void (*func) (void *closure, int status);
74 void *closure;
75 #ifdef USE_EXECNT
76 char *tempfile;
77 #endif
78 } cmdtab[MAXJOBS] = {{0}};
82 * onintr() - bump intr to note command interruption
84 void onintr (int disp) {
85 intr++;
86 printf("...interrupted\n");
91 * execcmd() - launch an async command execution
93 void execcmd (
94 char *string,
95 void (*func) (void *closure, int status),
96 void *closure,
97 LIST *shell)
99 int pid;
100 int slot;
101 const char *argv[MAXARGC+1]; /* +1 for NULL */
102 #ifdef USE_EXECNT
103 char *p;
104 #endif
106 /* Find a slot in the running commands table for this one. */
107 for (slot = 0; slot < MAXJOBS; slot++) if (!cmdtab[slot].pid) break;
108 if (slot == MAXJOBS) {
109 printf("no slots for child!\n");
110 exit(EXITBAD);
113 #ifdef USE_EXECNT
114 if (!cmdtab[slot].tempfile) {
115 char *tempdir;
117 if (!(tempdir = getenv("TEMP")) && !(tempdir = getenv("TMP"))) tempdir = "\\temp";
118 /* +32 is room for \jamXXXXXtSS.bat (at least) */
119 cmdtab[slot].tempfile = malloc(strlen(tempdir)+32);
120 sprintf(cmdtab[slot].tempfile, "%s\\jam%ut%d.bat", tempdir, (unsigned int)(GetCurrentProcessId()), slot);
123 /* Trim leading, ending white space */
124 while(isspace(*string)) ++string;
125 p = strchr(string, '\n');
126 while (p && isspace(*p)) ++p;
128 /* If multi line, or too long, or JAMSHELL is set, write to bat file. */
129 /* Otherwise, exec directly. */
130 /* Frankly, if it is a single long line I don't think the */
131 /* command interpreter will do any better -- it will fail. */
132 if ((p && *p) || strlen(string) > MAXLINE || shell) {
133 FILE *f;
135 /* Write command to bat file. */
136 f = fopen(cmdtab[slot].tempfile, "w");
137 fputs(string, f);
138 fclose(f);
139 string = cmdtab[slot].tempfile;
141 #endif
143 /* Forumulate argv */
144 /* If shell was defined, be prepared for % and ! subs. */
145 /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */
146 if (shell) {
147 int i;
148 char jobno[4];
149 int gotpercent = 0;
151 sprintf(jobno, "%d", slot+1);
153 for (i = 0; shell && i < MAXARGC; i++, shell = list_next(shell)) {
154 switch (shell->string[0]) {
155 case '%': argv[i] = string; gotpercent++; break;
156 case '!': argv[i] = jobno; break;
157 default: argv[i] = shell->string;
159 if (DEBUG_EXECCMD) printf("argv[%d] = '%s'\n", i, argv[i]);
161 if (!gotpercent) argv[i++] = string;
162 argv[i] = 0;
163 } else {
164 #ifdef USE_EXECNT
165 argv[0] = "cmd.exe";
166 argv[1] = "/Q/C"; /* anything more is non-portable */
167 #else
168 argv[0] = "/bin/sh";
169 argv[1] = "-c";
170 #endif
171 argv[2] = string;
172 argv[3] = 0;
175 /* Catch interrupts whenever commands are running. */
176 if (!cmdsrunning++) istat = signal(SIGINT, onintr);
178 /* Start the command */
179 #ifdef USE_EXECNT
180 if ((pid = spawnvp(P_NOWAIT, argv[0], argv)) == -1) {
181 perror("spawn");
182 exit(EXITBAD);
184 #else
185 #ifdef NO_VFORK
186 if ((pid = fork()) == 0) {
187 execvp(argv[0], argv);
188 _exit(127);
190 #else
191 if ((pid = vfork()) == 0) {
192 execvp(argv[0], (void *)argv);
193 _exit(127);
195 #endif
196 if (pid == -1) {
197 perror("vfork");
198 exit(EXITBAD);
200 #endif
201 /* Save the operation for execwait() to find. */
202 cmdtab[slot].pid = pid;
203 cmdtab[slot].func = func;
204 cmdtab[slot].closure = closure;
206 /* Wait until we're under the limit of concurrent commands. */
207 /* Don't trust globs.jobs alone. */
208 while (cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs) if (!execwait()) break;
213 * execwait() - wait and drive at most one execution completion
215 int execwait () {
216 int i;
217 int status, w;
218 int rstat;
220 /* Handle naive make1() which doesn't know if cmds are running. */
221 if (!cmdsrunning) return 0;
222 /* Pick up process pid and status */
223 while ((w = wait(&status)) == -1 && errno == EINTR) ;
224 if (w == -1) {
225 printf("child process(es) lost!\n");
226 perror("wait");
227 exit(EXITBAD);
230 /* Find the process in the cmdtab. */
231 for (i = 0; i < MAXJOBS; i++) if (w == cmdtab[i].pid) break;
232 if (i == MAXJOBS) {
233 printf("waif child found!\n");
234 exit(EXITBAD);
236 #ifdef USE_EXECNT
237 /* Clear the temp file */
238 unlink(cmdtab[i].tempfile);
239 #endif
241 /* Drive the completion */
242 if (!--cmdsrunning) signal(SIGINT, istat);
244 if (intr) rstat = EXEC_CMD_INTR;
245 else if (w == -1 || status != 0) rstat = EXEC_CMD_FAIL;
246 else rstat = EXEC_CMD_OK;
248 cmdtab[i].pid = 0;
249 (*cmdtab[i].func)(cmdtab[i].closure, rstat);
251 return 1;
255 #ifdef USE_MYWAIT
256 static int my_wait (int *status) {
257 int i, num_active = 0;
258 DWORD exitcode, waitcode;
259 static HANDLE *active_handles = 0;
261 if (!active_handles) active_handles = (HANDLE *)malloc(globs.jobs*sizeof(HANDLE));
262 /* first see if any non-waited-for processes are dead, and return if so. */
263 for (i = 0; i < globs.jobs; i++) {
264 if (cmdtab[i].pid) {
265 if (GetExitCodeProcess((HANDLE)cmdtab[i].pid, &exitcode)) {
266 if (exitcode == STILL_ACTIVE) {
267 active_handles[num_active++] = (HANDLE)cmdtab[i].pid;
268 } else {
269 CloseHandle((HANDLE)cmdtab[i].pid);
270 *status = (int)((exitcode & 0xff) << 8);
271 return cmdtab[i].pid;
273 } else goto FAILED;
276 /* if a child exists, wait for it to die */
277 if (!num_active) {
278 errno = ECHILD;
279 return -1;
281 waitcode = WaitForMultipleObjects(num_active, active_handles, FALSE, INFINITE);
282 if (waitcode != WAIT_FAILED) {
283 if (waitcode >= WAIT_ABANDONED_0 && waitcode < WAIT_ABANDONED_0+num_active) i = waitcode-WAIT_ABANDONED_0;
284 else i = waitcode-WAIT_OBJECT_0;
285 if (GetExitCodeProcess(active_handles[i], &exitcode)) {
286 CloseHandle(active_handles[i]);
287 *status = (int)((exitcode & 0xff) << 8);
288 return (int)active_handles[i];
292 FAILED:
293 errno = GetLastError();
294 return -1;
296 # endif /* USE_MYWAIT */
299 # endif /* USE_EXECUNIX */