]> ruderich.org/simon Gitweb - coloredstderr/coloredstderr.git/blob - src/coloredstderr.c
Hook __swbuf() on FreeBSD.
[coloredstderr/coloredstderr.git] / src / coloredstderr.c
1 /*
2  * Hook output functions (like printf(3)) with LD_PRELOAD to color stderr (or
3  * other file descriptors).
4  *
5  * Copyright (C) 2013  Simon Ruderich
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "compiler.h"
24
25 /* Must be loaded before the following headers. */
26 #include "ldpreload.h"
27
28 /* Disable assert()s if not compiled with --enable-debug. */
29 #ifndef DEBUG
30 # define NDEBUG
31 #endif
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41
42 #ifdef HAVE_ERR_H
43 # include <err.h>
44 #endif
45 #ifdef HAVE_ERROR_H
46 # include <error.h>
47 #endif
48 #ifdef HAVE_STRUCT__IO_FILE__FILENO
49 # include <libio.h>
50 #endif
51
52 /* The following functions may be macros. Undefine them or they cause build
53  * failures when used in our hook macros below. */
54
55 /* In glibc, real fwrite_unlocked() is called in macro. */
56 #ifdef HAVE_FWRITE_UNLOCKED
57 # undef fwrite_unlocked
58 #endif
59 /* In Clang when compiling with hardening flags (fortify) on Debian Wheezy. */
60 #undef printf
61 #undef fprintf
62 /* On FreeBSD (9.1), __swbuf() is used instead of these macros. */
63 #ifdef HAVE___SWBUF
64 # undef putc
65 # undef putc_unlocked
66 # undef putchar
67 # undef putchar_unlocked
68 #endif
69
70
71 /* Used by various functions, including debug(). */
72 static ssize_t (*real_write)(int, void const *, size_t);
73 static int (*real_close)(int);
74 static size_t (*real_fwrite)(void const *, size_t, size_t, FILE *);
75
76 /* Did we already (try to) parse the environment and setup the necessary
77  * variables? */
78 static int initialized;
79 /* Force hooked writes even when not writing to a tty. Used for tests. */
80 static int force_write_to_non_tty;
81
82
83 #include "constants.h"
84 #ifdef WARNING
85 # include "debug.h"
86 #endif
87
88 #include "hookmacros.h"
89 #include "trackfds.h"
90
91
92
93 /* See hookmacros.h for the decision if a function call is colored. */
94
95
96 /* Prevent inlining into hook functions because it may increase the number of
97  * spilled registers unnecessarily. As it's not called very often accept the
98  * additional call. */
99 static int isatty_noinline(int fd) noinline;
100 static int isatty_noinline(int fd) {
101     assert(fd >= 0);
102
103     int saved_errno = errno;
104     int result = isatty(fd);
105     errno = saved_errno;
106
107     return result;
108 }
109
110
111 static void dup_fd(int oldfd, int newfd) {
112 #ifdef DEBUG
113     debug("%3d -> %3d\t\t\t[%d]\n", oldfd, newfd, getpid());
114 #endif
115
116     assert(oldfd >= 0 && newfd >= 0);
117
118     if (unlikely(!initialized)) {
119         init_from_environment();
120     }
121
122     /* We are already tracking this file descriptor, add newfd to the list as
123      * it will reference the same descriptor. */
124     if (tracked_fds_find(oldfd)) {
125         if (!tracked_fds_find(newfd)) {
126             tracked_fds_add(newfd);
127         }
128     /* We are not tracking this file descriptor, remove newfd from the list
129      * (if present). */
130     } else {
131         tracked_fds_remove(newfd);
132     }
133 }
134
135 static void close_fd(int fd) {
136 #ifdef DEBUG
137     debug("%3d ->   .\t\t\t[%d]\n", fd, getpid());
138 #endif
139
140     assert(fd >= 0);
141
142     if (unlikely(!initialized)) {
143         init_from_environment();
144     }
145
146     tracked_fds_remove(fd);
147 }
148
149
150 /* "Action" handlers called when a file descriptor is matched. */
151
152 static char const *pre_string;
153 static size_t pre_string_size;
154 static char const *post_string;
155 static size_t post_string_size;
156
157 /* Load alternative pre/post strings from the environment if available, fall
158  * back to default values. */
159 static void init_pre_post_string(void) {
160     pre_string = getenv(ENV_NAME_PRE_STRING);
161     if (!pre_string) {
162         pre_string = DEFAULT_PRE_STRING;
163     }
164     pre_string_size = strlen(pre_string);
165
166     post_string = getenv(ENV_NAME_POST_STRING);
167     if (!post_string) {
168         post_string = DEFAULT_POST_STRING;
169     }
170     post_string_size = strlen(post_string);
171 }
172
173 /* Don't inline any of the pre/post functions. Keep the hook function as small
174  * as possible for speed reasons. */
175 static void handle_fd_pre(int fd) noinline;
176 static void handle_fd_post(int fd) noinline;
177 static void handle_file_pre(FILE *stream) noinline;
178 static void handle_file_post(FILE *stream) noinline;
179
180 static void handle_fd_pre(int fd) {
181     int saved_errno = errno;
182
183     if (unlikely(!pre_string || !post_string)) {
184         init_pre_post_string();
185     }
186
187     DLSYM_FUNCTION(real_write, "write");
188     real_write(fd, pre_string, pre_string_size);
189
190     errno = saved_errno;
191 }
192 static void handle_fd_post(int fd) {
193     int saved_errno = errno;
194
195     /* write() already loaded above in handle_fd_pre(). */
196     real_write(fd, post_string, post_string_size);
197
198     errno = saved_errno;
199 }
200
201 static void handle_file_pre(FILE *stream) {
202     int saved_errno = errno;
203
204     if (unlikely(!pre_string || !post_string)) {
205         init_pre_post_string();
206     }
207
208     DLSYM_FUNCTION(real_fwrite, "fwrite");
209     real_fwrite(pre_string, pre_string_size, 1, stream);
210
211     errno = saved_errno;
212 }
213 static void handle_file_post(FILE *stream) {
214     int saved_errno = errno;
215
216     /* fwrite() already loaded above in handle_file_pre(). */
217     real_fwrite(post_string, post_string_size, 1, stream);
218
219     errno = saved_errno;
220 }
221
222
223
224 /* Hook all important output functions to manipulate their output. */
225
226 HOOK_FD3(ssize_t, write, fd,
227          int, fd, void const *, buf, size_t, count)
228 HOOK_FILE4(size_t, fwrite, stream,
229            void const *, ptr, size_t, size, size_t, nmemb, FILE *, stream)
230
231 /* puts(3) */
232 HOOK_FILE2(int, fputs, stream,
233            char const *, s, FILE *, stream)
234 HOOK_FILE2(int, fputc, stream,
235            int, c, FILE *, stream)
236 HOOK_FILE2(int, putc, stream,
237            int, c, FILE *, stream)
238 HOOK_FILE1(int, putchar, stdout,
239            int, c)
240 HOOK_FILE1(int, puts, stdout,
241            char const *, s)
242
243 /* printf(3), excluding all s*() and vs*() functions (no output) */
244 HOOK_VAR_FILE1(int, printf, stdout, vprintf,
245                char const *, format)
246 HOOK_VAR_FILE2(int, fprintf, stream, vfprintf,
247                FILE *, stream, char const *, format)
248 HOOK_FILE2(int, vprintf, stdout,
249            char const *, format, va_list, ap)
250 HOOK_FILE3(int, vfprintf, stream,
251            FILE *, stream, char const *, format, va_list, ap)
252 /* Hardening functions (-D_FORTIFY_SOURCE=2), only functions from above */
253 HOOK_VAR_FILE2(int, __printf_chk, stdout, __vprintf_chk,
254                int, flag, char const *, format)
255 HOOK_VAR_FILE3(int, __fprintf_chk, fp, __vfprintf_chk,
256                FILE *, fp, int, flag, char const *, format)
257 HOOK_FILE3(int, __vprintf_chk, stdout,
258            int, flag, char const *, format, va_list, ap)
259 HOOK_FILE4(int, __vfprintf_chk, stream,
260            FILE *, stream, int, flag, char const *, format, va_list, ap)
261
262 /* unlocked_stdio(3), only functions from above are hooked */
263 #ifdef HAVE_FWRITE_UNLOCKED
264 HOOK_FILE4(size_t, fwrite_unlocked, stream,
265            void const *, ptr, size_t, size, size_t, nmemb, FILE *, stream)
266 #endif
267 #ifdef HAVE_FPUTS_UNLOCKED
268 HOOK_FILE2(int, fputs_unlocked, stream,
269            char const *, s, FILE *, stream)
270 #endif
271 #ifdef HAVE_FPUTC_UNLOCKED
272 HOOK_FILE2(int, fputc_unlocked, stream,
273            int, c, FILE *, stream)
274 #endif
275 HOOK_FILE2(int, putc_unlocked, stream,
276            int, c, FILE *, stream)
277 HOOK_FILE1(int, putchar_unlocked, stdout,
278            int, c)
279 /* glibc defines (_IO_)putc_unlocked() to a macro which either updates the
280  * output buffer or calls __overflow(). As this code is inlined we can't
281  * handle the first case, but if __overflow() is called we can color that
282  * part. As writes to stderr are never buffered, __overflow() is always called
283  * and everything works fine. This is only a problem if stdout is dupped to
284  * stderr (which shouldn't be the case too often). */
285 #if defined(HAVE_STRUCT__IO_FILE__FILENO) && defined(HAVE___OVERFLOW)
286 /* _IO_FILE is glibc's representation of FILE. */
287 HOOK_FILE2(int, __overflow, f, _IO_FILE *, f, int, ch)
288 #endif
289 /* Same for FreeBSD's libc. However it's more aggressive: The inline writing
290  * and __swbuf() are also used for normal output (e.g. putc()). Writing to
291  * stderr is still fine; it always calls __swbuf() as stderr is always
292  * unbufferd. */
293 #ifdef HAVE___SWBUF
294 HOOK_FILE2(int, __swbuf, f, int, c, FILE *, f)
295 #endif
296
297 /* perror(3) */
298 HOOK_VOID1(void, perror, STDERR_FILENO,
299            char const *, s)
300
301 /* err(3), non standard BSD extension */
302 #ifdef HAVE_ERR_H
303 HOOK_VAR_VOID2(void, err, STDERR_FILENO, verr,
304                int, eval, char const *, fmt)
305 HOOK_VAR_VOID2(void, errx, STDERR_FILENO, verrx,
306                int, eval, char const *, fmt)
307 HOOK_VAR_VOID1(void, warn, STDERR_FILENO, vwarn,
308                char const *, fmt)
309 HOOK_VAR_VOID1(void, warnx, STDERR_FILENO, vwarnx,
310                char const *, fmt)
311 HOOK_FUNC_SIMPLE3(void, verr, int, eval, const char *, fmt, va_list, args) {
312     /* Can't use verr() directly as it terminates the process which prevents
313      * the post string from being printed. */
314     vwarn(fmt, args);
315     exit(eval);
316 }
317 HOOK_FUNC_SIMPLE3(void, verrx, int, eval, const char *, fmt, va_list, args) {
318     /* See verr(). */
319     vwarnx(fmt, args);
320     exit(eval);
321 }
322 HOOK_VOID2(void, vwarn, STDERR_FILENO,
323            char const *, fmt, va_list, args)
324 HOOK_VOID2(void, vwarnx, STDERR_FILENO,
325            char const *, fmt, va_list, args)
326 #endif
327
328 /* error(3), non-standard GNU extension */
329 #ifdef HAVE_ERROR_H
330 static void error_vararg(int status, int errnum,
331                          char const *filename, unsigned int linenum,
332                          char const *format, va_list ap) {
333     static char const *last_filename;
334     static unsigned int last_linenum;
335
336     /* Skip this error message if requested and if there was already an error
337      * in the same file/line. */
338     if (error_one_per_line
339             && filename != NULL && linenum != 0
340             && filename == last_filename && linenum == last_linenum) {
341         return;
342     }
343     last_filename = filename;
344     last_linenum  = linenum;
345
346     error_message_count++;
347
348     fflush(stdout);
349
350     if (error_print_progname) {
351         error_print_progname();
352     } else {
353         fprintf(stderr, "%s:", program_invocation_name);
354     }
355     if (filename != NULL && linenum != 0) {
356         fprintf(stderr, "%s:%u:", filename, linenum);
357         if (error_print_progname) {
358             fprintf(stderr, " ");
359         }
360     }
361     if (!error_print_progname) {
362         fprintf(stderr, " ");
363     }
364
365
366     vfprintf(stderr, format, ap);
367
368     if (errnum != 0) {
369         fprintf(stderr, ": %s", strerror(errnum));
370     }
371
372     fprintf(stderr, "\n");
373
374     if (status != 0) {
375         exit(status);
376     }
377 }
378 void error_at_line(int status, int errnum,
379                    char const *filename, unsigned int linenum,
380                    char const *format, ...) {
381     va_list ap;
382
383     va_start(ap, format);
384     error_vararg(status, errnum, filename, linenum, format, ap);
385     va_end(ap);
386 }
387 void error(int status, int errnum, char const *format, ...) {
388     va_list ap;
389
390     va_start(ap, format);
391     error_vararg(status, errnum, NULL, 0, format, ap);
392     va_end(ap);
393 }
394 #endif
395
396
397 /* Hook functions which duplicate file descriptors to track them. */
398
399 /* int dup(int) */
400 HOOK_FUNC_DEF1(int, dup, int, oldfd) {
401     int newfd;
402
403     DLSYM_FUNCTION(real_dup, "dup");
404
405     newfd = real_dup(oldfd);
406     if (newfd != -1) {
407         dup_fd(oldfd, newfd);
408     }
409
410     return newfd;
411 }
412 /* int dup2(int, int) */
413 HOOK_FUNC_DEF2(int, dup2, int, oldfd, int, newfd) {
414     DLSYM_FUNCTION(real_dup2, "dup2");
415
416     newfd = real_dup2(oldfd, newfd);
417     if (newfd != -1) {
418         dup_fd(oldfd, newfd);
419     }
420
421     return newfd;
422 }
423 /* int dup3(int, int, int) */
424 HOOK_FUNC_DEF3(int, dup3, int, oldfd, int, newfd, int, flags) {
425     DLSYM_FUNCTION(real_dup3, "dup3");
426
427     newfd = real_dup3(oldfd, newfd, flags);
428     if (newfd != -1) {
429         dup_fd(oldfd, newfd);
430     }
431
432     return newfd;
433 }
434
435 /* int fcntl(int, int, ...) */
436 HOOK_FUNC_VAR_DEF2(int, fcntl, int, fd, int, cmd /*, ... */) {
437     int result;
438     va_list ap;
439
440     DLSYM_FUNCTION(real_fcntl, "fcntl");
441
442     /* fcntl() takes different types of arguments depending on the cmd type
443      * (int, void and pointers are used at the moment). Handling these
444      * arguments for different systems and with possible changes in the future
445      * is error prone.
446      *
447      * Therefore always retrieve a void-pointer from our arguments (even if it
448      * wasn't there) and pass it to real_fcntl(). This shouldn't cause any
449      * problems because a void-pointer is most-likely bigger than an int
450      * (something which is not true in reverse) and shouldn't cause
451      * truncation. For register based calling conventions an invalid register
452      * content is passed, but ignored by real_fcntl(). Not perfect, but should
453      * work fine.
454      */
455     va_start(ap, cmd);
456     result = real_fcntl(fd, cmd, va_arg(ap, void *));
457     va_end(ap);
458
459     /* We only care about duping fds. */
460     if (cmd == F_DUPFD && result != -1) {
461         dup_fd(fd, result);
462     }
463
464     return result;
465 }
466
467 /* int close(int) */
468 HOOK_FUNC_DEF1(int, close, int, fd) {
469     DLSYM_FUNCTION(real_close, "close");
470
471     if (fd >= 0) {
472         close_fd(fd);
473     }
474     return real_close(fd);
475 }
476 /* int fclose(FILE *) */
477 HOOK_FUNC_DEF1(int, fclose, FILE *, fp) {
478     int fd;
479
480     DLSYM_FUNCTION(real_fclose, "fclose");
481
482     if (fp != NULL && (fd = fileno(fp)) >= 0) {
483         close_fd(fd);
484     }
485     return real_fclose(fp);
486 }
487
488
489 /* Hook functions which are necessary for correct tracking. */
490
491 #if defined(HAVE_VFORK) && defined(HAVE_FORK)
492 pid_t vfork(void) {
493     /* vfork() is similar to fork() but the address space is shared between
494      * father and child. It's designed for fork()/exec() usage because it's
495      * faster than fork(). However according to the POSIX standard the "child"
496      * isn't allowed to perform any memory-modifications before the exec()
497      * (except the pid_t result variable of vfork()).
498      *
499      * As some programs don't adhere to the standard (e.g. the "child" closes
500      * or dups a descriptor before the exec()) and this breaks our tracking of
501      * file descriptors (e.g. it gets closed in the parent as well), we just
502      * fork() instead. This is in compliance with the POSIX standard and as
503      * most systems use copy-on-write anyway not a performance issue. */
504     return fork();
505 }
506 #endif
507
508
509 /* Hook execve() and the other exec*() functions. Some shells use exec*() with
510  * a custom environment which doesn't necessarily contain our updates to
511  * ENV_NAME_FDS. It's also faster to update the environment only when
512  * necessary, right before the exec() to pass it to the new process. */
513
514 /* int execve(char const *, char * const [], char * const []) */
515 HOOK_FUNC_DEF3(int, execve, char const *, filename, char * const *, argv, char * const *, env) {
516     DLSYM_FUNCTION(real_execve, "execve");
517
518     int found = 0;
519     size_t index = 0;
520
521     /* Count arguments and search for existing ENV_NAME_FDS environment
522      * variable. */
523     size_t count = 0;
524     char * const *x = env;
525     while (*x) {
526         if (!strncmp(*x, ENV_NAME_FDS "=", strlen(ENV_NAME_FDS) + 1)) {
527             found = 1;
528             index = count;
529         }
530
531         x++;
532         count++;
533     }
534     /* Terminating NULL. */
535     count++;
536
537     char *env_copy[count + 1 /* space for our new entry if necessary */];
538     memcpy(env_copy, env, count * sizeof(char *));
539
540     /* Make sure the information from the environment is loaded. We can't just
541      * do nothing (like update_environment()) because the caller might pass a
542      * different environment which doesn't include any of our settings. */
543     if (!initialized) {
544         init_from_environment();
545     }
546
547     char fds_env[strlen(ENV_NAME_FDS) + 1 + update_environment_buffer_size()];
548     strcpy(fds_env, ENV_NAME_FDS "=");
549     update_environment_buffer(fds_env + strlen(ENV_NAME_FDS) + 1);
550
551     if (found) {
552         env_copy[index] = fds_env;
553     } else {
554         /* If the process removed ENV_NAME_FDS from the environment, re-add
555          * it. */
556         env_copy[count-1] = fds_env;
557         env_copy[count] = NULL;
558     }
559
560     return real_execve(filename, argv, env_copy);
561 }
562
563 #define EXECL_COPY_VARARGS_START(args) \
564     va_list ap; \
565     char *x; \
566     \
567     /* Count arguments. */ \
568     size_t count = 1; /* arg */ \
569     va_start(ap, arg); \
570     while (va_arg(ap, char *)) { \
571         count++; \
572     } \
573     va_end(ap); \
574     \
575     /* Copy varargs. */ \
576     char *args[count + 1 /* terminating NULL */]; \
577     args[0] = (char *)arg; /* there's no other way around the cast */ \
578     \
579     size_t i = 1; \
580     va_start(ap, arg); \
581     while ((x = va_arg(ap, char *))) { \
582         args[i++] = x; \
583     } \
584     args[i] = NULL;
585 #define EXECL_COPY_VARARGS_END(args) \
586     va_end(ap);
587 #define EXECL_COPY_VARARGS(args) \
588     EXECL_COPY_VARARGS_START(args); \
589     EXECL_COPY_VARARGS_END(args);
590
591 int execl(char const *path, char const *arg, ...) {
592     EXECL_COPY_VARARGS(args);
593
594     /* execv() updates the environment. */
595     return execv(path, args);
596 }
597 int execlp(char const *file, char const *arg, ...) {
598     EXECL_COPY_VARARGS(args);
599
600     /* execvp() updates the environment. */
601     return execvp(file, args);
602 }
603 int execle(char const *path, char const *arg, ... /*, char * const envp[] */) {
604     char * const *envp;
605
606     EXECL_COPY_VARARGS_START(args);
607     /* Get envp[] located after arguments. */
608     envp = va_arg(ap, char * const *);
609     EXECL_COPY_VARARGS_END(args);
610
611     /* execve() updates the environment. */
612     return execve(path, args, envp);
613 }
614
615 /* int execv(char const *, char * const []) */
616 HOOK_FUNC_DEF2(int, execv, char const *, path, char * const *, argv) {
617     DLSYM_FUNCTION(real_execv, "execv");
618
619     update_environment();
620     return real_execv(path, argv);
621 }
622
623 /* int execvp(char const *, char * const []) */
624 HOOK_FUNC_DEF2(int, execvp, char const *, file, char * const *, argv) {
625     DLSYM_FUNCTION(real_execvp, "execvp");
626
627     update_environment();
628     return real_execvp(file, argv);
629 }
630
631 #ifdef HAVE_EXECVPE
632 extern char **environ;
633 int execvpe(char const *file, char * const argv[], char * const envp[]) {
634     /* Fake the environment so we can reuse execvp(). */
635     environ = (char **)envp;
636
637     /* execvp() updates the environment. */
638     return execvp(file, argv);
639 }
640 #endif