1061da546Spatrick //===-- FDInterposing.cpp ---------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick // This file helps with catching double close calls on unix integer file
10061da546Spatrick // descriptors by interposing functions for all file descriptor create and
11061da546Spatrick // close operations. A stack backtrace for every create and close function is
12061da546Spatrick // maintained, and every create and close operation is logged. When a double
13061da546Spatrick // file descriptor close is encountered, it will be logged.
14061da546Spatrick //
15061da546Spatrick // To enable the interposing in a darwin program, set the DYLD_INSERT_LIBRARIES
16061da546Spatrick // environment variable as follows:
17061da546Spatrick // For sh:
18061da546Spatrick // DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib /path/to/executable
19061da546Spatrick // For tcsh:
20061da546Spatrick // (setenv DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib ;
21061da546Spatrick // /path/to/executable)
22061da546Spatrick //
23061da546Spatrick // Other environment variables that can alter the default actions of this
24061da546Spatrick // interposing shared library include:
25061da546Spatrick //
26061da546Spatrick // "FileDescriptorStackLoggingNoCompact"
27061da546Spatrick //
28061da546Spatrick // With this environment variable set, all file descriptor create and
29061da546Spatrick // delete operations will be permanantly maintained in the event map.
30061da546Spatrick // The default action is to compact the create/delete events by removing
31061da546Spatrick // any previous file descriptor create events that are matched with a
32061da546Spatrick // corresponding file descriptor delete event when the next valid file
33061da546Spatrick // descriptor create event is detected.
34061da546Spatrick //
35061da546Spatrick // "FileDescriptorMinimalLogging"
36061da546Spatrick //
37061da546Spatrick // By default every file descriptor create and delete operation is logged
38061da546Spatrick // (to STDOUT by default, see the "FileDescriptorLogFile"). This can be
39061da546Spatrick // suppressed to only show errors and warnings by setting this environment
40061da546Spatrick // variable (the value in not important).
41061da546Spatrick //
42061da546Spatrick // "FileDescriptorLogFile=<path>"
43061da546Spatrick //
44061da546Spatrick // By default logging goes to STDOUT_FILENO, but this can be changed by
45061da546Spatrick // setting FileDescriptorLogFile. The value is a path to a file that
46061da546Spatrick // will be opened and used for logging.
47061da546Spatrick //===----------------------------------------------------------------------===//
48061da546Spatrick
49061da546Spatrick #include <assert.h>
50061da546Spatrick #include <dirent.h>
51061da546Spatrick #include <errno.h>
52061da546Spatrick #include <execinfo.h>
53061da546Spatrick #include <fcntl.h>
54061da546Spatrick #include <libgen.h>
55061da546Spatrick #include <mach-o/dyld-interposing.h>
56061da546Spatrick #include <mach-o/dyld.h>
57061da546Spatrick #include <map>
58061da546Spatrick #include <stdio.h>
59061da546Spatrick #include <stdlib.h>
60061da546Spatrick #include <string.h>
61061da546Spatrick #include <string>
62061da546Spatrick #include <sys/event.h>
63061da546Spatrick #include <sys/mman.h>
64061da546Spatrick #include <sys/socket.h>
65061da546Spatrick #include <sys/time.h>
66061da546Spatrick #include <sys/types.h>
67061da546Spatrick #include <tr1/memory>
68061da546Spatrick #include <unistd.h>
69061da546Spatrick #include <vector>
70061da546Spatrick
71061da546Spatrick extern "C" {
72061da546Spatrick int accept$NOCANCEL(int, struct sockaddr *__restrict, socklen_t *__restrict);
73061da546Spatrick int close$NOCANCEL(int);
74061da546Spatrick int open$NOCANCEL(const char *, int, ...);
75061da546Spatrick int __open_extended(const char *, int, uid_t, gid_t, int,
76061da546Spatrick struct kauth_filesec *);
77061da546Spatrick }
78061da546Spatrick
79061da546Spatrick namespace fd_interposing {
80061da546Spatrick
81061da546Spatrick // String class so we can get formatted strings without having to worry
82061da546Spatrick // about the memory storage since it will allocate the memory it needs.
83061da546Spatrick class String {
84061da546Spatrick public:
String()85061da546Spatrick String() : m_str(NULL) {}
86061da546Spatrick
String(const char * format,...)87061da546Spatrick String(const char *format, ...) : m_str(NULL) {
88061da546Spatrick va_list args;
89061da546Spatrick va_start(args, format);
90061da546Spatrick vprintf(format, args);
91061da546Spatrick va_end(args);
92061da546Spatrick }
93061da546Spatrick
~String()94061da546Spatrick ~String() { reset(); }
95061da546Spatrick
reset(char * s=NULL)96061da546Spatrick void reset(char *s = NULL) {
97061da546Spatrick if (m_str)
98061da546Spatrick ::free(m_str);
99061da546Spatrick m_str = s;
100061da546Spatrick }
101061da546Spatrick
c_str() const102061da546Spatrick const char *c_str() const { return m_str; }
103061da546Spatrick
printf(const char * format,...)104061da546Spatrick void printf(const char *format, ...) {
105061da546Spatrick va_list args;
106061da546Spatrick va_start(args, format);
107061da546Spatrick vprintf(format, args);
108061da546Spatrick va_end(args);
109061da546Spatrick }
vprintf(const char * format,va_list args)110061da546Spatrick void vprintf(const char *format, va_list args) {
111061da546Spatrick reset();
112061da546Spatrick ::vasprintf(&m_str, format, args);
113061da546Spatrick }
114061da546Spatrick
log(int log_fd)115061da546Spatrick void log(int log_fd) {
116061da546Spatrick if (m_str && log_fd >= 0) {
117061da546Spatrick const int len = strlen(m_str);
118061da546Spatrick if (len > 0) {
119061da546Spatrick write(log_fd, m_str, len);
120061da546Spatrick const char last_char = m_str[len - 1];
121061da546Spatrick if (!(last_char == '\n' || last_char == '\r'))
122061da546Spatrick write(log_fd, "\n", 1);
123061da546Spatrick }
124061da546Spatrick }
125061da546Spatrick }
126061da546Spatrick
127061da546Spatrick protected:
128061da546Spatrick char *m_str;
129061da546Spatrick
130061da546Spatrick private:
131*dda28197Spatrick String(const String &) = delete;
132*dda28197Spatrick const String &operator=(const String &) = delete;
133061da546Spatrick };
134061da546Spatrick
135061da546Spatrick // Type definitions
136061da546Spatrick typedef std::vector<void *> Frames;
137061da546Spatrick class FDEvent;
138061da546Spatrick typedef std::vector<void *> Frames;
139061da546Spatrick typedef std::tr1::shared_ptr<FDEvent> FDEventSP;
140061da546Spatrick typedef std::tr1::shared_ptr<String> StringSP;
141061da546Spatrick
142061da546Spatrick // FDEvent
143061da546Spatrick //
144*dda28197Spatrick // A class that describes a file descriptor event.
145061da546Spatrick //
146061da546Spatrick // File descriptor events fall into one of two categories: create events
147061da546Spatrick // and delete events.
148061da546Spatrick class FDEvent {
149061da546Spatrick public:
FDEvent(int fd,int err,const StringSP & string_sp,bool is_create,const Frames & frames)150061da546Spatrick FDEvent(int fd, int err, const StringSP &string_sp, bool is_create,
151061da546Spatrick const Frames &frames)
152061da546Spatrick : m_string_sp(string_sp), m_frames(frames.begin(), frames.end()),
153061da546Spatrick m_fd(fd), m_err(err), m_is_create(is_create) {}
154061da546Spatrick
~FDEvent()155061da546Spatrick ~FDEvent() {}
156061da546Spatrick
IsCreateEvent() const157061da546Spatrick bool IsCreateEvent() const { return m_is_create; }
158061da546Spatrick
IsDeleteEvent() const159061da546Spatrick bool IsDeleteEvent() const { return !m_is_create; }
160061da546Spatrick
GetFrames()161061da546Spatrick Frames &GetFrames() { return m_frames; }
162061da546Spatrick
GetFrames() const163061da546Spatrick const Frames &GetFrames() const { return m_frames; }
164061da546Spatrick
GetFD() const165061da546Spatrick int GetFD() const { return m_fd; }
166061da546Spatrick
GetError() const167061da546Spatrick int GetError() const { return m_err; }
168061da546Spatrick
169061da546Spatrick void Dump(int log_fd) const;
170061da546Spatrick
SetCreateEvent(FDEventSP & create_event_sp)171061da546Spatrick void SetCreateEvent(FDEventSP &create_event_sp) {
172061da546Spatrick m_create_event_sp = create_event_sp;
173061da546Spatrick }
174061da546Spatrick
175061da546Spatrick private:
176061da546Spatrick // A shared pointer to a String that describes this event in
177061da546Spatrick // detail (all args and return and error values)
178061da546Spatrick StringSP m_string_sp;
179061da546Spatrick // The frames for the stack backtrace for this event
180061da546Spatrick Frames m_frames;
181061da546Spatrick // If this is a file descriptor delete event, this might contain
182*dda28197Spatrick // the corresponding file descriptor create event
183061da546Spatrick FDEventSP m_create_event_sp;
184061da546Spatrick // The file descriptor for this event
185061da546Spatrick int m_fd;
186061da546Spatrick // The error code (if any) for this event
187061da546Spatrick int m_err;
188061da546Spatrick // True if this event is a file descriptor create event, false
189061da546Spatrick // if it is a file descriptor delete event
190061da546Spatrick bool m_is_create;
191061da546Spatrick };
192061da546Spatrick
193061da546Spatrick // Templatized class that will save errno only if the "value" it is
194061da546Spatrick // constructed with is equal to INVALID. When the class goes out of
195061da546Spatrick // scope, it will restore errno if it was saved.
196061da546Spatrick template <int INVALID> class Errno {
197061da546Spatrick public:
198061da546Spatrick // Save errno only if we are supposed to
Errno(int value)199061da546Spatrick Errno(int value)
200061da546Spatrick : m_saved_errno((value == INVALID) ? errno : 0),
201061da546Spatrick m_restore(value == INVALID) {}
202061da546Spatrick
203061da546Spatrick // Restore errno only if we are supposed to
~Errno()204061da546Spatrick ~Errno() {
205061da546Spatrick if (m_restore)
206061da546Spatrick errno = m_saved_errno;
207061da546Spatrick }
208061da546Spatrick
209061da546Spatrick // Accessor for the saved value of errno
get_errno() const210061da546Spatrick int get_errno() const { return m_saved_errno; }
211061da546Spatrick
212061da546Spatrick protected:
213061da546Spatrick const int m_saved_errno;
214061da546Spatrick const bool m_restore;
215061da546Spatrick };
216061da546Spatrick
217061da546Spatrick typedef Errno<-1> InvalidFDErrno;
218061da546Spatrick typedef Errno<-1> NegativeErrorErrno;
219061da546Spatrick typedef std::vector<FDEventSP> FDEventArray;
220061da546Spatrick typedef std::map<int, FDEventArray> FDEventMap;
221061da546Spatrick
222061da546Spatrick // Globals
223061da546Spatrick // Global event map that contains all file descriptor events. As file
224061da546Spatrick // descriptor create and close events come in, they will get filled
225061da546Spatrick // into this map (protected by g_mutex). When a file descriptor close
226061da546Spatrick // event is detected, the open event will be removed and placed into
227061da546Spatrick // the close event so if something tries to double close a file
228061da546Spatrick // descriptor we can show the previous close event and the file
229*dda28197Spatrick // descriptor event that created it. When a new file descriptor create
230061da546Spatrick // event comes in, we will remove the previous one for that file
231*dda28197Spatrick // descriptor unless the environment variable
232061da546Spatrick // "FileDescriptorStackLoggingNoCompact"
233*dda28197Spatrick // is set. The file descriptor history can be accessed using the
234061da546Spatrick // get_fd_history() function.
235061da546Spatrick static FDEventMap g_fd_event_map;
236061da546Spatrick // A mutex to protect access to our data structures in g_fd_event_map
237061da546Spatrick // and also our logging messages
238061da546Spatrick static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
239061da546Spatrick // Log all file descriptor create and close events by default. Only log
240*dda28197Spatrick // warnings and errors if the "FileDescriptorMinimalLogging" environment
241061da546Spatrick // variable is set.
242061da546Spatrick static int g_log_all_calls = 1;
243061da546Spatrick // We compact the file descriptor events by default. Set the environment
244061da546Spatrick // varible "FileDescriptorStackLoggingNoCompact" to keep a full history.
245061da546Spatrick static int g_compact = 1;
246061da546Spatrick // The current process ID
247061da546Spatrick static int g_pid = -1;
248061da546Spatrick static bool g_enabled = true;
249061da546Spatrick // Mutex class that will lock a mutex when it is constructed, and unlock
250061da546Spatrick // it when is goes out of scope
251061da546Spatrick class Locker {
252061da546Spatrick public:
Locker(pthread_mutex_t * mutex_ptr)253061da546Spatrick Locker(pthread_mutex_t *mutex_ptr) : m_mutex_ptr(mutex_ptr) {
254061da546Spatrick ::pthread_mutex_lock(m_mutex_ptr);
255061da546Spatrick }
256061da546Spatrick
257061da546Spatrick // This allows clients to test try and acquire the mutex...
Locker(pthread_mutex_t * mutex_ptr,bool & lock_acquired)258061da546Spatrick Locker(pthread_mutex_t *mutex_ptr, bool &lock_acquired) : m_mutex_ptr(NULL) {
259061da546Spatrick lock_acquired = ::pthread_mutex_trylock(mutex_ptr) == 0;
260061da546Spatrick if (lock_acquired)
261061da546Spatrick m_mutex_ptr = mutex_ptr;
262061da546Spatrick }
263061da546Spatrick
~Locker()264061da546Spatrick ~Locker() {
265061da546Spatrick if (m_mutex_ptr)
266061da546Spatrick ::pthread_mutex_unlock(m_mutex_ptr);
267061da546Spatrick }
268061da546Spatrick
269061da546Spatrick protected:
270061da546Spatrick pthread_mutex_t *m_mutex_ptr;
271061da546Spatrick };
272061da546Spatrick
273061da546Spatrick static void log(const char *format, ...) __attribute__((format(printf, 1, 2)));
274061da546Spatrick
275061da546Spatrick static void log(int log_fd, const FDEvent *event, const char *format, ...)
276061da546Spatrick __attribute__((format(printf, 3, 4)));
277061da546Spatrick
278061da546Spatrick static void backtrace_log(const char *format, ...)
279061da546Spatrick __attribute__((format(printf, 1, 2)));
280061da546Spatrick
281061da546Spatrick static void backtrace_error(const char *format, ...)
282061da546Spatrick __attribute__((format(printf, 1, 2)));
283061da546Spatrick
284061da546Spatrick static void log_to_fd(int log_fd, const char *format, ...)
285061da546Spatrick __attribute__((format(printf, 2, 3)));
286061da546Spatrick
get_backtrace(Frames & frame_buffer,size_t frames_to_remove)287061da546Spatrick static inline size_t get_backtrace(Frames &frame_buffer,
288061da546Spatrick size_t frames_to_remove) {
289061da546Spatrick void *frames[2048];
290061da546Spatrick int count = ::backtrace(&frames[0], sizeof(frames) / sizeof(void *));
291061da546Spatrick if (count > frames_to_remove)
292061da546Spatrick frame_buffer.assign(&frames[frames_to_remove], &frames[count]);
293061da546Spatrick else
294061da546Spatrick frame_buffer.assign(&frames[0], &frames[count]);
295061da546Spatrick while (frame_buffer.back() < (void *)1024)
296061da546Spatrick frame_buffer.pop_back();
297061da546Spatrick return frame_buffer.size();
298061da546Spatrick }
299061da546Spatrick
300061da546Spatrick static int g_log_fd = STDOUT_FILENO;
301061da546Spatrick static int g_initialized = 0;
302061da546Spatrick
get_process_fullpath(bool force=false)303061da546Spatrick const char *get_process_fullpath(bool force = false) {
304061da546Spatrick static char g_process_fullpath[PATH_MAX] = {0};
305061da546Spatrick if (force || g_process_fullpath[0] == '\0') {
306061da546Spatrick // If DST is NULL, then return the number of bytes needed.
307061da546Spatrick uint32_t len = sizeof(g_process_fullpath);
308061da546Spatrick if (_NSGetExecutablePath(g_process_fullpath, &len) != 0)
309061da546Spatrick strncpy(g_process_fullpath, "<error>", sizeof(g_process_fullpath));
310061da546Spatrick }
311061da546Spatrick return g_process_fullpath;
312061da546Spatrick }
313061da546Spatrick
314061da546Spatrick // Returns the current process ID, or -1 if inserposing not enabled for
315061da546Spatrick // this process
get_interposed_pid()316061da546Spatrick static int get_interposed_pid() {
317061da546Spatrick if (!g_enabled)
318061da546Spatrick return -1;
319061da546Spatrick
320061da546Spatrick const pid_t pid = getpid();
321061da546Spatrick if (g_pid != pid) {
322061da546Spatrick if (g_pid == -1) {
323061da546Spatrick g_pid = pid;
324061da546Spatrick log("Interposing file descriptor create and delete functions for %s "
325061da546Spatrick "(pid=%i)\n",
326061da546Spatrick get_process_fullpath(true), pid);
327061da546Spatrick } else {
328061da546Spatrick log("pid=%i: disabling interposing file descriptor create and delete "
329061da546Spatrick "functions for child process %s (pid=%i)\n",
330061da546Spatrick g_pid, get_process_fullpath(true), pid);
331061da546Spatrick g_enabled = false;
332061da546Spatrick return -1;
333061da546Spatrick }
334061da546Spatrick // Log when our process changes
335061da546Spatrick }
336061da546Spatrick return g_pid;
337061da546Spatrick }
338061da546Spatrick
get_logging_fd()339061da546Spatrick static int get_logging_fd() {
340061da546Spatrick if (!g_enabled)
341061da546Spatrick return -1;
342061da546Spatrick
343061da546Spatrick if (!g_initialized) {
344061da546Spatrick g_initialized = 1;
345061da546Spatrick
346061da546Spatrick const pid_t pid = get_interposed_pid();
347061da546Spatrick
348061da546Spatrick if (g_enabled) {
349061da546Spatrick // Keep all stack info around for all fd create and delete calls.
350061da546Spatrick // Otherwise we will remove the fd create call when a corresponding
351061da546Spatrick // fd delete call is received
352061da546Spatrick if (getenv("FileDescriptorStackLoggingNoCompact"))
353061da546Spatrick g_compact = 0;
354061da546Spatrick
355061da546Spatrick if (getenv("FileDescriptorMinimalLogging"))
356061da546Spatrick g_log_all_calls = 0;
357061da546Spatrick
358061da546Spatrick const char *log_path = getenv("FileDescriptorLogFile");
359061da546Spatrick if (log_path)
360061da546Spatrick g_log_fd = ::creat(log_path, 0660);
361061da546Spatrick else
362061da546Spatrick g_log_fd = STDOUT_FILENO;
363061da546Spatrick
364061da546Spatrick // Only let this interposing happen on the first time this matches
365061da546Spatrick // and stop this from happening so any child processes don't also
366061da546Spatrick // log their file descriptors
367061da546Spatrick ::unsetenv("DYLD_INSERT_LIBRARIES");
368061da546Spatrick } else {
369061da546Spatrick log("pid=%i: logging disabled\n", getpid());
370061da546Spatrick }
371061da546Spatrick }
372061da546Spatrick return g_log_fd;
373061da546Spatrick }
374061da546Spatrick
log_to_fd(int log_fd,const char * format,va_list args)375061da546Spatrick void log_to_fd(int log_fd, const char *format, va_list args) {
376061da546Spatrick if (format && format[0] && log_fd >= 0) {
377061da546Spatrick char buffer[PATH_MAX];
378061da546Spatrick const int count = ::vsnprintf(buffer, sizeof(buffer), format, args);
379061da546Spatrick if (count > 0)
380061da546Spatrick write(log_fd, buffer, count);
381061da546Spatrick }
382061da546Spatrick }
383061da546Spatrick
log_to_fd(int log_fd,const char * format,...)384061da546Spatrick void log_to_fd(int log_fd, const char *format, ...) {
385061da546Spatrick if (format && format[0]) {
386061da546Spatrick va_list args;
387061da546Spatrick va_start(args, format);
388061da546Spatrick log_to_fd(log_fd, format, args);
389061da546Spatrick va_end(args);
390061da546Spatrick }
391061da546Spatrick }
392061da546Spatrick
log(const char * format,va_list args)393061da546Spatrick void log(const char *format, va_list args) {
394061da546Spatrick log_to_fd(get_logging_fd(), format, args);
395061da546Spatrick }
396061da546Spatrick
log(const char * format,...)397061da546Spatrick void log(const char *format, ...) {
398061da546Spatrick if (format && format[0]) {
399061da546Spatrick va_list args;
400061da546Spatrick va_start(args, format);
401061da546Spatrick log(format, args);
402061da546Spatrick va_end(args);
403061da546Spatrick }
404061da546Spatrick }
405061da546Spatrick
log(int log_fd,const FDEvent * event,const char * format,...)406061da546Spatrick void log(int log_fd, const FDEvent *event, const char *format, ...) {
407061da546Spatrick if (format && format[0]) {
408061da546Spatrick va_list args;
409061da546Spatrick va_start(args, format);
410061da546Spatrick log_to_fd(log_fd, format, args);
411061da546Spatrick va_end(args);
412061da546Spatrick }
413061da546Spatrick if (event)
414061da546Spatrick event->Dump(log_fd);
415061da546Spatrick }
416061da546Spatrick
Dump(int log_fd) const417061da546Spatrick void FDEvent::Dump(int log_fd) const {
418061da546Spatrick if (log_fd >= 0) {
419061da546Spatrick log_to_fd(log_fd, "%s\n", m_string_sp->c_str());
420061da546Spatrick if (!m_frames.empty())
421061da546Spatrick ::backtrace_symbols_fd(m_frames.data(), m_frames.size(), log_fd);
422061da546Spatrick
423061da546Spatrick if (m_create_event_sp) {
424061da546Spatrick log_to_fd(log_fd, "\nfd=%i was created with this event:\n", m_fd);
425061da546Spatrick m_create_event_sp->Dump(log_fd);
426061da546Spatrick log_to_fd(log_fd, "\n");
427061da546Spatrick }
428061da546Spatrick }
429061da546Spatrick }
430061da546Spatrick
backtrace_log(const char * format,...)431061da546Spatrick void backtrace_log(const char *format, ...) {
432061da546Spatrick const int log_fd = get_logging_fd();
433061da546Spatrick if (log_fd >= 0) {
434061da546Spatrick if (format && format[0]) {
435061da546Spatrick va_list args;
436061da546Spatrick va_start(args, format);
437061da546Spatrick log(format, args);
438061da546Spatrick va_end(args);
439061da546Spatrick }
440061da546Spatrick
441061da546Spatrick Frames frames;
442061da546Spatrick if (get_backtrace(frames, 2))
443061da546Spatrick ::backtrace_symbols_fd(frames.data(), frames.size(), log_fd);
444061da546Spatrick }
445061da546Spatrick }
446061da546Spatrick
backtrace_error(const char * format,...)447061da546Spatrick void backtrace_error(const char *format, ...) {
448061da546Spatrick const int pid = get_interposed_pid();
449061da546Spatrick if (pid >= 0) {
450061da546Spatrick const int log_fd = get_logging_fd();
451061da546Spatrick if (log_fd >= 0) {
452061da546Spatrick log("\nerror: %s (pid=%i): ", get_process_fullpath(), pid);
453061da546Spatrick
454061da546Spatrick if (format && format[0]) {
455061da546Spatrick va_list args;
456061da546Spatrick va_start(args, format);
457061da546Spatrick log(format, args);
458061da546Spatrick va_end(args);
459061da546Spatrick }
460061da546Spatrick
461061da546Spatrick Frames frames;
462061da546Spatrick if (get_backtrace(frames, 2))
463061da546Spatrick ::backtrace_symbols_fd(frames.data(), frames.size(), log_fd);
464061da546Spatrick }
465061da546Spatrick }
466061da546Spatrick }
467061da546Spatrick
save_backtrace(int fd,int err,const StringSP & string_sp,bool is_create)468061da546Spatrick void save_backtrace(int fd, int err, const StringSP &string_sp,
469061da546Spatrick bool is_create) {
470061da546Spatrick Frames frames;
471061da546Spatrick get_backtrace(frames, 2);
472061da546Spatrick
473061da546Spatrick FDEventSP fd_event_sp(new FDEvent(fd, err, string_sp, is_create, frames));
474061da546Spatrick
475061da546Spatrick FDEventMap::iterator pos = g_fd_event_map.find(fd);
476061da546Spatrick
477061da546Spatrick if (pos != g_fd_event_map.end()) {
478061da546Spatrick // We have history for this fd...
479061da546Spatrick
480061da546Spatrick FDEventArray &event_array = g_fd_event_map[fd];
481061da546Spatrick if (fd_event_sp->IsCreateEvent()) {
482061da546Spatrick // The current fd event is a function that creates
483061da546Spatrick // a descriptor, check in case last event was
484061da546Spatrick // a create event.
485061da546Spatrick if (event_array.back()->IsCreateEvent()) {
486061da546Spatrick const int log_fd = get_logging_fd();
487061da546Spatrick // Two fd create functions in a row, we missed
488061da546Spatrick // a function that closes a fd...
489061da546Spatrick log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "
490061da546Spatrick "create event fd=%i (we missed a file "
491061da546Spatrick "descriptor close event):\n",
492061da546Spatrick fd);
493061da546Spatrick } else if (g_compact) {
494061da546Spatrick // We are compacting so we remove previous create event
495*dda28197Spatrick // when we get the corresponding delete event
496061da546Spatrick event_array.pop_back();
497061da546Spatrick }
498061da546Spatrick } else {
499061da546Spatrick // The current fd event is a function that deletes
500061da546Spatrick // a descriptor, check in case last event for this
501061da546Spatrick // fd was a delete event (double close!)
502061da546Spatrick if (event_array.back()->IsDeleteEvent()) {
503061da546Spatrick const int log_fd = get_logging_fd();
504061da546Spatrick // Two fd delete functions in a row, we must
505061da546Spatrick // have missed some function that opened a descriptor
506061da546Spatrick log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "
507061da546Spatrick "close event for fd=%d (we missed the "
508061da546Spatrick "file descriptor create event):\n",
509061da546Spatrick fd);
510061da546Spatrick } else if (g_compact) {
511061da546Spatrick // Since this is a close event, we want to remember the open event
512061da546Spatrick // that this close if for...
513061da546Spatrick fd_event_sp->SetCreateEvent(event_array.back());
514061da546Spatrick // We are compacting so we remove previous create event
515*dda28197Spatrick // when we get the corresponding delete event
516061da546Spatrick event_array.pop_back();
517061da546Spatrick }
518061da546Spatrick }
519061da546Spatrick
520061da546Spatrick event_array.push_back(fd_event_sp);
521061da546Spatrick } else {
522061da546Spatrick g_fd_event_map[fd].push_back(fd_event_sp);
523061da546Spatrick }
524061da546Spatrick }
525061da546Spatrick
526061da546Spatrick // socket() interpose function
socket$__interposed__(int domain,int type,int protocol)527061da546Spatrick extern "C" int socket$__interposed__(int domain, int type, int protocol) {
528061da546Spatrick const int pid = get_interposed_pid();
529061da546Spatrick if (pid >= 0) {
530061da546Spatrick Locker locker(&g_mutex);
531061da546Spatrick const int fd = ::socket(domain, type, protocol);
532061da546Spatrick InvalidFDErrno fd_errno(fd);
533061da546Spatrick StringSP description_sp(new String);
534061da546Spatrick if (fd == -1)
535061da546Spatrick description_sp->printf("pid=%i: socket (domain = %i, type = %i, protocol "
536061da546Spatrick "= %i) => fd=%i errno = %i",
537061da546Spatrick pid, domain, type, protocol, fd,
538061da546Spatrick fd_errno.get_errno());
539061da546Spatrick else
540061da546Spatrick description_sp->printf(
541061da546Spatrick "pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i",
542061da546Spatrick pid, domain, type, protocol, fd);
543061da546Spatrick if (g_log_all_calls)
544061da546Spatrick description_sp->log(get_logging_fd());
545061da546Spatrick if (fd >= 0)
546061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
547061da546Spatrick return fd;
548061da546Spatrick } else {
549061da546Spatrick return ::socket(domain, type, protocol);
550061da546Spatrick }
551061da546Spatrick }
552061da546Spatrick
553061da546Spatrick // socketpair() interpose function
socketpair$__interposed__(int domain,int type,int protocol,int fds[2])554061da546Spatrick extern "C" int socketpair$__interposed__(int domain, int type, int protocol,
555061da546Spatrick int fds[2]) {
556061da546Spatrick const int pid = get_interposed_pid();
557061da546Spatrick if (pid >= 0) {
558061da546Spatrick Locker locker(&g_mutex);
559061da546Spatrick fds[0] = -1;
560061da546Spatrick fds[1] = -1;
561061da546Spatrick const int err = socketpair(domain, type, protocol, fds);
562061da546Spatrick NegativeErrorErrno err_errno(err);
563061da546Spatrick StringSP description_sp(
564061da546Spatrick new String("pid=%i: socketpair (domain=%i, type=%i, protocol=%i, "
565061da546Spatrick "{fd=%i, fd=%i}) -> err=%i",
566061da546Spatrick pid, domain, type, protocol, fds[0], fds[1], err));
567061da546Spatrick if (g_log_all_calls)
568061da546Spatrick description_sp->log(get_logging_fd());
569061da546Spatrick if (fds[0] >= 0)
570061da546Spatrick save_backtrace(fds[0], err_errno.get_errno(), description_sp, true);
571061da546Spatrick if (fds[1] >= 0)
572061da546Spatrick save_backtrace(fds[1], err_errno.get_errno(), description_sp, true);
573061da546Spatrick return err;
574061da546Spatrick } else {
575061da546Spatrick return socketpair(domain, type, protocol, fds);
576061da546Spatrick }
577061da546Spatrick }
578061da546Spatrick
579061da546Spatrick // open() interpose function
open$__interposed__(const char * path,int oflag,int mode)580061da546Spatrick extern "C" int open$__interposed__(const char *path, int oflag, int mode) {
581061da546Spatrick const int pid = get_interposed_pid();
582061da546Spatrick if (pid >= 0) {
583061da546Spatrick Locker locker(&g_mutex);
584061da546Spatrick int fd = -2;
585061da546Spatrick StringSP description_sp(new String);
586061da546Spatrick if (oflag & O_CREAT) {
587061da546Spatrick fd = ::open(path, oflag, mode);
588061da546Spatrick description_sp->printf(
589061da546Spatrick "pid=%i: open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,
590061da546Spatrick path, oflag, mode, fd);
591061da546Spatrick } else {
592061da546Spatrick fd = ::open(path, oflag);
593061da546Spatrick description_sp->printf("pid=%i: open (path = '%s', oflag = %i) -> fd=%i",
594061da546Spatrick pid, path, oflag, fd);
595061da546Spatrick }
596061da546Spatrick
597061da546Spatrick InvalidFDErrno fd_errno(fd);
598061da546Spatrick if (g_log_all_calls)
599061da546Spatrick description_sp->log(get_logging_fd());
600061da546Spatrick if (fd >= 0)
601061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
602061da546Spatrick return fd;
603061da546Spatrick } else {
604061da546Spatrick return ::open(path, oflag, mode);
605061da546Spatrick }
606061da546Spatrick }
607061da546Spatrick
608061da546Spatrick // open$NOCANCEL() interpose function
open$NOCANCEL$__interposed__(const char * path,int oflag,int mode)609061da546Spatrick extern "C" int open$NOCANCEL$__interposed__(const char *path, int oflag,
610061da546Spatrick int mode) {
611061da546Spatrick const int pid = get_interposed_pid();
612061da546Spatrick if (pid >= 0) {
613061da546Spatrick Locker locker(&g_mutex);
614061da546Spatrick const int fd = ::open$NOCANCEL(path, oflag, mode);
615061da546Spatrick InvalidFDErrno fd_errno(fd);
616061da546Spatrick StringSP description_sp(new String(
617061da546Spatrick "pid=%i: open$NOCANCEL (path = '%s', oflag = %i, mode = %i) -> fd=%i",
618061da546Spatrick pid, path, oflag, mode, fd));
619061da546Spatrick if (g_log_all_calls)
620061da546Spatrick description_sp->log(get_logging_fd());
621061da546Spatrick if (fd >= 0)
622061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
623061da546Spatrick return fd;
624061da546Spatrick } else {
625061da546Spatrick return ::open$NOCANCEL(path, oflag, mode);
626061da546Spatrick }
627061da546Spatrick }
628061da546Spatrick
629061da546Spatrick // __open_extended() interpose function
__open_extended$__interposed__(const char * path,int oflag,uid_t uid,gid_t gid,int mode,struct kauth_filesec * fsacl)630061da546Spatrick extern "C" int __open_extended$__interposed__(const char *path, int oflag,
631061da546Spatrick uid_t uid, gid_t gid, int mode,
632061da546Spatrick struct kauth_filesec *fsacl) {
633061da546Spatrick const int pid = get_interposed_pid();
634061da546Spatrick if (pid >= 0) {
635061da546Spatrick Locker locker(&g_mutex);
636061da546Spatrick const int fd = ::__open_extended(path, oflag, uid, gid, mode, fsacl);
637061da546Spatrick InvalidFDErrno fd_errno(fd);
638061da546Spatrick StringSP description_sp(
639061da546Spatrick new String("pid=%i: __open_extended (path='%s', oflag=%i, uid=%i, "
640061da546Spatrick "gid=%i, mode=%i, fsacl=%p) -> fd=%i",
641061da546Spatrick pid, path, oflag, uid, gid, mode, fsacl, fd));
642061da546Spatrick if (g_log_all_calls)
643061da546Spatrick description_sp->log(get_logging_fd());
644061da546Spatrick if (fd >= 0)
645061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
646061da546Spatrick return fd;
647061da546Spatrick } else {
648061da546Spatrick return ::__open_extended(path, oflag, uid, gid, mode, fsacl);
649061da546Spatrick }
650061da546Spatrick }
651061da546Spatrick
652061da546Spatrick // kqueue() interpose function
kqueue$__interposed__(void)653061da546Spatrick extern "C" int kqueue$__interposed__(void) {
654061da546Spatrick const int pid = get_interposed_pid();
655061da546Spatrick if (pid >= 0) {
656061da546Spatrick Locker locker(&g_mutex);
657061da546Spatrick const int fd = ::kqueue();
658061da546Spatrick InvalidFDErrno fd_errno(fd);
659061da546Spatrick StringSP description_sp(new String("pid=%i: kqueue () -> fd=%i", pid, fd));
660061da546Spatrick if (g_log_all_calls)
661061da546Spatrick description_sp->log(get_logging_fd());
662061da546Spatrick if (fd >= 0)
663061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
664061da546Spatrick return fd;
665061da546Spatrick } else {
666061da546Spatrick return ::kqueue();
667061da546Spatrick }
668061da546Spatrick }
669061da546Spatrick
670061da546Spatrick // shm_open() interpose function
shm_open$__interposed__(const char * path,int oflag,int mode)671061da546Spatrick extern "C" int shm_open$__interposed__(const char *path, int oflag, int mode) {
672061da546Spatrick const int pid = get_interposed_pid();
673061da546Spatrick if (pid >= 0) {
674061da546Spatrick Locker locker(&g_mutex);
675061da546Spatrick const int fd = ::shm_open(path, oflag, mode);
676061da546Spatrick InvalidFDErrno fd_errno(fd);
677061da546Spatrick StringSP description_sp(new String(
678061da546Spatrick "pid=%i: shm_open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,
679061da546Spatrick path, oflag, mode, fd));
680061da546Spatrick if (g_log_all_calls)
681061da546Spatrick description_sp->log(get_logging_fd());
682061da546Spatrick if (fd >= 0)
683061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
684061da546Spatrick return fd;
685061da546Spatrick } else {
686061da546Spatrick return ::shm_open(path, oflag, mode);
687061da546Spatrick }
688061da546Spatrick }
689061da546Spatrick
690061da546Spatrick // accept() interpose function
accept$__interposed__(int socket,struct sockaddr * address,socklen_t * address_len)691061da546Spatrick extern "C" int accept$__interposed__(int socket, struct sockaddr *address,
692061da546Spatrick socklen_t *address_len) {
693061da546Spatrick const int pid = get_interposed_pid();
694061da546Spatrick if (pid >= 0) {
695061da546Spatrick Locker locker(&g_mutex);
696061da546Spatrick const int fd = ::accept(socket, address, address_len);
697061da546Spatrick InvalidFDErrno fd_errno(fd);
698061da546Spatrick StringSP description_sp(new String(
699061da546Spatrick "pid=%i: accept (socket=%i, ...) -> fd=%i", pid, socket, fd));
700061da546Spatrick if (g_log_all_calls)
701061da546Spatrick description_sp->log(get_logging_fd());
702061da546Spatrick if (fd >= 0)
703061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
704061da546Spatrick return fd;
705061da546Spatrick } else {
706061da546Spatrick return ::accept(socket, address, address_len);
707061da546Spatrick }
708061da546Spatrick }
709061da546Spatrick
710061da546Spatrick // accept$NOCANCEL() interpose function
accept$NOCANCEL$__interposed__(int socket,struct sockaddr * address,socklen_t * address_len)711061da546Spatrick extern "C" int accept$NOCANCEL$__interposed__(int socket,
712061da546Spatrick struct sockaddr *address,
713061da546Spatrick socklen_t *address_len) {
714061da546Spatrick const int pid = get_interposed_pid();
715061da546Spatrick if (pid >= 0) {
716061da546Spatrick Locker locker(&g_mutex);
717061da546Spatrick const int fd = ::accept$NOCANCEL(socket, address, address_len);
718061da546Spatrick InvalidFDErrno fd_errno(fd);
719061da546Spatrick StringSP description_sp(new String(
720061da546Spatrick "pid=%i: accept$NOCANCEL (socket=%i, ...) -> fd=%i", pid, socket, fd));
721061da546Spatrick if (g_log_all_calls)
722061da546Spatrick description_sp->log(get_logging_fd());
723061da546Spatrick if (fd >= 0)
724061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
725061da546Spatrick return fd;
726061da546Spatrick } else {
727061da546Spatrick return ::accept$NOCANCEL(socket, address, address_len);
728061da546Spatrick }
729061da546Spatrick }
730061da546Spatrick
731061da546Spatrick // dup() interpose function
dup$__interposed__(int fd2)732061da546Spatrick extern "C" int dup$__interposed__(int fd2) {
733061da546Spatrick const int pid = get_interposed_pid();
734061da546Spatrick if (pid >= 0) {
735061da546Spatrick Locker locker(&g_mutex);
736061da546Spatrick const int fd = ::dup(fd2);
737061da546Spatrick InvalidFDErrno fd_errno(fd);
738061da546Spatrick StringSP description_sp(
739061da546Spatrick new String("pid=%i: dup (fd2=%i) -> fd=%i", pid, fd2, fd));
740061da546Spatrick if (g_log_all_calls)
741061da546Spatrick description_sp->log(get_logging_fd());
742061da546Spatrick if (fd >= 0)
743061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
744061da546Spatrick return fd;
745061da546Spatrick } else {
746061da546Spatrick return ::dup(fd2);
747061da546Spatrick }
748061da546Spatrick }
749061da546Spatrick
750061da546Spatrick // dup2() interpose function
dup2$__interposed__(int fd1,int fd2)751061da546Spatrick extern "C" int dup2$__interposed__(int fd1, int fd2) {
752061da546Spatrick const int pid = get_interposed_pid();
753061da546Spatrick if (pid >= 0) {
754061da546Spatrick Locker locker(&g_mutex);
755061da546Spatrick // If "fd2" is already opened, it will be closed during the
756061da546Spatrick // dup2 call below, so we need to see if we have fd2 in our
757061da546Spatrick // open map and treat it as a close(fd2)
758061da546Spatrick FDEventMap::iterator pos = g_fd_event_map.find(fd2);
759061da546Spatrick StringSP dup2_close_description_sp(
760061da546Spatrick new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> will close (fd=%i)", pid,
761061da546Spatrick fd1, fd2, fd2));
762061da546Spatrick if (pos != g_fd_event_map.end() && pos->second.back()->IsCreateEvent())
763061da546Spatrick save_backtrace(fd2, 0, dup2_close_description_sp, false);
764061da546Spatrick
765061da546Spatrick const int fd = ::dup2(fd1, fd2);
766061da546Spatrick InvalidFDErrno fd_errno(fd);
767061da546Spatrick StringSP description_sp(new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> fd=%i",
768061da546Spatrick pid, fd1, fd2, fd));
769061da546Spatrick if (g_log_all_calls)
770061da546Spatrick description_sp->log(get_logging_fd());
771061da546Spatrick
772061da546Spatrick if (fd >= 0)
773061da546Spatrick save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
774061da546Spatrick return fd;
775061da546Spatrick } else {
776061da546Spatrick return ::dup2(fd1, fd2);
777061da546Spatrick }
778061da546Spatrick }
779061da546Spatrick
780061da546Spatrick // close() interpose function
close$__interposed__(int fd)781061da546Spatrick extern "C" int close$__interposed__(int fd) {
782061da546Spatrick const int pid = get_interposed_pid();
783061da546Spatrick if (pid >= 0) {
784061da546Spatrick Locker locker(&g_mutex);
785061da546Spatrick const int err = close(fd);
786061da546Spatrick NegativeErrorErrno err_errno(err);
787061da546Spatrick StringSP description_sp(new String);
788061da546Spatrick if (err == -1)
789061da546Spatrick description_sp->printf("pid=%i: close (fd=%i) => %i errno = %i (%s))",
790061da546Spatrick pid, fd, err, err_errno.get_errno(),
791061da546Spatrick strerror(err_errno.get_errno()));
792061da546Spatrick else
793061da546Spatrick description_sp->printf("pid=%i: close (fd=%i) => %i", pid, fd, err);
794061da546Spatrick if (g_log_all_calls)
795061da546Spatrick description_sp->log(get_logging_fd());
796061da546Spatrick
797061da546Spatrick if (err == 0) {
798061da546Spatrick if (fd >= 0)
799061da546Spatrick save_backtrace(fd, err, description_sp, false);
800061da546Spatrick } else if (err == -1) {
801061da546Spatrick if (err_errno.get_errno() == EBADF && fd != -1) {
802061da546Spatrick backtrace_error("close (fd=%d) resulted in EBADF:\n", fd);
803061da546Spatrick
804061da546Spatrick FDEventMap::iterator pos = g_fd_event_map.find(fd);
805061da546Spatrick if (pos != g_fd_event_map.end()) {
806061da546Spatrick log(get_logging_fd(), pos->second.back().get(),
807061da546Spatrick "\nfd=%d was previously %s with this event:\n", fd,
808061da546Spatrick pos->second.back()->IsCreateEvent() ? "opened" : "closed");
809061da546Spatrick }
810061da546Spatrick }
811061da546Spatrick }
812061da546Spatrick return err;
813061da546Spatrick } else {
814061da546Spatrick return close(fd);
815061da546Spatrick }
816061da546Spatrick }
817061da546Spatrick
818061da546Spatrick // close$NOCANCEL() interpose function
close$NOCANCEL$__interposed__(int fd)819061da546Spatrick extern "C" int close$NOCANCEL$__interposed__(int fd) {
820061da546Spatrick const int pid = get_interposed_pid();
821061da546Spatrick if (pid >= 0) {
822061da546Spatrick Locker locker(&g_mutex);
823061da546Spatrick const int err = close$NOCANCEL(fd);
824061da546Spatrick NegativeErrorErrno err_errno(err);
825061da546Spatrick StringSP description_sp(new String);
826061da546Spatrick if (err == -1)
827061da546Spatrick description_sp->printf(
828061da546Spatrick "pid=%i: close$NOCANCEL (fd=%i) => %i errno = %i (%s))", pid, fd, err,
829061da546Spatrick err_errno.get_errno(), strerror(err_errno.get_errno()));
830061da546Spatrick else
831061da546Spatrick description_sp->printf("pid=%i: close$NOCANCEL (fd=%i) => %i", pid, fd,
832061da546Spatrick err);
833061da546Spatrick if (g_log_all_calls)
834061da546Spatrick description_sp->log(get_logging_fd());
835061da546Spatrick
836061da546Spatrick if (err == 0) {
837061da546Spatrick if (fd >= 0)
838061da546Spatrick save_backtrace(fd, err, description_sp, false);
839061da546Spatrick } else if (err == -1) {
840061da546Spatrick if (err_errno.get_errno() == EBADF && fd != -1) {
841061da546Spatrick backtrace_error("close$NOCANCEL (fd=%d) resulted in EBADF\n:", fd);
842061da546Spatrick
843061da546Spatrick FDEventMap::iterator pos = g_fd_event_map.find(fd);
844061da546Spatrick if (pos != g_fd_event_map.end()) {
845061da546Spatrick log(get_logging_fd(), pos->second.back().get(),
846061da546Spatrick "\nfd=%d was previously %s with this event:\n", fd,
847061da546Spatrick pos->second.back()->IsCreateEvent() ? "opened" : "closed");
848061da546Spatrick }
849061da546Spatrick }
850061da546Spatrick }
851061da546Spatrick return err;
852061da546Spatrick } else {
853061da546Spatrick return close$NOCANCEL(fd);
854061da546Spatrick }
855061da546Spatrick }
856061da546Spatrick
857061da546Spatrick // pipe() interpose function
pipe$__interposed__(int fds[2])858061da546Spatrick extern "C" int pipe$__interposed__(int fds[2]) {
859061da546Spatrick const int pid = get_interposed_pid();
860061da546Spatrick if (pid >= 0) {
861061da546Spatrick Locker locker(&g_mutex);
862061da546Spatrick fds[0] = -1;
863061da546Spatrick fds[1] = -1;
864061da546Spatrick const int err = pipe(fds);
865061da546Spatrick const int saved_errno = errno;
866061da546Spatrick StringSP description_sp(new String(
867061da546Spatrick "pid=%i: pipe ({fd=%i, fd=%i}) -> err=%i", pid, fds[0], fds[1], err));
868061da546Spatrick if (g_log_all_calls)
869061da546Spatrick description_sp->log(get_logging_fd());
870061da546Spatrick if (fds[0] >= 0)
871061da546Spatrick save_backtrace(fds[0], saved_errno, description_sp, true);
872061da546Spatrick if (fds[1] >= 0)
873061da546Spatrick save_backtrace(fds[1], saved_errno, description_sp, true);
874061da546Spatrick errno = saved_errno;
875061da546Spatrick return err;
876061da546Spatrick } else {
877061da546Spatrick return pipe(fds);
878061da546Spatrick }
879061da546Spatrick }
880061da546Spatrick
881061da546Spatrick // get_fd_history()
882061da546Spatrick //
883061da546Spatrick // This function allows runtime access to the file descriptor history.
884061da546Spatrick //
885061da546Spatrick // @param[in] log_fd
886061da546Spatrick // The file descriptor to log to
887061da546Spatrick //
888061da546Spatrick // @param[in] fd
889061da546Spatrick // The file descriptor whose history should be dumped
get_fd_history(int log_fd,int fd)890061da546Spatrick extern "C" void get_fd_history(int log_fd, int fd) {
891061da546Spatrick // "create" below needs to be outside of the mutex locker scope
892061da546Spatrick if (log_fd >= 0) {
893061da546Spatrick bool got_lock = false;
894061da546Spatrick Locker locker(&g_mutex, got_lock);
895061da546Spatrick if (got_lock) {
896061da546Spatrick FDEventMap::iterator pos = g_fd_event_map.find(fd);
897061da546Spatrick log_to_fd(log_fd, "Dumping file descriptor history for fd=%i:\n", fd);
898061da546Spatrick if (pos != g_fd_event_map.end()) {
899061da546Spatrick FDEventArray &event_array = g_fd_event_map[fd];
900061da546Spatrick const size_t num_events = event_array.size();
901061da546Spatrick for (size_t i = 0; i < num_events; ++i)
902061da546Spatrick event_array[i]->Dump(log_fd);
903061da546Spatrick } else {
904061da546Spatrick log_to_fd(log_fd, "error: no file descriptor events found for fd=%i\n",
905061da546Spatrick fd);
906061da546Spatrick }
907061da546Spatrick } else {
908061da546Spatrick log_to_fd(log_fd, "error: fd event mutex is locked...\n");
909061da546Spatrick }
910061da546Spatrick }
911061da546Spatrick }
912061da546Spatrick
913061da546Spatrick // Interposing
914061da546Spatrick // FD creation routines
915061da546Spatrick DYLD_INTERPOSE(accept$__interposed__, accept);
916061da546Spatrick DYLD_INTERPOSE(accept$NOCANCEL$__interposed__, accept$NOCANCEL);
917061da546Spatrick DYLD_INTERPOSE(dup$__interposed__, dup);
918061da546Spatrick DYLD_INTERPOSE(dup2$__interposed__, dup2);
919061da546Spatrick DYLD_INTERPOSE(kqueue$__interposed__, kqueue);
920061da546Spatrick DYLD_INTERPOSE(open$__interposed__, open);
921061da546Spatrick DYLD_INTERPOSE(open$NOCANCEL$__interposed__, open$NOCANCEL);
922061da546Spatrick DYLD_INTERPOSE(__open_extended$__interposed__, __open_extended);
923061da546Spatrick DYLD_INTERPOSE(pipe$__interposed__, pipe);
924061da546Spatrick DYLD_INTERPOSE(shm_open$__interposed__, shm_open);
925061da546Spatrick DYLD_INTERPOSE(socket$__interposed__, socket);
926061da546Spatrick DYLD_INTERPOSE(socketpair$__interposed__, socketpair);
927061da546Spatrick
928061da546Spatrick // FD deleting routines
929061da546Spatrick DYLD_INTERPOSE(close$__interposed__, close);
930061da546Spatrick DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL);
931061da546Spatrick
932061da546Spatrick } // namespace fd_interposing
933