1/* 2 * Copyright (c) 1998-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "DAMain.h" 25 26#include "DABase.h" 27#include "DADialog.h" 28#include "DADisk.h" 29#include "DAFileSystem.h" 30#include "DAInternal.h" 31#include "DALog.h" 32#include "DAServer.h" 33#include "DASession.h" 34#include "DAStage.h" 35#include "DASupport.h" 36#include "DAThread.h" 37 38#include <assert.h> 39#include <dirent.h> 40#include <libgen.h> 41#include <notify.h> 42#include <signal.h> 43#include <sysexits.h> 44#include <unistd.h> 45#include <sys/param.h> 46#include <sys/stat.h> 47#include <sys/wait.h> 48#include <CoreFoundation/CoreFoundation.h> 49#include <IOKit/IOKitLib.h> 50#include <IOKit/storage/IOMedia.h> 51 52static const CFStringRef __kDABundlePath = CFSTR( "/System/Library/Frameworks/DiskArbitration.framework" ); 53 54static SCDynamicStoreRef __gDAConfigurationPort = NULL; 55static Boolean __gDAOptionDebug = FALSE; 56static CFMachPortRef __gDAVolumeMountedPort = NULL; 57static CFMachPortRef __gDAVolumeUnmountedPort = NULL; 58 59const char * kDAMainMountPointFolder = "/Volumes"; 60const char * kDAMainMountPointFolderCookieFile = ".autodiskmounted"; 61 62CFURLRef gDABundlePath = NULL; 63CFStringRef gDAConsoleUser = NULL; 64gid_t gDAConsoleUserGID = 0; 65uid_t gDAConsoleUserUID = 0; 66CFArrayRef gDAConsoleUserList = NULL; 67CFMutableArrayRef gDADiskList = NULL; 68Boolean gDAExit = FALSE; 69CFMutableArrayRef gDAFileSystemList = NULL; 70CFMutableArrayRef gDAFileSystemProbeList = NULL; 71Boolean gDAIdle = TRUE; 72io_iterator_t gDAMediaAppearedNotification = IO_OBJECT_NULL; 73io_iterator_t gDAMediaDisappearedNotification = IO_OBJECT_NULL; 74IONotificationPortRef gDAMediaPort = NULL; 75CFMutableArrayRef gDAMountMapList1 = NULL; 76CFMutableArrayRef gDAMountMapList2 = NULL; 77CFMutableDictionaryRef gDAPreferenceList = NULL; 78pid_t gDAProcessID = 0; 79char * gDAProcessName = NULL; 80char * gDAProcessNameID = NULL; 81CFMutableArrayRef gDARequestList = NULL; 82CFMutableArrayRef gDAResponseList = NULL; 83CFMutableArrayRef gDASessionList = NULL; 84CFMutableDictionaryRef gDAUnitList = NULL; 85 86static void __usage( void ) 87{ 88 /* 89 * Print usage. 90 */ 91 92 fprintf( stderr, "%s: [-d]\n", gDAProcessName ); 93 fprintf( stderr, "options:\n" ); 94 fprintf( stderr, "\t-d\tenable debugging\n" ); 95 96 exit( EX_USAGE ); 97} 98 99static Boolean __DAMainCreateMountPointFolder( void ) 100{ 101 /* 102 * Create the mount point folder in which our mounts will be made. 103 */ 104 105 struct stat status; 106 Boolean success; 107 108 /* 109 * Determine whether the mount point folder exists. 110 */ 111 112 if ( stat( kDAMainMountPointFolder, &status ) ) 113 { 114 /* 115 * Create the mount point folder. 116 */ 117 118 success = ___mkdir( kDAMainMountPointFolder, 01777 ) ? FALSE : TRUE; 119 } 120 else 121 { 122 /* 123 * Determine whether the mount point folder is a folder. 124 */ 125 126 success = S_ISDIR( status.st_mode ) ? TRUE : FALSE; 127 128 if ( success ) 129 { 130 DIR * folder; 131 132 /* 133 * Correct the mount point folder's contents. 134 */ 135 136 folder = opendir( kDAMainMountPointFolder ); 137 138 if ( folder ) 139 { 140 struct dirent * item; 141 142 while ( ( item = readdir( folder ) ) ) 143 { 144 char path[MAXPATHLEN]; 145 146 if ( item->d_type == DT_DIR ) 147 { 148 /* 149 * Determine whether the mount point cookie file exists. 150 */ 151 152 strlcpy( path, kDAMainMountPointFolder, sizeof( path ) ); 153 strlcat( path, "/", sizeof( path ) ); 154 strlcat( path, item->d_name, sizeof( path ) ); 155 strlcat( path, "/", sizeof( path ) ); 156 strlcat( path, kDAMainMountPointFolderCookieFile, sizeof( path ) ); 157 158 if ( stat( path, &status ) == 0 ) 159 { 160 /* 161 * Remove the mount point cookie file. 162 */ 163 164 unlink( path ); 165 } 166 167 /* 168 * Remove the mount point. 169 */ 170 171 rmdir( dirname( path ) ); 172 } 173 else if ( item->d_type == DT_LNK ) 174 { 175 /* 176 * Remove the link. 177 */ 178 179 strlcpy( path, kDAMainMountPointFolder, sizeof( path ) ); 180 strlcat( path, "/", sizeof( path ) ); 181 strlcat( path, item->d_name, sizeof( path ) ); 182 183 unlink( path ); 184 } 185 } 186 187 closedir( folder ); 188 } 189 } 190 } 191 192 return success; 193} 194 195static void __DAMainSignal( int sig ) 196{ 197 /* 198 * Process a SIGTERM signal. 199 */ 200 201 gDAExit = TRUE; 202} 203 204static void __DAMain( void ) 205{ 206 FILE * file; 207 CFStringRef key; 208 CFMutableArrayRef keys; 209 char path[MAXPATHLEN]; 210 mach_port_t port; 211 CFRunLoopSourceRef source; 212 int token; 213 214 /* 215 * Initialize classes. 216 */ 217 218 DADiskInitialize( ); 219 220 DAFileSystemInitialize( ); 221 222 DASessionInitialize( ); 223 224 /* 225 * Initialize bundle path. 226 */ 227 228 gDABundlePath = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, __kDABundlePath, kCFURLPOSIXPathStyle, TRUE ); 229 230 assert( gDABundlePath ); 231 232 /* 233 * Initialize console user. 234 */ 235 236 gDAConsoleUser = ___SCDynamicStoreCopyConsoleUser( NULL, &gDAConsoleUserUID, &gDAConsoleUserGID ); 237 gDAConsoleUserList = ___SCDynamicStoreCopyConsoleInformation( NULL ); 238 239 /* 240 * Initialize log. 241 */ 242 243 DALogOpen( gDAProcessName, __gDAOptionDebug, TRUE, TRUE ); 244 245 /* 246 * Initialize process ID. 247 */ 248 249 gDAProcessID = getpid( ); 250 251 /* 252 * Initialize process ID tag. 253 */ 254 255 asprintf( &gDAProcessNameID, "%s [%d]", gDAProcessName, gDAProcessID ); 256 257 assert( gDAProcessNameID ); 258 259 /* 260 * Create the disk list. 261 */ 262 263 gDADiskList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 264 265 assert( gDADiskList ); 266 267 /* 268 * Create the file system list. 269 */ 270 271 gDAFileSystemList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 272 273 assert( gDAFileSystemList ); 274 275 /* 276 * Create the file system probe list. 277 */ 278 279 gDAFileSystemProbeList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 280 281 assert( gDAFileSystemProbeList ); 282 283 /* 284 * Create the mount map list. 285 */ 286 287 gDAMountMapList1 = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 288 289 assert( gDAMountMapList1 ); 290 291 /* 292 * Create the mount map list. 293 */ 294 295 gDAMountMapList2 = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 296 297 assert( gDAMountMapList2 ); 298 299 /* 300 * Create the preference list. 301 */ 302 303 gDAPreferenceList = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); 304 305 assert( gDAPreferenceList ); 306 307 /* 308 * Create the request list. 309 */ 310 311 gDARequestList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 312 313 assert( gDARequestList ); 314 315 /* 316 * Create the response list. 317 */ 318 319 gDAResponseList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 320 321 assert( gDAResponseList ); 322 323 /* 324 * Create the session list. 325 */ 326 327 gDASessionList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 328 329 assert( gDASessionList ); 330 331 /* 332 * Create the unit list. 333 */ 334 335 gDAUnitList = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); 336 337 assert( gDAUnitList ); 338 339 /* 340 * Create the Disk Arbitration master run loop source. 341 */ 342 343 source = DAServerCreateRunLoopSource( kCFAllocatorDefault, 0 ); 344 345 if ( source == NULL ) 346 { 347 DALogError( "could not create Disk Arbitration master port." ); 348 exit( EX_SOFTWARE ); 349 } 350 351 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 352 353 CFRelease( source ); 354 355 /* 356 * Create the BSD notification run loop source. 357 */ 358 359 __gDAVolumeMountedPort = CFMachPortCreate( kCFAllocatorDefault, _DAVolumeMountedCallback, NULL, NULL ); 360 361 if ( __gDAVolumeMountedPort == NULL ) 362 { 363 DALogError( "could not create BSD notification port." ); 364 exit( EX_SOFTWARE ); 365 } 366 367 source = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, __gDAVolumeMountedPort, 0 ); 368 369 if ( source == NULL ) 370 { 371 DALogError( "could not create BSD notification run loop source." ); 372 exit( EX_SOFTWARE ); 373 } 374 375 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 376 377 CFRelease( source ); 378 379 /* 380 * Create the BSD notification run loop source. 381 */ 382 383 __gDAVolumeUnmountedPort = CFMachPortCreate( kCFAllocatorDefault, _DAVolumeUnmountedCallback, NULL, NULL ); 384 385 if ( __gDAVolumeUnmountedPort == NULL ) 386 { 387 DALogError( "could not create BSD notification port." ); 388 exit( EX_SOFTWARE ); 389 } 390 391 source = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, __gDAVolumeUnmountedPort, 0 ); 392 393 if ( source == NULL ) 394 { 395 DALogError( "could not create BSD notification run loop source." ); 396 exit( EX_SOFTWARE ); 397 } 398 399 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 400 401 CFRelease( source ); 402 403 /* 404 * Create the I/O Kit notification run loop source. 405 */ 406 407 gDAMediaPort = IONotificationPortCreate( kIOMasterPortDefault ); 408 409 if ( gDAMediaPort == NULL ) 410 { 411 DALogError( "could not create I/O Kit notification port." ); 412 exit( EX_SOFTWARE ); 413 } 414 415 source = IONotificationPortGetRunLoopSource( gDAMediaPort ), 416 417 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 418 419 /* 420 * Create the System Configuration notification run loop source. 421 */ 422 423 __gDAConfigurationPort = SCDynamicStoreCreate( kCFAllocatorDefault, CFSTR( _kDADaemonName ), _DAConfigurationCallback, NULL ); 424 425 if ( __gDAConfigurationPort == NULL ) 426 { 427 DALogError( "could not create System Configuration notification port." ); 428 exit( EX_SOFTWARE ); 429 } 430 431 source = SCDynamicStoreCreateRunLoopSource( kCFAllocatorDefault, __gDAConfigurationPort, 0 ); 432 433 if ( source == NULL ) 434 { 435 DALogError( "could not create System Configuration notification run loop source." ); 436 exit( EX_SOFTWARE ); 437 } 438 439 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 440 441 CFRelease( source ); 442 443 /* 444 * Create the file system run loop source. 445 */ 446 447 source = DAFileSystemCreateRunLoopSource( kCFAllocatorDefault, 0 ); 448 449 if ( source == NULL ) 450 { 451 DALogError( "could not create file system run loop source." ); 452 exit( EX_SOFTWARE ); 453 } 454 455 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 456 457 CFRelease( source ); 458 459 /* 460 * Create the stage run loop source. 461 */ 462 463 source = DAStageCreateRunLoopSource( kCFAllocatorDefault, 0 ); 464 465 if ( source == NULL ) 466 { 467 DALogError( "could not create stage run loop source." ); 468 exit( EX_SOFTWARE ); 469 } 470 471 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 472 473 CFRelease( source ); 474 475 /* 476 * Create the thread run loop source. 477 */ 478 479 source = DAThreadCreateRunLoopSource( kCFAllocatorDefault, 0 ); 480 481 if ( source == NULL ) 482 { 483 DALogError( "could not create thread run loop source." ); 484 exit( EX_SOFTWARE ); 485 } 486 487 CFRunLoopAddSource( CFRunLoopGetCurrent( ), source, kCFRunLoopDefaultMode ); 488 489 CFRelease( source ); 490 491 /* 492 * Create the "media disappeared" notification. 493 */ 494 495 IOServiceAddMatchingNotification( gDAMediaPort, 496 kIOTerminatedNotification, 497 IOServiceMatching( kIOMediaClass ), 498 _DAMediaDisappearedCallback, 499 NULL, 500 &gDAMediaDisappearedNotification ); 501 502 if ( gDAMediaDisappearedNotification == IO_OBJECT_NULL ) 503 { 504 DALogError( "could not create \"media disappeared\" notification." ); 505 exit( EX_SOFTWARE ); 506 } 507 508 /* 509 * Create the "media appeared" notification. 510 */ 511 512 IOServiceAddMatchingNotification( gDAMediaPort, 513 kIOMatchedNotification, 514 IOServiceMatching( kIOMediaClass ), 515 _DAMediaAppearedCallback, 516 NULL, 517 &gDAMediaAppearedNotification ); 518 519 if ( gDAMediaAppearedNotification == IO_OBJECT_NULL ) 520 { 521 DALogError( "could not create \"media appeared\" notification." ); 522 exit( EX_SOFTWARE ); 523 } 524 525 /* 526 * Create the "configuration changed" notification. 527 */ 528 529 key = SCDynamicStoreKeyCreateConsoleUser( kCFAllocatorDefault ); 530 keys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); 531 532 assert( key ); 533 assert( keys ); 534 535 CFArrayAppendValue( keys, key ); 536 537 if ( SCDynamicStoreSetNotificationKeys( __gDAConfigurationPort, keys, NULL ) == FALSE ) 538 { 539 DALogError( "could not create \"configuration changed\" notification." ); 540 exit( EX_SOFTWARE ); 541 } 542 543 CFRelease( key ); 544 CFRelease( keys ); 545 546 /* 547 * Create the "file system mounted" notification. 548 */ 549 550 port = CFMachPortGetPort( __gDAVolumeMountedPort ); 551 552 if ( notify_register_mach_port( "com.apple.system.kernel.mount", &port, NOTIFY_REUSE, &token ) ) 553 { 554 DALogError( "could not create \"file system mounted\" notification." ); 555 exit( EX_SOFTWARE ); 556 } 557 558 /* 559 * Create the "file system unmounted" notification. 560 */ 561 562 port = CFMachPortGetPort( __gDAVolumeUnmountedPort ); 563 564 if ( notify_register_mach_port( "com.apple.system.kernel.unmount", &port, NOTIFY_REUSE, &token ) ) 565 { 566 DALogError( "could not create \"file system unmounted\" notification." ); 567 exit( EX_SOFTWARE ); 568 } 569 570 /* 571 * Create the mount point folder. 572 */ 573 574 if ( __DAMainCreateMountPointFolder( ) == FALSE ) 575 { 576 DALogError( "could not create mount point folder." ); 577 exit( EX_SOFTWARE ); 578 } 579 580 /* 581 * Create the process ID file. 582 */ 583 584 snprintf( path, sizeof( path ), "/var/run/%s.pid", gDAProcessName ); 585 586 file = fopen( path, "w" ); 587 588 if ( file ) 589 { 590 fprintf( file, "%d\n", gDAProcessID ); 591 fclose( file ); 592 } 593 594 /* 595 * Announce our arrival in the debug log. 596 */ 597 598 DALogDebug( "" ); 599 DALogDebug( "server has been started." ); 600 601 if ( gDAConsoleUser ) 602 { 603 DALogDebug( " console user = %@ [%d].", gDAConsoleUser, gDAConsoleUserUID ); 604 } 605 else 606 { 607 DALogDebug( " console user = none." ); 608 } 609 610 /* 611 * Freshen the file system list. 612 */ 613 614 DAFileSystemListRefresh( ); 615 616 /* 617 * Freshen the mount map list. 618 */ 619 620 DAMountMapListRefresh1( ); 621 622 /* 623 * Freshen the mount map list. 624 */ 625 626 DAMountMapListRefresh2( ); 627 628 /* 629 * Freshen the preference list. 630 */ 631 632 DAPreferenceListRefresh( ); 633 634 /* 635 * Process the initial set of media objects in I/O Kit. 636 */ 637 638 _DAMediaDisappearedCallback( NULL, gDAMediaDisappearedNotification ); 639 640 _DAMediaAppearedCallback( NULL, gDAMediaAppearedNotification ); 641 642 /* 643 * Start the server. 644 */ 645 646 CFRunLoopRun( ); 647} 648 649int main( int argc, char * argv[], char * envp[] ) 650{ 651 /* 652 * Start. 653 */ 654 655 char option; 656 657 /* 658 * Initialize. 659 */ 660 661 gDAProcessName = basename( argv[0] ); 662 663 /* 664 * Check credentials. 665 */ 666 667 if ( geteuid( ) ) 668 { 669 fprintf( stderr, "%s: permission denied.\n", gDAProcessName ); 670 671 exit( EX_NOPERM ); 672 } 673 674 /* 675 * Process arguments. 676 */ 677 678 while ( ( option = getopt( argc, argv, "d" ) ) != -1 ) 679 { 680 switch ( option ) 681 { 682 case 'd': 683 { 684 __gDAOptionDebug = TRUE; 685 686 break; 687 } 688 default: 689 { 690 __usage( ); 691 692 break; 693 } 694 } 695 } 696 697 /* 698 * Continue to start up. 699 */ 700 701 signal( SIGTERM, __DAMainSignal ); 702 703 __DAMain( ); 704 705 exit( EX_OK ); 706} 707