2 * Hook output functions (like printf(3)) with LD_PRELOAD to color stderr (or
3 * other file descriptors).
5 * Copyright (C) 2013 Simon Ruderich
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.
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.
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/>.
23 /* Must be loaded before the following headers. */
24 #include "ldpreload.h"
26 /* FIXME: use correct declaration for fcntl() */
27 #define fcntl fcntl_ignore
37 /* Conflicting declaration in glibc. */
38 #undef fwrite_unlocked
41 /* Used by various functions, including debug(). */
42 static ssize_t (*real_write)(int, const void *, size_t);
43 static int (*real_close)(int);
44 static size_t (*real_fwrite)(const void *, size_t, size_t, FILE *);
47 #include "constants.h"
52 #include "hookmacros.h"
57 /* Should the "action" handler be invoked for this file descriptor? */
58 static int check_handle_fd(int fd) {
59 /* Never touch anything not going to a terminal. */
64 /* Load state from environment. Only necessary once per process. */
66 init_from_environment();
69 if (tracked_fds_count == 0) {
72 return tracked_fds_find(fd);
75 static void dup_fd(int oldfd, int newfd) {
77 debug("%d -> %d\t\t\t[%d]\n", oldfd, newfd, getpid());
81 init_from_environment();
83 if (tracked_fds_count == 0) {
87 /* We are already tracking this file descriptor, add newfd to the list as
88 * it will reference the same descriptor. */
89 if (tracked_fds_find(oldfd)) {
90 if (!tracked_fds_find(newfd)) {
91 tracked_fds_add(newfd);
94 /* We are not tracking this file descriptor, remove newfd from the list
97 if (tracked_fds_remove(newfd)) {
103 static void close_fd(int fd) {
105 debug("%d -> .\t\t\t[%d]\n", fd, getpid());
109 init_from_environment();
112 if (tracked_fds_count == 0) {
115 tracked_fds_remove(fd);
119 /* "Action" handlers called when a file descriptor is matched. */
121 static char *pre_string;
122 static size_t pre_string_size;
123 static char *post_string;
124 static size_t post_string_size;
126 /* Load alternative pre/post strings from the environment if available, fall
127 * back to default values. */
128 inline static void init_pre_post_string() {
129 pre_string = getenv(ENV_NAME_PRE_STRING);
131 pre_string = DEFAULT_PRE_STRING;
133 pre_string_size = strlen(pre_string);
135 post_string = getenv(ENV_NAME_POST_STRING);
137 post_string = DEFAULT_POST_STRING;
139 post_string_size = strlen(post_string);
142 static void handle_fd_pre(int fd, int action) {
145 if (!pre_string || !post_string) {
146 init_pre_post_string();
149 DLSYM_FUNCTION(real_write, "write");
150 real_write(fd, pre_string, pre_string_size);
152 static void handle_fd_post(int fd, int action) {
155 /* write() already loaded above in handle_fd_pre(). */
156 real_write(fd, post_string, post_string_size);
159 static void handle_file_pre(FILE *stream, int action) {
162 if (!pre_string || !post_string) {
163 init_pre_post_string();
166 DLSYM_FUNCTION(real_fwrite, "fwrite");
167 real_fwrite(pre_string, pre_string_size, 1, stream);
169 static void handle_file_post(FILE *stream, int action) {
172 /* fwrite() already loaded above in handle_file_pre(). */
173 real_fwrite(post_string, post_string_size, 1, stream);
178 /* Hook all important output functions to manipulate their output. */
180 HOOK_FD3(ssize_t, write, fd,
181 int, fd, const void *, buf, size_t, count)
182 HOOK_FILE4(size_t, fwrite, stream,
183 const void *, ptr, size_t, size, size_t, nmemb, FILE *, stream)
186 HOOK_FILE2(int, fputs, stream,
187 const char *, s, FILE *, stream)
188 HOOK_FILE2(int, fputc, stream,
189 int, c, FILE *, stream)
190 HOOK_FILE2(int, putc, stream,
191 int, c, FILE *, stream)
192 HOOK_FILE1(int, putchar, stdout,
194 HOOK_FILE1(int, puts, stdout,
197 /* printf(3), excluding all s*() and vs*() functions (no output) */
198 HOOK_VAR_FILE1(int, printf, stdout, vprintf,
199 const char *, format)
200 HOOK_VAR_FILE2(int, fprintf, stream, vfprintf,
201 FILE *, stream, const char *, format)
202 HOOK_FILE2(int, vprintf, stdout,
203 const char *, format, va_list, ap)
204 HOOK_FILE3(int, vfprintf, stream,
205 FILE *, stream, const char *, format, va_list, ap)
206 /* Hardening functions (-D_FORTIFY_SOURCE=2). */
207 HOOK_VAR_FILE2(int, __printf_chk, stdout, __vprintf_chk,
208 int, flag, const char *, format)
209 HOOK_VAR_FILE3(int, __fprintf_chk, fp, __vfprintf_chk,
210 FILE *, fp, int, flag, const char *, format)
211 HOOK_FILE3(int, __vprintf_chk, stdout,
212 int, flag, const char *, format, va_list, ap)
213 HOOK_FILE4(int, __vfprintf_chk, stream,
214 FILE *, stream, int, flag, const char *, format, va_list, ap)
216 /* unlocked_stdio(3), only functions from above are hooked */
217 HOOK_FILE4(size_t, fwrite_unlocked, stream,
218 const void *, ptr, size_t, size, size_t, nmemb, FILE *, stream)
219 HOOK_FILE2(int, fputs_unlocked, stream,
220 const char *, s, FILE *, stream)
221 HOOK_FILE2(int, fputc_unlocked, stream,
222 int, c, FILE *, stream)
223 HOOK_FILE2(int, putc_unlocked, stream,
224 int, c, FILE *, stream)
225 HOOK_FILE1(int, putchar_unlocked, stdout,
227 HOOK_FILE1(int, puts_unlocked, stdout,
231 /* Hook functions which duplicate file descriptors to track them. */
233 static int (*real_dup)(int);
234 static int (*real_dup2)(int, int);
235 static int (*real_dup3)(int, int, int);
239 DLSYM_FUNCTION(real_dup, "dup");
241 newfd = real_dup(oldfd);
243 int saved_errno = errno;
244 dup_fd(oldfd, newfd);
250 int dup2(int oldfd, int newfd) {
251 DLSYM_FUNCTION(real_dup2, "dup2");
253 newfd = real_dup2(oldfd, newfd);
255 int saved_errno = errno;
256 dup_fd(oldfd, newfd);
262 int dup3(int oldfd, int newfd, int flags) {
263 DLSYM_FUNCTION(real_dup3, "dup3");
265 newfd = real_dup3(oldfd, newfd, flags);
267 int saved_errno = errno;
268 dup_fd(oldfd, newfd);
275 static int (*real_fcntl)(int, int, int);
276 int fcntl(int fd, int cmd, int arg) {
279 DLSYM_FUNCTION(real_fcntl, "fcntl");
281 result = real_fcntl(fd, cmd, arg);
282 /* We only care about duping fds. */
283 if (cmd == F_DUPFD && result != -1) {
284 int saved_errno = errno;
292 static int (*real_close)(int);
294 DLSYM_FUNCTION(real_close, "close");
297 return real_close(fd);
299 static int (*real_fclose)(FILE *);
300 int fclose(FILE *fp) {
301 DLSYM_FUNCTION(real_fclose, "fclose");
303 close_fd(fileno(fp));
304 return real_fclose(fp);