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 <SystemConfiguration/SCDynamicStorePrivate.h>
#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 "COSUtils.h"
#include "CPlugInList.h"
#include "DirServicesTypes.h"
using namespace std;
dsBool gLogAPICalls = false;
dsBool gDebugLogging = false;
time_t gSunsetTime = time( nil);
io_object_t gPMDeregisterNotifier;
io_connect_t gPMKernelPort;
CFRunLoopRef gServerRunLoop = NULL;
static bool _Terminate = false;
static pid_t _ChildPid = 0;
static int _ChildStatus = 0;
enum
{
kSignalMessage = 1000
};
typedef struct SignalMessage
{
mach_msg_header_t header;
mach_msg_body_t body;
int signum;
mach_msg_trailer_t trailer;
} SignalMessage;
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 _HandleSigChildInParent ( ... )
{
int nStatus = 0;
pid_t pidChild;
pidChild = ::waitpid( -1, &nStatus, WNOHANG );
if ( pidChild == 0 )
{
return;
}
if ( pidChild != _ChildPid )
{
return;
}
if ( WIFEXITED( nStatus ) )
{
::fprintf( stderr, "Daemonization failed with exit status %d.\n", WEXITSTATUS( nStatus ) );
}
else if ( WIFSIGNALED( nStatus ) )
{
::fprintf( stderr, "Daemonization failed due to signal %d.\n", WTERMSIG( nStatus ) );
}
else
{
::fprintf(stderr, "Daemonization failed with status %d.\n", nStatus );
}
_ChildStatus = nStatus;
_Terminate = true;
}
static void _HandleSigTermInParent ( ... )
{
_Terminate = true;
#if DEBUG
fprintf( stderr, "Caught a terminating signal (SIGTERM or SIGABRT)\n" );
fflush( stderr );
#endif
}
static void _HandleSIGTERM ( ... )
{
CFRunLoopStop(CFRunLoopGetCurrent());
}
static void _HandleSigHup ( ... )
{
if ( gSrvrCntl != nil )
{
gSrvrCntl->HandleNetworkTransition(); }
}
static void _HandleSIGUSR1 ( ... )
{
if (gDebugLogging)
{
gDebugLogging = false;
CLog::StopDebugLog();
syslog(LOG_INFO,"Debug Logging turned OFF after receiving USR1 signal.");
}
else
{
gDebugLogging = true;
CLog::StartDebugLog();
syslog(LOG_INFO,"Debug Logging turned ON after receiving USR1 signal.");
}
}
static void _HandleSIGUSR2 ( ... )
{
if (gLogAPICalls)
{
gLogAPICalls = false;
syslog(LOG_INFO,"Logging of API Calls turned OFF after receiving USR2 signal.");
}
else
{
gLogAPICalls = true;
gSunsetTime = time(nil) + 300;
syslog(LOG_INFO,"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();
}
}
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, COSUtils::GetStringFromList( kSysStringListID, kStrVersion ) );
}
static void _AppleVersion ( FILE *fp )
{
static const char * const _szpUsage =
"Apple Computer, Inc. Version DirectoryService-%s\n";
::fprintf( fp, _szpUsage, COSUtils::GetStringFromList( kSysStringListID, kStrBuildNumber ) );
}
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"
" -appleframework Start the daemon like the Framework does using configd(normal operation).\n"
" -applenodaemon Run the daemon in the foreground.\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 );
}
boolean_t NetworkChangeCallBack(SCDynamicStoreRef aSCDStore, void *callback_argument)
{
if ( gSrvrCntl != nil )
{
gSrvrCntl->HandleNetworkTransition(); }
return true;
}
void dsPMNotificationHandler ( void *refContext, io_service_t service, natural_t messageType, void *notificationID )
{
switch (messageType)
{
case kIOMessageSystemWillSleep:
case kIOMessageSystemWillPowerOff:
break;
case kIOMessageSystemWillNotSleep:
case kIOMessageSystemHasPoweredOn:
break;
#ifdef DEBUG
case kIOMessageServiceIsTerminated:
case kIOMessageServiceIsSuspended:
case kIOMessageServiceIsRequestingClose:
case kIOMessageServiceWasClosed:
case kIOMessageServiceIsResumed:
case kIOMessageServiceBusyStateChange:
case kIOMessageCanDevicePowerOff:
case kIOMessageDeviceWillPowerOff:
case kIOMessageDeviceWillNotPowerOff:
case kIOMessageDeviceHasPoweredOn:
case kIOMessageCanSystemPowerOff:
case kIOMessageSystemWillNotPowerOff:
case kIOMessageCanSystemSleep:
#endif
default:
break;
}
}
int main ( int argc, char * const *argv )
{
#if DEBUG
bool bDebug = true;
OptionBits debugOpts = kLogEverything;
bool bProfiling = true;
OptionBits profileOpts = kLogEverything;
#else
bool bDebug = false;
OptionBits debugOpts = kLogMeta;
bool bProfiling = false;
OptionBits profileOpts = kLogMeta;
#endif
bool bDaemonize = true;
kern_return_t machErr = eDSNoErr;
mach_port_t aPort = 0;
mach_port_t bPort = 0;
char *p = nil;
bool bFound = false;
SCDynamicStoreRef scdStore = 0;
Boolean scdStatus = FALSE;
pid_t pidval1 = -1;
pid_t pidval2 = -1;
struct sigaction sSigAction;
struct sigaction sSigOldAction;
sigset_t sSigMask = 0;
if ( argc > 1 )
{
p = strstr( argv[1], "-" );
if ( strstr( argv[1], "daemon:DirectoryService") )
{
bDaemonize = true;
}
else if ( p != nil )
{
if ( strstr( p, "appledebug" ) )
{
bFound = true;
bDaemonize = false;
bDebug = true;
debugOpts = kLogEverything;
}
if ( strstr( p, "applenodaemon" ) )
{
bFound = true;
bDaemonize = false;
}
if ( strstr( p, "appleperformance" ) )
{
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, "appleframework" ) )
{
scdStore = SCDynamicStoreCreate(NULL, CFSTR("DirectoryService"), NULL, NULL); if (scdStore != 0)
{
scdStatus = SCDynamicStoreNotifyValue(scdStore, CFSTR("daemon:DirectoryService"));
CFRelease(scdStore);
if (scdStatus)
{
exit(0);
}
}
bFound = true;
printf("DirectoryService: Continuing after failing to relaunch itself via configd kicker.\n");
printf("DirectoryService: No guarantee that process in in the global mach port space.\n");
}
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 );
}
}
machErr = task_get_bootstrap_port( mach_task_self(), &aPort );
if ( machErr != eDSNoErr )
{
printf("Unable to launch DirectoryService server.\n");
printf(" Error: task_get_bootstrap_port() failed: %s \n", mach_error_string( machErr ));
exit( 0 );
}
machErr = ::bootstrap_look_up( aPort, (char *)"DirectoryService", &bPort );
if ( machErr == eDSNoErr )
{
printf("DirectoryService server is already running.\n");
printf("Unable to bind to mach port.\n");
printf("Terminating this process.\n");
exit( 0 );
}
if ( bDaemonize == true )
{
::memset( &sSigAction, 0, sizeof( struct sigaction ) );
::memset( &sSigOldAction, 0, sizeof( struct sigaction ) );
sSigAction.sa_mask = 0;
sSigAction.sa_flags = 0;
sigemptyset( &sSigAction.sa_mask );
sSigAction.sa_handler = (void (*) (int))_HandleSigTermInParent;
::sigaction( SIGTERM, &sSigAction, &sSigOldAction );
sSigAction.sa_handler = (void (*) (int))_HandleSigTermInParent;
sSigAction.sa_flags = 0;
::sigaction( SIGABRT, &sSigAction, &sSigOldAction );
sSigAction.sa_handler = (void (*) (int))_HandleSigChildInParent;
sSigAction.sa_flags |= SA_NOCLDSTOP;
::sigaction( SIGCHLD, &sSigAction, &sSigOldAction );
sSigAction.sa_handler = (void (*) (int))SIG_IGN;
sSigAction.sa_flags = 0;
::sigaction( SIGPIPE, &sSigAction, &sSigOldAction );
sigemptyset( &sSigMask );
_ChildPid = ::fork();
if ( 0 < _ChildPid )
{
if ( bDebug == true )
{
printf("Child forked, new pid is %d \n", (int)_ChildPid);
}
while ( !_Terminate )
{
::sigsuspend( &sSigMask );
}
if ( bDebug == true )
{
::printf( "Exiting; child status %x.\n", _ChildStatus );
}
::exit( _ChildStatus );
return( _ChildStatus );
}
else if ( _ChildPid == -1 )
{
::printf( "fork() failed: errno= %d \n", (int)errno );
::exit (errno);
return errno;
}
else if ( _ChildPid == 0 )
{
pidval1 = ::getpid();
::setpgid( 0, pidval1 );
pidval2 = ::getppid();
if (pidval2 != -1)
{
::kill( pidval2, SIGTERM );
}
}
execl( "/usr/sbin/", "DirectoryService", "-applenodaemon", NULL );
if ( bDebug == true )
{
::sleep( 20 );
}
}
else
{
::fprintf( stderr, "Daemonization off.\n" );
}
try
{
CLog::Initialize( kLogEverything, kLogEverything, debugOpts, profileOpts, bDebug, bProfiling );
SRVRLOG( kLogApplication, "\n\n" );
SRVRLOG2( kLogApplication, "DirectoryService %s (v%s) starting up...",
COSUtils::GetStringFromList( kSysStringListID, kStrVersion ),
COSUtils::GetStringFromList( kSysStringListID, kStrBuildNumber ) );
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();
gSrvrCntl = new ServerControl();
if ( gSrvrCntl == nil ) throw( (sInt32)eMemoryAllocError );
sInt32 startSrvr;
startSrvr = gSrvrCntl->StartUpServer();
if ( startSrvr != eDSNoErr ) throw( startSrvr );
::CFRunLoopRun();
if ( gSrvrCntl != NULL )
{
SRVRLOG( kLogApplication, "Shutting down DirectoryService..." );
gSrvrCntl->ShutDownServer();
}
}
catch ( eAppError 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 );
}