aboutsummaryrefslogtreecommitdiff
path: root/toys/example/logwrapper.c
blob: 0f87df245b802120f626c693e9a53445fea8dd8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/* logwrapper.c - Record commands called out of $PATH to a log
 *
 * Copyright 2019 Rob Landley <rob@landley.net>
 *
 * I made it up. Must be built standalone to work. (Is its own multiplexer.)

USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN))

config LOGWRAPPER
  bool "logwrapper"
  default n
  help
    usage: logwrapper ...

    Append command line to $WRAPLOG, then call second instance
    of command in $PATH.
*/

#define FOR_logwrapper
#include "toys.h"

void logwrapper_main(void)
{
  char *log = getenv("WRAPLOG"), *omnom = basename(*toys.argv),
       *s, *ss, *sss;
  struct string_list *list;
  int i, len;

  // Log the command line
  if (!log) error_exit("no $WRAPLOG");
  len = strlen(omnom)+2;
  for (i = 1; i<toys.optc; i++) len += 2*strlen(toys.argv[i])+3;
  ss = stpcpy(s = xmalloc(len), omnom);

  // Copy arguments surrounded by quotes with \ escapes for " \ or \n
  for (i = 0; i<toys.optc; i++) {
    *(ss++) = ' ';
    *(ss++) = '"';
    for (sss = toys.optargs[i]; *sss; sss++) {
      if (-1 == (len = stridx("\n\\\"", *sss))) *(ss++) = *sss;
      else {
        *(ss++) = '\\';
        *(ss++) = "n\\\""[len];
      }
    }
    *(ss++) = '"';
  }
  *(ss++) = '\n';

  // Atomically append to log and free buffer
  i = xcreate(log, O_RDWR|O_CREAT|O_APPEND, 0644);
  xwrite(i, s, ss-s);
  close(i);
  free(s);

  // Run next instance in $PATH after this one. If we were called via absolute
  // path search for this instance, otherwise assume we're first instance
  list = find_in_path(getenv("PATH"), omnom);
  if (**toys.argv == '/') {
    if (!readlink0("/proc/self/exe", s = toybuf, sizeof(toybuf)))
      perror_exit("/proc/self/exe");

    while (list) {
      if (!strcmp(list->str, s)) break;
      free(llist_pop(&list));
    }
  }

  // Skip first instance and try to run next one, until out of instances.
  for (;;) {
    if (list) free(llist_pop(&list));
    if (!list) error_exit("no %s after logwrapper in $PATH", omnom);
    *toys.argv = list->str;
    execve(list->str, toys.argv, environ);
  }
}