/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /*! * @header ServerControl */ #if DEBUG_SRVR # include // for stderr, fprintf(), et al #endif #include "ServerControl.h" #include "DirServicesConst.h" #include "DirServicesPriv.h" #include "CHandlers.h" #include "DSTCPListener.h" #include "CMsgQueue.h" #include "CRefTable.h" #include "DSMutexSemaphore.h" #include "DSCThread.h" #include "CServerPlugin.h" #include "CPluginHandler.h" #include "CNodeList.h" #include "CLog.h" #include "CPluginConfig.h" #include "SharedConsts.h" #include "CFile.h" #include "CAuditUtils.h" #include "DSMachEndian.h" #include #include #include #include //used for mkdir and stat #include #include //required for power management handling #include // for syslog() #include // for time #include // This is for MIG extern "C" { #include "DirectoryServiceMIGServer.h" } //#include // can't use this header in C++ extern "C" int mbr_reset_cache(); // for memberd flush cache call extern "C" int _lookupd_port(int); // for lookupd flush cache calls extern "C" int _lookup_link(mach_port_t, char *, int *); extern "C" int _lookup_one(mach_port_t, int, char *, int, char **, int *); extern "C" int _lu_running(); extern void LoggingTimerCallBack( CFRunLoopTimerRef timer, void *info ); //power management extern void dsPMNotificationHandler ( void *refContext, io_service_t service, natural_t messageType, void *notificationID ); extern io_object_t gPMDeregisterNotifier; extern io_connect_t gPMKernelPort; //network change extern void NetworkChangeCallBack(SCDynamicStoreRef aSCDStore, CFArrayRef changedKeys, void *callback_argument); extern CFRunLoopRef gServerRunLoop; extern CFAbsoluteTime gSunsetTime; extern dsBool gLogAPICalls; extern dsBool gDebugLogging; extern dsBool gDSFWCSBPDebugLogging; extern bool gServerOS; extern mach_port_t gServerMachPort; // --------------------------------------------------------------------------- // * Globals // // --------------------------------------------------------------------------- CFRunLoopTimerRef ServerControl::fNSPCTimerRef = NULL; CFRunLoopTimerRef ServerControl::fLDFCTimerRef = NULL; CFRunLoopTimerRef ServerControl::fMDFCTimerRef = NULL; CFRunLoopTimerRef ServerControl::fNIASTimerRef = NULL; uInt32 gAPICallCount = 0; ServerControl *gSrvrCntl = nil; CRefTable *gRefTable = nil; CPlugInList *gPlugins = nil; CMsgQueue *gTCPMsgQueue = nil; CPluginConfig *gPluginConfig = nil; CNodeList *gNodeList = nil; CPluginHandler *gPluginHandler = nil; DSMutexSemaphore *gTCPHandlerLock = new DSMutexSemaphore(); //mutex on create and destroy of CHandler threads DSMutexSemaphore *gPerformanceLoggingLock = new DSMutexSemaphore(); //mutex on manipulating performance logging matrix DSMutexSemaphore *gLazyPluginLoadingLock = new DSMutexSemaphore(); //mutex on loading plugins lazily DSMutexSemaphore *gHashAuthFailedMapLock = new DSMutexSemaphore(); //mutex on failed shadow hash login table DSMutexSemaphore *gMachThreadLock = new DSMutexSemaphore(); //mutex on count of mig handler threads DSMutexSemaphore *gTimerMutex = new DSMutexSemaphore(); //mutex for creating/deleting timers uInt32 gDaemonPID; uInt32 gDaemonIPAddress; uInt32 gRefCountWarningLimit = 500; uInt32 gDelayFailedLocalAuthReturnsDeltaInSeconds = 1; uInt32 gMaxHandlerThreadCount = kMaxHandlerThreads; dsBool gToggleDebugging = false; mach_port_t gMachAPISet = 0; map gPIDMachMap; char *gNIHierarchyTagString = nil; uInt32 gActiveMachThreads = 0; uInt32 gActiveLongRequests = 0; bool gFirstNetworkUpAtBoot = false; //PFIXdsBool gLocalNodeNotAvailable = true; static void DoSearchPolicyChange(CFRunLoopTimerRef timer, void *info); void DoSearchPolicyChange(CFRunLoopTimerRef timer, void *info) { if ( info != nil ) { ((ServerControl *)info)->DoNodeSearchPolicyChange(); } }// DoSearchPolicyChange static void DoLookupDaemonFlushCache(CFRunLoopTimerRef timer, void *info); void DoLookupDaemonFlushCache(CFRunLoopTimerRef timer, void *info) { if ( info != nil ) { ((ServerControl *)info)->FlushLookupDaemonCache(); } }// DoLookupDaemonFlushCache static void DoMemberDaemonFlushCache(CFRunLoopTimerRef timer, void *info); void DoMemberDaemonFlushCache(CFRunLoopTimerRef timer, void *info) { if ( info != nil ) { ((ServerControl *)info)->FlushMemberDaemonCache(); } }// DoMemberDaemonFlushCache static void DoNIAutoSwitchNetworkChange(CFRunLoopTimerRef timer, void *info); void DoNIAutoSwitchNetworkChange(CFRunLoopTimerRef timer, void *info) { if ( info != nil ) { ((ServerControl *)info)->NIAutoSwitchCheck(); } }// DoNIAutoSwitchNetworkChange CFStringRef SearchPolicyChangeCopyStringCallback( const void *item ); CFStringRef SearchPolicyChangeCopyStringCallback( const void *item ) { return CFSTR("SearchPolicyChange"); } CFStringRef LookupDaemonFlushCacheCopyStringCallback( const void *item ); CFStringRef LookupDaemonFlushCacheCopyStringCallback( const void *item ) { return CFSTR("LookupDaemonFlushCache"); } CFStringRef MemberDaemonFlushCacheCopyStringCallback( const void *item ); CFStringRef MemberDaemonFlushCacheCopyStringCallback( const void *item ) { return CFSTR("MemberDaemonFlushCache"); } CFStringRef NetworkChangeNIAutoSwitchCopyStringCallback( const void *item ); CFStringRef NetworkChangeNIAutoSwitchCopyStringCallback( const void *item ) { return CFSTR("NetworkChangeNIAutoSwitchCheck"); } void DoPeriodicTask(CFRunLoopTimerRef timer, void *info); CFStringRef PeriodicTaskCopyStringCallback( const void *item ); CFStringRef PeriodicTaskCopyStringCallback( const void *item ) { return CFSTR("PeriodicTask"); } #pragma mark - #pragma mark MIG Support Routines void mig_spawnonceifnecessary( void ) { // need to lock while checking cause we could get a race condition gMachThreadLock->Wait(); // see if we've reached our limit and if we don't have enought threads to handle requests bool bSpawnThread = ( gActiveMachThreads < gMaxHandlerThreadCount && gActiveLongRequests > gActiveMachThreads ); gMachThreadLock->Signal(); if( bSpawnThread ) { CMigHandlerThread* aMigHandlerThread = new CMigHandlerThread(DSCThread::kTSMigHandlerThread, true); if (aMigHandlerThread != NULL) aMigHandlerThread->StartThread(); //we don't keep a handle to the mig handler threads and don't check if they get created } } #pragma mark - #pragma mark MIG Call Handler Routines kern_return_t dsmig_do_checkUsernameAndPassword( mach_port_t server, sStringPtr username, sStringPtr password, int32_t *result, audit_token_t atoken ) { CRequestHandler handler; char *debugDataTag = NULL; gMachThreadLock->Wait(); gActiveLongRequests++; gMachThreadLock->Signal(); // we should spawn the thread after we've incremented the number of requests active, otherwise thread will spawn and exit too soon mig_spawnonceifnecessary(); if ( (gDebugLogging) || (gLogAPICalls) ) { pid_t aPID; audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL ); debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "checkpw()", "Server" ); DBGLOG2( kLogHandler, "%s : dsmig DAC : Username = %s", debugDataTag, username ); } #if USE_BSM_AUDIT uid_t auidp; uid_t euidp; gid_t egidp; uid_t ruidp; gid_t rgidp; pid_t pidp; au_asid_t asidp; au_tid_t tidp; audit_token_to_au32( atoken, &auidp, &euidp, &egidp, &ruidp, &rgidp, &pidp, &asidp, &tidp ); char *textStr = nil; uInt32 bsmEventCode = AuditForThisEvent( kCheckUserNameAndPassword, username, &textStr ); #endif *result = handler.DoCheckUserNameAndPassword( username, password, eDSExact, NULL, NULL ); #if USE_BSM_AUDIT // BSM Audit if ( bsmEventCode > 0 ) { token_t *tok; if ( *result == eDSNoErr ) { tok = au_to_text( textStr ); audit_write_success( bsmEventCode, tok, auidp, euidp, egidp, ruidp, rgidp, pidp, asidp, &tidp ); } else { audit_write_failure( bsmEventCode, textStr, (int)*result, auidp, euidp, egidp, ruidp, rgidp, pidp, asidp, &tidp ); } } DSFreeString( textStr ); // sets to NULL; required #endif if ( debugDataTag ) { DBGLOG3( kLogHandler, "%s : dsmig DAR : Username %s : Result code = %d", debugDataTag, username, *result ); free( debugDataTag ); } gMachThreadLock->Wait(); gActiveLongRequests--; gMachThreadLock->Signal(); return KERN_SUCCESS; } kern_return_t dsmig_do_create_api_session( mach_port_t server, mach_port_t *newServer, audit_token_t atoken ) { mach_port_t oldTargetOfNotification = MACH_PORT_NULL; (void) mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, newServer ); (void) mach_port_move_member( mach_task_self(), *newServer, gMachAPISet ); // Request no-senders notification so we can tell when server dies (void) mach_port_request_notification( mach_task_self(), *newServer, MACH_NOTIFY_NO_SENDERS, 1, *newServer, MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldTargetOfNotification ); // let's get the audit data PID pid_t aPID; audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL ); gMachThreadLock->Wait(); gPIDMachMap[*newServer] = aPID; gMachThreadLock->Signal(); return KERN_SUCCESS; } kern_return_t dsmig_do_api_call( mach_port_t server, mach_msg_type_name_t serverPoly, sComDataPtr msg_data, mach_msg_type_number_t msg_dataCnt, vm_offset_t msg_data_ool, mach_msg_type_number_t msg_data_oolCnt, sComDataPtr reply_msg, mach_msg_type_number_t *reply_msgCnt, vm_offset_t *reply_msg_ool, mach_msg_type_number_t *reply_msg_oolCnt, audit_token_t atoken ) { kern_return_t kr = KERN_FAILURE; sComDataPtr pComData = NULL; uInt32 uiLength = 0; uInt32 dataLength = 0; uInt32 dataSize = 0; if( msg_dataCnt ) { pComData = (sComDataPtr) msg_data; uiLength = msg_dataCnt; dataLength = pComData->fDataLength; dataSize = pComData->fDataSize; #ifdef __LITTLE_ENDIAN__ if (pComData->type.msgt_translate == 1) //need to swap data length if mach msg came from BIG_ENDIAN translator { dataLength = DSGetLong(&pComData->fDataLength, false); dataSize = DSGetLong(&pComData->fDataSize, false); } #endif } else { pComData = (sComDataPtr) msg_data_ool; uiLength = msg_data_oolCnt; dataLength = pComData->fDataLength; dataSize = pComData->fDataSize; #ifdef __LITTLE_ENDIAN__ if (pComData->type.msgt_translate == 1) //need to swap data length if mach msg came from BIG_ENDIAN translator { dataLength = DSGetLong(&pComData->fDataLength, false); dataSize = DSGetLong(&pComData->fDataSize, false); } #endif } // lets see if the packet is big enough.. and see if the length matches the msg_data size minus 1 if( uiLength >= (sizeof(sComData) - 1) ) { if( dataLength == (uiLength - (sizeof(sComData) - 1)) ) { // we need to copy because we will allocate/deallocate it in the handler // but based on the size it thinks it is sComData *pRequest = (sComData *) calloc( sizeof(sComData) + dataSize, 1 ); CRequestHandler handler; bcopy( (void *)pComData, pRequest, uiLength ); gMachThreadLock->Wait(); gActiveLongRequests ++; gMachThreadLock->Signal(); // spawn a thread request mig_spawnonceifnecessary(); #ifdef __LITTLE_ENDIAN__ if (pRequest->type.msgt_translate == 1) //need to swap since mach msg came from BIG_ENDIAN translator { DSMachEndian swapper(pRequest, kDSSwapToHost); swapper.SwapMessage(); } #endif // let's get the audit data and add it to the sComData audit_token_to_au32( atoken, NULL, (uid_t *)&pRequest->fEffectiveUID, NULL, (uid_t *)&pRequest->fUID, NULL, (pid_t *)&pRequest->fPID, NULL, NULL ); handler.HandleRequest( &pRequest ); gMachThreadLock->Wait(); gActiveLongRequests --; gMachThreadLock->Signal(); // set the PID in the return to our PID for RefTable purposes pRequest->fPID = gDaemonPID; uInt32 dataLen = pRequest->fDataLength; #ifdef __LITTLE_ENDIAN__ if (pRequest->type.msgt_translate == 1) //need to swap since mach msg being sent back to BIG_ENDIAN translator { DSMachEndian swapper(pRequest, kDSSwapToBig); swapper.SwapMessage(); } #endif // if it will fit in the fixed buffer, use it otherwise use OOL if( sizeof(sComData) + dataLen <= *reply_msgCnt ) { *reply_msgCnt = sizeof(sComData) + dataLen - 1; bcopy( pRequest, reply_msg, *reply_msgCnt ); *reply_msg_oolCnt = 0; } else { *reply_msgCnt = 0; // ool, set the other to 0 vm_read( mach_task_self(), (vm_address_t)pRequest, (sizeof(sComData) + dataLen - 1), reply_msg_ool, reply_msg_oolCnt ); } // free our allocated request data... free( pRequest ); pRequest = NULL; gAPICallCount++; if ( (gAPICallCount % 1023) == 1023 ) // every 1023 calls so we can do bit-wise check { if (gLogAPICalls) { syslog(LOG_CRIT,"API clients have called APIs %d times", gAPICallCount); } } kr = KERN_SUCCESS; } else { syslog( LOG_ALERT, "dsmig_do_api_call: Bad message size %d, does not correlate with contents length %d + header %d", uiLength, dataLength, (sizeof(sComData) - 1) ); } } else { syslog( LOG_ALERT, "dsmig_do_api_call message is too small to be valid message %d < %d", uiLength, sizeof(sComData) - 1 ); } if( msg_data_oolCnt ) { vm_deallocate( mach_task_self(), msg_data_ool, msg_data_oolCnt ); } return kr; } #pragma mark - #pragma mark ServerControl Routines // --------------------------------------------------------------------------- // * ServerControl() // // --------------------------------------------------------------------------- ServerControl::ServerControl ( void ) { gDaemonPID = getpid(); //we use a non valid IP Address of zero for ourselves gDaemonIPAddress = 0; fTCPListener = nil; fTCPHandlerThreadsCnt = 0; fSCDStore = 0; fPerformanceStatGatheringActive = false; //default fLookupDaemonFlushCacheRequestCount = 0; fMemberDaemonFlushCacheRequestCount = 0; fHoldStore = NULL; fTCPHandlers = nil; #ifdef BUILD_IN_PERFORMANCE fLastPluginCalled = 0; fPerfTableNumPlugins = 0; fPerfTable = nil; #if PERFORMANCE_STATS_ALWAYS_ON fPerformanceStatGatheringActive = true; #else fPerformanceStatGatheringActive = false; #endif #endif fTCPHandlerSemaphore = new DSSemaphore( fTCPHandlerThreadsCnt ); fServiceNameString = CFStringCreateWithCString( NULL, kDSStdMachPortName, kCFStringEncodingUTF8 ); if (gDaemonPID > 100) //assumption here is that we crashed and restarted so say netowrk is already running ie. we are usually less than 50 { gFirstNetworkUpAtBoot = true; } } // ServerControl // --------------------------------------------------------------------------- // * ~ServerControl() // // --------------------------------------------------------------------------- ServerControl::~ServerControl ( void ) { if ( fTCPHandlerSemaphore != nil ) { delete( fTCPHandlerSemaphore ); fTCPHandlerSemaphore = nil; } } // ~ServerControl // --------------------------------------------------------------------------- // * StartUpServer () // // --------------------------------------------------------------------------- sInt32 ServerControl::StartUpServer ( void ) { sInt32 result = eDSNoErr; struct stat statResult; try { if ( gNodeList == nil ) { gNodeList = new CNodeList(); if ( gNodeList == nil ) throw((sInt32)eMemoryAllocError); } if ( gRefTable == nil ) { gRefTable = new CRefTable( CHandlerThread::RefDeallocProc ); if ( gRefTable == nil ) throw( (sInt32)eMemoryAllocError ); } if ( gPluginConfig == nil ) { gPluginConfig = new CPluginConfig(); if ( gPluginConfig == nil ) throw( (sInt32)eMemoryAllocError ); gPluginConfig->Initialize(); } //gMaxHandlerThreadCount may be discovered in the DS plist config file read by gPluginConfig above fTCPHandlers = (CHandlerThread **)calloc(gMaxHandlerThreadCount, sizeof(CHandlerThread *)); if ( gPlugins == nil ) { gPlugins = new CPlugInList(); if ( gPlugins == nil ) throw( (sInt32)eMemoryAllocError ); } if ( gTCPMsgQueue == nil ) { gTCPMsgQueue = new CMsgQueue(); if ( gTCPMsgQueue == nil ) throw((sInt32)eMemoryAllocError); } if (::stat( "/Library/Preferences/DirectoryService/.DSLogAPIAtStart", &statResult ) == eDSNoErr) { gSunsetTime = CFAbsoluteTimeGetCurrent() + 300; CFRunLoopTimerRef timer = CFRunLoopTimerCreate( kCFAllocatorDefault, gSunsetTime + 1, 0, 0, 0, LoggingTimerCallBack, NULL ); CFRunLoopAddTimer( gServerRunLoop, timer, kCFRunLoopDefaultMode ); CFRelease( timer ); timer = NULL; gLogAPICalls = true; syslog(LOG_ALERT,"Logging of API Calls turned ON at Startup of DS Daemon."); gDebugLogging = true; CLog::StartDebugLog(); syslog(LOG_ALERT,"Debug Logging turned ON at Startup of DS Daemon."); } // let's start the MIG listener fMigListener = new CMigHandlerThread(); if ( fMigListener == nil ) throw((sInt32)eMemoryAllocError); fMigListener->StartThread(); // see if we need TCP too if ( ( (::stat( "/Library/Preferences/DirectoryService/.DSTCPListening", &statResult ) == eDSNoErr) || (gServerOS) ) && (::stat( "/Library/Preferences/DirectoryService/.DSTCPNotListening", &statResult ) != eDSNoErr) ) { // Start the TCP listener thread result = StartTCPListener(kDSDefaultListenPort); if ( result != eDSNoErr ) throw( result ); } if ( gPluginHandler == nil ) { gPluginHandler = new CPluginHandler(); if ( gPluginHandler == nil ) throw((sInt32)eMemoryAllocError); //this call could throw gPluginHandler->StartThread(); } result = RegisterForSystemPower(); if ( result != eDSNoErr ) throw( result ); result = (sInt32)RegisterForNetworkChange(); if ( result != eDSNoErr ) throw( result ); result = SetUpPeriodicTask(); if ( result != eDSNoErr ) throw( result ); //at boot we wait the same as a network transition for the NI auto switch check HandleMultipleNetworkTransitionsForNIAutoSwitch(); } catch( sInt32 err ) { result = err; } return( result ); } // StartUpServer // --------------------------------------------------------------------------- // * ShutDownServer () // // --------------------------------------------------------------------------- sInt32 ServerControl::ShutDownServer ( void ) { sInt32 result = eDSNoErr; uInt32 i = 0; uInt32 uiStopCnt = 0; struct stat statResult; try { result = (sInt32)UnRegisterForNetworkChange(); if ( result != eDSNoErr ) throw( result ); // result = UnRegisterForSystemPower(); // if ( result != eDSNoErr ) throw( result ); //fMigListener is stopped by destroying the port set mach_port_destroy( mach_task_self(), gMachAPISet ); gMachAPISet = MACH_PORT_NULL; if (::stat( "/Library/Preferences/DirectoryService/.DSTCPListening", &statResult ) == eDSNoErr) { //need to stop the TCP listener before anything else //assume that the listener itself will close all of its connections if (fTCPListener != nil) { fTCPListener->StopThread(); gTCPHandlerLock->Wait(); // Stop the handler threads for ( i = 0; i < gMaxHandlerThreadCount; i++ ) { if ( fTCPHandlers[ i ] != nil ) { uiStopCnt += 1; fTCPHandlers[ i ]->StopThread(); fTCPHandlers[ i ] = nil; } } gTCPHandlerLock->Signal(); while (uiStopCnt > 0) { WakeAHandler(DSCThread::kTSTCPHandlerThread); uiStopCnt--; } uiStopCnt = 0; } } //no need to delete the global objects as this process is going away and //we don't want to create a race condition on the threads dying that //could lead to a crash /* if ( gNodeList != nil ) { delete( gNodeList ); gNodeList = nil; } if ( gRefTable != nil ) { delete( gRefTable ); gRefTable = nil; } if ( gPlugins != nil ) { delete( gPlugins ); gPlugins = nil; } if ( gTCPMsgQueue != nil ) { delete( gTCPMsgQueue ); gTCPMsgQueue = nil; } if ( gMsgQueue != nil ) { delete( gMsgQueue ); gMsgQueue = nil; } if ( gInternalMsgQueue != nil ) { delete( gInternalMsgQueue ); gInternalMsgQueue = nil; } if ( gCheckpwMsgQueue != nil ) { delete( gCheckpwMsgQueue ); gCheckpwMsgQueue = nil; } */ //no need to delete the global mutexes as this process is going away and //we don't want to create a reace condition on the threads dying that //could lead to a crash //delete(gTCPHandlerLock); //delete(gHandlerLock); //delete(gInternalHandlerLock); //delete(gCheckpwHandlerLock); //delete(gPerformanceLoggingLock); CLog::Deinitialize(); } catch( sInt32 err ) { result = err; } return( result ); } // ShutDownServer // --------------------------------------------------------------------------- // * StartTCPListener () // // --------------------------------------------------------------------------- sInt32 ServerControl::StartTCPListener ( uInt32 inPort ) { sInt32 result = eDSNoErr; try { fTCPListener = new DSTCPListener(inPort); if ( fTCPListener == nil ) throw((sInt32)eMemoryAllocError); //this call could throw fTCPListener->StartThread(); } catch( sInt32 err ) { result = err; DBGLOG2( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ ); DBGLOG1( kLogApplication, " Caught exception = %d.", err ); } return( result ); } // StartTCPListener // --------------------------------------------------------------------------- // * StopTCPListener () // // --------------------------------------------------------------------------- sInt32 ServerControl::StopTCPListener ( void ) { sInt32 result = eDSNoErr; try { if ( fTCPListener == nil ) throw((sInt32)eMemoryAllocError); //this call could throw fTCPListener->StopThread(); } catch( sInt32 err ) { result = err; DBGLOG2( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ ); DBGLOG1( kLogApplication, " Caught exception = %d.", err ); } return( result ); } // StopTCPListener // --------------------------------------------------------------------------- // * StartAHandler () // // --------------------------------------------------------------------------- sInt32 ServerControl:: StartAHandler ( const FourCharCode inThreadSignature ) { volatile uInt32 iThread; sInt32 result = eDSNoErr; try { // If we have less than the max handlers then we add one //decide from which set of handlers to start one if (inThreadSignature == DSCThread::kTSTCPHandlerThread) { if ( (fTCPHandlerThreadsCnt >= 0) && (fTCPHandlerThreadsCnt < gMaxHandlerThreadCount) ) { for (iThread =0; iThread < gMaxHandlerThreadCount; iThread++) { if (fTCPHandlers[ iThread ] == nil) { // Start a handler thread fTCPHandlers[ iThread ] = new CHandlerThread(DSCThread::kTSTCPHandlerThread, iThread); if ( fTCPHandlers[ iThread ] == nil ) throw((sInt32)eMemoryAllocError); fTCPHandlerThreadsCnt++; //this call could throw fTCPHandlers[ iThread ]->StartThread(); break; } else if ( fTCPHandlers[iThread]->GetOurThreadRunState() == DSCThread::kThreadStop) { // Start a handler thread fTCPHandlers[ iThread ] = new CHandlerThread(DSCThread::kTSTCPHandlerThread, iThread); if ( fTCPHandlers[ iThread ] == nil ) throw((sInt32)eMemoryAllocError); //fTCPHandlerThreadsCnt++; //no need since replacing //this call could throw fTCPHandlers[ iThread ]->StartThread(); break; } } } } } catch( sInt32 err ) { result = err; } return( result ); } // StartAHandler //-------------------------------------------------------------------------------------------------- // * WakeAHandler() // //-------------------------------------------------------------------------------------------------- void ServerControl:: WakeAHandler ( const FourCharCode inThreadSignature ) { if (inThreadSignature == DSCThread::kTSTCPHandlerThread) { fTCPHandlerSemaphore->Signal(); } } // WakeAHandler // --------------------------------------------------------------------------- // * StopAHandler () // // --------------------------------------------------------------------------- sInt32 ServerControl:: StopAHandler ( const FourCharCode inThreadSignature, uInt32 iThread, CHandlerThread *inThread ) { sInt32 result = eDSNoErr; try { // DBGLOG2( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ ); // DBGLOG2( kLogApplication, "StopAHandler: sig = %d and index = %d", inThreadSignature, iThread ); if (inThreadSignature == DSCThread::kTSTCPHandlerThread) { if ( (iThread >= 0) && (iThread < gMaxHandlerThreadCount) ) { if (fTCPHandlers[ iThread ] == inThread) { // Remove a handler thread from the list fTCPHandlers[ iThread ] = nil; fTCPHandlerThreadsCnt--; } } } } catch( sInt32 err ) { result = err; } return( result ); } // StopAHandler //-------------------------------------------------------------------------------------------------- // * SleepAHandler(const FourCharCode inThreadSignature, uInt32 waitTime) // //-------------------------------------------------------------------------------------------------- void ServerControl:: SleepAHandler ( const FourCharCode inThreadSignature, uInt32 waitTime ) { if (inThreadSignature == DSCThread::kTSTCPHandlerThread) { fTCPHandlerSemaphore->Wait( waitTime ); } } // SleepAHandler //-------------------------------------------------------------------------------------------------- // * GetHandlerCount(const FourCharCode inThreadSignature) // //-------------------------------------------------------------------------------------------------- uInt32 ServerControl::GetHandlerCount ( const FourCharCode inThreadSignature ) { if (inThreadSignature == DSCThread::kTSTCPHandlerThread) { return fTCPHandlerThreadsCnt; } return 0; } // GetHandlerCount // --------------------------------------------------------------------------- // * RegisterForNetworkChange () // // --------------------------------------------------------------------------- sInt32 ServerControl:: RegisterForNetworkChange ( void ) { sInt32 scdStatus = eDSNoErr; CFStringRef ipKey = 0; //ip changes key CFStringRef dhcpKey = 0; //DHCP changes key CFStringRef niKey = 0; //NetInfo changes key CFMutableArrayRef notifyKeys = 0; CFMutableArrayRef notifyPatterns = 0; Boolean setStatus = FALSE; CFStringRef aPIDString = NULL; SCDynamicStoreRef store = NULL; CFRunLoopSourceRef rls = NULL; DBGLOG( kLogApplication, "RegisterForNetworkChange(): " ); notifyKeys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); notifyPatterns = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); // ip changes /* watch for IPv4 configuration changes (e.g. new default route) */ ipKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); CFArrayAppendValue(notifyKeys, ipKey); CFRelease(ipKey); /* watch for IPv4 interface configuration changes */ ipKey = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); CFArrayAppendValue(notifyPatterns, ipKey); CFRelease(ipKey); //DHCP changes dhcpKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetDHCP); CFArrayAppendValue(notifyPatterns, dhcpKey); CFRelease(dhcpKey); // NetInfo changes niKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetNetInfo); CFArrayAppendValue(notifyKeys, niKey); CFRelease(niKey); //not checking bool return store = SCDynamicStoreCreate(NULL, fServiceNameString, NetworkChangeCallBack, NULL); if (store != NULL && notifyKeys != NULL && notifyPatterns != NULL) { SCDynamicStoreSetNotificationKeys(store, notifyKeys, notifyPatterns); rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); if (rls != NULL) { CFRunLoopAddSource(gServerRunLoop, rls, kCFRunLoopDefaultMode); CFRelease(rls); rls = NULL; } else { syslog(LOG_ALERT,"Unable to add source to RunLoop for SystemConfiguration registration for Network Notification"); } CFRelease(notifyKeys); notifyKeys = NULL; CFRelease(notifyPatterns); notifyPatterns = NULL; CFRelease(store); store = NULL; } else { syslog(LOG_ALERT,"Unable to create DirectoryService store for SystemConfiguration registration for Network Notification"); } if (fHoldStore == NULL) { fHoldStore = SCDynamicStoreCreate(NULL, fServiceNameString, NULL, NULL); } if (fHoldStore != NULL) { //this is update code for things like NetInfo and DHCP issues //ie. calls to the search node that verify or re-establish the default NI and LDAPv3(from DHCP) nodes //send SIGHUP on a network transition //setStatus = SCDynamicStoreNotifySignal( fHoldStore, getpid(), SIGHUP); aPIDString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), (uInt32)getpid()); if (aPIDString != NULL) { setStatus = SCDynamicStoreAddTemporaryValue( fHoldStore, CFSTR("DirectoryService:PID"), aPIDString ); CFRelease(aPIDString); } else { syslog(LOG_ALERT,"Unable to create DirectoryService:PID string for SystemConfiguration registration - DSAgent will be disabled in lookupd"); } //DO NOT release the store here since we use SCDynamicStoreAddTemporaryValue above //CFRelease(fHoldStore); //fHoldStore = NULL; } else { syslog(LOG_ALERT,"Unable to create DirectoryService store for SystemConfiguration registration of DirectoryService:PID string - DSAgent will be disabled in lookupd"); } return scdStatus; } // RegisterForNetworkChange // --------------------------------------------------------------------------- // * UnRegisterForNetworkChange () // // --------------------------------------------------------------------------- sInt32 ServerControl:: UnRegisterForNetworkChange ( void ) { sInt32 scdStatus = eDSNoErr; DBGLOG( kLogApplication, "UnRegisterForNetworkChange(): " ); return scdStatus; } // UnRegisterForNetworkChange // --------------------------------------------------------------------------- // * RegisterForSystemPower () // // --------------------------------------------------------------------------- sInt32 ServerControl::RegisterForSystemPower ( void ) { IONotificationPortRef pmNotificationPortRef; CFRunLoopSourceRef pmNotificationRunLoopSource; DBGLOG( kLogApplication, "RegisterForSystemPower(): " ); gPMKernelPort = IORegisterForSystemPower(this, &pmNotificationPortRef, dsPMNotificationHandler, &gPMDeregisterNotifier); if (gPMKernelPort == nil || pmNotificationPortRef == nil) { ERRORLOG( kLogApplication, "RegisterForSystemPower(): IORegisterForSystemPower failed" ); } else { pmNotificationRunLoopSource = IONotificationPortGetRunLoopSource(pmNotificationPortRef); if (pmNotificationRunLoopSource == nil) { ERRORLOG( kLogApplication, "RegisterForSystemPower(): IONotificationPortGetRunLoopSource failed" ); gPMKernelPort = nil; } else { CFRunLoopAddSource(gServerRunLoop, pmNotificationRunLoopSource, kCFRunLoopCommonModes); } } return (gPMKernelPort != nil) ? eDSNoErr : -1; } // RegisterForSystemPower // --------------------------------------------------------------------------- // * UnRegisterForSystemPower () // // --------------------------------------------------------------------------- sInt32 ServerControl::UnRegisterForSystemPower ( void ) { sInt32 ioResult = eDSNoErr; DBGLOG( kLogApplication, "UnRegisterForSystemPower(): " ); if (gPMKernelPort != nil) { gPMKernelPort = nil; ioResult = (sInt32)IODeregisterForSystemPower(&gPMDeregisterNotifier); if (ioResult != eDSNoErr) { DBGLOG1( kLogApplication, "UnRegisterForSystemPower(): IODeregisterForSystemPower failed, error= %d", ioResult ); } } return ioResult; } // UnRegisterForSystemPower // --------------------------------------------------------------------------- // * HandleSystemWillSleep () // // --------------------------------------------------------------------------- sInt32 ServerControl::HandleSystemWillSleep ( void ) { sInt32 siResult = eDSNoErr; uInt32 iterator = 0; CServerPlugin *pPlugin = nil; sHeader aHeader; CPlugInList::sTableData *pPIInfo = nil; SRVRLOG( kLogApplication, "Sleep Notification occurred."); aHeader.fType = kHandleSystemWillSleep; aHeader.fResult = eDSNoErr; aHeader.fContextData = nil; if ( gPlugins != nil ) { pPlugin = gPlugins->Next( &iterator ); while (pPlugin != nil) { pPIInfo = gPlugins->GetPlugInInfo( iterator-1 ); if (pPIInfo->fState & kActive) //only notify Active plugins { siResult = eDSNoErr; siResult = pPlugin->ProcessRequest( (void*)&aHeader ); if (siResult != eDSNoErr && siResult != eNotHandledByThisNode && siResult != eNotYetImplemented) { if (pPIInfo != nil) { ERRORLOG2( kLogApplication, "SystemWillSleep Notification in %s plugin returned error %d", pPIInfo->fName, siResult ); } else { ERRORLOG1( kLogApplication, "SystemWillSleep Notification of unnamed plugin returned error %d", siResult ); } } } pPlugin = gPlugins->Next( &iterator ); } } return siResult; } // --------------------------------------------------------------------------- // * HandleSystemWillPowerOn () // // --------------------------------------------------------------------------- sInt32 ServerControl::HandleSystemWillPowerOn ( void ) { sInt32 siResult = eDSNoErr; uInt32 iterator = 0; CServerPlugin *pPlugin = nil; sHeader aHeader; CPlugInList::sTableData *pPIInfo = nil; SRVRLOG( kLogApplication, "Will Power On (Wake) Notification occurred."); aHeader.fType = kHandleSystemWillPowerOn; aHeader.fResult = eDSNoErr; aHeader.fContextData = nil; if ( gPlugins != nil ) { pPlugin = gPlugins->Next( &iterator ); while (pPlugin != nil) { pPIInfo = gPlugins->GetPlugInInfo( iterator-1 ); if (pPIInfo->fState & kActive) //only notify Active plugins { siResult = eDSNoErr; siResult = pPlugin->ProcessRequest( (void*)&aHeader ); if (siResult != eDSNoErr && siResult != eNotHandledByThisNode && siResult != eNotYetImplemented) { if (pPIInfo != nil) { ERRORLOG2( kLogApplication, "WillPowerOn Notification in %s plugin returned error %d", pPIInfo->fName, siResult ); } else { ERRORLOG1( kLogApplication, "WillPowerOn Notification of unnamed plugin returned error %d", siResult ); } } } pPlugin = gPlugins->Next( &iterator ); } } return siResult; } // --------------------------------------------------------------------------- // * HandleNetworkTransition () // // --------------------------------------------------------------------------- sInt32 ServerControl::HandleNetworkTransition ( void ) { sInt32 siResult = eDSNoErr; uInt32 iterator = 0; CServerPlugin *pPlugin = nil; sHeader aHeader; CPlugInList::sTableData *pPIInfo = nil; CServerPlugin *searchPlugin = nil; uInt32 searchIterator = 0; aHeader.fType = kHandleNetworkTransition; aHeader.fResult = eDSNoErr; aHeader.fContextData = nil; SRVRLOG( kLogApplication, "Network transition occurred." ); gFirstNetworkUpAtBoot = true; //call thru to each plugin if ( gPlugins != nil ) { pPlugin = gPlugins->Next( &iterator ); while (pPlugin != nil) { pPIInfo = gPlugins->GetPlugInInfo( iterator-1 ); if (pPIInfo->fState & kActive) //only notify Active plugins { if ( ::strcmp(pPIInfo->fName,"Search") != 0) { siResult = eDSNoErr; siResult = pPlugin->ProcessRequest( (void*)&aHeader ); if (siResult != eDSNoErr) { if (pPIInfo != nil) { ERRORLOG2( kLogApplication, "Network transition in %s plugin returned error %d", pPIInfo->fName, siResult ); } else { ERRORLOG1( kLogApplication, "Network transition of unnamed plugin returned error %d", siResult ); } } } else { searchIterator = iterator; searchPlugin = pPlugin; } } pPlugin = gPlugins->Next( &iterator ); } } //handle the search plugin transition last to ensure at least NetInfo and LDAPv3 have gone first if (searchPlugin != nil) { siResult = eDSNoErr; //now do the network transition itself aHeader.fType = kHandleNetworkTransition; siResult = searchPlugin->ProcessRequest( (void*)&aHeader ); if (siResult != eDSNoErr) { ERRORLOG1( kLogApplication, "Network transition in Search returned error %d", siResult ); } } //NIAutoSwitch checking HandleMultipleNetworkTransitionsForNIAutoSwitch(); return siResult; } // HandleNetworkTransition // --------------------------------------------------------------------------- // * SetUpPeriodicTask () // // --------------------------------------------------------------------------- sInt32 ServerControl::SetUpPeriodicTask ( void ) { sInt32 siResult = eDSNoErr; void *ptInfo = nil; CFRunLoopTimerContext c = {0, (void*)ptInfo, NULL, NULL, PeriodicTaskCopyStringCallback}; CFRunLoopTimerRef timer = CFRunLoopTimerCreate( NULL, CFAbsoluteTimeGetCurrent() + 120, 30, 0, 0, DoPeriodicTask, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(gServerRunLoop, timer, kCFRunLoopDefaultMode); if (timer) CFRelease(timer); return siResult; } // SetUpPeriodicTask void ServerControl::NodeSearchPolicyChanged( void ) { void *ptInfo = nil; gTimerMutex->Wait(); if (gServerRunLoop != nil) { if( fNSPCTimerRef != NULL ) { DBGLOG1( kLogPlugin, "T[%X] ServerControl::NodeSearchPolicyChanged invalidating previous timer", pthread_self() ); CFRunLoopTimerInvalidate( fNSPCTimerRef ); CFRelease( fNSPCTimerRef ); fNSPCTimerRef = NULL; } ptInfo = (void *)this; CFRunLoopTimerContext c = {0, (void*)ptInfo, NULL, NULL, SearchPolicyChangeCopyStringCallback}; fNSPCTimerRef = CFRunLoopTimerCreate( NULL, CFAbsoluteTimeGetCurrent() + 2, 0, 0, 0, DoSearchPolicyChange, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(gServerRunLoop, fNSPCTimerRef, kCFRunLoopDefaultMode); } gTimerMutex->Signal(); } void ServerControl::DoNodeSearchPolicyChange( void ) { SCDynamicStoreRef store = NULL; DBGLOG( kLogApplication, "DoNodeSearchPolicyChange" ); store = SCDynamicStoreCreate(NULL, fServiceNameString, NULL, NULL); if (store != NULL) { if ( !SCDynamicStoreSetValue( store, CFSTR(kDSStdNotifySearchPolicyChanged), CFSTR("") ) ) { ERRORLOG( kLogApplication, "Could not set the DirectoryService:SearchPolicyChangeToken in System Configuration" ); } CFRelease(store); store = NULL; } else { ERRORLOG( kLogApplication, "ServerControl::DoNodeSearchPolicyChange SCDynamicStoreCreate not yet available from System Configuration" ); } LaunchKerberosAutoConfigTool(); }// DoNodeSearchPolicyChange void ServerControl::NotifySearchPolicyFoundNIParent( void ) { SCDynamicStoreRef store = NULL; DBGLOG( kLogApplication, "NotifySearchPolicyFoundNIParent" ); store = SCDynamicStoreCreate(NULL, fServiceNameString, NULL, NULL); if (store != NULL) { if ( !SCDynamicStoreSetValue( store, CFSTR(kDSStdNotifySearchPolicyFoundNIParent), CFSTR("") ) ) { ERRORLOG( kLogApplication, "Could not set the DirectoryService:NotifySearchPolicyFoundNIParent in System Configuration" ); } CFRelease(store); store = NULL; } else { ERRORLOG( kLogApplication, "ServerControl::NotifySearchPolicyFoundNIParent SCDynamicStoreCreate not yet available from System Configuration" ); } } void ServerControl::NotifyDirNodeAdded( const char* newNode ) { SCDynamicStoreRef store = NULL; if ( newNode != nil ) { CFStringRef newNodeRef = CFStringCreateWithCString( NULL, newNode, kCFStringEncodingUTF8 ); if ( newNodeRef == NULL ) { ERRORLOG1( kLogApplication, "Could not notify that dir node: (%s) was added due to an encoding problem", newNode ); } else { store = SCDynamicStoreCreate(NULL, fServiceNameString, NULL, NULL); if (store != NULL) { if ( !SCDynamicStoreSetValue( store, CFSTR(kDSStdNotifyDirectoryNodeAdded), newNodeRef ) ) { ERRORLOG( kLogApplication, "Could not set the DirectoryService:NotifyDirNodeAdded in System Configuration" ); } CFRelease(store); store = NULL; } else { ERRORLOG( kLogApplication, "ServerControl::NotifyDirNodeAdded SCDynamicStoreCreate not yet available from System Configuration" ); } CFRelease( newNodeRef ); newNodeRef = NULL; } } } void ServerControl::NotifyDirNodeDeleted( char* oldNode ) { SCDynamicStoreRef store = NULL; if ( oldNode != nil ) { CFStringRef oldNodeRef = CFStringCreateWithCString( NULL, oldNode, kCFStringEncodingUTF8 ); if ( oldNodeRef == NULL ) { ERRORLOG1( kLogApplication, "Could not notify that dir node: (%s) was deleted due to an encoding problem", oldNode ); } else { store = SCDynamicStoreCreate(NULL, fServiceNameString, NULL, NULL); if (store != NULL) { if ( !SCDynamicStoreSetValue( store, CFSTR(kDSStdNotifyDirectoryNodeDeleted), oldNodeRef ) ) { ERRORLOG( kLogApplication, "Could not set the DirectoryService:NotifyDirNodeDeleted in System Configuration" ); } CFRelease(store); store = NULL; } else { ERRORLOG( kLogApplication, "ServerControl::NotifyDirNodeDeleted SCDynamicStoreCreate not yet available from System Configuration" ); } CFRelease( oldNodeRef ); oldNodeRef = NULL; } } } #ifdef BUILD_IN_PERFORMANCE void ServerControl::DeletePerfStatTable( void ) { PluginPerformanceStats** table = fPerfTable; uInt32 pluginCount = fPerfTableNumPlugins; fPerfTable = NULL; fPerfTableNumPlugins = 0; if ( table ) { for ( uInt32 i=0; iGetPlugInCount(); if ( fPerfTable ) DeletePerfStatTable(); // how many plugins? table = (PluginPerformanceStats**)calloc( sizeof(PluginPerformanceStats*), pluginCount+1 ); // create table for #plugins + 1 for server for ( uInt32 i=0; ipluginSignature = gPlugins->GetPlugInInfo(i)->fKey; table[i]->pluginName = gPlugins->GetPlugInInfo(i)->fName; } table[pluginCount] = (PluginPerformanceStats*)calloc( sizeof(PluginPerformanceStats), 1 ); table[pluginCount]->pluginSignature = 0; table[pluginCount]->pluginName = "Server"; fPerfTableNumPlugins = pluginCount; fPerfTable = table; return table; } double gLastDump =0; #define kNumSecsBetweenDumps 60*2 void ServerControl::HandlePerformanceStats( uInt32 msgType, FourCharCode pluginSig, sInt32 siResult, sInt32 clientPID, double inTime, double outTime ) { // Since the number of plugins is so small, just doing an O(n)/2 search is probably fine... gPerformanceLoggingLock->Wait(); PluginPerformanceStats* curPluginStats = NULL; uInt32 pluginCount = gPlugins->GetPlugInCount(); if ( !fPerfTable || fPerfTableNumPlugins != pluginCount ) { // first api call, or number of plugins changed, (re)create the table fPerfTable = CreatePerfStatTable(); } if ( !pluginSig ) curPluginStats = fPerfTable[pluginCount]; // last entry in the table is reserved for the server if ( fPerfTable[fLastPluginCalled]->pluginSignature == pluginSig ) curPluginStats = fPerfTable[fLastPluginCalled]; for ( uInt32 i=0; !curPluginStats && ipluginSignature ) { curPluginStats = fPerfTable[i]; fLastPluginCalled = i; } } if ( curPluginStats ) { PluginPerformanceAPIStat* curAPI = &(curPluginStats->apiStats[msgType]); double duration = outTime-inTime; curAPI->msgCnt++; if ( siResult ) { for( int i=kNumErrorsToTrack-1; i>0; i-- ) { curAPI->lastNErrors[i].error = curAPI->lastNErrors[i-1].error; curAPI->lastNErrors[i].clientPID = curAPI->lastNErrors[i-1].clientPID; } curAPI->lastNErrors[0].error = siResult; curAPI->lastNErrors[0].clientPID = clientPID; curAPI->errCnt++; } if ( curAPI->minTime == 0 || curAPI->minTime > duration ) curAPI->minTime = duration; if ( curAPI->maxTime == 0 || curAPI->maxTime < duration ) curAPI->maxTime = duration; curAPI->totTime += duration; } gPerformanceLoggingLock->Signal(); } #define USEC_PER_HOUR (double)60*60*USEC_PER_SEC /* microseconds per hour */ #define USEC_PER_DAY (double)24*USEC_PER_HOUR /* microseconds per day */ void ServerControl::LogStats( void ) { PluginPerformanceStats* curPluginStats = NULL; uInt32 pluginCount = fPerfTableNumPlugins; char logBuf[1024]; char totTimeStr[256]; gPerformanceLoggingLock->Wait(); syslog( LOG_CRIT, "**Usage Stats**\n"); syslog( LOG_CRIT, "\tPlugin\tAPI\tMsgCnt\tErrCnt\tminTime (usec)\tmaxTime (usec)\taverageTime (usec)\ttotTime (usec|secs|hours|days)\tLast PID\tLast Error\tPrev PIDs/Errors\n" ); for ( uInt32 i=0; iapiStats[j].msgCnt > 0 ) { if ( curPluginStats->apiStats[j].totTime < USEC_PER_SEC ) sprintf( totTimeStr, "%0.f usecs", curPluginStats->apiStats[j].totTime ); else if ( curPluginStats->apiStats[j].totTime < USEC_PER_HOUR ) { double time = curPluginStats->apiStats[j].totTime / USEC_PER_SEC; sprintf( totTimeStr, "%0.4f secs", time ); } else if ( curPluginStats->apiStats[j].totTime < USEC_PER_DAY ) { double time = curPluginStats->apiStats[j].totTime / USEC_PER_HOUR; sprintf( totTimeStr, "%0.4f hours", time ); } else { double time = curPluginStats->apiStats[j].totTime / USEC_PER_DAY; sprintf( totTimeStr, "%0.4f days", time ); } sprintf( logBuf, "\t%s\t%s\t%ld\t%ld\t%.0f\t%0.f\t%0.f\t%s\t%ld/%ld\t%ld/%ld\t%ld/%ld\t%ld/%ld\t%ld/%ld\n", curPluginStats->pluginName, CRequestHandler::GetCallName(j), curPluginStats->apiStats[j].msgCnt, curPluginStats->apiStats[j].errCnt, curPluginStats->apiStats[j].minTime, curPluginStats->apiStats[j].maxTime, (curPluginStats->apiStats[j].totTime/curPluginStats->apiStats[j].msgCnt), totTimeStr, curPluginStats->apiStats[j].lastNErrors[0].clientPID, curPluginStats->apiStats[j].lastNErrors[0].error, curPluginStats->apiStats[j].lastNErrors[1].clientPID, curPluginStats->apiStats[j].lastNErrors[1].error, curPluginStats->apiStats[j].lastNErrors[2].clientPID, curPluginStats->apiStats[j].lastNErrors[2].error, curPluginStats->apiStats[j].lastNErrors[3].clientPID, curPluginStats->apiStats[j].lastNErrors[3].error, curPluginStats->apiStats[j].lastNErrors[4].clientPID, curPluginStats->apiStats[j].lastNErrors[4].error ); syslog( LOG_CRIT, logBuf ); } } } gPerformanceLoggingLock->Signal(); } #endif // --------------------------------------------------------------------------- // * DoPeriodicTask () // // --------------------------------------------------------------------------- void DoPeriodicTask(CFRunLoopTimerRef timer, void *info) { sInt32 siResult = eDSNoErr; uInt32 iterator = 0; CServerPlugin *pPlugin = nil; CPlugInList::sTableData *pPIInfo = nil; //call thru to each plugin if ( gPlugins != nil ) { pPlugin = gPlugins->Next( &iterator ); while (pPlugin != nil) { pPIInfo = gPlugins->GetPlugInInfo( iterator-1 ); if (pPIInfo->fState & kActive) //only pulse the Active plugins { siResult = pPlugin->PeriodicTask(); if (siResult != eDSNoErr) { if (pPIInfo != nil) { DBGLOG2( kLogApplication, "Periodic Task in %s plugin returned error %d", pPIInfo->fName, siResult ); } else { DBGLOG1( kLogApplication, "Periodic Task of unnamed plugin returned error %d", siResult ); } } } pPlugin = gPlugins->Next( &iterator ); } } return; } // DoPeriodicTask // --------------------------------------------------------------------------- // * FlushLookupDaemonCache () // // --------------------------------------------------------------------------- sInt32 ServerControl::FlushLookupDaemonCache ( void ) { sInt32 siResult = eDSNoErr; int i = 0; int proc = 0; char str[32]; mach_port_t port = MACH_PORT_NULL; DBGLOG( kLogApplication, "Sending lookupd flushcache" ); _lu_running(); port = _lookupd_port(0); if( port != MACH_PORT_NULL ) { _lookup_link(port, "_invalidatecache", &proc); _lookup_one(port, proc, NULL, 0, (char **)&str, &i); } return(siResult); }// FlushLookupDaemonCache // --------------------------------------------------------------------------- // * FlushMemberDaemonCache () // // --------------------------------------------------------------------------- sInt32 ServerControl::FlushMemberDaemonCache ( void ) { sInt32 siResult = eDSNoErr; //routine created for potential other additions DBGLOG( kLogApplication, "Sending memberd flushcache" ); mbr_reset_cache(); return(siResult); }// FlushMemberDaemonCache // --------------------------------------------------------------------------- // * NIAutoSwitchCheck () // // --------------------------------------------------------------------------- sInt32 ServerControl::NIAutoSwitchCheck ( void ) { sInt32 siResult = eDSNoErr; uInt32 iterator = 0; CServerPlugin *pPlugin = nil; sHeader aHeader; CPlugInList::sTableData *pPIInfo = nil; aHeader.fType = kCheckNIAutoSwitch; aHeader.fResult = eDSNoErr; aHeader.fContextData = nil; //should be assured that the search plugin is online already at boot //but regardless since the plugin will block the request for up //to two minutes allowing the plugin to initialize //TODO KW looks like the plugin ptr is not there yet always when this is called at boot // we know we need the search node, so let's wait until it is available. gNodeList->WaitForAuthenticationSearchNode(); //call thru to only the search policy plugin if ( gPlugins != nil ) { pPlugin = gPlugins->Next( &iterator ); while (pPlugin != nil) { pPIInfo = gPlugins->GetPlugInInfo( iterator-1 ); if ( ( strcmp(pPIInfo->fName,"Search") == 0) && (pPIInfo->fState & kActive) ) { siResult = pPlugin->ProcessRequest( (void*)&aHeader ); if (siResult == eDSContinue) { //here we need to turn off netinfo bindings sInt32 unbindResult = UnbindToNetInfo(); if (unbindResult == eDSNoErr) { DBGLOG( kLogApplication, "NIAutoSwitchCheck(): NIAutoSwitch record found in NetInfo parent has directed addition of LDAP directory to the search policy" ); //if success then NIBindings turn off will generate a network transition itself } else { DBGLOG( kLogApplication, "NIAutoSwitchCheck(): NIAutoSwitch record found in NetInfo parent has directed addition of LDAP directory to the search policy but NetInfo Unbind failed" ); } } break; } pPlugin = gPlugins->Next( &iterator ); } } return(siResult); } // NIAutoSwitchCheck // --------------------------------------------------------------------------- // * UnbindToNetInfo () // // --------------------------------------------------------------------------- sInt32 ServerControl::UnbindToNetInfo ( void ) { sInt32 siResult = eUnknownServerError; SCPreferencesRef scpRef = NULL; bool scpStatus = false; CFTypeRef cfTypeRef = NULL; char *pCurrSet = nil; char charArray[ 1024 ]; const char *NetInfoPath = "%s/Network/Global/NetInfo"; CFStringRef cfNetInfoKey = NULL; const char *InactiveTag = "\ __INACTIVE__\ 1\ "; unsigned long uiDataLen = 0; CFDataRef dataRef = NULL; CFPropertyListRef plistRef = NULL; CFDictionaryRef dictRef = NULL; CFMutableDictionaryRef dictMutableRef = NULL; scpRef = SCPreferencesCreate( NULL, CFSTR("NIAutoSwitch"), 0 ); if ( scpRef == NULL ) { return(siResult); } // Get the current set cfTypeRef = SCPreferencesGetValue( scpRef, CFSTR( "CurrentSet" ) ); if ( cfTypeRef != NULL ) { pCurrSet = (char *)CFStringGetCStringPtr( (CFStringRef)cfTypeRef, kCFStringEncodingMacRoman ); if ( pCurrSet != nil ) { sprintf( charArray, NetInfoPath, pCurrSet ); } } else { // Modify the system config file sprintf( charArray, NetInfoPath, "/Sets/0" ); } cfNetInfoKey = CFStringCreateWithCString( kCFAllocatorDefault, charArray, kCFStringEncodingMacRoman ); if (cfNetInfoKey != NULL) { dictRef = SCPreferencesPathGetValue( scpRef, cfNetInfoKey); if (dictRef != NULL) { dictMutableRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dictRef); if ( dictMutableRef != NULL) { int intValue = 1; CFNumberRef cfNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &intValue); // add the INACTIVE key/value with the old binding methods still present CFDictionarySetValue( dictMutableRef, CFSTR( "__INACTIVE__"), cfNumber ); } //CFRelease(dictRef); //retrieved with Get so don't release } else { uiDataLen = strlen( InactiveTag ); dataRef = CFDataCreate( kCFAllocatorDefault, (const UInt8 *)InactiveTag, uiDataLen ); if ( dataRef != nil ) { plistRef = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, dataRef, kCFPropertyListMutableContainers, nil ); if ( plistRef != nil ) { dictMutableRef = (CFMutableDictionaryRef)plistRef; } CFRelease( dataRef ); } } if (dictMutableRef != NULL) //either of two ways to get the dict better have succeeded { //update the local copy with the dict entry scpStatus = SCPreferencesPathSetValue( scpRef, cfNetInfoKey, dictMutableRef ); CFRelease( dictMutableRef ); } CFRelease( cfNetInfoKey ); } if (scpStatus) { scpStatus = SCPreferencesCommitChanges( scpRef ); if (scpStatus) { scpStatus = SCPreferencesApplyChanges( scpRef ); } } CFRelease( scpRef ); if (scpStatus) siResult = eDSNoErr; return(siResult); } // UnbindToNetInfo //------------------------------------------------------------------------------------ // * HandleLookupDaemonFlushCache //------------------------------------------------------------------------------------ void ServerControl::HandleLookupDaemonFlushCache ( void ) { void *ptInfo = nil; uInt32 timeOffset = 2; gTimerMutex->Wait(); //wait one second to fire off the timer //consolidate multiple requests that come in faster than one second fLookupDaemonFlushCacheRequestCount++; if (gServerRunLoop != nil) { if( fLDFCTimerRef != NULL ) { DBGLOG1( kLogPlugin, "T[%X] ServerControl::HandleLookupDaemonFlushCache invalidating previous timer", pthread_self() ); CFRunLoopTimerInvalidate( fLDFCTimerRef ); CFRelease( fLDFCTimerRef ); fLDFCTimerRef = NULL; } ptInfo = (void *)this; CFRunLoopTimerContext c = {0, (void*)ptInfo, NULL, NULL, LookupDaemonFlushCacheCopyStringCallback}; if (fLookupDaemonFlushCacheRequestCount > kDSActOnThisNumberOfFlushRequests) { fLookupDaemonFlushCacheRequestCount = 0; timeOffset = 0; //do it now } fLDFCTimerRef = CFRunLoopTimerCreate( NULL, CFAbsoluteTimeGetCurrent() + timeOffset, 0, 0, 0, DoLookupDaemonFlushCache, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(gServerRunLoop, fLDFCTimerRef, kCFRunLoopDefaultMode); } gTimerMutex->Signal(); } // HandleLookupDaemonFlushCache //------------------------------------------------------------------------------------ // * HandleMemberDaemonFlushCache //------------------------------------------------------------------------------------ void ServerControl::HandleMemberDaemonFlushCache ( void ) { void *ptInfo = nil; uInt32 timeOffset = 2; gTimerMutex->Wait(); //wait one second to fire off the timer //consolidate multiple requests that come in faster than one second fMemberDaemonFlushCacheRequestCount++; if (gServerRunLoop != nil) { if( fMDFCTimerRef != NULL ) { DBGLOG1( kLogPlugin, "T[%X] ServerControl::HandleLookupDaemonFlushCache invalidating previous timer", pthread_self() ); CFRunLoopTimerInvalidate( fMDFCTimerRef ); CFRelease( fMDFCTimerRef ); fMDFCTimerRef = NULL; } ptInfo = (void *)this; CFRunLoopTimerContext c = {0, (void*)ptInfo, NULL, NULL, MemberDaemonFlushCacheCopyStringCallback}; if (fMemberDaemonFlushCacheRequestCount > kDSActOnThisNumberOfFlushRequests) { fMemberDaemonFlushCacheRequestCount = 0; timeOffset = 0; //do it now } fMDFCTimerRef = CFRunLoopTimerCreate( NULL, CFAbsoluteTimeGetCurrent() + timeOffset, 0, 0, 0, DoMemberDaemonFlushCache, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(gServerRunLoop, fMDFCTimerRef, kCFRunLoopDefaultMode); } gTimerMutex->Signal(); } // HandleMemberDaemonFlushCache //------------------------------------------------------------------------------------ // * HandleMultipleNetworkTransitionsForNIAutoSwitch //------------------------------------------------------------------------------------ void ServerControl::HandleMultipleNetworkTransitionsForNIAutoSwitch ( void ) { void *ptInfo = nil; gTimerMutex->Wait(); //let us be smart about doing the check //we would like to wait a short period for the Network transitions to subside //since we don't want to re-init multiple times during this wait period //however we do go ahead and fire off timers each time //each call in here we update the delay time by 11 seconds //Need to ensure that this does not conflict with NetInfo connection //re-establishment after a network transition if (gServerRunLoop != nil) { if( fNIASTimerRef != NULL ) { DBGLOG1( kLogPlugin, "T[%X] ServerControl::HandleLookupDaemonFlushCache invalidating previous timer", pthread_self() ); CFRunLoopTimerInvalidate( fNIASTimerRef ); CFRelease( fNIASTimerRef ); fNIASTimerRef = NULL; } ptInfo = (void *)this; CFRunLoopTimerContext c = {0, (void*)ptInfo, NULL, NULL, NetworkChangeNIAutoSwitchCopyStringCallback}; fNIASTimerRef = CFRunLoopTimerCreate( NULL, CFAbsoluteTimeGetCurrent() + 11, 0, 0, 0, DoNIAutoSwitchNetworkChange, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(gServerRunLoop, fNIASTimerRef, kCFRunLoopDefaultMode); } gTimerMutex->Signal(); } // HandleMultipleNetworkTransitionsForNIAutoSwitch //------------------------------------------------------------------------------ // * LaunchKerberosAutoConfigTool // //------------------------------------------------------------------------------ void ServerControl:: LaunchKerberosAutoConfigTool ( void ) { sInt32 result = eDSNoErr; mach_port_t mach_init_port = MACH_PORT_NULL; //lookup mach init port to launch Kerberos AutoConfig Tool on demand result = bootstrap_look_up( bootstrap_port, "com.apple.KerberosAutoConfig", &mach_init_port ); if ( result != eDSNoErr ) { syslog( LOG_ALERT, "Error with bootstrap_look_up for com.apple.KerberosAutoConfig on mach_init port: %s at: %d: Msg = %s\n", __FILE__, __LINE__, mach_error_string( result ) ); } else { sIPCMsg aMsg; aMsg.fHeader.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND ); aMsg.fHeader.msgh_size = sizeof(sIPCMsg) - sizeof( mach_msg_audit_trailer_t ); aMsg.fHeader.msgh_id = 0; aMsg.fHeader.msgh_remote_port = mach_init_port; aMsg.fHeader.msgh_local_port = MACH_PORT_NULL; aMsg.fMsgType = 0; aMsg.fCount = 1; aMsg.fPort = MACH_PORT_NULL; aMsg.fPID = 0; aMsg.fMsgID = 0; aMsg.fOf = 1; //tickle the mach init port - should this really be required to start the daemon mach_msg((mach_msg_header_t *)&aMsg, MACH_SEND_MSG | MACH_SEND_TIMEOUT, aMsg.fHeader.msgh_size, 0, MACH_PORT_NULL, 1, MACH_PORT_NULL); //don't retain the mach init port since only using it to launch the Kerberos AutoConfig tool mach_port_destroy(mach_task_self(), mach_init_port); mach_init_port = MACH_PORT_NULL; } } // CheckForServer // --------------------------------------------------------------------------- // * ResetDebugging () // // --------------------------------------------------------------------------- sInt32 ServerControl::ResetDebugging ( void ) { sInt32 siResult = eDSNoErr; uInt32 uiDataSize = 0; char *pData = nil; CFile *pFile = nil; struct stat statbuf; CFDataRef dataRef = nil; CFBooleanRef cfBool = false; bool bDebugging = false; bool bFileUsed = false; if (gToggleDebugging) { gToggleDebugging = false; //here we turn everything off if (gDebugLogging) { CLog::StopDebugLog(); gDebugLogging = false; syslog(LOG_ALERT,"Debug Logging turned OFF after receiving USR1 signal."); } gDSFWCSBPDebugLogging = false; } else { //next time this is called we turn everything off gToggleDebugging = true; // Does the debug config file exist siResult = ::stat( kDSDebugConfigFilePath, &statbuf ); if ( siResult == eDSNoErr ) { // Attempt to get config info from file pFile = new CFile( kDSDebugConfigFilePath ); if (pFile != nil) { if ( (pFile->is_open()) && (pFile->FileSize() > 0) ) { // Allocate space for the file data pData = (char *)::calloc( 1, pFile->FileSize() + 1 ); if ( pData != nil ) { // Read from the config file uiDataSize = pFile->ReadBlock( pData, pFile->FileSize() ); dataRef = ::CFDataCreate( nil, (const uInt8 *)pData, uiDataSize ); if ( dataRef != nil ) { CFPropertyListRef aPlistRef = 0; CFDictionaryRef aDictRef = 0; // Is it valid XML data aPlistRef = ::CFPropertyListCreateFromXMLData( kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nil ); if ( aPlistRef != nil ) { // Is it a plist type if ( ::CFDictionaryGetTypeID() == ::CFGetTypeID( aPlistRef ) ) { bFileUsed = true; aDictRef = (CFDictionaryRef)aPlistRef; //now set up the debugging according to the plist settings //debug logging boolean if ( CFDictionaryContainsKey( aDictRef, CFSTR( kXMLDSDebugLoggingKey ) ) ) { cfBool= (CFBooleanRef)CFDictionaryGetValue( aDictRef, CFSTR( kXMLDSDebugLoggingKey ) ); if (cfBool != nil) { bDebugging = CFBooleanGetValue( cfBool ); //CFRelease( cfBool ); // no since pointer only from Get if (gDebugLogging && !bDebugging) { CLog::StopDebugLog(); gDebugLogging = false; syslog(LOG_ALERT,"Debug Logging turned OFF after receiving USR1 signal."); } else if (!gDebugLogging && bDebugging) { gDebugLogging = true; CLog::StartDebugLog(); syslog(LOG_ALERT,"Debug Logging turned ON after receiving USR1 signal."); } } } else if (gDebugLogging) { CLog::StopDebugLog(); gDebugLogging = false; syslog(LOG_ALERT,"Debug Logging turned OFF after receiving USR1 signal."); } //FW CSBP debug logging boolean if ( CFDictionaryContainsKey( aDictRef, CFSTR( kXMLDSCSBPDebugLoggingKey ) ) ) { cfBool= (CFBooleanRef)CFDictionaryGetValue( aDictRef, CFSTR( kXMLDSCSBPDebugLoggingKey ) ); if (cfBool != nil) { gDSFWCSBPDebugLogging = CFBooleanGetValue( cfBool ); //CFRelease( cfBool ); // no since pointer only from Get } } else { gDSFWCSBPDebugLogging = false; } aDictRef = 0; } //free the propertylist CFRelease(aPlistRef); } CFRelease( dataRef ); dataRef = nil; } free( pData ); pData = nil; } } delete( pFile ); pFile = nil; } } if (!bFileUsed) { //write a default file and setup debugging sInt32 result = eDSNoErr; gDebugLogging = true; CLog::StartDebugLog(); syslog(LOG_ALERT,"Debug Logging turned ON after receiving USR1 signal."); uiDataSize = ::strlen( kDefaultDebugConfig ); dataRef = ::CFDataCreate( nil, (const uInt8 *)kDefaultDebugConfig, uiDataSize ); if ( dataRef != nil ) { //see if the file exists //if not then make sure the directories exist or create them //then create a new file if necessary result = ::stat( kDSDebugConfigFilePath, &statbuf ); //if file does not exist if (result != eDSNoErr) { //move down the path from the system defined local directory and check if it exists //if not create it result = ::stat( "/Library/Preferences", &statbuf ); //if first sub directory does not exist if (result != eDSNoErr) { ::mkdir( "/Library/Preferences", 0775 ); ::chmod( "/Library/Preferences", 0775 ); //above 0775 doesn't seem to work - looks like umask modifies it } result = ::stat( "/Library/Preferences/DirectoryService", &statbuf ); //if second sub directory does not exist if (result != eDSNoErr) { ::mkdir( "/Library/Preferences/DirectoryService", 0775 ); ::chmod( "/Library/Preferences/DirectoryService", 0775 ); //above 0775 doesn't seem to work - looks like umask modifies it } } UInt8 *pData = (UInt8*)::calloc( CFDataGetLength(dataRef), 1 ); CFDataGetBytes( dataRef, CFRangeMake(0,CFDataGetLength(dataRef)), pData ); if ( (pData != nil) && (pData[0] != 0) ) { try { CFile *pFile = new CFile( kDSDebugConfigFilePath, true ); if ( pFile != nil ) { if ( pFile->is_open() ) { pFile->seteof( 0 ); pFile->write( pData, CFDataGetLength(dataRef) ); ::chmod( kDSDebugConfigFilePath, 0600 ); } delete( pFile ); pFile = nil; } } catch ( ... ) { } free(pData); } CFRelease( dataRef ); dataRef = nil; } } } return(siResult); } // ResetDebugging