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.
1585 lines
60 KiB
1585 lines
60 KiB
/*---------------------------------------------------------------------------
|
|
File: DCTDispatcher.cpp
|
|
|
|
Comments: Implementation of dispatcher COM object. Remotely installs and
|
|
launches the DCT Agent on remote computers.
|
|
|
|
The CDCTDispatcher class implements the COM interface for the dispatcher.
|
|
It takes a varset, containing a list of machines, and dispatches agents to
|
|
each specified machine.
|
|
|
|
A job file (varset persisted to a file) is created for each agent, and
|
|
necessary initialization configuration (such as building an account mapping file for
|
|
security translation) is done. The DCDTDispatcher class instantiates a
|
|
thread pool (CPooledDispatch), and uses the CDCTInstaller class to remotely
|
|
install and start the agent service on each machine.
|
|
|
|
(c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
|
|
Proprietary and confidential to Mission Critical Software, Inc.
|
|
|
|
REVISION LOG ENTRY
|
|
Revision By: Christy Boles
|
|
Revised on 02/15/99 11:23:57
|
|
|
|
---------------------------------------------------------------------------
|
|
*/// DCTDispatcher.cpp : Implementation of CDCTDispatcher
|
|
#include "stdafx.h"
|
|
#include "resource.h"
|
|
#include <locale.h>
|
|
|
|
//#include "..\Common\Include\McsDispatcher.h"
|
|
#include "Dispatch.h"
|
|
#include "DDisp.h"
|
|
#include "DInst.h"
|
|
|
|
#include "Common.hpp"
|
|
#include "ErrDct.hpp"
|
|
#include "UString.hpp"
|
|
#include "EaLen.hpp"
|
|
#include "Cipher.hpp"
|
|
#include "TNode.hpp"
|
|
|
|
#include "TPool.h" // Thread pool for dispatching jobs
|
|
#include "LSAUtils.h"
|
|
|
|
#include "TxtSid.h"
|
|
#include "sd.hpp"
|
|
#include "SecObj.hpp"
|
|
#include "BkupRstr.hpp"
|
|
#include "TReg.hpp"
|
|
|
|
#include "ResStr.h"
|
|
|
|
#include "TaskChk.h"
|
|
#include "CommaLog.hpp"
|
|
#include "TInst.h"
|
|
|
|
#include <lm.h>
|
|
#include "GetDcName.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDCTDispatcher
|
|
|
|
//#import "\bin\McsEADCTAgent.tlb" named_guids
|
|
//#include "..\AgtSvc\AgSvc.h"
|
|
#import "Engine.tlb" named_guids
|
|
#include "AgSvc.h"
|
|
#include "AgSvc_c.c"
|
|
#include "AgRpcUtl.h"
|
|
|
|
//#import "\bin\McsDctWorkerObjects.tlb"
|
|
//#include "..\Common\Include\McsPI.h"
|
|
#import "WorkObj.tlb"
|
|
#include "McsPI.h"
|
|
#include "McsPI_i.c"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
TErrorDct errLog; // used to write dispatch log that is read by the agent monitor
|
|
TErrorDct errTrace;
|
|
TCriticalSection gCS;
|
|
// TServerNodes make up an internally used list of machines to install to
|
|
class TServerNode : public TNode
|
|
{
|
|
WCHAR sourceName[LEN_Computer];
|
|
WCHAR targetName[LEN_Computer];
|
|
BOOL bTranslate;
|
|
BOOL bChangeDomain;
|
|
BOOL bReboot;
|
|
DWORD dwRebootDelay;
|
|
public:
|
|
TServerNode() { sourceName[0] = 0; targetName[0] = 0; bTranslate = FALSE; bChangeDomain = FALSE; bReboot= FALSE; dwRebootDelay = 0; }
|
|
WCHAR const * SourceName() { return sourceName; }
|
|
WCHAR const * TargetName() { return targetName; }
|
|
BOOL Translate() { return bTranslate; }
|
|
BOOL Reboot() { return bReboot; }
|
|
BOOL ChangeDomain() { return bChangeDomain; }
|
|
DWORD RebootDelay() { return dwRebootDelay; }
|
|
|
|
void SourceName(WCHAR const * src) { safecopy(sourceName,src); }
|
|
void TargetName(WCHAR const * tgt) { safecopy(targetName,tgt); }
|
|
void Translate(BOOL v) { bTranslate = v; }
|
|
void ChangeDomain(BOOL v) { bChangeDomain = v; }
|
|
void Reboot(BOOL v) { bReboot = v; }
|
|
void RebootDelay(DWORD d) { dwRebootDelay = d; }
|
|
};
|
|
|
|
|
|
extern
|
|
TErrorDct err;
|
|
|
|
// defined in CkPlugIn.cpp
|
|
BOOL IsValidPlugIn(IMcsDomPlugIn * pPlugIn);
|
|
|
|
|
|
BOOL // ret - TRUE if need to dump debug information
|
|
DumpDebugInfo(
|
|
WCHAR * filename // out - where to dump debug information
|
|
)
|
|
{
|
|
DWORD rc = 0;
|
|
BOOL bFound = FALSE;
|
|
TRegKey key;
|
|
|
|
rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
|
|
if ( ! rc )
|
|
{
|
|
rc = key.ValueGetStr(L"DispatchVarSet",filename,MAX_PATH);
|
|
if ( ! rc )
|
|
{
|
|
if ( *filename )
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
return bFound;
|
|
}
|
|
|
|
HRESULT
|
|
BuildPlugInFileList(
|
|
TNodeList * pList, // i/o- list that files needed by plug-ins wil be added to
|
|
IVarSet * pVarSet // in - varset containing list of plug-ins to query
|
|
)
|
|
{
|
|
// for now, build a list of all plug-ins, and add it to the varset
|
|
MCSDCTWORKEROBJECTSLib::IPlugInInfoPtr pPtr;
|
|
SAFEARRAY * pArray = NULL;
|
|
HRESULT hr = S_OK;
|
|
LONG bound;
|
|
LONG ndx[1];
|
|
WCHAR key[LEN_Path];
|
|
|
|
hr = pPtr.CreateInstance(__uuidof(MCSDCTWORKEROBJECTSLib::PlugInInfo));
|
|
|
|
_bstr_t bStrGuid;
|
|
|
|
swprintf(key,GET_STRING(IDS_DCTVS_Fmt_PlugIn_D),0);
|
|
bStrGuid = pVarSet->get(key);
|
|
|
|
if (! bStrGuid.length() )
|
|
{
|
|
// if no plug-ins are specified, use the ones in the plug-ins directory
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pPtr->raw_EnumeratePlugIns(&pArray);
|
|
}
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
SafeArrayGetUBound(pArray,1,&bound);
|
|
for ( ndx[0] = 0 ; ndx[0] <= bound ; ndx[0]++ )
|
|
{
|
|
BSTR val = NULL;
|
|
|
|
SafeArrayGetElement(pArray,ndx,&val);
|
|
swprintf(key,GET_STRING(IDS_DCTVS_Fmt_PlugIn_D),ndx[0]);
|
|
pVarSet->put(key,val);
|
|
SysFreeString(val);
|
|
}
|
|
SafeArrayDestroy(pArray);
|
|
pArray = NULL;
|
|
}
|
|
}
|
|
// enumerate the plug-ins specified in the varset, and make a list of their needed files
|
|
int nRegFiles = 0;
|
|
|
|
for ( int i = 0 ; ; i++ )
|
|
{
|
|
swprintf(key,GET_STRING(IDS_DCTVS_Fmt_PlugIn_D),i);
|
|
bStrGuid = pVarSet->get(key);
|
|
|
|
if ( bStrGuid.length() == 0 )
|
|
break;
|
|
|
|
if(!_wcsicmp(bStrGuid, L"None"))
|
|
continue;
|
|
|
|
IMcsDomPlugIn * pPlugIn = NULL;
|
|
SAFEARRAY * pFileArray = NULL;
|
|
TFileNode * pNode;
|
|
CLSID clsid;
|
|
|
|
hr = CLSIDFromString(bStrGuid,&clsid);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = CoCreateInstance(clsid,NULL,CLSCTX_ALL,IID_IMcsDomPlugIn,(void**)&pPlugIn);
|
|
}
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
if ( IsValidPlugIn(pPlugIn) )
|
|
{
|
|
hr = pPlugIn->GetRequiredFiles(&pFileArray);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
SafeArrayGetUBound(pFileArray,1,&bound);
|
|
for ( ndx[0] = 0 ; ndx[0] <= bound ; ndx[0]++ )
|
|
{
|
|
BSTR val = NULL;
|
|
|
|
SafeArrayGetElement(pFileArray,ndx,&val);
|
|
pNode = new TFileNode(val);
|
|
pList->InsertBottom(pNode);
|
|
SysFreeString(val);
|
|
}
|
|
SafeArrayDestroy(pFileArray);
|
|
pFileArray = NULL;
|
|
}
|
|
hr = pPlugIn->GetRegisterableFiles(&pFileArray);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
SafeArrayGetUBound(pFileArray,1,&bound);
|
|
for (ndx[0] = 0; ndx[0] <= bound ; ndx[0]++ )
|
|
{
|
|
BSTR val = NULL;
|
|
|
|
SafeArrayGetElement(pFileArray,ndx,&val);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_PlugIn_RegisterFiles_D),nRegFiles);
|
|
pVarSet->put(key,val);
|
|
SysFreeString(val);
|
|
nRegFiles++;
|
|
}
|
|
SafeArrayDestroy(pFileArray);
|
|
pFileArray = NULL;
|
|
}
|
|
}
|
|
pPlugIn->Release();
|
|
}
|
|
|
|
// we should bail out immediately if error occurs
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
// InstallJobInfo defines a Domain Migration 'job' to be installed and launched
|
|
struct InstallJobInfo
|
|
{
|
|
IVarSetPtr pVarSetList; // varset defining the server list
|
|
IVarSetPtr pVarSet; // VarSet defining the job to run
|
|
_bstr_t serverName; // computer to install and run on
|
|
_bstr_t serverNameDns; // computer to install and run on
|
|
long ndx; // index of this server in the server list
|
|
TNodeList * pPlugInFileList; // list of files to install for plug-ins
|
|
std::vector<CComBSTR>* pStartFailedVector;
|
|
std::vector<CComBSTR>* pFailureDescVector;
|
|
std::vector<CComBSTR>* pStartedVector;
|
|
std::vector<CComBSTR>* pJobidVector;
|
|
HANDLE hMutex;
|
|
_bstr_t jobfile; // uses the specified job file instead of creating one
|
|
int nErrCount;
|
|
|
|
PCTSTR GetServerName()
|
|
{
|
|
return serverNameDns.length() ? serverNameDns : serverName;
|
|
}
|
|
|
|
PCTSTR GetServerNameDns()
|
|
{
|
|
return serverNameDns;
|
|
}
|
|
|
|
PCTSTR GetServerNameFlat()
|
|
{
|
|
return serverName;
|
|
}
|
|
};
|
|
|
|
// WaitInfo is used to pass information to a thread that waits and does cleanup
|
|
// after all the Dispatcher's work is done
|
|
struct WaitInfo
|
|
{
|
|
IUnknown * pUnknown; // IUnknown interface to the DCTDisptacher object
|
|
TJobDispatcher **ppPool; // pointer to thread pool performing the tasks (installations)
|
|
TNodeList * pPlugInFileList; // pointer to plug-in files list that will need to be freed
|
|
};
|
|
|
|
WCHAR gComputerName[LEN_Computer] = L""; // name of local computer
|
|
|
|
// Calls DCTAgentService to start the Domain Migration Job on a remote computer
|
|
DWORD // ret- OS return code
|
|
StartJob(
|
|
WCHAR const * serverName, // in - computer to start job on
|
|
WCHAR const * fullname, // in - full path (including filename) to file containing the job's VarSet
|
|
WCHAR const * filename, // in - filename of file containing the varset for the job
|
|
_bstr_t& strJobid
|
|
)
|
|
{
|
|
DWORD rc = 0;
|
|
handle_t hBinding = NULL;
|
|
WCHAR * sBinding = NULL;
|
|
WCHAR jobGUID[LEN_Guid];
|
|
WCHAR passwordW[1] = { 0 };
|
|
|
|
rc = EaxBindCreate(serverName,&hBinding,&sBinding,TRUE);
|
|
if ( rc )
|
|
{
|
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_BIND_FAILED_SD, serverName,rc);
|
|
}
|
|
if( ! rc )
|
|
{
|
|
RpcTryExcept
|
|
{
|
|
// the job file has been copied to the remote computer
|
|
// during the installation
|
|
rc = EaxcSubmitJob(hBinding,filename,passwordW,jobGUID);
|
|
if ( ! rc )
|
|
{
|
|
err.MsgWrite(0,DCT_MSG_AGENT_JOB_STARTED_SSS,serverName,filename,jobGUID);
|
|
strJobid = jobGUID;
|
|
}
|
|
else
|
|
{
|
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
|
|
}
|
|
}
|
|
RpcExcept(1)
|
|
{
|
|
rc = RpcExceptionCode();
|
|
if ( rc != RPC_S_SERVER_UNAVAILABLE )
|
|
{
|
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
|
|
}
|
|
}
|
|
RpcEndExcept
|
|
if ( rc == RPC_S_SERVER_UNAVAILABLE )
|
|
{
|
|
// maybe the agent hasn't started up yet for some reason
|
|
|
|
for ( int tries = 0 ; tries < 6 ; tries++ )
|
|
{
|
|
Sleep(5000); // wait a few seconds and try again
|
|
|
|
RpcTryExcept
|
|
{
|
|
rc = EaxcSubmitJob(hBinding,filename,passwordW,jobGUID);
|
|
if ( ! rc )
|
|
{
|
|
err.MsgWrite(0,DCT_MSG_AGENT_JOB_STARTED_SSS,serverName,filename,jobGUID);
|
|
strJobid = jobGUID;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ( tries == 5 )
|
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
|
|
}
|
|
}
|
|
RpcExcept(1)
|
|
{
|
|
rc = RpcExceptionCode();
|
|
if ( tries == 5 )
|
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
|
|
}
|
|
RpcEndExcept
|
|
}
|
|
}
|
|
}
|
|
if ( ! rc )
|
|
{
|
|
// if the job was started successfully, remove the job file
|
|
if ( ! MoveFileEx(fullname,NULL, MOVEFILE_DELAY_UNTIL_REBOOT) )
|
|
{
|
|
// DWORD rc2 = GetLastError();
|
|
}
|
|
}
|
|
// this indicates whether the server was started
|
|
if ( ! rc )
|
|
{
|
|
errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld,%ls,%ls",serverName,L"Start",rc,filename,jobGUID);
|
|
}
|
|
else
|
|
{
|
|
errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",serverName,L"Start",rc);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
// Gets the domain sid for the specified domain
|
|
BOOL // ret- TRUE if successful
|
|
GetSidForDomain(
|
|
LPWSTR DomainName, // in - name of domain to get SID for
|
|
PSID * pDomainSid // out- SID for domain, free with FreeSid
|
|
)
|
|
{
|
|
PSID pSid = NULL;
|
|
DWORD rc = 0;
|
|
_bstr_t domctrl;
|
|
|
|
if ( DomainName[0] != L'\\' )
|
|
{
|
|
rc = GetAnyDcName5(DomainName, domctrl);
|
|
}
|
|
if ( ! rc )
|
|
{
|
|
rc = GetDomainSid(domctrl,&pSid);
|
|
}
|
|
(*pDomainSid) = pSid;
|
|
|
|
return ( pSid != NULL);
|
|
}
|
|
|
|
// Set parameters in the varset that are specific to this particular computer
|
|
void
|
|
SetupVarSetForJob(
|
|
InstallJobInfo * pInfo, // structure defining job
|
|
IVarSet * pVarSet, // varset describing job
|
|
WCHAR const * uncname, // UNC path for results directory
|
|
WCHAR const * filename, // UNC path for results file for this job
|
|
WCHAR const * relativeFileName, // relative results file name for this job
|
|
BOOL bUpdate = FALSE // whether it is just to update the varset (for retry)
|
|
)
|
|
{
|
|
WCHAR uncresult[MAX_PATH];
|
|
WCHAR serverName[MAX_PATH];
|
|
WCHAR relativeResultFileName[MAX_PATH];
|
|
_bstr_t text;
|
|
|
|
// Set server-specific parameters in the varset
|
|
swprintf(uncresult,L"%s.result",filename);
|
|
swprintf(relativeResultFileName,L"%s.result",relativeFileName);
|
|
swprintf(serverName,L"\\\\%s",gComputerName);
|
|
|
|
pVarSet->put(GET_BSTR(DCTVS_Options_ResultFile),uncresult);
|
|
pVarSet->put(GET_BSTR(DCTVS_Options_RelativeResultFileName), relativeResultFileName);
|
|
|
|
// this part is only necessary when we are not just trying to update
|
|
// the varset
|
|
if (!bUpdate)
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_Options_Credentials_Server),serverName);
|
|
pVarSet->put(GET_BSTR(DCTVS_Options_DeleteFileAfterLoad),GET_BSTR(IDS_YES));
|
|
pVarSet->put(GET_BSTR(DCTVS_Options_RemoveAgentOnCompletion),GET_BSTR(IDS_YES));
|
|
pVarSet->put(GET_BSTR(DCTVS_Options_LogToTemp),GET_BSTR(IDS_YES));
|
|
pVarSet->put(GET_BSTR(DCTVS_Server_Index), CComVariant((long)pInfo->ndx));
|
|
|
|
text = pVarSet->get(GET_BSTR(DCTVS_GatherInformation_UserRights));
|
|
if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
|
|
{
|
|
swprintf(uncresult,L"%s.userrights",filename);
|
|
pVarSet->put(GET_BSTR(DCTVS_GatherInformation_UserRights),uncresult);
|
|
}
|
|
}
|
|
|
|
text = pVarSet->get(GET_BSTR(DCTVS_Security_ReportAccountReferences));
|
|
if ( ! UStrICmp(text,GET_STRING(IDS_YES)) )
|
|
{
|
|
swprintf(uncresult,L"%s.secrefs",filename);
|
|
swprintf(relativeResultFileName, L"%s.secrefs", relativeFileName);
|
|
pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences),uncresult);
|
|
pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferencesRelativeFileName), relativeResultFileName);
|
|
}
|
|
else
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences), L"");
|
|
pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferencesRelativeFileName), L"");
|
|
}
|
|
|
|
// this part is only necessary when we are not just trying to update
|
|
// the varset
|
|
if (!bUpdate)
|
|
pVarSet->put(GET_BSTR(DCTVS_Options_LocalProcessingOnly),GET_BSTR(IDS_YES));
|
|
}
|
|
|
|
// Entry point for thread, waits until all agents are installed and started,
|
|
// then cleans up and exits
|
|
ULONG __stdcall // ret- returns 0
|
|
Wait(
|
|
void * arg // in - WaitInfo structure containing needed pointers
|
|
)
|
|
{
|
|
WaitInfo * w = (WaitInfo*)arg;
|
|
|
|
SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
|
|
|
|
// wait for all jobs to finish
|
|
(*(w->ppPool))->WaitForCompletion();
|
|
|
|
if ( w->pUnknown )
|
|
w->pUnknown->Release();
|
|
|
|
// delete the plug-in file list
|
|
TNodeListEnum tEnum;
|
|
TFileNode * fNode;
|
|
TFileNode * fNext;
|
|
|
|
for ( fNode = (TFileNode*)tEnum.OpenFirst(w->pPlugInFileList); fNode; fNode = fNext )
|
|
{
|
|
fNext = (TFileNode*)tEnum.Next();
|
|
w->pPlugInFileList->Remove(fNode);
|
|
delete fNode;
|
|
}
|
|
tEnum.Close();
|
|
|
|
delete w->pPlugInFileList;
|
|
|
|
delete *(w->ppPool);
|
|
*(w->ppPool) = NULL;
|
|
|
|
err.MsgWrite(0,DCT_MSG_DISPATCHER_DONE);
|
|
errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",L"All",L"Finished",0);
|
|
err.LogClose();
|
|
errLog.LogClose();
|
|
return 0;
|
|
}
|
|
|
|
// Thread entry point, installs and starts agent on a single computer
|
|
ULONG __stdcall // ret- HRESULT error code
|
|
DoInstall(
|
|
void * arg // in - InstallJobInfo structure
|
|
)
|
|
{
|
|
SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
|
|
|
|
HRESULT hr = S_OK;
|
|
InstallJobInfo * pInfo = (InstallJobInfo*)arg;
|
|
_bstr_t strJobid;
|
|
_bstr_t strFailureDesc(GET_STRING(IDS_START_FAILED));
|
|
BOOL bErrLogged = FALSE; // indicates whether the error in dispatching
|
|
// has been written into the dispatcher.csv
|
|
|
|
if(pInfo->nErrCount == 0)
|
|
hr = CoInitializeEx(0,COINIT_MULTITHREADED );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
IWorkNode * pInstaller = NULL;
|
|
IVarSetPtr pVarSet(CLSID_VarSet);
|
|
WCHAR filename[MAX_PATH];
|
|
WCHAR relativeFileName[MAX_PATH];
|
|
WCHAR tempdir[MAX_PATH];
|
|
WCHAR key[MAX_PATH];
|
|
|
|
if ( pVarSet == NULL )
|
|
{
|
|
if(pInfo->nErrCount == 0)
|
|
CoUninitialize();
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
DWORD uniqueNumber = (LONG)pInfo->pVarSet->get(GET_BSTR(DCTVS_Options_UniqueNumberForResultsFile));
|
|
_bstr_t bstrResultPath = pInfo->pVarSet->get(GET_BSTR(DCTVS_Dispatcher_ResultPath));
|
|
// Copy the common information from the source varset
|
|
gCS.Enter();
|
|
// pInfo->pVarSet contains all the information except the server list
|
|
// we don't want to copy the server list each time, so we create our new varset from pInfo->pVarSet
|
|
pVarSet->ImportSubTree("",pInfo->pVarSet);
|
|
gCS.Leave();
|
|
// Set the server-specific data in the varset
|
|
swprintf(key,GET_BSTR(IDS_DCTVSFmt_Servers_RenameTo_D),pInfo->ndx);
|
|
|
|
// pInfo->pVarSetList contains the entire varset including the server list
|
|
_bstr_t text = pInfo->pVarSetList->get(key);
|
|
|
|
if ( text.length() )
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_LocalServer_RenameTo),text);
|
|
}
|
|
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),pInfo->ndx);
|
|
text = pInfo->pVarSetList->get(key);
|
|
if ( text.length() )
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_LocalServer_ChangeDomain),text);
|
|
}
|
|
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),pInfo->ndx);
|
|
text = pInfo->pVarSetList->get(key);
|
|
pVarSet->put(GET_BSTR(DCTVS_LocalServer_MigrateOnly),text);
|
|
|
|
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),pInfo->ndx);
|
|
text = pInfo->pVarSetList->get(key);
|
|
if ( text.length() )
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_LocalServer_Reboot),text);
|
|
LONG delay;
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),pInfo->ndx);
|
|
delay = pInfo->pVarSetList->get(key);
|
|
if ( delay )
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_LocalServer_RebootDelay),delay);
|
|
}
|
|
}
|
|
// remove the password from the varset, so that we are not writing it
|
|
// to a file in plain text. Instead, it will be passed to the agent service
|
|
// when the job is submitted
|
|
pVarSet->put(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password),"");
|
|
|
|
if ( ! uniqueNumber )
|
|
{
|
|
uniqueNumber = GetTickCount();
|
|
}
|
|
|
|
MCSASSERT(bstrResultPath.length());
|
|
|
|
safecopy(tempdir,(WCHAR*)bstrResultPath);
|
|
|
|
pInstaller = new CComObject<CDCTInstaller>;
|
|
if (pInstaller)
|
|
{
|
|
pInstaller->AddRef();
|
|
((CDCTInstaller*)pInstaller)->SetFileList(pInfo->pPlugInFileList);
|
|
}
|
|
else
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
_bstr_t strCacheFile;
|
|
BOOL bJobFileAlreadyExists = (pInfo->jobfile.length() == 0) ? FALSE : TRUE;
|
|
|
|
int relFilenameLen = sizeof(relativeFileName)/sizeof(relativeFileName[0]);
|
|
int absFilenameLen = sizeof(filename)/sizeof(filename[0]);
|
|
|
|
// figure out filename and relativeFileName
|
|
// make sure their lengths will be less than MAX_PATH
|
|
// otherwise, the mismatch of two names will cause agents to fail later one
|
|
if (bJobFileAlreadyExists)
|
|
{
|
|
WCHAR* path = (WCHAR*) pInfo->jobfile;
|
|
WCHAR jobFilename[_MAX_FNAME];
|
|
_wsplitpath(path, NULL, NULL, jobFilename, NULL);
|
|
|
|
if (wcslen(jobFilename) < relFilenameLen
|
|
&& wcslen(jobFilename) + wcslen(tempdir) < absFilenameLen)
|
|
{
|
|
wcscpy(relativeFileName, jobFilename);
|
|
swprintf(filename,L"%s%s",tempdir,relativeFileName);
|
|
}
|
|
else
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
|
|
}
|
|
else
|
|
{
|
|
WCHAR sNumber[20];
|
|
swprintf(sNumber,L"%ld",uniqueNumber);
|
|
int filenameLen = wcslen(pInfo->GetServerNameFlat() + 2) + wcslen(sNumber);
|
|
if (wcslen(tempdir) + filenameLen < absFilenameLen && filenameLen < relFilenameLen)
|
|
{
|
|
swprintf(filename,L"%s%s%s",tempdir,pInfo->GetServerNameFlat() + 2,sNumber);
|
|
swprintf(relativeFileName, L"%s%s", pInfo->GetServerNameFlat() + 2,sNumber);
|
|
}
|
|
else
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
|
|
}
|
|
|
|
// obtain the cache file name and if an existing job file is in old version
|
|
// convert it to the new version
|
|
if (SUCCEEDED(hr) && bJobFileAlreadyExists)
|
|
{
|
|
IStoragePtr spStorage;
|
|
|
|
hr = StgOpenStorage(filename, NULL, STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &spStorage);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IVarSetPtr spJobVarSet;
|
|
|
|
hr = OleLoad(spStorage, IID_IUnknown, NULL, (void**)&spJobVarSet);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// for retry case, we need to build plugin file list using
|
|
// the job file
|
|
hr = BuildPlugInFileList(pInfo->pPlugInFileList, spJobVarSet);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
strCacheFile = spJobVarSet->get(GET_BSTR(DCTVS_Accounts_InputFile));
|
|
|
|
// this is used to indicate whether newly added varset fields in job file are
|
|
// present or not: these fields include DCTVS_Options_RelatvieResultFileName,
|
|
// DCTVS_Security_ReportAccountReferencesRelativeFileName
|
|
// if not, we have to add them so that the agent code will work
|
|
_bstr_t storedRelativeResultFileName =
|
|
spJobVarSet->get(GET_BSTR(DCTVS_Options_RelativeResultFileName));
|
|
if (storedRelativeResultFileName.length() == 0)
|
|
{
|
|
SetupVarSetForJob(pInfo,spJobVarSet,tempdir,filename,relativeFileName, TRUE);
|
|
IPersistStoragePtr ps = NULL;
|
|
hr = spJobVarSet->QueryInterface(IID_IPersistStorage, (void**)&ps);
|
|
if (SUCCEEDED(hr))
|
|
hr = OleSave(ps,spStorage,FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// retrieve cache file name from varset
|
|
strCacheFile = pVarSet->get(GET_BSTR(DCTVS_Accounts_InputFile));
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && (!bJobFileAlreadyExists))
|
|
{
|
|
SetupVarSetForJob(pInfo,pVarSet,tempdir,filename,relativeFileName);
|
|
|
|
// Save the input varset to a file
|
|
IPersistStoragePtr ps = NULL;
|
|
IStoragePtr store = NULL;
|
|
|
|
hr = pVarSet->QueryInterface(IID_IPersistStorage,(void**)&ps);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = StgCreateDocfile(filename,STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE |STGM_FAILIFTHERE,0,&store);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = OleSave(ps,store,FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
IUnknown * pWorkItem = NULL;
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_ConfigurationFile),filename);
|
|
pVarSet->put(GET_BSTR(DCTVS_InstallToServer),pInfo->GetServerName());
|
|
pVarSet->put(GET_BSTR(DCTVS_CacheFile), strCacheFile);
|
|
|
|
hr = pVarSet->QueryInterface(IID_IUnknown,(void**)&pWorkItem);
|
|
}
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Do the installation to the server
|
|
hr = pInstaller->Process(pWorkItem);
|
|
if(hr == 0x88070040)
|
|
strFailureDesc = GET_STRING(IDS_AGENT_RUNNING);
|
|
|
|
pWorkItem->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
err.MsgWrite(0,DCT_MSG_AGENT_INSTALLED_S,pInfo->GetServerName());
|
|
// try to start the job
|
|
DWORD rc = StartJob(pInfo->GetServerName(),filename,filename + UStrLen(tempdir), strJobid );
|
|
if ( rc )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(rc);
|
|
// if we couldn't start the job, then try to stop the service
|
|
TDCTInstall x( pInfo->GetServerName(), NULL );
|
|
x.SetServiceInformation(GET_STRING(IDS_DISPLAY_NAME),GET_STRING(IDS_SERVICE_NAME),L"EXE",NULL);
|
|
DWORD rcOs = x.ScmOpen();
|
|
|
|
if ( ! rcOs )
|
|
{
|
|
x.ServiceStop();
|
|
}
|
|
}
|
|
}
|
|
|
|
// by now, we know for sure that the error will be logged into dispatcher.csv
|
|
// if there is any
|
|
bErrLogged = TRUE;
|
|
|
|
}
|
|
pInstaller->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pInfo->nErrCount == 0)
|
|
CoUninitialize();
|
|
|
|
if ( hr )
|
|
{
|
|
if ( hr == HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) )
|
|
{
|
|
err.MsgWrite(ErrE,DCT_MSG_AGENT_SERVICE_NOT_STARTED_SS,pInfo->GetServerName(),pInfo->GetServerName());
|
|
}
|
|
else
|
|
{
|
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_AGENT_LAUNCH_FAILED_SD,pInfo->GetServerName(),hr);
|
|
}
|
|
|
|
if(hr == 0x80070040 && pInfo->nErrCount < 10)
|
|
{
|
|
Sleep(1000);
|
|
pInfo->nErrCount++;
|
|
err.DbgMsgWrite(0,L"Retrying install...");
|
|
hr = DoInstall((LPVOID)pInfo);
|
|
}
|
|
else if (hr == CO_E_NOT_SUPPORTED)
|
|
{
|
|
err.MsgWrite(ErrI,DCT_MSG_AGENT_ALPHA_NOTSUPPORTED,pInfo->GetServerName());
|
|
strFailureDesc = GET_STRING(IDS_UNSOUPPORTED_OS);
|
|
::WaitForSingleObject(pInfo->hMutex, 30000);
|
|
pInfo->pStartFailedVector->push_back(pInfo->GetServerName());
|
|
pInfo->pFailureDescVector->push_back((BSTR)strFailureDesc);
|
|
::ReleaseMutex(pInfo->hMutex);
|
|
}
|
|
else
|
|
{
|
|
::WaitForSingleObject(pInfo->hMutex, 30000);
|
|
pInfo->pStartFailedVector->push_back(pInfo->GetServerName());
|
|
pInfo->pFailureDescVector->push_back((BSTR)strFailureDesc);
|
|
::ReleaseMutex(pInfo->hMutex);
|
|
}
|
|
|
|
// if the error has been logged yet, log the error into dispatcher.csv
|
|
if (hr && !bErrLogged)
|
|
errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",pInfo->GetServerName(),L"Install",HRESULT_CODE(hr));
|
|
}
|
|
else
|
|
{
|
|
// DWORD res = ::WaitForSingleObject(pInfo->hMutex, 30000);
|
|
::WaitForSingleObject(pInfo->hMutex, 30000);
|
|
pInfo->pStartedVector->push_back(pInfo->GetServerName());
|
|
_ASSERTE(strJobid != _bstr_t(L""));
|
|
pInfo->pJobidVector->push_back((BSTR)strJobid);
|
|
::ReleaseMutex(pInfo->hMutex);
|
|
}
|
|
|
|
if(pInfo->nErrCount == 0)
|
|
delete pInfo;
|
|
return hr;
|
|
}
|
|
|
|
// DispatchToServers
|
|
// VarSet input:
|
|
//
|
|
STDMETHODIMP // ret- HRESULT
|
|
CDCTDispatcher::DispatchToServers(
|
|
IUnknown ** ppData // i/o- pointer to varset
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
|
|
|
|
//Sleep(60000); //delay for debugging
|
|
(*ppData)->AddRef();
|
|
hr = Process(*ppData,NULL,NULL);
|
|
return hr;
|
|
}
|
|
|
|
// BuildInputFile constructs a cache file to be used for security translation
|
|
// VarSet input:
|
|
// Options.UniqueNumberForResultsFile -unique number to append
|
|
// Dispatcher.ResultPath -directory to write file to
|
|
//
|
|
HRESULT // ret- HRESULT
|
|
CDCTDispatcher::BuildInputFile(
|
|
IVarSet * pVarSet // in - varset containing data
|
|
)
|
|
{
|
|
IVarSetPtr pVarSetST(CLSID_VarSet); // varset to use to run security translator
|
|
IVarSetPtr pVarSetTemp;
|
|
HRESULT hr = S_OK;
|
|
_bstr_t key = GET_BSTR(DCTVS_Options);
|
|
WCHAR tempdir[MAX_PATH];
|
|
WCHAR resultPath[MAX_PATH];
|
|
WCHAR logfile[MAX_PATH];
|
|
|
|
DWORD uniqueNumber = (LONG)pVarSet->get(GET_BSTR(DCTVS_Options_UniqueNumberForResultsFile));
|
|
_bstr_t bstrResultDir = pVarSet->get(GET_BSTR(DCTVS_Dispatcher_ResultPath));
|
|
long lActionId = pVarSet->get(L"ActionID");
|
|
|
|
if ( pVarSetST == NULL )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (! NeedToUseST(pVarSet,TRUE) )
|
|
{
|
|
return S_OK;
|
|
}
|
|
// construct a filename for the cache
|
|
if ( ! uniqueNumber )
|
|
{
|
|
uniqueNumber = GetTickCount();
|
|
}
|
|
|
|
if ( bstrResultDir.length() )
|
|
{
|
|
safecopy(tempdir,(WCHAR*)bstrResultDir);
|
|
}
|
|
else
|
|
{
|
|
// if no result path specified, use temp directory
|
|
hr = GetTempPath(DIM(tempdir),tempdir);
|
|
}
|
|
|
|
//
|
|
// Generate cache file name based on the action id
|
|
// so that account mapping information is persisted
|
|
// for each security related migration task.
|
|
// The cache file name is persisted in the job file
|
|
// for each server.
|
|
//
|
|
|
|
WCHAR szActionId[32];
|
|
swprintf(szActionId, L".%03ld", lActionId);
|
|
|
|
_bstr_t strCacheFile = GET_BSTR(IDS_CACHE_FILE_NAME) + szActionId;
|
|
_bstr_t strCachePath = tempdir + strCacheFile;
|
|
|
|
// copy 'Options' settings to ST varset
|
|
hr = pVarSet->raw_getReference(key,&pVarSetTemp);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pVarSetST->ImportSubTree(key,pVarSetTemp);
|
|
}
|
|
|
|
// copy 'Accounts' settings to ST varset
|
|
key = GET_BSTR(DCTVS_Accounts);
|
|
hr = pVarSet->raw_getReference(key,&pVarSetTemp);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pVarSetST->ImportSubTree(key,pVarSetTemp);
|
|
}
|
|
|
|
pVarSetST->put(GET_BSTR(DCTVS_Security_TranslationMode),
|
|
pVarSet->get(GET_BSTR(DCTVS_Security_TranslationMode)));
|
|
pVarSetST->put(GET_BSTR(DCTVS_Options_NoChange),GET_BSTR(IDS_YES));
|
|
|
|
pVarSetST->put(GET_BSTR(DCTVS_Options_LogLevel),(LONG)0);
|
|
pVarSetST->put(GET_BSTR(DCTVS_Security_BuildCacheFile),strCachePath);
|
|
|
|
// change the log file - the building of the cache file happens behind the scenes
|
|
// so we won't put it in the regular log file because it would cause confusion
|
|
swprintf(logfile,L"%s%s",tempdir,L"BuildCacheFileLog.txt");
|
|
pVarSetST->put(GET_BSTR(DCTVS_Options_Logfile),logfile);
|
|
|
|
//are we using a sID mapping file to perform security translation
|
|
pVarSetST->put(GET_BSTR(DCTVS_AccountOptions_SecurityInputMOT),
|
|
pVarSet->get(GET_BSTR(DCTVS_AccountOptions_SecurityInputMOT)));
|
|
pVarSetST->put(GET_BSTR(DCTVS_AccountOptions_SecurityMapFile),
|
|
pVarSet->get(GET_BSTR(DCTVS_AccountOptions_SecurityMapFile)));
|
|
|
|
MCSEADCTAGENTLib::IDCTAgentPtr pAgent(MCSEADCTAGENTLib::CLSID_DCTAgent);
|
|
|
|
try {
|
|
if ( pAgent == NULL )
|
|
return E_FAIL;
|
|
|
|
_bstr_t jobID;
|
|
BSTR b = NULL;
|
|
|
|
// turn on the alternative log file
|
|
swprintf(logfile, GetMigrationLogPath());
|
|
pVarSetST->put(GET_BSTR(DCTVS_Options_AlternativeLogfile), logfile);
|
|
|
|
hr = pAgent->raw_SubmitJob(pVarSetST,&b);
|
|
|
|
// turn off the alternative log file
|
|
pVarSetST->put(GET_BSTR(DCTVS_Options_AlternativeLogfile),_bstr_t(L""));
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// since this is a local agent, we should go ahead to signal Ok to shut down
|
|
// the reason HRESULT is not checked is that when there is no reference to
|
|
// agent COM server, it will be shut down anyway
|
|
pAgent->raw_SignalOKToShutDown();
|
|
|
|
jobID = b;
|
|
|
|
IVarSetPtr pVarSetStatus; // used to retrieve status of running job
|
|
_bstr_t jobStatus;
|
|
IUnknown * pUnk;
|
|
|
|
// loop until the agent is finished
|
|
do {
|
|
|
|
Sleep(1000);
|
|
|
|
hr = pAgent->QueryJobStatus(jobID,&pUnk);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pVarSetStatus = pUnk;
|
|
jobStatus = pVarSetStatus->get(GET_BSTR(DCTVS_JobStatus));
|
|
pUnk->Release();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
} while ( UStrICmp(jobStatus,GET_STRING(IDS_DCT_Status_Completed))
|
|
&& UStrICmp(jobStatus,GET_STRING(IDS_DCT_Status_Completed_With_Errors)));
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pVarSet->put(GET_BSTR(DCTVS_Accounts_InputFile),strCacheFile);
|
|
pVarSet->put(GET_BSTR(DCTVS_Accounts_WildcardSpec),"");
|
|
err.MsgWrite(0,DCT_MSG_CACHE_FILE_BUILT_S,(WCHAR*)strCacheFile);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// These are TNodeListSortable sorting functions
|
|
|
|
int ServerNodeCompare(TNode const * t1,TNode const * t2)
|
|
{
|
|
TServerNode * n1 = (TServerNode *)t1;
|
|
TServerNode * n2 = (TServerNode *)t2;
|
|
|
|
return UStrICmp(n1->SourceName(),n2->SourceName());
|
|
}
|
|
|
|
int ServerValueCompare(TNode const * t1, void const * val)
|
|
{
|
|
TServerNode * n1 = (TServerNode *)t1;
|
|
WCHAR const * name = (WCHAR const *) val;
|
|
|
|
return UStrICmp(n1->SourceName(),name);
|
|
}
|
|
|
|
// MergeServerList combines the security translation server list in Servers.* with the computer migration
|
|
// server list in MigrateServers.*
|
|
// The combined list is stored in the varset under Servers.* with subkeys specifying which actions to take for
|
|
// each computer
|
|
void
|
|
CDCTDispatcher::MergeServerList(
|
|
IVarSet * pVarSet // in - varset containing list of servers to migrate and translate on
|
|
)
|
|
{
|
|
int ndx = 0;
|
|
WCHAR key[1000];
|
|
_bstr_t text;
|
|
_bstr_t serverName;
|
|
BOOL bNoChange;
|
|
int lastndx = -1;
|
|
long totalsrvs;
|
|
|
|
// if it is in test mode, there is no skipping
|
|
text = pVarSet->get(GET_BSTR(DCTVS_Options_NoChange));
|
|
bNoChange = (!UStrICmp(text, GET_STRING(IDS_YES))) ? TRUE : FALSE;
|
|
|
|
|
|
//get the number of servers in the varset
|
|
totalsrvs = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems));
|
|
|
|
// if there are computers being migrated
|
|
if (totalsrvs > 0)
|
|
{
|
|
//add code to move varset server entries, with SkipDispatch set, to the bottom
|
|
//of the server list and decrease the number of server items by each server
|
|
//to be skipped
|
|
//check each server in the list moving all to be skipped to the end of the list and
|
|
//decreasing the server count for each to be skipped
|
|
for (ndx = 0; ndx < totalsrvs; ndx++)
|
|
{
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),ndx);
|
|
text = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(DCTVSFmt_Servers_D),ndx);
|
|
serverName = pVarSet->get(key);
|
|
|
|
//if the server is not to be skipped, we may have to move it above
|
|
//a server that is being skipped
|
|
if (serverName.length()
|
|
&& (bNoChange || !text || !UStrICmp(text,GET_STRING(IDS_No))))
|
|
{
|
|
//if the last server looked at is not being skipped then we don't
|
|
//need to swap any servers in the list and we can increment the
|
|
//last server not being skipped
|
|
if (lastndx == (ndx - 1))
|
|
{
|
|
lastndx = ndx;
|
|
}
|
|
else //else swap servers in the varset so skipped server comes after
|
|
{ //the one not being skipped
|
|
_bstr_t tempName, tempDnsName, tempNewName, tempChngDom, tempReboot, tempMigOnly;
|
|
long tempRebootDelay;
|
|
_bstr_t skipName, skipDnsName, skipNewName, skipChngDom, skipReboot, skipMigOnly;
|
|
long skipRebootDelay;
|
|
lastndx++; //move to the skipped server that we will swap with
|
|
|
|
//copy skipped server's values to temp
|
|
swprintf(key,GET_STRING(DCTVSFmt_Servers_D),lastndx);
|
|
skipName = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),lastndx);
|
|
skipDnsName = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),lastndx);
|
|
skipNewName = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),lastndx);
|
|
skipChngDom = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),lastndx);
|
|
skipReboot = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),lastndx);
|
|
skipMigOnly = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),lastndx);
|
|
skipRebootDelay = pVarSet->get(key);
|
|
|
|
//copy current, non-skipped, server valuesto second temp
|
|
swprintf(key,GET_STRING(DCTVSFmt_Servers_D),ndx);
|
|
tempName = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),ndx);
|
|
tempDnsName = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),ndx);
|
|
tempNewName = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),ndx);
|
|
tempChngDom = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),ndx);
|
|
tempReboot = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),ndx);
|
|
tempMigOnly = pVarSet->get(key);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),ndx);
|
|
tempRebootDelay = pVarSet->get(key);
|
|
|
|
//place current server's values in place of values for the one
|
|
//being skipped
|
|
swprintf(key,GET_STRING(DCTVSFmt_Servers_D),lastndx);
|
|
pVarSet->put(key,tempName);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),lastndx);
|
|
pVarSet->put(key,tempDnsName);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),lastndx);
|
|
pVarSet->put(key,tempNewName);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),lastndx);
|
|
pVarSet->put(key,tempChngDom);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),lastndx);
|
|
pVarSet->put(key,tempReboot);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),lastndx);
|
|
pVarSet->put(key,tempMigOnly);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),lastndx);
|
|
pVarSet->put(key,tempRebootDelay);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),lastndx);
|
|
pVarSet->put(key,GET_BSTR(IDS_No));
|
|
|
|
//place skipped server's values in place of values for current server
|
|
swprintf(key,GET_STRING(DCTVSFmt_Servers_D),ndx);
|
|
pVarSet->put(key,skipName);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),ndx);
|
|
pVarSet->put(key,skipDnsName);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),ndx);
|
|
pVarSet->put(key,skipNewName);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),ndx);
|
|
pVarSet->put(key,skipChngDom);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),ndx);
|
|
pVarSet->put(key,skipReboot);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),ndx);
|
|
pVarSet->put(key,skipMigOnly);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),ndx);
|
|
pVarSet->put(key,skipRebootDelay);
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),ndx);
|
|
pVarSet->put(key,GET_BSTR(IDS_YES));
|
|
}//end else need to swap with skipped server
|
|
}//end if not skipping dispatch for this server
|
|
}//end for each server in the server list
|
|
//exclude servers to be skipped for dispatch from being included in the server count
|
|
pVarSet->put(GET_BSTR(DCTVS_Servers_NumItems),(long)++lastndx);
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP // ret- HRESULT
|
|
CDCTDispatcher::Process(
|
|
IUnknown * pWorkItem, // in - varset containing job information and list of servers
|
|
IUnknown ** ppResponse, // out- not used
|
|
UINT * pDisposition // out- not used
|
|
)
|
|
{
|
|
// initialize output parameters
|
|
if ( ppResponse )
|
|
{
|
|
(*ppResponse) = NULL;
|
|
}
|
|
HRESULT hr = S_OK;
|
|
IVarSetPtr pVarSetIn = pWorkItem;
|
|
LONG nThreads;
|
|
WCHAR key[100];
|
|
_bstr_t serverName;
|
|
_bstr_t serverNameDns;
|
|
LONG nServers = 0;
|
|
_bstr_t log;
|
|
_bstr_t useTempCredentials;
|
|
BOOL bFatalError = FALSE;
|
|
_bstr_t text;
|
|
WCHAR debugLog[MAX_PATH];
|
|
long bAppend = 0;
|
|
_bstr_t skip;
|
|
_bstr_t sWizard;
|
|
BOOL bSkipSourceSid;
|
|
|
|
|
|
if ( DumpDebugInfo(debugLog) )
|
|
{
|
|
if ( pVarSetIn != NULL )
|
|
{
|
|
// temporarily remove the password fromthe varset, so that we don't write it to the file
|
|
pVarSetIn->DumpToFile(debugLog);
|
|
}
|
|
}
|
|
//get the wizard being run
|
|
sWizard = pVarSetIn->get(GET_BSTR(DCTVS_Options_Wizard));
|
|
if (!UStrICmp(sWizard, L"security"))
|
|
bSkipSourceSid = TRUE;
|
|
else
|
|
bSkipSourceSid = FALSE;
|
|
|
|
nThreads = pVarSetIn->get(GET_BSTR(DCTVS_Options_MaxThreads));
|
|
|
|
text = pVarSetIn->get(GET_BSTR(DCTVS_Options_AppendToLogs));
|
|
if (! UStrICmp(text,GET_STRING(IDS_YES)) )
|
|
{
|
|
bAppend = 1;
|
|
}
|
|
log = pVarSetIn->get(GET_BSTR(DCTVS_Options_DispatchLog));
|
|
err.LogOpen((WCHAR*)log,bAppend);
|
|
|
|
// open the internal log "dispatcher.csv" and try to reserve enough
|
|
// disk space for it
|
|
// if we cannot, agent dispatching fails with an error
|
|
DWORD rc = ERROR_SUCCESS;
|
|
_bstr_t internalLog = pVarSetIn->get(GET_BSTR(DCTVS_Options_DispatchCSV));
|
|
BOOL bLogOpened = errLog.LogOpen((WCHAR*)internalLog,0,0,true);
|
|
if (!bLogOpened)
|
|
rc = GetLastError();
|
|
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
LONG lServers = pVarSetIn->get(GET_BSTR(DCTVS_Servers_NumItems));
|
|
DWORD dwNumOfBytes = sizeof(WCHAR) * (2 * (22 + MAX_PATH) // the first two lines
|
|
+ (22 + 10) // the third line
|
|
+ lServers * 2000 // 2000 WCHAR per server
|
|
);
|
|
//dwNumOfBytes = 1000000000;
|
|
rc = errLog.ExtendSize(dwNumOfBytes);
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(rc);
|
|
errLog.LogClose();
|
|
_bstr_t errMsg = errLog.GetMsgText(DCT_MSG_CANNOT_WRITE_INTERNAL_DISPATCH_LOG_D, hr);
|
|
err.SysMsgWrite(ErrE, HRESULT_CODE(hr),DCT_MSG_CANNOT_WRITE_INTERNAL_DISPATCH_LOG_D, hr);
|
|
return Error((WCHAR*) errMsg, GUID_NULL, hr);
|
|
}
|
|
|
|
// make sure this dispatcher.csv file is written starting from the current file pointer
|
|
errLog.SetWriteOnCurrentPosition(TRUE);
|
|
|
|
// write the log file into dispatcher.csv
|
|
errLog.DbgMsgWrite(0,L"%ls",(WCHAR*)log);
|
|
|
|
// default to 20 threads if the client doesn't specify
|
|
if ( ! nThreads )
|
|
{
|
|
nThreads = 20;
|
|
}
|
|
|
|
// Get the name of the local computer
|
|
DWORD dim = DIM(gComputerName);
|
|
|
|
GetComputerName(gComputerName,&dim);
|
|
|
|
m_pThreadPool = new TJobDispatcher(nThreads);
|
|
if (!m_pThreadPool)
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
// write the common result path into dispatcher.csv
|
|
_bstr_t bstrResultDir = pVarSetIn->get(GET_BSTR(DCTVS_Dispatcher_ResultPath));
|
|
errLog.DbgMsgWrite(0,L"%ls",(WCHAR*)bstrResultDir);
|
|
|
|
// check whether it is account reference report
|
|
BOOL bAccountReferenceReport = FALSE;
|
|
_bstr_t bstrGenerateReport = pVarSetIn->get(GET_BSTR(DCTVS_Reports_Generate));
|
|
_bstr_t bstrAccountRefReport = pVarSetIn->get(GET_BSTR(DCTVS_Reports_AccountReferences));
|
|
if (!UStrICmp(bstrGenerateReport, GET_STRING(IDS_YES))
|
|
&& !UStrICmp(bstrAccountRefReport,GET_STRING(IDS_YES)))
|
|
bAccountReferenceReport = TRUE;
|
|
|
|
//
|
|
// only generate a new cache file if migration task is not the
|
|
// retry task as the retry task uses persisted cache file(s)
|
|
// a cache file is not necessary if it is account reference report
|
|
//
|
|
if (UStrICmp(sWizard, L"retry") != 0 && !bAccountReferenceReport)
|
|
{
|
|
// Build an input file for the ST cache, to send to each server
|
|
hr = BuildInputFile(pVarSetIn);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
err.SysMsgWrite(ErrE,HRESULT_CODE(hr),DCT_MSG_CACHE_CONSTRUCTION_FAILED);
|
|
bFatalError = TRUE;
|
|
}
|
|
}
|
|
|
|
// Split out the remotable tasks for each server
|
|
// Get the sids for the source and target domains
|
|
// note: if a sid mapping file is used, we do not need to get sids for source and target domains
|
|
//
|
|
// In case of account reference report, we need to set up the dctcache on the client machine
|
|
// as if we're using migrated object table because the account reference report relies on
|
|
// TRidCache::Lookup (called when MOT is used) which requires valid source and target sids.
|
|
// This logic is added because with a fresh install of database, DCTVS_AccountOptions_SecurityInputMOT
|
|
// is not set for account reference report.
|
|
_bstr_t bstrUseMOT = pVarSetIn->get(GET_BSTR(DCTVS_AccountOptions_SecurityInputMOT));
|
|
if (bAccountReferenceReport || !UStrICmp(bstrUseMOT,GET_STRING(IDS_YES)))
|
|
{
|
|
PSID pSidSrc = NULL;
|
|
PSID pSidTgt = NULL;
|
|
|
|
_bstr_t source = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomain));
|
|
_bstr_t target = pVarSetIn->get(GET_BSTR(DCTVS_Options_TargetDomain));
|
|
|
|
//if security translation, retrieve source sid and convert so
|
|
//that it can be convert back below
|
|
if (bSkipSourceSid)
|
|
{
|
|
_bstr_t sSid = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomainSid));
|
|
pSidSrc = SidFromString((WCHAR*)sSid);
|
|
}
|
|
else //else get the sid now
|
|
GetSidForDomain((WCHAR*)source,&pSidSrc);
|
|
GetSidForDomain((WCHAR*)target,&pSidTgt);
|
|
|
|
if ( pSidSrc && pSidTgt )
|
|
{
|
|
WCHAR txtSid[200];
|
|
DWORD lenTxt = DIM(txtSid);
|
|
|
|
if ( GetTextualSid(pSidSrc,txtSid,&lenTxt) )
|
|
{
|
|
pVarSetIn->put(GET_BSTR(DCTVS_Options_SourceDomainSid),txtSid);
|
|
}
|
|
lenTxt = DIM(txtSid);
|
|
if ( GetTextualSid(pSidTgt,txtSid,&lenTxt) )
|
|
{
|
|
pVarSetIn->put(GET_BSTR(DCTVS_Options_TargetDomainSid),txtSid);
|
|
}
|
|
FreeSid(pSidSrc);
|
|
FreeSid(pSidTgt);
|
|
}
|
|
else
|
|
{
|
|
if ( source.length() && ! pSidSrc )
|
|
{
|
|
err.MsgWrite(ErrE,DCT_MSG_DOMAIN_SID_NOT_FOUND_S,(WCHAR*)source);
|
|
bFatalError = TRUE;
|
|
}
|
|
else if ( target.length() && ! pSidTgt )
|
|
{
|
|
err.MsgWrite(ErrE,DCT_MSG_DOMAIN_SID_NOT_FOUND_S,(WCHAR*)target);
|
|
bFatalError = TRUE;
|
|
}
|
|
}
|
|
}
|
|
MergeServerList(pVarSetIn);
|
|
|
|
TNodeList * fileList = new TNodeList;
|
|
if (!fileList)
|
|
{
|
|
delete m_pThreadPool;
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
// Build list of files to install for plug-ins (if any)
|
|
// but for retry case, we should not build the plugin file list now
|
|
if (UStrICmp(sWizard, L"retry") != 0)
|
|
hr = BuildPlugInFileList(fileList,pVarSetIn);
|
|
|
|
if(!SUCCEEDED(hr))
|
|
{
|
|
|
|
delete m_pThreadPool;
|
|
delete fileList;
|
|
return hr;
|
|
}
|
|
|
|
// Make a copy of the varset with the server lists removed,
|
|
// so we don't have to copy the entire server list for each agent
|
|
gCS.Enter();
|
|
IVarSet * pTemp = NULL;
|
|
IVarSetPtr pVarSetTemp(CLSID_VarSet);
|
|
|
|
hr = pVarSetTemp->ImportSubTree(_bstr_t(L""),pVarSetIn);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pVarSetTemp->raw_getReference(SysAllocString(L"MigrateServers"),&pTemp);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pTemp->Clear();
|
|
pTemp->Release();
|
|
pTemp = NULL;
|
|
}
|
|
hr = pVarSetTemp->raw_getReference(SysAllocString(L"Servers"),&pTemp);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pTemp->Clear();
|
|
pTemp->Release();
|
|
pTemp = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bFatalError = TRUE;
|
|
}
|
|
gCS.Leave();
|
|
|
|
m_startFailedVector.clear();
|
|
|
|
// log the number of agents to be dispatched into dispatcher.csv
|
|
LONG nServerCount = pVarSetIn->get(GET_BSTR(DCTVS_Servers_NumItems));
|
|
if ( nServerCount && ! bFatalError )
|
|
{
|
|
err.MsgWrite(0,DCT_MSG_DISPATCH_SERVER_COUNT_D,nServerCount);
|
|
errLog.DbgMsgWrite(0,L"%ld",nServerCount);
|
|
}
|
|
else
|
|
{
|
|
// no agent will be dispatched
|
|
err.MsgWrite(0,DCT_MSG_DISPATCH_SERVER_COUNT_D,0);
|
|
errLog.DbgMsgWrite(0,L"%ld",0);
|
|
}
|
|
|
|
// if it is in test mode, there is no skipping
|
|
text = pVarSetIn->get(GET_BSTR(DCTVS_Options_NoChange));
|
|
BOOL bNoChange = (!UStrICmp(text, GET_STRING(IDS_YES))) ? TRUE : FALSE;
|
|
|
|
// reset the index for servers
|
|
nServers = 0;
|
|
|
|
while (nServers < nServerCount)
|
|
{
|
|
if ( bFatalError )
|
|
{
|
|
break;
|
|
}
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),nServers);
|
|
skip = pVarSetIn->get(key);
|
|
|
|
swprintf(key,GET_STRING(DCTVSFmt_Servers_D),nServers);
|
|
serverName = pVarSetIn->get(key);
|
|
|
|
swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),nServers);
|
|
serverNameDns = pVarSetIn->get(key);
|
|
|
|
// to test whether to skip, use the same logic as in mergeserverlist
|
|
if ((serverName.length())
|
|
&& (bNoChange || !skip || !UStrICmp(skip,GET_STRING(IDS_No))))
|
|
{
|
|
IVarSetPtr pVS(CLSID_VarSet);
|
|
|
|
InstallJobInfo * pInfo = new InstallJobInfo;
|
|
if (!pInfo)
|
|
{
|
|
delete fileList;
|
|
delete m_pThreadPool;
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
if ( pVS == NULL )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
swprintf(key, L"Servers.%ld.JobFile", nServers);
|
|
_bstr_t file = pVarSetIn->get(key);
|
|
|
|
// Set up job structure
|
|
pInfo->pVarSetList = pVarSetIn;
|
|
pInfo->pVarSet = pVarSetTemp;
|
|
pInfo->serverName = serverName;
|
|
pInfo->serverNameDns = serverNameDns;
|
|
pInfo->ndx = nServers;
|
|
pInfo->pPlugInFileList = fileList;
|
|
pInfo->pStartFailedVector = &m_startFailedVector;
|
|
pInfo->pFailureDescVector = &m_failureDescVector;
|
|
pInfo->pStartedVector = &m_startedVector;
|
|
pInfo->pJobidVector = &m_jobidVector;
|
|
pInfo->hMutex = m_hMutex;
|
|
pInfo->nErrCount = 0;
|
|
if ( file.length() )
|
|
{
|
|
pInfo->jobfile = file;
|
|
}
|
|
err.MsgWrite(0,DCT_MSG_DISPATCHING_TO_SERVER_S,pInfo->GetServerName());
|
|
errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",(WCHAR*)pInfo->GetServerName(),L"WillInstall",0);
|
|
m_pThreadPool->SubmitJob(&DoInstall,(void *)pInfo);
|
|
nServers++;
|
|
}
|
|
}
|
|
|
|
// launch a thread to wait for all jobs to finish, then clean up and exit
|
|
WaitInfo* wInfo = new WaitInfo;
|
|
if (!wInfo)
|
|
{
|
|
delete fileList;
|
|
delete m_pThreadPool;
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
wInfo->ppPool = &m_pThreadPool;
|
|
wInfo->pUnknown = NULL;
|
|
wInfo->pPlugInFileList = fileList;
|
|
|
|
QueryInterface(IID_IUnknown,(LPVOID*)&(wInfo->pUnknown));
|
|
|
|
DWORD id = 0;
|
|
HANDLE waitHandle = CreateThread(NULL,0,&Wait,(void *)wInfo,0,&id);
|
|
|
|
if(waitHandle)
|
|
{
|
|
CloseHandle(waitHandle);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDCTDispatcher::AllAgentsStarted(long *bAllAgentsStarted)
|
|
{
|
|
*bAllAgentsStarted = m_pThreadPool == NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
SAFEARRAY* MakeSafeArray(std::vector<CComBSTR>& stVector)
|
|
{
|
|
SAFEARRAYBOUND rgsabound[1];
|
|
rgsabound[0].lLbound = 0;
|
|
rgsabound[0].cElements = stVector.size();
|
|
|
|
SAFEARRAY FAR* psa = SafeArrayCreate(VT_BSTR, 1, rgsabound);
|
|
if (psa)
|
|
{
|
|
std::vector<CComBSTR>::iterator iter = stVector.begin();
|
|
for(long i=0; iter != stVector.end(); ++iter, ++i)
|
|
{
|
|
_ASSERTE(*iter && *iter != L"");
|
|
BSTR insert = (*iter).Copy();
|
|
if (insert == NULL || FAILED(SafeArrayPutElement(psa, &i, (void*)insert)))
|
|
{
|
|
if (insert)
|
|
SysFreeString(insert);
|
|
SafeArrayDestroy(psa);
|
|
psa = NULL;
|
|
break;
|
|
}
|
|
}
|
|
stVector.clear();
|
|
}
|
|
return psa;
|
|
}
|
|
|
|
STDMETHODIMP CDCTDispatcher::GetStartedAgentsInfo(long* bAllAgentsStarted, SAFEARRAY** ppbstrStartedAgents, SAFEARRAY** ppbstrJobid, SAFEARRAY** ppbstrFailedAgents, SAFEARRAY** ppbstrFailureDesc)
|
|
{
|
|
*bAllAgentsStarted = m_pThreadPool == NULL;
|
|
|
|
// DWORD res = ::WaitForSingleObject(m_hMutex, 30000);
|
|
::WaitForSingleObject(m_hMutex, 30000);
|
|
*ppbstrFailedAgents = MakeSafeArray(m_startFailedVector);
|
|
*ppbstrFailureDesc = MakeSafeArray(m_failureDescVector);
|
|
|
|
_ASSERTE(m_startedVector.size() == m_jobidVector.size());
|
|
*ppbstrStartedAgents = MakeSafeArray(m_startedVector);
|
|
*ppbstrJobid = MakeSafeArray(m_jobidVector);
|
|
::ReleaseMutex(m_hMutex);
|
|
|
|
return S_OK;
}
|