#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <sys/stat.h>
#include <syslog.h> // for syslog() to log calls
#include <SystemConfiguration/SystemConfiguration.h>
#include <netinfo/ni.h>
#include <netinfo/ni_util.h>
#include <netinfo/ni_prot.h>
#include "CNodeRegister.h"
#include "CNiNodeList.h"
#include "CNetInfoPlugin.h"
#include "ServerModuleLib.h"
#include "CSharedData.h"
#include "PrivateTypes.h"
#include "CString.h"
#include "DSSemaphore.h"
#include "CLog.h"
#include "DSUtils.h"
#include "NiLib2.h"
#include "nibind_glue.h"
#include "netinfo_open.h"
static const sInt32 kCatchErr = -80128;
extern char *gNIHierarchyTagString;
CNodeRegister::CNodeRegister ( uInt32 inToken, CNiNodeList *inNodeList, bool inbReInit, CNetInfoPlugin *parentClass )
: DSCThread( kTSNodeRegisterThread )
{
fThreadSignature = kTSNodeRegisterThread;
fToken = inToken;
fCount = 0;
fTotal = 0;
fNiNodeList = inNodeList;
bReInit = inbReInit;
bRestart = true;
fParentClass= parentClass;
bNewNIHierarchy = false;
}
CNodeRegister::~CNodeRegister()
{
}
void CNodeRegister::StartThread ( void )
{
if ( this == nil ) throw((sInt32)eMemoryError);
this->Resume();
}
void CNodeRegister::StopThread ( void )
{
if ( this == nil ) throw((sInt32)eMemoryError);
SetThreadRunState( kThreadStop );
}
long CNodeRegister::ThreadMain ( void )
{
#ifdef DEBUG
fMutex.Wait( 60 * kMilliSecsPerSec );
#endif
DBGLOG( kLogPlugin, "Begin registering NetInfo nodes..." );
nibind_registration *reg;
ni_status status = NI_FAILED;
unsigned nreg;
void *nb;
struct in_addr addr;
ni_name dname = NULL;
void *domain = nil;
tDataList *pDataList = nil;
CString tmpName( 254 );
struct stat statResult;
bool bSetLocallyHosted = false;
time_t delayedNI = 0;
while (bRestart)
{
bRestart = false;
bSetLocallyHosted = false;
gNetInfoMutex->Wait();
try
{
if (stat( "/var/run/nibindd.pid", &statResult ) == eDSNoErr)
{
addr.s_addr = htonl( INADDR_LOOPBACK );
nb = nibind_new( &addr );
if ( nb != NULL)
{
status = nibind_listreg( nb, ®, &nreg );
if ( status == NI_OK )
{
for ( unsigned i = 0; i < nreg; i++ )
{
if ((reg[i].tag) != NULL)
{
struct in_addr addr;
addr.s_addr = htonl(INADDR_LOOPBACK);
status = (ni_status)netinfo_connect( &addr, reg[i].tag, &domain, 30 ); if ( status == NI_OK )
{
delayedNI = time(nil) + 2; dname = netinfo_domainname( domain );
if ( delayedNI < time(nil) )
{
syslog(LOG_ALERT,"CNodeRegister:ThreadMain::Call to netinfo_domainname was with argument domain name from tag: %s and lasted %d seconds.", reg[i].tag, (uInt32)(2 + time(nil) - delayedNI));
if (dname != nil)
{
syslog(LOG_ALERT,"CNodeRegister:ThreadMain::Call to netinfo_domainname returned domain name: %s.", dname);
}
}
if ( dname != NULL )
{
tmpName.Set( "/NetInfo/root" );
if ( ::strcmp( dname, "/" ) != 0 )
{
tmpName.Append( dname );
}
pDataList = ::dsBuildFromPathPriv( tmpName.GetData(), (char *)"/" );
if ( pDataList != nil )
{
CServerPlugin::_RegisterNode( fToken, pDataList, kLocalHostedType );
fTotal++;
::dsDataListDeallocatePriv( pDataList );
free( pDataList );
pDataList = nil;
}
ni_name_free( &dname );
dname = NULL;
}
} } }
}
nibind_free(nb); } }
else
{
bSetLocallyHosted = true;
}
}
catch( sInt32 err )
{
}
fNiNodeList->CleanUpUnknownConnections(fToken);
gNetInfoMutex->Signal();
if (!bRestart)
{
int myPolicy;
sched_param myStruct;
if (pthread_getschedparam( pthread_self(), &myPolicy, &myStruct) == 0)
{
myStruct.sched_priority = sched_get_priority_max(myPolicy);
if (pthread_setschedparam( pthread_self(), myPolicy, &myStruct) == 0)
{
DBGLOG( kLogPlugin, "Thread priority set to the maximum for registering NetInfo local hierarchy nodes." );
}
}
}
RegisterLocalNetInfoHierarchy(bSetLocallyHosted);
if( bNewNIHierarchy )
{
bNewNIHierarchy = false;
CFStringRef service = CFStringCreateWithCString( NULL, "DirectoryService", kCFStringEncodingUTF8 );
if ( service )
{
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, service, NULL, NULL);
if (store != NULL)
{ CFStringRef notify = CFStringCreateWithCString( NULL, "com.apple.DirectoryService.NotifyTypeStandard:NI_HIERARCHY_CHANGE", kCFStringEncodingUTF8 );
if( notify ) {
SCDynamicStoreNotifyValue( store, notify );
CFRelease( notify );
}
CFRelease( store );
}
CFRelease( service );
}
}
}
if (fParentClass != nil)
{
fParentClass->NodeRegisterComplete(this);
}
return( 0 );
}
sInt32 CNodeRegister::RegisterLocalNetInfoHierarchy ( bool inSetLocallyHosted )
{
sInt32 siResult = eDSNoErr;
void *domain = nil;
char *cpDomName = nil;
tDataList *pDataList = nil;
tDataList *qDataList = nil;
char *domName = nil;
uInt16 retryCount = 0;
DSSemaphore timedWait;
time_t delayedNI = 0;
int numLocalRetries = 0;
ni_status niStatus = NI_OK;
try
{
if ( fNiNodeList == nil ) throw( (sInt32)ePlugInError );
if (bReInit) {
fMutex.Wait( 3 * kMilliSecsPerSec );
}
gNetInfoMutex->Wait();
do
{
numLocalRetries = 0;
do
{
niStatus = ::netinfo_open( nil, ".", &domain, 10 );
if (niStatus != NI_OK)
{
if (numLocalRetries == 0)
{
syslog(LOG_ALERT,"RegisterLocalNetInfoHierarchy::netinfod likely not yet initialized - netinfo_open error is %d.", niStatus);
}
fMutex.Wait( 2 * kMilliSecsPerSec );
numLocalRetries++;
}
if (numLocalRetries >= 60) {
syslog(LOG_ALERT,"RegisterLocalNetInfoHierarchy::netinfod not reachable for the local domain after two minutes of trying.");
break;
}
} while ( (niStatus != NI_OK) && !(bReInit) );
if (niStatus == NI_OK)
{
delayedNI = time(nil) + 2; cpDomName = netinfo_domainname( domain );
if ( delayedNI < time(nil) )
{
syslog(LOG_ALERT,"CNodeRegister:RegisterLocalNetInfoHierarchy::Call to netinfo_domainname for local domain name lasted %d seconds.", (uInt32)(2 + time(nil) - delayedNI));
if (cpDomName != nil)
{
syslog(LOG_ALERT,"CNodeRegister:RegisterLocalNetInfoHierarchy::Call to netinfo_domainname returned domain name: %s.", cpDomName);
}
}
if ( cpDomName == nil )
{
DBGLOG1( kLogPlugin, "RegisterLocalNetInfoHierarchy: netinfo_domainname call # %u failed", retryCount+1 );
timedWait.Wait( 2 * kMilliSecsPerSec );
retryCount++;
}
}
} while ( (niStatus != NI_OK) && (cpDomName != nil) && (retryCount < 3) ); gNetInfoMutex->Signal();
uInt32 localOrParentCount = 1;
if ( cpDomName != nil )
{
if (gNIHierarchyTagString == nil)
{
gNIHierarchyTagString = strdup(cpDomName);
bNewNIHierarchy = true;
}
else
{
if (strcmp(gNIHierarchyTagString, cpDomName) != 0)
{
{
char *regNodes = strdup(gNIHierarchyTagString);
do
{
if (domName != nil)
{
free(domName);
domName = nil;
}
domName = (char *)calloc(1,strlen(kstrRootNodeName)+strlen(regNodes)+1);
strcpy(domName,kstrRootNodeName);
strcat(domName,regNodes);
qDataList = ::dsBuildFromPathPriv( domName, "/" );
if ( qDataList == nil ) throw( (sInt32)eMemoryAllocError );
DBGLOG1( kLogPlugin, "Unregistering node %s.", domName );
if ( CNetInfoPlugin::UnregisterNode( fToken, qDataList ) == eDSNoErr )
{
fTotal--;
}
::dsDataListDeallocatePriv( qDataList );
free(qDataList);
qDataList = nil;
if (strcmp(domName, kstrRootNodeName) != 0)
{
char *lastDel = strrchr(regNodes,'/');
*lastDel = '\0';
}
} while ( strcmp(domName, kstrRootNodeName) != 0 );
if (domName != nil)
{
free(domName);
domName = nil;
}
free(regNodes);
}
bNewNIHierarchy = true;
free(gNIHierarchyTagString);
gNIHierarchyTagString = strdup(cpDomName);
}
}
if ( bNewNIHierarchy && (::strcmp( cpDomName, "/" ) != 0 ) )
{
char *p = cpDomName + ::strlen( cpDomName );
do
{
if (domName != nil)
{
free(domName);
domName = nil;
}
domName = (char *)calloc(1,strlen(kstrRootNodeName)+strlen(cpDomName)+1);
strcpy(domName,kstrRootNodeName);
strcat(domName,cpDomName);
pDataList = ::dsBuildFromPathPriv( domName, "/" );
qDataList = ::dsBuildFromPathPriv( domName, "/" );
if ( pDataList == nil ) throw( (sInt32)eMemoryAllocError );
if ( qDataList == nil ) throw( (sInt32)eMemoryAllocError );
DBGLOG1( kLogPlugin, "Registering node %s.", domName );
fNiNodeList->AddNode( cpDomName, pDataList, true, localOrParentCount );
pDataList = nil;
if ( CServerPlugin::_RegisterNode( fToken, qDataList, kDirNodeType ) == eDSNoErr )
{
fTotal++;
}
::dsDataListDeallocatePriv( qDataList );
free(qDataList);
qDataList = nil;
if (inSetLocallyHosted)
{
tDataList *aDataList = nil;
inSetLocallyHosted = false;
aDataList = ::dsBuildFromPathPriv( kstrDefaultLocalNodeName, "/" );
if (aDataList != nil)
{
CServerPlugin::_RegisterNode( fToken, aDataList, kLocalHostedType );
::dsDataListDeallocatePriv( aDataList );
free( aDataList );
aDataList = nil;
}
}
while ( (p != cpDomName) && (*p != '/') )
{
p--;
}
if ( *p == '/' )
{
*p = '\0';
}
localOrParentCount++;
} while ( p != cpDomName );
if (domName != nil)
{
free(domName);
domName = nil;
}
}
free( cpDomName );
cpDomName = nil;
}
if (bNewNIHierarchy)
{
if (localOrParentCount == 1) {
pDataList = ::dsBuildFromPathPriv( kstrDefaultLocalNodeName, "/" );
qDataList = ::dsBuildFromPathPriv( kstrDefaultLocalNodeName, "/" );
if ( ( pDataList != nil ) && ( qDataList != nil ) )
{
DBGLOG1( kLogPlugin, "Registering node %s.", kstrDefaultLocalNodeName );
fNiNodeList->AddNode( ".", pDataList, true, localOrParentCount );
pDataList = nil;
if ( CServerPlugin::_RegisterNode( fToken, qDataList, kDirNodeType ) == eDSNoErr )
{
fTotal++;
}
if (inSetLocallyHosted)
{
CServerPlugin::_RegisterNode( fToken, qDataList, kLocalHostedType );
}
::dsDataListDeallocatePriv( qDataList );
free(qDataList);
qDataList = nil;
}
}
else
{
pDataList = ::dsBuildFromPathPriv( kstrRootNodeName, "/" );
qDataList = ::dsBuildFromPathPriv( kstrRootNodeName, "/" );
if ( ( pDataList != nil ) && ( qDataList != nil ) )
{
DBGLOG1( kLogPlugin, "Registering node %s.", kstrRootNodeName );
fNiNodeList->AddNode( "/", pDataList, true, localOrParentCount );
pDataList = nil;
if ( CServerPlugin::_RegisterNode( fToken, qDataList, kDirNodeType ) == eDSNoErr )
{
fTotal++;
}
::dsDataListDeallocatePriv( qDataList );
free(qDataList);
qDataList = nil;
}
}
}
}
catch( sInt32 err )
{
siResult = err;
}
return( siResult );
}
bool CNodeRegister::IsValidNameList ( ni_namelist *inNameList )
{
bool result = true;
if ( inNameList == nil )
{
result = false;
}
if ( result != false )
{
if ( inNameList->ni_namelist_len == 0 )
{
result = false;
}
}
return( result );
}
bool CNodeRegister::IsValidName ( char *inName )
{
bool result = true;
char *p = inName;
if ( ::strncmp( p, "./", 2 ) == 0 )
{
result = false;
}
if ( ::strncmp( p, "../", 3 ) == 0 )
{
result = false;
}
return( result );
}
bool CNodeRegister::IsLocalDomain ( char *inName )
{
bool result = false;
char *p = inName;
if ( ::strstr( p, "/local" ) != nil )
{
result = true;
}
return( result );
}
void CNodeRegister::Restart ( void )
{
bRestart = true;
bReInit = true;
}