#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 <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 "my_ni_pwdomain.h"
static const sInt32 kCatchErr = -80128;
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;
}
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++ )
{
tmpName.Set( "127.0.0.1" );
tmpName.Append( "/" );
tmpName.Append( reg[i].tag );
status = (ni_status)do_open( nil, tmpName.GetData(), &domain, true, 30, NULL, NULL );
if ( status == NI_OK )
{
delayedNI = time(nil) + 2; status = ::my_ni_pwdomain( domain, &dname );
if ( delayedNI < time(nil) )
{
syslog(LOG_INFO,"CNodeRegister:ThreadMain::Call to my_ni_pwdomain was with argument domain name: %s and lasted %d seconds.", tmpName.GetData(), (uInt32)(2 + time(nil) - delayedNI));
if (dname != nil)
{
syslog(LOG_INFO,"CNodeRegister:ThreadMain::Call to my_ni_pwdomain returned domain name: %s.", dname);
}
}
if ( status == NI_OK )
{
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;
}
} if (domain != nil)
{
ni_free(domain);
domain = nil;
}
} }
nibind_free(nb); } }
else
{
bSetLocallyHosted = true;
}
}
catch( sInt32 err )
{
}
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 ( (!bRestart) && (bReInit) ) {
fNiNodeList->CleanAllDirty(fToken);
}
if (!bRestart)
{
if (stat( "/System/Library/CoreServices/ServerVersion.plist", &statResult ) == eDSNoErr)
{
int myPolicy;
sched_param myStruct;
if (pthread_getschedparam( pthread_self(), &myPolicy, &myStruct) == 0)
{
myStruct.sched_priority = sched_get_priority_min(myPolicy);
if (pthread_setschedparam( pthread_self(), myPolicy, &myStruct) == 0)
{
DBGLOG( kLogPlugin, "Thread priority set to the minimum for registering NetInfo non-local hierarchy nodes." );
}
}
if ( !(bReInit) ) {
fMutex.Wait( 30 * kMilliSecsPerSec );
}
RegisterNodes( (char *)"/" );
}
DBGLOG( kLogPlugin, "Finished register NetInfo nodes." );
DBGLOG1( kLogPlugin, "NetInfo nodes registered = %l.", fTotal );
}
if (bRestart) {
fMutex.Wait( 2 * kMilliSecsPerSec );
fNiNodeList->SetAllDirty();
}
}
if (fParentClass != nil)
{
fParentClass->NodeRegisterComplete(this);
}
return( 0 );
}
sInt32 CNodeRegister::RegisterLocalNetInfoHierarchy ( bool inSetLocallyHosted )
{
sInt32 siResult = eDSNoErr;
sInt32 timeOutSecs = 3; void *domain = nil;
char *domainName = nil;
char *cpDomName = nil;
tDataList *pDataList = nil;
tDataList *qDataList = nil;
char *domName = nil;
ni_id niRootDir;
uInt16 retryCount = 0;
DSSemaphore timedWait;
time_t delayedNI = 0;
int numLocalRetries = 0;
try
{
if ( fNiNodeList == nil ) throw( (sInt32)ePlugInError );
if (bReInit) {
fMutex.Wait( 3 * kMilliSecsPerSec );
}
do
{
numLocalRetries = 0;
do
{
siResult = CNetInfoPlugin::SafeOpen( ".", timeOutSecs, &niRootDir, &domain, &domainName );
if (siResult == eDSOpenNodeFailed)
{
if (numLocalRetries == 0)
{
syslog(LOG_INFO,"RegisterLocalNetInfoHierarchy::netinfod likely not yet initialized - Error is %d.", siResult);
}
fMutex.Wait( 2 * kMilliSecsPerSec );
numLocalRetries++;
}
if (numLocalRetries >= 60) {
syslog(LOG_INFO,"RegisterLocalNetInfoHierarchy::netinfod not reachable for the local domain after two minutes of trying.");
break;
}
} while ( (siResult != eDSNoErr) && !(bReInit) );
if (domainName != nil) free(domainName);
if (siResult == eDSNoErr)
{
gNetInfoMutex->Wait();
delayedNI = time(nil) + 2; siResult = ::my_ni_pwdomain( domain, &cpDomName );
if ( delayedNI < time(nil) )
{
syslog(LOG_INFO,"CNodeRegister:RegisterLocalNetInfoHierarchy::Call to my_ni_pwdomain for local domain name lasted %d seconds.", (uInt32)(2 + time(nil) - delayedNI));
if (cpDomName != nil)
{
syslog(LOG_INFO,"CNodeRegister:RegisterLocalNetInfoHierarchy::Call to my_ni_pwdomain returned domain name: %s.", cpDomName);
}
}
gNetInfoMutex->Signal();
if ( (siResult != eDSNoErr) || (cpDomName == nil) )
{
DBGLOG1( kLogPlugin, "RegisterLocalNetInfoHierarchy: my_ni_pwdomain call # %u failed", retryCount+1 );
timedWait.Wait( 2 * kMilliSecsPerSec );
retryCount++;
}
siResult = CNetInfoPlugin::SafeClose( "." );
}
} while ( (siResult != eDSNoErr) && (retryCount < 3) );
if ( cpDomName != nil )
{
if (::strcmp( cpDomName, "/" ) != 0 )
{
char *p = cpDomName + ::strlen( cpDomName );
uInt32 localOrParentCount = 1;
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 );
if (localOrParentCount < 3)
{
fNiNodeList->AddNode( cpDomName, pDataList, true, localOrParentCount );
}
else
{
fNiNodeList->AddNode( cpDomName, pDataList, true, 0 );
}
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;
}
pDataList = ::dsBuildFromPathPriv( kstrRootNodeName, "/" );
qDataList = ::dsBuildFromPathPriv( kstrRootNodeName, "/" );
if ( ( pDataList != nil ) && ( qDataList != nil ) )
{
DBGLOG1( kLogPlugin, "Registering node %s.", kstrRootNodeName );
fNiNodeList->AddNode( "/", pDataList, true );
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;
}
}
}
}
catch( sInt32 err )
{
siResult = err;
}
return( siResult );
}
sInt32 CNodeRegister::RegisterNodes ( char *inDomainName )
{
sInt32 siResult = 0;
sInt32 entryListCnt = 0;
sInt32 entryListLen = 0;
sInt32 nameListCnt = 0;
sInt32 nameListlen = 0;
sInt32 timeOutSecs = 3;
ni_status niStatus = NI_OK;
bool isLocal = false;
bool bIsPresent = false;
void *domain = nil;
tDataList *pDataList = nil;
char *name = nil;
char *unused = nil;
char *p = nil;
ni_namelist *nameList = nil;
ni_id machines;
ni_id niDirID;
ni_entrylist niEntryList;
char *domName = nil;
char *tempName = nil;
bool bNodeServes = false;
ni_proplist niPropList;
uInt32 pv = 0;
ni_index niIndex = 0;
if (bRestart)
{
return(eDSOperationFailed);
}
try
{
if ( fNiNodeList == nil ) throw( (sInt32)ePlugInError );
DBGLOG1( kLogPlugin, "Registering nodes in domain = %s", inDomainName );
fCount = 0;
domain = nil;
NI_INIT( &niDirID );
siResult = CNetInfoPlugin::SafeOpen( inDomainName, timeOutSecs, &niDirID, &domain, &unused );
if (unused != nil) free(unused);
if ( siResult != eDSNoErr ) throw( siResult );
fMutex.Wait( 1 * kMilliSecsPerSec );
gNetInfoMutex->Wait();
try
{
NI_INIT( &machines );
siResult = ::ni_pathsearch( domain, &machines, "/machines" );
if ( siResult != eDSNoErr )
{
domain = nil;
throw( siResult );
}
NI_INIT( &niEntryList );
niStatus = ::ni_list( domain, &machines, "serves", &niEntryList );
if ( niStatus != NI_OK )
{
domain = nil;
throw( (sInt32)niStatus );
}
}
catch( sInt32 err )
{
niStatus = (ni_status)err;
}
catch ( ... )
{
niStatus = (ni_status)kCatchErr;
}
gNetInfoMutex->Signal();
if ( niStatus != 0 ) throw( (sInt32)niStatus );
entryListLen = niEntryList.ni_entrylist_len;
for ( entryListCnt = 0; entryListCnt < entryListLen; entryListCnt++ )
{
if (bRestart) {
break;
}
NI_INIT( &niPropList );
bNodeServes = false;
machines.nii_object = niEntryList.ni_entrylist_val[ entryListCnt ].id;
gNetInfoMutex->Wait();
siResult = ::ni_read( domain, &machines, &niPropList );
if ( siResult == NI_OK )
{
niIndex = ::ni_proplist_match( niPropList, "serves", NULL );
if ( niIndex != NI_INDEX_NULL )
{
for ( pv = 0; pv < niPropList.nipl_val[ niIndex ].nip_val.ni_namelist_len; pv++ )
{
if (nil == strstr(niPropList.nipl_val[ niIndex ].nip_val.ni_namelist_val[ pv ],"/local"))
{
bNodeServes = true;
break;
}
}
}
::ni_proplist_free( &niPropList );
}
gNetInfoMutex->Signal();
if (!bNodeServes)
{
continue;
}
nameList = niEntryList.ni_entrylist_val[ entryListCnt ].names;
if ( IsValidNameList( nameList ) == false )
{
continue;
}
nameListlen = nameList->ni_namelist_len;
for ( nameListCnt = 0; nameListCnt < nameListlen; nameListCnt++ )
{
if (bRestart) {
break;
}
name = nameList->ni_namelist_val[ nameListCnt ];
if ( this->IsValidName( name ) == false )
{
continue;
}
isLocal = this->IsLocalDomain( name );
p = ::strchr( name, '/' );
if ( p == nil )
{
continue;
}
*p = '\0';
if (domName != nil)
{
free(domName);
domName = nil;
}
domName = (char *)calloc(1,strlen(inDomainName)+1+strlen(name)+1);
strcpy(domName,inDomainName);
if (strlen(domName) > 1)
{
strcat(domName,"/");
}
strcat(domName,name);
pDataList = nil;
bIsPresent = fNiNodeList->IsPresent( domName );
if ( bIsPresent == false )
{
if (tempName != nil)
{
free(tempName);
tempName = nil;
}
tempName = (char *)calloc(1,strlen(kstrRootNodeName)+strlen(domName)+1);
strcpy(tempName,kstrRootNodeName);
strcat(tempName,domName);
pDataList = dsBuildFromPathPriv( tempName, (char *)"/" );
if ( pDataList != nil )
{
if (isLocal == false)
{
if ( CServerPlugin::_RegisterNode( fToken, pDataList, kDirNodeType ) == eDSNoErr )
{
fNiNodeList->AddNode( domName, pDataList, true ); pDataList = nil;
fCount++;
fTotal++;
}
else
{
::dsDataListDeallocatePriv( pDataList );
free( pDataList );
pDataList = nil;
}
DBGLOG2( kLogPlugin, "Nodes registered for domain %s = %l.", domName, fCount );
siResult = this->RegisterNodes( domName );
if ( siResult != 0 )
{
break;
}
}
else
{
::dsDataListDeallocatePriv( pDataList );
free( pDataList );
pDataList = nil;
}
}
}
}
if (tempName != nil)
{
free(tempName);
tempName = nil;
}
if (domName != nil)
{
free(domName);
domName = nil;
}
}
gNetInfoMutex->Wait();
if (inDomainName != nil)
{
siResult = CNetInfoPlugin::SafeClose( inDomainName );
}
::ni_entrylist_free(&niEntryList);
gNetInfoMutex->Signal();
fMutex.Wait( 5 * kMilliSecsPerSec );
}
catch( sInt32 err )
{
siResult = err;
}
catch ( ... )
{
siResult = kCatchErr;
}
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;
}