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