CNetInfoPlugin.cpp [plain text]
#include <stdlib.h>
#include <string.h>
#include <syslog.h> // for syslog() to log calls
#include <unistd.h>
#include <notify.h>
#include <netinfo/ni.h>
#include <netinfo/ni_util.h>
#include <netinfo/ni_prot.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "CNetInfoPlugin.h"
#include "DSUtils.h"
#include "CNiNodeList.h"
#include "CNiPlugIn.h"
#include "SharedConsts.h"
#include "CString.h"
#include "DSCThread.h"
#include "DSEventSemaphore.h"
#include "CSharedData.h"
#include "CLog.h"
#include "NiLib2.h"
#include "DirServicesPriv.h"
#include "netinfo_open.h"
extern bool gFirstNetworkUpAtBoot;
DSEventSemaphore *gKickNodeRequests = nil;
DSMutexSemaphore *gNetInfoMutex = nil;
bool gBlockNINetworkChange = false;
CNiNodeList *gNiNodeList = nil;
FourCharCode CNetInfoPlugin::fToken = 0;
static void DoNIPINetworkChange(CFRunLoopTimerRef timer, void *info);
void DoNIPINetworkChange(CFRunLoopTimerRef timer, void *info)
{
if ( info != nil )
{
((CNetInfoPlugin *)info)->ReDiscoverNetwork();
}
}
CFStringRef NetworkChangeNIPICopyStringCallback( const void *item );
CFStringRef NetworkChangeNIPICopyStringCallback( const void *item )
{
return CFSTR("NetworkChangeinNetInfoModule");
}
void SearchPolicyFoundNIParentNotification(SCDynamicStoreRef aSCDStore, CFArrayRef changedKeys, void *callback_argument)
{
if (gNiNodeList != nil)
{
gNiNodeList->AdjustForParent();
}
}
CNetInfoPlugin::CNetInfoPlugin ( FourCharCode inSig, const char *inName ) : CServerPlugin(inSig, inName)
{
fState = kUnknownState;
fRegisterNodePtr = nil;
fServerRunLoop = nil; fTransitionCheckTime = 0;
bFirstNetworkTransition = true;
CNetInfoPlugin::fToken = inSig;
if ( gKickNodeRequests == nil )
{
gKickNodeRequests = new DSEventSemaphore();
if ( gKickNodeRequests == nil ) throw((sInt32)eMemoryAllocError);
}
if ( gNetInfoMutex == nil )
{
gNetInfoMutex = new DSMutexSemaphore();
if ( gNetInfoMutex == nil ) throw((sInt32)eMemoryAllocError);
}
}
CNetInfoPlugin::~CNetInfoPlugin ( void )
{
}
sInt32 CNetInfoPlugin::Validate ( const char *inVersionStr, const uInt32 inSignature )
{
CNetInfoPlugin::fToken = fPlugInSignature = inSignature;
return( eDSNoErr );
}
sInt32 CNetInfoPlugin::Initialize ( void )
{
sInt32 siResult = eDSOpenNodeFailed;
tDataList *pDataList = nil;
tDataList *aDataList = nil;
gNetInfoMutex->Wait();
try
{
{
void *domain = nil;
int tokcheck = 0;
int tokplain = 0;
int status = eDSNoErr;
int binding = 0;
int retcheck = 0;
int numRetries = 0;
DSSemaphore aCheckNIMutex;
DBGLOG( kLogPlugin, "CNetInfoPlugin::Initialize - kick start netinfod initiated." );
status = netinfo_open( NULL, ".", &domain, 10 );
if (status == NI_OK)
{
DBGLOG( kLogPlugin, "CNetInfoPlugin::Initialize - netinfo_open on <.> succeeded." );
}
else
{
DBGLOG( kLogPlugin, "CNetInfoPlugin::Initialize - netinfo_open on <.> failed." );
}
DBGLOG( kLogPlugin, "CNetInfoPlugin::Initialize - netinfo notification check attempted." );
status = notify_register_check(NETINFO_BINDING_KEY, &tokcheck);
if (status == NOTIFY_STATUS_OK)
{
status = notify_check(tokcheck, &retcheck);
int checkCount = 0;
while ( (status == NOTIFY_STATUS_OK) && (retcheck == 0) && (checkCount < 3) )
{
checkCount++;
aCheckNIMutex.Wait( 1 * kMilliSecsPerSec );
status = notify_check(tokcheck, &retcheck);
}
if (retcheck == 1)
{
DBGLOG( kLogPlugin, "CNetInfoPlugin::Initialize - netinfo notification check succeeded." );
}
if (tokcheck != 0)
{
notify_cancel(tokcheck);
tokcheck = 0;
}
}
DBGLOG( kLogPlugin, "CNetInfoPlugin::Initialize - netinfo binding discovery attempted." );
status = notify_register_plain(NETINFO_BINDING_KEY, &tokplain);
if (status != NOTIFY_STATUS_OK)
{
DBGLOG1( kLogPlugin, "CNetInfoPlugin::Initialize - notify_register_plain failed for netinfo binding (status %d).", status );
syslog(LOG_ALERT,"CNetInfoPlugin::Initialize - notify_register_plain failed for netinfo binding (status %d)\n", status);
}
else
{
binding = BINDING_STATE_UNBOUND;
DBGLOG1( kLogPlugin, "CNetInfoPlugin::Initialize - notify_register_plain succeeded for netinfo binding (status %d).", status );
while (binding == BINDING_STATE_UNBOUND)
{
status = notify_get_state(tokplain, &binding);
if (status != NOTIFY_STATUS_OK)
{
DBGLOG1( kLogPlugin, "CNetInfoPlugin::Initialize - notify_get_state failed for netinfo binding (status %d).", status );
syslog(LOG_ALERT,"CNetInfoPlugin::Initialize - notify_get_state failed for netinfo binding (status %d)\n", status);
status = -1;
break;
}
if (binding != BINDING_STATE_UNBOUND)
{
status = binding;
DBGLOG1( kLogPlugin, "CNetInfoPlugin::Initialize - netinfo binding bound after %d queries on netinfod status.", numRetries );
break;
}
numRetries++;
if (numRetries > 10)
{
status = -1;
DBGLOG1( kLogPlugin, "CNetInfoPlugin::Initialize - notify_get_state failed over last minute (status %d).", status );
syslog(LOG_ALERT,"CNetInfoPlugin::Initialize - notify_get_state failed over last minute (status %d)\n", status);
break;
}
aCheckNIMutex.Wait( 2 * kMilliSecsPerSec );
if ((numRetries % 5) == 0)
{
notify_cancel(tokplain);
tokplain = 0;
DBGLOG2( kLogPlugin, "CNetInfoPlugin::Initialize - retry attempt %d on notify_register_plain for netinfo binding (status %d).", numRetries, status );
status = notify_register_plain(NETINFO_BINDING_KEY, &tokplain);
if (status != NOTIFY_STATUS_OK)
{
DBGLOG2( kLogPlugin, "CNetInfoPlugin::Initialize - retry %d on notify_register_plain failed for netinfo binding (status %d)\n", numRetries, status );
syslog(LOG_ALERT,"CNetInfoPlugin::Initialize - retry %d on notify_register_plain failed for netinfo binding (status %d)\n", numRetries, status);
status = -1;
break;
}
}
}
if (tokplain != 0)
{
notify_cancel(tokplain);
}
switch (status)
{
case BINDING_STATE_BOUND:
case BINDING_STATE_NETROOT:
siResult = eDSNoErr;
break;
default:
siResult = eDSOpenNodeFailed;
break;
}
}
}
if (siResult != eDSNoErr)
{
throw(siResult);
}
if ( gNiNodeList == nil )
{
gNiNodeList = new CNiNodeList();
}
if ( gNiNodeList == nil ) throw( (sInt32)eMemoryAllocError );
pDataList = ::dsBuildFromPathPriv( kstrDefaultLocalNodeName, "/" );
aDataList = ::dsBuildFromPathPriv( kstrDefaultLocalNodeName, "/" );
if ( pDataList != nil )
{
gNiNodeList->AddNode( ".", pDataList, true, 1 );
pDataList = nil;
}
if ( aDataList != nil )
{
DBGLOG1( kLogPlugin, "Registering node %s.", kstrDefaultLocalNodeName );
CServerPlugin::_RegisterNode( fPlugInSignature, aDataList, kLocalNodeType );
::dsDataListDeallocatePriv( aDataList );
free(aDataList);
aDataList = nil;
}
if (gFirstNetworkUpAtBoot) {
fRegisterMutex.Wait();
if (fRegisterNodePtr == nil)
{
fRegisterNodePtr = new CNodeRegister( fPlugInSignature, gNiNodeList, false, this );
if ( fRegisterNodePtr != nil )
{
fRegisterNodePtr->StartThread();
}
}
fRegisterMutex.Signal();
}
fState &= ~kFailedToInit;
fState += kActive;
fState += kInitialized;
CNetInfoPlugin::WakeUpRequests();
}
catch( sInt32 err )
{
fState += kFailedToInit;
siResult = err;
}
gNetInfoMutex->Signal();
return( siResult );
}
sInt32 CNetInfoPlugin::PeriodicTask ( void )
{
return( eDSNoErr );
}
void CNetInfoPlugin::WakeUpRequests ( void )
{
gKickNodeRequests->Signal();
}
void CNetInfoPlugin::WaitForInit ( void )
{
volatile uInt32 uiAttempts = 0;
while ( !(fState & kInitialized) &&
!(fState & kFailedToInit) )
{
try
{
if ( uiAttempts++ >= 240 )
{
return;
}
gKickNodeRequests->Wait( (uInt32)(.5 * kMilliSecsPerSec) );
}
catch( sInt32 err1 )
{
}
}
}
sInt32 CNetInfoPlugin::ProcessRequest ( void *inData )
{
sInt32 siResult = eDSNoErr;
CNiPlugIn cNiPlugin;
char *pathStr = nil;
try
{
if ( inData == nil )
{
return( ePlugInDataError );
}
if (((sHeader *)inData)->fType == kOpenDirNode)
{
if (((sOpenDirNode *)inData)->fInDirNodeName != nil)
{
pathStr = ::dsGetPathFromListPriv( ((sOpenDirNode *)inData)->fInDirNodeName, "/" );
if ( (pathStr != nil) && (strncmp(pathStr,"/NetInfo",8) != 0) )
{
throw( (sInt32)eDSOpenNodeFailed );
}
}
}
if ( ((sHeader *)inData)->fType == kServerRunLoop )
{
if ( (((sHeader *)inData)->fContextData) != nil )
{
fServerRunLoop = (CFRunLoopRef)(((sHeader *)inData)->fContextData);
CFStringRef service = CFStringCreateWithCString( NULL, "DirectoryService", kCFStringEncodingUTF8 );
if( service )
{
SCDynamicStoreRef store = SCDynamicStoreCreate( NULL, service, SearchPolicyFoundNIParentNotification, NULL );
CFMutableArrayRef notifyKeys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if( notifyKeys )
{
CFStringRef notify = CFStringCreateWithCString( NULL, kDSStdNotifySearchPolicyFoundNIParent, kCFStringEncodingUTF8 );
if( notify )
{
CFArrayAppendValue( notifyKeys, notify );
CFRelease( notify );
}
else
{
CFRelease( notifyKeys );
notifyKeys = NULL;
}
}
if (store != NULL && notifyKeys != NULL)
{
SCDynamicStoreSetNotificationKeys(store, notifyKeys, NULL);
CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
if (rls != NULL)
{
CFRunLoopAddSource(fServerRunLoop, rls, kCFRunLoopDefaultMode);
CFRelease(rls);
}
else
{
syslog(LOG_ALERT,"Unable to add source to RunLoop for SystemConfiguration registration for DS Search Policy Changes");
}
}
if( notifyKeys )
CFRelease(notifyKeys);
if( store )
CFRelease(store);
CFRelease(service);
}
return (siResult);
}
}
WaitForInit();
if (fState == kUnknownState)
{
throw( (sInt32)ePlugInCallTimedOut );
}
if ( (fState & kFailedToInit) || !(fState & kInitialized) )
{
throw( (sInt32)ePlugInFailedToInitialize );
}
if ( (fState & kInactive) || !(fState & kActive) )
{
throw( (sInt32)ePlugInNotActive );
}
if ( ((sHeader *)inData)->fType == kHandleNetworkTransition )
{
if (!gBlockNINetworkChange)
{
HandleMultipleNetworkTransitions();
}
}
else if ( ((sHeader *)inData)->fType == kHandleSystemWillSleep )
{
SystemGoingToSleep();
}
else if ( ((sHeader *)inData)->fType == kHandleSystemWillPowerOn )
{
SystemWillPowerOn();
}
else
{
siResult = cNiPlugin.HandleRequest( inData );
}
}
catch( sInt32 err )
{
siResult = err;
}
if (pathStr != nil)
{
free(pathStr);
pathStr = nil;
}
return( siResult );
}
void CNetInfoPlugin::HandleMultipleNetworkTransitions ( void )
{
void *ptInfo = nil;
CFRunLoopTimerRef timer;
if (bFirstNetworkTransition) {
fTransitionCheckTime = time(nil);
}
else
{
fTransitionCheckTime = time(nil) + 5;
}
if (fServerRunLoop != nil)
{
ptInfo = (void *)this;
CFRunLoopTimerContext c = {0, (void*)ptInfo, NULL, NULL, NetworkChangeNIPICopyStringCallback};
if (bFirstNetworkTransition)
{
timer = CFRunLoopTimerCreate( NULL,
CFAbsoluteTimeGetCurrent(),
0,
0,
0,
DoNIPINetworkChange,
(CFRunLoopTimerContext*)&c);
}
else
{
timer = CFRunLoopTimerCreate( NULL,
CFAbsoluteTimeGetCurrent() + 5,
0,
0,
0,
DoNIPINetworkChange,
(CFRunLoopTimerContext*)&c);
}
CFRunLoopAddTimer(fServerRunLoop, timer, kCFRunLoopDefaultMode);
if (timer) CFRelease(timer);
}
}
void CNetInfoPlugin::ReDiscoverNetwork(void)
{
sInt32 siResult = eDSNoErr;
if ( ( (time(nil) >= fTransitionCheckTime) || (bFirstNetworkTransition) ) && !gBlockNINetworkChange )
{
bFirstNetworkTransition = false;
if ( gNiNodeList != nil )
{
fRegisterMutex.Wait();
if (fRegisterNodePtr == nil)
{
fRegisterNodePtr = new CNodeRegister( fPlugInSignature, gNiNodeList, true, this );
if ( fRegisterNodePtr != nil )
{
try
{
fRegisterNodePtr->StartThread();
}
catch( sInt32 err )
{
siResult = err;
}
}
}
else
{
fRegisterNodePtr->Restart();
}
fRegisterMutex.Signal();
}
}
}
void CNetInfoPlugin::SystemGoingToSleep( void )
{
gBlockNINetworkChange = true;
}
void CNetInfoPlugin::SystemWillPowerOn( void )
{
gBlockNINetworkChange = false;
}
sInt32 CNetInfoPlugin::SafeOpen ( const char *inName, sInt32 inTimeoutSecs, char **outDomainName )
{
sInt32 siResult = eDSNoErr;
ni_status niStatus = NI_OK;
void *domain = nil;
char *domName = nil;
uInt32 localOrParent = 0;
time_t delayedNI = 0;
tDataList *pDataList = nil;
gNiNodeList->Lock();
try
{
if ( gNiNodeList->IsOpen( inName, outDomainName ) == false )
{
localOrParent = gNiNodeList->CheckForLocalOrParent( inName );
delayedNI = time(nil) + 2; if (localOrParent == 1)
{
niStatus = netinfo_open( nil, ".", &domain, inTimeoutSecs );
if ( niStatus == NI_OK )
{
DBGLOG( kLogPlugin, "CNiPlugIn::SafeOpen: <netinfo_open> opening <.>" );
}
if ( delayedNI < time(nil) )
{
syslog(LOG_ALERT,"SafeOpen::Call to netinfo_open was with fixed domain name: <.> and lasted %d seconds while timeout was %d", (uInt32)(2 + time(nil) - delayedNI), inTimeoutSecs);
}
}
else if (localOrParent == 2)
{
niStatus = netinfo_open( nil, "..", &domain, inTimeoutSecs ); if ( niStatus == NI_OK )
{
DBGLOG( kLogPlugin, "CNiPlugIn::SafeOpen: <netinfo_open> opening <..>" );
}
if ( delayedNI < time(nil) )
{
syslog(LOG_ALERT,"SafeOpen::Call to netinfo_open was with fixed domain name: <..> and lasted %d seconds while timeout was %d", (uInt32)(2 + time(nil) - delayedNI), inTimeoutSecs);
}
}
else
{
niStatus = netinfo_open( nil, (char *)inName, &domain, inTimeoutSecs );
if ( niStatus == NI_OK )
{
DBGLOG1( kLogPlugin, "CNiPlugIn::SafeOpen: <netinfo_open> opening <%s>", inName );
}
if ( delayedNI < time(nil) )
{
syslog(LOG_ALERT,"SafeOpen::Call to netinfo_open was with argument domain name: %s and lasted %d seconds while timeout was %d", inName, (uInt32)(2 + time(nil) - delayedNI), inTimeoutSecs);
}
}
if ( niStatus != NI_OK )
{
throw( (sInt32)eDSOpenNodeFailed );
}
if ( ::strcmp( inName, "/" ) == 0 )
{
domName = (char *)::calloc( ::strlen( kstrRootNodeName ) + 1, sizeof( char ) );
::strcpy( domName, kstrRootNodeName );
}
else if ( ::strcmp( inName, "." ) == 0 )
{
domName = strdup( kstrDefaultLocalNodeName );
}
else if ( ::strcmp( inName, ".." ) == 0 )
{
char * niDomName = nil;
delayedNI = time(nil) + 2; niDomName = netinfo_domainname( domain );
if ( delayedNI < time(nil) )
{
syslog(LOG_ALERT,"SafeOpen::Call to netinfo_domainname was with argument domain name: %s and lasted %d seconds.", inName, (uInt32)(2 + time(nil) - delayedNI));
}
if ( niDomName != nil )
{
if ( ::strcmp( niDomName, "/" ) == 0 )
{
domName = (char *)::calloc( ::strlen( kstrRootNodeName ) + 1, sizeof( char ) );
::strcpy( domName, kstrRootNodeName );
}
else
{
domName = (char *)::calloc( ::strlen( niDomName ) + ::strlen( kstrRootNodeName ) + 1, sizeof( char ) );
::strcpy( domName, kstrRootNodeName );
::strcat( domName, niDomName );
}
if (niDomName != nil)
{
free(niDomName);
niDomName = nil;
}
}
else
{
domName = (char *)::calloc( ::strlen( "Unknown Locations" ) + 1, sizeof( char ) );
::strcpy( domName, "Unknown Locations" );
}
}
else
{
domName = (char *)::calloc( ::strlen( kstrRootNodeName ) + ::strlen( inName ) + 1, sizeof( char ) );
::strcpy( domName, kstrRootNodeName );
::strcat( domName, inName );
}
gNiNodeList->SetDomainInfo( inName, domain, domName );
pDataList = ::dsBuildFromPathPriv( domName, "/" );
if ( pDataList != nil )
{
if (CServerPlugin::_RegisterNode( CNetInfoPlugin::fToken, pDataList, kDirNodeType ) == eDSNoErr)
{
DBGLOG1( kLogPlugin, "CNetInfoPlugin::SafeOpen: Registering node that was not already registered %s.", domName );
}
::dsDataListDeallocatePriv( pDataList );
free( pDataList );
pDataList = nil;
}
*outDomainName = strdup(domName);
if (domName != nil)
{
free(domName);
domName = nil;
}
}
}
catch( sInt32 err )
{
siResult = err;
}
catch ( ... )
{
siResult = eDSInvalidSession;
}
gNiNodeList->UnLock();
return( siResult );
}
void* CNetInfoPlugin::GetNIDomain(const char *inDomainName)
{
void *outDomain = NULL;
if (gNiNodeList != nil)
{
outDomain = gNiNodeList->RetrieveNode(inDomainName);
}
else
{
DBGLOG( kLogPlugin, "CNetInfoPlugin::GetNIDomain - NI node list is empty." );
}
return outDomain;
}
sInt32 CNetInfoPlugin::SafeClose ( const char *inName )
{
sInt32 siResult = eDSNoErr;
void *domain = nil;
gNetInfoMutex->Wait();
domain = gNiNodeList->DeleteNode( inName );
gNetInfoMutex->Signal();
return( siResult );
}
sInt32 CNetInfoPlugin::UnregisterNode ( const uInt32 inToken, tDataList *inNode )
{
sInt32 siResult = eDSNoErr;
siResult = CServerPlugin::_UnregisterNode( inToken, inNode );
return (siResult);
}
void CNetInfoPlugin::NodeRegisterComplete ( CNodeRegister *aRegisterThread )
{
fRegisterMutex.Wait();
if (fRegisterNodePtr == aRegisterThread)
{
fRegisterNodePtr = nil;
}
fRegisterMutex.Signal();
}
sInt32 CNetInfoPlugin::SetPluginState ( const uInt32 inState )
{
return( eDSNoErr );
}