VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin.c@ 3387

Last change on this file since 3387 was 3352, checked in by bird, 4 years ago

Adding kmk_kill for windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* $Id: kmkbuiltin.c 3352 2020-06-05 00:31:50Z bird $ */
2/** @file
3 * kMk Builtin command execution.
4 */
5
6/*
7 * Copyright (c) 2005-2018 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
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/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30#include <string.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <ctype.h>
34#include <assert.h>
35#include <sys/stat.h>
36#ifdef _MSC_VER
37# include <io.h>
38#endif
39
40#include "makeint.h"
41#include "job.h"
42#include "variable.h"
43#if defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
44# include "w32/winchildren.h"
45#endif
46#include "kmkbuiltin/err.h"
47#include "kmkbuiltin.h"
48
49#ifndef _MSC_VER
50extern char **environ;
51#endif
52
53
54/*********************************************************************************************************************************
55* Global Variables *
56*********************************************************************************************************************************/
57#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
58extern int print_stats_flag;
59#endif
60
61
62
63int kmk_builtin_command(const char *pszCmd, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
64{
65 int argc;
66 char **argv;
67 int rc;
68 char *pszzCmd;
69 char *pszDst;
70 int fOldStyle = 0;
71
72 /*
73 * Check and skip the prefix.
74 */
75 if (strncmp(pszCmd, "kmk_builtin_", sizeof("kmk_builtin_") - 1))
76 {
77 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
78 return 1;
79 }
80
81 /*
82 * Parse arguments.
83 */
84 rc = 0;
85 argc = 0;
86 argv = NULL;
87 pszzCmd = pszDst = (char *)strdup(pszCmd);
88 if (!pszDst)
89 {
90 fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
91 return 1;
92 }
93 do
94 {
95 const char * const pszSrcStart = pszCmd;
96 char ch;
97 char chQuote;
98
99 /*
100 * Start new argument.
101 */
102 if (!(argc % 16))
103 {
104 void *pv = realloc(argv, sizeof(char *) * (argc + 17));
105 if (!pv)
106 {
107 fprintf(stderr, "kmk_builtin: out of memory. argc=%d\n", argc);
108 rc = 1;
109 break;
110 }
111 argv = (char **)pv;
112 }
113 argv[argc++] = pszDst;
114 argv[argc] = NULL;
115
116 if (!fOldStyle)
117 {
118 /*
119 * Process the next argument, bourne style.
120 */
121 chQuote = 0;
122 ch = *pszCmd++;
123 do
124 {
125 /* Unquoted mode? */
126 if (chQuote == 0)
127 {
128 if (ch != '\'' && ch != '"')
129 {
130 if (!isspace(ch))
131 {
132 if (ch != '\\')
133 *pszDst++ = ch;
134 else
135 {
136 ch = *pszCmd++;
137 if (ch == '\n') /* escaped end-of-line */
138 break;
139 if (ch == '\r' && *pszCmd == '\n') /* escaped end-of-line */
140 {
141 pszCmd++;
142 break;
143 }
144 if (ch)
145 *pszDst++ = ch;
146 else
147 {
148 fprintf(stderr, "kmk_builtin: Incomplete escape sequence in argument %d: %s\n",
149 argc, pszSrcStart);
150 rc = 1;
151 break;
152 }
153 }
154 }
155 else
156 break;
157 }
158 else
159 chQuote = ch;
160 }
161 /* Quoted mode */
162 else if (ch != chQuote)
163 {
164 if ( ch != '\\'
165 || chQuote == '\'')
166 *pszDst++ = ch;
167 else
168 {
169 ch = *pszCmd++;
170 if (ch)
171 {
172 if ( ch != '\\'
173 && ch != '"'
174 && ch != '`'
175 && ch != '$'
176 && ch != '\n')
177 *pszDst++ = '\\';
178 *pszDst++ = ch;
179 }
180 else
181 {
182 fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
183 rc = 1;
184 break;
185 }
186 }
187 }
188 else
189 chQuote = 0;
190 } while ((ch = *pszCmd++) != '\0');
191 }
192 else
193 {
194 /*
195 * Old style in case we ever need it.
196 */
197 ch = *pszCmd++;
198 if (ch != '"' && ch != '\'')
199 {
200 do
201 *pszDst++ = ch;
202 while ((ch = *pszCmd++) != '\0' && !isspace(ch));
203 }
204 else
205 {
206 chQuote = ch;
207 for (;;)
208 {
209 char *pszEnd = strchr(pszCmd, chQuote);
210 if (pszEnd)
211 {
212 fprintf(stderr, "kmk_builtin: Unbalanced quote in argument %d: %s\n", argc, pszSrcStart);
213 rc = 1;
214 break;
215 }
216 memcpy(pszDst, pszCmd, pszEnd - pszCmd);
217 pszDst += pszEnd - pszCmd;
218 if (pszEnd[1] != chQuote)
219 break;
220 *pszDst++ = chQuote;
221 }
222 }
223 }
224 *pszDst++ = '\0';
225
226 /*
227 * Skip argument separators (IFS=space() for now). Check for EOS.
228 */
229 if (ch != 0)
230 while ( (ch = *pszCmd)
231 && ( isspace(ch)
232 || (ch == '\\' && (pszCmd[1] == '\n' || (pszCmd[1] == '\r' && pszCmd[2] == '\n')))))
233 pszCmd++;
234 if (ch == 0)
235 break;
236 } while (rc == 0);
237
238 /*
239 * Execute the command if parsing was successful.
240 */
241 if (rc == 0)
242 rc = kmk_builtin_command_parsed(argc, argv, pChild, ppapszArgvToSpawn, pPidSpawned);
243
244 /* clean up and return. */
245 free(argv);
246 free(pszzCmd);
247 return rc;
248}
249
250
251/**
252 * kmk built command.
253 */
254static const KMKBUILTINENTRY g_aBuiltIns[] =
255{
256#define BUILTIN_ENTRY(a_fn, a_sz, a_uFnSignature, fMtSafe, fNeedEnv) \
257 { { { sizeof(a_sz) - 1, a_sz, } }, { (uintptr_t)a_fn }, a_uFnSignature, fMtSafe, fNeedEnv }
258
259 /* More frequently used commands: */
260 BUILTIN_ENTRY(kmk_builtin_append, "append", FN_SIG_MAIN_SPAWNS, 0, 0),
261 BUILTIN_ENTRY(kmk_builtin_printf, "printf", FN_SIG_MAIN, 0, 0),
262 BUILTIN_ENTRY(kmk_builtin_echo, "echo", FN_SIG_MAIN, 0, 0),
263 BUILTIN_ENTRY(kmk_builtin_install, "install", FN_SIG_MAIN, 1, 0),
264 BUILTIN_ENTRY(kmk_builtin_kDepObj, "kDepObj", FN_SIG_MAIN, 1, 0),
265#ifdef KBUILD_OS_WINDOWS
266 BUILTIN_ENTRY(kmk_builtin_kSubmit, "kSubmit", FN_SIG_MAIN_SPAWNS, 0, 1),
267 BUILTIN_ENTRY(kmk_builtin_kill, "kill", FN_SIG_MAIN, 0, 0),
268#endif
269 BUILTIN_ENTRY(kmk_builtin_mkdir, "mkdir", FN_SIG_MAIN, 0, 0),
270 BUILTIN_ENTRY(kmk_builtin_mv, "mv", FN_SIG_MAIN, 0, 0),
271 BUILTIN_ENTRY(kmk_builtin_redirect, "redirect", FN_SIG_MAIN_SPAWNS, 1, 1),
272 BUILTIN_ENTRY(kmk_builtin_rm, "rm", FN_SIG_MAIN, 1, 1),
273 BUILTIN_ENTRY(kmk_builtin_rmdir, "rmdir", FN_SIG_MAIN, 0, 0),
274 BUILTIN_ENTRY(kmk_builtin_test, "test", FN_SIG_MAIN_TO_SPAWN, 0, 0),
275 /* Less frequently used commands: */
276 BUILTIN_ENTRY(kmk_builtin_kDepIDB, "kDepIDB", FN_SIG_MAIN, 0, 0),
277 BUILTIN_ENTRY(kmk_builtin_chmod, "chmod", FN_SIG_MAIN, 0, 0),
278 BUILTIN_ENTRY(kmk_builtin_cp, "cp", FN_SIG_MAIN, 1, 1),
279 BUILTIN_ENTRY(kmk_builtin_expr, "expr", FN_SIG_MAIN, 0, 0),
280 BUILTIN_ENTRY(kmk_builtin_ln, "ln", FN_SIG_MAIN, 0, 0),
281 BUILTIN_ENTRY(kmk_builtin_md5sum, "md5sum", FN_SIG_MAIN, 1, 0),
282 BUILTIN_ENTRY(kmk_builtin_cmp, "cmp", FN_SIG_MAIN, 0, 0),
283 BUILTIN_ENTRY(kmk_builtin_cat, "cat", FN_SIG_MAIN, 0, 0),
284 BUILTIN_ENTRY(kmk_builtin_touch, "touch", FN_SIG_MAIN, 0, 0),
285 BUILTIN_ENTRY(kmk_builtin_sleep, "sleep", FN_SIG_MAIN, 1, 0),
286 BUILTIN_ENTRY(kmk_builtin_dircache, "dircache", FN_SIG_MAIN, 0, 0),
287};
288
289#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
290/** Statistics running in parallel to g_aBuiltIns. */
291struct
292{
293 big_int cNs;
294 unsigned cTimes;
295 unsigned cAsyncTimes;
296} g_aBuiltInStats[sizeof(g_aBuiltIns) / sizeof(g_aBuiltIns[0])];
297#endif
298
299
300int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char ***ppapszArgvToSpawn, pid_t *pPidSpawned)
301{
302 /*
303 * Check and skip the prefix.
304 */
305 static const char s_szPrefix[] = "kmk_builtin_";
306 const char *pszCmd = argv[0];
307 if (strncmp(pszCmd, s_szPrefix, sizeof(s_szPrefix) - 1) == 0)
308 {
309 struct KMKBUILTINENTRY const *pEntry;
310 size_t cchAndStart;
311#if K_ENDIAN == K_ENDIAN_BIG
312 size_t cch;
313#endif
314 int cLeft;
315
316 pszCmd += sizeof(s_szPrefix) - 1;
317
318 /*
319 * Calc the length and start word to avoid calling memcmp/strcmp on each entry.
320 */
321#if K_ARCH_BITS != 64 && K_ARCH_BITS != 32
322# error "PORT ME!"
323#endif
324 cchAndStart = strlen(pszCmd);
325#if K_ENDIAN == K_ENDIAN_BIG
326 cch = cchAndStart;
327 cchAndStart <<= K_ARCH_BITS - 8;
328 switch (cch)
329 {
330 default: /* fall thru */
331# if K_ARCH_BITS >= 64
332 case 7: cchAndStart |= (size_t)pszCmd[6]; /* fall thru */
333 case 6: cchAndStart |= (size_t)pszCmd[5] << (K_ARCH_BITS - 56); /* fall thru */
334 case 5: cchAndStart |= (size_t)pszCmd[4] << (K_ARCH_BITS - 48); /* fall thru */
335 case 4: cchAndStart |= (size_t)pszCmd[3] << (K_ARCH_BITS - 40); /* fall thru */
336# endif
337 /* fall thru - gcc 8.2.0 is confused by # endif */
338 case 3: cchAndStart |= (size_t)pszCmd[2] << (K_ARCH_BITS - 32); /* fall thru */
339 case 2: cchAndStart |= (size_t)pszCmd[1] << (K_ARCH_BITS - 24); /* fall thru */
340 case 1: cchAndStart |= (size_t)pszCmd[0] << (K_ARCH_BITS - 16); /* fall thru */
341 case 0: break;
342 }
343#else
344 switch (cchAndStart)
345 {
346 default: /* fall thru */
347# if K_ARCH_BITS >= 64
348 case 7: cchAndStart |= (size_t)pszCmd[6] << 56; /* fall thru */
349 case 6: cchAndStart |= (size_t)pszCmd[5] << 48; /* fall thru */
350 case 5: cchAndStart |= (size_t)pszCmd[4] << 40; /* fall thru */
351 case 4: cchAndStart |= (size_t)pszCmd[3] << 32; /* fall thru */
352# endif
353 /* fall thru - gcc 8.2.0 is confused by # endif */
354 case 3: cchAndStart |= (size_t)pszCmd[2] << 24; /* fall thru */
355 case 2: cchAndStart |= (size_t)pszCmd[1] << 16; /* fall thru */
356 case 1: cchAndStart |= (size_t)pszCmd[0] << 8; /* fall thru */
357 case 0: break;
358 }
359#endif
360
361 /*
362 * Look up the builtin command in the table.
363 */
364 pEntry = &g_aBuiltIns[0];
365 cLeft = sizeof(g_aBuiltIns) / sizeof(g_aBuiltIns[0]);
366 while (cLeft-- > 0)
367 if ( pEntry->uName.cchAndStart != cchAndStart
368 || ( pEntry->uName.s.cch >= sizeof(cchAndStart)
369 && memcmp(pEntry->uName.s.sz, pszCmd, pEntry->uName.s.cch) != 0) )
370 pEntry++;
371 else
372 {
373 /*
374 * That's a match!
375 *
376 * First get the environment if it is actually needed. This is
377 * especially important when we run on a worker thread as it must
378 * not under any circumstances do stuff like target_environment.
379 */
380 int rc;
381 char **papszEnvVars = NULL;
382 if (pEntry->fNeedEnv)
383 {
384 papszEnvVars = pChild->environment;
385 if (!papszEnvVars)
386 pChild->environment = papszEnvVars = target_environment(pChild->file);
387 }
388
389#if defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
390 /*
391 * If the built-in is multi thread safe, we will run it on a job slot thread.
392 */
393 if (pEntry->fMtSafe)
394 {
395 rc = MkWinChildCreateBuiltIn(pEntry, argc, argv, papszEnvVars, pChild, pPidSpawned);
396# ifdef CONFIG_WITH_KMK_BUILTIN_STATS
397 g_aBuiltInStats[pEntry - &g_aBuiltIns[0]].cAsyncTimes++;
398# endif
399 }
400 else
401#endif
402 {
403 /*
404 * Call the worker function, making sure to preserve umask.
405 */
406#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
407 big_int nsStart = print_stats_flag ? nano_timestamp() : 0;
408#endif
409 KMKBUILTINCTX Ctx;
410 int const iUmask = umask(0); /* save umask */
411 umask(iUmask);
412
413 Ctx.pszProgName = pEntry->uName.s.sz;
414 Ctx.pOut = pChild ? &pChild->output : NULL;
415
416 if (pEntry->uFnSignature == FN_SIG_MAIN)
417 rc = pEntry->u.pfnMain(argc, argv, papszEnvVars, &Ctx);
418 else if (pEntry->uFnSignature == FN_SIG_MAIN_SPAWNS)
419 rc = pEntry->u.pfnMainSpawns(argc, argv, papszEnvVars, &Ctx, pChild, pPidSpawned);
420 else if (pEntry->uFnSignature == FN_SIG_MAIN_TO_SPAWN)
421 {
422 /*
423 * When we got something to execute, check if the child is a kmk_builtin thing.
424 * We recurse here, both because I'm lazy and because it's easier to debug a
425 * problem then (the call stack shows what's been going on).
426 */
427 rc = pEntry->u.pfnMainToSpawn(argc, argv, papszEnvVars, &Ctx, ppapszArgvToSpawn);
428 if ( !rc
429 && *ppapszArgvToSpawn
430 && !strncmp(**ppapszArgvToSpawn, s_szPrefix, sizeof(s_szPrefix) - 1))
431 {
432 char **argv_new = *ppapszArgvToSpawn;
433 int argc_new = 1;
434 while (argv_new[argc_new])
435 argc_new++;
436
437 assert(argv_new[0] != argv[0]);
438 assert(!*pPidSpawned);
439
440 *ppapszArgvToSpawn = NULL;
441 rc = kmk_builtin_command_parsed(argc_new, argv_new, pChild, ppapszArgvToSpawn, pPidSpawned);
442
443 free(argv_new[0]);
444 free(argv_new);
445 }
446 }
447 else
448 rc = 99;
449
450 umask(iUmask); /* restore it */
451
452#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
453 if (print_stats_flag)
454 {
455 uintptr_t iEntry = pEntry - &g_aBuiltIns[0];
456 g_aBuiltInStats[iEntry].cTimes++;
457 g_aBuiltInStats[iEntry].cNs += nano_timestamp() - nsStart;
458 }
459#endif
460 }
461 return rc;
462 }
463
464 /*
465 * No match! :-(
466 */
467 fprintf(stderr, "kmk_builtin: Unknown command '%s%s'!\n", s_szPrefix, pszCmd);
468 }
469 else
470 fprintf(stderr, "kmk_builtin: Invalid command prefix '%s'!\n", pszCmd);
471 return 1;
472}
473
474#ifndef KBUILD_OS_WINDOWS
475/** Dummy. */
476int kmk_builtin_dircache(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
477{
478 (void)argc; (void)argv; (void)envp; (void)pCtx;
479 return 0;
480}
481#endif
482
483#ifdef CONFIG_WITH_KMK_BUILTIN_STATS
484/**
485 * Prints the statistiscs to the given output stream.
486 */
487extern void kmk_builtin_print_stats(FILE *pOutput, const char *pszPrefix)
488{
489 const unsigned cEntries = sizeof(g_aBuiltInStats) / sizeof(g_aBuiltInStats[0]);
490 unsigned i;
491 assert(print_stats_flag);
492 fprintf(pOutput, "\n%skmk built-in command statistics:\n", pszPrefix);
493 for (i = 0; i < cEntries; i++)
494 if (g_aBuiltInStats[i].cTimes > 0)
495 {
496 char szTotal[64];
497 char szAvg[64];
498 format_elapsed_nano(szTotal, sizeof(szTotal), g_aBuiltInStats[i].cNs);
499 format_elapsed_nano(szAvg, sizeof(szAvg), g_aBuiltInStats[i].cNs / g_aBuiltInStats[i].cTimes);
500 fprintf(pOutput, "%s kmk_builtin_%-9s: %4u times, %9s total, %9s/call\n",
501 pszPrefix, g_aBuiltIns[i].uName.s.sz, g_aBuiltInStats[i].cTimes, szTotal, szAvg);
502 }
503 else if (g_aBuiltInStats[i].cAsyncTimes > 0)
504 fprintf(pOutput, "%s kmk_builtin_%-9s: %4u times in worker thread\n",
505 pszPrefix, g_aBuiltIns[i].uName.s.sz, g_aBuiltInStats[i].cAsyncTimes);
506}
507#endif
508
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use