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()
#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 "DirServiceMain.h"
#include "ServerControl.h"
#include "CLog.h"
#include "PrivateTypes.h"
#include "DirServicesPriv.h"
#include "DirServicesConst.h"
#include "CPlugInList.h"
#include "DirServicesTypes.h"
using namespace std;
dsBool gServerOS = false; dsBool gLogAPICalls = false;
dsBool gDebugLogging = false;
dsBool gDSFWCSBPDebugLogging = false;
CFAbsoluteTime gSunsetTime = 0;
io_object_t gPMDeregisterNotifier;
io_connect_t gPMKernelPort;
CFRunLoopRef gServerRunLoop = NULL;
DSMutexSemaphore *gKerberosMutex = NULL;
mach_port_t gServerMachPort = MACH_PORT_NULL;
#warning VERIFY the version string before each distinct build submission
const char* gStrDaemonAppleVersion = "2.1";
const char* gStrDaemonBuildVersion = "353.5";
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 + 1, 0,
0,
0,
LoggingTimerCallBack,
NULL );
CFRunLoopAddTimer( gServerRunLoop, 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)
{
}
}
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 =
"Apple Computer, Inc. Version DirectoryService %s\n";
::fprintf( fp, _szpUsage, gStrDaemonAppleVersion );
}
static void _AppleVersion ( FILE *fp )
{
static const char * const _szpUsage =
"Apple Computer, Inc. Version 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"
" -appleperformance Log everything and run in the foreground.\n"
" -appleversion Display the Apple build version.\n"
" -v Display the release version.\n";
::fprintf( fp, _szpUsage, argv0 );
}
void LoggingTimerCallBack( CFRunLoopTimerRef timer, void *info )
{
if ( gLogAPICalls && CFAbsoluteTimeGetCurrent() >= gSunsetTime )
{
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)
{
for( CFIndex i=0; i<CFArrayGetCount(changedKeys); i++ )
{
char keyName[256];
CFStringGetCString( (CFStringRef)CFArrayGetValueAtIndex( changedKeys, i ), keyName, sizeof(keyName), kCFStringEncodingUTF8 );
DBGLOG1( kLogApplication, "NetworkChangeCallBack key: %s", keyName );
}
if ( gSrvrCntl != nil )
{
gSrvrCntl->HandleNetworkTransition(); }
}
void dsPMNotificationHandler ( void *refContext, io_service_t service, natural_t messageType, void *notificationID )
{
switch (messageType)
{
case kIOMessageSystemHasPoweredOn:
DBGLOG( kLogApplication, "dsPMNotificationHandler(): kIOMessageSystemHasPoweredOn\n" );
break;
case kIOMessageSystemWillPowerOn:
DBGLOG( kLogApplication, "dsPMNotificationHandler(): kIOMessageSystemWillPowerOn\n" );
gSrvrCntl->HandleSystemWillPowerOn();
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
sys_server_status(char *name)
{
kern_return_t status;
int active;
status = bootstrap_status(bootstrap_port, name, &active);
if (status == BOOTSTRAP_UNKNOWN_SERVICE) return 0;
if (status != KERN_SUCCESS) return -1;
return active;
}
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();
bool bDebugMode = false;
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;
bDebugMode = true;
}
if ( strstr( p, "appleperformance" ) && ourUID == 0 )
{
bFound = true;
bProfiling = true;
profileOpts = kLogEverything;
}
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 );
if (!bDebugMode)
{
mach_port_t send_port = MACH_PORT_NULL;
mach_port_t priv_bootstrap_port = MACH_PORT_NULL;
int status = sys_server_status(kDSStdMachPortName);
if (status == BOOTSTRAP_STATUS_ACTIVE)
{
syslog(LOG_ALERT, "DirectoryService is already running!\n");
exit(0);
}
status = bootstrap_check_in(bootstrap_port, kDSStdMachPortName, &gServerMachPort);
if (status == BOOTSTRAP_SUCCESS)
{
priv_bootstrap_port = bootstrap_port;
}
else if (status == BOOTSTRAP_NOT_PRIVILEGED)
{
syslog(LOG_ALERT, "DirectoryService instance is already starting up - exiting this instance" );
exit(0);
}
else if (status == BOOTSTRAP_SERVICE_ACTIVE)
{
syslog(LOG_ALERT, "DirectoryService instance is already running - exiting this instance" );
exit(0);
}
else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
{
syslog(LOG_ALERT, "bootstrap_check_in() for mach_init port returned BOOTSTRAP_UNKNOWN_SERVICE so we'll 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, kDSStdMachPortName, &send_port);
if (status == KERN_SUCCESS)
{
status = bootstrap_check_in(priv_bootstrap_port, kDSStdMachPortName, &gServerMachPort);
if (status != KERN_SUCCESS)
{
syslog(LOG_ALERT, "unable to create our own portset - exiting" );
exit(0);
}
}
}
}
status = bootstrap_unprivileged(priv_bootstrap_port, &bootstrap_port);
if (status != BOOTSTRAP_SUCCESS)
{
syslog(LOG_ALERT, "bootstrap_unprivileged() for bootstrap port did not return BOOTSTRAP_SUCCESS so forked processes may block restarts of DirectoryService" );
}
status = task_set_bootstrap_port(mach_task_self(), bootstrap_port);
if (status != BOOTSTRAP_SUCCESS)
{
syslog(LOG_ALERT, "task_set_bootstrap_port() for bootstrap port did not return BOOTSTRAP_SUCCESS so forked processes may block restarts of DirectoryService" );
}
int mib[6] = { 0 };
int oldstate = 0;
size_t oldsize = 4;
int newstate = 1;
mib[0] = CTL_KERN;
mib[1] = KERN_PROCDELAYTERM;
if (sysctl(mib, 2, &oldstate, &oldsize, &newstate, 4) < 0)
{
syslog(LOG_INFO, "cannot mark for delayed termination");
}
}
try
{
unlink( "/var/run/.DSRunningSP4" );
if (stat( "/System/Library/CoreServices/ServerVersion.plist", &statResult ) == eDSNoErr)
{
gServerOS = true;
}
if (!gDebugLogging && stat( "/Library/Preferences/DirectoryService/.DSLogDebugAtStart", &statResult ) == eDSNoErr)
{
gDebugLogging = true;
debugOpts = kLogEverything;
}
CLog::Initialize( kLogEverything, kLogEverything, debugOpts, profileOpts, gDebugLogging, bProfiling );
SRVRLOG( kLogApplication, "\n\n" );
SRVRLOG2( 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);
gServerRunLoop = CFRunLoopGetCurrent();
gKerberosMutex = new DSMutexSemaphore();
gSrvrCntl = new ServerControl();
if ( gSrvrCntl == nil ) throw( (sInt32)eMemoryAllocError );
if ( gDebugLogging )
{
gSrvrCntl->ResetDebugging(); }
sInt32 startSrvr;
startSrvr = gSrvrCntl->StartUpServer();
if ( startSrvr != eDSNoErr ) throw( startSrvr );
::CFRunLoopRun();
if ( gSrvrCntl != NULL )
{
SRVRLOG( kLogApplication, "Shutting down DirectoryService..." );
gSrvrCntl->ShutDownServer();
}
}
catch ( sInt32 err )
{
DBGLOG2( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ );
DBGLOG1( kLogApplication, " ***main() error = %d.", err );
}
catch( ... )
{
DBGLOG2( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ );
DBGLOG( kLogApplication, " *** Caught an unexpected exception in main()!!!!" );
}
exit( 0 );
}