2012-01-10 08:29:36 +08:00
//===-- FDInterposing.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file helps with catching double close calls on unix integer file
// descriptors by interposing functions for all file descriptor create and
// close operations. A stack backtrace for every create and close function is
// maintained, and every create and close operation is logged. When a double
// file descriptor close is encountered, it will be logged.
//
// To enable the interposing in a darwin program, set the DYLD_INSERT_LIBRARIES
// environment variable as follows:
// For sh:
// DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib /path/to/executable
// For tcsh:
// (setenv DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib ; /path/to/executable)
//
// Other environment variables that can alter the default actions of this
// interposing shared library include:
//
// "FileDescriptorStackLoggingNoCompact"
//
// With this environment variable set, all file descriptor create and
// delete operations will be permanantly maintained in the event map.
// The default action is to compact the create/delete events by removing
// any previous file descriptor create events that are matched with a
// corresponding file descriptor delete event when the next valid file
// descriptor create event is detected.
//
// "FileDescriptorMinimalLogging"
//
// By default every file descriptor create and delete operation is logged
// (to STDOUT by default, see the "FileDescriptorLogFile"). This can be
// suppressed to only show errors and warnings by setting this environment
// variable (the value in not important).
//
// "FileDescriptorLogFile=<path>"
//
// By default logging goes to STDOUT_FILENO, but this can be changed by
// setting FileDescriptorLogFile. The value is a path to a file that
// will be opened and used for logging.
//===----------------------------------------------------------------------===//
# include <assert.h>
# include <dirent.h>
# include <errno.h>
# include <fcntl.h>
# include <execinfo.h>
# include <libgen.h>
# include <mach-o/dyld.h>
# include <mach-o/dyld-interposing.h>
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <sys/event.h>
# include <sys/mman.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <sys/time.h>
2012-02-22 03:02:29 +08:00
# include <tr1/memory> // for std::tr1::shared_ptr
2012-01-10 08:29:36 +08:00
# include <unistd.h>
# include <string>
# include <vector>
# include <map>
//----------------------------------------------------------------------
/// @def DISALLOW_COPY_AND_ASSIGN(TypeName)
/// Macro definition for easily disallowing copy constructor and
/// assignment operators in C++ classes.
//----------------------------------------------------------------------
# define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName ( const TypeName & ) ; \
const TypeName & operator = ( const TypeName & )
2012-01-17 06:21:29 +08:00
extern " C " {
int accept $ NOCANCEL ( int , struct sockaddr * __restrict , socklen_t * __restrict ) ;
int close $ NOCANCEL ( int ) ;
int open $ NOCANCEL ( const char * , int , . . . ) ;
int __open_extended ( const char * , int , uid_t , gid_t , int , struct kauth_filesec * ) ;
}
2012-01-10 08:29:36 +08:00
namespace fd_interposing {
//----------------------------------------------------------------------
// String class so we can get formatted strings without having to worry
// about the memory storage since it will allocate the memory it needs.
//----------------------------------------------------------------------
class String
{
public :
String ( ) :
m_str ( NULL )
{ }
String ( const char * format , . . . ) :
m_str ( NULL )
{
va_list args ;
va_start ( args , format ) ;
vprintf ( format , args ) ;
va_end ( args ) ;
}
~ String ( )
{
reset ( ) ;
}
void
reset ( char * s = NULL )
{
if ( m_str )
: : free ( m_str ) ;
m_str = s ;
}
const char *
c_str ( ) const
{
return m_str ;
}
void
printf ( const char * format , . . . )
{
va_list args ;
va_start ( args , format ) ;
vprintf ( format , args ) ;
va_end ( args ) ;
}
void
vprintf ( const char * format , va_list args )
{
reset ( ) ;
: : vasprintf ( & m_str , format , args ) ;
}
void
log ( int log_fd )
{
if ( m_str & & log_fd > = 0 )
{
const int len = strlen ( m_str ) ;
if ( len > 0 )
{
write ( log_fd , m_str , len ) ;
const char last_char = m_str [ len - 1 ] ;
if ( ! ( last_char = = ' \n ' | | last_char = = ' \r ' ) )
write ( log_fd , " \n " , 1 ) ;
}
}
}
protected :
char * m_str ;
private :
DISALLOW_COPY_AND_ASSIGN ( String ) ;
} ;
//----------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------
typedef std : : vector < void * > Frames ;
class FDEvent ;
typedef std : : vector < void * > Frames ;
2012-02-22 03:02:29 +08:00
typedef std : : tr1 : : shared_ptr < FDEvent > FDEventSP ;
typedef std : : tr1 : : shared_ptr < String > StringSP ;
2012-01-10 08:29:36 +08:00
//----------------------------------------------------------------------
// FDEvent
//
// A class that describes a file desciptor event.
//
// File descriptor events fall into one of two categories: create events
// and delete events.
//----------------------------------------------------------------------
class FDEvent
{
public :
FDEvent ( int fd , int err , const StringSP & string_sp , bool is_create , const Frames & frames ) :
m_string_sp ( string_sp ) ,
m_frames ( frames . begin ( ) , frames . end ( ) ) ,
m_fd ( fd ) ,
m_err ( err ) ,
m_is_create ( is_create )
{ }
~ FDEvent ( ) { }
bool
IsCreateEvent ( ) const
{
return m_is_create ;
}
bool
IsDeleteEvent ( ) const
{
return ! m_is_create ;
}
Frames &
GetFrames ( )
{
return m_frames ;
}
const Frames &
GetFrames ( ) const
{
return m_frames ;
}
int
GetFD ( ) const
{
return m_fd ;
}
int
GetError ( ) const
{
return m_err ;
}
void
Dump ( int log_fd ) const ;
void
SetCreateEvent ( FDEventSP & create_event_sp )
{
m_create_event_sp = create_event_sp ;
}
private :
// A shared pointer to a String that describes this event in
// detail (all args and return and error values)
StringSP m_string_sp ;
// The frames for the stack backtrace for this event
Frames m_frames ;
// If this is a file descriptor delete event, this might contain
// the correspoding file descriptor create event
FDEventSP m_create_event_sp ;
// The file descriptor for this event
int m_fd ;
// The error code (if any) for this event
int m_err ;
// True if this event is a file descriptor create event, false
// if it is a file descriptor delete event
bool m_is_create ;
} ;
//----------------------------------------------------------------------
// Templatized class that will save errno only if the "value" it is
// constructed with is equal to INVALID. When the class goes out of
// scope, it will restore errno if it was saved.
//----------------------------------------------------------------------
template < int INVALID >
class Errno
{
public :
// Save errno only if we are supposed to
Errno ( int value ) :
m_saved_errno ( ( value = = INVALID ) ? errno : 0 ) ,
m_restore ( value = = INVALID )
{
}
// Restore errno only if we are supposed to
~ Errno ( )
{
if ( m_restore )
errno = m_saved_errno ;
}
// Accessor for the saved value of errno
int
get_errno ( ) const
{
return m_saved_errno ;
}
protected :
const int m_saved_errno ;
const bool m_restore ;
} ;
typedef Errno < - 1 > InvalidFDErrno ;
typedef Errno < - 1 > NegativeErrorErrno ;
typedef std : : vector < FDEventSP > FDEventArray ;
typedef std : : map < int , FDEventArray > FDEventMap ;
//----------------------------------------------------------------------
// Globals
//----------------------------------------------------------------------
// Global event map that contains all file descriptor events. As file
// descriptor create and close events come in, they will get filled
// into this map (protected by g_mutex). When a file descriptor close
// event is detected, the open event will be removed and placed into
// the close event so if something tries to double close a file
// descriptor we can show the previous close event and the file
// desctiptor event that created it. When a new file descriptor create
// event comes in, we will remove the previous one for that file
// desctiptor unless the environment variable "FileDescriptorStackLoggingNoCompact"
// is set. The file desctiptor history can be accessed using the
// get_fd_history() function.
static FDEventMap g_fd_event_map ;
// A mutex to protect access to our data structures in g_fd_event_map
// and also our logging messages
static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER ;
// Log all file descriptor create and close events by default. Only log
// warnings and erros if the "FileDescriptorMinimalLogging" environment
// variable is set.
static int g_log_all_calls = 1 ;
// We compact the file descriptor events by default. Set the environment
// varible "FileDescriptorStackLoggingNoCompact" to keep a full history.
static int g_compact = 1 ;
2012-01-17 06:21:29 +08:00
// The current process ID
static int g_pid = - 1 ;
static bool g_enabled = true ;
2012-01-10 08:29:36 +08:00
//----------------------------------------------------------------------
// Mutex class that will lock a mutex when it is constructed, and unlock
// it when is goes out of scope
//----------------------------------------------------------------------
class Locker
{
public :
Locker ( pthread_mutex_t * mutex_ptr ) :
m_mutex_ptr ( mutex_ptr )
{
: : pthread_mutex_lock ( m_mutex_ptr ) ;
}
// This allows clients to test try and acquire the mutex...
Locker ( pthread_mutex_t * mutex_ptr , bool & lock_acquired ) :
m_mutex_ptr ( NULL )
{
lock_acquired = : : pthread_mutex_trylock ( mutex_ptr ) = = 0 ;
if ( lock_acquired )
m_mutex_ptr = mutex_ptr ;
}
~ Locker ( )
{
if ( m_mutex_ptr )
: : pthread_mutex_unlock ( m_mutex_ptr ) ;
}
protected :
pthread_mutex_t * m_mutex_ptr ;
} ;
static void
log ( const char * format , . . . ) __attribute__ ( ( format ( printf , 1 , 2 ) ) ) ;
static void
log ( int log_fd , const FDEvent * event , const char * format , . . . ) __attribute__ ( ( format ( printf , 3 , 4 ) ) ) ;
static void
backtrace_log ( const char * format , . . . ) __attribute__ ( ( format ( printf , 1 , 2 ) ) ) ;
2012-01-17 06:21:29 +08:00
static void
backtrace_error ( const char * format , . . . ) __attribute__ ( ( format ( printf , 1 , 2 ) ) ) ;
2012-01-10 08:29:36 +08:00
static void
log_to_fd ( int log_fd , const char * format , . . . ) __attribute__ ( ( format ( printf , 2 , 3 ) ) ) ;
static inline size_t
get_backtrace ( Frames & frame_buffer , size_t frames_to_remove )
{
void * frames [ 2048 ] ;
int count = : : backtrace ( & frames [ 0 ] , sizeof ( frames ) / sizeof ( void * ) ) ;
if ( count > frames_to_remove )
frame_buffer . assign ( & frames [ frames_to_remove ] , & frames [ count ] ) ;
else
frame_buffer . assign ( & frames [ 0 ] , & frames [ count ] ) ;
while ( frame_buffer . back ( ) < ( void * ) 1024 )
frame_buffer . pop_back ( ) ;
return frame_buffer . size ( ) ;
}
2012-01-17 06:21:29 +08:00
static int g_log_fd = STDOUT_FILENO ;
static int g_initialized = 0 ;
const char *
get_process_fullpath ( bool force = false )
{
static char g_process_fullpath [ PATH_MAX ] = { 0 } ;
if ( force | | g_process_fullpath [ 0 ] = = ' \0 ' )
{
// If DST is NULL, then return the number of bytes needed.
uint32_t len = sizeof ( g_process_fullpath ) ;
if ( _NSGetExecutablePath ( g_process_fullpath , & len ) ! = 0 )
strncpy ( g_process_fullpath , " <error> " , sizeof ( g_process_fullpath ) ) ;
}
return g_process_fullpath ;
}
// Returns the current process ID, or -1 if inserposing not enabled for
// this process
static int
get_interposed_pid ( )
{
if ( ! g_enabled )
return - 1 ;
const pid_t pid = getpid ( ) ;
if ( g_pid ! = pid )
{
if ( g_pid = = - 1 )
{
g_pid = pid ;
log ( " Interposing file descriptor create and delete functions for %s (pid=%i) \n " , get_process_fullpath ( true ) , pid ) ;
}
else
{
log ( " pid=%i: disabling interposing file descriptor create and delete functions for child process %s (pid=%i) \n " , g_pid , get_process_fullpath ( true ) , pid ) ;
g_enabled = false ;
return - 1 ;
}
// Log when our process changes
}
return g_pid ;
}
2012-01-10 08:29:36 +08:00
static int
get_logging_fd ( )
{
2012-01-17 06:21:29 +08:00
if ( ! g_enabled )
return - 1 ;
2012-01-10 08:29:36 +08:00
2012-01-17 06:21:29 +08:00
if ( ! g_initialized )
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
g_initialized = 1 ;
2012-01-10 08:29:36 +08:00
2012-01-17 06:21:29 +08:00
const pid_t pid = get_interposed_pid ( ) ;
2012-01-10 08:29:36 +08:00
2012-01-17 06:21:29 +08:00
if ( g_enabled )
{
// Keep all stack info around for all fd create and delete calls.
// Otherwise we will remove the fd create call when a corresponding
// fd delete call is received
if ( getenv ( " FileDescriptorStackLoggingNoCompact " ) )
g_compact = 0 ;
if ( getenv ( " FileDescriptorMinimalLogging " ) )
g_log_all_calls = 0 ;
const char * log_path = getenv ( " FileDescriptorLogFile " ) ;
if ( log_path )
g_log_fd = : : creat ( log_path , 0660 ) ;
else
g_log_fd = STDOUT_FILENO ;
2012-01-10 08:29:36 +08:00
2012-01-17 06:21:29 +08:00
// Only let this interposing happen on the first time this matches
// and stop this from happening so any child processes don't also
// log their file descriptors
: : unsetenv ( " DYLD_INSERT_LIBRARIES " ) ;
}
else
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
log ( " pid=%i: logging disabled \n " , getpid ( ) ) ;
2012-01-10 08:29:36 +08:00
}
}
return g_log_fd ;
}
void
log_to_fd ( int log_fd , const char * format , va_list args )
{
if ( format & & format [ 0 ] & & log_fd > = 0 )
{
char buffer [ PATH_MAX ] ;
const int count = : : vsnprintf ( buffer , sizeof ( buffer ) , format , args ) ;
if ( count > 0 )
write ( log_fd , buffer , count ) ;
}
}
void
log_to_fd ( int log_fd , const char * format , . . . )
{
if ( format & & format [ 0 ] )
{
va_list args ;
va_start ( args , format ) ;
log_to_fd ( log_fd , format , args ) ;
va_end ( args ) ;
}
}
void
log ( const char * format , va_list args )
{
log_to_fd ( get_logging_fd ( ) , format , args ) ;
}
void
log ( const char * format , . . . )
{
if ( format & & format [ 0 ] )
{
va_list args ;
va_start ( args , format ) ;
log ( format , args ) ;
va_end ( args ) ;
}
}
void
log ( int log_fd , const FDEvent * event , const char * format , . . . )
{
if ( format & & format [ 0 ] )
{
va_list args ;
va_start ( args , format ) ;
log_to_fd ( log_fd , format , args ) ;
va_end ( args ) ;
}
if ( event )
event - > Dump ( log_fd ) ;
}
void
FDEvent : : Dump ( int log_fd ) const
{
if ( log_fd > = 0 )
{
log_to_fd ( log_fd , " %s \n " , m_string_sp - > c_str ( ) ) ;
if ( ! m_frames . empty ( ) )
: : backtrace_symbols_fd ( m_frames . data ( ) , m_frames . size ( ) , log_fd ) ;
if ( m_create_event_sp )
{
log_to_fd ( log_fd , " \n fd=%i was created with this event: \n " , m_fd ) ;
m_create_event_sp - > Dump ( log_fd ) ;
log_to_fd ( log_fd , " \n " ) ;
}
}
}
void
backtrace_log ( const char * format , . . . )
{
const int log_fd = get_logging_fd ( ) ;
if ( log_fd > = 0 )
{
if ( format & & format [ 0 ] )
{
va_list args ;
va_start ( args , format ) ;
log ( format , args ) ;
va_end ( args ) ;
}
Frames frames ;
2012-01-15 02:18:31 +08:00
if ( get_backtrace ( frames , 2 ) )
2012-01-10 08:29:36 +08:00
: : backtrace_symbols_fd ( frames . data ( ) , frames . size ( ) , log_fd ) ;
}
}
void
2012-01-17 06:21:29 +08:00
backtrace_error ( const char * format , . . . )
{
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
const int log_fd = get_logging_fd ( ) ;
if ( log_fd > = 0 )
{
log ( " \n error: %s (pid=%i): " , get_process_fullpath ( ) , pid ) ;
if ( format & & format [ 0 ] )
{
va_list args ;
va_start ( args , format ) ;
log ( format , args ) ;
va_end ( args ) ;
}
Frames frames ;
if ( get_backtrace ( frames , 2 ) )
: : backtrace_symbols_fd ( frames . data ( ) , frames . size ( ) , log_fd ) ;
}
}
}
2012-01-10 08:29:36 +08:00
void
save_backtrace ( int fd , int err , const StringSP & string_sp , bool is_create )
{
Frames frames ;
get_backtrace ( frames , 2 ) ;
FDEventSP fd_event_sp ( new FDEvent ( fd , err , string_sp , is_create , frames ) ) ;
FDEventMap : : iterator pos = g_fd_event_map . find ( fd ) ;
if ( pos ! = g_fd_event_map . end ( ) )
{
// We have history for this fd...
FDEventArray & event_array = g_fd_event_map [ fd ] ;
if ( fd_event_sp - > IsCreateEvent ( ) )
{
// The current fd event is a function that creates
// a descriptor, check in case last event was
// a create event.
if ( event_array . back ( ) - > IsCreateEvent ( ) )
{
const int log_fd = get_logging_fd ( ) ;
// Two fd create functions in a row, we missed
// a function that closes a fd...
log ( log_fd , fd_event_sp . get ( ) , " \n warning: unmatched file descriptor create event fd=%i (we missed a file descriptor close event): \n " , fd ) ;
}
else if ( g_compact )
{
// We are compacting so we remove previous create event
// when we get the correspinding delete event
event_array . pop_back ( ) ;
}
}
else
{
// The current fd event is a function that deletes
// a descriptor, check in case last event for this
// fd was a delete event (double close!)
if ( event_array . back ( ) - > IsDeleteEvent ( ) )
{
const int log_fd = get_logging_fd ( ) ;
// Two fd delete functions in a row, we must
// have missed some function that opened a descriptor
log ( log_fd , fd_event_sp . get ( ) , " \n warning: unmatched file descriptor close event for fd=%d (we missed the file descriptor create event): \n " , fd ) ;
}
else if ( g_compact )
{
// Since this is a close event, we want to remember the open event
// that this close if for...
fd_event_sp - > SetCreateEvent ( event_array . back ( ) ) ;
// We are compacting so we remove previous create event
// when we get the correspinding delete event
event_array . pop_back ( ) ;
}
}
event_array . push_back ( fd_event_sp ) ;
}
else
{
g_fd_event_map [ fd ] . push_back ( fd_event_sp ) ;
}
}
//----------------------------------------------------------------------
// socket() interpose function
//----------------------------------------------------------------------
extern " C " int
socket $ __interposed__ ( int domain , int type , int protocol )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : socket ( domain , type , protocol ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ) ;
if ( fd = = - 1 )
description_sp - > printf ( " pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i errno = %i " , pid , domain , type , protocol , fd , fd_errno . get_errno ( ) ) ;
else
description_sp - > printf ( " pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i " , pid , domain , type , protocol , fd ) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
2012-01-10 08:29:36 +08:00
else
2012-01-17 06:21:29 +08:00
{
return : : socket ( domain , type , protocol ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// socketpair() interpose function
//----------------------------------------------------------------------
extern " C " int
socketpair $ __interposed__ ( int domain , int type , int protocol , int fds [ 2 ] )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
fds [ 0 ] = - 1 ;
fds [ 1 ] = - 1 ;
const int err = socketpair ( domain , type , protocol , fds ) ;
NegativeErrorErrno err_errno ( err ) ;
StringSP description_sp ( new String ( " pid=%i: socketpair (domain=%i, type=%i, protocol=%i, {fd=%i, fd=%i}) - > err = % i " , pid, domain, type, protocol, fds[0], fds[1], err)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fds [ 0 ] > = 0 )
save_backtrace ( fds [ 0 ] , err_errno . get_errno ( ) , description_sp , true ) ;
if ( fds [ 1 ] > = 0 )
save_backtrace ( fds [ 1 ] , err_errno . get_errno ( ) , description_sp , true ) ;
return err ;
}
else
{
return socketpair ( domain , type , protocol , fds ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// open() interpose function
//----------------------------------------------------------------------
extern " C " int
open $ __interposed__ ( const char * path , int oflag , int mode )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
Locker locker ( & g_mutex ) ;
int fd = - 2 ;
StringSP description_sp ( new String ) ;
if ( oflag & O_CREAT )
{
fd = : : open ( path , oflag , mode ) ;
description_sp - > printf ( " pid=%i: open (path = '%s', oflag = %i, mode = %i) -> fd=%i " , pid , path , oflag , mode , fd ) ;
}
else
{
fd = : : open ( path , oflag ) ;
description_sp - > printf ( " pid=%i: open (path = '%s', oflag = %i) -> fd=%i " , pid , path , oflag , fd ) ;
}
InvalidFDErrno fd_errno ( fd ) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
2012-01-10 08:29:36 +08:00
}
else
{
2012-01-17 06:21:29 +08:00
return : : open ( path , oflag , mode ) ;
2012-01-10 08:29:36 +08:00
}
}
//----------------------------------------------------------------------
// open$NOCANCEL() interpose function
//----------------------------------------------------------------------
extern " C " int
open $ NOCANCEL $ __interposed__ ( const char * path , int oflag , int mode )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : open $ NOCANCEL ( path , oflag , mode ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: open$NOCANCEL (path = '%s', oflag = %i, mode = %i) - > fd = % i " , pid, path, oflag, mode, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : open $ NOCANCEL ( path , oflag , mode ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// __open_extended() interpose function
//----------------------------------------------------------------------
extern " C " int
__open_extended $ __interposed__ ( const char * path , int oflag , uid_t uid , gid_t gid , int mode , struct kauth_filesec * fsacl )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : __open_extended ( path , oflag , uid , gid , mode , fsacl ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: __open_extended (path='%s', oflag=%i, uid=%i, gid=%i, mode=%i, fsacl=%p) - > fd = % i " , pid, path, oflag, uid, gid, mode, fsacl, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : __open_extended ( path , oflag , uid , gid , mode , fsacl ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// kqueue() interpose function
//----------------------------------------------------------------------
extern " C " int
kqueue $ __interposed__ ( void )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : kqueue ( ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: kqueue () - > fd = % i " , pid, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : kqueue ( ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// shm_open() interpose function
//----------------------------------------------------------------------
extern " C " int
shm_open $ __interposed__ ( const char * path , int oflag , int mode )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : shm_open ( path , oflag , mode ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: shm_open (path = '%s', oflag = %i, mode = %i) - > fd = % i " , pid, path, oflag, mode, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : shm_open ( path , oflag , mode ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// accept() interpose function
//----------------------------------------------------------------------
extern " C " int
accept $ __interposed__ ( int socket , struct sockaddr * address , socklen_t * address_len )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : accept ( socket , address , address_len ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: accept (socket=%i, ...) - > fd = % i " , pid, socket, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : accept ( socket , address , address_len ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// accept$NOCANCEL() interpose function
//----------------------------------------------------------------------
extern " C " int
accept $ NOCANCEL $ __interposed__ ( int socket , struct sockaddr * address , socklen_t * address_len )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : accept $ NOCANCEL ( socket , address , address_len ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: accept$NOCANCEL (socket=%i, ...) - > fd = % i " , pid, socket, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : accept $ NOCANCEL ( socket , address , address_len ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// dup() interpose function
//----------------------------------------------------------------------
extern " C " int
dup $ __interposed__ ( int fd2 )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
const int fd = : : dup ( fd2 ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: dup (fd2=%i) - > fd = % i " , pid, fd2, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : dup ( fd2 ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// dup2() interpose function
//----------------------------------------------------------------------
extern " C " int
dup2 $ __interposed__ ( int fd1 , int fd2 )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
// If "fd2" is already opened, it will be closed during the
// dup2 call below, so we need to see if we have fd2 in our
// open map and treat it as a close(fd2)
FDEventMap : : iterator pos = g_fd_event_map . find ( fd2 ) ;
StringSP dup2_close_description_sp ( new String ( " pid=%i: dup2 (fd1=%i, fd2=%i) - > will close ( fd = % i ) " , pid, fd1, fd2, fd2)) ;
if ( pos ! = g_fd_event_map . end ( ) & & pos - > second . back ( ) - > IsCreateEvent ( ) )
save_backtrace ( fd2 , 0 , dup2_close_description_sp , false ) ;
const int fd = : : dup2 ( fd1 , fd2 ) ;
InvalidFDErrno fd_errno ( fd ) ;
StringSP description_sp ( new String ( " pid=%i: dup2 (fd1=%i, fd2=%i) - > fd = % i " , pid, fd1, fd2, fd)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fd > = 0 )
save_backtrace ( fd , fd_errno . get_errno ( ) , description_sp , true ) ;
return fd ;
}
else
{
return : : dup2 ( fd1 , fd2 ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// close() interpose function
//----------------------------------------------------------------------
extern " C " int
close $ __interposed__ ( int fd )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
Locker locker ( & g_mutex ) ;
const int err = close ( fd ) ;
NegativeErrorErrno err_errno ( err ) ;
StringSP description_sp ( new String ) ;
if ( err = = - 1 )
description_sp - > printf ( " pid=%i: close (fd=%i) => %i errno = %i (%s)) " , pid , fd , err , err_errno . get_errno ( ) , strerror ( err_errno . get_errno ( ) ) ) ;
else
description_sp - > printf ( " pid=%i: close (fd=%i) => %i " , pid , fd , err ) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
2012-01-10 08:29:36 +08:00
2012-01-17 06:21:29 +08:00
if ( err = = 0 )
{
if ( fd > = 0 )
save_backtrace ( fd , err , description_sp , false ) ;
}
else if ( err = = - 1 )
{
if ( err_errno . get_errno ( ) = = EBADF & & fd ! = - 1 )
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
backtrace_error ( " close (fd=%d) resulted in EBADF: \n " , fd ) ;
FDEventMap : : iterator pos = g_fd_event_map . find ( fd ) ;
if ( pos ! = g_fd_event_map . end ( ) )
{
log ( get_logging_fd ( ) , pos - > second . back ( ) . get ( ) , " \n fd=%d was previously %s with this event: \n " , fd , pos - > second . back ( ) - > IsCreateEvent ( ) ? " opened " : " closed " ) ;
}
2012-01-10 08:29:36 +08:00
}
}
2012-01-17 06:21:29 +08:00
return err ;
}
else
{
return close ( fd ) ;
2012-01-10 08:29:36 +08:00
}
}
//----------------------------------------------------------------------
// close$NOCANCEL() interpose function
//----------------------------------------------------------------------
extern " C " int
close $ NOCANCEL $ __interposed__ ( int fd )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
Locker locker ( & g_mutex ) ;
const int err = close $ NOCANCEL ( fd ) ;
NegativeErrorErrno err_errno ( err ) ;
StringSP description_sp ( new String ) ;
if ( err = = - 1 )
description_sp - > printf ( " pid=%i: close$NOCANCEL (fd=%i) => %i errno = %i (%s)) " , pid , fd , err , err_errno . get_errno ( ) , strerror ( err_errno . get_errno ( ) ) ) ;
else
description_sp - > printf ( " pid=%i: close$NOCANCEL (fd=%i) => %i " , pid , fd , err ) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( err = = 0 )
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
if ( fd > = 0 )
save_backtrace ( fd , err , description_sp , false ) ;
}
else if ( err = = - 1 )
{
if ( err_errno . get_errno ( ) = = EBADF & & fd ! = - 1 )
2012-01-10 08:29:36 +08:00
{
2012-01-17 06:21:29 +08:00
backtrace_error ( " close$NOCANCEL (fd=%d) resulted in EBADF \n : " , fd ) ;
FDEventMap : : iterator pos = g_fd_event_map . find ( fd ) ;
if ( pos ! = g_fd_event_map . end ( ) )
{
log ( get_logging_fd ( ) , pos - > second . back ( ) . get ( ) , " \n fd=%d was previously %s with this event: \n " , fd , pos - > second . back ( ) - > IsCreateEvent ( ) ? " opened " : " closed " ) ;
}
2012-01-10 08:29:36 +08:00
}
}
2012-01-17 06:21:29 +08:00
return err ;
2012-01-10 08:29:36 +08:00
}
2012-01-17 06:21:29 +08:00
else
{
return close $ NOCANCEL ( fd ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// pipe() interpose function
//----------------------------------------------------------------------
extern " C " int
pipe $ __interposed__ ( int fds [ 2 ] )
{
2012-01-17 06:21:29 +08:00
const int pid = get_interposed_pid ( ) ;
if ( pid > = 0 )
{
Locker locker ( & g_mutex ) ;
fds [ 0 ] = - 1 ;
fds [ 1 ] = - 1 ;
const int err = pipe ( fds ) ;
const int saved_errno = errno ;
StringSP description_sp ( new String ( " pid=%i: pipe ({fd=%i, fd=%i}) - > err = % i " , pid, fds[0], fds[1], err)) ;
if ( g_log_all_calls )
description_sp - > log ( get_logging_fd ( ) ) ;
if ( fds [ 0 ] > = 0 )
save_backtrace ( fds [ 0 ] , saved_errno , description_sp , true ) ;
if ( fds [ 1 ] > = 0 )
save_backtrace ( fds [ 1 ] , saved_errno , description_sp , true ) ;
errno = saved_errno ;
return err ;
}
else
{
return pipe ( fds ) ;
}
2012-01-10 08:29:36 +08:00
}
//----------------------------------------------------------------------
// get_fd_history()
//
// This function allows runtime access to the file descriptor history.
//
// @param[in] log_fd
// The file descriptor to log to
//
// @param[in] fd
// The file descriptor whose history should be dumped
//----------------------------------------------------------------------
extern " C " void
get_fd_history ( int log_fd , int fd )
{
// "create" below needs to be outside of the mutex locker scope
if ( log_fd > = 0 )
{
bool got_lock = false ;
Locker locker ( & g_mutex , got_lock ) ;
if ( got_lock )
{
FDEventMap : : iterator pos = g_fd_event_map . find ( fd ) ;
log_to_fd ( log_fd , " Dumping file descriptor history for fd=%i: \n " , fd ) ;
if ( pos ! = g_fd_event_map . end ( ) )
{
FDEventArray & event_array = g_fd_event_map [ fd ] ;
const size_t num_events = event_array . size ( ) ;
for ( size_t i = 0 ; i < num_events ; + + i )
event_array [ i ] - > Dump ( log_fd ) ;
}
else
{
log_to_fd ( log_fd , " error: no file descriptor events found for fd=%i \n " , fd ) ;
}
}
else
{
log_to_fd ( log_fd , " error: fd event mutex is locked... \n " ) ;
}
}
}
//----------------------------------------------------------------------
// Interposing
//----------------------------------------------------------------------
// FD creation routines
DYLD_INTERPOSE ( accept $ __interposed__ , accept ) ;
DYLD_INTERPOSE ( accept $ NOCANCEL $ __interposed__ , accept $ NOCANCEL ) ;
DYLD_INTERPOSE ( dup $ __interposed__ , dup ) ;
DYLD_INTERPOSE ( dup2 $ __interposed__ , dup2 ) ;
DYLD_INTERPOSE ( kqueue $ __interposed__ , kqueue ) ;
DYLD_INTERPOSE ( open $ __interposed__ , open ) ;
DYLD_INTERPOSE ( open $ NOCANCEL $ __interposed__ , open $ NOCANCEL ) ;
DYLD_INTERPOSE ( __open_extended $ __interposed__ , __open_extended ) ;
DYLD_INTERPOSE ( pipe $ __interposed__ , pipe ) ;
DYLD_INTERPOSE ( shm_open $ __interposed__ , shm_open ) ;
DYLD_INTERPOSE ( socket $ __interposed__ , socket ) ;
DYLD_INTERPOSE ( socketpair $ __interposed__ , socketpair ) ;
// FD deleting routines
DYLD_INTERPOSE ( close $ __interposed__ , close ) ;
DYLD_INTERPOSE ( close $ NOCANCEL $ __interposed__ , close $ NOCANCEL ) ;
} // namespace fd_interposing