#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 "CRefTable.h"
#include "DSMutexSemaphore.h"
#include "DSCThread.h"
#include "CServerPlugin.h"
#include "CPluginHandler.h"
#include "CSearchPlugin.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 "DSTCPEndpoint.h"
#include "DSTCPEndian.h"
#include "CInternalDispatch.h"
#include "COSUtils.h"
#include "BaseDirectoryPlugin.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>
#include <netdb.h>
#include <sys/socket.h>
#include <assert.h>
#include <SystemConfiguration/SCDynamicStore.h>
#include <dispatch/dispatch.h>
#include <sys/sysctl.h> // for struct kinfo_proc and sysctl()
#include <fcntl.h>
extern "C" {
#include "DirectoryServiceMIGServer.h"
#include "DSlibinfoMIGServer.h"
#include "DSmemberdMIGServer.h"
#include "DSlibinfoMIGAsyncReply.h"
}
dispatch_source_t ServerControl::fTCPListener = NULL;
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 bool gLogAPICalls;
extern bool gDebugLogging;
extern dsBool gDSFWCSBPDebugLogging;
extern bool gIgnoreSunsetTime;
extern bool gServerOS;
extern dsBool gDSDebugMode;
extern CCachePlugin *gCacheNode;
extern dsBool gDSLocalOnlyMode;
extern dsBool gDSInstallDaemonMode;
extern DSEventSemaphore gKickCacheRequests;
extern int gKernelTimeout;
extern uint32_t gNumberOfCores;
UInt32 gAPICallCount = 0;
UInt32 gLookupAPICallCount = 0;
ServerControl *gSrvrCntl = nil;
CRefTable gRefTable( ServerControl::RefDeallocProc );
CPlugInList *gPlugins = nil;
dispatch_queue_t gLibinfoQueue = NULL;
CPluginConfig *gPluginConfig = nil;
CNodeList *gNodeList = nil;
CPluginHandler *gPluginHandler = nil;
char *gDSLocalFilePath = nil;
UInt32 gLocalSessionCount = 0;
DSSemaphore gLocalSessionLock("::gLocalSessionLock");
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");
pid_t gDaemonPID;
in_addr_t gDaemonIPAddress;
UInt32 gRefCountWarningLimit = 500;
UInt32 gDelayFailedLocalAuthReturnsDeltaInSeconds = 1;
UInt32 gMaxHandlerThreadCount = kMaxHandlerThreads;
dsBool gToggleDebugging = false;
bool gFirstNetworkUpAtBoot = false;
bool gNetInfoPluginIsLoaded = false;
dispatch_source_t gLibinfoDispatchSource = NULL;
dispatch_source_t gAPIDispatchSource = NULL;
dispatch_source_t gMembershipDispatchSource = NULL;
extern mach_port_t gLibinfoMachPort;
extern mach_port_t gAPIMachPort;
extern mach_port_t gMembershipMachPort;
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",
#ifdef HANDLE_DNS_LOOKUPS
"getaddrinfo",
"getnameinfo",
#endif
"gethostbyname",
"gethostbyaddr",
"gethostent",
"getmacbyname",
"gethostbymac",
"getbootpbyhw",
"getbootpbyaddr",
#ifdef HANDLE_DNS_LOOKUPS
"dns_proxy",
#endif
"gethostbyname_service",
"_flushcache",
"_flushentry",
"getpwnam_ext",
"getpwnam_initext",
"getgrnam_ext",
"getgrnam_initext",
"lastprocnum",
NULL };
#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;
if ( (gDebugLogging) || (gLogAPICalls) )
{
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( NULL, 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 = NULL;
if ( *result == eDSNoErr )
{
if ( textStr != NULL ) 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 );
}
return KERN_SUCCESS;
}
static boolean_t dsmig_demux_internaldispatch( mach_msg_header_t *request, mach_msg_header_t *reply )
{
boolean_t result;
CInternalDispatch::AddCapability();
if ( request->msgh_id >= 60000 ) result = DSmemberdMIG_server( request, reply );
else if ( request->msgh_id >= 50000 ) result = DSlibinfoMIG_server( request, reply );
else
result = DirectoryServiceMIG_server(request, reply);
if ( request->msgh_id == MACH_NOTIFY_NO_SENDERS ) {
mach_vm_address_t context = NULL;
mach_port_get_context( mach_task_self(), request->msgh_local_port, &context );
DbgLog( kLogDebug, "dsdispatch_no_senders_notification:: %u", request->msgh_local_port );
gRefTable.CleanRefsForMachRefs( request->msgh_local_port );
if ( context != 0 ) {
dispatch_cancel( (dispatch_source_t) context );
dispatch_release( (dispatch_source_t) context );
}
}
return result;
}
static void
dsdispatch_process_wentaway( pid_t inProc )
{
DbgLog( kLogNotice, "dsdispatch_process_wentaway:: PID: %d - has exited, exec'd, or closed session", inProc );
dispatch_source_t source = dispatch_source_timer_create( DISPATCH_TIMER_ONESHOT,
5ULL * NSEC_PER_SEC,
1ULL * NSEC_PER_SEC,
NULL,
dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
^(dispatch_source_t ds) {
if ( dispatch_source_get_error(ds, NULL) == 0 && gLocalSessionLock.WaitTryLock() ) {
if ( gLocalSessionCount == 0 ) {
CFRunLoopStop( CFRunLoopGetMain() );
} else {
gLocalSessionLock.SignalLock();
}
}
} );
assert( source != NULL );
}
static dispatch_source_t
CreateDispatchSourceForMachPort( mach_port_t newServer, size_t maxSize, pid_t inPID, bool bPerClientPort )
{
#define MY_MIG_OPTIONS (MACH_RCV_TIMEOUT | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | \
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0))
dispatch_source_t machSource;
dispatch_queue_t machQueue;
if ( bPerClientPort == true ) {
machQueue = dispatch_queue_create( "per-client MIG queue", NULL );
assert( machQueue != NULL );
machSource = dispatch_source_machport_create( newServer,
DISPATCH_MACHPORT_RECV,
DISPATCH_SOURCE_CREATE_SUSPENDED,
machQueue,
^(dispatch_source_t ds) {
if ( dispatch_source_get_error(ds, NULL) == 0 ) {
mach_msg_server( dsmig_demux_internaldispatch,
maxSize,
newServer,
MY_MIG_OPTIONS );
} else {
mach_port_mod_refs( mach_task_self(), newServer, MACH_PORT_RIGHT_RECEIVE, -1 );
}
} );
assert( machSource != NULL );
mach_port_set_context( mach_task_self(), newServer, (mach_vm_address_t) machSource );
mach_port_t oldTargetOfNotification = MACH_PORT_NULL;
mach_port_request_notification( mach_task_self(), newServer, MACH_NOTIFY_NO_SENDERS, 1, newServer,
MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldTargetOfNotification );
dispatch_release( machQueue );
if ( gDSLocalOnlyMode == true && inPID > 0 ) {
dispatch_source_t procSource = dispatch_source_proc_create( inPID,
DISPATCH_PROC_EXIT | DISPATCH_PROC_EXEC,
NULL,
dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
^(dispatch_source_t ds) {
if ( dispatch_source_get_error(ds, NULL) == 0 ) {
dsdispatch_process_wentaway( inPID );
dispatch_cancel( ds );
}
else {
dispatch_release( ds );
}
} );
assert( procSource != NULL );
}
}
else {
machQueue = dispatch_get_concurrent_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT );
dispatch_group_t migGroup = dispatch_group_create();
machSource = dispatch_source_machport_create( newServer,
DISPATCH_MACHPORT_RECV,
DISPATCH_SOURCE_CREATE_SUSPENDED,
machQueue,
^(dispatch_source_t ds) {
if ( dispatch_source_get_error(ds, NULL) == 0 ) {
dispatch_group_async( migGroup,
machQueue,
^(void) {
mach_msg_server( dsmig_demux_internaldispatch,
maxSize,
newServer,
MY_MIG_OPTIONS );
} );
} else {
dispatch_group_wait( migGroup, UINT64_MAX );
dispatch_release( migGroup );
mach_port_mod_refs( mach_task_self(), newServer, MACH_PORT_RIGHT_RECEIVE, -1 );
}
} );
assert( machSource != NULL );
}
dispatch_resume( machSource );
return machSource;
}
kern_return_t dsmig_do_create_api_session( mach_port_t server, mach_port_t *newServer, audit_token_t *atoken )
{
pid_t aPID;
audit_token_to_au32( *atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, newServer );
CreateDispatchSourceForMachPort( *newServer, kMaxMIGMsg, aPID, true );
if ( LoggingEnabled(kLogInfo) == true ) {
char *pName = dsGetNameForProcessID( aPID );
DbgLog( kLogDebug, "dsdispatch_create_api_session - created new dispatch source for Mach port: %u Client: '%s' PID: %d",
*newServer, pName, aPID );
DSFree( pName );
}
return KERN_SUCCESS;
}
kern_return_t dsmig_do_close_api_session( mach_port_t server, audit_token_t atoken )
{
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
gRefTable.CleanRefsForMachRefs( server );
if ( LoggingEnabled(kLogInfo) == true ) {
char *pName = dsGetNameForProcessID( aPID );
DbgLog( kLogDebug, "dsdispatch_close_api_session - cleaning up for port: %u Client: '%s' PID: %d",
server, pName, aPID );
DSFree( pName );
}
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;
}
else
{
pComData = (sComDataPtr) msg_data_ool;
uiLength = msg_data_oolCnt;
}
if ( pComData->type.msgt_translate == 0 )
{
dataLength = pComData->fDataLength;
dataSize = pComData->fDataSize;
}
else
{
dataLength = DSGetLong( &pComData->fDataLength, kDSSwapNetworkToHostOrder );
dataSize = DSGetLong( &pComData->fDataSize, kDSSwapNetworkToHostOrder );
}
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 );
if ( pRequest->type.msgt_translate != 0 ) {
SwapMachMessage( pRequest, kDSSwapNetworkToHostOrder );
}
pRequest->fMachPort = server;
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) )
{
reqEndTime = dsTimestamp();
double totalTime = (reqEndTime - reqStartTime) / USEC_PER_SEC;
if (totalTime > 2.0)
{
char *debugDataTag = handler.BuildAPICallDebugDataTag( NULL, pRequest->fPID, "API", "Server" );
DbgLog( kLogHandler, "%s : dsmig DAR : Excessive request time %f seconds", debugDataTag, totalTime );
free( debugDataTag );
}
}
pRequest->fPID = gDaemonPID;
UInt32 dataLen = pRequest->fDataLength;
if ( pRequest->type.msgt_translate != 0 ) {
SwapMachMessage( pRequest, kDSSwapHostToNetworkOrder );
}
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;
if ( (gDebugLogging) || (gLogAPICalls) )
{
pid_t aPID;
CRequestHandler handler;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( NULL, 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 );
}
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;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
if ( bValidProcedure )
{
debugDataTag = handler.BuildAPICallDebugDataTag( NULL, 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( NULL, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Invalid Procedure = %d", debugDataTag, procnumber );
}
}
#ifdef HANDLE_DNS_LOOKUPS
if( gCacheNode == NULL && (aPID != (SInt32)gDaemonPID || (procnumber >= kDSLUgetaddrinfo && procnumber <= kDSLUdns_proxy )) )
{
gKickCacheRequests.WaitForEvent(); }
#endif
if ( gCacheNode != NULL && bValidProcedure )
{
struct stat sb;
if ( procnumber == kDSLUflushcache && lstat("/AppleInternal", &sb) == 0 && lstat("/var/db/disableAppleInternal", &sb) == -1 ) {
char *pName = dsGetNameForProcessID( aPID );
syslog( LOG_ERR, "***Mobility: PID: %d '%s' requested flush of libinfo cache - this could affect sleep/wake/etc.", aPID,
pName );
DSFree( pName );
}
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);
}
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;
__block 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( NULL, 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( NULL, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Invalid Async Procedure = %d", debugDataTag, procnumber );
}
}
if( bValidProcedure )
{
pLibinfoRequest = new sLibinfoRequest;
if ( pLibinfoRequest != NULL )
{
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;
void (^theBlock)(void) = ^(void) {
char *debugDataTag = NULL;
pid_t aPID;
CRequestHandler handler;
audit_token_to_au32( pLibinfoRequest->fToken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
if ( (gDebugLogging) || (gLogAPICalls) )
{
debugDataTag = handler.BuildAPICallDebugDataTag( NULL, aPID, "libinfo", "Server" );
DbgLog( kLogHandler, "%s : libinfomig DAC : Async Procedure = %s (%d) : Handle request %X", debugDataTag, lookupProcedures[pLibinfoRequest->fProcedure], pLibinfoRequest->fProcedure, pLibinfoRequest );
}
kvbuf_t *pResponse = gCacheNode->ProcessLookupRequest( pLibinfoRequest->fProcedure, pLibinfoRequest->fBuffer, pLibinfoRequest->fBufferLen, aPID );
if ( pResponse != NULL )
{
if( pResponse->datalen <= MAX_MIG_INLINE_DATA )
{
if ( libinfoDSmig_Response_async( pLibinfoRequest->fReplyPort, pResponse->databuf, pResponse->datalen, 0, 0, pLibinfoRequest->fCallbackAddr ) != MACH_MSG_SUCCESS )
{
mach_port_destroy( mach_task_self(), pLibinfoRequest->fReplyPort );
}
}
else
{
vm_offset_t data = 0;
mach_msg_type_number_t dataCnt = 0;
vm_read( mach_task_self(), (vm_address_t) pResponse->databuf, pResponse->datalen, &data, &dataCnt );
if ( libinfoDSmig_Response_async( pLibinfoRequest->fReplyPort, (char *)"", 0, data, dataCnt, pLibinfoRequest->fCallbackAddr ) != MACH_MSG_SUCCESS )
{
vm_deallocate( mach_task_self(), data, dataCnt );
mach_port_destroy( mach_task_self(), pLibinfoRequest->fReplyPort );
}
}
}
else
{
if ( libinfoDSmig_Response_async( pLibinfoRequest->fReplyPort, (char *)"", 0, 0, 0, pLibinfoRequest->fCallbackAddr ) != MACH_MSG_SUCCESS )
{
mach_port_destroy( mach_task_self(), pLibinfoRequest->fReplyPort );
}
}
kvbuf_free( pResponse );
pResponse = NULL;
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : libinfomig DAR : Async Procedure = %s (%d)", debugDataTag, lookupProcedures[pLibinfoRequest->fProcedure], pLibinfoRequest->fProcedure );
DSFree( debugDataTag );
}
DSFree( pLibinfoRequest->fBuffer );
DSDelete( pLibinfoRequest );
};
dispatch_async( gLibinfoQueue, theBlock );
}
else
{
DSDelete( pLibinfoRequest );
}
}
if ( pLibinfoRequest == NULL ) {
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);
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( *atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( NULL, aPID, "MembershipCall", "Server" );
DbgLog( kLogHandler, "%s : mbr_mig DAC %s", debugDataTag, (needsSwap ? " : Via Rosetta" : "") );
if( aPID == (SInt32)gDaemonPID )
{
DbgLog( kLogHandler, "%s : mbr_mig DAC : Dispatching from/to ourself", debugDataTag );
}
}
Mbrd_ProcessLookup( request );
if (needsSwap)
Mbrd_SwapRequest(request);
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbr_mig 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( NULL, aPID, "ClearStats", "Server" );
DbgLog( kLogHandler, "%s : mbr_mig DAC", debugDataTag );
}
Mbrd_ProcessResetStats();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbr_mig 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( NULL, aPID, "MapName", "Server" );
DbgLog( kLogHandler, "%s : mbr_mig DAC : Name = %s : isUser = %d", debugDataTag, name, (int) isUser );
}
result = Mbrd_ProcessMapIdentifier( isUser ? ID_TYPE_USERNAME : ID_TYPE_GROUPNAME, name, -1, guid );
if ( debugDataTag )
{
if (result == KERN_SUCCESS)
{
char uuid_string[37] = { 0, };
uuid_unparse_upper( guid->g_guid, uuid_string );
DbgLog( kLogHandler, "%s : mbr_mig DAR : Name = %s : GUID = %s", debugDataTag, name, uuid_string );
}
else
{
DbgLog( kLogHandler, "%s : mbr_mig DAR : Name = %s : Not found", debugDataTag, name );
}
free( debugDataTag );
}
return result;
}
kern_return_t memberdDSmig_do_MapIdentifier(mach_port_t server, int idType,
vm_offset_t identifier, mach_msg_type_number_t identifierCnt,
vm_offset_t ooidentifier, mach_msg_type_number_t ooidentifierCnt,
guid_t *guid, audit_token_t *atoken)
{
kern_return_t result;
char *debugDataTag = NULL;
void *idValue = (void *) (identifierCnt ? identifier : ooidentifier);
size_t idLength = (identifierCnt ? : ooidentifierCnt);
if ( (gDebugLogging) || (gLogAPICalls) )
{
CRequestHandler handler;
pid_t aPID;
audit_token_to_au32( *atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
debugDataTag = handler.BuildAPICallDebugDataTag( NULL, aPID, "MapIdentifier", "Server" );
switch ( idType ) {
case ID_TYPE_UID:
case ID_TYPE_GID:
DbgLog( kLogHandler, "%s : mbr_mig DAC : ID Type = %d, value = %d", debugDataTag, idType, *((id_t *) idValue) );
break;
case ID_TYPE_USERNAME:
case ID_TYPE_GROUPNAME:
case ID_TYPE_X509_DN:
case ID_TYPE_KERBEROS:
DbgLog( kLogHandler, "%s : mbr_mig DAC : ID Type = %d, value = %s", debugDataTag, idType, idValue );
break;
case ID_TYPE_GSS_EXPORT_NAME:
default:
DbgLog( kLogHandler, "%s : mbr_mig DAC : ID Type = %d", debugDataTag, idType );
break;
}
}
result = Mbrd_ProcessMapIdentifier( idType, idValue, idLength, guid );
if ( debugDataTag )
{
if (result == KERN_SUCCESS)
{
if ( (gDebugLogging) || (gLogAPICalls) )
{
char uuid_string[37] = { 0, };
uuid_unparse_upper( guid->g_guid, uuid_string );
DbgLog( kLogHandler, "%s : mbr_mig DAR : GUID = %s", debugDataTag, uuid_string );
}
}
else
{
DbgLog( kLogHandler, "%s : mbr_mig DAR : Not found", debugDataTag );
}
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( NULL, aPID, "GetGroups", "Server" );
DbgLog( kLogHandler, "%s : mbr_mig DAC : uid = %u", debugDataTag, uid );
}
result = Mbrd_ProcessGetGroups(uid, numGroups, gids);
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbr_mig 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( NULL, aPID, "UserGroup_GetAllGroups", "Server" );
DbgLog( kLogHandler, "%s : mbr_mig DAC : uid = %u", debugDataTag, uid );
}
GIDList tempList = NULL;
(*gids) = NULL;
(*gidsCnt) = 0;
result = Mbrd_ProcessGetAllGroups(uid, numGroups, &tempList);
if ( result == KERN_SUCCESS && (*numGroups) > 0 && tempList != NULL )
{
result = vm_read( mach_task_self(), (vm_address_t) tempList, ((*numGroups) * sizeof(gid_t)), (vm_offset_t *) gids, gidsCnt );
if ( result == KERN_SUCCESS )
{
(*gidsCnt) = (*numGroups);
}
DSFree( tempList );
}
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbr_mig 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;
struct stat sb;
if ( gDebugLogging == true ||
gLogAPICalls == true ||
(lstat("/AppleInternal", &sb) == 0 && lstat("/var/db/disableAppleInternal", &sb) == -1) )
{
pid_t aPID;
audit_token_to_au32( atoken, NULL, NULL, NULL, NULL, NULL, &aPID, NULL, NULL );
if ( gDebugLogging == true || gLogAPICalls == true )
{
CRequestHandler handler;
debugDataTag = handler.BuildAPICallDebugDataTag( NULL, aPID, "ClearCache", "Server" );
DbgLog( kLogHandler, "%s : mbr_mig DAC", debugDataTag );
}
else
{
char *pName = dsGetNameForProcessID( aPID );
syslog( LOG_ERR, "***Mobility: PID: %d '%s' requested flush of membership cache - this could affect sleep/wake/etc.", aPID,
pName );
DSFree( pName );
}
}
Mbrd_ProcessResetCache();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbr_mig 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( NULL, aPID, "DumpState", "Server" );
DbgLog( kLogHandler, "%s : mbr_mig DAC", debugDataTag );
}
Mbrd_ProcessDumpState();
if ( debugDataTag )
{
DbgLog( kLogHandler, "%s : mbr_mig DAR", debugDataTag );
free( debugDataTag );
}
return KERN_SUCCESS;
}
#pragma mark -
#pragma mark ServerControl Routines
#pragma mark -
ServerControl::ServerControl ( void )
{
gDaemonPID = getpid();
gDaemonIPAddress = 0;
fTCPHandlerThreadsCnt = 0;
fSCDStore = 0;
fPerformanceStatGatheringActive = false; fMemberDaemonFlushCacheRequestCount = 0;
#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::RefDeallocProc ( UInt32 inRefNum, eRefType inRefType, CServerPlugin *inPluginPtr )
{
SInt32 dsResult = eDSNoErr;
double inTime = 0;
double outTime = 0;
if (inPluginPtr != nil)
{
sCloseDirNode closeData;
closeData.fResult = eDSNoErr;
closeData.fInNodeRef = inRefNum;
switch (inRefType)
{
case eRefTypeDirNode:
closeData.fType = kCloseDirNode;
break;
case eRefTypeRecord:
closeData.fType = kCloseRecord;
break;
case eRefTypeAttributeList:
closeData.fType = kCloseAttributeList;
break;
case eRefTypeAttributeValueList:
closeData.fType = kCloseAttributeValueList;
break;
default:
closeData.fType = 0;
break;
}
if (closeData.fType != 0)
{
if (gLogAPICalls)
{
inTime = dsTimestamp();
}
inPluginPtr->ProcessRequest( &closeData );
dsResult = closeData.fResult;
if (gLogAPICalls)
{
outTime = dsTimestamp();
syslog(LOG_CRIT,"Ref table dealloc callback, API Call: %s, PlugIn Used: %s, Result: %d, Duration: %.2f usec",
CRequestHandler::GetCallName( closeData.fType ), inPluginPtr->GetPluginName(), dsResult,
(outTime - inTime));
}
}
}
return( dsResult );
}
#pragma mark -
#pragma mark Kernel Routines
struct kauth_request {
STAILQ_ENTRY(kauth_request) stailq_entry;
kauth_identity_extlookup kauth;
};
static dispatch_semaphore_t active_semaphore;
static int
_RegisterKernel(void)
{
int kresult = syscall(SYS_identitysvc, KAUTH_EXTLOOKUP_REGISTER, (void *) gKernelTimeout);
if (kresult != 0) {
DbgLog( kLogError, "Fatal error registering for Kernel identity service requests - %d: %s", errno, strerror(errno) );
syslog( LOG_ERR, "Fatal error registering for Kernel identity service requests - %d: %s", errno, strerror(errno) );
CFRunLoopStop(CFRunLoopGetMain());
} else {
SrvrLog(kLogApplication, "Successfully registered for Kernel identity service requests");
}
return kresult;
}
static void
__kauth_worker(void *context)
{
struct kauth_request *request = (struct kauth_request *) context;
char *debugDataTag = NULL;
int result;
CInternalDispatch::AddCapability();
if (gDebugLogging || gLogAPICalls) {
static CRequestHandler handler;
debugDataTag = handler.BuildAPICallDebugDataTag(NULL, request->kauth.el_info_pid, "mbr_syscall", "Server" );
DbgLog( kLogAPICalls, "%s : process kauth result %X", debugDataTag, request );
}
request->kauth.el_flags |= kKernelRequest;
Mbrd_ProcessLookup(&request->kauth);
request->kauth.el_flags &= ~kKernelRequest;
result = syscall(SYS_identitysvc, KAUTH_EXTLOOKUP_RESULT, &request->kauth);
if (result == 0) {
if (debugDataTag != NULL) {
DbgLog( kLogAPICalls, "%s : delivered kauth result %X", debugDataTag, request );
}
} else {
SrvrLog(kLogApplication, "Kernel identity service failed to deliver result - %d: %s", errno, strerror(errno));
syslog(LOG_ERR, "Kernel identity service failed to deliver result - %d: %s", errno, strerror(errno));
}
DSFree(debugDataTag);
DSFree(request);
dispatch_semaphore_signal(active_semaphore); }
static void __StartKernelListener( void )
{
static dispatch_once_t once;
static dispatch_semaphore_t workq_semaphore;
static dispatch_group_t group;
static STAILQ_HEAD( , kauth_request) request_workq = STAILQ_HEAD_INITIALIZER(request_workq);
static volatile OSSpinLock lock = OS_SPINLOCK_INIT;
dispatch_once(&once, ^(void) {
active_semaphore = dispatch_semaphore_create(gNumberOfCores);
workq_semaphore = dispatch_semaphore_create(0);
group = dispatch_group_create();
});
dispatch_async(dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_HIGH), ^(void) {
for (;;) {
kauth_request *request;
dispatch_semaphore_wait(workq_semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(active_semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_enter(group);
OSSpinLockLock(&lock);
request = STAILQ_FIRST(&request_workq);
STAILQ_REMOVE_HEAD(&request_workq, stailq_entry);
OSSpinLockUnlock(&lock);
dispatch_group_async_f(group, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_HIGH), request, __kauth_worker);
dispatch_group_leave(group);
}
});
dispatch_async(dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_HIGH), ^(void) {
int kresult = _RegisterKernel();
if (kresult == 0) {
do {
struct kauth_request *request = (struct kauth_request *) calloc(1, sizeof(*request));
kresult = syscall(SYS_identitysvc, KAUTH_EXTLOOKUP_WORKER, &request->kauth);
if (kresult == KERN_SUCCESS) {
OSSpinLockLock(&lock);
STAILQ_INSERT_TAIL(&request_workq, request, stailq_entry);
OSSpinLockUnlock(&lock);
dispatch_semaphore_signal(workq_semaphore);
request = NULL;
} else {
if (errno == EPERM) {
SrvrLog( kLogApplication, "Kernel identity service worker error - Permission Denied");
syslog( LOG_ERR, "Kernel identity service worker error - Permission Denied");
OSSpinLockLock(&lock);
if (!STAILQ_EMPTY(&request_workq)) {
DbgLog(kLogNotice, "Kernel identity service flushing backlog due to kernel Permission error");
}
for (struct kauth_request *drainReq = STAILQ_FIRST(&request_workq); drainReq != NULL; drainReq = STAILQ_FIRST(&request_workq)) {
STAILQ_REMOVE_HEAD(&request_workq, stailq_entry);
DSFree(drainReq);
}
OSSpinLockUnlock(&lock);
} else {
SrvrLog( kLogApplication, "Fatal error for Kernel identity service worker - %d: %s", errno, strerror(errno) );
syslog( LOG_ERR, "Fatal error for Kernel identity service worker - %d: %s", errno, strerror(errno) );
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
kresult = syscall(SYS_identitysvc, KAUTH_EXTLOOKUP_DEREGISTER, NULL); if (kresult != 0) {
SrvrLog(kLogApplication, "Error deregistering for Kernel identity service - %d: %s", errno, strerror(errno));
}
kresult = _RegisterKernel();
}
DSFree(request);
} while (kresult == KERN_SUCCESS);
syscall(SYS_identitysvc, KAUTH_EXTLOOKUP_DEREGISTER, 0);
}
});
}
void ServerControl::StartKernelListener( void )
{
__StartKernelListener(); }
#pragma mark -
SInt32 ServerControl::StartUpServer ( void )
{
SInt32 result = eDSNoErr;
struct stat statResult;
try
{
if ( gNodeList == nil )
{
gNodeList = new CNodeList();
if ( gNodeList == nil ) throw((SInt32)eMemoryAllocError);
}
Mbrd_InitializeGlobals();
if ( gPluginConfig == nil )
{
gPluginConfig = new CPluginConfig();
if ( gPluginConfig == nil ) throw( (SInt32)eMemoryAllocError );
gPluginConfig->Initialize();
}
if ( gPlugins == nil )
{
gPlugins = new CPlugInList();
if ( gPlugins == nil ) throw( (SInt32)eMemoryAllocError );
gPlugins->ReadRecordTypeRestrictions();
}
if ( gLibinfoQueue == NULL )
{
gLibinfoQueue = dispatch_queue_create( "async_libinfo", NULL );
}
CreateDebugPrefFileIfNecessary();
bool bOnce = false;
if ( lstat("/Library/Preferences/DirectoryService/.DSLogAPIAtStartOnce", &statResult) == 0 ) {
bOnce = true;
unlink( "/Library/Preferences/DirectoryService/.DSLogAPIAtStartOnce" );
}
if ( bOnce == true || lstat("/Library/Preferences/DirectoryService/.DSLogAPIAtStart", &statResult) == 0)
{
if ( __sync_bool_compare_and_swap(&gLogAPICalls, false, true) == true ) {
ToggleAPILogging( false );
syslog( LOG_ALERT, "%s%s", "Logging of API Calls turned ON at Startup of DS Daemon",
(bOnce == true ? " (once)." : ".") );
}
}
if ( lstat("/Library/Preferences/DirectoryService/.DSLogDebugAtStartOnce", &statResult) == 0 ) {
bOnce = true;
unlink( "/Library/Preferences/DirectoryService/.DSLogDebugAtStartOnce" );
}
else {
bOnce = false;
}
if ( lstat("/Library/Preferences/DirectoryService/.DSLogDebugAtStart", &statResult) == 0 )
{
if ( __sync_bool_compare_and_swap(&gDebugLogging, false, true) == true ) {
ResetDebugging(); syslog( LOG_ALERT, "%s%s", "Debug Logging turned ON at Startup of DS Daemon",
(bOnce == true ? " (once)." : ".") );
}
}
if (!gDSLocalOnlyMode && !gDSInstallDaemonMode && !gDSDebugMode)
{
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();
}
result = RegisterForSystemPower();
if ( result != eDSNoErr ) throw( result );
result = (SInt32)RegisterForNetworkChange();
if ( result != eDSNoErr ) throw( result );
dispatch_source_t source = dispatch_source_timer_create( DISPATCH_TIMER_INTERVAL,
30ULL * NSEC_PER_SEC,
1ULL * NSEC_PER_SEC,
NULL,
dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
^(dispatch_source_t ds) {
ServerControl::DoPeriodicTask( ds );
} );
if ( source == NULL ) result = eMemoryAllocError;
dispatch_queue_t concurrentQueue = dispatch_get_concurrent_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT );
dispatch_async( concurrentQueue,
^(void) {
if ( gDSLocalOnlyMode == false ) {
gNodeList->WaitForAuthenticationSearchNode();
gNodeList->WaitForLocalNode();
gNodeList->WaitForCacheNode();
}
else {
gNodeList->WaitForLocalNode();
}
if ( gAPIMachPort != MACH_PORT_NULL ) {
gAPIDispatchSource = CreateDispatchSourceForMachPort( gAPIMachPort, kMaxMIGMsg, 0, false );
SrvrLog( kLogApplication, "Listening for DirectoryService API mach messages" );
DbgLog( kLogDebug, "Created mig source for API calls" );
}
if ( gLibinfoMachPort != MACH_PORT_NULL || gMembershipMachPort != MACH_PORT_NULL ) {
CInternalDispatch::AddCapability();
Mbrd_Initialize();
}
if ( gLibinfoMachPort != MACH_PORT_NULL ) {
gLibinfoDispatchSource = CreateDispatchSourceForMachPort( gLibinfoMachPort, kMaxMIGMsg, 0, false );
SrvrLog( kLogApplication, "Listening for Libinfo API mach messages" );
DbgLog( kLogDebug, "Created mig source for Libinfo calls" );
}
if ( gMembershipMachPort != MACH_PORT_NULL ) {
if ( gDSLocalOnlyMode == false && gDSDebugMode == false ) {
StartKernelListener();
}
dispatch_source_t ::mbrSweep = dispatch_source_timer_create( DISPATCH_TIMER_INTERVAL,
24ull * 60ull * 60ull * NSEC_PER_SEC,
15ULL * NSEC_PER_SEC,
NULL,
concurrentQueue,
^(dispatch_source_t) {
Mbrd_SweepCache();
} );
assert( mbrSweep != NULL );
gMembershipDispatchSource = CreateDispatchSourceForMachPort( gMembershipMachPort, kMaxMIGMsg, 0, false );
SrvrLog( kLogApplication, "Listening for Membership API mach messages" );
DbgLog( kLogDebug, "Created mig source for Membership calls" );
}
} );
}
catch( SInt32 err )
{
result = err;
}
return( result );
}
SInt32 ServerControl::ShutDownServer ( void )
{
SInt32 result = eDSNoErr;
try
{
result = (SInt32)UnRegisterForNetworkChange();
if ( result != eDSNoErr ) throw( result );
gAPIMachPort = MACH_PORT_NULL;
gLibinfoMachPort = MACH_PORT_NULL;
gMembershipMachPort = MACH_PORT_NULL;
if ( gLibinfoDispatchSource ) {
dispatch_cancel( gLibinfoDispatchSource );
dispatch_release( gLibinfoDispatchSource );
}
if ( gAPIDispatchSource ) {
dispatch_cancel( gAPIDispatchSource );
dispatch_release( gAPIDispatchSource );
}
if ( gMembershipDispatchSource ) {
dispatch_cancel( gMembershipDispatchSource );
dispatch_release( gMembershipDispatchSource );
}
if ( gDSLocalOnlyMode == false && gDSDebugMode == false) {
result = syscall(SYS_identitysvc, KAUTH_EXTLOOKUP_DEREGISTER, 0);
if (result == 0 ) {
SrvrLog( kLogApplication, "Deregistered Kernel identity service" );
}
else {
DbgLog( kLogError, "Failed to deregister Kernel identity service - %d: %s", errno, strerror(errno) );
}
}
CLog::Deinitialize();
}
catch( SInt32 err )
{
result = err;
}
return( result );
}
struct sProxyHeader {
char tag[kDSTCPEndpointMessageTagSize];
uint32_t payloadLen;
};
struct sProxyPacket {
sProxyHeader header;
char payload[];
};
struct sReadContext
{
uint32_t bufferSize;
uint32_t bufferLen;
union {
sProxyPacket *packet;
char *buffer;
} u;
};
static void __TCPListenerEventCallback( int listenFD )
{
sockaddr_storage address;
socklen_t aLen = sizeof( address );
char addr_string[INET6_ADDRSTRLEN];
int sock = accept( listenFD, (sockaddr *) &address, (socklen_t *)&aLen );
if ( sock != -1 )
{
int yes = 1;
setsockopt( sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes) );
setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(yes) );
int fcntlFlags = fcntl( sock, F_GETFL, 0 );
assert( fcntlFlags != -1 );
assert( fcntl(sock, F_SETFL, fcntlFlags | O_NONBLOCK) != -1 );
sReadContext *context = (sReadContext *) calloc( 1, sizeof(sReadContext) );
DSTCPEndpoint *endPoint = new DSTCPEndpoint( kTCPOpenTimeout, kTCPRWTimeout, sock );
in_port_t tcpPort;
switch ( address.ss_family ) {
case AF_INET:
tcpPort = ((sockaddr_in *) &address)->sin_port;
break;
case AF_INET6:
tcpPort = ((sockaddr_in6 *) &address)->sin6_port;
break;
default:
tcpPort = 0;
break;
}
void (^readBlock)(dispatch_source_t) = ^(dispatch_source_t ds) {
long err_code = 0;
long err_domain = dispatch_source_get_error( ds, &err_code );
switch ( err_domain )
{
case DISPATCH_ERROR_DOMAIN_NO_ERROR:
break;
case DISPATCH_ERROR_DOMAIN_POSIX:
if ( err_code == ECANCELED )
return;
default:
DbgLog( kLogError, "ServerControl::TCPReadEventCallback - dispatch timer receved error domain %ld error %ld",
err_domain, err_code );
}
uint32_t bytesAvail = dispatch_source_get_data( ds );
uint32_t newLen = context->bufferLen + bytesAvail;
DbgLog( kLogDebug, "ServerControl::TCPReadEventCallback - event %d bytes available", bytesAvail );
if ( context->u.buffer == NULL ) {
context->bufferSize = (bytesAvail ?: sizeof(sProxyHeader));
context->u.buffer = (char *) malloc( context->bufferSize );
assert( context->u.buffer != NULL );
}
else if ( newLen > context->bufferSize ) {
uint32_t expectedLen = (context->bufferLen > sizeof(sProxyHeader) ? ntohl(context->u.packet->header.payloadLen) : 0);
if ( expectedLen > newLen ) {
newLen = expectedLen;
}
context->bufferSize = newLen;
context->u.buffer = (char *) reallocf( context->u.buffer, context->bufferSize );
assert( context->u.buffer != NULL );
}
uint32_t bytes = read( sock, context->u.buffer + context->bufferLen, context->bufferSize - context->bufferLen );
if ( bytes < 0 ) {
if ( errno == EAGAIN ) return;
DbgLog( kLogError, "ServerControl::TCPReadEventCallback - error %d - releasing the source for socket %d", errno, sock );
dispatch_cancel( ds );
dispatch_release( ds );
return;
}
if ( bytes == 0 ) {
DbgLog( kLogInfo, "ServerControl::TCPReadEventCallback - Disconnect - releasing the source for socket %d", sock );
dispatch_cancel( ds );
dispatch_release( ds );
return;
};
context->bufferLen += bytes;
if ( context->bufferLen < sizeof(sProxyHeader) ) return;
if ( memcmp(context->u.packet->header.tag, "DSPX", kDSTCPEndpointMessageTagSize) != 0 ) {
context->bufferLen = 0;
return;
};
uint32_t expectedLen = ntohl( context->u.packet->header.payloadLen );
if ( expectedLen > 0 && context->bufferLen >= (expectedLen + sizeof(sProxyHeader)) )
{
char *payload = context->u.packet->payload;
char *buffer = context->u.buffer;
context->u.buffer = NULL;
context->bufferSize = 0;
context->bufferLen = 0;
dispatch_async( dispatch_get_current_queue(),
^(void) {
SInt32 siResult = eDSNoErr;
if ( endPoint->Negotiated() == true )
{
void *decryptedData = NULL;
UInt32 decryptedDataLen = 0;
siResult = endPoint->ProcessData( false,
payload, expectedLen,
decryptedData, decryptedDataLen );
if ( decryptedData != NULL )
{
sComProxyData *pProxyData = (sComProxyData *) decryptedData;
decryptedData = NULL;
if ( pProxyData->type.msgt_translate != 2 ) {
SwapProxyMessage( pProxyData, kDSSwapNetworkToHostOrder );
}
if ( pProxyData->fDataSize <= (decryptedDataLen - sizeof(sComProxyData)) ) {
CRequestHandler handler;
sComData *pComData;
pComData = endPoint->AllocFromProxyStruct( pProxyData );
DSFree( pProxyData );
bcopy( &address, &pComData->fIPAddress, address.ss_len );
pComData->fSocket = sock;
CInternalDispatch::AddCapability();
handler.HandleRequest( &pComData );
endPoint->SendMessage( pComData );
DSFree( pComData );
}
}
}
else
{
siResult = endPoint->ServerNegotiateKey( payload, expectedLen );
}
free( buffer );
if ( siResult != eDSNoErr ) {
char *status = dsCopyDirStatusName( siResult );
DbgLog( kLogError, "ServerControl::TCPReadEventCallback - %s (%d) - releasing the source for socket %d",
status, siResult, sock );
DSFree( status );
gRefTable.CleanRefsForSocket( sock ); dispatch_release( ds );
}
} );
}
};
char q_name[64];
static int32_t sReqNum = 0;
snprintf( q_name, sizeof(q_name), "request #%d socket #%d", __sync_add_and_fetch(&sReqNum, 1), sock );
dispatch_queue_t queue = dispatch_queue_create( q_name, NULL );
dispatch_source_attr_t attr = dispatch_source_attr_create();
dispatch_source_attr_set_finalizer( attr,
^(dispatch_source_t ds) {
DSFree( context->u.buffer );
free( context );
gRefTable.CleanRefsForSocket( dispatch_source_get_descriptor(ds) );
delete endPoint; } );
dispatch_source_t source = dispatch_source_read_create( sock, attr, queue, readBlock );
assert( source != NULL );
dispatch_release( attr );
dispatch_release( queue );
}
}
void ServerControl::TCPListenerEventCallback( int listenFD )
{
__TCPListenerEventCallback( listenFD ); }
SInt32 ServerControl::StartTCPListener ( UInt32 inPort )
{
SInt32 result = eDSNoErr;
char port[16];
addrinfo ai_hints;
addrinfo *my_addr = NULL;
int sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
bzero( &ai_hints, sizeof(ai_hints) );
ai_hints.ai_flags = AI_PASSIVE;
ai_hints.ai_family = PF_INET;
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_protocol = IPPROTO_TCP;
snprintf( port, sizeof(port), "%d", (int) inPort );
if ( getaddrinfo(NULL, port, &ai_hints, &my_addr) == 0 )
{
if ( bind(sock, my_addr->ai_addr, my_addr->ai_addr->sa_len) == 0 )
{
if ( listen(sock, 25) == 0 )
{
DbgLog( kLogInfo, "ServerControl::StartTCPListener - started TCP Listener on port %d", inPort );
dispatch_queue_t queue = dispatch_queue_create( "TCP listener", NULL );
dispatch_source_attr_t attr = dispatch_source_attr_create();
dispatch_source_attr_set_finalizer( attr,
^(dispatch_source_t ds) {
DbgLog( kLogNotice, "ServerControl::StartTCPListener - exiting listener on socket %d", sock );
close( sock );
} );
dispatch_source_t tempSource = dispatch_source_read_create( sock,
attr,
queue,
^(dispatch_source_t ds) {
if ( dispatch_source_get_error(ds, NULL) == 0 ) {
__TCPListenerEventCallback(sock);
}
} );
dispatch_release( attr );
dispatch_release( queue );
if ( __sync_bool_compare_and_swap(&ServerControl::fTCPListener, NULL, tempSource) == false ) {
dispatch_release( tempSource );
tempSource = NULL;
}
}
else
{
close( sock );
}
}
freeaddrinfo( my_addr );
}
return result;
}
SInt32 ServerControl::StopTCPListener ( void )
{
if ( fTCPListener == nil ) return eMemoryAllocError;
dispatch_source_t tempSource = fTCPListener;
if ( __sync_bool_compare_and_swap(&fTCPListener, tempSource, NULL) == true )
{
DbgLog( kLogNotice, "ServerControl::StopTCPListener stopping listener" );
dispatch_cancel( tempSource );
dispatch_release( tempSource );
}
return eDSNoErr;
}
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 != NULL && pPIInfo->fState & kActive) {
siResult = eDSNoErr;
siResult = pPlugin->ProcessRequest( (void*)&aHeader );
if (siResult != eDSNoErr && siResult != eNotHandledByThisNode && siResult != eNotYetImplemented)
{
ErrLog( kLogApplication, "SystemWillSleep Notification in %s plugin returned error %d", pPIInfo->fName, 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 != NULL && pPIInfo->fState & kActive) {
siResult = eDSNoErr;
siResult = pPlugin->ProcessRequest( (void*)&aHeader );
if (siResult != eDSNoErr && siResult != eNotHandledByThisNode && siResult != eNotYetImplemented)
{
ErrLog( kLogApplication, "WillPowerOn Notification in %s plugin returned error %d", pPIInfo->fName, siResult );
}
}
pPlugin = gPlugins->Next( &iterator );
}
}
return siResult;
}
void 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 != NULL && pPIInfo->fState & kActive) {
if ( ::strcmp(pPIInfo->fName,"Search") != 0)
{
siResult = eDSNoErr;
siResult = pPlugin->ProcessRequest( (void*)&aHeader );
if (siResult != eDSNoErr)
{
ErrLog( kLogApplication, "Network transition in %s plugin returned error %d", pPIInfo->fName, 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 );
}
}
}
void
ServerControl::ToggleAPILogging( bool fromSignal )
{
static dispatch_source_t loggingTimer = NULL;
static pthread_mutex_t localLock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock( &localLock );
if ( loggingTimer != NULL ) {
dispatch_cancel( loggingTimer );
dispatch_release( loggingTimer );
loggingTimer = NULL;
}
if ( __sync_bool_compare_and_swap(&gLogAPICalls, false, true) == true ) {
loggingTimer = dispatch_source_timer_create( DISPATCH_TIMER_ONESHOT,
300ULL * NSEC_PER_SEC,
1 * NSEC_PER_SEC,
NULL,
dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
^(dispatch_source_t ds) {
if ( dispatch_source_get_error(ds, NULL) == 0 &&
gIgnoreSunsetTime == false &&
__sync_bool_compare_and_swap(&gLogAPICalls, true, false) == true )
{
syslog( LOG_CRIT, "Logging of API Calls automatically turned OFF after reaching sunset duration of five minutes." );
}
} );
if ( fromSignal == true ) syslog( LOG_ALERT, "Logging of API Calls turned ON after receiving USR2 signal." );
}
else if ( __sync_bool_compare_and_swap(&gLogAPICalls, true, false) == true ) {
if ( fromSignal == true ) syslog( LOG_ALERT, "Logging of API Calls turned OFF after receiving USR2 signal." );
}
pthread_mutex_unlock( &localLock );
}
#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%d\t%d\t%.0f\t%0.f\t%0.f\t%s\t%d/%d\t%d/%d\t%d/%d\t%d/%d\t%d/%d\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 ServerControl::DoPeriodicTask( dispatch_source_t )
{
SInt32 siResult = eDSNoErr;
UInt32 iterator = 0;
CServerPlugin *pPlugin = nil;
CPlugInList::sTableData *pPIInfo = nil;
if ( gPlugins != nil )
{
pPlugin = gPlugins->Next( &iterator );
while (pPlugin != nil)
{
pPIInfo = gPlugins->GetPlugInInfo( iterator-1 );
if (pPIInfo != NULL && pPIInfo->fState & kActive) {
siResult = pPlugin->PeriodicTask();
if (siResult != eDSNoErr)
{
DbgLog( kLogApplication, "Periodic Task in %s plugin returned error %d", pPIInfo->fName, siResult );
}
}
pPlugin = gPlugins->Next( &iterator );
}
}
return;
}
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;
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;
}
}
}
void ServerControl::ResetDebugging( void )
{
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.");
DbgLog( kLogNotice ,"Debug Logging turned OFF after receiving USR1 signal." );
}
gDSFWCSBPDebugLogging = false;
}
else
{
gToggleDebugging = true;
if ( stat( kDSDebugConfigFilePath, &statbuf ) == 0 )
{
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.");
DbgLog( kLogNotice, "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);
DbgLog( kLogNotice,"Debug Logging priority %u set.", priorityValue );
syslog(LOG_ALERT,"Debug Logging priority %u set.", priorityValue);
}
}
else
{
CLog::SetLoggingPriority(keDebugLog, 5);
syslog(LOG_ALERT,"Debug Logging default priority 5 set.");
DbgLog( kLogNotice, "Debug Logging default priority 5 set." );
}
CLog::StartDebugLog();
if (!gDebugLogging)
{
syslog(LOG_ALERT,"Debug Logging turned ON after receiving USR1 signal.");
DbgLog( kLogNotice, "Debug Logging turned ON after receiving USR1 signal." );
gDebugLogging = true;
}
}
}
}
else if (gDebugLogging)
{
CLog::StopDebugLog();
gDebugLogging = false;
syslog(kLogNotice,"Debug Logging turned OFF after receiving USR1 signal.");
DbgLog( kLogNotice, "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 );
}
}