* updated fastfetch (2.11.0 -> 2.18.1)
[t2sde.git] / misc / tools-source / fl_wrapper.c.sh
blob6ad754a3473902e44a6b14fcb7f49361f9044f25
1 #!/usr/bin/env bash
3 # This shell-script genereates the fl_wrapper.c source file.
5 cat << EOT
6 /* Wrapper for getting a list of created files.
8 * --- T2-COPYRIGHT-NOTE-BEGIN ---
9 * T2 SDE: misc/tools-source/fl_wrapper.c.sh
10 * Copyright (C) 2006 - 2023 René Rebe <rene@exactcode.de>
11 * Copyright (C) 2004 - 2024 The T2 SDE Project
12 * Copyright (C) 1998 - 2003 ROCK Linux Project
14 * This Copyright note is generated by scripts/Create-CopyPatch,
15 * more information can be found in the files COPYING and README.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License version 2.
19 * --- T2-COPYRIGHT-NOTE-END ---
21 * gcc -Wall -O2 -ldl -shared -o fl_wrapper.so fl_wrapper.c
23 * !!! THIS FILE IS AUTO-GENERATED BY $0 !!!
25 * ELF Dynamic Loading Documentation:
26 * - http://www.linuxdoc.org/HOWTO/GCC-HOWTO-7.html
27 * - http://www.educ.umu.se/~bjorn/mhonarc-files/linux-gcc/msg00576.html
28 * - /usr/include/dlfcn.h
32 /* Headers and prototypes */
34 #define DEBUG 0
35 #define DLOPEN_LIBC 1
36 #ifndef FLWRAPPER_LIBC
37 # define FLWRAPPER_LIBC "libc.so.6"
38 #endif
40 #define _GNU_SOURCE
41 #define _REENTRANT
43 #define open xxx_open
44 #define open64 xxx_open64
45 #define openat xxx_openat
46 #define openat64 xxx_openat64
47 #define mkfifo xxx_mkfifo
48 #define mknod xxx_mknod
49 #define __xmknod xxx___xmknod
51 #define _LARGEFILE_SOURCE
52 #define _LARGEFILE64_SOURCE
54 #include <dlfcn.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <sys/file.h>
61 #include <sys/stat.h>
62 #include <sys/types.h>
63 #include <unistd.h>
64 #include <utime.h>
65 #include <stdarg.h>
66 #include <limits.h>
67 /* somehow it can happen that PATH_MAX does not get defined...? -- jsaw */
68 #ifndef PATH_MAX
69 #include <linux/limits.h>
70 #endif
71 #ifndef PATH_MAX
72 #warning "PATH_MAX was not defined - BUG in your system headers?"
73 #define PATH_MAX 4095
74 #endif
75 #include <libgen.h>
77 #undef _LARGEFILE64_SOURCE
78 #undef _LARGEFILE_SOURCE
80 #undef __xmknod
81 #undef mknod
82 #undef mkfifo
83 #undef open
84 #undef open64
85 #undef openat
86 #undef openat64
87 #undef creat64
88 #undef fopen64
90 static void * get_dl_symbol(char *);
92 struct status_t {
93 ino_t inode;
94 off_t size;
95 time_t mtime;
96 time_t ctime;
99 static int initialized = 0;
101 #ifdef FLWRAPPER_BASEDIR
102 static void check_write_access(const char * , const char * );
103 #endif
104 static void handle_file_access_before(const char *, const char *, struct status_t *);
105 static void handle_file_access_after(const char *, const char *, struct status_t *);
107 char *cmdname = "unkown";
108 char wlog[PATH_MAX], rlog[PATH_MAX], filterdir[PATH_MAX], atpath[PATH_MAX];
110 const char* getatpath(int dirfd, const char* pathname) {
111 int ret;
112 char selffd[64];
114 ret = snprintf(selffd, sizeof(selffd), "/proc/self/fd/%d", dirfd);
115 /* TODO: short buffer handling */
116 ret = readlink(selffd, atpath, sizeof(atpath) - 1);
117 if (ret < 0) {
118 fprintf(stderr, "Failed to get atpath: %d, %s\n", dirfd, pathname);
119 return pathname;
121 atpath[ret] = 0;
123 strncat(atpath, "/", sizeof(atpath) - 1);
124 strncat(atpath, pathname, sizeof(atpath) - 1);
125 /* TODO: short buffer handling */
127 return atpath;
130 /* Wrapper Functions */
133 # This has been made with cpp-macros before until they turned to be absolutely
134 # unreadable ...
136 add_wrapper()
138 line="$( echo "$*" | sed 's/ *, */,/g' )"
139 old_ifs="$IFS" ; IFS="," ; set $line ; IFS="$old_ifs"
141 ret_type=$1 ; shift ; function=$1 ; shift
142 p1="" ; p2="" ; for x ; do p1="$p1$x, " ; done
143 for x ; do x="${x%%\[*\]}" ; p2="$p2${x##* }, " ; done
144 p1="${p1%, }" ; p2="${p2%, }"
146 # sanity check all new-style *at dirfd
147 atcheck=""
149 for x; do
150 if [[ "$x" = *atfd ]]; then
151 atcheck="${atcheck}if (${x##* } != AT_FDCWD) {f2 = getatpath(${x##* }, f);}"
153 done
155 case ${function} in
156 open*)
157 # remove varg from $p2
158 p2=${p2%, ...}
159 echo ; cat << EOT
160 extern $ret_type $function($p1);
161 $ret_type (*orig_$function)($p1) = 0;
163 $ret_type $function($p1)
165 struct status_t status;
166 const char* f2 = f;
167 int old_errno=errno;
168 $ret_type rc;
169 mode_t b = 0;
171 #ifdef AT_FDCWD
172 $atcheck
173 #endif
175 #ifdef FLWRAPPER_BASEDIR
176 if (a & (O_WRONLY|O_CREAT|O_APPEND))
177 check_write_access("$function", f2);
178 #endif
180 handle_file_access_before("$function", f2, &status);
181 if (!orig_$function) orig_$function = get_dl_symbol("$function");
182 errno=old_errno;
184 #if DEBUG == 1
185 fprintf(stderr, "fl_wrapper.so debug [%d]: going to run original $function() at %p (wrapper at %p).\n",
186 getpid(), orig_$function, $function);
187 #endif
189 if (a & O_CREAT) {
190 va_list ap;
192 va_start(ap, a);
193 b = va_arg(ap, mode_t);
194 va_end(ap);
196 rc = orig_$function($p2, b);
198 else
199 rc = orig_$function($p2);
201 old_errno=errno;
202 handle_file_access_after("$function", f2, &status);
203 errno=old_errno;
205 return rc;
209 exec*)
210 echo ; cat << EOT
211 extern $ret_type $function($p1);
212 $ret_type (*orig_$function)($p1) = 0;
214 $ret_type $function($p1)
216 int old_errno=errno;
218 handle_file_access_after("$function", f, 0);
219 if (!orig_$function) orig_$function = get_dl_symbol("$function");
220 errno=old_errno;
222 return orig_$function($p2);
227 echo ; cat << EOT
228 extern $ret_type $function($p1);
229 $ret_type (*orig_$function)($p1) = 0;
231 $ret_type $function($p1)
233 struct status_t status;
234 const char* f2 = f;
235 int old_errno=errno;
236 $ret_type rc;
238 #ifdef AT_FDCWD
239 $atcheck
240 #endif
242 handle_file_access_before("$function", f2, &status);
243 if (!orig_$function) orig_$function = get_dl_symbol("$function");
244 errno=old_errno;
246 #if DEBUG == 1
247 fprintf(stderr, "fl_wrapper.so debug [%d]: going to run original $function() at %p (wrapper at %p).\n",
248 getpid(), orig_$function, $function);
249 #endif
251 rc = orig_$function($p2);
253 old_errno=errno;
254 handle_file_access_after("$function", f2, &status);
255 errno=old_errno;
257 return rc;
261 esac
264 add_wrapper 'int, open, const char* f, int a, ...'
265 add_wrapper 'int, open64, const char* f, int a, ...'
266 add_wrapper 'int, openat, int atfd, const char* f, int a, ...'
267 add_wrapper 'int, openat64,int atfd, const char* f, int a, ...'
269 add_wrapper 'FILE*, fopen, const char* f, const char* g'
270 add_wrapper 'FILE*, fopen64, const char* f, const char* g'
272 add_wrapper 'int, creat, const char* f, mode_t m'
273 add_wrapper 'int, creat64, const char* f, mode_t m'
275 add_wrapper 'int, mkdir, const char* f, mode_t m'
276 add_wrapper 'int, mkfifo, const char* f, mode_t m'
277 add_wrapper 'int, mknod, const char* f, mode_t m, dev_t d'
278 add_wrapper 'int, __xmknod, int ver, const char* f, mode_t m, dev_t* d'
280 add_wrapper 'int, link, const char* s, const char* f'
281 add_wrapper 'int, linkat, int atfd, const char* s, int atfd2, const char* f, int flags'
283 add_wrapper 'int, symlink, const char* s, const char* f'
284 add_wrapper 'int, symlinkat, const char* s, int atfd, const char* f'
285 add_wrapper 'int, rename, const char* s, const char* f'
286 add_wrapper 'int, renameat, int atfds, const char* s, int atfd, const char* f'
287 add_wrapper 'int, renameat2, int atfds, const char* s, int atfd, const char* f, unsigned int flags'
289 add_wrapper 'int, utime, const char* f, const struct utimbuf* t'
290 add_wrapper 'int, utimes, const char* f, struct timeval* t'
291 add_wrapper 'int, utimensat, int atfd, const char* f, const struct timespec t[2], int flags'
293 add_wrapper 'int, execv, const char* f, char* const a[]'
294 add_wrapper 'int, execve, const char* f, char* const a[], char* const e[]'
296 echo
297 cat misc/tools-source/fl_wrapper_execl.c
299 echo ; cat << "EOT"
300 /* Internal Functions */
302 static void * get_dl_symbol(char * symname)
304 void * rc;
305 #if DLOPEN_LIBC
306 static void * libc_handle = 0;
308 if (!libc_handle) libc_handle=dlopen(FLWRAPPER_LIBC, RTLD_LAZY);
309 if (!libc_handle) {
310 fprintf(stderr, "fl_wrapper.so: Can't dlopen libc: %s\n", dlerror()); fflush(stderr);
311 abort();
314 rc = dlsym(libc_handle, symname);
315 # if DEBUG == 1
316 fprintf(stderr, "fl_wrapper.so debug [%d]: Symbol '%s' in libc (%p) has been resolved to %p.\n",
317 getpid(), symname, libc_handle, rc);
318 # endif
319 #else
320 rc = dlsym(RTLD_NEXT, symname);
321 # if DEBUG == 1
322 fprintf(stderr, "fl_wrapper.so debug [%d]: Symbol '%s' (RTLD_NEXT) has been resolved to %p.\n",
323 getpid(), symname, rc);
324 # endif
325 #endif
326 if (!rc) {
327 fprintf(stderr, "fl_wrapper.so: Can't resolve %s: %s\n",
328 symname, dlerror()); fflush(stderr);
329 abort();
332 return rc;
335 static pid_t pid2ppid(pid_t pid)
337 char buffer[100];
338 int fd, rc;
339 pid_t ppid = 0;
341 snprintf(buffer, sizeof(buffer), "/proc/%d/stat", pid);
342 if ( (fd = open(buffer, O_RDONLY, 0)) < 0 ) return 0;
343 if ( (rc = read(fd, buffer, sizeof(buffer))) > 0) {
344 buffer[--rc] = 0;
345 /* format: 27910 (bash) S 27315 ... */
346 sscanf(buffer, "%*[^ ] %*[^ ] %*[^ ] %d", &ppid);
348 close(fd);
350 return ppid;
353 /* this is only called from fl_wrapper_init(). so it doesn't need to be
354 * reentrant. */
355 static char *getpname(int pid)
357 static char p[512];
358 char buffer[100];
359 char *arg=0, *b;
360 int i, fd, rc;
362 snprintf(buffer, sizeof(buffer), "/proc/%d/cmdline", pid);
363 if ( (fd = open(buffer, O_RDONLY, 0)) < 0 ) return "unkown";
364 if ( (rc = read(fd, buffer, sizeof(buffer))) > 0) {
365 buffer[rc - 1] = 0;
366 for (i=0; i<rc; i++)
367 if (!buffer[i]) { arg = buffer+i+1; break; }
369 close(fd);
371 b = basename(buffer);
372 snprintf(p, sizeof(p), "%s", b);
374 if ( !strcmp(b, "bash") || !strcmp(b, "sh") || !strcmp(b, "perl") )
375 if (arg && *arg && *arg != '-')
376 snprintf(p, sizeof(p), "%s(%s)", b, basename(arg));
378 return p;
381 /* invert the order by recursion. there will be only one recursion tree
382 * so we can use a static var for managing the last ent */
383 static void addptree(int *txtpos, char *cmdtxt, int pid, int basepid)
385 static char l[512] = "";
386 char *p;
388 if (!pid || pid == basepid) return;
390 addptree(txtpos, cmdtxt, pid2ppid(pid), basepid);
392 p = getpname(pid);
394 if ( strcmp(l, p) )
395 *txtpos += snprintf(cmdtxt+*txtpos, 4096-*txtpos, "%s%s",
396 *txtpos ? "." : "", getpname(pid));
397 else
398 *txtpos += snprintf(cmdtxt+*txtpos, 4096-*txtpos, "*");
400 strcpy(l, p);
403 void copy_getenv (char* s, const char* name, int maxlen, int term)
405 char* v = getenv(name);
406 if (v) {
407 if (term) --maxlen;
408 char* send = s + maxlen;
410 s = stpncpy(s, v, maxlen);
411 if (s >= send) {
412 fprintf(stderr, "fl_wrapper.so: env string too large: %s\n", v); fflush(stderr);
413 abort();
416 if (term) s[1] = 0;
417 } else {
418 s[0] = 0;
422 void __attribute__ ((constructor)) fl_wrapper_init()
424 char cmdtxt[4096] = "";
425 char *basepid_txt = getenv("FLWRAPPER_BASEPID");
426 char *tfilterdir;
427 int basepid = 0, txtpos=0;
429 if (basepid_txt)
430 basepid = atoi(basepid_txt);
432 addptree(&txtpos, cmdtxt, getpid(), basepid);
433 cmdname = strdup(cmdtxt);
435 /* we copy the vars, so evil code can not unset them ... e.g.
436 the perl/spamassassin build ... -ReneR */
437 copy_getenv(wlog, "FLWRAPPER_WLOG", sizeof(wlog), 0);
438 copy_getenv(rlog, "FLWRAPPER_RLOG", sizeof(rlog), 0);
439 copy_getenv(filterdir, "FLWRAPPER_FILTERDIR", sizeof(filterdir), 1);
441 /* split filterdir strings once at startup to 0 terminated list */
442 for (tfilterdir = strtok(filterdir, ":"); tfilterdir; tfilterdir = strtok(NULL, ":") ) {
445 initialized = 1;
448 #ifdef FLWRAPPER_BASEDIR
449 static void check_write_access(const char * func, const char * file)
451 if (*file == '/') { /* do only check rooted paths */
452 while (file[1] == '/') file++;
454 if (!strcmp(file, "/dev/null") ||
455 !strncmp(file, "/dev/fd/", 8) ||
456 !strncmp(file, "/tmp", 4)) {
458 else if (strncmp(file, FLWRAPPER_BASEDIR, sizeof(FLWRAPPER_BASEDIR)-1)) {
459 fprintf(stderr, "fl_wrapper.so: write outside basedir (%s): %s\n", FLWRAPPER_BASEDIR, file);
460 fflush(stderr);
461 abort();
465 #endif
467 static void handle_file_access_before(const char * func, const char * file,
468 struct status_t * status)
470 struct stat st;
472 if (!initialized) return;
473 #if DEBUG == 1
474 fprintf(stderr, "fl_wrapper.so debug [%d]: begin of handle_file_access_before(\"%s\", \"%s\", ...)\n",
475 getpid(), func, file);
476 #endif
477 if ( lstat(file,&st) ) {
478 status->inode=0; status->size=0;
479 status->mtime=0; status->ctime=0;
480 } else {
481 status->inode=st.st_ino; status->size=st.st_size;
482 status->mtime=st.st_mtime; status->ctime=st.st_ctime;
484 #if DEBUG == 1
485 fprintf(stderr, "fl_wrapper.so debug [%d]: end of handle_file_access_before(\"%s\", \"%s\", ...)\n",
486 getpid(), func, file);
487 #endif
490 /* sort of, private realpath, mostly not readlink() */
491 static void sort_of_realpath (const char *file, char absfile[PATH_MAX])
493 /* make sure the filename is absolute */
494 if (file[0] != '/') {
495 char cwd[PATH_MAX];
496 getcwd(cwd, PATH_MAX);
497 snprintf(absfile, PATH_MAX, "%s/%s", cwd, file);
498 file = absfile;
501 const char* src = file; char* dst = absfile;
502 /* till the end, remove ./ and ../ parts */
503 while (dst < absfile + PATH_MAX && *src) {
504 if (*src == '/' && src[1] == '/')
505 while (src[1] == '/') src++;
506 else if (*src == '.') {
507 if (src[1] == '.' && (src[2] == '/' || src[2] == 0)) {
508 if (dst > absfile+1) --dst; /* jump to last '/' */
509 while (dst > absfile+1 && dst[-1] != '/')
510 --dst;
511 src += 2; if (*src) src++;
512 while (*src == '/') src++;
513 continue;
515 else if (src[1] == '/' || src[1] == 0) {
516 src += 1; if (*src) src++;
517 while (*src == '/') src++;
518 continue;
521 *dst++ = *src++;
523 *dst = 0;
524 /* remove trailing slashes */
525 while (--dst, dst > absfile+1 && *dst == '/')
526 *dst = 0;
529 static void handle_file_access_after(const char * func, const char * file,
530 struct status_t * status)
532 char buf[PATH_MAX], *logfile;
533 char absfile [PATH_MAX];
534 char *tfilterdir;
535 int fd, len; struct stat st;
537 if (!initialized) return;
538 #if DEBUG == 1
539 fprintf(stderr, "fl_wrapper.so debug [%d]: begin of handle_file_access_after(\"%s\", \"%s\", ...)\n",
540 getpid(), func, file);
541 #endif
542 if ( !strcmp(file, wlog) ) return;
543 if ( !strcmp(file, rlog) ) return;
544 if ( lstat(file, &st) ) return;
546 if ( (status != 0) && (status->inode != st.st_ino ||
547 status->size != st.st_size || status->mtime != st.st_mtime ||
548 status->ctime != st.st_ctime) ) { logfile = wlog; }
549 else { logfile = rlog; }
550 if ( logfile == 0 ) return;
552 /* make sure the filename is "canonical" */
553 sort_of_realpath (file, absfile);
555 /* We ignore access inside the collon seperated directory list
556 $FLWRAPPER_BASE, to keep the log smaller and reduce post
557 processing time. -ReneR */
558 for (tfilterdir = filterdir; (len = strlen(tfilterdir)) > 0; tfilterdir += len + 1) {
559 if (!strncmp(absfile, tfilterdir, len)) {
560 #if DEBUG == 1
561 fprintf(stderr,
562 "fl_wrapper.so debug [%d]: \"%s\" dropped due to filterdir \"%s\"\n",
563 getpid(), absfile, tfilterdir);
564 #endif
565 return;
569 #ifdef __USE_LARGEFILE
570 fd=open64(logfile,O_APPEND|O_WRONLY|O_LARGEFILE,0);
571 #else
572 #warning "The wrapper library may not work properly for large logs!"
573 fd=open(logfile,O_APPEND|O_WRONLY,0);
574 #endif
575 if (fd == -1) return;
577 snprintf(buf,sizeof(buf), "%s.%s:\t%s\n", cmdname, func, absfile);
578 write(fd,buf,strlen(buf));
580 close(fd);
581 #if DEBUG == 1
582 fprintf(stderr, "fl_wrapper.so debug [%d]: end of handle_file_access_after(\"%s\", \"%s\", ...)\n",
583 getpid(), func, file);
584 #endif