xref: /openbsd-src/gnu/llvm/lldb/examples/interposing/darwin/fd_interposing/FDInterposing.cpp (revision dda2819751e49c83612958492e38917049128b41)
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