2 * --- T2-COPYRIGHT-NOTE-BEGIN ---
3 * This copyright note is auto-generated by scripts/Create-CopyPatch.
5 * T2 SDE: misc/tools-source/cmd_wrapper.c
6 * Copyright (C) 2004 - 2022 The T2 SDE Project
7 * Copyright (C) 2006 - 2022 Rene Rebe <rene@exactcode.de>
8 * Copyright (C) 1998 - 2003 ROCK Linux Project
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.
52 # error You must use -DENVPREFIX=".." when compiling this tool.
56 # error You must use -DMYNAME=".." when compiling this tool.
59 #define VERBOSE_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;
69 setenv(name
, "", 0); /* no overwrite - make sure it is defined */
71 tmp2
= malloc(strlen(tmp1
)+1);
73 while ( tmp1
[pos1
] ) {
74 if ( tmp1
[pos1
] == ch
) {
80 tmp2
[pos2
++] = tmp1
[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"
92 * Nesting is currently only impelemented for rhs matches.
94 char* eval_cond_arg(char * arg
, int argc
, char ** argv
) {
96 char * lhs
= NULL
, * rhs
;
100 while (*c
&& *c
!= '?') c
++;
106 if (debug
) fprintf(stderr
, "Conditonal arg: '%s'\n", arg
);
109 /* split conditional (arg), left hand and right hand statement */
113 /* scan for ':' and 0 terminate */
114 while (*c
&& *c
!= ':') c
++;
119 if (debug
) fprintf(stderr
, "Conditonal: '%s', lhs: '%s', rhs: '%s'\n", arg
, lhs
, rhs
);
121 /* match arguments against conditional */
122 for (i
= 0; i
< argc
; ++i
) {
123 if (!fnmatch(arg
, argv
[i
], 0)) {
125 if (debug
) fprintf(stderr
, "Conditonal: '%s', matched: '%s'\n", arg
, argv
[i
]);
127 /* TODO: support nested lhs expressions, too */
132 /* support nested rhs, evaluate again */
133 return eval_cond_arg(rhs
, argc
, argv
);
136 /* newargv memory management, realloc if newargv is filled */
137 static inline char** realloc_if_needed(int c1
, int* newargc
, char** newargv
) {
138 if (c1
+ 1 >= *newargc
) { /* +1 for NULL-sentinel */
140 newargv
= realloc (newargv
, sizeof(char*) * *newargc
);
148 int main(int argc
, char ** argv
) {
149 int newargc
= 64; /* initial newargv size, anyhting >3 (name, other, NULL) */
151 char *other
, *other_done
;
153 char *delim
, *optbuf
, *wrdir
;
154 FILE *logfile
= NULL
;
156 /* Calling the wrapper with an absolute path results in an
157 endless-loop. Use basename only to force a $PATH lookup. */
158 argv
[0] = basename(argv
[0]);
159 if ( !strcmp(argv
[0], MYNAME
) ) {
162 argv
[0] = basename(argv
[0]);
168 /* Open logfile (if any) */
169 delim
= getenv(ENVPREFIX
"_WRAPPER_LOGFILE");
170 if (delim
&& delim
[0]) {
171 logfile
= fopen(delim
, "a");
174 delim
= malloc(FILENAME_MAX
);
175 if ( getcwd(delim
, FILENAME_MAX
) == NULL
) delim
[0]=0;
176 fprintf(logfile
, "\n%s:\n-", delim
);
177 for (c3
=0; c3
<argc
; c3
++) fprintf(logfile
, " %s", argv
[c3
]);
178 fprintf(logfile
, "\n");
183 * Read prefix_WRAPPER_DEBUG and prefix_WRAPPER_BYPASS
186 if ( (delim
=getenv(ENVPREFIX
"_WRAPPER_DEBUG")) != NULL
&&
187 delim
[0] ) debug
= atoi(delim
);
189 if ( (delim
=getenv(ENVPREFIX
"_WRAPPER_BYPASS")) != NULL
&&
190 delim
[0] && atoi(delim
)) {
192 if (debug
) fprintf(stderr
, "Bypassing cmd_wrapper by "
193 "clearing all config variables.\n");
195 setenv(ENVPREFIX
"_WRAPPER_OTHERS", "", 1);
196 setenv(ENVPREFIX
"_WRAPPER_INSERT", "", 1);
197 setenv(ENVPREFIX
"_WRAPPER_REMOVE", "", 1);
198 setenv(ENVPREFIX
"_WRAPPER_APPEND", "", 1);
199 setenv(ENVPREFIX
"_WRAPPER_FILTER", "", 1);
202 cleanenv(ENVPREFIX
"_WRAPPER_OTHERS", ':');
203 cleanenv(ENVPREFIX
"_WRAPPER_INSERT", ' ');
204 cleanenv(ENVPREFIX
"_WRAPPER_REMOVE", ' ');
205 cleanenv(ENVPREFIX
"_WRAPPER_APPEND", ' ');
206 cleanenv(ENVPREFIX
"_WRAPPER_FILTER", '|');
210 fprintf(stderr
, "Old Command:");
211 for (c3
=0; c3
<argc
; c3
++) fprintf(stderr
, " %s", argv
[c3
]);
212 fprintf(stderr
, "\n");
213 fprintf(stderr
, "ENVPREFIX = '%s'\n", ENVPREFIX
);
214 fprintf(stderr
, "OTHERS = '%s'\n",
215 getenv(ENVPREFIX
"_WRAPPER_OTHERS"));
216 if (getenv(ENVPREFIX
"_WRAPPER_OTHERS_DONE"))
217 fprintf(stderr
, "OTHERS DONE = '%s'\n",
218 getenv(ENVPREFIX
"_WRAPPER_OTHERS_DONE"));
219 fprintf(stderr
, "INSERT = '%s'\n",
220 getenv(ENVPREFIX
"_WRAPPER_INSERT"));
221 fprintf(stderr
, "REMOVE = '%s'\n",
222 getenv(ENVPREFIX
"_WRAPPER_REMOVE"));
223 fprintf(stderr
, "APPEND = '%s'\n",
224 getenv(ENVPREFIX
"_WRAPPER_APPEND"));
225 fprintf(stderr
, "FILTER = '%s'\n",
226 getenv(ENVPREFIX
"_WRAPPER_FILTER"));
230 /* extract the next other wrapper */
231 other
= getenv(ENVPREFIX
"_WRAPPER_OTHERS");
232 other_done
= getenv(ENVPREFIX
"_WRAPPER_OTHERS_DONE");
234 if (other
&& strlen(other
) > 0) {
235 other
= strdup(other
);
239 char *str
= strstr(other
, other_done
);
241 fprintf(stderr
, "OTHERS_DONE set but does not match.\n");
245 other
+= strlen(other_done
);
253 newother_done
= (char *) malloc(strlen(getenv(ENVPREFIX
"_WRAPPER_OTHERS")));
254 strcpy(newother_done
, other_done
);
255 strcat(newother_done
, ":");
256 strcat(newother_done
, other
);
258 setenv(ENVPREFIX
"_WRAPPER_OTHERS_DONE", newother_done
, 1);
262 other
= strtok(other
, ":");
263 setenv(ENVPREFIX
"_WRAPPER_OTHERS_DONE", other
, 1);
271 newargv
= malloc( sizeof(char*) * newargc
);
273 /* init newargv[], c1 and c2 */
275 if (other
&& strlen(other
) > 0)
276 newargv
[c1
++] = other
;
277 newargv
[c1
++] = argv
[c2
++];
280 * Copy options from prefix_WRAPPER_INSERT to newargv[]
282 if ( (delim
=getenv(ENVPREFIX
"_WRAPPER_INSERT")) != NULL
) {
283 optbuf
= malloc( strlen(delim
) + 1 );
284 strcpy(optbuf
, delim
);
286 /* TODO: handle escapes to support cond. lhs nesting */
287 delim
= strtok(optbuf
, " ");
288 while (delim
!= NULL
) {
289 delim
= eval_cond_arg(delim
, argc
, argv
);
293 if (debug
) fprintf(stderr
, "Insert: %s\n", delim
);
295 newargv
= realloc_if_needed(c1
, &newargc
, newargv
);
296 newargv
[c1
++] = delim
;
298 delim
= strtok(NULL
, " ");
304 * Copy options from argv[] to newargv[] if they are not listed
305 * in prefix_WRAPPER_REMOVE
307 for (; c2
<argc
; c2
++) {
308 if ( (delim
=getenv(ENVPREFIX
"_WRAPPER_REMOVE")) != NULL
) {
309 optbuf
= malloc( strlen(delim
) + 1 );
310 strcpy(optbuf
, delim
);
312 /* TODO: handle escapes to support cond. lhs nesting */
313 delim
= strtok(optbuf
, " ");
314 while (delim
!= NULL
) {
315 delim
= eval_cond_arg(delim
, argc
, argv
);
317 !fnmatch(delim
, argv
[c2
], 0)) break;
318 delim
= strtok(NULL
, " ");
324 if (debug
) fprintf(stderr
, "Copy: %s\n", argv
[c2
]);
327 newargv
= realloc_if_needed(c1
, &newargc
, newargv
);
328 newargv
[c1
++] = argv
[c2
];
331 if (debug
) fprintf(stderr
, "Remove: %s\n", argv
[c2
]);
338 * Copy options from prefix_WRAPPER_APPEND to newargv[]
340 if ( (delim
=getenv(ENVPREFIX
"_WRAPPER_APPEND")) != NULL
) {
341 optbuf
= malloc( strlen(delim
) + 1 );
342 strcpy(optbuf
, delim
);
344 /* TODO: handle escapes to support cond. lhs nesting */
345 delim
= strtok(optbuf
, " ");
346 while (delim
!= NULL
) {
347 delim
= eval_cond_arg(delim
, argc
, argv
);
350 if (debug
) fprintf(stderr
, "Append: %s\n", delim
);
353 newargv
= realloc_if_needed(c1
, &newargc
, newargv
);
354 newargv
[c1
++] = delim
;
356 delim
= strtok(NULL
, " ");
362 * Use prefix_WRAPPER_FILTER if set and not ""
364 * (Maybe we make a nice re-write of this code-block later.)
366 if ( (delim
=getenv(ENVPREFIX
"_WRAPPER_FILTER")) != NULL
&& delim
[0] ) {
368 /* Open temp files. */
369 char outfn
[] = "/tmp/gccfilter_out.XXXXXX";
370 char infn
[] = "/tmp/gccfilter_in.XXXXXX";
371 int outfd
= mkstemp(outfn
);
372 int infd
= mkstemp(infn
);
375 /* Create content of input file */
376 for (c3
=0; c3
<c1
; c3
++) {
377 write(infd
, newargv
[c3
], strlen(newargv
[c3
]));
378 write(infd
, "\n", 1);
380 lseek(infd
, 0, SEEK_SET
);
382 /* Run filter command with shell (sh -c xxx) */
384 if (debug
) fprintf(stderr
, "Run Filter: %s\n", delim
);
388 dup2(infd
, 0); close(infd
);
389 dup2(outfd
, 1); close(outfd
);
390 execlp("sh", "sh", "-c", delim
, NULL
);
392 } else if (pid
== -1) {
393 fprintf(stderr
, "Fork failed: %d: %s\n", errno
, strerror(errno
));
395 /* We don't expect any signals and have no other child processes. */
397 if (WEXITSTATUS(status
) != 0) {
398 fprintf(stderr
, "Filter failed: %d\n", WEXITSTATUS(status
));
402 /* Re-read parameter list. */
404 size_t argvsize
= lseek(outfd
, 0, SEEK_END
);
405 char* argvmem
= malloc (argvsize
+ 1); /* might not have trailing \n */
406 lseek(outfd
, 0, SEEK_SET
);
407 read(outfd
, argvmem
, argvsize
);
410 fprintf(stderr
, "Filter produced no output!\n");
414 for (c1
= c2
= 0; c2
< argvsize
; ++c2
) {
415 newargv
= realloc_if_needed(c1
, &newargc
, newargv
);
416 newargv
[c1
++] = argvmem
+ c2
;
418 /* scan for newlines, terminate, next */
419 while (c2
< argvsize
&& argvmem
[c2
] != '\n')
425 /* Close and remove temp files */
426 close(outfd
); unlink(outfn
);
427 close(infd
); unlink(infn
);
431 * Run other wrappers first. They will re-start us.
433 if (other
&& strlen(other
) > 0) {
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");
445 fprintf(logfile
, "+");
446 for (c3
=0; c3
<c1
; c3
++)
447 fprintf(logfile
, " %s", newargv
[c3
]);
448 fprintf(logfile
, "\n");
453 execvp(newargv
[0], newargv
);
454 fprintf(stderr
, "cmd_wrapper: execvp(%s,...) - %s\n",
455 newargv
[0], strerror(errno
));
461 * Remove the wrapper dir from PATH
463 if ( (delim
=getenv("PATH")) != NULL
&& delim
[0] &&
464 (wrdir
=getenv(ENVPREFIX
"_WRAPPER_MYPATH")) != NULL
&&
466 optbuf
= malloc( strlen(delim
) + 1 );
471 fprintf(stderr
, "Old PATH: %s\n", delim
);
474 delim
= strtok(delim
, ":");
475 while ( delim
!= NULL
) {
476 if (strcmp(delim
, wrdir
)) {
477 if (optbuf
[0]) strcat(optbuf
, ":");
478 strcat(optbuf
, delim
);
480 delim
= strtok(NULL
, ":");
482 setenv("PATH", optbuf
, 1);
485 if (debug
) fprintf(stderr
, "New PATH: %s\n", optbuf
);
495 if ( (delim
=getenv(ENVPREFIX
"_WRAPPER_NOLOOP")) != NULL
&&
496 delim
[0] && delim
[0] != '0') {
499 setenv(ENVPREFIX
"_WRAPPER_NOLOOP", "1", 1);
502 * Run the new command
507 fprintf(stderr
, "New Command:");
508 for (c3
=0; c3
<c1
; c3
++) fprintf(stderr
, " %s", newargv
[c3
]);
509 fprintf(stderr
, "\n");
514 fprintf(logfile
, "+");
515 for (c3
=0; c3
<c1
; c3
++) fprintf(logfile
, " %s", newargv
[c3
]);
516 fprintf(logfile
, "\n");
521 execvp(newargv
[0], newargv
);
522 fprintf(stderr
, "cmd_wrapper: execvp(%s,...) - %s\n",
523 newargv
[0], strerror(errno
));