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 <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>
#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>
#if HAVE_CORE_SERVER
#include <XSEventPort.h>
#endif
#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;
dsBool gServerOS = false; dsBool gLogAPICalls = false;
dsBool gDebugLogging = false;
dsBool gDSFWCSBPDebugLogging = false;
dsBool gIgnoreSunsetTime = false;
dsBool gDSDebugMode = false;
dsBool gDSLocalOnlyMode = false;
dsBool gDSInstallDaemonMode = false;
dsBool gProperShutdown = false;
CFAbsoluteTime gSunsetTime = 0;
#if HAVE_CORE_SERVER
XSEventPortRef gEventPort = NULL;
#else
void *gEventPort = NULL;
#endif
io_object_t gPMDeregisterNotifier;
io_connect_t gPMKernelPort;
CFRunLoopRef gPluginRunLoop = NULL; DSMutexSemaphore *gKerberosMutex = NULL;
mach_port_t gMachMIGSet = MACH_PORT_NULL;
DSEventSemaphore gPluginRunLoopEvent;
extern CDSLocalPlugin *gLocalNode;
#warning VERIFY the version string before each software release
const char* gStrDaemonAppleVersion = "5.0";
const char* gStrDaemonBuildVersion = "unlabeled/engineering";
enum
{
kSignalMessage = 1000
};
typedef struct SignalMessage
{
mach_msg_header_t header;
mach_msg_body_t body;
int signum;
mach_msg_audit_trailer_t trailer;
} SignalMessage;
void LoggingTimerCallBack( CFRunLoopTimerRef timer, void *info );
static void SignalHandler(int signum);
static void SignalMessageHandler(CFMachPortRef port,SignalMessage *msg,CFIndex size,void *info);
static mach_port_t gSignalPort = MACH_PORT_NULL;
void SignalHandler(int signum)
{
SignalMessage msg;
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0);
msg.header.msgh_size = sizeof(msg) - sizeof(mach_msg_trailer_t);
msg.header.msgh_remote_port = gSignalPort;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_id = kSignalMessage;
msg.body.msgh_descriptor_count = 0;
msg.signum = signum;
mach_msg(&msg.header,(MACH_SEND_MSG | MACH_SEND_TIMEOUT),
msg.header.msgh_size,0,MACH_PORT_NULL,0,MACH_PORT_NULL);
}
static void _HandleSIGTERM ( ... )
{
CFRunLoopStop(CFRunLoopGetCurrent());
}
static void _HandleSigHup ( ... )
{
if ( gSrvrCntl != nil )
{
gSrvrCntl->HandleNetworkTransition(); }
}
static void _HandleSIGUSR1 ( ... )
{
if ( gSrvrCntl != nil )
{
gSrvrCntl->ResetDebugging(); }
}
static void _HandleSIGUSR2 ( ... )
{
if (gLogAPICalls)
{
gLogAPICalls = false;
syslog(LOG_ALERT,"Logging of API Calls turned OFF after receiving USR2 signal.");
}
else
{
gLogAPICalls = true;
gSunsetTime = CFAbsoluteTimeGetCurrent() + 300;
CFRunLoopTimerRef timer = CFRunLoopTimerCreate( kCFAllocatorDefault,
gSunsetTime, 0,
0,
0,
LoggingTimerCallBack,
NULL );
CFRunLoopAddTimer( CFRunLoopGetMain(), timer, kCFRunLoopDefaultMode );
CFRelease( timer );
timer = NULL;
syslog(LOG_ALERT,"Logging of API Calls turned ON after receiving USR2 signal.");
}
}
void SignalMessageHandler(CFMachPortRef port,SignalMessage *msg,CFIndex size,void *info)
{
if (msg->signum == SIGUSR1)
{
_HandleSIGUSR1();
}
else if (msg->signum == SIGUSR2)
{
_HandleSIGUSR2();
}
else if (msg->signum == SIGHUP)
{
_HandleSigHup();
}
else if ( (msg->signum == SIGTERM) || (msg->signum == SIGABRT) )
{
_HandleSIGTERM();
}
else if (msg->signum == SIGPIPE || msg->signum == SIGURG)
{
}
}
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 LoggingTimerCallBack( CFRunLoopTimerRef timer, void *info )
{
if ( gLogAPICalls && !gIgnoreSunsetTime )
{
gLogAPICalls = false;
syslog(LOG_CRIT,"Logging of API Calls automatically turned OFF at reaching sunset duration of five minutes.");
}
}
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, (SInt32)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;
}
}
void RedirectCrashReporting( void )
{
mach_port_t exceptionPort = MACH_PORT_NULL;
bootstrap_look_up(bootstrap_port, "com.apple.ReportCrash.DirectoryService", &exceptionPort);
mach_msg_type_number_t count = 0;
exception_mask_t originalMasks[EXC_TYPES_COUNT];
exception_port_t originalPorts[EXC_TYPES_COUNT];
exception_behavior_t originalBehaviors[EXC_TYPES_COUNT];
thread_state_flavor_t originalFlavors[EXC_TYPES_COUNT];
#if defined(EXC_MASK_CRASH)
kern_return_t err = task_get_exception_ports(mach_task_self(), EXC_MASK_CRASH, originalMasks, &count, originalPorts, originalBehaviors, originalFlavors);
#else
kern_return_t err = task_get_exception_ports(mach_task_self(), EXC_MASK_RPC_ALERT, originalMasks, &count, originalPorts, originalBehaviors, originalFlavors);
#endif
if (err != KERN_SUCCESS) {
asl_log(NULL, NULL, ASL_LEVEL_ERR, "task_get_exception_ports() failed with error (%d: %s)", err, mach_error_string(err));
count = 0;
}
unsigned int i;
for (i = 0; i < count; i++) {
err = task_set_exception_ports(mach_task_self(), originalMasks[i], exceptionPort, originalBehaviors[i], originalFlavors[i]);
if (err != KERN_SUCCESS) {
asl_log(NULL, NULL, ASL_LEVEL_ERR, "task_set_exception_ports(%X, %X, %X, %X, %X) failed with error (%d: %s)", mach_task_self(), originalMasks[i], exceptionPort, originalBehaviors[i], originalFlavors[i], err, mach_error_string(err));
}
}
}
int dsPostEvent( CFStringRef inEventType, CFDictionaryRef inEventData )
{
#if HAVE_CORE_SERVER
return ((gEventPort != NULL) ? XSEventPortPostEvent(gEventPort, inEventType, inEventData) : -1);
#else
return 0;
#endif
}
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();
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 );
}
syslog(LOG_ALERT,"Launched version %s (v%s)", gStrDaemonAppleVersion, gStrDaemonBuildVersion );
mach_port_t send_port = MACH_PORT_NULL;
mach_port_t priv_bootstrap_port = MACH_PORT_NULL;
mach_port_t tempMachPort = MACH_PORT_NULL;
int status = eDSNoErr;
mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &gMachMIGSet );
if (!gDSDebugMode)
{
char* usedPortName = nil;
if (gDSLocalOnlyMode) {
usedPortName = kDSStdMachLocalPortName;
}
else
{
usedPortName = kDSStdMachPortName;
}
status = bootstrap_check_in(bootstrap_port, usedPortName, &tempMachPort);
if ( status == BOOTSTRAP_SUCCESS )
{
mach_port_move_member( mach_task_self(), tempMachPort, gMachMIGSet );
tempMachPort = MACH_PORT_NULL;
if ( !gDSLocalOnlyMode )
{
status = bootstrap_check_in(bootstrap_port, kDSStdMachDSLookupPortName, &tempMachPort);
if ( status == BOOTSTRAP_SUCCESS )
{
mach_port_move_member( mach_task_self(), tempMachPort, gMachMIGSet );
tempMachPort = MACH_PORT_NULL;
}
}
}
if (status == BOOTSTRAP_SERVICE_ACTIVE)
{
syslog(LOG_ALERT, "DirectoryService %s instance is already running - exiting this instance", usedPortName );
exit(0);
}
else if ( (status != BOOTSTRAP_SUCCESS) && gDSLocalOnlyMode )
{
syslog(LOG_ALERT, "bootstrap_check_in() for mach_init local port returned BOOTSTRAP_UNKNOWN_SERVICE so we will create our own portset" );
status = bootstrap_create_server(bootstrap_port, "/usr/sbin/DirectoryService -localonly", 0, true, &priv_bootstrap_port);
if (status == KERN_SUCCESS)
{
status = bootstrap_create_service(priv_bootstrap_port, kDSStdMachLocalPortName, &send_port);
if (status == KERN_SUCCESS)
{
status = bootstrap_check_in(priv_bootstrap_port, kDSStdMachLocalPortName, &tempMachPort);
if (status != KERN_SUCCESS)
{
syslog(LOG_ALERT, "unable to bootstrap_check_in our own debug portset - exiting" );
exit(0);
}
mach_port_move_member( mach_task_self(), tempMachPort, gMachMIGSet );
tempMachPort = MACH_PORT_NULL;
}
else
{
syslog(LOG_ALERT, "unable to bootstrap_create_service our own debug portset - exiting" );
exit(0);
}
}
}
else if (status != BOOTSTRAP_SUCCESS) {
syslog(LOG_ALERT, "launchd has failed to launch DirectoryService %s instance - exiting this instance with error <%d>", usedPortName, status );
exit(0);
}
}
else {
status = bootstrap_check_in(bootstrap_port, kDSStdMachDebugPortName, &tempMachPort);
if (status == BOOTSTRAP_SUCCESS)
{
mach_port_move_member( mach_task_self(), tempMachPort, gMachMIGSet );
tempMachPort = MACH_PORT_NULL;
status = bootstrap_check_in(bootstrap_port, kDSStdMachDSLookupPortName, &tempMachPort);
if (status == BOOTSTRAP_SUCCESS)
{
mach_port_move_member( mach_task_self(), tempMachPort, gMachMIGSet );
tempMachPort = MACH_PORT_NULL;
}
}
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, "/usr/sbin/DirectoryService", 0, false, &priv_bootstrap_port);
if (status == KERN_SUCCESS)
{
status = bootstrap_create_service(priv_bootstrap_port, kDSStdMachDebugPortName, &send_port);
if (status == KERN_SUCCESS)
{
status = bootstrap_check_in(priv_bootstrap_port, kDSStdMachDebugPortName, &tempMachPort);
if (status != KERN_SUCCESS)
{
syslog(LOG_ALERT, "unable to create our own debug portset - exiting" );
exit(0);
}
mach_port_move_member( mach_task_self(), tempMachPort, gMachMIGSet );
tempMachPort = MACH_PORT_NULL;
status = bootstrap_check_in(priv_bootstrap_port, kDSStdMachDSLookupPortName, &tempMachPort);
if (status != KERN_SUCCESS)
{
status = bootstrap_create_service(priv_bootstrap_port, kDSStdMachDSLookupPortName, &send_port);
if (status == KERN_SUCCESS)
{
status = bootstrap_check_in(priv_bootstrap_port, kDSStdMachDSLookupPortName, &tempMachPort);
if (status != KERN_SUCCESS)
{
syslog(LOG_ALERT, "unable to create our own debug portset - exiting" );
exit(0);
}
mach_port_move_member( mach_task_self(), tempMachPort, gMachMIGSet );
tempMachPort = MACH_PORT_NULL;
}
}
}
}
}
}
try
{
if (!gDSLocalOnlyMode)
{
unlink( "/var/run/.DSRunningSP4" );
}
if (stat( "/System/Library/CoreServices/ServerVersion.plist", &statResult ) == eDSNoErr)
{
gServerOS = true;
}
if (gDSLocalOnlyMode || gDSInstallDaemonMode || stat(kDSPIDFile, &statResult) != 0 ||
stat(kDSRunningFile, &statResult) != 0)
{
gProperShutdown = true;
if ( !gDSLocalOnlyMode && !gDSInstallDaemonMode )
{
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 );
}
dsTouch( kDSRunningFile );
}
}
if (!gDebugLogging && stat( "/Library/Preferences/DirectoryService/.DSLogDebugAtStart", &statResult ) == eDSNoErr)
{
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 );
mach_port_limits_t limits = { 1 };
CFMachPortRef port;
port = CFMachPortCreate(NULL,(CFMachPortCallBack)SignalMessageHandler,NULL,NULL);
CFRunLoopAddSource( CFRunLoopGetCurrent(),
CFMachPortCreateRunLoopSource(NULL,port,0),
kCFRunLoopCommonModes);
gSignalPort = CFMachPortGetPort(port);
mach_port_set_attributes( mach_task_self(),
gSignalPort,
MACH_PORT_LIMITS_INFO,
(mach_port_info_t)&limits,
sizeof(limits) / sizeof(natural_t));
signal(SIGTERM,SignalHandler);
signal(SIGHUP,SignalHandler);
signal(SIGUSR1,SignalHandler);
signal(SIGUSR2,SignalHandler);
signal(SIGABRT,SignalHandler);
signal(SIGINT,SignalHandler);
signal(SIGPIPE,SignalHandler);
signal(SIGURG,SignalHandler);
CPluginRunLoopThread *pluginRunLoopThread = new CPluginRunLoopThread();
gPluginRunLoopEvent.ResetEvent();
pluginRunLoopThread->StartThread();
gPluginRunLoopEvent.WaitForEvent();
gKerberosMutex = new DSMutexSemaphore("::gKerberosMutex");
if( !gDSDebugMode )
{
RedirectCrashReporting();
}
gSrvrCntl = new ServerControl();
if ( gSrvrCntl == nil ) throw( (SInt32)eMemoryAllocError );
if ( gDebugLogging )
{
gSrvrCntl->ResetDebugging(); }
#if HAVE_CORE_SERVER
gEventPort = XSEventPortCreate( NULL );
#endif
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 )
{
dsRemove( kDSRunningFile );
dsRemove( kDSPIDFile );
}
}
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 );
}