#if DEBUG_SRVR
# include <stdio.h> // 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 "WorkstationService.h"
#include "Mbrd_MembershipResolver.h"
#include "DSLDAPUtils.h"
#include "CDSPluginUtils.h"
#include <mach/mach.h>
#include <mach/notify.h>
#include <sys/stat.h> //used for mkdir and stat
#include <IOKit/pwr_mgt/IOPMLib.h> //required for power management handling
#include <syslog.h> // for syslog()
#include <time.h> // for time
#include <bsm/libbsm.h>
#include <uuid/uuid.h>
extern "C" {
#include "DirectoryServiceMIGServer.h"
#include "DSlibinfoMIGServer.h"
#include "DSmemberdMIGServer.h"
}
extern void LoggingTimerCallBack( CFRunLoopTimerRef timer, void *info );
extern void dsPMNotificationHandler ( void *refContext, io_service_t service, natural_t messageType, void *notificationID );
extern io_object_t gPMDeregisterNotifier;
extern io_connect_t gPMKernelPort;
extern void NetworkChangeCallBack(SCDynamicStoreRef aSCDStore, CFArrayRef changedKeys, void *callback_argument);
extern CFRunLoopRef gPluginRunLoop;
extern CFAbsoluteTime gSunsetTime;
extern dsBool gLogAPICalls;
extern dsBool gDebugLogging;
extern dsBool gDSFWCSBPDebugLogging;
extern dsBool gIgnoreSunsetTime;
extern bool gServerOS;
extern dsBool gDSDebugMode;
extern CCachePlugin *gCacheNode;
extern dsBool gDSLocalOnlyMode;
extern dsBool gDSInstallDaemonMode;
extern DSEventSemaphore gKickCacheRequests;
extern mach_port_t gMachMIGSet;
CFRunLoopTimerRef ServerControl::fNSPCTimerRef = NULL; CFRunLoopTimerRef ServerControl::fSPCNTimerRef = NULL;
UInt32 gAPICallCount = 0;
UInt32 gLookupAPICallCount = 0;
ServerControl *gSrvrCntl = nil;
CRefTable *gRefTable = nil;
CPlugInList *gPlugins = nil;
CMsgQueue *gLibinfoQueue = nil;
CPluginConfig *gPluginConfig = nil;
CNodeList *gNodeList = nil;
CPluginHandler *gPluginHandler = nil;
char *gDSLocalFilePath = nil;
UInt32 gLocalSessionCount = 0;
DSMutexSemaphore *gTCPHandlerLock = new DSMutexSemaphore("::gTCPHandlerLock"); DSMutexSemaphore *gPerformanceLoggingLock = new DSMutexSemaphore("::gPerformanceLoggingLock"); DSMutexSemaphore *gLazyPluginLoadingLock = new DSMutexSemaphore("::gLazyPluginLoadingLock"); DSMutexSemaphore *gHashAuthFailedMapLock = new DSMutexSemaphore("::gHashAuthFailedMapLock"); DSMutexSemaphore *gHashAuthFailedLocalMapLock = gHashAuthFailedMapLock; DSMutexSemaphore *gMachThreadLock = new DSMutexSemaphore("::gMachThreadLock"); DSMutexSemaphore *gTimerMutex = new DSMutexSemaphore("::gTimerMutex"); DSMutexSemaphore *gLibinfoQueueLock = new DSMutexSemaphore("::gLibinfoQueueLock");
UInt32 gDaemonPID;
UInt32 gDaemonIPAddress;
UInt32 gRefCountWarningLimit = 500;
UInt32 gDelayFailedLocalAuthReturnsDeltaInSeconds = 1;
UInt32 gMaxHandlerThreadCount = kMaxHandlerThreads;
dsBool gToggleDebugging = false;
map<mach_port_t, pid_t> gPIDMachMap;
char *gNIHierarchyTagString = nil;
UInt32 gActiveMachThreads = 0;
UInt32 gActiveLongRequests = 0;
bool gFirstNetworkUpAtBoot = false;
bool gNetInfoPluginIsLoaded = false;
const char *lookupProcedures[] =
{
"firstprocnum",
"getpwnam",
"getpwuuid",
"getpwuid",
"getpwent",
"getgrnam",
"getgruuid",
"getgrgid",
"getgrent",
"getservbyname",
"getservbyport",
"getservent",
"getprotobyname",
"getprotobynumber",
"getprotoent",
"getrpcbyname",
"getrpcbynumber",
"getrpcent",
"getfsbyname",
"getfsent",
"alias_getbyname",
"alias_getent",
"getnetent",
"getnetbyname",
"getnetbyaddr",
"innetgr",
"getnetgrent",
"getaddrinfo",
"getnameinfo",
"gethostbyname",
"gethostbyaddr",
"gethostent",
"getmacbyname",
"gethostbymac",
"getbootpbyhw",
"getbootpbyaddr",
"dns_proxy",
"_flushcache",
"_flushentry",
"lastprocnum",
NULL };
__BEGIN_DECLS
extern int ShouldRegisterWorkstation(void);
__END_DECLS
void DoPeriodicTask(CFRunLoopTimerRef timer, void *info);
static void NodeSearchPolicyChangeCallback(CFRunLoopTimerRef timer, void *info)
{
if ( info != nil )
{
((ServerControl *)info)->DoNodeSearchPolicyChange();
}
}
static void SearchPolicyChangedNotifyCallback( CFRunLoopTimerRef timer, void *info )
{
if ( info != nil )
{
((ServerControl *)info)->DoSearchPolicyChangedNotify();
}
}
static CFStringRef SearchPolicyChangeCopyStringCallback( const void *item )
{
return CFSTR("SearchPolicyChange");
}
static CFStringRef NotifySearchPolicyChangeCopyStringCallback( const void *item )
{
return CFSTR("NotifySearchPolicyChange");
}
static CFStringRef PeriodicTaskCopyStringCallback( const void *item )
{
return CFSTR("PeriodicTask");
}
#pragma mark -
#pragma mark MIG Support Routines - separate DS, Lookup, and memberd servers
#pragma mark -
void mig_spawnonceifnecessary( void )
{
gMachThreadLock->WaitLock();
bool bSpawnThread = ( gActiveMachThreads < gMaxHandlerThreadCount && gActiveLongRequests > gActiveMachThreads );
gMachThreadLock->SignalLock();
if( bSpawnThread )
{
CMigHandlerThread* aMigHandlerThread = new CMigHandlerThread(DSCThread::kTSMigHandlerThread, true);
if (aMigHandlerThread != NULL)
aMigHandlerThread->StartThread();
}
}
#pragma mark -
#pragma mark MIG Call Handler Routines - separate DS, Lookup, and memberd servers
#pragma mark -
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->WaitLock();
gActiveLongRequests++;
gMachThreadLock->SignalLock();
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" );
DbgLog( 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
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 ); #endif
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : dsmig DAR : Username %s : Result code = %d", debugDataTag, username, *result );
free( debugDataTag );
}
gMachThreadLock->WaitLock();
gActiveLongRequests--;
gMachThreadLock->SignalLock();
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, gMachMIGSet );
(void) mach_port_request_notification( mach_task_self(), *newServer, MACH_NOTIFY_NO_SENDERS, 1, *newServer, MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldTargetOfNotification );
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
gMachThreadLock->WaitLock();
gPIDMachMap[*newServer] = aPID;
gMachThreadLock->SignalLock();
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) {
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) {
dataLength = DSGetLong(&pComData->fDataLength, false);
dataSize = DSGetLong(&pComData->fDataSize, false);
}
#endif
}
if( uiLength >= (sizeof(sComData) - 1) )
{
if( dataLength == (uiLength - (sizeof(sComData) - 1)) )
{
sComData *pRequest = (sComData *) calloc( sizeof(sComData) + dataSize, 1 );
if ( pRequest == NULL )
return KERN_MEMORY_ERROR;
CRequestHandler handler;
double reqStartTime = 0;
double reqEndTime = 0;
bcopy( (void *)pComData, pRequest, uiLength );
gMachThreadLock->WaitLock();
gActiveLongRequests ++;
gMachThreadLock->SignalLock();
mig_spawnonceifnecessary();
#ifdef __LITTLE_ENDIAN__
if (pRequest->type.msgt_translate == 1) {
DSMachEndian swapper(pRequest, kDSSwapToHost);
swapper.SwapMessage();
}
#endif
audit_token_to_au32( atoken, NULL, (uid_t *)&pRequest->fEffectiveUID, NULL, (uid_t *)&pRequest->fUID, NULL, (pid_t *)&pRequest->fPID, NULL, NULL );
if ( (gDebugLogging) || (gLogAPICalls) )
{
reqStartTime = dsTimestamp();
}
handler.HandleRequest( &pRequest );
if ( (gDebugLogging) || (gLogAPICalls) )
{
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
reqEndTime = dsTimestamp();
double totalTime = (reqEndTime - reqStartTime) / USEC_PER_SEC;
if (totalTime > 2.0)
{
char *debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "API", "Server" );
DbgLog( kLogHandler, "%s : dsmig DAR : Excessive request time %f seconds", debugDataTag, totalTime );
free( debugDataTag );
}
}
gMachThreadLock->WaitLock();
gActiveLongRequests --;
gMachThreadLock->SignalLock();
pRequest->fPID = gDaemonPID;
UInt32 dataLen = pRequest->fDataLength;
#ifdef __LITTLE_ENDIAN__
if (pRequest->type.msgt_translate == 1) {
DSMachEndian swapper(pRequest, kDSSwapToBig);
swapper.SwapMessage();
}
#endif
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; vm_read( mach_task_self(), (vm_address_t)pRequest, (sizeof(sComData) + dataLen - 1), reply_msg_ool, reply_msg_oolCnt );
}
free( pRequest );
pRequest = NULL;
gAPICallCount++;
if (gLogAPICalls)
{
if ( (gAPICallCount % 1023) == 1023 ) {
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;
}
kern_return_t libinfoDSmig_do_GetProcedureNumber
( mach_port_t server,
char* indata,
int *procnumber,
audit_token_t atoken )
{
kern_return_t kr = KERN_FAILURE;
char *debugDataTag = NULL;
gMachThreadLock->WaitLock();
gActiveLongRequests ++;
gMachThreadLock->SignalLock();
if ( (gDebugLogging) || (gLogAPICalls) )
{
pid_t aPID;
CRequestHandler handler;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Procedure Request = %s", debugDataTag, indata );
}
*procnumber = 0;
if (indata != NULL)
{
for (int idx = 1; idx < (int)kDSLUlastprocnum && lookupProcedures[idx] != NULL; idx++)
{
if ( 0 == strcmp(indata, lookupProcedures[idx]) )
{
*procnumber = idx;
kr = KERN_SUCCESS;
break;
}
}
}
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : libinfomig DAR : Procedure = %s (%d) : Result code = %d", debugDataTag, indata,
*procnumber, kr );
free( debugDataTag );
}
gMachThreadLock->WaitLock();
gActiveLongRequests --;
gMachThreadLock->SignalLock();
return kr;
}
kern_return_t libinfoDSmig_do_Query( mach_port_t server,
int32_t procnumber,
inline_data_t request,
mach_msg_type_number_t requestCnt,
inline_data_t reply,
mach_msg_type_number_t *replyCnt,
vm_offset_t *ooreply,
mach_msg_type_number_t *ooreplyCnt,
audit_token_t atoken )
{
kern_return_t kr = KERN_FAILURE;
kvbuf_t *returnedBuf = NULL;
Boolean bValidProcedure = ( (procnumber > 0) && (procnumber < (int)kDSLUlastprocnum) ? TRUE : FALSE);
char *debugDataTag = NULL;
pid_t aPID;
gMachThreadLock->WaitLock();
gActiveLongRequests ++;
gMachThreadLock->SignalLock();
mig_spawnonceifnecessary();
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
if ( bValidProcedure )
{
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Procedure = %s (%d)", debugDataTag, lookupProcedures[procnumber], procnumber );
if( aPID == (SInt32)gDaemonPID )
{
DbgLog( kLogHandler, "%s : libinfomig DAC : Dispatching from/to ourself", debugDataTag, lookupProcedures[procnumber],
procnumber );
}
}
else
{
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Invalid Procedure = %d", debugDataTag, procnumber );
}
}
if( gCacheNode == NULL && (aPID != (SInt32)gDaemonPID || (procnumber >= kDSLUgetaddrinfo && procnumber <= kDSLUdns_proxy )) )
{
gKickCacheRequests.WaitForEvent(); }
if ( gCacheNode != NULL && bValidProcedure )
{
returnedBuf = gCacheNode->ProcessLookupRequest(procnumber, request, requestCnt, aPID);
if ( (returnedBuf != NULL) && (returnedBuf->databuf != NULL) )
{
if( returnedBuf->datalen <= *replyCnt )
{
*replyCnt = returnedBuf->datalen;
bcopy( returnedBuf->databuf, reply, returnedBuf->datalen );
*ooreplyCnt = 0; }
else
{
vm_read( mach_task_self(), (vm_address_t)(returnedBuf->databuf), returnedBuf->datalen, ooreply,
ooreplyCnt );
*replyCnt = 0; }
}
else
{
*replyCnt = 0;
*ooreplyCnt = 0;
}
kr = KERN_SUCCESS;
kvbuf_free(returnedBuf);
}
gMachThreadLock->WaitLock();
gActiveLongRequests --;
gMachThreadLock->SignalLock();
if ( debugDataTag )
{
if ( bValidProcedure )
{
DbgLog( kLogHandler, "%s : libinfomig DAR : Procedure = %s (%d) : Result code = %d", debugDataTag,
lookupProcedures[procnumber], procnumber, kr );
}
else
{
DbgLog( kLogHandler, "%s : libinfomig DAR : Invalid Procedure = %d", debugDataTag, procnumber );
}
free( debugDataTag );
}
return kr;
}
kern_return_t libinfoDSmig_do_Query_async( mach_port_t server,
mach_port_t replyToPort,
int32_t procnumber,
inline_data_t request,
mach_msg_type_number_t requestCnt,
mach_vm_address_t callbackAddr,
audit_token_t atoken )
{
Boolean bValidProcedure = ( (procnumber > 0) && (procnumber < (int)kDSLUlastprocnum) ? TRUE : FALSE);
char *debugDataTag = NULL;
sLibinfoRequest *pLibinfoRequest = NULL;
if ( (gDebugLogging) || (gLogAPICalls) )
{
pid_t aPID;
CRequestHandler handler;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
if ( bValidProcedure )
{
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Async Procedure = %s (%d)", debugDataTag, lookupProcedures[procnumber], procnumber );
if( aPID == (pid_t)gDaemonPID )
{
DbgLog( kLogHandler, "%s : libinfomig DAC : Dispatching from/to ourself", debugDataTag, lookupProcedures[procnumber],
procnumber );
}
}
else
{
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Invalid Async Procedure = %d", debugDataTag, procnumber );
}
}
if( bValidProcedure )
{
pLibinfoRequest = new sLibinfoRequest;
pLibinfoRequest->fBuffer = (char *) calloc( requestCnt, sizeof(char) );
if( pLibinfoRequest->fBuffer != NULL )
{
pLibinfoRequest->fReplyPort = replyToPort;
pLibinfoRequest->fProcedure = procnumber;
pLibinfoRequest->fToken = atoken;
pLibinfoRequest->fCallbackAddr = callbackAddr;
bcopy( request, pLibinfoRequest->fBuffer, requestCnt );
pLibinfoRequest->fBufferLen = requestCnt;
if( gLibinfoQueue->QueueMessage( pLibinfoRequest ) == false )
{
gLibinfoQueueLock->WaitLock();
gSrvrCntl->StartAHandler( DSCThread::kTSLibinfoQueueThread );
gLibinfoQueueLock->SignalLock();
}
}
else
{
mach_port_mod_refs( mach_task_self(), replyToPort, MACH_PORT_RIGHT_SEND_ONCE, -1 );
}
}
if ( debugDataTag )
{
if ( bValidProcedure )
{
if( pLibinfoRequest != NULL )
{
DbgLog( kLogHandler, "%s : libinfomig DAR : Async Procedure = %s (%d) : Request %X queued", debugDataTag,
lookupProcedures[procnumber], procnumber, pLibinfoRequest );
}
else
{
DbgLog( kLogHandler, "%s : libinfomig DAR : Async Procedure = %s (%d) : Request not queued", debugDataTag,
lookupProcedures[procnumber], procnumber );
}
}
else
{
DbgLog( kLogHandler, "%s : libinfomig DAR : Invalid Procedure = %d", debugDataTag, procnumber );
}
free( debugDataTag );
}
return KERN_SUCCESS;
}
kern_return_t memberdDSmig_do_MembershipCall( mach_port_t server, kauth_identity_extlookup *request, audit_token_t *atoken )
{
char *debugDataTag = NULL;
int needsSwap = (request->el_seqno != 1) && (request->el_seqno == ntohl(1));
if (needsSwap)
Mbrd_SwapRequest(request);
gMachThreadLock->WaitLock();
gActiveLongRequests ++;
gMachThreadLock->SignalLock();
mig_spawnonceifnecessary();
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( *atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "MembershipCall", "Server" );
DbgLog( kLogHandler, "%s : mbrmig DAC %s", debugDataTag, (needsSwap ? " : Via Rosetta" : "") );
if( aPID == (SInt32)gDaemonPID )
{
DbgLog( kLogHandler, "%s : mbrmig DAC : Dispatching from/to ourself", debugDataTag );
}
}
Mbrd_ProcessLookup( request );
if (needsSwap)
Mbrd_SwapRequest(request);
gMachThreadLock->WaitLock();
gActiveLongRequests --;
gMachThreadLock->SignalLock();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbrmig DAR", debugDataTag );
free( debugDataTag );
}
return KERN_SUCCESS;
}
kern_return_t memberdDSmig_do_GetStats(mach_port_t server, StatBlock *stats)
{
Mbrd_ProcessGetStats( stats );
return KERN_SUCCESS;
}
kern_return_t memberdDSmig_do_ClearStats(mach_port_t server, audit_token_t atoken)
{
char *debugDataTag = NULL;
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "ClearStats", "Server" );
DbgLog( kLogHandler, "%s : mbrmig DAC", debugDataTag );
}
Mbrd_ProcessResetStats();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbrmig DAR", debugDataTag );
free( debugDataTag );
}
return KERN_SUCCESS;
}
kern_return_t memberdDSmig_do_MapName(mach_port_t server, uint8_t isUser, mstring name, guid_t *guid, audit_token_t *atoken)
{
kern_return_t result;
char *debugDataTag = NULL;
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( *atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "MapName", "Server" );
DbgLog( kLogHandler, "%s : mbrmig DAC : Name = %s : isUser = %d", debugDataTag, name, (int) isUser );
}
gMachThreadLock->WaitLock();
gActiveLongRequests ++;
gMachThreadLock->SignalLock();
mig_spawnonceifnecessary();
result = Mbrd_ProcessMapName(isUser, name, guid);
gMachThreadLock->WaitLock();
gActiveLongRequests --;
gMachThreadLock->SignalLock();
if ( debugDataTag )
{
if (result == KERN_SUCCESS)
{
char uuid_string[37] = { 0, };
uuid_unparse_upper( guid->g_guid, uuid_string );
DbgLog( kLogHandler, "%s : mbrmig DAR : Name = %s : GUID = %s", debugDataTag, name, uuid_string );
}
else
{
DbgLog( kLogHandler, "%s : mbrmig DAR : Name = %s : Not found", debugDataTag, name );
}
free( debugDataTag );
}
return result;
}
kern_return_t memberdDSmig_do_GetGroups(mach_port_t server, uint32_t uid, uint32_t* numGroups, GIDArray gids, audit_token_t *atoken)
{
int result;
char *debugDataTag = NULL;
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( *atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "GetGroups", "Server" );
DbgLog( kLogHandler, "%s : mbrmig DAC : uid = %u", debugDataTag, uid );
}
gMachThreadLock->WaitLock();
gActiveLongRequests ++;
gMachThreadLock->SignalLock();
mig_spawnonceifnecessary();
result = Mbrd_ProcessGetGroups(uid, numGroups, gids);
gMachThreadLock->WaitLock();
gActiveLongRequests --;
gMachThreadLock->SignalLock();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbrmig DAR : Total groups = %u", debugDataTag, (*numGroups) );
free( debugDataTag );
}
return (kern_return_t)result;
}
kern_return_t memberdDSmig_do_GetAllGroups(mach_port_t server, uint32_t uid, uint32_t* numGroups, GIDList *gids, mach_msg_type_number_t *gidsCnt,
audit_token_t *atoken)
{
int result;
char *debugDataTag = NULL;
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( *atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "GetAllGroups", "Server" );
DbgLog( kLogHandler, "%s : mbrmig DAC : uid = %u", debugDataTag, uid );
}
gMachThreadLock->WaitLock();
gActiveLongRequests ++;
gMachThreadLock->SignalLock();
mig_spawnonceifnecessary();
GIDList tempList = NULL;
result = Mbrd_ProcessGetAllGroups(uid, numGroups, &tempList);
if ( (*numGroups) > 0 && tempList != NULL )
{
vm_read( mach_task_self(), (vm_address_t) tempList, ((*numGroups) * sizeof(gid_t)), (vm_offset_t *) gids, gidsCnt );
DSFree( tempList );
}
gMachThreadLock->WaitLock();
gActiveLongRequests --;
gMachThreadLock->SignalLock();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbrmig DAR : Total groups = %u", debugDataTag, (*numGroups) );
free( debugDataTag );
}
return (kern_return_t)result;
}
kern_return_t memberdDSmig_do_ClearCache(mach_port_t server, audit_token_t atoken)
{
char *debugDataTag = NULL;
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "ClearCache", "Server" );
DbgLog( kLogHandler, "%s : mbrmig DAC", debugDataTag );
}
Mbrd_ProcessResetCache();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbrmig DAR", debugDataTag );
free( debugDataTag );
}
return KERN_SUCCESS;
}
kern_return_t memberdDSmig_do_DumpState(mach_port_t server, audit_token_t atoken)
{
char *debugDataTag = NULL;
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( gDaemonIPAddress, aPID, "DumpState", "Server" );
DbgLog( kLogHandler, "%s : mbrmig DAC", debugDataTag );
}
Mbrd_ProcessDumpState();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbrmig DAR", debugDataTag );
free( debugDataTag );
}
return KERN_SUCCESS;
}
#pragma mark -
#pragma mark ServerControl Routines
#pragma mark -
ServerControl::ServerControl ( void )
{
gDaemonPID = getpid();
gDaemonIPAddress = 0;
fTCPListener = nil;
fTCPHandlerThreadsCnt = 0;
fLibinfoHandlerThreadCnt = 0;
fSCDStore = 0;
fPerformanceStatGatheringActive = false; fMemberDaemonFlushCacheRequestCount = 0;
fTCPHandlers = nil;
#ifdef BUILD_IN_PERFORMANCE
fLastPluginCalled = 0;
fPerfTableNumPlugins = 0;
fPerfTable = nil;
#if PERFORMANCE_STATS_ALWAYS_ON
fPerformanceStatGatheringActive = true;
#else
fPerformanceStatGatheringActive = false;
#endif
#endif
if (gDSDebugMode)
{
fServiceNameString = CFStringCreateWithCString( NULL, kDSStdMachDebugPortName, kCFStringEncodingUTF8 );
}
else if (gDSLocalOnlyMode)
{
fServiceNameString = CFStringCreateWithCString( NULL, kDSStdMachLocalPortName, kCFStringEncodingUTF8 );
}
else
{
fServiceNameString = CFStringCreateWithCString( NULL, kDSStdMachPortName, kCFStringEncodingUTF8 );
}
if (gDaemonPID > 100) {
gFirstNetworkUpAtBoot = true;
}
}
ServerControl::~ServerControl ( void )
{
}
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 );
}
Mbrd_InitializeGlobals();
if ( gPluginConfig == nil )
{
gPluginConfig = new CPluginConfig();
if ( gPluginConfig == nil ) throw( (SInt32)eMemoryAllocError );
gPluginConfig->Initialize();
}
fTCPHandlers = (CHandlerThread **)calloc(gMaxHandlerThreadCount, sizeof(CHandlerThread *));
fLibinfoHandlers = (CHandlerThread **)calloc(gMaxHandlerThreadCount, sizeof(CHandlerThread *));
if ( gPlugins == nil )
{
gPlugins = new CPlugInList();
if ( gPlugins == nil ) throw( (SInt32)eMemoryAllocError );
gPlugins->ReadRecordTypeRestrictions();
}
if ( gLibinfoQueue == nil )
{
gLibinfoQueue = new CMsgQueue();
if ( gLibinfoQueue == nil ) throw((SInt32)eMemoryAllocError);
}
CreateDebugPrefFileIfNecessary();
if (::stat( "/Library/Preferences/DirectoryService/.DSLogAPIAtStart", &statResult ) == eDSNoErr)
{
gSunsetTime = CFAbsoluteTimeGetCurrent() + 300;
CFRunLoopTimerRef timer = CFRunLoopTimerCreate( kCFAllocatorDefault,
gSunsetTime + 1,
0,
0,
0,
LoggingTimerCallBack,
NULL );
CFRunLoopAddTimer( CFRunLoopGetMain(), timer, kCFRunLoopDefaultMode );
CFRelease( timer );
timer = NULL;
gLogAPICalls = true;
syslog(LOG_ALERT,"Logging of API Calls turned ON at Startup of DS Daemon.");
if (!gDebugLogging)
{
gDebugLogging = true;
gSrvrCntl->ResetDebugging(); syslog(LOG_ALERT,"Debug Logging turned ON at Startup of DS Daemon.");
}
}
CMigHandlerThread *migListener = new CMigHandlerThread( DSCThread::kTSMigHandlerThread, false );
if ( migListener == nil ) throw((SInt32)eMemoryAllocError);
migListener->StartThread();
if (!gDSLocalOnlyMode && !gDSInstallDaemonMode)
{
if ( ( (::stat( "/Library/Preferences/DirectoryService/.DSTCPListening", &statResult ) == eDSNoErr) ||
(gServerOS) ) &&
(::stat( "/Library/Preferences/DirectoryService/.DSTCPNotListening", &statResult ) != eDSNoErr) )
{
result = StartTCPListener(kDSDefaultListenPort);
if ( result != eDSNoErr ) throw( result );
}
}
if ( gPluginHandler == nil )
{
gPluginHandler = new CPluginHandler();
if ( gPluginHandler == nil ) throw((SInt32)eMemoryAllocError);
gPluginHandler->StartThread();
}
if (!gDSLocalOnlyMode)
{
kern_return_t kresult = syscall( SYS_identitysvc, KAUTH_EXTLOOKUP_REGISTER, 0 );
if (kresult == 0)
{
CMemberdKernelHandlerThread *aThread = new CMemberdKernelHandlerThread( DSCThread::kTSMemberdKernelHndlrThread );
aThread->StartThread();
}
else
{
syslog(LOG_CRIT, "Got error %d trying to register with kernel\n", kresult);
}
}
result = RegisterForSystemPower();
if ( result != eDSNoErr ) throw( result );
result = (SInt32)RegisterForNetworkChange();
if ( result != eDSNoErr ) throw( result );
result = SetUpPeriodicTask();
if ( result != eDSNoErr ) throw( result );
}
catch( SInt32 err )
{
result = err;
}
return( result );
}
SInt32 ServerControl::ShutDownServer ( void )
{
SInt32 result = eDSNoErr;
UInt32 i = 0;
try
{
result = (SInt32)UnRegisterForNetworkChange();
if ( result != eDSNoErr ) throw( result );
mach_port_destroy( mach_task_self(), gMachMIGSet );
gMachMIGSet = MACH_PORT_NULL;
gLibinfoQueueLock->WaitLock();
for (i = 0; i < gMaxHandlerThreadCount; i++)
{
if (fLibinfoHandlers[ i ] != nil)
{
fLibinfoHandlers[ i ]->StopThread();
fLibinfoHandlers[ i ] = nil;
}
}
gLibinfoQueue->ClearMsgQueue();
gLibinfoQueueLock->SignalLock();
CLog::Deinitialize();
}
catch( SInt32 err )
{
result = err;
}
return( result );
}
SInt32 ServerControl::StartTCPListener ( UInt32 inPort )
{
SInt32 result = eDSNoErr;
try
{
fTCPListener = new DSTCPListener(inPort);
if ( fTCPListener == nil ) throw((SInt32)eMemoryAllocError);
fTCPListener->StartThread();
}
catch( SInt32 err )
{
result = err;
DbgLog( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ );
DbgLog( kLogApplication, " Caught exception = %d.", err );
}
return( result );
}
SInt32 ServerControl::StopTCPListener ( void )
{
SInt32 result = eDSNoErr;
try
{
if ( fTCPListener == nil ) throw((SInt32)eMemoryAllocError);
fTCPListener->StopThread();
}
catch( SInt32 err )
{
result = err;
DbgLog( kLogApplication, "File: %s. Line: %d", __FILE__, __LINE__ );
DbgLog( kLogApplication, " Caught exception = %d.", err );
}
return( result );
}
SInt32 ServerControl:: StartAHandler ( const FourCharCode inThreadSignature )
{
volatile UInt32 iThread;
SInt32 result = eDSNoErr;
try
{
if (inThreadSignature == DSCThread::kTSLibinfoQueueThread)
{
if ( (fLibinfoHandlerThreadCnt >= 0) && (fLibinfoHandlerThreadCnt < gMaxHandlerThreadCount) )
{
for (iThread = 0; iThread < gMaxHandlerThreadCount; iThread++)
{
if (fLibinfoHandlers[ iThread ] == nil)
{
fLibinfoHandlers[ iThread ] = new CHandlerThread(DSCThread::kTSLibinfoQueueThread, iThread);
if ( fLibinfoHandlers[ iThread ] == nil ) throw((SInt32)eMemoryAllocError);
fLibinfoHandlerThreadCnt++;
fLibinfoHandlers[ iThread ]->StartThread();
break;
}
}
}
}
}
catch( SInt32 err )
{
result = err;
}
return( result );
}
void ServerControl:: WakeAHandler ( const FourCharCode inThreadSignature )
{
}
SInt32 ServerControl:: StopAHandler ( const FourCharCode inThreadSignature, UInt32 iThread, CHandlerThread *inThread )
{
SInt32 result = eDSNoErr;
try
{
if (inThreadSignature == DSCThread::kTSLibinfoQueueThread)
{
if ( (iThread >= 0) && (iThread < gMaxHandlerThreadCount) )
{
if (fLibinfoHandlers[ iThread ] == inThread)
{
fLibinfoHandlers[ iThread ] = nil;
fLibinfoHandlerThreadCnt--;
}
}
}
}
catch( SInt32 err )
{
result = err;
}
return( result );
}
void ServerControl:: SleepAHandler ( const FourCharCode inThreadSignature, UInt32 waitTime )
{
}
UInt32 ServerControl::GetHandlerCount ( const FourCharCode inThreadSignature )
{
if (inThreadSignature == DSCThread::kTSLibinfoQueueThread)
{
return fLibinfoHandlerThreadCnt;
}
return 0;
}
SInt32 ServerControl:: RegisterForNetworkChange ( void )
{
SInt32 scdStatus = eDSNoErr;
CFStringRef ipKey = 0; CFMutableArrayRef notifyKeys = 0;
CFMutableArrayRef notifyPatterns = 0;
SCDynamicStoreRef store = NULL;
CFRunLoopSourceRef rls = NULL;
DbgLog( kLogApplication, "RegisterForNetworkChange(): " );
notifyKeys = CFArrayCreateMutable( kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
notifyPatterns = CFArrayCreateMutable( kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
ipKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
CFArrayAppendValue(notifyKeys, ipKey);
CFRelease(ipKey);
ipKey = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
CFArrayAppendValue(notifyPatterns, ipKey);
CFRelease(ipKey);
ipKey = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
CFArrayAppendValue(notifyPatterns, ipKey);
CFRelease(ipKey);
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(gPluginRunLoop, rls, kCFRunLoopDefaultMode);
CFRelease(rls);
rls = NULL;
}
else
{
syslog(LOG_ALERT,"Unable to add source to RunLoop for SystemConfiguration registration for Network Notification");
}
}
else
{
syslog(LOG_ALERT,"Unable to create DirectoryService store for SystemConfiguration registration for Network Notification");
}
DSCFRelease(notifyKeys);
DSCFRelease(notifyPatterns);
DSCFRelease(store);
return scdStatus;
}
SInt32 ServerControl:: UnRegisterForNetworkChange ( void )
{
SInt32 scdStatus = eDSNoErr;
DbgLog( kLogApplication, "UnRegisterForNetworkChange(): " );
return scdStatus;
}
SInt32 ServerControl::RegisterForSystemPower ( void )
{
IONotificationPortRef pmNotificationPortRef;
CFRunLoopSourceRef pmNotificationRunLoopSource;
DbgLog( kLogApplication, "RegisterForSystemPower(): " );
gPMKernelPort = IORegisterForSystemPower(this, &pmNotificationPortRef, dsPMNotificationHandler, &gPMDeregisterNotifier);
if (gPMKernelPort == 0 || pmNotificationPortRef == nil)
{
ErrLog( kLogApplication, "RegisterForSystemPower(): IORegisterForSystemPower failed" );
}
else
{
pmNotificationRunLoopSource = IONotificationPortGetRunLoopSource(pmNotificationPortRef);
if (pmNotificationRunLoopSource == nil)
{
ErrLog( kLogApplication, "RegisterForSystemPower(): IONotificationPortGetRunLoopSource failed" );
gPMKernelPort = nil;
}
else
{
CFRunLoopAddSource( CFRunLoopGetMain(), pmNotificationRunLoopSource, kCFRunLoopCommonModes );
}
}
return (gPMKernelPort != 0) ? eDSNoErr : -1;
}
SInt32 ServerControl::UnRegisterForSystemPower ( void )
{
SInt32 ioResult = eDSNoErr;
DbgLog( kLogApplication, "UnRegisterForSystemPower(): " );
if (gPMKernelPort != 0) {
gPMKernelPort = 0;
ioResult = (SInt32)IODeregisterForSystemPower(&gPMDeregisterNotifier);
if (ioResult != eDSNoErr)
{
DbgLog( kLogApplication, "UnRegisterForSystemPower(): IODeregisterForSystemPower failed, error= %d", ioResult );
}
}
return ioResult;
}
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) {
siResult = eDSNoErr;
siResult = pPlugin->ProcessRequest( (void*)&aHeader );
if (siResult != eDSNoErr && siResult != eNotHandledByThisNode && siResult != eNotYetImplemented)
{
if (pPIInfo != nil)
{
ErrLog( kLogApplication, "SystemWillSleep Notification in %s plugin returned error %d", pPIInfo->fName, siResult );
}
else
{
ErrLog( kLogApplication, "SystemWillSleep Notification of unnamed plugin returned error %d", siResult );
}
}
}
pPlugin = gPlugins->Next( &iterator );
}
}
return siResult;
}
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) {
siResult = eDSNoErr;
siResult = pPlugin->ProcessRequest( (void*)&aHeader );
if (siResult != eDSNoErr && siResult != eNotHandledByThisNode && siResult != eNotYetImplemented)
{
if (pPIInfo != nil)
{
ErrLog( kLogApplication, "WillPowerOn Notification in %s plugin returned error %d", pPIInfo->fName, siResult );
}
else
{
ErrLog( kLogApplication, "WillPowerOn Notification of unnamed plugin returned error %d", siResult );
}
}
}
pPlugin = gPlugins->Next( &iterator );
}
}
return siResult;
}
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;
if ( gPlugins != nil )
{
pPlugin = gPlugins->Next( &iterator );
while (pPlugin != nil)
{
pPIInfo = gPlugins->GetPlugInInfo( iterator-1 );
if (pPIInfo->fState & kActive) {
if ( ::strcmp(pPIInfo->fName,"Search") != 0)
{
siResult = eDSNoErr;
siResult = pPlugin->ProcessRequest( (void*)&aHeader );
if (siResult != eDSNoErr)
{
if (pPIInfo != nil)
{
ErrLog( kLogApplication, "Network transition in %s plugin returned error %d", pPIInfo->fName, siResult );
}
else
{
ErrLog( kLogApplication, "Network transition of unnamed plugin returned error %d", siResult );
}
}
}
else
{
searchIterator = iterator;
searchPlugin = pPlugin;
}
}
pPlugin = gPlugins->Next( &iterator );
}
}
if (searchPlugin != nil)
{
siResult = eDSNoErr;
aHeader.fType = kHandleNetworkTransition;
siResult = searchPlugin->ProcessRequest( (void*)&aHeader );
if (siResult != eDSNoErr)
{
ErrLog( kLogApplication, "Network transition in Search returned error %d", siResult );
}
}
return siResult;
}
SInt32 ServerControl::SetUpPeriodicTask ( void )
{
SInt32 siResult = eDSNoErr;
void *ptInfo = nil;
CFRunLoopTimerContext c = {0, (void*)ptInfo, NULL, NULL, PeriodicTaskCopyStringCallback};
CFRunLoopTimerRef timer = CFRunLoopTimerCreate( NULL,
CFAbsoluteTimeGetCurrent() + 30,
30,
0,
0,
DoPeriodicTask,
(CFRunLoopTimerContext*)&c);
CFRunLoopAddTimer(gPluginRunLoop, timer, kCFRunLoopDefaultMode);
if (timer) CFRelease(timer);
return siResult;
}
void ServerControl::SearchPolicyChangedNotify( void )
{
gTimerMutex->WaitLock();
if (gPluginRunLoop != nil)
{
if( fSPCNTimerRef != NULL )
{
DbgLog( kLogPlugin, "ServerControl::SearchPolicyChangedNotify invalidating previous timer" );
CFRunLoopTimerInvalidate( fSPCNTimerRef );
CFRelease( fSPCNTimerRef );
fSPCNTimerRef = NULL;
}
CFRunLoopTimerContext c = {0, (void*)this, NULL, NULL, NotifySearchPolicyChangeCopyStringCallback};
fSPCNTimerRef = CFRunLoopTimerCreate( NULL,
CFAbsoluteTimeGetCurrent() + 2,
0,
0,
0,
SearchPolicyChangedNotifyCallback,
(CFRunLoopTimerContext*)&c);
CFRunLoopAddTimer(gPluginRunLoop, fSPCNTimerRef, kCFRunLoopDefaultMode);
}
gTimerMutex->SignalLock();
}
void ServerControl::NodeSearchPolicyChanged( void )
{
gTimerMutex->WaitLock();
if (gPluginRunLoop != nil)
{
if( fNSPCTimerRef != NULL )
{
DbgLog( kLogPlugin, "ServerControl::NodeSearchPolicyChanged invalidating previous timer" );
CFRunLoopTimerInvalidate( fNSPCTimerRef );
CFRelease( fNSPCTimerRef );
fNSPCTimerRef = NULL;
}
CFRunLoopTimerContext c = {0, (void*)this, NULL, NULL, SearchPolicyChangeCopyStringCallback};
fNSPCTimerRef = CFRunLoopTimerCreate( NULL,
CFAbsoluteTimeGetCurrent() + 2,
0,
0,
0,
NodeSearchPolicyChangeCallback,
(CFRunLoopTimerContext*)&c);
CFRunLoopAddTimer(gPluginRunLoop, fNSPCTimerRef, kCFRunLoopDefaultMode);
}
gTimerMutex->SignalLock();
}
void ServerControl::DoSearchPolicyChangedNotify( 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" );
}
switch( ShouldRegisterWorkstation() )
{
case -1:
WorkstationServiceUnregister();
break;
case 1:
WorkstationServiceRegister();
break;
}
}
void ServerControl::DoNodeSearchPolicyChange( void )
{
gSrvrCntl->FlushMemberDaemonCache();
DoSearchPolicyChangedNotify();
LaunchKerberosAutoConfigTool();
}
void ServerControl::NotifyDirNodeAdded( const char* newNode )
{
SCDynamicStoreRef store = NULL;
if ( newNode != nil )
{
CFStringRef newNodeRef = CFStringCreateWithCString( NULL, newNode, kCFStringEncodingUTF8 );
if ( newNodeRef == NULL )
{
ErrLog( 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 ) )
{
ErrLog( kLogApplication, "Could not set the DirectoryService:NotifyDirNodeAdded in System Configuration" );
}
CFRelease(store);
store = NULL;
}
else
{
ErrLog( 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 )
{
ErrLog( 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 ) )
{
ErrLog( kLogApplication, "Could not set the DirectoryService:NotifyDirNodeDeleted in System Configuration" );
}
CFRelease(store);
store = NULL;
}
else
{
ErrLog( 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; i<pluginCount+1; i++ )
{
if ( table[i] )
{
free( table[i] );
table[i] = NULL;
}
}
free( table );
}
}
PluginPerformanceStats** ServerControl::CreatePerfStatTable( void )
{
DbgLog( kLogPerformanceStats, "ServerControl::CreatePerfStatTable called\n" );
PluginPerformanceStats** table = NULL;
UInt32 pluginCount = gPlugins->GetPlugInCount();
if ( fPerfTable )
DeletePerfStatTable();
table = (PluginPerformanceStats**)calloc( sizeof(PluginPerformanceStats*), pluginCount+1 );
for ( UInt32 i=0; i<pluginCount; i++ )
{
table[i] = (PluginPerformanceStats*)calloc( sizeof(PluginPerformanceStats), 1 );
table[i]->pluginSignature = 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 )
{
gPerformanceLoggingLock->WaitLock();
PluginPerformanceStats* curPluginStats = NULL;
UInt32 pluginCount = gPlugins->GetPlugInCount();
if ( !fPerfTable || fPerfTableNumPlugins != pluginCount )
{
fPerfTable = CreatePerfStatTable();
}
if ( !pluginSig )
curPluginStats = fPerfTable[pluginCount];
if ( fPerfTable[fLastPluginCalled]->pluginSignature == pluginSig )
curPluginStats = fPerfTable[fLastPluginCalled];
for ( UInt32 i=0; !curPluginStats && i<pluginCount; i++ )
{
if ( pluginSig == fPerfTable[i]->pluginSignature )
{
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->SignalLock();
}
#define USEC_PER_HOUR (double)60*60*USEC_PER_SEC
#define USEC_PER_DAY (double)24*USEC_PER_HOUR
void ServerControl::LogStats( void )
{
PluginPerformanceStats* curPluginStats = NULL;
UInt32 pluginCount = fPerfTableNumPlugins;
char logBuf[1024];
char totTimeStr[256];
gPerformanceLoggingLock->WaitLock();
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; i<pluginCount+1; i++ ) {
if ( !fPerfTable[i] )
continue;
curPluginStats = fPerfTable[i];
for ( UInt32 j=0; j<kDSPlugInCallsEnd; j++ )
{
if ( curPluginStats->apiStats[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->SignalLock();
}
#endif
void DoPeriodicTask(CFRunLoopTimerRef timer, void *info)
{
SInt32 siResult = eDSNoErr;
UInt32 iterator = 0;
CServerPlugin *pPlugin = nil;
CPlugInList::sTableData *pPIInfo = nil;
if ( gDSLocalOnlyMode && (gLocalSessionCount == 0) )
{
SrvrLog( kLogApplication, "Shutting down DirectoryService..." );
gSrvrCntl->ShutDownServer();
exit(0);
}
if ( gPlugins != nil )
{
pPlugin = gPlugins->Next( &iterator );
while (pPlugin != nil)
{
pPIInfo = gPlugins->GetPlugInInfo( iterator-1 );
if (pPIInfo->fState & kActive) {
siResult = pPlugin->PeriodicTask();
if (siResult != eDSNoErr)
{
if (pPIInfo != nil)
{
DbgLog( kLogApplication, "Periodic Task in %s plugin returned error %d", pPIInfo->fName, siResult );
}
else
{
DbgLog( kLogApplication, "Periodic Task of unnamed plugin returned error %d", siResult );
}
}
}
pPlugin = gPlugins->Next( &iterator );
}
}
return;
}
SInt32 ServerControl::FlushMemberDaemonCache ( void )
{
SInt32 siResult = eDSNoErr;
DbgLog( kLogApplication, "Flushing membership cache" );
Mbrd_ProcessResetCache();
return(siResult);
}
void ServerControl::CreateDebugPrefFileIfNecessary( bool bForceCreate )
{
UInt32 uiDataSize = 0;
CFile *pFile = nil;
struct stat statbuf;
CFDataRef dataRef = nil;
if ( bForceCreate )
unlink( kDSDebugConfigFilePath );
if ( lstat(kDSDebugConfigFilePath, &statbuf) != 0 )
{
SInt32 result = eDSNoErr;
gDebugLogging = true;
CLog::SetLoggingPriority(keDebugLog, 5);
uiDataSize = ::strlen( kDefaultDebugConfig );
dataRef = ::CFDataCreate( nil, (const UInt8 *)kDefaultDebugConfig, uiDataSize );
if ( dataRef != nil )
{
result = dsCreatePrefsDirectory();
CFIndex dataLen = CFDataGetLength( dataRef );
const UInt8 *pUData = CFDataGetBytePtr( dataRef );
if ( pUData != NULL && dataLen > 0 )
{
try
{
pFile = new CFile( kDSDebugConfigFilePath, true );
if ( pFile != nil )
{
if ( pFile->is_open() )
{
pFile->seteof( 0 );
pFile->write( pUData, dataLen );
chmod( kDSDebugConfigFilePath, 0600 );
}
delete( pFile );
pFile = nil;
}
}
catch ( ... )
{
}
}
CFRelease( dataRef );
dataRef = nil;
}
}
}
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;
if (gDebugLogging)
{
CLog::StopDebugLog();
gDebugLogging = false;
syslog(LOG_ALERT,"Debug Logging turned OFF after receiving USR1 signal.");
}
gDSFWCSBPDebugLogging = false;
}
else
{
gToggleDebugging = true;
siResult = ::stat( kDSDebugConfigFilePath, &statbuf );
if ( siResult == eDSNoErr )
{
pFile = new CFile( kDSDebugConfigFilePath );
if (pFile != nil)
{
if ( (pFile->is_open()) && (pFile->FileSize() > 0) )
{
pData = (char *)::calloc( 1, pFile->FileSize() + 1 );
if ( pData != nil )
{
uiDataSize = pFile->ReadBlock( pData, pFile->FileSize() );
dataRef = ::CFDataCreate( nil, (const UInt8 *)pData, uiDataSize );
if ( dataRef != nil )
{
CFPropertyListRef aPlistRef = 0;
CFDictionaryRef aDictRef = 0;
aPlistRef = ::CFPropertyListCreateFromXMLData( kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nil );
if ( aPlistRef != nil )
{
if ( ::CFDictionaryGetTypeID() == ::CFGetTypeID( aPlistRef ) )
{
bFileUsed = true;
aDictRef = (CFDictionaryRef)aPlistRef;
if ( CFDictionaryContainsKey( aDictRef, CFSTR( kXMLDSIgnoreSunsetTimeKey ) ) )
{
cfBool= (CFBooleanRef)CFDictionaryGetValue( aDictRef, CFSTR( kXMLDSIgnoreSunsetTimeKey ) );
if (cfBool != nil)
{
gIgnoreSunsetTime = CFBooleanGetValue( cfBool );
}
}
if ( CFDictionaryContainsKey( aDictRef, CFSTR( kXMLDSDebugLoggingKey ) ) )
{
cfBool= (CFBooleanRef)CFDictionaryGetValue( aDictRef, CFSTR( kXMLDSDebugLoggingKey ) );
if (cfBool != nil)
{
bDebugging = CFBooleanGetValue( cfBool );
if (gDebugLogging && !bDebugging)
{
CLog::StopDebugLog();
gDebugLogging = false;
syslog(LOG_ALERT,"Debug Logging turned OFF after receiving USR1 signal.");
}
else if (bDebugging)
{
if ( CFDictionaryContainsKey( aDictRef, CFSTR( kXMLDSDebugLoggingPriority ) ) )
{
CFNumberRef logPriority = (CFNumberRef)CFDictionaryGetValue( aDictRef, CFSTR(kXMLDSDebugLoggingPriority) );
UInt32 priorityValue = 0;
CFNumberGetValue(logPriority, kCFNumberIntType, &priorityValue);
if (priorityValue > 0 && priorityValue < 9) {
CLog::SetLoggingPriority(keDebugLog, priorityValue);
syslog(LOG_ALERT,"Debug Logging priority %u set.", priorityValue);
}
}
else
{
CLog::SetLoggingPriority(keDebugLog, 5);
syslog(LOG_ALERT,"Debug Logging default priority 5 set.");
}
CLog::StartDebugLog();
if (!gDebugLogging)
{
syslog(LOG_ALERT,"Debug Logging turned ON after receiving USR1 signal.");
gDebugLogging = true;
}
}
}
}
else if (gDebugLogging)
{
CLog::StopDebugLog();
gDebugLogging = false;
syslog(LOG_ALERT,"Debug Logging turned OFF after receiving USR1 signal.");
}
if ( CFDictionaryContainsKey( aDictRef, CFSTR( kXMLDSCSBPDebugLoggingKey ) ) )
{
cfBool= (CFBooleanRef)CFDictionaryGetValue( aDictRef, CFSTR( kXMLDSCSBPDebugLoggingKey ) );
if (cfBool != nil)
{
gDSFWCSBPDebugLogging = CFBooleanGetValue( cfBool );
}
}
else
{
gDSFWCSBPDebugLogging = false;
}
aDictRef = 0;
}
CFRelease(aPlistRef);
}
CFRelease( dataRef );
dataRef = nil;
}
free( pData );
pData = nil;
}
}
delete( pFile );
pFile = nil;
}
}
if (!bFileUsed)
CreateDebugPrefFileIfNecessary( true );
}
return(siResult);
}