X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=src%2Fcoloredstderr.c;h=a55ce38d15fd67dcc3abcc787f3cf6a1de608003;hb=a9db9083b6fd9c1bd6a09df6d64386d806488f7c;hp=32b1dd8f106f570535f614c4b410fdcc2f7ffd7e;hpb=abc3d7889f3774717baf5795ffab2efb396d2570;p=coloredstderr%2Fcoloredstderr.git diff --git a/src/coloredstderr.c b/src/coloredstderr.c index 32b1dd8..a55ce38 100644 --- a/src/coloredstderr.c +++ b/src/coloredstderr.c @@ -23,9 +23,6 @@ /* Must be loaded before the following headers. */ #include "ldpreload.h" -/* FIXME: use correct declaration for fcntl() */ -#define fcntl fcntl_ignore - #include #include #include @@ -33,7 +30,6 @@ #include #include -#undef fcntl /* Conflicting declaration in glibc. */ #undef fwrite_unlocked @@ -43,29 +39,36 @@ static ssize_t (*real_write)(int, const void *, size_t); static int (*real_close)(int); static size_t (*real_fwrite)(const void *, size_t, size_t, FILE *); +/* Did we already (try to) parse the environment and setup the necessary + * variables? */ +static int initialized; +/* Force hooked writes even when not writing to a tty. Used for tests. */ +static int force_write_to_non_tty; + #include "constants.h" #ifdef DEBUG # include "debug.h" #endif -#include "macros.h" +#include "hookmacros.h" #include "trackfds.h" /* Should the "action" handler be invoked for this file descriptor? */ static int check_handle_fd(int fd) { - /* Never touch anything not going to a terminal. */ - if (!isatty(fd)) { - return 0; - } - /* Load state from environment. Only necessary once per process. */ - if (!tracked_fds) { + if (!initialized) { init_from_environment(); } + /* Never touch anything not going to a terminal - unless we are explicitly + * asked to do so. */ + if (!force_write_to_non_tty && !isatty(fd)) { + return 0; + } + if (tracked_fds_count == 0) { return 0; } @@ -74,10 +77,10 @@ static int check_handle_fd(int fd) { static void dup_fd(int oldfd, int newfd) { #ifdef DEBUG - debug("%d -> %d\t\t\t[%d]\n", oldfd, newfd, getpid()); + debug("%3d -> %3d\t\t\t[%d]\n", oldfd, newfd, getpid()); #endif - if (!tracked_fds) { + if (!initialized) { init_from_environment(); } if (tracked_fds_count == 0) { @@ -102,10 +105,10 @@ static void dup_fd(int oldfd, int newfd) { static void close_fd(int fd) { #ifdef DEBUG - debug("%d -> .\t\t\t[%d]\n", fd, getpid()); + debug("%3d -> .\t\t\t[%d]\n", fd, getpid()); #endif - if (!tracked_fds) { + if (!initialized) { init_from_environment(); } @@ -203,7 +206,7 @@ HOOK_FILE2(int, vprintf, stdout, const char *, format, va_list, ap) HOOK_FILE3(int, vfprintf, stream, FILE *, stream, const char *, format, va_list, ap) -/* Hardening functions (-D_FORTIFY_SOURCE=2). */ +/* Hardening functions (-D_FORTIFY_SOURCE=2), only functions from above */ HOOK_VAR_FILE2(int, __printf_chk, stdout, __vprintf_chk, int, flag, const char *, format) HOOK_VAR_FILE3(int, __fprintf_chk, fp, __vfprintf_chk, @@ -227,6 +230,10 @@ HOOK_FILE1(int, putchar_unlocked, stdout, HOOK_FILE1(int, puts_unlocked, stdout, const char *, s) +/* perror(3) */ +HOOK_VOID1(void, perror, STDERR_FILENO, + const char *, s) + /* Hook functions which duplicate file descriptors to track them. */ @@ -272,15 +279,32 @@ int dup3(int oldfd, int newfd, int flags) { return newfd; } -static int (*real_fcntl)(int, int, int); -int fcntl(int fd, int cmd, int arg) { +static int (*real_fcntl)(int, int, ...); +int fcntl(int fd, int cmd, ...) { int result; + va_list ap; DLSYM_FUNCTION(real_fcntl, "fcntl"); - result = real_fcntl(fd, cmd, arg); + /* fcntl() takes different types of arguments depending on the cmd type + * (int, void and pointers are used at the moment). Handling these + * arguments for different systems and with possible changes in the future + * is error prone. + * + * Therefore always retrieve a void-pointer from our arguments (even if it + * wasn't there) and pass it to real_fcntl(). This shouldn't cause any + * problems because a void-pointer is most-likely bigger than an int + * (something which is not true in reverse) and shouldn't cause + * truncation. For register based calling conventions an invalid register + * content is passed, but ignored by real_fcntl(). Not perfect, but should + * work fine. + */ + va_start(ap, cmd); + result = real_fcntl(fd, cmd, va_arg(ap, void *)); + va_end(ap); + /* We only care about duping fds. */ - if (cmd == F_DUPFD) { + if (cmd == F_DUPFD && result != -1) { int saved_errno = errno; dup_fd(fd, result); errno = saved_errno; @@ -303,3 +327,23 @@ int fclose(FILE *fp) { close_fd(fileno(fp)); return real_fclose(fp); } + + +/* Hook functions which are necessary for correct tracking. */ + +#if defined(HAVE_VFORK) && defined(HAVE_FORK) +pid_t vfork(void) { + /* vfork() is similar to fork() but the address space is shared between + * father and child. It's designed for fork()/exec() usage because it's + * faster than fork(). However according to the POSIX standard the "child" + * isn't allowed to perform any memory-modifications before the exec() + * (except the pid_t result variable of vfork()). + * + * As some programs don't adhere to the standard (e.g. the "child" closes + * or dups a descriptor before the exec()) and this breaks our tracking of + * file descriptors (e.g. it gets closed in the parent as well), we just + * fork() instead. This is in compliance with the POSIX standard and as + * most systems use copy-on-write anyway not a performance issue. */ + return fork(); +} +#endif