DirServiceMain.cpp [plain text]
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/stat.h> // for file and dir stat calls
#include <sys/mount.h> // for file system check
#include <sys/wait.h> // for waitpid() et al
#include <sys/resource.h> // for getrlimit()
#include <fcntl.h> // for open() and O_* flags
#include <paths.h> // for _PATH_DEVNULL
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <signal.h> // for signal handling
#include <time.h>
#include <sys/syscall.h>
#include <sys/kauth.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include <sys/sysctl.h> // for struct kinfo_proc and sysctl()
#include <syslog.h> // for syslog()
#include <asl.h>
#include <vproc.h>
#define USE_SYSTEMCONFIGURATION_PUBLIC_APIS
#include <SystemConfiguration/SystemConfiguration.h> //required for the configd kicker operation
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/IOPMLib.h> //required for power management handling
#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach_init.h>
#include <mach/port.h>
#include <dispatch/dispatch.h>
#include "DirServicesConstPriv.h"
#include "DirServiceMain.h"
#include "ServerControl.h"
#include "CLog.h"
#include "PrivateTypes.h"
#include "DirServicesPriv.h"
#include "DirServicesConst.h"
#include "CPlugInList.h"
#include "CHandlers.h"
#include "DirServicesTypes.h"
#include "CDSLocalPlugin.h"
#include "COSUtils.h"
#include "buildnumber.h"
#define kDSPIDFile "/var/run/DirectoryService.pid"
#define kDSRunningFile "/Library/Preferences/DirectoryService/.DSIsRunning"
using namespace std;
typedef CFTypeRef _XSEventPortCreate( CFAllocatorRef allocator );
typedef int _XSEventPortPostEvent( CFTypeRef inPortRef, CFStringRef inEventType, CFDictionaryRef inEventData );
static _XSEventPortPostEvent *_xsEventPortPostEvent = NULL;
static _XSEventPortCreate *_xsEventPortCreate = NULL;
bool gCacheFlushDisabled = false;
dsBool gServerOS = false; bool gLogAPICalls = false;
bool gDebugLogging = false;
dsBool gDSFWCSBPDebugLogging = false;
bool gIgnoreSunsetTime = false;
dsBool gDSDebugMode = false;
dsBool gDSLocalOnlyMode = false;
dsBool gDSInstallDaemonMode = false;
dsBool gProperShutdown = false;
dsBool gSafeBoot = false;
CFTypeRef gEventPort = NULL;
uint32_t gNumberOfCores = 0;
io_object_t gPMDeregisterNotifier;
io_connect_t gPMKernelPort;
CFRunLoopRef gPluginRunLoop = NULL; DSMutexSemaphore *gKerberosMutex = NULL;
DSEventSemaphore gPluginRunLoopEvent;
mach_port_t gLibinfoMachPort = MACH_PORT_NULL;
mach_port_t gAPIMachPort = MACH_PORT_NULL;
mach_port_t gMembershipMachPort = MACH_PORT_NULL;
extern CDSLocalPlugin *gLocalNode;
#warning VERIFY the version string before each software release
const char* gStrDaemonAppleVersion = "6.5"; const char* gStrDaemonBuildVersion = "unlabeled/engineering";
static void _Usage ( FILE *fp, const char *argv0 )
{
static const char * const _szpUsage =
"Usage:\t%s [-hv]\n"
" -h Display this list of options.\n"
" -v Display the release version.\n";
::fprintf( fp, _szpUsage, argv0 );
}
static void _Version ( FILE *fp )
{
static const char * const _szpUsage =
"Version %s (build %s)\n";
::fprintf( fp, _szpUsage, gStrDaemonAppleVersion, gStrDaemonBuildVersion );
}
static void _AppleVersion ( FILE *fp )
{
static const char * const _szpUsage =
"DirectoryService-%s\n";
::fprintf( fp, _szpUsage, gStrDaemonBuildVersion );
}
static void _AppleOptions ( FILE *fp, const char *argv0 )
{
static const char * const _szpUsage =
"Usage:\t%s [-applexxxxx OR -v]\n"
" -appledebug Run the daemon in debug mode as standalone.\n"
" -appleoptions List these options.\n"
" -appleversion Display the Apple build version.\n"
" -v Display the release version.\n"
" -localonly Separate daemon runs with only the local node accessible.\n";
::fprintf( fp, _szpUsage, argv0 );
}
void NetworkChangeCallBack(SCDynamicStoreRef aSCDStore, CFArrayRef changedKeys, void *callback_argument)
{
bool bNotify = false;
for( CFIndex i=0; i<CFArrayGetCount(changedKeys); i++ )
{
char keyName[256];
CFStringGetCString( (CFStringRef)CFArrayGetValueAtIndex( changedKeys, i ), keyName, sizeof(keyName), kCFStringEncodingUTF8 );
if (strstr(keyName, "/lo0/") != NULL)
{
DbgLog( kLogApplication, "NetworkChangeCallBack key: %s - skipping loopback", keyName );
continue;
}
DbgLog( kLogApplication, "NetworkChangeCallBack key: %s", keyName );
bNotify = true;
}
if ( gSrvrCntl != nil && bNotify )
{
gSrvrCntl->HandleNetworkTransition(); }
}
void dsPMNotificationHandler ( void *refContext, io_service_t service, natural_t messageType, void *notificationID )
{
switch (messageType)
{
case kIOMessageSystemWillPowerOn:
DbgLog( kLogApplication, "dsPMNotificationHandler(): kIOMessageSystemWillPowerOn\n" );
gSrvrCntl->HandleSystemWillPowerOn();
break;
case kIOMessageSystemHasPoweredOn:
break;
case kIOMessageSystemWillSleep:
DbgLog( kLogApplication, "dsPMNotificationHandler(): kIOMessageSystemWillSleep\n" );
gSrvrCntl->HandleSystemWillSleep();
case kIOMessageSystemWillPowerOff:
case kIOMessageCanSystemSleep:
case kIOMessageCanSystemPowerOff:
case kIOMessageCanDevicePowerOff:
IOAllowPowerChange(gPMKernelPort, (long) notificationID); break;
case kIOMessageSystemWillNotSleep:
break;
#ifdef DEBUG
case kIOMessageServiceIsTerminated:
case kIOMessageServiceIsSuspended:
case kIOMessageServiceIsRequestingClose:
case kIOMessageServiceWasClosed:
case kIOMessageServiceIsResumed:
case kIOMessageServiceBusyStateChange:
case kIOMessageDeviceWillPowerOff:
case kIOMessageDeviceWillNotPowerOff:
case kIOMessageSystemWillNotPowerOff:
#endif
default:
break;
}
}
int dsPostEvent( CFStringRef inEventType, CFDictionaryRef inEventData )
{
return ((gEventPort != NULL && _xsEventPortPostEvent != NULL) ? _xsEventPortPostEvent(gEventPort, inEventType, inEventData) : -1);
}
int main ( int argc, char * const *argv )
{
#if DEBUG
OptionBits debugOpts = kLogEverything;
bool bProfiling = true;
OptionBits profileOpts = kLogEverything;
#else
OptionBits debugOpts = kLogMeta;
bool bProfiling = false;
OptionBits profileOpts = kLogMeta;
#endif
char *p = nil;
bool bFound = false;
struct stat statResult;
pid_t ourUID = ::getuid();
static CFBundleRef coreServerBundle = NULL;
if ( sizeof(BUILDNUMBER) > sizeof("") )
gStrDaemonBuildVersion = BUILDNUMBER;
openlog( "DirectoryService", LOG_PID | LOG_NOWAIT, LOG_DAEMON );
if ( argc > 1 )
{
p = strstr( argv[1], "-" );
if ( p != NULL )
{
if ( strstr( p, "appledebug" ) && ourUID == 0 )
{
bFound = true;
debugOpts = kLogEverything;
gDebugLogging = true;
gDSDebugMode = true;
}
if ( strstr( p, "localonly" ) && ourUID == 0 )
{
bFound = true;
gDSLocalOnlyMode = true;
}
if ( strstr( p, "installdaemon" ) && ourUID == 0 )
{
bFound = true;
gDSInstallDaemonMode= true;
}
if ( strstr( p, "appleversion" ) )
{
_AppleVersion( stdout );
::exit( 1 );
}
if ( strstr( p, "appleoptions" ) )
{
_AppleOptions( stdout, argv[0] );
::exit( 1 );
}
if ( strstr( p, "v" ) )
{
_Version( stdout );
::exit( 1 );
}
if ( strstr( p, "h" ) )
{
::_Usage( stderr, argv[0] );
::exit( 1 );
}
if ( bFound == false )
{
::_Usage( stderr, argv[0] );
::exit( 1 );
}
}
else
{
::_Usage( stderr, argv[0] );
::exit( 1 );
}
}
if ( ourUID != 0 )
{
syslog(LOG_ALERT, "DirectoryService needs to be launched as root.\n");
exit( 1 );
}
if ( gDSLocalOnlyMode == false && gDSInstallDaemonMode == false && gDSDebugMode == false &&
lstat("/etc/rc.cdrom", &statResult) == 0 && lstat("/System/Installation", &statResult) == 0 )
{
gDSInstallDaemonMode = true;
syslog( LOG_NOTICE, "Launched version %s (v%s) - installer mode", gStrDaemonAppleVersion, gStrDaemonBuildVersion );
}
else if ( gDSDebugMode == true )
{
printf( "Debug mode enabled.\nTo access this daemon define environment variable DS_DEBUG_MODE.\n" );
}
else
{
syslog( LOG_INFO, "Launched version %s (v%s)", gStrDaemonAppleVersion, gStrDaemonBuildVersion );
}
mach_port_t priv_bootstrap_port = MACH_PORT_NULL;
int status = eDSNoErr;
if (!gDSDebugMode)
{
char* usedPortName = (char *) (gDSLocalOnlyMode == true ? kDSStdMachLocalPortName : kDSStdMachPortName);
status = bootstrap_check_in( bootstrap_port, usedPortName, &gAPIMachPort );
if ( status == BOOTSTRAP_SERVICE_ACTIVE ) {
syslog(LOG_ALERT, "DirectoryService %s instance is already running - exiting this instance", usedPortName );
exit( 0 );
}
assert( status == BOOTSTRAP_SUCCESS );
if ( gDSLocalOnlyMode == false ) {
status = bootstrap_check_in(bootstrap_port, (char *)kDSStdMachDSLookupPortName, &gLibinfoMachPort);
assert( status == BOOTSTRAP_SUCCESS );
status = bootstrap_check_in( bootstrap_port, (char *)kDSStdMachMembershipPortName, &gMembershipMachPort );
assert( status == BOOTSTRAP_SUCCESS );
}
}
else {
status = bootstrap_check_in(bootstrap_port, kDSStdMachPortName"Debug", &gAPIMachPort);
if (status == BOOTSTRAP_SUCCESS)
{
bootstrap_check_in( bootstrap_port, kDSStdMachDSLookupPortName"Debug", &gLibinfoMachPort );
bootstrap_check_in( bootstrap_port, kDSStdMachMembershipPortName"Debug", &gMembershipMachPort );
}
if (status == BOOTSTRAP_SERVICE_ACTIVE)
{
syslog(LOG_ALERT, "DirectoryService debug instance is already running - exiting this instance" );
exit(0);
}
else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
{
syslog(LOG_ALERT, "bootstrap_check_in() for mach_init debug port returned BOOTSTRAP_UNKNOWN_SERVICE so we will create our own portset" );
status = bootstrap_create_server(bootstrap_port, (char *)"/usr/sbin/DirectoryService", 0, false, &priv_bootstrap_port);
if (status == KERN_SUCCESS)
{
mach_port_t (^registerDebugService)(const char *) = ^(const char *service) {
char debugPortName[256];
mach_port_t tempPort = MACH_PORT_NULL;
mach_port_t send_port = MACH_PORT_NULL;
snprintf( debugPortName, sizeof(debugPortName), "%sDebug", service );
kern_return_t kr = bootstrap_check_in( priv_bootstrap_port, debugPortName, &tempPort );
if ( kr != KERN_SUCCESS ) {
kr = bootstrap_create_service( priv_bootstrap_port, debugPortName, &send_port );
if ( kr == KERN_SUCCESS )
{
kr = bootstrap_check_in( priv_bootstrap_port, debugPortName, &tempPort );
if (kr != KERN_SUCCESS)
{
syslog(LOG_ALERT, "unable to create our own debug portset - exiting" );
exit(0);
}
}
}
return tempPort;
};
gAPIMachPort = registerDebugService( kDSStdMachDebugPortName );
if ( gAPIMachPort != MACH_PORT_NULL ) {
gLibinfoMachPort = registerDebugService( kDSStdMachDSLookupPortName );
gMembershipMachPort = registerDebugService( kDSStdMachDSLookupPortName );
}
}
}
}
try
{
if (!gDSLocalOnlyMode)
{
unlink( "/var/run/.DSRunningSP4" );
}
if ( lstat("/System/Library/CoreServices/ServerVersion.plist", &statResult) == 0 ) {
gServerOS = true;
}
if ( gDSDebugMode == true ||
gDSLocalOnlyMode == true ||
gDSInstallDaemonMode == true ||
(stat(kDSPIDFile, &statResult) != 0 && stat(kDSRunningFile, &statResult) != 0) )
{
gProperShutdown = true;
}
if ( !gDSLocalOnlyMode && !gDSInstallDaemonMode && !gDSDebugMode )
{
char pidStr[256];
int fd = open( kDSPIDFile, (O_CREAT | O_TRUNC | O_WRONLY | O_EXLOCK), 0644 );
if ( fd != -1 )
{
snprintf( pidStr, sizeof(pidStr), "%d", getpid() );
write( fd, pidStr, strlen(pidStr) );
close( fd );
}
if ( stat(kDSRunningFile, &statResult) != 0 )
dsTouch( kDSRunningFile );
}
if ( gDebugLogging == false && lstat( "/Library/Preferences/DirectoryService/.DSLogDebugAtStartOnce", &statResult ) == 0 )
{
dsRemove( "/Library/Preferences/DirectoryService/.DSLogDebugAtStartOnce" );
gDebugLogging = true;
debugOpts = kLogEverything;
}
if ( gDebugLogging == false && lstat( "/Library/Preferences/DirectoryService/.DSLogDebugAtStart", &statResult ) == 0 )
{
gDebugLogging = true;
debugOpts = kLogEverything;
}
if (gDSDebugMode)
{
debugOpts |= kLogDebugHeader;
}
CLog::Initialize( kLogEverything, kLogEverything, debugOpts, profileOpts, gDebugLogging, bProfiling, gDSLocalOnlyMode );
SrvrLog( kLogApplication, "\n\n" );
SrvrLog( kLogApplication, "DirectoryService %s (v%s) starting up...",
gStrDaemonAppleVersion,
gStrDaemonBuildVersion );
if ( gProperShutdown == false ) {
DbgLog( kLogCritical, "Improper shutdown detected" );
syslog( LOG_NOTICE, "Improper shutdown detected" );
}
int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
uint32_t sb = 0;
size_t sbsz = sizeof(sb);
if ( sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == -1 )
gSafeBoot = false;
if ( sb == true ) {
gSafeBoot = true;
SrvrLog( kLogApplication, "Safe Boot is enabled" );
}
struct rlimit rl = { FD_SETSIZE, FD_SETSIZE };
if ( setrlimit(RLIMIT_NOFILE, &rl) != 0 ) {
syslog( LOG_NOTICE, "Unable to increase file limit to %d", rl.rlim_cur );
}
dispatch_source_t source;
source = dispatch_source_signal_create( SIGTERM, NULL, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), ^(dispatch_source_t ds) {
DbgLog( kLogInfo, "dsdispatch - SIGTERM - attempting to stop main runloop" );
CFRunLoopStop( CFRunLoopGetMain() );
} );
assert( source != NULL );
signal( SIGTERM, SIG_IGN );
source = dispatch_source_signal_create( SIGUSR1, NULL, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), ^(dispatch_source_t ds){ ServerControl::ResetDebugging(); } );
assert( source != NULL );
signal( SIGUSR1, SIG_IGN );
source = dispatch_source_signal_create( SIGUSR2, NULL, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), ^(dispatch_source_t ds) { ServerControl::ToggleAPILogging(true); } );
assert( source != NULL );
signal( SIGUSR2, SIG_IGN );
source = dispatch_source_signal_create( SIGHUP, NULL, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), ^(dispatch_source_t ds) {
ServerControl::HandleNetworkTransition();
DbgLog( kLogInfo, "dsdispatch - SIGHUP - simulating network transition" );
} );
assert( source != NULL );
signal( SIGHUP, SIG_IGN );
if ( gDSDebugMode == false ) {
signal( SIGINT, SIG_IGN );
}
signal( SIGPIPE, SIG_IGN );
CPluginRunLoopThread *pluginRunLoopThread = new CPluginRunLoopThread();
gPluginRunLoopEvent.ResetEvent();
pluginRunLoopThread->StartThread();
gPluginRunLoopEvent.WaitForEvent();
gKerberosMutex = new DSMutexSemaphore("::gKerberosMutex");
gSrvrCntl = new ServerControl();
if ( gSrvrCntl == nil ) throw( (SInt32)eMemoryAllocError );
if ( gDebugLogging )
{
gSrvrCntl->ResetDebugging(); }
struct stat statBlock;
if ( stat("/System/Library/PrivateFrameworks/CoreServer.framework", &statBlock) == 0 ) {
CFURLRef coreServerURL = CFURLCreateWithFileSystemPath( NULL, CFSTR("/System/Library/PrivateFrameworks/CoreServer.framework"),
kCFURLPOSIXPathStyle, false );
if ( coreServerURL != NULL ) {
coreServerBundle = CFBundleCreate( kCFAllocatorDefault, coreServerURL );
if ( coreServerBundle != NULL ) {
Boolean isLoaded = CFBundleIsExecutableLoaded( coreServerBundle );
if ( isLoaded == FALSE )
isLoaded = CFBundleLoadExecutable( coreServerBundle );
if ( isLoaded == TRUE ) {
_xsEventPortPostEvent = (_XSEventPortPostEvent *) CFBundleGetFunctionPointerForName( coreServerBundle, CFSTR("XSEventPortPostEvent") );
_xsEventPortCreate = (_XSEventPortCreate *) CFBundleGetFunctionPointerForName( coreServerBundle, CFSTR("XSEventPortCreate") );
if ( _xsEventPortPostEvent != NULL && _xsEventPortCreate != NULL )
SrvrLog( kLogApplication, "CoreServer.framework found using for events" );
else
DbgLog( kLogError, "CoreServer.framework found but unable to map functions" );
}
}
DSCFRelease( coreServerURL );
}
}
if ( _xsEventPortCreate != NULL )
gEventPort = _xsEventPortCreate( NULL );
vproc_transaction_t vProcTransaction = NULL;
if ( !gDSLocalOnlyMode && !gDSInstallDaemonMode && !gDSDebugMode )
{
vProcTransaction = vproc_transaction_begin( NULL );
}
sbsz = sizeof( gNumberOfCores );
sysctlbyname( "hw.logicalcpu_max", &gNumberOfCores, &sbsz, NULL, 0 );
SrvrLog( kLogApplication, "Detected %d logical CPUs", gNumberOfCores );
SInt32 startSrvr;
startSrvr = gSrvrCntl->StartUpServer();
if ( startSrvr != eDSNoErr ) throw( startSrvr );
CFRunLoopRun();
CFRunLoopStop( gPluginRunLoop );
gLocalNode->CloseDatabases();
if ( gSrvrCntl != NULL )
{
SrvrLog( kLogApplication, "Shutting down DirectoryService..." );
gSrvrCntl->ShutDownServer();
}
if ( !gDSLocalOnlyMode && !gDSInstallDaemonMode && !gDSDebugMode )
{
fcntl( 0, F_FULLFSYNC );
dsRemove( kDSRunningFile );
dsRemove( kDSPIDFile );
if ( vProcTransaction != NULL )
vproc_transaction_end( NULL, vProcTransaction );
}
}
catch ( SInt32 err )
{
DbgLog( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ );
DbgLog( kLogApplication, " ***main() error = %d.", err );
}
catch( ... )
{
DbgLog( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ );
DbgLog( kLogApplication, " *** Caught an unexpected exception in main()!!!!" );
}
exit( 0 );
}