1 | /* $Id: submit.c 2840 2016-08-25 21:47:04Z bird $ */
|
---|
2 | /** @file
|
---|
3 | * kMk Builtin command - submit job to a kWorker.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (c) 2007-2016 knut st. osmundsen <[email protected]>
|
---|
8 | *
|
---|
9 | * This file is part of kBuild.
|
---|
10 | *
|
---|
11 | * kBuild is free software; you can redistribute it and/or modify
|
---|
12 | * it under the terms of the GNU General Public License as published by
|
---|
13 | * the Free Software Foundation; either version 3 of the License, or
|
---|
14 | * (at your option) any later version.
|
---|
15 | *
|
---|
16 | * kBuild is distributed in the hope that it will be useful,
|
---|
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
19 | * GNU General Public License for more details.
|
---|
20 | *
|
---|
21 | * You should have received a copy of the GNU General Public License
|
---|
22 | * along with kBuild. If not, see <http://www.gnu.org/licenses/>
|
---|
23 | *
|
---|
24 | */
|
---|
25 |
|
---|
26 | /*******************************************************************************
|
---|
27 | * Header Files *
|
---|
28 | *******************************************************************************/
|
---|
29 | #ifdef __APPLE__
|
---|
30 | # define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
|
---|
31 | #endif
|
---|
32 | #include "make.h"
|
---|
33 | #include "job.h"
|
---|
34 | #include "variable.h"
|
---|
35 | #include "pathstuff.h"
|
---|
36 | #include <stdio.h>
|
---|
37 | #include <stdlib.h>
|
---|
38 | #include <string.h>
|
---|
39 | #include <errno.h>
|
---|
40 | #ifdef HAVE_ALLOCA_H
|
---|
41 | # include <alloca.h>
|
---|
42 | #endif
|
---|
43 | #if defined(_MSC_VER)
|
---|
44 | # include <ctype.h>
|
---|
45 | # include <io.h>
|
---|
46 | # include <direct.h>
|
---|
47 | # include <process.h>
|
---|
48 | #else
|
---|
49 | # include <unistd.h>
|
---|
50 | #endif
|
---|
51 |
|
---|
52 | #include "kmkbuiltin.h"
|
---|
53 | #include "err.h"
|
---|
54 |
|
---|
55 | #ifdef __OS2__
|
---|
56 | # define INCL_BASE
|
---|
57 | # include <os2.h>
|
---|
58 | # ifndef LIBPATHSTRICT
|
---|
59 | # define LIBPATHSTRICT 3
|
---|
60 | # endif
|
---|
61 | #endif
|
---|
62 |
|
---|
63 | /*********************************************************************************************************************************
|
---|
64 | * Structures and Typedefs *
|
---|
65 | *********************************************************************************************************************************/
|
---|
66 | typedef struct WORKERINSTANCE *PWORKERINSTANCE;
|
---|
67 | typedef struct WORKERINSTANCE
|
---|
68 | {
|
---|
69 | /** Pointer to the next worker instance. */
|
---|
70 | PWORKERINSTANCE pNext;
|
---|
71 | /** Pointer to the previous worker instance. */
|
---|
72 | PWORKERINSTANCE pPrev;
|
---|
73 | /** 32 or 64. */
|
---|
74 | unsigned cBits;
|
---|
75 | /** The process handle. */
|
---|
76 | HANDLE hProcess;
|
---|
77 |
|
---|
78 | } WORKERINSTANCE;
|
---|
79 |
|
---|
80 |
|
---|
81 | /*********************************************************************************************************************************
|
---|
82 | * Global Variables *
|
---|
83 | *********************************************************************************************************************************/
|
---|
84 | static PWORKERINSTANCE g_pIdleHead;
|
---|
85 | static PWORKERINSTANCE g_pIdleTail;
|
---|
86 |
|
---|
87 |
|
---|
88 |
|
---|
89 | static int usage(FILE *pOut, const char *argv0)
|
---|
90 | {
|
---|
91 | fprintf(pOut,
|
---|
92 | "usage: %s [-Z|--zap-env] [-E|--set <var=val>] [-U|--unset <var=val>]\n"
|
---|
93 | " [-C|--chdir <dir>] [--wcc-brain-damage]\n"
|
---|
94 | " [-3|--32-bit] [-6|--64-bit] [-v] -- <program> [args]\n"
|
---|
95 | " or: %s --help\n"
|
---|
96 | " or: %s --version\n"
|
---|
97 | "\n"
|
---|
98 | "Options:\n"
|
---|
99 | " -Z, --zap-env, -i, --ignore-environment\n"
|
---|
100 | " Zaps the environment. Position dependent.\n"
|
---|
101 | " -E, --set <var>=[value]\n"
|
---|
102 | " Sets an enviornment variable putenv fashion. Position dependent.\n"
|
---|
103 | " -U, --unset <var>\n"
|
---|
104 | " Removes an environment variable. Position dependent.\n"
|
---|
105 | " -C, --chdir <dir>\n"
|
---|
106 | " Specifies the current directory for the program. Relative paths\n"
|
---|
107 | " are relative to the previous -C option. Default is getcwd value.\n"
|
---|
108 | " -3, --32-bit\n"
|
---|
109 | " Selects a 32-bit kWorker process. Default: kmk bit count\n"
|
---|
110 | " -6, --64-bit\n"
|
---|
111 | " Selects a 64-bit kWorker process. Default: kmk bit count\n"
|
---|
112 | " --wcc-brain-damage\n"
|
---|
113 | " Works around wcc and wcc386 (Open Watcom) not following normal\n"
|
---|
114 | " quoting conventions on Windows, OS/2, and DOS.\n"
|
---|
115 | " -v,--verbose\n"
|
---|
116 | " More verbose execution.\n"
|
---|
117 | " -V,--version\n"
|
---|
118 | " Show the version number.\n"
|
---|
119 | " -h,--help\n"
|
---|
120 | " Show this usage information.\n"
|
---|
121 | "\n"
|
---|
122 | ,
|
---|
123 | argv0, argv0, argv0);
|
---|
124 | return 1;
|
---|
125 | }
|
---|
126 |
|
---|
127 |
|
---|
128 | int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild)
|
---|
129 | {
|
---|
130 | int rcExit = 0;
|
---|
131 | int iArg;
|
---|
132 | unsigned cAllocatedEnvVars;
|
---|
133 | unsigned iEnvVar;
|
---|
134 | unsigned cEnvVars;
|
---|
135 | char **papszEnv = NULL;
|
---|
136 | const char *pszCwd = NULL;
|
---|
137 | unsigned cBitsWorker = 0;
|
---|
138 | int fWatcomBrainDamage = 0;
|
---|
139 | int cVerbosity = 0;
|
---|
140 | size_t const cbCwdBuf = GET_PATH_MAX;
|
---|
141 | PATH_VAR(szCwd);
|
---|
142 |
|
---|
143 | g_progname = argv[0];
|
---|
144 |
|
---|
145 | /*
|
---|
146 | * Create default program environment.
|
---|
147 | */
|
---|
148 | if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
|
---|
149 | { /* likely */ }
|
---|
150 | else
|
---|
151 | return err(1, "getcwd_fs failed\n");
|
---|
152 |
|
---|
153 | papszEnv = pChild->environment;
|
---|
154 | if (papszEnv)
|
---|
155 | pChild->environment = papszEnv = target_environment(pChild->file);
|
---|
156 | cEnvVars = 0;
|
---|
157 | while (papszEnv[cEnvVars] != NULL)
|
---|
158 | cEnvVars++;
|
---|
159 | cAllocatedEnvVars = cEnvVars;
|
---|
160 |
|
---|
161 | /*
|
---|
162 | * Parse the command line.
|
---|
163 | */
|
---|
164 | for (iArg = 1; iArg < argc; iArg++)
|
---|
165 | {
|
---|
166 | const char *pszArg = argv[iArg];
|
---|
167 | if (*pszArg == '-')
|
---|
168 | {
|
---|
169 | char chOpt = *++pszArg;
|
---|
170 | if (chOpt != '-')
|
---|
171 | {
|
---|
172 | if (chOpt != '\0')
|
---|
173 | { /* likely */ }
|
---|
174 | else
|
---|
175 | {
|
---|
176 | errx(1, "Incomplete option: '-'");
|
---|
177 | return usage(stderr, argv[0]);
|
---|
178 | }
|
---|
179 | }
|
---|
180 | else
|
---|
181 | {
|
---|
182 | pszArg++;
|
---|
183 |
|
---|
184 | /* '--' indicates where the bits to execute start. */
|
---|
185 | if (*pszArg == '\0')
|
---|
186 | {
|
---|
187 | iArg++;
|
---|
188 | break;
|
---|
189 | }
|
---|
190 |
|
---|
191 | if (strcmp(pszArg, "watcom-brain-damage") == 0)
|
---|
192 | {
|
---|
193 | fWatcomBrainDamage = 1;
|
---|
194 | continue;
|
---|
195 | }
|
---|
196 |
|
---|
197 | /* convert to short. */
|
---|
198 | if (strcmp(pszArg, "help") == 0)
|
---|
199 | chOpt = 'h';
|
---|
200 | else if (strcmp(pszArg, "version") == 0)
|
---|
201 | chOpt = 'V';
|
---|
202 | else if (strcmp(pszArg, "set") == 0)
|
---|
203 | chOpt = 'E';
|
---|
204 | else if (strcmp(pszArg, "unset") == 0)
|
---|
205 | chOpt = 'U';
|
---|
206 | else if ( strcmp(pszArg, "zap-env") == 0
|
---|
207 | || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
|
---|
208 | chOpt = 'Z';
|
---|
209 | else if (strcmp(pszArg, "chdir") == 0)
|
---|
210 | chOpt = 'C';
|
---|
211 | else if (strcmp(pszArg, "32-bit") == 0)
|
---|
212 | chOpt = '3';
|
---|
213 | else if (strcmp(pszArg, "64-bit") == 0)
|
---|
214 | chOpt = '6';
|
---|
215 | else if (strcmp(pszArg, "verbose") == 0)
|
---|
216 | chOpt = 'v';
|
---|
217 | else
|
---|
218 | {
|
---|
219 | errx(1, "Unknown option: '%s'", pszArg - 2);
|
---|
220 | return usage(stderr, argv[0]);
|
---|
221 | }
|
---|
222 | pszArg = "";
|
---|
223 | }
|
---|
224 |
|
---|
225 | do
|
---|
226 | {
|
---|
227 | /* Get option value first, if the option takes one. */
|
---|
228 | const char *pszValue = NULL;
|
---|
229 | switch (chOpt)
|
---|
230 | {
|
---|
231 | case 'E':
|
---|
232 | case 'U':
|
---|
233 | case 'C':
|
---|
234 | if (*pszArg != '\0')
|
---|
235 | pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
|
---|
236 | else if (++iArg < argc)
|
---|
237 | pszValue = argv[iArg];
|
---|
238 | else
|
---|
239 | {
|
---|
240 | errx(1, "Option -%c requires an value!", chOpt);
|
---|
241 | return usage(stderr, argv[0]);
|
---|
242 | }
|
---|
243 | break;
|
---|
244 | }
|
---|
245 |
|
---|
246 | switch (chOpt)
|
---|
247 | {
|
---|
248 | case 'Z':
|
---|
249 | case 'i': /* GNU env compatibility. */
|
---|
250 | for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
|
---|
251 | free(papszEnv[iEnvVar]);
|
---|
252 | papszEnv[0] = NULL;
|
---|
253 | cEnvVars = 0;
|
---|
254 | break;
|
---|
255 |
|
---|
256 | case 'E':
|
---|
257 | {
|
---|
258 | const char *pszEqual = strchr(pszValue, '=');
|
---|
259 | if (pszEqual)
|
---|
260 | {
|
---|
261 | size_t const cchVar = pszValue - pszEqual;
|
---|
262 | for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
|
---|
263 | if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
|
---|
264 | && papszEnv[iEnvVar][cchVar] == '=')
|
---|
265 | {
|
---|
266 | if (cVerbosity > 0)
|
---|
267 | fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);
|
---|
268 | free(papszEnv[iEnvVar]);
|
---|
269 | papszEnv[iEnvVar] = xstrdup(pszValue);
|
---|
270 | break;
|
---|
271 | }
|
---|
272 | if (iEnvVar == cEnvVars)
|
---|
273 | {
|
---|
274 | /* Append new variable. We probably need to resize the vector. */
|
---|
275 | if ((cEnvVars + 2) > cAllocatedEnvVars)
|
---|
276 | {
|
---|
277 | cAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
|
---|
278 | pChild->environment = papszEnv = (char **)xrealloc(papszEnv,
|
---|
279 | cAllocatedEnvVars * sizeof(papszEnv[0]));
|
---|
280 | }
|
---|
281 | papszEnv[cEnvVars++] = xstrdup(pszValue);
|
---|
282 | papszEnv[cEnvVars] = NULL;
|
---|
283 | if (cVerbosity > 0)
|
---|
284 | fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);
|
---|
285 | }
|
---|
286 | else
|
---|
287 | {
|
---|
288 | /* Check for duplicates. */
|
---|
289 | for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
|
---|
290 | if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
|
---|
291 | && papszEnv[iEnvVar][cchVar] == '=')
|
---|
292 | {
|
---|
293 | if (cVerbosity > 0)
|
---|
294 | fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
|
---|
295 | free(papszEnv[iEnvVar]);
|
---|
296 | cEnvVars--;
|
---|
297 | if (iEnvVar != cEnvVars)
|
---|
298 | papszEnv[iEnvVar] = papszEnv[cEnvVars];
|
---|
299 | papszEnv[cEnvVars] = NULL;
|
---|
300 | iEnvVar--;
|
---|
301 | }
|
---|
302 | }
|
---|
303 | }
|
---|
304 | else
|
---|
305 | return errx(1, "Missing '=': -E %s", pszValue);
|
---|
306 | break;
|
---|
307 | }
|
---|
308 |
|
---|
309 | case 'U':
|
---|
310 | {
|
---|
311 | if (strchr(pszValue, '=') == NULL)
|
---|
312 | {
|
---|
313 | unsigned cRemoved = 0;
|
---|
314 | size_t const cchVar = strlen(pszValue);
|
---|
315 | for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
|
---|
316 | if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
|
---|
317 | && papszEnv[iEnvVar][cchVar] == '=')
|
---|
318 | {
|
---|
319 | if (cVerbosity > 0)
|
---|
320 | fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n"
|
---|
321 | : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
|
---|
322 | free(papszEnv[iEnvVar]);
|
---|
323 | cEnvVars--;
|
---|
324 | if (iEnvVar != cEnvVars)
|
---|
325 | papszEnv[iEnvVar] = papszEnv[cEnvVars];
|
---|
326 | papszEnv[cEnvVars] = NULL;
|
---|
327 | cRemoved++;
|
---|
328 | iEnvVar--;
|
---|
329 | }
|
---|
330 | if (cVerbosity > 0 && !cRemoved)
|
---|
331 | fprintf(stderr, "kSubmit: not found '%s'\n", pszValue);
|
---|
332 | }
|
---|
333 | else
|
---|
334 | return errx(1, "Found invalid variable name character '=' in: -U %s", pszValue);
|
---|
335 | break;
|
---|
336 | }
|
---|
337 |
|
---|
338 | case 'C':
|
---|
339 | {
|
---|
340 | size_t cchNewCwd = strlen(pszValue);
|
---|
341 | size_t offDst;
|
---|
342 | if (cchNewCwd)
|
---|
343 | {
|
---|
344 | #ifdef HAVE_DOS_PATHS
|
---|
345 | if (*pszValue == '/' || *pszValue == '\\')
|
---|
346 | {
|
---|
347 | if (pszValue[1] == '/' || pszValue[1] == '\\')
|
---|
348 | offDst = 0; /* UNC */
|
---|
349 | else if (szCwd[1] == ':' && isalpha(szCwd[0]))
|
---|
350 | offDst = 2; /* Take drive letter from CWD. */
|
---|
351 | else
|
---|
352 | return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", szCwd, pszValue);
|
---|
353 | }
|
---|
354 | else if ( pszValue[1] == ':'
|
---|
355 | && isalpha(pszValue[0]))
|
---|
356 | {
|
---|
357 | if (pszValue[2] == '/'|| pszValue[2] == '\\')
|
---|
358 | offDst = 0; /* DOS style absolute path. */
|
---|
359 | else if ( szCwd[1] == ':'
|
---|
360 | && tolower(szCwd[0]) == tolower(pszValue[0]) )
|
---|
361 | {
|
---|
362 | pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
|
---|
363 | cchNewCwd -= 2;
|
---|
364 | offDst = strlen(szCwd);
|
---|
365 | }
|
---|
366 | else
|
---|
367 | {
|
---|
368 | /* Get current CWD on the specified drive and append value. */
|
---|
369 | int iDrive = tolower(pszValue[0]) - 'a' + 1;
|
---|
370 | if (!_getdcwd(iDrive, szCwd, cbCwdBuf))
|
---|
371 | return err(1, "_getdcwd(%d,,) failed", iDrive);
|
---|
372 | pszValue += 2;
|
---|
373 | cchNewCwd -= 2;
|
---|
374 | }
|
---|
375 | }
|
---|
376 | #else
|
---|
377 | if (*pszValue == '/')
|
---|
378 | offDst = 0;
|
---|
379 | #endif
|
---|
380 | else
|
---|
381 | offDst = strlen(szCwd); /* Relative path, append to the existing CWD value. */
|
---|
382 |
|
---|
383 | /* Do the copying. */
|
---|
384 | #ifdef HAVE_DOS_PATHS
|
---|
385 | if (offDst > 0 && szCwd[offDst - 1] != '/' && szCwd[offDst - 1] != '\\')
|
---|
386 | #else
|
---|
387 | if (offDst > 0 && szCwd[offDst - 1] != '/')
|
---|
388 | #endif
|
---|
389 | szCwd[offDst++] = '/';
|
---|
390 | if (offDst + cchNewCwd >= cbCwdBuf)
|
---|
391 | return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, szCwd, pszValue);
|
---|
392 | memcpy(&szCwd[offDst], pszValue, cchNewCwd + 1);
|
---|
393 | }
|
---|
394 | /* else: relative, no change - quitely ignore. */
|
---|
395 | break;
|
---|
396 | }
|
---|
397 |
|
---|
398 | case '3':
|
---|
399 | cBitsWorker = 32;
|
---|
400 | break;
|
---|
401 |
|
---|
402 | case '6':
|
---|
403 | cBitsWorker = 64;
|
---|
404 | break;
|
---|
405 |
|
---|
406 | case 'v':
|
---|
407 | cVerbosity++;
|
---|
408 | break;
|
---|
409 |
|
---|
410 | case 'h':
|
---|
411 | usage(stdout, argv[0]);
|
---|
412 | return 0;
|
---|
413 |
|
---|
414 | case 'V':
|
---|
415 | printf("kmk_submit - kBuild version %d.%d.%d (r%u)\n"
|
---|
416 | "Copyright (C) 2007-2016 knut st. osmundsen\n",
|
---|
417 | KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH,
|
---|
418 | KBUILD_SVN_REV);
|
---|
419 | return 0;
|
---|
420 | }
|
---|
421 | } while ((chOpt = *pszArg++) != '\0');
|
---|
422 | }
|
---|
423 | else
|
---|
424 | {
|
---|
425 | errx(1, "Unknown argument: '%s'", pszArg);
|
---|
426 | return usage(stderr, argv[0]);
|
---|
427 | }
|
---|
428 | }
|
---|
429 |
|
---|
430 | /*
|
---|
431 | * Check that we've got something to execute.
|
---|
432 | */
|
---|
433 | if (iArg < argc)
|
---|
434 | {
|
---|
435 |
|
---|
436 | }
|
---|
437 | else
|
---|
438 | {
|
---|
439 | errx(1, "Nothing to executed!");
|
---|
440 | rcExit = usage(stderr, argv[0]);
|
---|
441 | }
|
---|
442 |
|
---|
443 | return rcExit;
|
---|
444 | }
|
---|
445 |
|
---|
446 |
|
---|
447 |
|
---|