Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

964 lines
34 KiB

//--------------------------------------------------------------------
// TimeMonitor - implementation
// Copyright (C) Microsoft Corporation, 1999
//
// Created by: Louis Thomas (louisth), 10-4-99
//
// Monitor time servers
//
#include "pch.h" // precompiled headers
//####################################################################
struct ComputerRecord {
WCHAR * wszName;
bool bIsPdc;
// DNS
in_addr * rgiaLocalIpAddrs;
in_addr * rgiaRemoteIpAddrs;
unsigned int nIpAddrs;
HRESULT hrIPs;
// ICMP
HRESULT hrIcmp;
DWORD dwIcmpDelay;
// NTP
NtTimeOffset toOffset;
HRESULT hrNtp;
NtpRefId refid;
unsigned int nStratum;
ComputerRecord * pcrReferer;
WCHAR * wszReferer;
unsigned int nTimeout;
// SERVICE
bool bDoingService;
HRESULT hrService;
DWORD dwStartType;
DWORD dwCurrentState;
};
struct NameHolder {
WCHAR * wszName;
bool bIsDomain;
NameHolder * pnhNext;
};
enum AlertType {
e_MaxSpreadAlert,
e_MinServersAlert,
};
struct AlertRecord {
AlertType eType;
unsigned int nParam1;
DWORD dwError;
AlertRecord * parNext;
};
struct ThreadSharedContext {
ComputerRecord ** rgpcrList;
unsigned int nComputers;
volatile unsigned int nNextComputer;
volatile unsigned int nFinishedComputers;
};
struct ThreadContext {
HANDLE hThread;
volatile unsigned int nCurRecord;
ThreadSharedContext * ptsc;
};
MODULEPRIVATE const DWORD gc_dwTimeout=1000;
//####################################################################
//--------------------------------------------------------------------
MODULEPRIVATE inline void ClearLine(void) {
wprintf(L" \r");
}
//--------------------------------------------------------------------
MODULEPRIVATE void FreeComputerRecord(ComputerRecord * pcr) {
if (NULL==pcr) {
return;
}
if (NULL!=pcr->wszName) {
LocalFree(pcr->wszName);
}
if (NULL!=pcr->rgiaLocalIpAddrs) {
LocalFree(pcr->rgiaLocalIpAddrs);
}
if (NULL!=pcr->rgiaRemoteIpAddrs) {
LocalFree(pcr->rgiaRemoteIpAddrs);
}
if (NULL!=pcr->wszReferer) {
LocalFree(pcr->wszReferer);
}
LocalFree(pcr);
};
//--------------------------------------------------------------------
MODULEPRIVATE HRESULT AnalyzeComputer(ComputerRecord * pcr) {
HRESULT hr;
NtpPacket npPacket;
NtTimeEpoch teDestinationTimestamp;
DebugWPrintf1(L"%s:\n", pcr->wszName);
// look up Ip addrs if necessary
if (0==pcr->nIpAddrs) {
hr=MyGetIpAddrs(pcr->wszName, &pcr->rgiaLocalIpAddrs, &pcr->rgiaRemoteIpAddrs, &pcr->nIpAddrs, NULL);
pcr->hrIPs=hr;
_JumpIfError(hr, error, "MyGetIpAddrs");
}
// do an ICMP ping
DebugWPrintf0(L" ICMP: ");
hr=MyIcmpPing(&(pcr->rgiaRemoteIpAddrs[0]), gc_dwTimeout, &pcr->dwIcmpDelay);
pcr->hrIcmp=hr;
// Some machines do not have ping servers, but still serve time. We can still try an NTP ping
// if this fails.
_IgnoreIfError(hr, "MyIcmpPing");
// do an NTP ping
DebugWPrintf0(L" NTP: ");
hr=MyNtpPing(&(pcr->rgiaRemoteIpAddrs[0]), pcr->nTimeout, &npPacket, &teDestinationTimestamp);
pcr->hrNtp=hr;
_JumpIfError(hr, error, "MyNtpPing");
{
// calculate the offset
NtTimeEpoch teOriginateTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teOriginateTimestamp);
NtTimeEpoch teReceiveTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teReceiveTimestamp);
NtTimeEpoch teTransmitTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teTransmitTimestamp);
NtTimeOffset toLocalClockOffset=
(teReceiveTimestamp-teOriginateTimestamp)
+ (teTransmitTimestamp-teDestinationTimestamp);
toLocalClockOffset/=2;
pcr->toOffset=toLocalClockOffset;
// new referer?
if (pcr->refid.value!=npPacket.refid.value || pcr->nStratum!=npPacket.nStratum) {
// clean out the old values
if (NULL!=pcr->wszReferer) {
LocalFree(pcr->wszReferer);
pcr->wszReferer=NULL;
}
pcr->pcrReferer=NULL;
pcr->refid.value=npPacket.refid.value;
pcr->nStratum=npPacket.nStratum;
}
}
hr=S_OK;
error:
return hr;
}
//--------------------------------------------------------------------
MODULEPRIVATE DWORD WINAPI AnalysisThread(void * pvContext) {
ThreadContext * ptc=(ThreadContext *)pvContext;
while (true) {
ptc->nCurRecord=InterlockedIncrement((LONG *)&(ptc->ptsc->nNextComputer))-1;
if (ptc->nCurRecord<ptc->ptsc->nComputers) {
AnalyzeComputer(ptc->ptsc->rgpcrList[ptc->nCurRecord]);
ptc->ptsc->nFinishedComputers++; // atomic
} else {
break;
}
}
return S_OK;
}
//--------------------------------------------------------------------
MODULEPRIVATE HRESULT ResolveReferer(ComputerRecord ** rgpcrList, unsigned int nComputers, unsigned int nCur) {
HRESULT hr;
unsigned int nIndex;
HOSTENT * phe;
int nChars;
// see if it is nobody
if (0==rgpcrList[nCur]->refid.value || 1>=rgpcrList[nCur]->nStratum) {
// no referer
} else if (NULL==rgpcrList[nCur]->wszReferer && NULL==rgpcrList[nCur]->pcrReferer) {
// referer not yet determined
// first, see if it is someone we are checking
for (nIndex=0; nIndex<nComputers; nIndex++) {
if (rgpcrList[nIndex]->nIpAddrs>0 &&
rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_addr==rgpcrList[nCur]->refid.value) {
rgpcrList[nCur]->pcrReferer=rgpcrList[nIndex];
}
}
// if we still don't know, do a reverse DNS lookup
if (NULL==rgpcrList[nCur]->pcrReferer) {
phe=gethostbyaddr((char *)&(rgpcrList[nCur]->refid.value), 4, AF_INET);
if (NULL==phe) {
// not worth aborting over.
_IgnoreLastError("gethostbyaddr");
} else {
// save the result as a unicode string
nChars=MultiByteToWideChar(CP_ACP, 0, phe->h_name, -1, NULL, 0);
if (0==nChars) {
_JumpLastError(hr, error, "MultiByteToWideChar(1)");
}
rgpcrList[nCur]->wszReferer=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*nChars);
_JumpIfOutOfMemory(hr, error, rgpcrList[nCur]->wszReferer);
nChars=MultiByteToWideChar(CP_ACP, 0, phe->h_name, -1, rgpcrList[nCur]->wszReferer, nChars);
if (0==nChars) {
_JumpLastError(hr, error, "MultiByteToWideChar(2)");
}
} // <- end if lookup successful
} // <- end if need to to reverse DNS lookup
} // <- end if need to determine referer
hr=S_OK;
error:
return hr;
}
//--------------------------------------------------------------------
MODULEPRIVATE HRESULT ParseCmdLineForComputerNames(CmdArgs * pca, NameHolder ** ppnhList) {
HRESULT hr;
NameHolder * pnhTemp;
WCHAR * wszComputerList;
WCHAR * wszDomainName;
unsigned int nComputerIndex;
unsigned int nDomainIndex;
// must be cleaned up
NameHolder * pnhList=NULL;
NameHolder ** ppnhTail=&pnhList;
// check for list of computers
while (FindArg(pca, L"computers", &wszComputerList, &nComputerIndex)) {
// allocate
pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder));
_JumpIfOutOfMemory(hr, error, pnhTemp);
// link to tail of list
*ppnhTail=pnhTemp;
ppnhTail=&(pnhTemp->pnhNext);
// remember the arg we found
pnhTemp->bIsDomain=false;
pnhTemp->wszName=wszComputerList;
// mark arg as used
MarkArgUsed(pca, nComputerIndex);
}
// check for domain
while (FindArg(pca, L"domain", &wszDomainName, &nDomainIndex)) {
// allocate
pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder));
_JumpIfOutOfMemory(hr, error, pnhTemp);
// link to tail of list
*ppnhTail=pnhTemp;
ppnhTail=&(pnhTemp->pnhNext);
// remember the arg we found
pnhTemp->bIsDomain=true;
pnhTemp->wszName=wszDomainName;
// mark arg as used
MarkArgUsed(pca, nDomainIndex);
}
// put in the default domain if nothing specified
if (NULL==pnhList) {
// allocate
pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder));
_JumpIfOutOfMemory(hr, error, pnhTemp);
// link to tail of list
*ppnhTail=pnhTemp;
ppnhTail=&(pnhTemp->pnhNext);
// add default
pnhTemp->bIsDomain=true;
pnhTemp->wszName=L"";
}
// successful
hr=S_OK;
*ppnhList=pnhList;
pnhList=NULL;
error:
while (NULL!=pnhList) {
pnhTemp=pnhList;
pnhList=pnhTemp->pnhNext;
LocalFree(pnhTemp);
}
return hr;
}
//--------------------------------------------------------------------
MODULEPRIVATE HRESULT BuildComputerList(NameHolder * pnhList, ComputerRecord *** prgpcrList, unsigned int * pnComputers, unsigned int nTimeout)
{
HRESULT hr;
unsigned int nComputers=0;
unsigned int nDcs;
unsigned int nPrevComputers;
unsigned int nIndex;
// must be cleaned up
ComputerRecord ** rgpcrList=NULL;
DcInfo * rgdiDcList=NULL;
ComputerRecord ** rgpcrPrev=NULL;
// for each set of names in our list
while (NULL!=pnhList) {
if (pnhList->bIsDomain) {
// get the dc list
if (L'\0'==pnhList->wszName[0]) {
LocalizedWPrintf2(IDS_W32TM_STATUS_GETTING_DC_LIST_FOR_DEFAULT_DOMAIN, L"\r");
} else {
LocalizedWPrintf2(IDS_W32TM_STATUS_GETTING_DC_LIST_FOR, L" %s...\r", pnhList->wszName);
}
DebugWPrintf0(L"\n");
hr=GetDcList(pnhList->wszName, false, &rgdiDcList, &nDcs);
ClearLine();
if (FAILED(hr)) {
LocalizedWPrintf2(IDS_W32TM_ERRORTIMEMONITOR_GETDCLIST_FAILED, L" 0x%08X.\n", hr);
}
_JumpIfError(hr, error, "GetDcList");
// allow for previous list
nPrevComputers=nComputers;
rgpcrPrev=rgpcrList;
rgpcrList=NULL;
nComputers+=nDcs;
// allocate memory
rgpcrList=(ComputerRecord **)LocalAlloc(LPTR, nComputers*sizeof(ComputerRecord *));
_JumpIfOutOfMemory(hr, error, rgpcrList);
for (nIndex=nPrevComputers; nIndex<nComputers; nIndex++) {
rgpcrList[nIndex]=(ComputerRecord *)LocalAlloc(LPTR, sizeof(ComputerRecord));
_JumpIfOutOfMemory(hr, error, rgpcrList[nIndex]);
}
// move the computers from the previous list
if (0!=nPrevComputers) {
for (nIndex=0; nIndex<nPrevComputers; nIndex++) {
rgpcrList[nIndex]=rgpcrPrev[nIndex];
}
LocalFree(rgpcrPrev);
rgpcrPrev=NULL;
}
// steal the data from the DC list
for (nIndex=0; nIndex<nDcs; nIndex++) {
rgpcrList[nIndex+nPrevComputers]->wszName=rgdiDcList[nIndex].wszDnsName;
rgpcrList[nIndex+nPrevComputers]->nIpAddrs=rgdiDcList[nIndex].nIpAddresses;
rgpcrList[nIndex+nPrevComputers]->rgiaLocalIpAddrs=rgdiDcList[nIndex].rgiaLocalIpAddresses;
rgpcrList[nIndex+nPrevComputers]->rgiaRemoteIpAddrs=rgdiDcList[nIndex].rgiaRemoteIpAddresses;
rgpcrList[nIndex+nPrevComputers]->bIsPdc=rgdiDcList[nIndex].bIsPdc;
rgdiDcList[nIndex].wszDnsName=NULL;
rgdiDcList[nIndex].rgiaLocalIpAddresses=NULL;
rgdiDcList[nIndex].rgiaRemoteIpAddresses=NULL;
}
} else {
// allow for previous list
nPrevComputers=nComputers;
rgpcrPrev=rgpcrList;
rgpcrList=NULL;
// count the number of computers in the computer list
WCHAR * wszTravel=pnhList->wszName;
nComputers=1;
while (NULL!=(wszTravel=wcschr(wszTravel, L','))) {
nComputers++;
wszTravel++;
}
nComputers+=nPrevComputers;
// allocate memory
rgpcrList=(ComputerRecord **)LocalAlloc(LPTR, nComputers*sizeof(ComputerRecord *));
_JumpIfOutOfMemory(hr, error, rgpcrList);
for (nIndex=nPrevComputers; nIndex<nComputers; nIndex++) {
rgpcrList[nIndex]=(ComputerRecord *)LocalAlloc(LPTR, sizeof(ComputerRecord));
_JumpIfOutOfMemory(hr, error, rgpcrList[nIndex]);
}
// move the computers from the previous list
if (0!=nPrevComputers) {
for (nIndex=0; nIndex<nPrevComputers; nIndex++) {
rgpcrList[nIndex]=rgpcrPrev[nIndex];
}
LocalFree(rgpcrPrev);
rgpcrPrev=NULL;
}
// fill in each record
wszTravel=pnhList->wszName;
for (nIndex=nPrevComputers; nIndex<nComputers; nIndex++) {
WCHAR * wszComma=wcschr(wszTravel, L',');
if (NULL!=wszComma) {
wszComma[0]=L'\0';
}
if (L'*'==wszTravel[0]) {
rgpcrList[nIndex]->bIsPdc=true;
wszTravel++;
}
rgpcrList[nIndex]->wszName=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(wszTravel)+1));
_JumpIfOutOfMemory(hr, error, rgpcrList[nIndex]->wszName);
wcscpy(rgpcrList[nIndex]->wszName, wszTravel);
wszTravel=wszComma+1;
}
}
pnhList=pnhList->pnhNext;
}
// Fill in shared computer data:
for (nIndex=0; nIndex<nComputers; nIndex++) {
rgpcrList[nIndex]->nTimeout = nTimeout;
}
// success
hr=S_OK;
*pnComputers=nComputers;
*prgpcrList=rgpcrList;
rgpcrList=NULL;
error:
if (NULL!=rgpcrPrev) {
for (nIndex=0; nIndex<nPrevComputers; nIndex++) {
FreeComputerRecord(rgpcrPrev[nIndex]);
}
LocalFree(rgpcrPrev);
}
if (NULL!=rgdiDcList) {
for (nIndex=0; nIndex<nDcs; nIndex++) {
FreeDcInfo(&(rgdiDcList[nIndex]));
}
LocalFree(rgdiDcList);
}
if (NULL!=rgpcrList) {
for (nIndex=0; nIndex<nComputers; nIndex++) {
FreeComputerRecord(rgpcrList[nIndex]);
}
LocalFree(rgpcrList);
}
return hr;
}
//--------------------------------------------------------------------
MODULEPRIVATE void FreeAlertRecords(AlertRecord * parList) {
while (NULL!=parList) {
AlertRecord * parTemp=parList;
parList=parList->parNext;
LocalFree(parTemp);
}
}
//--------------------------------------------------------------------
MODULEPRIVATE HRESULT ParseCmdLineForAlerts(CmdArgs * pca, AlertRecord ** pparList) {
HRESULT hr;
WCHAR * rgwszAlertParams[10];
WCHAR * wszAlert;
unsigned int nAlertIndex;
AlertRecord * parTemp;
unsigned int nIndex;
// must be cleaned up
AlertRecord * parList=NULL;
AlertRecord ** pparTail=&parList;
// check for list of computers
while (FindArg(pca, L"alert", &wszAlert, &nAlertIndex)) {
// parse out comma separates params
nIndex=0;
rgwszAlertParams[0]=wszAlert;
while (nIndex<10 && NULL!=(rgwszAlertParams[nIndex]=wcschr(rgwszAlertParams[nIndex], L','))) {
rgwszAlertParams[nIndex][0]=L'\0';
rgwszAlertParams[nIndex]++;
rgwszAlertParams[nIndex+1]=rgwszAlertParams[nIndex];
nIndex++;
}
// is it "maxspread"
if (0==_wcsicmp(wszAlert, L"maxspread")) {
// quick validy check on params
if (NULL==rgwszAlertParams[0] || NULL==rgwszAlertParams[1] || NULL!=rgwszAlertParams[2]) {
LocalizedWPrintf2(IDS_W32TM_ERRORPARAMETER_INCORRECT_NUMBER_FOR_ALERT, L" '%s'.\n", wszAlert);
hr=E_INVALIDARG;
_JumpError(hr, error, "command line parsing");
}
// allocate
parTemp=(AlertRecord *)LocalAlloc(LPTR, sizeof(AlertRecord));
_JumpIfOutOfMemory(hr, error, parTemp);
// link to tail of list
*pparTail=parTemp;
pparTail=&(parTemp->parNext);
// remember the args we found
parTemp->eType=e_MaxSpreadAlert;
parTemp->nParam1=wcstoul(rgwszAlertParams[0],NULL,0);
parTemp->dwError=wcstoul(rgwszAlertParams[1],NULL,0);
// is it "minservers
} else if (0==_wcsicmp(wszAlert, L"minservers")) {
// quick validy check on params
if (NULL==rgwszAlertParams[0] || NULL==rgwszAlertParams[1] || NULL!=rgwszAlertParams[2]) {
LocalizedWPrintf2(IDS_W32TM_ERRORPARAMETER_INCORRECT_NUMBER_FOR_ALERT, L" '%s'.\n", wszAlert);
hr=E_INVALIDARG;
_JumpError(hr, error, "command line parsing");
}
// allocate
parTemp=(AlertRecord *)LocalAlloc(LPTR, sizeof(AlertRecord));
_JumpIfOutOfMemory(hr, error, parTemp);
// link to tail of list
*pparTail=parTemp;
pparTail=&(parTemp->parNext);
// remember the args we found
parTemp->eType=e_MinServersAlert;
parTemp->nParam1=wcstoul(rgwszAlertParams[0],NULL,0);
parTemp->dwError=wcstoul(rgwszAlertParams[1],NULL,0);
} else {
wprintf(L"Alert '%s' unknown.\n", wszAlert);
hr=E_INVALIDARG;
_JumpError(hr, error, "command line parsing");
}
if (!(parTemp->dwError&0x80000000)) { // check sign bit
wprintf(L"Retval not negative for alert '%s'.\n", wszAlert);
hr=E_INVALIDARG;
_JumpError(hr, error, "command line parsing");
}
// mark arg as used
MarkArgUsed(pca, nAlertIndex);
} // <- end FindArg loop
// success
hr=S_OK;
*pparList=parList;
parList=NULL;
error:
if (NULL!=parList) {
FreeAlertRecords(parList);
}
return hr;
}
//--------------------------------------------------------------------
MODULEPRIVATE HRESULT CheckForAlerts(ComputerRecord ** rgpcrList, unsigned int nComputers, AlertRecord * parList) {
HRESULT hr;
unsigned int nIndex;
for (; NULL!=parList; parList=parList->parNext) {
if (e_MaxSpreadAlert==parList->eType) {
// see how big the spread is
NtTimeOffset toMax;
NtTimeOffset toMin;
bool bFirst=true;
for (nIndex=0; nIndex<nComputers; nIndex++) {
if (S_OK==rgpcrList[nIndex]->hrIPs &&
S_OK==rgpcrList[nIndex]->hrIcmp &&
S_OK==rgpcrList[nIndex]->hrNtp) {
if (bFirst) {
toMin=toMax=rgpcrList[nIndex]->toOffset;
bFirst=false;
} else {
if (toMin>rgpcrList[nIndex]->toOffset) {
toMin=rgpcrList[nIndex]->toOffset;
}
if (toMax<rgpcrList[nIndex]->toOffset) {
toMax=rgpcrList[nIndex]->toOffset;
}
}
}
}
if (bFirst) {
// no valid data!
// ignore this alert
continue;
}
unsigned __int64 qwSpread=(unsigned __int64)(toMax.qw-toMin.qw);
if (qwSpread>((unsigned __int64)(parList->nParam1))*10000000) {
DWORD dwFraction=(DWORD)(qwSpread%10000000);
qwSpread/=10000000;
wprintf(L"** ALERT: Current spread %I64u.%07us is greater than maximum\n"
L" spread %us. Returning 0x%08X\n",
qwSpread, dwFraction, parList->nParam1, parList->dwError);
hr=parList->dwError;
_JumpError(hr, error, "maxspread alert evaluation");
}
} else if (e_MinServersAlert==parList->eType) {
// see how many usable servers there are
unsigned int nServers=0;
for (nIndex=0; nIndex<nComputers; nIndex++) {
if (S_OK==rgpcrList[nIndex]->hrIPs &&
S_OK==rgpcrList[nIndex]->hrIcmp &&
S_OK==rgpcrList[nIndex]->hrNtp) {
nServers++;
}
}
if (nServers<parList->nParam1) {
wprintf(L"** ALERT: Current usable servers (%u) is less than the minimum\n"
L" usable servers (%u). Returning 0x%08X\n",
nServers, parList->nParam1, parList->dwError);
hr=parList->dwError;
_JumpError(hr, error, "e_MinServersAlert alert evaluation");
}
} else {
// unknown alert type
_MyAssert(false);
}
} // <- end alert checking loop
hr=S_OK;
error:
return hr;
}
//####################################################################
//--------------------------------------------------------------------
void PrintHelpTimeMonitor(void) {
UINT idsText[] = {
IDS_W32TM_MONITORHELP_LINE1, IDS_W32TM_MONITORHELP_LINE2,
IDS_W32TM_MONITORHELP_LINE3, IDS_W32TM_MONITORHELP_LINE4,
IDS_W32TM_MONITORHELP_LINE5, IDS_W32TM_MONITORHELP_LINE6,
IDS_W32TM_MONITORHELP_LINE7, IDS_W32TM_MONITORHELP_LINE8,
IDS_W32TM_MONITORHELP_LINE9, IDS_W32TM_MONITORHELP_LINE10,
IDS_W32TM_MONITORHELP_LINE11, IDS_W32TM_MONITORHELP_LINE12,
IDS_W32TM_MONITORHELP_LINE13, IDS_W32TM_MONITORHELP_LINE14,
IDS_W32TM_MONITORHELP_LINE15, IDS_W32TM_MONITORHELP_LINE16,
IDS_W32TM_MONITORHELP_LINE17, IDS_W32TM_MONITORHELP_LINE18,
IDS_W32TM_MONITORHELP_LINE19, IDS_W32TM_MONITORHELP_LINE20,
IDS_W32TM_MONITORHELP_LINE21, IDS_W32TM_MONITORHELP_LINE22,
IDS_W32TM_MONITORHELP_LINE23, IDS_W32TM_MONITORHELP_LINE24,
IDS_W32TM_MONITORHELP_LINE25
};
for (int n=0; n<ARRAYSIZE(idsText); n++) {
LocalizedWPrintf(idsText[n]);
}
}
//--------------------------------------------------------------------
HRESULT TimeMonitor(CmdArgs * pca) {
HRESULT hr;
unsigned int nComputers;
unsigned int nIndex;
unsigned int nThreads;
unsigned int nTimeout;
ComputerRecord * pcrOffsetsFrom;
WCHAR * wszNumThreads;
WCHAR * wszTimeout;
ThreadSharedContext tscContext;
// must be cleaned up
ComputerRecord ** rgpcrList=NULL;
NameHolder * pnhList=NULL;
AlertRecord * parList=NULL;
bool bSocketLayerOpen=false;
ThreadContext * rgtcThreads=NULL;
// init winsock
hr=OpenSocketLayer();
_JumpIfError(hr, error, "OpenSocketLayer");
bSocketLayerOpen=true;
//
// parse command line
//
hr=ParseCmdLineForComputerNames(pca, &pnhList);
_JumpIfError(hr, error, "ParseTimeMonCmdLineForComputerNames");
hr=ParseCmdLineForAlerts(pca, &parList);
_JumpIfError(hr, error, "ParseCmdLineForAlerts");
// get number of threads to use
if (FindArg(pca, L"threads", &wszNumThreads, &nThreads)) {
MarkArgUsed(pca, nThreads);
nThreads=wcstoul(wszNumThreads, NULL, 0);
if (nThreads<1 || nThreads>50) {
LocalizedWPrintf2(IDS_W32TM_ERRORTIMEMONITOR_INVALID_NUMBER_THREADS, L" (%u).\n", nThreads);
hr=E_INVALIDARG;
_JumpError(hr, error, "command line parsing");
}
} else {
nThreads=3;
}
// get timeout to use for NTP ping
if (FindArg(pca, L"timeout", &wszTimeout, &nTimeout)) {
MarkArgUsed(pca, nTimeout);
nTimeout=wcstoul(wszTimeout, NULL, 0);
nTimeout*=1000;
} else {
nTimeout = gc_dwTimeout;
}
// all args should be parsed
if (pca->nArgs!=pca->nNextArg) {
LocalizedWPrintf(IDS_W32TM_ERRORGENERAL_UNEXPECTED_PARAMS);
for(; pca->nArgs!=pca->nNextArg; pca->nNextArg++) {
wprintf(L" %s", pca->rgwszArgs[pca->nNextArg]);
}
wprintf(L"\n");
hr=E_INVALIDARG;
_JumpError(hr, error, "command line parsing");
}
//
// build list of computers to analyze
//
hr=BuildComputerList(pnhList, &rgpcrList, &nComputers, nTimeout);
_JumpIfError(hr, error, "BuildComputerList");
//
// Do Analysis
//
// analyze each of the computers
if (nThreads>nComputers) {
nThreads=nComputers;
}
if (nThreads<=1) {
for (nIndex=0; nIndex<nComputers; nIndex++) {
ClearLine();
wprintf(L"Analyzing %s (%u of %u)...\r", rgpcrList[nIndex]->wszName, nIndex+1, nComputers);
DebugWPrintf0(L"\n");
hr=AnalyzeComputer(rgpcrList[nIndex]);
// errors are saved in the ComputerRecord and reported later
}
} else {
// get ready to use threads
DWORD dwThreadID;
tscContext.nComputers=nComputers;
tscContext.rgpcrList=rgpcrList;
tscContext.nNextComputer=0;
tscContext.nFinishedComputers=0;
rgtcThreads=(ThreadContext *)LocalAlloc(LPTR, nThreads*sizeof(ThreadContext));
_JumpIfOutOfMemory(hr, error, rgtcThreads);
for (nIndex=0; nIndex<nThreads; nIndex++) {
rgtcThreads[nIndex].ptsc=&tscContext;
rgtcThreads[nIndex].nCurRecord=-1;
rgtcThreads[nIndex].hThread=CreateThread(NULL, 0, AnalysisThread, &(rgtcThreads[nIndex]), 0, &dwThreadID);
if (NULL==rgtcThreads[nIndex].hThread) {
_JumpLastError(hr, error, "CreateThread");
}
}
// wait for the threads to finish
while (tscContext.nFinishedComputers<nComputers) {
wprintf(L"Analyzing:");
for (nIndex=0; nIndex<nThreads && nIndex<16; nIndex++) {
unsigned int nCurRecord=rgtcThreads[nIndex].nCurRecord;
if (nCurRecord<nComputers) {
wprintf(L" %2u", nCurRecord+1);
} else {
wprintf(L" --");
}
}
wprintf(L" (%u of %u)\r", tscContext.nFinishedComputers, nComputers);
Sleep(250);
}
}
// resolve referers
for (nIndex=0; nIndex<nComputers; nIndex++) {
ClearLine();
wprintf(L"resolving referer %u.%u.%u.%u (%u of %u)...\r",
rgpcrList[nIndex]->refid.rgnIpAddr[0],
rgpcrList[nIndex]->refid.rgnIpAddr[1],
rgpcrList[nIndex]->refid.rgnIpAddr[2],
rgpcrList[nIndex]->refid.rgnIpAddr[3],
nIndex+1, nComputers);
DebugWPrintf0(L"\n");
hr=ResolveReferer(rgpcrList, nComputers, nIndex);
_JumpIfError(hr, error, "ResolveReferer"); // only fatal errors are returned
}
ClearLine();
// if there is a PDC, base the offsets from that
pcrOffsetsFrom=NULL;
for (nIndex=0; nIndex<nComputers; nIndex++) {
if (rgpcrList[nIndex]->bIsPdc) {
pcrOffsetsFrom=rgpcrList[nIndex];
unsigned int nSubIndex;
NtTimeOffset toPdc=rgpcrList[nIndex]->toOffset;
for (nSubIndex=0; nSubIndex<nComputers; nSubIndex++) {
rgpcrList[nSubIndex]->toOffset-=toPdc;
}
break;
}
}
//
// print the results
//
for (nIndex=0; nIndex<nComputers; nIndex++) {
// print who we are looking at
wprintf(L"%s%s", rgpcrList[nIndex]->wszName, rgpcrList[nIndex]->bIsPdc?L" *** PDC ***":L"");
if (0==rgpcrList[nIndex]->nIpAddrs) {
if (HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND)==rgpcrList[nIndex]->hrIPs) {
wprintf(L" [error WSAHOST_NOT_FOUND]\n");
} else {
wprintf(L" [error 0x%08X]\n", rgpcrList[nIndex]->hrIPs);
}
// don't bother with anything else if this doesn't work
continue;
} else {
wprintf(L" [%u.%u.%u.%u]:\n",
rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b1,
rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b2,
rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b3,
rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b4
);
}
// display an ICMP ping
wprintf(L" ICMP: ");
if (FAILED(rgpcrList[nIndex]->hrIcmp)) {
if (HRESULT_FROM_WIN32(IP_REQ_TIMED_OUT)==rgpcrList[nIndex]->hrIcmp) {
wprintf(L"error IP_REQ_TIMED_OUT - no response in %ums\n", gc_dwTimeout);
} else {
wprintf(L"error 0x%08X\n",rgpcrList[nIndex]->hrIcmp);
}
// NOTE: we could still have successfully done an NTP ping, even if an ICMP
// ping fails, as some servers disable ICMP.
} else {
wprintf(L"%ums delay.\n", rgpcrList[nIndex]->dwIcmpDelay);
}
// display an NTP ping
wprintf(L" NTP: ");
if (FAILED(rgpcrList[nIndex]->hrNtp)) {
if (HRESULT_FROM_WIN32(WSAECONNRESET)==rgpcrList[nIndex]->hrNtp) {
wprintf(L"error WSAECONNRESET - no server listening on NTP port\n");
} else if (HRESULT_FROM_WIN32(ERROR_TIMEOUT)==rgpcrList[nIndex]->hrNtp) {
wprintf(L"error ERROR_TIMEOUT - no response from server in %ums\n", rgpcrList[nIndex]->nTimeout);
} else {
wprintf(L"error 0x%08X\n" ,rgpcrList[nIndex]->hrNtp);
}
} else {
// display the offset
DWORD dwSecFraction;
NtTimeOffset toLocalClockOffset=rgpcrList[nIndex]->toOffset;
WCHAR * wszSign;
if (toLocalClockOffset.qw<0) {
toLocalClockOffset=-toLocalClockOffset;
wszSign=L"-";
} else {
wszSign=L"+";
}
dwSecFraction=(DWORD)(toLocalClockOffset.qw%10000000);
toLocalClockOffset/=10000000;
wprintf(L"%s%I64u.%07us offset from %s\n", wszSign, toLocalClockOffset.qw, dwSecFraction,
((NULL!=pcrOffsetsFrom)?pcrOffsetsFrom->wszName:L"local clock"));
// deterine and display the referer
WCHAR * wszReferer;
WCHAR wszRefName[7];
if (0==rgpcrList[nIndex]->refid.value) {
wszReferer=L"unspecified / unsynchronized";
} else if (1>=rgpcrList[nIndex]->nStratum) {
wszReferer=wszRefName;
wszRefName[0]=L'\'';
wszRefName[1]=rgpcrList[nIndex]->refid.rgnName[0];
wszRefName[2]=rgpcrList[nIndex]->refid.rgnName[1];
wszRefName[3]=rgpcrList[nIndex]->refid.rgnName[2];
wszRefName[4]=rgpcrList[nIndex]->refid.rgnName[3];
wszRefName[5]=L'\'';
wszRefName[6]=0;
} else if (NULL!=rgpcrList[nIndex]->pcrReferer) {
wszReferer=rgpcrList[nIndex]->pcrReferer->wszName;
} else if (NULL!=rgpcrList[nIndex]->wszReferer) {
wszReferer=rgpcrList[nIndex]->wszReferer;
} else {
wszReferer=L"(unknown)";
}
wprintf(L" RefID: %s [%u.%u.%u.%u]\n",
wszReferer,
rgpcrList[nIndex]->refid.rgnIpAddr[0],
rgpcrList[nIndex]->refid.rgnIpAddr[1],
rgpcrList[nIndex]->refid.rgnIpAddr[2],
rgpcrList[nIndex]->refid.rgnIpAddr[3]
);
// BUGBUG: change not approved for beta2, checkin to beta 3:
// wprintf(L" Stratum: %d\n", rgpcrList[nIndex]->nStratum);
}
} // <- end ComputerRecord display loop
hr=CheckForAlerts(rgpcrList, nComputers, parList);
_JumpIfError(hr, error, "CheckForAlerts");
hr=S_OK;
error:
if (NULL!=rgpcrList) {
for (nIndex=0; nIndex<nComputers; nIndex++) {
FreeComputerRecord(rgpcrList[nIndex]);
}
LocalFree(rgpcrList);
}
while (NULL!=pnhList) {
NameHolder * pnhTemp=pnhList;
pnhList=pnhList->pnhNext;
LocalFree(pnhTemp);
}
if (true==bSocketLayerOpen) {
CloseSocketLayer();
}
if (NULL!=parList) {
FreeAlertRecords(parList);
}
if (NULL!=rgtcThreads) {
// clean up threads
tscContext.nNextComputer=tscContext.nComputers; // indicate to stop
for (nIndex=0; nIndex<nThreads; nIndex++) {
if (NULL!=rgtcThreads[nIndex].hThread) {
WaitForSingleObject(rgtcThreads[nIndex].hThread, INFINITE);
CloseHandle(rgtcThreads[nIndex].hThread);
}
}
LocalFree(rgtcThreads);
}
if (S_OK!=hr) {
wprintf(L"Exiting with error 0x%08X\n", hr);
}
return hr;
}