1/* logwrapper.c - Record commands called out of $PATH to a log
2 *
3 * Copyright 2019 Rob Landley <rob@landley.net>
4 *
5 * I made it up. Must be built standalone to work. (Is its own multiplexer.)
6
7USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN))
8
9config LOGWRAPPER
10  bool "logwrapper"
11  default n
12  help
13    usage: logwrapper ...
14
15    Append command line to $WRAPLOG, then call second instance
16    of command in $PATH.
17*/
18
19#define FOR_logwrapper
20#include "toys.h"
21
22void logwrapper_main(void)
23{
24  char *log = getenv("WRAPLOG"), *omnom = basename(*toys.argv),
25       *s, *ss, *sss;
26  struct string_list *list;
27  int i, len;
28
29  // Log the command line
30  if (!log) error_exit("no $WRAPLOG");
31  len = strlen(omnom)+2;
32  for (i = 0; i<toys.optc; i++) len += 2*strlen(toys.optargs[i])+3;
33  ss = stpcpy(s = xmalloc(len), omnom);
34
35  // Copy arguments surrounded by quotes with \ escapes for " \ or \n
36  for (i = 0; i<toys.optc; i++) {
37    *(ss++) = ' ';
38    *(ss++) = '"';
39    for (sss = toys.optargs[i]; *sss; sss++) {
40      if (-1 == (len = stridx("\n\\\"", *sss))) *(ss++) = *sss;
41      else {
42        *(ss++) = '\\';
43        *(ss++) = "n\\\""[len];
44      }
45    }
46    *(ss++) = '"';
47  }
48  *(ss++) = '\n';
49
50  // Atomically append to log and free buffer
51  i = xcreate(log, O_RDWR|O_CREAT|O_APPEND, 0644);
52  xwrite(i, s, ss-s);
53  close(i);
54  free(s);
55
56  // Run next instance in $PATH after this one. If we were called via absolute
57  // path search for this instance, otherwise assume we're first instance
58  list = find_in_path(getenv("PATH"), omnom);
59  if (**toys.argv == '/') {
60    while (list) {
61      if (!strcmp(list->str, *toys.argv)) break;
62      free(llist_pop(&list));
63    }
64  }
65
66  // Skip first instance and try to run next one, until out of instances.
67  for (;;) {
68    if (list) free(llist_pop(&list));
69    if (!list)
70      error_exit("no %s after %s in $PATH=%s", omnom,
71        **toys.argv == '/' ? *toys.argv : "logwrapper", getenv("PATH"));
72    *toys.argv = list->str;
73    execve(list->str, toys.argv, environ);
74  }
75}
76