10f66f451Sopenharmony_ci/* logwrapper.c - Record commands called out of $PATH to a log
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2019 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * I made it up. Must be built standalone to work. (Is its own multiplexer.)
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig LOGWRAPPER
100f66f451Sopenharmony_ci  bool "logwrapper"
110f66f451Sopenharmony_ci  default n
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: logwrapper ...
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    Append command line to $WRAPLOG, then call second instance
160f66f451Sopenharmony_ci    of command in $PATH.
170f66f451Sopenharmony_ci*/
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci#define FOR_logwrapper
200f66f451Sopenharmony_ci#include "toys.h"
210f66f451Sopenharmony_ci
220f66f451Sopenharmony_civoid logwrapper_main(void)
230f66f451Sopenharmony_ci{
240f66f451Sopenharmony_ci  char *log = getenv("WRAPLOG"), *omnom = basename(*toys.argv),
250f66f451Sopenharmony_ci       *s, *ss, *sss;
260f66f451Sopenharmony_ci  struct string_list *list;
270f66f451Sopenharmony_ci  int i, len;
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_ci  // Log the command line
300f66f451Sopenharmony_ci  if (!log) error_exit("no $WRAPLOG");
310f66f451Sopenharmony_ci  len = strlen(omnom)+2;
320f66f451Sopenharmony_ci  for (i = 0; i<toys.optc; i++) len += 2*strlen(toys.optargs[i])+3;
330f66f451Sopenharmony_ci  ss = stpcpy(s = xmalloc(len), omnom);
340f66f451Sopenharmony_ci
350f66f451Sopenharmony_ci  // Copy arguments surrounded by quotes with \ escapes for " \ or \n
360f66f451Sopenharmony_ci  for (i = 0; i<toys.optc; i++) {
370f66f451Sopenharmony_ci    *(ss++) = ' ';
380f66f451Sopenharmony_ci    *(ss++) = '"';
390f66f451Sopenharmony_ci    for (sss = toys.optargs[i]; *sss; sss++) {
400f66f451Sopenharmony_ci      if (-1 == (len = stridx("\n\\\"", *sss))) *(ss++) = *sss;
410f66f451Sopenharmony_ci      else {
420f66f451Sopenharmony_ci        *(ss++) = '\\';
430f66f451Sopenharmony_ci        *(ss++) = "n\\\""[len];
440f66f451Sopenharmony_ci      }
450f66f451Sopenharmony_ci    }
460f66f451Sopenharmony_ci    *(ss++) = '"';
470f66f451Sopenharmony_ci  }
480f66f451Sopenharmony_ci  *(ss++) = '\n';
490f66f451Sopenharmony_ci
500f66f451Sopenharmony_ci  // Atomically append to log and free buffer
510f66f451Sopenharmony_ci  i = xcreate(log, O_RDWR|O_CREAT|O_APPEND, 0644);
520f66f451Sopenharmony_ci  xwrite(i, s, ss-s);
530f66f451Sopenharmony_ci  close(i);
540f66f451Sopenharmony_ci  free(s);
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ci  // Run next instance in $PATH after this one. If we were called via absolute
570f66f451Sopenharmony_ci  // path search for this instance, otherwise assume we're first instance
580f66f451Sopenharmony_ci  list = find_in_path(getenv("PATH"), omnom);
590f66f451Sopenharmony_ci  if (**toys.argv == '/') {
600f66f451Sopenharmony_ci    while (list) {
610f66f451Sopenharmony_ci      if (!strcmp(list->str, *toys.argv)) break;
620f66f451Sopenharmony_ci      free(llist_pop(&list));
630f66f451Sopenharmony_ci    }
640f66f451Sopenharmony_ci  }
650f66f451Sopenharmony_ci
660f66f451Sopenharmony_ci  // Skip first instance and try to run next one, until out of instances.
670f66f451Sopenharmony_ci  for (;;) {
680f66f451Sopenharmony_ci    if (list) free(llist_pop(&list));
690f66f451Sopenharmony_ci    if (!list)
700f66f451Sopenharmony_ci      error_exit("no %s after %s in $PATH=%s", omnom,
710f66f451Sopenharmony_ci        **toys.argv == '/' ? *toys.argv : "logwrapper", getenv("PATH"));
720f66f451Sopenharmony_ci    *toys.argv = list->str;
730f66f451Sopenharmony_ci    execve(list->str, toys.argv, environ);
740f66f451Sopenharmony_ci  }
750f66f451Sopenharmony_ci}
76