Dash:
[t2-trunk.git] / misc / tools-source / fl_wrapper.c.sh
blobbd31c15951afdeefaedf366ba6b0d754ed9b5d81
1 #!/bin/bash
3 # This shell-script genereates the fl_wrapper.c source file.
5 cat << EOT
6 /* ROCK Linux Wrapper for getting a list of created files
8 * --- T2-COPYRIGHT-NOTE-BEGIN ---
9 * This copyright note is auto-generated by scripts/Create-CopyPatch.
11 * T2 SDE: misc/tools-source/fl_wrapper.c.sh
12 * Copyright (C) 2006 - 2020 René Rebe <rene@exactcode.de>
13 * Copyright (C) 2004 - 2021 The T2 SDE Project
14 * Copyright (C) 1998 - 2003 ROCK Linux Project
16 * More information can be found in the files COPYING and README.
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; version 2 of the License. A copy of the
21 * GNU General Public License can be found in the file COPYING.
22 * --- T2-COPYRIGHT-NOTE-END ---
24 * gcc -Wall -O2 -ldl -shared -o fl_wrapper.so fl_wrapper.c
26 * !!! THIS FILE IS AUTO-GENERATED BY $0 !!!
28 * ELF Dynamic Loading Documentation:
29 * - http://www.linuxdoc.org/HOWTO/GCC-HOWTO-7.html
30 * - http://www.educ.umu.se/~bjorn/mhonarc-files/linux-gcc/msg00576.html
31 * - /usr/include/dlfcn.h
35 /* Headers and prototypes */
37 #define DEBUG 0
38 #define DLOPEN_LIBC 1
39 #ifndef FLWRAPPER_LIBC
40 # define FLWRAPPER_LIBC "libc.so.6"
41 #endif
43 #define _GNU_SOURCE
44 #define _REENTRANT
46 #define open xxx_open
47 #define open64 xxx_open64
48 #define openat xxx_openat
49 #define openat64 xxx_openat64
50 #define mkfifo xxx_mkfifo
51 #define mknod xxx_mknod
52 #define __xmknod xxx___xmknod
54 #define _LARGEFILE_SOURCE
55 #define _LARGEFILE64_SOURCE
57 #include <dlfcn.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <sys/file.h>
64 #include <sys/stat.h>
65 #include <sys/types.h>
66 #include <unistd.h>
67 #include <utime.h>
68 #include <stdarg.h>
69 #include <limits.h>
70 /* somehow it can happen that PATH_MAX does not get defined...? -- jsaw */
71 #ifndef PATH_MAX
72 #include <linux/limits.h>
73 #endif
74 #ifndef PATH_MAX
75 #warning "PATH_MAX was not defined - BUG in your system headers?"
76 #define PATH_MAX 4095
77 #endif
78 #include <libgen.h>
80 #undef _LARGEFILE64_SOURCE
81 #undef _LARGEFILE_SOURCE
83 #undef __xmknod
84 #undef mknod
85 #undef mkfifo
86 #undef open
87 #undef open64
88 #undef openat
89 #undef openat64
90 #undef creat64
91 #undef fopen64
93 static void * get_dl_symbol(char *);
95 struct status_t {
96 ino_t inode;
97 off_t size;
98 time_t mtime;
99 time_t ctime;
102 static int initialized = 0;
104 #ifdef FLWRAPPER_BASEDIR
105 static void check_write_access(const char * , const char * );
106 #endif
107 static void handle_file_access_before(const char *, const char *, struct status_t *);
108 static void handle_file_access_after(const char *, const char *, struct status_t *);
110 char *cmdname = "unkown";
111 char wlog[PATH_MAX], rlog[PATH_MAX], filterdir[PATH_MAX], atpath[PATH_MAX];
113 const char* getatpath(int dirfd, const char* pathname) {
114 int ret;
115 char selffd[64];
117 ret = snprintf(selffd, sizeof(selffd), "/proc/self/fd/%d", dirfd);
118 /* TODO: short buffer handling */
119 ret = readlink(selffd, atpath, sizeof(atpath) - 1);
120 if (ret < 0) {
121 fprintf(stderr, "Failed to get atpath: %d, %s\n", dirfd, pathname);
122 return pathname;
124 atpath[ret] = 0;
126 strncat(atpath, "/", sizeof(atpath) - 1);
127 strncat(atpath, pathname, sizeof(atpath) - 1);
128 /* TODO: short buffer handling */
130 return atpath;
133 /* Wrapper Functions */
136 # This has been made with cpp-macros before until they turned to be absolutely
137 # unreadable ...
139 add_wrapper()
141 line="$( echo "$*" | sed 's/ *, */,/g' )"
142 old_ifs="$IFS" ; IFS="," ; set $line ; IFS="$old_ifs"
144 ret_type=$1 ; shift ; function=$1 ; shift
145 p1="" ; p2="" ; for x ; do p1="$p1$x, " ; done
146 for x ; do x="${x%%\[*\]}" ; p2="$p2${x##* }, " ; done
147 p1="${p1%, }" ; p2="${p2%, }"
149 # sanity check all new-style *at dirfd
150 atcheck=""
152 for x; do
153 if [[ "$x" = *atfd ]]; then
154 atcheck="${atcheck}if (${x##* } != AT_FDCWD) {f2 = getatpath(${x##* }, f);}"
156 done
158 case ${function} in
159 open*)
160 # remove varg from $p2
161 p2=${p2%, ...}
162 echo ; cat << EOT
163 extern $ret_type $function($p1);
164 $ret_type (*orig_$function)($p1) = 0;
166 $ret_type $function($p1)
168 struct status_t status;
169 const char* f2 = f;
170 int old_errno=errno;
171 $ret_type rc;
172 mode_t b = 0;
174 #ifdef AT_FDCWD
175 $atcheck
176 #endif
178 #ifdef FLWRAPPER_BASEDIR
179 if (a & (O_WRONLY|O_CREAT|O_APPEND))
180 check_write_access("$function", f2);
181 #endif
183 handle_file_access_before("$function", f2, &status);
184 if (!orig_$function) orig_$function = get_dl_symbol("$function");
185 errno=old_errno;
187 #if DEBUG == 1
188 fprintf(stderr, "fl_wrapper.so debug [%d]: going to run original $function() at %p (wrapper at %p).\n",
189 getpid(), orig_$function, $function);
190 #endif
192 if (a & O_CREAT) {
193 va_list ap;
195 va_start(ap, a);
196 b = va_arg(ap, mode_t);
197 va_end(ap);
199 rc = orig_$function($p2, b);
201 else
202 rc = orig_$function($p2);
204 old_errno=errno;
205 handle_file_access_after("$function", f2, &status);
206 errno=old_errno;
208 return rc;
212 exec*)
213 echo ; cat << EOT
214 extern $ret_type $function($p1);
215 $ret_type (*orig_$function)($p1) = 0;
217 $ret_type $function($p1)
219 int old_errno=errno;
221 handle_file_access_after("$function", f, 0);
222 if (!orig_$function) orig_$function = get_dl_symbol("$function");
223 errno=old_errno;
225 return orig_$function($p2);
230 echo ; cat << EOT
231 extern $ret_type $function($p1);
232 $ret_type (*orig_$function)($p1) = 0;
234 $ret_type $function($p1)
236 struct status_t status;
237 const char* f2 = f;
238 int old_errno=errno;
239 $ret_type rc;
241 #ifdef AT_FDCWD
242 $atcheck
243 #endif
245 handle_file_access_before("$function", f2, &status);
246 if (!orig_$function) orig_$function = get_dl_symbol("$function");
247 errno=old_errno;
249 #if DEBUG == 1
250 fprintf(stderr, "fl_wrapper.so debug [%d]: going to run original $function() at %p (wrapper at %p).\n",
251 getpid(), orig_$function, $function);
252 #endif
254 rc = orig_$function($p2);
256 old_errno=errno;
257 handle_file_access_after("$function", f2, &status);
258 errno=old_errno;
260 return rc;
264 esac
267 add_wrapper 'int, open, const char* f, int a, ...'
268 add_wrapper 'int, open64, const char* f, int a, ...'
269 add_wrapper 'int, openat, int atfd, const char* f, int a, ...'
270 add_wrapper 'int, openat64,int atfd, const char* f, int a, ...'
272 add_wrapper 'FILE*, fopen, const char* f, const char* g'
273 add_wrapper 'FILE*, fopen64, const char* f, const char* g'
275 add_wrapper 'int, creat, const char* f, mode_t m'
276 add_wrapper 'int, creat64, const char* f, mode_t m'
278 add_wrapper 'int, mkdir, const char* f, mode_t m'
279 add_wrapper 'int, mkfifo, const char* f, mode_t m'
280 add_wrapper 'int, mknod, const char* f, mode_t m, dev_t d'
281 add_wrapper 'int, __xmknod, int ver, const char* f, mode_t m, dev_t* d'
283 add_wrapper 'int, link, const char* s, const char* f'
284 add_wrapper 'int, linkat, int atfd, const char* s, int atfd2, const char* f, int flags'
286 add_wrapper 'int, symlink, const char* s, const char* f'
287 add_wrapper 'int, symlinkat, const char* s, int atfd, const char* f'
288 add_wrapper 'int, rename, const char* s, const char* f'
289 add_wrapper 'int, renameat, int atfds, const char* s, int atfd, const char* f'
290 add_wrapper 'int, renameat2, int atfds, const char* s, int atfd, const char* f, unsigned int flags'
292 add_wrapper 'int, utime, const char* f, const struct utimbuf* t'
293 add_wrapper 'int, utimes, const char* f, struct timeval* t'
294 add_wrapper 'int, utimensat, int atfd, const char* f, const struct timespec t[2], int flags'
296 add_wrapper 'int, execv, const char* f, char* const a[]'
297 add_wrapper 'int, execve, const char* f, char* const a[], char* const e[]'
299 echo
300 cat misc/tools-source/fl_wrapper_execl.c
302 echo ; cat << "EOT"
303 /* Internal Functions */
305 static void * get_dl_symbol(char * symname)
307 void * rc;
308 #if DLOPEN_LIBC
309 static void * libc_handle = 0;
311 if (!libc_handle) libc_handle=dlopen(FLWRAPPER_LIBC, RTLD_LAZY);
312 if (!libc_handle) {
313 fprintf(stderr, "fl_wrapper.so: Can't dlopen libc: %s\n", dlerror()); fflush(stderr);
314 abort();
317 rc = dlsym(libc_handle, symname);
318 # if DEBUG == 1
319 fprintf(stderr, "fl_wrapper.so debug [%d]: Symbol '%s' in libc (%p) has been resolved to %p.\n",
320 getpid(), symname, libc_handle, rc);
321 # endif
322 #else
323 rc = dlsym(RTLD_NEXT, symname);
324 # if DEBUG == 1
325 fprintf(stderr, "fl_wrapper.so debug [%d]: Symbol '%s' (RTLD_NEXT) has been resolved to %p.\n",
326 getpid(), symname, rc);
327 # endif
328 #endif
329 if (!rc) {
330 fprintf(stderr, "fl_wrapper.so: Can't resolve %s: %s\n",
331 symname, dlerror()); fflush(stderr);
332 abort();
335 return rc;
338 static pid_t pid2ppid(pid_t pid)
340 char buffer[100];
341 int fd, rc;
342 pid_t ppid = 0;
344 snprintf(buffer, sizeof(buffer), "/proc/%d/stat", pid);
345 if ( (fd = open(buffer, O_RDONLY, 0)) < 0 ) return 0;
346 if ( (rc = read(fd, buffer, sizeof(buffer))) > 0) {
347 buffer[--rc] = 0;
348 /* format: 27910 (bash) S 27315 ... */
349 sscanf(buffer, "%*[^ ] %*[^ ] %*[^ ] %d", &ppid);
351 close(fd);
353 return ppid;
356 /* this is only called from fl_wrapper_init(). so it doesn't need to be
357 * reentrant. */
358 static char *getpname(int pid)
360 static char p[512];
361 char buffer[100];
362 char *arg=0, *b;
363 int i, fd, rc;
365 snprintf(buffer, sizeof(buffer), "/proc/%d/cmdline", pid);
366 if ( (fd = open(buffer, O_RDONLY, 0)) < 0 ) return "unkown";
367 if ( (rc = read(fd, buffer, sizeof(buffer))) > 0) {
368 buffer[rc - 1] = 0;
369 for (i=0; i<rc; i++)
370 if (!buffer[i]) { arg = buffer+i+1; break; }
372 close(fd);
374 b = basename(buffer);
375 snprintf(p, 512, "%s", b);
377 if ( !strcmp(b, "bash") || !strcmp(b, "sh") || !strcmp(b, "perl") )
378 if (arg && *arg && *arg != '-')
379 snprintf(p, 512, "%s(%s)", b, basename(arg));
381 return p;
384 /* invert the order by recursion. there will be only one recursion tree
385 * so we can use a static var for managing the last ent */
386 static void addptree(int *txtpos, char *cmdtxt, int pid, int basepid)
388 static char l[512] = "";
389 char *p;
391 if (!pid || pid == basepid) return;
393 addptree(txtpos, cmdtxt, pid2ppid(pid), basepid);
395 p = getpname(pid);
397 if ( strcmp(l, p) )
398 *txtpos += snprintf(cmdtxt+*txtpos, 4096-*txtpos, "%s%s",
399 *txtpos ? "." : "", getpname(pid));
400 else
401 *txtpos += snprintf(cmdtxt+*txtpos, 4096-*txtpos, "*");
403 strcpy(l, p);
406 void copy_getenv (char* var, const char* name)
408 char *c = getenv(name);
409 if (c) strcpy (var, c);
410 else var[0]=0;
413 void __attribute__ ((constructor)) fl_wrapper_init()
415 char cmdtxt[4096] = "";
416 char *basepid_txt = getenv("FLWRAPPER_BASEPID");
417 int basepid = 0, txtpos=0;
419 if (basepid_txt)
420 basepid = atoi(basepid_txt);
422 addptree(&txtpos, cmdtxt, getpid(), basepid);
423 cmdname = strdup(cmdtxt);
425 /* we copy the vars, so evil code can not unset them ... e.g.
426 the perl/spamassassin build ... -ReneR */
427 copy_getenv(filterdir, "FLWRAPPER_FILTERDIR");
428 copy_getenv(wlog, "FLWRAPPER_WLOG");
429 copy_getenv(rlog, "FLWRAPPER_RLOG");
431 initialized = 1;
434 #ifdef FLWRAPPER_BASEDIR
435 static void check_write_access(const char * func, const char * file)
437 if (*file == '/') { /* do only check rooted paths */
438 while (file[1] == '/') file++;
440 if (!strcmp(file, "/dev/null") || !strncmp(file, "/tmp", 4)) {
442 else if (strncmp(file, FLWRAPPER_BASEDIR, sizeof(FLWRAPPER_BASEDIR)-1)) {
443 fprintf(stderr, "fl_wrapper.so: write outside basedir (%s): %s\n", FLWRAPPER_BASEDIR, file);
444 fflush(stderr);
445 exit(-1);
449 #endif
451 static void handle_file_access_before(const char * func, const char * file,
452 struct status_t * status)
454 struct stat st;
456 if (!initialized) return;
457 #if DEBUG == 1
458 fprintf(stderr, "fl_wrapper.so debug [%d]: begin of handle_file_access_before(\"%s\", \"%s\", ...)\n",
459 getpid(), func, file);
460 #endif
461 if ( lstat(file,&st) ) {
462 status->inode=0; status->size=0;
463 status->mtime=0; status->ctime=0;
464 } else {
465 status->inode=st.st_ino; status->size=st.st_size;
466 status->mtime=st.st_mtime; status->ctime=st.st_ctime;
468 #if DEBUG == 1
469 fprintf(stderr, "fl_wrapper.so debug [%d]: end of handle_file_access_before(\"%s\", \"%s\", ...)\n",
470 getpid(), func, file);
471 #endif
474 /* sort of, private realpath, mostly not readlink() */
475 static void sort_of_realpath (const char *file, char absfile[PATH_MAX])
477 /* make sure the filename is absolute */
478 if (file[0] != '/') {
479 char cwd[PATH_MAX];
480 getcwd(cwd, PATH_MAX);
481 snprintf(absfile, PATH_MAX, "%s/%s", cwd, file);
482 file = absfile;
485 const char* src = file; char* dst = absfile;
486 /* till the end, remove ./ and ../ parts */
487 while (dst < absfile + PATH_MAX && *src) {
488 if (*src == '/' && src[1] == '/')
489 while (src[1] == '/') src++;
490 else if (*src == '.') {
491 if (src[1] == '.' && (src[2] == '/' || src[2] == 0)) {
492 if (dst > absfile+1) --dst; /* jump to last '/' */
493 while (dst > absfile+1 && dst[-1] != '/')
494 --dst;
495 src += 2; if (*src) src++;
496 while (*src == '/') src++;
497 continue;
499 else if (src[1] == '/' || src[1] == 0) {
500 src += 1; if (*src) src++;
501 while (*src == '/') src++;
502 continue;
505 *dst++ = *src++;
507 *dst = 0;
508 /* remove trailing slashes */
509 while (--dst, dst > absfile+1 && *dst == '/')
510 *dst = 0;
513 static void handle_file_access_after(const char * func, const char * file,
514 struct status_t * status)
516 char buf[PATH_MAX], *logfile, filterdir2 [PATH_MAX], *tfilterdir;
517 char absfile [PATH_MAX];
518 int fd; struct stat st;
520 if (!initialized) return;
521 #if DEBUG == 1
522 fprintf(stderr, "fl_wrapper.so debug [%d]: begin of handle_file_access_after(\"%s\", \"%s\", ...)\n",
523 getpid(), func, file);
524 #endif
525 if ( !strcmp(file, wlog) ) return;
526 if ( !strcmp(file, rlog) ) return;
527 if ( lstat(file, &st) ) return;
529 if ( (status != 0) && (status->inode != st.st_ino ||
530 status->size != st.st_size || status->mtime != st.st_mtime ||
531 status->ctime != st.st_ctime) ) { logfile = wlog; }
532 else { logfile = rlog; }
533 if ( logfile == 0 ) return;
535 /* make sure the filename is "canonical" */
536 sort_of_realpath (file, absfile);
538 /* We ignore access inside the collon seperated directory list
539 $FLWRAPPER_BASE, to keep the log smaller and reduce post
540 processing time. -ReneR */
541 strcpy (filterdir2, filterdir); /* due to strtok - sigh */
542 tfilterdir = strtok(filterdir2, ":");
543 for ( ; tfilterdir ; tfilterdir = strtok(NULL, ":") )
545 if ( !strncmp(absfile, tfilterdir, strlen(tfilterdir)) ) {
546 #if DEBUG == 1
547 fprintf(stderr,
548 "fl_wrapper.so debug [%d]: \"%s\" dropped due to filterdir \"%s\"\n",
549 getpid(), absfile, tfilterdir);
550 #endif
551 return;
555 #ifdef __USE_LARGEFILE
556 fd=open64(logfile,O_APPEND|O_WRONLY|O_LARGEFILE,0);
557 #else
558 #warning "The wrapper library may not work properly for large logs!"
559 fd=open(logfile,O_APPEND|O_WRONLY,0);
560 #endif
561 if (fd == -1) return;
563 snprintf(buf,sizeof(buf), "%s.%s:\t%s\n", cmdname, func, absfile);
564 write(fd,buf,strlen(buf));
566 close(fd);
567 #if DEBUG == 1
568 fprintf(stderr, "fl_wrapper.so debug [%d]: end of handle_file_access_after(\"%s\", \"%s\", ...)\n",
569 getpid(), func, file);
570 #endif