#include "DAMain.h"
#include "DABase.h"
#include "DADialog.h"
#include "DADisk.h"
#include "DAFileSystem.h"
#include "DAInternal.h"
#include "DALog.h"
#include "DAServer.h"
#include "DASession.h"
#include "DAStage.h"
#include "DASupport.h"
#include "DAThread.h"
#include <assert.h>
#include <dirent.h>
#include <libgen.h>
#include <notify.h>
#include <notify_keys.h>
#include <signal.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOMedia.h>
static const CFStringRef __kDABundlePath = CFSTR( "/System/Library/Frameworks/DiskArbitration.framework" );
static SCDynamicStoreRef __gDAConfigurationPort = NULL;
static Boolean __gDAOptionDebug = FALSE;
static CFMachPortRef __gDAVolumeMountedPort = NULL;
static CFMachPortRef __gDAVolumeUnmountedPort = NULL;
static CFMachPortRef __gDAVolumeUpdatedPort = NULL;
const char * kDAMainMountPointFolder = "/Volumes";
const char * kDAMainMountPointFolderCookieFile = ".autodiskmounted";
CFURLRef gDABundlePath = NULL;
CFStringRef gDAConsoleUser = NULL;
gid_t gDAConsoleUserGID = 0;
uid_t gDAConsoleUserUID = 0;
CFArrayRef gDAConsoleUserList = NULL;
CFMutableArrayRef gDADiskList = NULL;
Boolean gDAExit = FALSE;
CFMutableArrayRef gDAFileSystemList = NULL;
CFMutableArrayRef gDAFileSystemProbeList = NULL;
Boolean gDAIdle = TRUE;
io_iterator_t gDAMediaAppearedNotification = IO_OBJECT_NULL;
io_iterator_t gDAMediaDisappearedNotification = IO_OBJECT_NULL;
IONotificationPortRef gDAMediaPort = NULL;
CFMutableArrayRef gDAMountMapList1 = NULL;
CFMutableArrayRef gDAMountMapList2 = NULL;
CFMutableDictionaryRef gDAPreferenceList = NULL;
pid_t gDAProcessID = 0;
char * gDAProcessName = NULL;
char * gDAProcessNameID = NULL;
CFMutableArrayRef gDARequestList = NULL;
CFMutableArrayRef gDAResponseList = NULL;
CFMutableArrayRef gDASessionList = NULL;
CFMutableDictionaryRef gDAUnitList = NULL;
static void __usage( void )
{
fprintf( stderr, "%s: [-d]\n", gDAProcessName );
fprintf( stderr, "options:\n" );
fprintf( stderr, "\t-d\tenable debugging\n" );
exit( EX_USAGE );
}
static Boolean __DAMainCreateMountPointFolder( void )
{
int status;
status = access( kDAMainMountPointFolder, F_OK );
if ( status )
{
status = ___mkdir( kDAMainMountPointFolder, 0755 );
}
else
{
DIR * folder;
chmod( kDAMainMountPointFolder, 0755 );
chown( kDAMainMountPointFolder, -1, ___GID_WHEEL );
folder = opendir( kDAMainMountPointFolder );
if ( folder )
{
struct dirent * item;
while ( ( item = readdir( folder ) ) )
{
char path[MAXPATHLEN];
strlcpy( path, kDAMainMountPointFolder, sizeof( path ) );
strlcat( path, "/", sizeof( path ) );
strlcat( path, item->d_name, sizeof( path ) );
if ( item->d_type == DT_DIR )
{
char file[MAXPATHLEN];
strlcpy( file, path, sizeof( file ) );
strlcat( file, "/", sizeof( file ) );
strlcat( file, kDAMainMountPointFolderCookieFile, sizeof( file ) );
unlink( file );
rmdir( path );
}
else if ( item->d_type == DT_LNK )
{
unlink( path );
}
}
closedir( folder );
}
else
{
status = ENOTDIR;
}
}
return status ? FALSE : TRUE;
}
static void __DAMainSignal( int sig )
{
gDAExit = TRUE;
}
static void __DAMain( void )
{
FILE * file;
CFStringRef key;
CFMutableArrayRef keys;
char path[MAXPATHLEN];
mach_port_t port;
CFRunLoopSourceRef source;
int token;
DADiskInitialize( );
DAFileSystemInitialize( );
DASessionInitialize( );
gDABundlePath = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, __kDABundlePath, kCFURLPOSIXPathStyle, TRUE );
assert( gDABundlePath );
gDAConsoleUser = ___SCDynamicStoreCopyConsoleUser( NULL, &gDAConsoleUserUID, &gDAConsoleUserGID );
gDAConsoleUserList = ___SCDynamicStoreCopyConsoleInformation( NULL );
DALogOpen( gDAProcessName, __gDAOptionDebug, TRUE, TRUE );
gDAProcessID = getpid( );
asprintf( &gDAProcessNameID, "%s [%d]", gDAProcessName, gDAProcessID );
assert( gDAProcessNameID );
gDADiskList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDADiskList );
gDAFileSystemList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDAFileSystemList );
gDAFileSystemProbeList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDAFileSystemProbeList );
gDAMountMapList1 = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDAMountMapList1 );
gDAMountMapList2 = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDAMountMapList2 );
gDAPreferenceList = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
assert( gDAPreferenceList );
gDARequestList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDARequestList );
gDAResponseList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDAResponseList );
gDASessionList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( gDASessionList );
gDAUnitList = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
assert( gDAUnitList );
source = DAServerCreateRunLoopSource( kCFAllocatorDefault, 0 );
if ( source == NULL )
{
DALogError( "could not create Disk Arbitration master port." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
__gDAVolumeMountedPort = CFMachPortCreate( kCFAllocatorDefault, _DAVolumeMountedCallback, NULL, NULL );
if ( __gDAVolumeMountedPort == NULL )
{
DALogError( "could not create BSD notification port." );
exit( EX_SOFTWARE );
}
source = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, __gDAVolumeMountedPort, 0 );
if ( source == NULL )
{
DALogError( "could not create BSD notification run loop source." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
__gDAVolumeUnmountedPort = CFMachPortCreate( kCFAllocatorDefault, _DAVolumeUnmountedCallback, NULL, NULL );
if ( __gDAVolumeUnmountedPort == NULL )
{
DALogError( "could not create BSD notification port." );
exit( EX_SOFTWARE );
}
source = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, __gDAVolumeUnmountedPort, 0 );
if ( source == NULL )
{
DALogError( "could not create BSD notification run loop source." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
__gDAVolumeUpdatedPort = CFMachPortCreate( kCFAllocatorDefault, _DAVolumeUpdatedCallback, NULL, NULL );
if ( __gDAVolumeUpdatedPort == NULL )
{
DALogError( "could not create BSD notification port." );
exit( EX_SOFTWARE );
}
source = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, __gDAVolumeUpdatedPort, 0 );
if ( source == NULL )
{
DALogError( "could not create BSD notification run loop source." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
gDAMediaPort = IONotificationPortCreate( kIOMasterPortDefault );
if ( gDAMediaPort == NULL )
{
DALogError( "could not create I/O Kit notification port." );
exit( EX_SOFTWARE );
}
source = IONotificationPortGetRunLoopSource( gDAMediaPort ),
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
__gDAConfigurationPort = SCDynamicStoreCreate( kCFAllocatorDefault, CFSTR( _kDADaemonName ), _DAConfigurationCallback, NULL );
if ( __gDAConfigurationPort == NULL )
{
DALogError( "could not create System Configuration notification port." );
exit( EX_SOFTWARE );
}
source = SCDynamicStoreCreateRunLoopSource( kCFAllocatorDefault, __gDAConfigurationPort, 0 );
if ( source == NULL )
{
DALogError( "could not create System Configuration notification run loop source." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
source = DAFileSystemCreateRunLoopSource( kCFAllocatorDefault, 0 );
if ( source == NULL )
{
DALogError( "could not create file system run loop source." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
source = DAStageCreateRunLoopSource( kCFAllocatorDefault, 0 );
if ( source == NULL )
{
DALogError( "could not create stage run loop source." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
source = DAThreadCreateRunLoopSource( kCFAllocatorDefault, 0 );
if ( source == NULL )
{
DALogError( "could not create thread run loop source." );
exit( EX_SOFTWARE );
}
CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode );
CFRelease( source );
IOServiceAddMatchingNotification( gDAMediaPort,
kIOTerminatedNotification,
IOServiceMatching( kIOMediaClass ),
_DAMediaDisappearedCallback,
NULL,
&gDAMediaDisappearedNotification );
if ( gDAMediaDisappearedNotification == IO_OBJECT_NULL )
{
DALogError( "could not create \"media disappeared\" notification." );
exit( EX_SOFTWARE );
}
IOServiceAddMatchingNotification( gDAMediaPort,
kIOMatchedNotification,
IOServiceMatching( kIOMediaClass ),
_DAMediaAppearedCallback,
NULL,
&gDAMediaAppearedNotification );
if ( gDAMediaAppearedNotification == IO_OBJECT_NULL )
{
DALogError( "could not create \"media appeared\" notification." );
exit( EX_SOFTWARE );
}
key = SCDynamicStoreKeyCreateConsoleUser( kCFAllocatorDefault );
keys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
assert( key );
assert( keys );
CFArrayAppendValue( keys, key );
if ( SCDynamicStoreSetNotificationKeys( __gDAConfigurationPort, keys, NULL ) == FALSE )
{
DALogError( "could not create \"configuration changed\" notification." );
exit( EX_SOFTWARE );
}
CFRelease( key );
CFRelease( keys );
port = CFMachPortGetPort( __gDAVolumeMountedPort );
if ( notify_register_mach_port( kNotifyVFSMount, &port, NOTIFY_REUSE, &token ) )
{
DALogError( "could not create \"file system mounted\" notification." );
exit( EX_SOFTWARE );
}
port = CFMachPortGetPort( __gDAVolumeUnmountedPort );
if ( notify_register_mach_port( kNotifyVFSUnmount, &port, NOTIFY_REUSE, &token ) )
{
DALogError( "could not create \"file system unmounted\" notification." );
exit( EX_SOFTWARE );
}
port = CFMachPortGetPort( __gDAVolumeUpdatedPort );
if ( notify_register_mach_port( kNotifyVFSUpdate, &port, NOTIFY_REUSE, &token ) )
{
DALogError( "could not create \"file system updated\" notification." );
exit( EX_SOFTWARE );
}
if ( __DAMainCreateMountPointFolder( ) == FALSE )
{
DALogError( "could not create mount point folder." );
exit( EX_SOFTWARE );
}
snprintf( path, sizeof( path ), "/var/run/%s.pid", gDAProcessName );
file = fopen( path, "w" );
if ( file )
{
fprintf( file, "%d\n", gDAProcessID );
fclose( file );
}
DALogDebug( "" );
DALogDebug( "server has been started." );
if ( gDAConsoleUser )
{
DALogDebug( " console user = %@ [%d].", gDAConsoleUser, gDAConsoleUserUID );
}
else
{
DALogDebug( " console user = none." );
}
DAFileSystemListRefresh( );
DAMountMapListRefresh1( );
DAMountMapListRefresh2( );
DAPreferenceListRefresh( );
_DAMediaDisappearedCallback( NULL, gDAMediaDisappearedNotification );
_DAMediaAppearedCallback( NULL, gDAMediaAppearedNotification );
CFRunLoopRun( );
}
int main( int argc, char * argv[], char * envp[] )
{
char option;
gDAProcessName = basename( argv[0] );
if ( geteuid( ) )
{
fprintf( stderr, "%s: permission denied.\n", gDAProcessName );
exit( EX_NOPERM );
}
while ( ( option = getopt( argc, argv, "d" ) ) != -1 )
{
switch ( option )
{
case 'd':
{
__gDAOptionDebug = TRUE;
break;
}
default:
{
__usage( );
break;
}
}
}
signal( SIGTERM, __DAMainSignal );
__DAMain( );
exit( EX_OK );
}