Dash:
[t2-trunk.git] / misc / tools-source / cmd_wrapper.c
blob462bc69de750df20776bb32822780ce5ff3caa76
1 /*
2 * --- T2-COPYRIGHT-NOTE-BEGIN ---
3 * This copyright note is auto-generated by scripts/Create-CopyPatch.
4 *
5 * T2 SDE: misc/tools-source/cmd_wrapper.c
6 * Copyright (C) 2004 - 2021 The T2 SDE Project
7 * Copyright (C) 1998 - 2003 ROCK Linux Project
8 * Copyright (C) 2006 - 2010 Rene Rebe <rene@exactcode.de>
9 *
10 * More information can be found in the files COPYING and README.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 2 of the License. A copy of the
15 * GNU General Public License can be found in the file COPYING.
16 * --- T2-COPYRIGHT-NOTE-END ---
18 * Generic command wrapper.
20 * Recognised Variables:
22 * prefix_WRAPPER_MYPATH "/path/to/binary"
23 * prefix_WRAPPER_LOGFILE ".cmd_wrapper_log"
25 * prefix_WRAPPER_DEBUG 0
26 * prefix_WRAPPER_BYPASS 0
28 * prefix_WRAPPER_OTHERS "other_cmd"
30 * prefix_WRAPPER_INSERT "-1st-opt -2nd-opt"
31 * prefix_WRAPPER_REMOVE "-del-this-opt -this-also [!-]*"
32 * prefix_WRAPPER_APPEND "-last-opt"
34 * prefix_WRAPPER_FILTER "sed '...' | awk '...' | foobar"
36 * prefix_WRAPPER_NOLOOP Internal use only.
37 * prefix_WRAPPER_OTHERS_DONE Internal use only.
41 #include <stdlib.h>
42 #include <string.h>
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <ctype.h>
46 #include <sys/wait.h>
47 #include <libgen.h>
48 #include <fnmatch.h>
49 #include <errno.h>
51 #ifndef ENVPREFIX
52 # error You must use -DENVPREFIX=".." when compiling this tool.
53 #endif
55 #ifndef MYNAME
56 # error You must use -DMYNAME=".." when compiling this tool.
57 #endif
59 #define VERBOSE_DEBUG 1
60 int debug=1;
63 * Clean config vars before using them
65 void cleanenv(const char * name, const char ch) {
66 int pos1=0, pos2=0, delim=1;
67 char *tmp1, *tmp2;
69 setenv(name, "", 0); /* no overwrite - make sure it is defined */
70 tmp1 = getenv(name);
71 tmp2 = malloc(strlen(tmp1)+1);
73 while ( tmp1[pos1] ) {
74 if ( tmp1[pos1] == ch ) {
75 if ( ! delim ) {
76 tmp2[pos2++] = ch;
77 delim = 1;
79 } else {
80 tmp2[pos2++] = tmp1[pos1];
81 delim = 0;
83 pos1++;
85 if (pos2 > 0 && tmp2[pos2-1] == ch) pos2--;
86 tmp2[pos2] = 0; setenv(name, tmp2, 1);
90 * Evaluate conditional argument in the form:
91 * "condition?matched-value:unmatched-value"
93 char* eval_cond_arg (char * arg, int argc, char ** argv) {
94 char * c = arg;
95 char * lhs = NULL, * rhs = NULL;
97 /* look for '?' */
98 while (*c && *c != '?') c++;
100 if (*c != '?') {
101 return arg;
103 #if VERBOSE_DEBUG
104 if (debug) fprintf(stderr, "Conditonal arg: '%s'\n", arg);
105 #endif
107 /* split conditional (arg), left hand and right hand statement */
108 *c++ = 0;
109 lhs = c;
111 /* look for ':' */
112 while (*c && *c != ':') c++;
113 if (*c == ':')
114 *c++ = 0;
115 rhs = c;
117 if (debug) fprintf(stderr, "Conditonal: '%s', lhs: '%s', rhs: '%s'\n", arg, lhs, rhs);
119 /* match arguments against conditional */
120 while (argc--)
122 if (!fnmatch(arg, *argv, 0)) {
123 #if VERBOSE_DEBUG
124 if (debug) fprintf(stderr, "Conditonal: '%s', matched: '%s'\n", arg, *argv);
125 #endif
126 return lhs;
128 argv++;
131 return rhs;
134 /* newargv memory management, realloc if newargv is filled */
135 static inline char** realloc_if_needed(int c1, int* newargc, char** newargv) {
136 if (c1 + 1 >= *newargc) { /* +1 for NULL-sentinel */
137 *newargc *= 2;
138 newargv = realloc (newargv, sizeof(char*) * *newargc);
140 return newargv;
144 * Main function.
146 int main(int argc, char ** argv) {
147 int newargc = 64; /* initial newargv size, anyhting >3 (name, other, NULL) */
148 char **newargv;
149 char *other, *other_done;
150 int c1,c2,c3;
151 char *delim, *optbuf, *wrdir;
152 FILE *logfile = NULL;
154 /* Calling the wrapper with an absolute path results in an
155 endless-loop. Use basename only to force a $PATH lookup. */
156 argv[0] = basename(argv[0]);
157 if ( !strcmp(argv[0], MYNAME) ) {
158 if ( argc > 1 ) {
159 argv++; argc--;
160 argv[0] = basename(argv[0]);
161 } else {
162 exit(250);
166 /* Open logfile (if any) */
167 delim = getenv(ENVPREFIX "_WRAPPER_LOGFILE");
168 if (delim && delim[0]) {
169 logfile = fopen(delim, "a");
171 if (logfile) {
172 delim = malloc(FILENAME_MAX);
173 if ( getcwd(delim, FILENAME_MAX) == NULL ) delim[0]=0;
174 fprintf(logfile, "\n%s:\n-", delim);
175 for (c3=0; c3<argc; c3++) fprintf(logfile, " %s", argv[c3]);
176 fprintf(logfile, "\n");
177 free(delim);
181 * Read prefix_WRAPPER_DEBUG and prefix_WRAPPER_BYPASS
184 if ( (delim=getenv(ENVPREFIX "_WRAPPER_DEBUG")) != NULL &&
185 delim[0] ) debug = atoi(delim);
187 if ( (delim=getenv(ENVPREFIX "_WRAPPER_BYPASS")) != NULL &&
188 delim[0] && atoi(delim)) {
189 #if VERBOSE_DEBUG
190 if (debug) fprintf(stderr, "Bypassing cmd_wrapper by "
191 "clearing all config variables.\n");
192 #endif
193 setenv(ENVPREFIX "_WRAPPER_OTHERS", "", 1);
194 setenv(ENVPREFIX "_WRAPPER_INSERT", "", 1);
195 setenv(ENVPREFIX "_WRAPPER_REMOVE", "", 1);
196 setenv(ENVPREFIX "_WRAPPER_APPEND", "", 1);
197 setenv(ENVPREFIX "_WRAPPER_FILTER", "", 1);
200 cleanenv(ENVPREFIX "_WRAPPER_OTHERS", ':');
201 cleanenv(ENVPREFIX "_WRAPPER_INSERT", ' ');
202 cleanenv(ENVPREFIX "_WRAPPER_REMOVE", ' ');
203 cleanenv(ENVPREFIX "_WRAPPER_APPEND", ' ');
204 cleanenv(ENVPREFIX "_WRAPPER_FILTER", '|');
206 #if VERBOSE_DEBUG
207 if (debug) {
208 fprintf(stderr, "Old Command:");
209 for (c3=0; c3<argc; c3++) fprintf(stderr, " %s", argv[c3]);
210 fprintf(stderr, "\n");
211 fprintf(stderr, "ENVPREFIX = '%s'\n", ENVPREFIX);
212 fprintf(stderr, "OTHERS = '%s'\n",
213 getenv(ENVPREFIX "_WRAPPER_OTHERS"));
214 if (getenv(ENVPREFIX "_WRAPPER_OTHERS_DONE"))
215 fprintf(stderr, "OTHERS DONE = '%s'\n",
216 getenv(ENVPREFIX "_WRAPPER_OTHERS_DONE"));
217 fprintf(stderr, "INSERT = '%s'\n",
218 getenv(ENVPREFIX "_WRAPPER_INSERT"));
219 fprintf(stderr, "REMOVE = '%s'\n",
220 getenv(ENVPREFIX "_WRAPPER_REMOVE"));
221 fprintf(stderr, "APPEND = '%s'\n",
222 getenv(ENVPREFIX "_WRAPPER_APPEND"));
223 fprintf(stderr, "FILTER = '%s'\n",
224 getenv(ENVPREFIX "_WRAPPER_FILTER"));
226 #endif
228 /* extract the next other wrapper */
229 other = getenv(ENVPREFIX "_WRAPPER_OTHERS");
230 other_done = getenv(ENVPREFIX "_WRAPPER_OTHERS_DONE");
232 if (other && strlen(other) > 0) {
233 other = strdup(other);
234 char *newother_done;
236 if (other_done) {
237 char *str = strstr(other, other_done);
238 if (str != other) {
239 fprintf(stderr, "OTHERS_DONE set but does not match.\n");
240 return 249;
243 other += strlen(other_done);
244 if (*other == 0) {
245 other = 0;
247 else {
248 if (*other == ':')
249 other++;
251 newother_done = (char *) malloc(strlen(getenv(ENVPREFIX "_WRAPPER_OTHERS")));
252 strcpy(newother_done, other_done);
253 strcat(newother_done, ":");
254 strcat(newother_done, other);
256 setenv(ENVPREFIX "_WRAPPER_OTHERS_DONE", newother_done, 1);
259 else {
260 other = strtok(other, ":");
261 setenv(ENVPREFIX "_WRAPPER_OTHERS_DONE", other, 1);
266 * Main work.
269 newargv = malloc( sizeof(char*) * newargc );
271 /* init newargv[], c1 and c2 */
272 c1 = c2 = 0;
273 if (other && strlen(other) > 0)
274 newargv[c1++] = other;
275 newargv[c1++] = argv[c2++];
278 * Copy options from prefix_WRAPPER_INSERT to newargv[]
281 if ( (delim=getenv(ENVPREFIX "_WRAPPER_INSERT")) != NULL ) {
282 optbuf = malloc( strlen(delim) + 1 );
283 strcpy(optbuf, delim);
285 delim = strtok(optbuf, " ");
286 while (delim != NULL) {
287 delim = eval_cond_arg (delim, argc, argv);
289 if (delim[0]) {
290 #if VERBOSE_DEBUG
291 if (debug) fprintf(stderr, "Insert: %s\n", delim);
292 #endif
293 newargv = realloc_if_needed(c1, &newargc, newargv);
294 newargv[c1++] = delim;
296 delim = strtok(NULL, " ");
302 * Copy options from argv[] to newargv[] if they are not listed
303 * in prefix_WRAPPER_REMOVE
306 for (; c2<argc; c2++) {
307 if ( (delim=getenv(ENVPREFIX "_WRAPPER_REMOVE")) != NULL ) {
308 optbuf = malloc( strlen(delim) + 1 );
309 strcpy(optbuf, delim);
311 delim = strtok(optbuf, " ");
312 while (delim != NULL) {
313 delim = eval_cond_arg (delim, argc, argv);
314 if ( delim[0] &&
315 !fnmatch(delim, argv[c2], 0) ) break;
316 delim = strtok(NULL, " ");
318 free(optbuf);
320 if (delim == NULL) {
321 #if VERBOSE_DEBUG
322 if (debug) fprintf(stderr, "Copy: %s\n", argv[c2]);
323 #endif
325 newargv = realloc_if_needed(c1, &newargc, newargv);
326 newargv[c1++] = argv[c2];
327 #if VERBOSE_DEBUG
328 } else {
329 if (debug) fprintf(stderr, "Remove: %s\n", argv[c2]);
330 #endif
336 * Copy options from prefix_WRAPPER_APPEND to newargv[]
339 if ( (delim=getenv(ENVPREFIX "_WRAPPER_APPEND")) != NULL ) {
340 optbuf = malloc( strlen(delim) + 1 );
341 strcpy(optbuf, delim);
343 delim = strtok(optbuf, " ");
344 while (delim != NULL) {
345 delim = eval_cond_arg (delim, argc, argv);
346 if (delim[0]) {
347 #if VERBOSE_DEBUG
348 if (debug) fprintf(stderr, "Append: %s\n", delim);
349 #endif
351 newargv = realloc_if_needed(c1, &newargc, newargv);
352 newargv[c1++] = delim;
354 delim = strtok(NULL, " ");
360 * Use prefix_WRAPPER_FILTER if set and not ""
362 * (Maybe we make a nice re-write of this code-block later.)
365 if ( (delim=getenv(ENVPREFIX "_WRAPPER_FILTER")) != NULL && delim[0] ) {
367 /* Open temp files. */
368 char outfn[] = "/tmp/gccfilter_out.XXXXXX";
369 char infn[] = "/tmp/gccfilter_in.XXXXXX";
370 int outfd = mkstemp(outfn);
371 int infd = mkstemp(infn);
372 int pid, status;
374 /* Create content of input file */
375 for (c3=0; c3<c1; c3++) {
376 write(infd, newargv[c3], strlen(newargv[c3]));
377 write(infd, "\n", 1);
379 lseek(infd, 0, SEEK_SET);
381 /* Run filter command with shell (sh -c xxx) */
382 #if VERBOSE_DEBUG
383 if (debug) fprintf(stderr, "Run Filter: %s\n", delim);
384 #endif
385 pid = fork();
386 if (!pid) {
387 dup2(infd, 0); close(infd);
388 dup2(outfd, 1); close(outfd);
389 execlp("sh", "sh", "-c", delim, NULL);
390 return 1;
391 } else if (pid == -1) {
392 fprintf(stderr, "Fork failed: %d: %s\n", errno, strerror(errno));
393 } else {
394 /* We don't expect any signals and have no other child processes. */
395 wait(&status);
396 if (WEXITSTATUS(status) != 0) {
397 fprintf(stderr, "Filter failed: %d\n", WEXITSTATUS(status));
401 /* Re-read parameter list. */
403 size_t argvsize = lseek(outfd, 0, SEEK_END);
404 char* argvmem = malloc (argvsize + 1); /* might not have trailing \n */
405 lseek(outfd, 0, SEEK_SET);
406 read(outfd, argvmem, argvsize);
408 if (argvsize == 0) {
409 fprintf(stderr, "Filter produced no output!\n");
410 return 248;
413 for (c1 = c2 = 0; c2 < argvsize; ++c2) {
414 newargv = realloc_if_needed(c1, &newargc, newargv);
415 newargv[c1++] = argvmem + c2;
417 /* scan for newlines, terminate, next */
418 while (c2 < argvsize && argvmem[c2] != '\n')
419 ++c2;
420 argvmem[c2] = 0;
424 /* Close and remove temp files */
425 close(outfd); unlink(outfn);
426 close(infd); unlink(infn);
430 * Run other wrappers first. They will re-start us.
433 if (other && strlen(other) > 0) {
435 #if VERBOSE_DEBUG
436 if (debug) {
437 fprintf(stderr, "Running external wrapper: %s\n", newargv[0]);
438 for (c3=0; c3<c1; c3++)
439 fprintf(stderr, " %s", newargv[c3]);
440 fprintf(stderr, "\n");
442 #endif
444 if (logfile) {
445 fprintf(logfile, "+");
446 for (c3=0; c3<c1; c3++)
447 fprintf(logfile, " %s", newargv[c3]);
448 fprintf(logfile, "\n");
449 fclose(logfile);
452 newargv[c1] = NULL;
453 execvp(newargv[0], newargv);
454 fprintf(stderr, "cmd_wrapper: execvp(%s,...) - %s\n",
455 newargv[0], strerror(errno));
457 return 253;
461 * Remove the wrapper dir from PATH
464 if ( (delim=getenv("PATH")) != NULL && delim[0] &&
465 (wrdir=getenv(ENVPREFIX "_WRAPPER_MYPATH")) != NULL &&
466 wrdir[0] ) {
467 optbuf = malloc( strlen(delim) + 1 );
468 optbuf[0] = 0;
470 #if VERBOSE_DEBUG
471 if (debug)
472 fprintf(stderr, "Old PATH: %s\n", delim);
473 #endif
475 delim = strtok(delim, ":");
476 while ( delim != NULL ) {
477 if (strcmp(delim, wrdir)) {
478 if (optbuf[0]) strcat(optbuf, ":");
479 strcat(optbuf, delim);
481 delim = strtok(NULL, ":");
483 setenv("PATH", optbuf, 1);
485 #if VERBOSE_DEBUG
486 if (debug) fprintf(stderr, "New PATH: %s\n", optbuf);
487 #endif
488 } else {
489 return 252;
493 * Detect loops
496 if ( (delim=getenv(ENVPREFIX "_WRAPPER_NOLOOP")) != NULL &&
497 delim[0] && delim[0] != '0') {
498 return 251;
500 setenv(ENVPREFIX "_WRAPPER_NOLOOP", "1", 1);
503 * Run the new command
506 #if VERBOSE_DEBUG
507 if (debug) {
508 fprintf(stderr, "New Command:");
509 for (c3=0; c3<c1; c3++) fprintf(stderr, " %s", newargv[c3]);
510 fprintf(stderr, "\n");
512 #endif
514 if (logfile) {
515 fprintf(logfile, "+");
516 for (c3=0; c3<c1; c3++) fprintf(logfile, " %s", newargv[c3]);
517 fprintf(logfile, "\n");
518 fclose(logfile);
521 newargv[c1]=NULL;
522 execvp(newargv[0], newargv);
523 fprintf(stderr, "cmd_wrapper: execvp(%s,...) - %s\n",
524 newargv[0], strerror(errno));
526 return 254;