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.

1585 lines
60 KiB

  1. /*---------------------------------------------------------------------------
  2. File: DCTDispatcher.cpp
  3. Comments: Implementation of dispatcher COM object. Remotely installs and
  4. launches the DCT Agent on remote computers.
  5. The CDCTDispatcher class implements the COM interface for the dispatcher.
  6. It takes a varset, containing a list of machines, and dispatches agents to
  7. each specified machine.
  8. A job file (varset persisted to a file) is created for each agent, and
  9. necessary initialization configuration (such as building an account mapping file for
  10. security translation) is done. The DCDTDispatcher class instantiates a
  11. thread pool (CPooledDispatch), and uses the CDCTInstaller class to remotely
  12. install and start the agent service on each machine.
  13. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
  14. Proprietary and confidential to Mission Critical Software, Inc.
  15. REVISION LOG ENTRY
  16. Revision By: Christy Boles
  17. Revised on 02/15/99 11:23:57
  18. ---------------------------------------------------------------------------
  19. */// DCTDispatcher.cpp : Implementation of CDCTDispatcher
  20. #include "stdafx.h"
  21. #include "resource.h"
  22. #include <locale.h>
  23. //#include "..\Common\Include\McsDispatcher.h"
  24. #include "Dispatch.h"
  25. #include "DDisp.h"
  26. #include "DInst.h"
  27. #include "Common.hpp"
  28. #include "ErrDct.hpp"
  29. #include "UString.hpp"
  30. #include "EaLen.hpp"
  31. #include "Cipher.hpp"
  32. #include "TNode.hpp"
  33. #include "TPool.h" // Thread pool for dispatching jobs
  34. #include "LSAUtils.h"
  35. #include "TxtSid.h"
  36. #include "sd.hpp"
  37. #include "SecObj.hpp"
  38. #include "BkupRstr.hpp"
  39. #include "TReg.hpp"
  40. #include "ResStr.h"
  41. #include "TaskChk.h"
  42. #include "CommaLog.hpp"
  43. #include "TInst.h"
  44. #include <lm.h>
  45. #include "GetDcName.h"
  46. /////////////////////////////////////////////////////////////////////////////
  47. // CDCTDispatcher
  48. //#import "\bin\McsEADCTAgent.tlb" named_guids
  49. //#include "..\AgtSvc\AgSvc.h"
  50. #import "Engine.tlb" named_guids
  51. #include "AgSvc.h"
  52. #include "AgSvc_c.c"
  53. #include "AgRpcUtl.h"
  54. //#import "\bin\McsDctWorkerObjects.tlb"
  55. //#include "..\Common\Include\McsPI.h"
  56. #import "WorkObj.tlb"
  57. #include "McsPI.h"
  58. #include "McsPI_i.c"
  59. #ifdef _DEBUG
  60. #define new DEBUG_NEW
  61. #undef THIS_FILE
  62. static char THIS_FILE[] = __FILE__;
  63. #endif
  64. TErrorDct errLog; // used to write dispatch log that is read by the agent monitor
  65. TErrorDct errTrace;
  66. TCriticalSection gCS;
  67. // TServerNodes make up an internally used list of machines to install to
  68. class TServerNode : public TNode
  69. {
  70. WCHAR sourceName[LEN_Computer];
  71. WCHAR targetName[LEN_Computer];
  72. BOOL bTranslate;
  73. BOOL bChangeDomain;
  74. BOOL bReboot;
  75. DWORD dwRebootDelay;
  76. public:
  77. TServerNode() { sourceName[0] = 0; targetName[0] = 0; bTranslate = FALSE; bChangeDomain = FALSE; bReboot= FALSE; dwRebootDelay = 0; }
  78. WCHAR const * SourceName() { return sourceName; }
  79. WCHAR const * TargetName() { return targetName; }
  80. BOOL Translate() { return bTranslate; }
  81. BOOL Reboot() { return bReboot; }
  82. BOOL ChangeDomain() { return bChangeDomain; }
  83. DWORD RebootDelay() { return dwRebootDelay; }
  84. void SourceName(WCHAR const * src) { safecopy(sourceName,src); }
  85. void TargetName(WCHAR const * tgt) { safecopy(targetName,tgt); }
  86. void Translate(BOOL v) { bTranslate = v; }
  87. void ChangeDomain(BOOL v) { bChangeDomain = v; }
  88. void Reboot(BOOL v) { bReboot = v; }
  89. void RebootDelay(DWORD d) { dwRebootDelay = d; }
  90. };
  91. extern
  92. TErrorDct err;
  93. // defined in CkPlugIn.cpp
  94. BOOL IsValidPlugIn(IMcsDomPlugIn * pPlugIn);
  95. BOOL // ret - TRUE if need to dump debug information
  96. DumpDebugInfo(
  97. WCHAR * filename // out - where to dump debug information
  98. )
  99. {
  100. DWORD rc = 0;
  101. BOOL bFound = FALSE;
  102. TRegKey key;
  103. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  104. if ( ! rc )
  105. {
  106. rc = key.ValueGetStr(L"DispatchVarSet",filename,MAX_PATH);
  107. if ( ! rc )
  108. {
  109. if ( *filename )
  110. bFound = TRUE;
  111. }
  112. }
  113. return bFound;
  114. }
  115. HRESULT
  116. BuildPlugInFileList(
  117. TNodeList * pList, // i/o- list that files needed by plug-ins wil be added to
  118. IVarSet * pVarSet // in - varset containing list of plug-ins to query
  119. )
  120. {
  121. // for now, build a list of all plug-ins, and add it to the varset
  122. MCSDCTWORKEROBJECTSLib::IPlugInInfoPtr pPtr;
  123. SAFEARRAY * pArray = NULL;
  124. HRESULT hr = S_OK;
  125. LONG bound;
  126. LONG ndx[1];
  127. WCHAR key[LEN_Path];
  128. hr = pPtr.CreateInstance(__uuidof(MCSDCTWORKEROBJECTSLib::PlugInInfo));
  129. _bstr_t bStrGuid;
  130. swprintf(key,GET_STRING(IDS_DCTVS_Fmt_PlugIn_D),0);
  131. bStrGuid = pVarSet->get(key);
  132. if (! bStrGuid.length() )
  133. {
  134. // if no plug-ins are specified, use the ones in the plug-ins directory
  135. if ( SUCCEEDED(hr) )
  136. {
  137. hr = pPtr->raw_EnumeratePlugIns(&pArray);
  138. }
  139. if ( SUCCEEDED(hr) )
  140. {
  141. SafeArrayGetUBound(pArray,1,&bound);
  142. for ( ndx[0] = 0 ; ndx[0] <= bound ; ndx[0]++ )
  143. {
  144. BSTR val = NULL;
  145. SafeArrayGetElement(pArray,ndx,&val);
  146. swprintf(key,GET_STRING(IDS_DCTVS_Fmt_PlugIn_D),ndx[0]);
  147. pVarSet->put(key,val);
  148. SysFreeString(val);
  149. }
  150. SafeArrayDestroy(pArray);
  151. pArray = NULL;
  152. }
  153. }
  154. // enumerate the plug-ins specified in the varset, and make a list of their needed files
  155. int nRegFiles = 0;
  156. for ( int i = 0 ; ; i++ )
  157. {
  158. swprintf(key,GET_STRING(IDS_DCTVS_Fmt_PlugIn_D),i);
  159. bStrGuid = pVarSet->get(key);
  160. if ( bStrGuid.length() == 0 )
  161. break;
  162. if(!_wcsicmp(bStrGuid, L"None"))
  163. continue;
  164. IMcsDomPlugIn * pPlugIn = NULL;
  165. SAFEARRAY * pFileArray = NULL;
  166. TFileNode * pNode;
  167. CLSID clsid;
  168. hr = CLSIDFromString(bStrGuid,&clsid);
  169. if ( SUCCEEDED(hr) )
  170. {
  171. hr = CoCreateInstance(clsid,NULL,CLSCTX_ALL,IID_IMcsDomPlugIn,(void**)&pPlugIn);
  172. }
  173. if ( SUCCEEDED(hr) )
  174. {
  175. if ( IsValidPlugIn(pPlugIn) )
  176. {
  177. hr = pPlugIn->GetRequiredFiles(&pFileArray);
  178. if ( SUCCEEDED(hr) )
  179. {
  180. SafeArrayGetUBound(pFileArray,1,&bound);
  181. for ( ndx[0] = 0 ; ndx[0] <= bound ; ndx[0]++ )
  182. {
  183. BSTR val = NULL;
  184. SafeArrayGetElement(pFileArray,ndx,&val);
  185. pNode = new TFileNode(val);
  186. pList->InsertBottom(pNode);
  187. SysFreeString(val);
  188. }
  189. SafeArrayDestroy(pFileArray);
  190. pFileArray = NULL;
  191. }
  192. hr = pPlugIn->GetRegisterableFiles(&pFileArray);
  193. if ( SUCCEEDED(hr) )
  194. {
  195. SafeArrayGetUBound(pFileArray,1,&bound);
  196. for (ndx[0] = 0; ndx[0] <= bound ; ndx[0]++ )
  197. {
  198. BSTR val = NULL;
  199. SafeArrayGetElement(pFileArray,ndx,&val);
  200. swprintf(key,GET_STRING(IDS_DCTVSFmt_PlugIn_RegisterFiles_D),nRegFiles);
  201. pVarSet->put(key,val);
  202. SysFreeString(val);
  203. nRegFiles++;
  204. }
  205. SafeArrayDestroy(pFileArray);
  206. pFileArray = NULL;
  207. }
  208. }
  209. pPlugIn->Release();
  210. }
  211. // we should bail out immediately if error occurs
  212. if(FAILED(hr))
  213. {
  214. return hr;
  215. }
  216. }
  217. return hr;
  218. }
  219. // InstallJobInfo defines a Domain Migration 'job' to be installed and launched
  220. struct InstallJobInfo
  221. {
  222. IVarSetPtr pVarSetList; // varset defining the server list
  223. IVarSetPtr pVarSet; // VarSet defining the job to run
  224. _bstr_t serverName; // computer to install and run on
  225. _bstr_t serverNameDns; // computer to install and run on
  226. long ndx; // index of this server in the server list
  227. TNodeList * pPlugInFileList; // list of files to install for plug-ins
  228. std::vector<CComBSTR>* pStartFailedVector;
  229. std::vector<CComBSTR>* pFailureDescVector;
  230. std::vector<CComBSTR>* pStartedVector;
  231. std::vector<CComBSTR>* pJobidVector;
  232. HANDLE hMutex;
  233. _bstr_t jobfile; // uses the specified job file instead of creating one
  234. int nErrCount;
  235. PCTSTR GetServerName()
  236. {
  237. return serverNameDns.length() ? serverNameDns : serverName;
  238. }
  239. PCTSTR GetServerNameDns()
  240. {
  241. return serverNameDns;
  242. }
  243. PCTSTR GetServerNameFlat()
  244. {
  245. return serverName;
  246. }
  247. };
  248. // WaitInfo is used to pass information to a thread that waits and does cleanup
  249. // after all the Dispatcher's work is done
  250. struct WaitInfo
  251. {
  252. IUnknown * pUnknown; // IUnknown interface to the DCTDisptacher object
  253. TJobDispatcher **ppPool; // pointer to thread pool performing the tasks (installations)
  254. TNodeList * pPlugInFileList; // pointer to plug-in files list that will need to be freed
  255. };
  256. WCHAR gComputerName[LEN_Computer] = L""; // name of local computer
  257. // Calls DCTAgentService to start the Domain Migration Job on a remote computer
  258. DWORD // ret- OS return code
  259. StartJob(
  260. WCHAR const * serverName, // in - computer to start job on
  261. WCHAR const * fullname, // in - full path (including filename) to file containing the job's VarSet
  262. WCHAR const * filename, // in - filename of file containing the varset for the job
  263. _bstr_t& strJobid
  264. )
  265. {
  266. DWORD rc = 0;
  267. handle_t hBinding = NULL;
  268. WCHAR * sBinding = NULL;
  269. WCHAR jobGUID[LEN_Guid];
  270. WCHAR passwordW[1] = { 0 };
  271. rc = EaxBindCreate(serverName,&hBinding,&sBinding,TRUE);
  272. if ( rc )
  273. {
  274. err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_BIND_FAILED_SD, serverName,rc);
  275. }
  276. if( ! rc )
  277. {
  278. RpcTryExcept
  279. {
  280. // the job file has been copied to the remote computer
  281. // during the installation
  282. rc = EaxcSubmitJob(hBinding,filename,passwordW,jobGUID);
  283. if ( ! rc )
  284. {
  285. err.MsgWrite(0,DCT_MSG_AGENT_JOB_STARTED_SSS,serverName,filename,jobGUID);
  286. strJobid = jobGUID;
  287. }
  288. else
  289. {
  290. err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
  291. }
  292. }
  293. RpcExcept(1)
  294. {
  295. rc = RpcExceptionCode();
  296. if ( rc != RPC_S_SERVER_UNAVAILABLE )
  297. {
  298. err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
  299. }
  300. }
  301. RpcEndExcept
  302. if ( rc == RPC_S_SERVER_UNAVAILABLE )
  303. {
  304. // maybe the agent hasn't started up yet for some reason
  305. for ( int tries = 0 ; tries < 6 ; tries++ )
  306. {
  307. Sleep(5000); // wait a few seconds and try again
  308. RpcTryExcept
  309. {
  310. rc = EaxcSubmitJob(hBinding,filename,passwordW,jobGUID);
  311. if ( ! rc )
  312. {
  313. err.MsgWrite(0,DCT_MSG_AGENT_JOB_STARTED_SSS,serverName,filename,jobGUID);
  314. strJobid = jobGUID;
  315. break;
  316. }
  317. else
  318. {
  319. if ( tries == 5 )
  320. err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
  321. }
  322. }
  323. RpcExcept(1)
  324. {
  325. rc = RpcExceptionCode();
  326. if ( tries == 5 )
  327. err.SysMsgWrite(ErrE,rc,DCT_MSG_AGENT_JOB_START_FAILED_SSD,serverName,filename,rc);
  328. }
  329. RpcEndExcept
  330. }
  331. }
  332. }
  333. if ( ! rc )
  334. {
  335. // if the job was started successfully, remove the job file
  336. if ( ! MoveFileEx(fullname,NULL, MOVEFILE_DELAY_UNTIL_REBOOT) )
  337. {
  338. // DWORD rc2 = GetLastError();
  339. }
  340. }
  341. // this indicates whether the server was started
  342. if ( ! rc )
  343. {
  344. errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld,%ls,%ls",serverName,L"Start",rc,filename,jobGUID);
  345. }
  346. else
  347. {
  348. errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",serverName,L"Start",rc);
  349. }
  350. return rc;
  351. }
  352. // Gets the domain sid for the specified domain
  353. BOOL // ret- TRUE if successful
  354. GetSidForDomain(
  355. LPWSTR DomainName, // in - name of domain to get SID for
  356. PSID * pDomainSid // out- SID for domain, free with FreeSid
  357. )
  358. {
  359. PSID pSid = NULL;
  360. DWORD rc = 0;
  361. _bstr_t domctrl;
  362. if ( DomainName[0] != L'\\' )
  363. {
  364. rc = GetAnyDcName5(DomainName, domctrl);
  365. }
  366. if ( ! rc )
  367. {
  368. rc = GetDomainSid(domctrl,&pSid);
  369. }
  370. (*pDomainSid) = pSid;
  371. return ( pSid != NULL);
  372. }
  373. // Set parameters in the varset that are specific to this particular computer
  374. void
  375. SetupVarSetForJob(
  376. InstallJobInfo * pInfo, // structure defining job
  377. IVarSet * pVarSet, // varset describing job
  378. WCHAR const * uncname, // UNC path for results directory
  379. WCHAR const * filename, // UNC path for results file for this job
  380. WCHAR const * relativeFileName, // relative results file name for this job
  381. BOOL bUpdate = FALSE // whether it is just to update the varset (for retry)
  382. )
  383. {
  384. WCHAR uncresult[MAX_PATH];
  385. WCHAR serverName[MAX_PATH];
  386. WCHAR relativeResultFileName[MAX_PATH];
  387. _bstr_t text;
  388. // Set server-specific parameters in the varset
  389. swprintf(uncresult,L"%s.result",filename);
  390. swprintf(relativeResultFileName,L"%s.result",relativeFileName);
  391. swprintf(serverName,L"\\\\%s",gComputerName);
  392. pVarSet->put(GET_BSTR(DCTVS_Options_ResultFile),uncresult);
  393. pVarSet->put(GET_BSTR(DCTVS_Options_RelativeResultFileName), relativeResultFileName);
  394. // this part is only necessary when we are not just trying to update
  395. // the varset
  396. if (!bUpdate)
  397. {
  398. pVarSet->put(GET_BSTR(DCTVS_Options_Credentials_Server),serverName);
  399. pVarSet->put(GET_BSTR(DCTVS_Options_DeleteFileAfterLoad),GET_BSTR(IDS_YES));
  400. pVarSet->put(GET_BSTR(DCTVS_Options_RemoveAgentOnCompletion),GET_BSTR(IDS_YES));
  401. pVarSet->put(GET_BSTR(DCTVS_Options_LogToTemp),GET_BSTR(IDS_YES));
  402. pVarSet->put(GET_BSTR(DCTVS_Server_Index), CComVariant((long)pInfo->ndx));
  403. text = pVarSet->get(GET_BSTR(DCTVS_GatherInformation_UserRights));
  404. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  405. {
  406. swprintf(uncresult,L"%s.userrights",filename);
  407. pVarSet->put(GET_BSTR(DCTVS_GatherInformation_UserRights),uncresult);
  408. }
  409. }
  410. text = pVarSet->get(GET_BSTR(DCTVS_Security_ReportAccountReferences));
  411. if ( ! UStrICmp(text,GET_STRING(IDS_YES)) )
  412. {
  413. swprintf(uncresult,L"%s.secrefs",filename);
  414. swprintf(relativeResultFileName, L"%s.secrefs", relativeFileName);
  415. pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences),uncresult);
  416. pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferencesRelativeFileName), relativeResultFileName);
  417. }
  418. else
  419. {
  420. pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences), L"");
  421. pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferencesRelativeFileName), L"");
  422. }
  423. // this part is only necessary when we are not just trying to update
  424. // the varset
  425. if (!bUpdate)
  426. pVarSet->put(GET_BSTR(DCTVS_Options_LocalProcessingOnly),GET_BSTR(IDS_YES));
  427. }
  428. // Entry point for thread, waits until all agents are installed and started,
  429. // then cleans up and exits
  430. ULONG __stdcall // ret- returns 0
  431. Wait(
  432. void * arg // in - WaitInfo structure containing needed pointers
  433. )
  434. {
  435. WaitInfo * w = (WaitInfo*)arg;
  436. SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
  437. // wait for all jobs to finish
  438. (*(w->ppPool))->WaitForCompletion();
  439. if ( w->pUnknown )
  440. w->pUnknown->Release();
  441. // delete the plug-in file list
  442. TNodeListEnum tEnum;
  443. TFileNode * fNode;
  444. TFileNode * fNext;
  445. for ( fNode = (TFileNode*)tEnum.OpenFirst(w->pPlugInFileList); fNode; fNode = fNext )
  446. {
  447. fNext = (TFileNode*)tEnum.Next();
  448. w->pPlugInFileList->Remove(fNode);
  449. delete fNode;
  450. }
  451. tEnum.Close();
  452. delete w->pPlugInFileList;
  453. delete *(w->ppPool);
  454. *(w->ppPool) = NULL;
  455. err.MsgWrite(0,DCT_MSG_DISPATCHER_DONE);
  456. errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",L"All",L"Finished",0);
  457. err.LogClose();
  458. errLog.LogClose();
  459. return 0;
  460. }
  461. // Thread entry point, installs and starts agent on a single computer
  462. ULONG __stdcall // ret- HRESULT error code
  463. DoInstall(
  464. void * arg // in - InstallJobInfo structure
  465. )
  466. {
  467. SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
  468. HRESULT hr = S_OK;
  469. InstallJobInfo * pInfo = (InstallJobInfo*)arg;
  470. _bstr_t strJobid;
  471. _bstr_t strFailureDesc(GET_STRING(IDS_START_FAILED));
  472. BOOL bErrLogged = FALSE; // indicates whether the error in dispatching
  473. // has been written into the dispatcher.csv
  474. if(pInfo->nErrCount == 0)
  475. hr = CoInitializeEx(0,COINIT_MULTITHREADED );
  476. if ( SUCCEEDED(hr) )
  477. {
  478. IWorkNode * pInstaller = NULL;
  479. IVarSetPtr pVarSet(CLSID_VarSet);
  480. WCHAR filename[MAX_PATH];
  481. WCHAR relativeFileName[MAX_PATH];
  482. WCHAR tempdir[MAX_PATH];
  483. WCHAR key[MAX_PATH];
  484. if ( pVarSet == NULL )
  485. {
  486. if(pInfo->nErrCount == 0)
  487. CoUninitialize();
  488. hr = E_FAIL;
  489. }
  490. if (SUCCEEDED(hr))
  491. {
  492. DWORD uniqueNumber = (LONG)pInfo->pVarSet->get(GET_BSTR(DCTVS_Options_UniqueNumberForResultsFile));
  493. _bstr_t bstrResultPath = pInfo->pVarSet->get(GET_BSTR(DCTVS_Dispatcher_ResultPath));
  494. // Copy the common information from the source varset
  495. gCS.Enter();
  496. // pInfo->pVarSet contains all the information except the server list
  497. // we don't want to copy the server list each time, so we create our new varset from pInfo->pVarSet
  498. pVarSet->ImportSubTree("",pInfo->pVarSet);
  499. gCS.Leave();
  500. // Set the server-specific data in the varset
  501. swprintf(key,GET_BSTR(IDS_DCTVSFmt_Servers_RenameTo_D),pInfo->ndx);
  502. // pInfo->pVarSetList contains the entire varset including the server list
  503. _bstr_t text = pInfo->pVarSetList->get(key);
  504. if ( text.length() )
  505. {
  506. pVarSet->put(GET_BSTR(DCTVS_LocalServer_RenameTo),text);
  507. }
  508. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),pInfo->ndx);
  509. text = pInfo->pVarSetList->get(key);
  510. if ( text.length() )
  511. {
  512. pVarSet->put(GET_BSTR(DCTVS_LocalServer_ChangeDomain),text);
  513. }
  514. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),pInfo->ndx);
  515. text = pInfo->pVarSetList->get(key);
  516. pVarSet->put(GET_BSTR(DCTVS_LocalServer_MigrateOnly),text);
  517. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),pInfo->ndx);
  518. text = pInfo->pVarSetList->get(key);
  519. if ( text.length() )
  520. {
  521. pVarSet->put(GET_BSTR(DCTVS_LocalServer_Reboot),text);
  522. LONG delay;
  523. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),pInfo->ndx);
  524. delay = pInfo->pVarSetList->get(key);
  525. if ( delay )
  526. {
  527. pVarSet->put(GET_BSTR(DCTVS_LocalServer_RebootDelay),delay);
  528. }
  529. }
  530. // remove the password from the varset, so that we are not writing it
  531. // to a file in plain text. Instead, it will be passed to the agent service
  532. // when the job is submitted
  533. pVarSet->put(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password),"");
  534. if ( ! uniqueNumber )
  535. {
  536. uniqueNumber = GetTickCount();
  537. }
  538. MCSASSERT(bstrResultPath.length());
  539. safecopy(tempdir,(WCHAR*)bstrResultPath);
  540. pInstaller = new CComObject<CDCTInstaller>;
  541. if (pInstaller)
  542. {
  543. pInstaller->AddRef();
  544. ((CDCTInstaller*)pInstaller)->SetFileList(pInfo->pPlugInFileList);
  545. }
  546. else
  547. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  548. if ( SUCCEEDED(hr) )
  549. {
  550. _bstr_t strCacheFile;
  551. BOOL bJobFileAlreadyExists = (pInfo->jobfile.length() == 0) ? FALSE : TRUE;
  552. int relFilenameLen = sizeof(relativeFileName)/sizeof(relativeFileName[0]);
  553. int absFilenameLen = sizeof(filename)/sizeof(filename[0]);
  554. // figure out filename and relativeFileName
  555. // make sure their lengths will be less than MAX_PATH
  556. // otherwise, the mismatch of two names will cause agents to fail later one
  557. if (bJobFileAlreadyExists)
  558. {
  559. WCHAR* path = (WCHAR*) pInfo->jobfile;
  560. WCHAR jobFilename[_MAX_FNAME];
  561. _wsplitpath(path, NULL, NULL, jobFilename, NULL);
  562. if (wcslen(jobFilename) < relFilenameLen
  563. && wcslen(jobFilename) + wcslen(tempdir) < absFilenameLen)
  564. {
  565. wcscpy(relativeFileName, jobFilename);
  566. swprintf(filename,L"%s%s",tempdir,relativeFileName);
  567. }
  568. else
  569. hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
  570. }
  571. else
  572. {
  573. WCHAR sNumber[20];
  574. swprintf(sNumber,L"%ld",uniqueNumber);
  575. int filenameLen = wcslen(pInfo->GetServerNameFlat() + 2) + wcslen(sNumber);
  576. if (wcslen(tempdir) + filenameLen < absFilenameLen && filenameLen < relFilenameLen)
  577. {
  578. swprintf(filename,L"%s%s%s",tempdir,pInfo->GetServerNameFlat() + 2,sNumber);
  579. swprintf(relativeFileName, L"%s%s", pInfo->GetServerNameFlat() + 2,sNumber);
  580. }
  581. else
  582. hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
  583. }
  584. // obtain the cache file name and if an existing job file is in old version
  585. // convert it to the new version
  586. if (SUCCEEDED(hr) && bJobFileAlreadyExists)
  587. {
  588. IStoragePtr spStorage;
  589. hr = StgOpenStorage(filename, NULL, STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &spStorage);
  590. if (SUCCEEDED(hr))
  591. {
  592. IVarSetPtr spJobVarSet;
  593. hr = OleLoad(spStorage, IID_IUnknown, NULL, (void**)&spJobVarSet);
  594. if (SUCCEEDED(hr))
  595. {
  596. // for retry case, we need to build plugin file list using
  597. // the job file
  598. hr = BuildPlugInFileList(pInfo->pPlugInFileList, spJobVarSet);
  599. }
  600. if (SUCCEEDED(hr))
  601. {
  602. strCacheFile = spJobVarSet->get(GET_BSTR(DCTVS_Accounts_InputFile));
  603. // this is used to indicate whether newly added varset fields in job file are
  604. // present or not: these fields include DCTVS_Options_RelatvieResultFileName,
  605. // DCTVS_Security_ReportAccountReferencesRelativeFileName
  606. // if not, we have to add them so that the agent code will work
  607. _bstr_t storedRelativeResultFileName =
  608. spJobVarSet->get(GET_BSTR(DCTVS_Options_RelativeResultFileName));
  609. if (storedRelativeResultFileName.length() == 0)
  610. {
  611. SetupVarSetForJob(pInfo,spJobVarSet,tempdir,filename,relativeFileName, TRUE);
  612. IPersistStoragePtr ps = NULL;
  613. hr = spJobVarSet->QueryInterface(IID_IPersistStorage, (void**)&ps);
  614. if (SUCCEEDED(hr))
  615. hr = OleSave(ps,spStorage,FALSE);
  616. }
  617. }
  618. }
  619. }
  620. else
  621. {
  622. // retrieve cache file name from varset
  623. strCacheFile = pVarSet->get(GET_BSTR(DCTVS_Accounts_InputFile));
  624. }
  625. if (SUCCEEDED(hr) && (!bJobFileAlreadyExists))
  626. {
  627. SetupVarSetForJob(pInfo,pVarSet,tempdir,filename,relativeFileName);
  628. // Save the input varset to a file
  629. IPersistStoragePtr ps = NULL;
  630. IStoragePtr store = NULL;
  631. hr = pVarSet->QueryInterface(IID_IPersistStorage,(void**)&ps);
  632. if ( SUCCEEDED(hr) )
  633. {
  634. hr = StgCreateDocfile(filename,STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE |STGM_FAILIFTHERE,0,&store);
  635. if ( SUCCEEDED(hr) )
  636. {
  637. hr = OleSave(ps,store,FALSE);
  638. }
  639. }
  640. }
  641. IUnknown * pWorkItem = NULL;
  642. if ( SUCCEEDED(hr) )
  643. {
  644. pVarSet->put(GET_BSTR(DCTVS_ConfigurationFile),filename);
  645. pVarSet->put(GET_BSTR(DCTVS_InstallToServer),pInfo->GetServerName());
  646. pVarSet->put(GET_BSTR(DCTVS_CacheFile), strCacheFile);
  647. hr = pVarSet->QueryInterface(IID_IUnknown,(void**)&pWorkItem);
  648. }
  649. if ( SUCCEEDED(hr) )
  650. {
  651. // Do the installation to the server
  652. hr = pInstaller->Process(pWorkItem);
  653. if(hr == 0x88070040)
  654. strFailureDesc = GET_STRING(IDS_AGENT_RUNNING);
  655. pWorkItem->Release();
  656. if ( SUCCEEDED(hr) )
  657. {
  658. err.MsgWrite(0,DCT_MSG_AGENT_INSTALLED_S,pInfo->GetServerName());
  659. // try to start the job
  660. DWORD rc = StartJob(pInfo->GetServerName(),filename,filename + UStrLen(tempdir), strJobid );
  661. if ( rc )
  662. {
  663. hr = HRESULT_FROM_WIN32(rc);
  664. // if we couldn't start the job, then try to stop the service
  665. TDCTInstall x( pInfo->GetServerName(), NULL );
  666. x.SetServiceInformation(GET_STRING(IDS_DISPLAY_NAME),GET_STRING(IDS_SERVICE_NAME),L"EXE",NULL);
  667. DWORD rcOs = x.ScmOpen();
  668. if ( ! rcOs )
  669. {
  670. x.ServiceStop();
  671. }
  672. }
  673. }
  674. // by now, we know for sure that the error will be logged into dispatcher.csv
  675. // if there is any
  676. bErrLogged = TRUE;
  677. }
  678. pInstaller->Release();
  679. }
  680. }
  681. }
  682. if(pInfo->nErrCount == 0)
  683. CoUninitialize();
  684. if ( hr )
  685. {
  686. if ( hr == HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) )
  687. {
  688. err.MsgWrite(ErrE,DCT_MSG_AGENT_SERVICE_NOT_STARTED_SS,pInfo->GetServerName(),pInfo->GetServerName());
  689. }
  690. else
  691. {
  692. err.SysMsgWrite(ErrE,hr,DCT_MSG_AGENT_LAUNCH_FAILED_SD,pInfo->GetServerName(),hr);
  693. }
  694. if(hr == 0x80070040 && pInfo->nErrCount < 10)
  695. {
  696. Sleep(1000);
  697. pInfo->nErrCount++;
  698. err.DbgMsgWrite(0,L"Retrying install...");
  699. hr = DoInstall((LPVOID)pInfo);
  700. }
  701. else if (hr == CO_E_NOT_SUPPORTED)
  702. {
  703. err.MsgWrite(ErrI,DCT_MSG_AGENT_ALPHA_NOTSUPPORTED,pInfo->GetServerName());
  704. strFailureDesc = GET_STRING(IDS_UNSOUPPORTED_OS);
  705. ::WaitForSingleObject(pInfo->hMutex, 30000);
  706. pInfo->pStartFailedVector->push_back(pInfo->GetServerName());
  707. pInfo->pFailureDescVector->push_back((BSTR)strFailureDesc);
  708. ::ReleaseMutex(pInfo->hMutex);
  709. }
  710. else
  711. {
  712. ::WaitForSingleObject(pInfo->hMutex, 30000);
  713. pInfo->pStartFailedVector->push_back(pInfo->GetServerName());
  714. pInfo->pFailureDescVector->push_back((BSTR)strFailureDesc);
  715. ::ReleaseMutex(pInfo->hMutex);
  716. }
  717. // if the error has been logged yet, log the error into dispatcher.csv
  718. if (hr && !bErrLogged)
  719. errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",pInfo->GetServerName(),L"Install",HRESULT_CODE(hr));
  720. }
  721. else
  722. {
  723. // DWORD res = ::WaitForSingleObject(pInfo->hMutex, 30000);
  724. ::WaitForSingleObject(pInfo->hMutex, 30000);
  725. pInfo->pStartedVector->push_back(pInfo->GetServerName());
  726. _ASSERTE(strJobid != _bstr_t(L""));
  727. pInfo->pJobidVector->push_back((BSTR)strJobid);
  728. ::ReleaseMutex(pInfo->hMutex);
  729. }
  730. if(pInfo->nErrCount == 0)
  731. delete pInfo;
  732. return hr;
  733. }
  734. // DispatchToServers
  735. // VarSet input:
  736. //
  737. STDMETHODIMP // ret- HRESULT
  738. CDCTDispatcher::DispatchToServers(
  739. IUnknown ** ppData // i/o- pointer to varset
  740. )
  741. {
  742. HRESULT hr;
  743. SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
  744. //Sleep(60000); //delay for debugging
  745. (*ppData)->AddRef();
  746. hr = Process(*ppData,NULL,NULL);
  747. return hr;
  748. }
  749. // BuildInputFile constructs a cache file to be used for security translation
  750. // VarSet input:
  751. // Options.UniqueNumberForResultsFile -unique number to append
  752. // Dispatcher.ResultPath -directory to write file to
  753. //
  754. HRESULT // ret- HRESULT
  755. CDCTDispatcher::BuildInputFile(
  756. IVarSet * pVarSet // in - varset containing data
  757. )
  758. {
  759. IVarSetPtr pVarSetST(CLSID_VarSet); // varset to use to run security translator
  760. IVarSetPtr pVarSetTemp;
  761. HRESULT hr = S_OK;
  762. _bstr_t key = GET_BSTR(DCTVS_Options);
  763. WCHAR tempdir[MAX_PATH];
  764. WCHAR resultPath[MAX_PATH];
  765. WCHAR logfile[MAX_PATH];
  766. DWORD uniqueNumber = (LONG)pVarSet->get(GET_BSTR(DCTVS_Options_UniqueNumberForResultsFile));
  767. _bstr_t bstrResultDir = pVarSet->get(GET_BSTR(DCTVS_Dispatcher_ResultPath));
  768. long lActionId = pVarSet->get(L"ActionID");
  769. if ( pVarSetST == NULL )
  770. {
  771. return E_FAIL;
  772. }
  773. if (! NeedToUseST(pVarSet,TRUE) )
  774. {
  775. return S_OK;
  776. }
  777. // construct a filename for the cache
  778. if ( ! uniqueNumber )
  779. {
  780. uniqueNumber = GetTickCount();
  781. }
  782. if ( bstrResultDir.length() )
  783. {
  784. safecopy(tempdir,(WCHAR*)bstrResultDir);
  785. }
  786. else
  787. {
  788. // if no result path specified, use temp directory
  789. hr = GetTempPath(DIM(tempdir),tempdir);
  790. }
  791. //
  792. // Generate cache file name based on the action id
  793. // so that account mapping information is persisted
  794. // for each security related migration task.
  795. // The cache file name is persisted in the job file
  796. // for each server.
  797. //
  798. WCHAR szActionId[32];
  799. swprintf(szActionId, L".%03ld", lActionId);
  800. _bstr_t strCacheFile = GET_BSTR(IDS_CACHE_FILE_NAME) + szActionId;
  801. _bstr_t strCachePath = tempdir + strCacheFile;
  802. // copy 'Options' settings to ST varset
  803. hr = pVarSet->raw_getReference(key,&pVarSetTemp);
  804. if ( SUCCEEDED(hr) )
  805. {
  806. pVarSetST->ImportSubTree(key,pVarSetTemp);
  807. }
  808. // copy 'Accounts' settings to ST varset
  809. key = GET_BSTR(DCTVS_Accounts);
  810. hr = pVarSet->raw_getReference(key,&pVarSetTemp);
  811. if ( SUCCEEDED(hr) )
  812. {
  813. pVarSetST->ImportSubTree(key,pVarSetTemp);
  814. }
  815. pVarSetST->put(GET_BSTR(DCTVS_Security_TranslationMode),
  816. pVarSet->get(GET_BSTR(DCTVS_Security_TranslationMode)));
  817. pVarSetST->put(GET_BSTR(DCTVS_Options_NoChange),GET_BSTR(IDS_YES));
  818. pVarSetST->put(GET_BSTR(DCTVS_Options_LogLevel),(LONG)0);
  819. pVarSetST->put(GET_BSTR(DCTVS_Security_BuildCacheFile),strCachePath);
  820. // change the log file - the building of the cache file happens behind the scenes
  821. // so we won't put it in the regular log file because it would cause confusion
  822. swprintf(logfile,L"%s%s",tempdir,L"BuildCacheFileLog.txt");
  823. pVarSetST->put(GET_BSTR(DCTVS_Options_Logfile),logfile);
  824. //are we using a sID mapping file to perform security translation
  825. pVarSetST->put(GET_BSTR(DCTVS_AccountOptions_SecurityInputMOT),
  826. pVarSet->get(GET_BSTR(DCTVS_AccountOptions_SecurityInputMOT)));
  827. pVarSetST->put(GET_BSTR(DCTVS_AccountOptions_SecurityMapFile),
  828. pVarSet->get(GET_BSTR(DCTVS_AccountOptions_SecurityMapFile)));
  829. MCSEADCTAGENTLib::IDCTAgentPtr pAgent(MCSEADCTAGENTLib::CLSID_DCTAgent);
  830. try {
  831. if ( pAgent == NULL )
  832. return E_FAIL;
  833. _bstr_t jobID;
  834. BSTR b = NULL;
  835. // turn on the alternative log file
  836. swprintf(logfile, GetMigrationLogPath());
  837. pVarSetST->put(GET_BSTR(DCTVS_Options_AlternativeLogfile), logfile);
  838. hr = pAgent->raw_SubmitJob(pVarSetST,&b);
  839. // turn off the alternative log file
  840. pVarSetST->put(GET_BSTR(DCTVS_Options_AlternativeLogfile),_bstr_t(L""));
  841. if ( SUCCEEDED(hr) )
  842. {
  843. // since this is a local agent, we should go ahead to signal Ok to shut down
  844. // the reason HRESULT is not checked is that when there is no reference to
  845. // agent COM server, it will be shut down anyway
  846. pAgent->raw_SignalOKToShutDown();
  847. jobID = b;
  848. IVarSetPtr pVarSetStatus; // used to retrieve status of running job
  849. _bstr_t jobStatus;
  850. IUnknown * pUnk;
  851. // loop until the agent is finished
  852. do {
  853. Sleep(1000);
  854. hr = pAgent->QueryJobStatus(jobID,&pUnk);
  855. if ( SUCCEEDED(hr) )
  856. {
  857. pVarSetStatus = pUnk;
  858. jobStatus = pVarSetStatus->get(GET_BSTR(DCTVS_JobStatus));
  859. pUnk->Release();
  860. }
  861. else
  862. {
  863. break;
  864. }
  865. } while ( UStrICmp(jobStatus,GET_STRING(IDS_DCT_Status_Completed))
  866. && UStrICmp(jobStatus,GET_STRING(IDS_DCT_Status_Completed_With_Errors)));
  867. }
  868. }
  869. catch(...)
  870. {
  871. hr = E_FAIL;
  872. }
  873. if ( SUCCEEDED(hr) )
  874. {
  875. pVarSet->put(GET_BSTR(DCTVS_Accounts_InputFile),strCacheFile);
  876. pVarSet->put(GET_BSTR(DCTVS_Accounts_WildcardSpec),"");
  877. err.MsgWrite(0,DCT_MSG_CACHE_FILE_BUILT_S,(WCHAR*)strCacheFile);
  878. }
  879. return hr;
  880. }
  881. // These are TNodeListSortable sorting functions
  882. int ServerNodeCompare(TNode const * t1,TNode const * t2)
  883. {
  884. TServerNode * n1 = (TServerNode *)t1;
  885. TServerNode * n2 = (TServerNode *)t2;
  886. return UStrICmp(n1->SourceName(),n2->SourceName());
  887. }
  888. int ServerValueCompare(TNode const * t1, void const * val)
  889. {
  890. TServerNode * n1 = (TServerNode *)t1;
  891. WCHAR const * name = (WCHAR const *) val;
  892. return UStrICmp(n1->SourceName(),name);
  893. }
  894. // MergeServerList combines the security translation server list in Servers.* with the computer migration
  895. // server list in MigrateServers.*
  896. // The combined list is stored in the varset under Servers.* with subkeys specifying which actions to take for
  897. // each computer
  898. void
  899. CDCTDispatcher::MergeServerList(
  900. IVarSet * pVarSet // in - varset containing list of servers to migrate and translate on
  901. )
  902. {
  903. int ndx = 0;
  904. WCHAR key[1000];
  905. _bstr_t text;
  906. _bstr_t serverName;
  907. BOOL bNoChange;
  908. int lastndx = -1;
  909. long totalsrvs;
  910. // if it is in test mode, there is no skipping
  911. text = pVarSet->get(GET_BSTR(DCTVS_Options_NoChange));
  912. bNoChange = (!UStrICmp(text, GET_STRING(IDS_YES))) ? TRUE : FALSE;
  913. //get the number of servers in the varset
  914. totalsrvs = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems));
  915. // if there are computers being migrated
  916. if (totalsrvs > 0)
  917. {
  918. //add code to move varset server entries, with SkipDispatch set, to the bottom
  919. //of the server list and decrease the number of server items by each server
  920. //to be skipped
  921. //check each server in the list moving all to be skipped to the end of the list and
  922. //decreasing the server count for each to be skipped
  923. for (ndx = 0; ndx < totalsrvs; ndx++)
  924. {
  925. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),ndx);
  926. text = pVarSet->get(key);
  927. swprintf(key,GET_STRING(DCTVSFmt_Servers_D),ndx);
  928. serverName = pVarSet->get(key);
  929. //if the server is not to be skipped, we may have to move it above
  930. //a server that is being skipped
  931. if (serverName.length()
  932. && (bNoChange || !text || !UStrICmp(text,GET_STRING(IDS_No))))
  933. {
  934. //if the last server looked at is not being skipped then we don't
  935. //need to swap any servers in the list and we can increment the
  936. //last server not being skipped
  937. if (lastndx == (ndx - 1))
  938. {
  939. lastndx = ndx;
  940. }
  941. else //else swap servers in the varset so skipped server comes after
  942. { //the one not being skipped
  943. _bstr_t tempName, tempDnsName, tempNewName, tempChngDom, tempReboot, tempMigOnly;
  944. long tempRebootDelay;
  945. _bstr_t skipName, skipDnsName, skipNewName, skipChngDom, skipReboot, skipMigOnly;
  946. long skipRebootDelay;
  947. lastndx++; //move to the skipped server that we will swap with
  948. //copy skipped server's values to temp
  949. swprintf(key,GET_STRING(DCTVSFmt_Servers_D),lastndx);
  950. skipName = pVarSet->get(key);
  951. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),lastndx);
  952. skipDnsName = pVarSet->get(key);
  953. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),lastndx);
  954. skipNewName = pVarSet->get(key);
  955. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),lastndx);
  956. skipChngDom = pVarSet->get(key);
  957. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),lastndx);
  958. skipReboot = pVarSet->get(key);
  959. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),lastndx);
  960. skipMigOnly = pVarSet->get(key);
  961. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),lastndx);
  962. skipRebootDelay = pVarSet->get(key);
  963. //copy current, non-skipped, server valuesto second temp
  964. swprintf(key,GET_STRING(DCTVSFmt_Servers_D),ndx);
  965. tempName = pVarSet->get(key);
  966. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),ndx);
  967. tempDnsName = pVarSet->get(key);
  968. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),ndx);
  969. tempNewName = pVarSet->get(key);
  970. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),ndx);
  971. tempChngDom = pVarSet->get(key);
  972. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),ndx);
  973. tempReboot = pVarSet->get(key);
  974. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),ndx);
  975. tempMigOnly = pVarSet->get(key);
  976. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),ndx);
  977. tempRebootDelay = pVarSet->get(key);
  978. //place current server's values in place of values for the one
  979. //being skipped
  980. swprintf(key,GET_STRING(DCTVSFmt_Servers_D),lastndx);
  981. pVarSet->put(key,tempName);
  982. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),lastndx);
  983. pVarSet->put(key,tempDnsName);
  984. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),lastndx);
  985. pVarSet->put(key,tempNewName);
  986. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),lastndx);
  987. pVarSet->put(key,tempChngDom);
  988. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),lastndx);
  989. pVarSet->put(key,tempReboot);
  990. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),lastndx);
  991. pVarSet->put(key,tempMigOnly);
  992. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),lastndx);
  993. pVarSet->put(key,tempRebootDelay);
  994. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),lastndx);
  995. pVarSet->put(key,GET_BSTR(IDS_No));
  996. //place skipped server's values in place of values for current server
  997. swprintf(key,GET_STRING(DCTVSFmt_Servers_D),ndx);
  998. pVarSet->put(key,skipName);
  999. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),ndx);
  1000. pVarSet->put(key,skipDnsName);
  1001. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),ndx);
  1002. pVarSet->put(key,skipNewName);
  1003. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),ndx);
  1004. pVarSet->put(key,skipChngDom);
  1005. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),ndx);
  1006. pVarSet->put(key,skipReboot);
  1007. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),ndx);
  1008. pVarSet->put(key,skipMigOnly);
  1009. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RebootDelay_D),ndx);
  1010. pVarSet->put(key,skipRebootDelay);
  1011. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),ndx);
  1012. pVarSet->put(key,GET_BSTR(IDS_YES));
  1013. }//end else need to swap with skipped server
  1014. }//end if not skipping dispatch for this server
  1015. }//end for each server in the server list
  1016. //exclude servers to be skipped for dispatch from being included in the server count
  1017. pVarSet->put(GET_BSTR(DCTVS_Servers_NumItems),(long)++lastndx);
  1018. }
  1019. }
  1020. STDMETHODIMP // ret- HRESULT
  1021. CDCTDispatcher::Process(
  1022. IUnknown * pWorkItem, // in - varset containing job information and list of servers
  1023. IUnknown ** ppResponse, // out- not used
  1024. UINT * pDisposition // out- not used
  1025. )
  1026. {
  1027. // initialize output parameters
  1028. if ( ppResponse )
  1029. {
  1030. (*ppResponse) = NULL;
  1031. }
  1032. HRESULT hr = S_OK;
  1033. IVarSetPtr pVarSetIn = pWorkItem;
  1034. LONG nThreads;
  1035. WCHAR key[100];
  1036. _bstr_t serverName;
  1037. _bstr_t serverNameDns;
  1038. LONG nServers = 0;
  1039. _bstr_t log;
  1040. _bstr_t useTempCredentials;
  1041. BOOL bFatalError = FALSE;
  1042. _bstr_t text;
  1043. WCHAR debugLog[MAX_PATH];
  1044. long bAppend = 0;
  1045. _bstr_t skip;
  1046. _bstr_t sWizard;
  1047. BOOL bSkipSourceSid;
  1048. if ( DumpDebugInfo(debugLog) )
  1049. {
  1050. if ( pVarSetIn != NULL )
  1051. {
  1052. // temporarily remove the password fromthe varset, so that we don't write it to the file
  1053. pVarSetIn->DumpToFile(debugLog);
  1054. }
  1055. }
  1056. //get the wizard being run
  1057. sWizard = pVarSetIn->get(GET_BSTR(DCTVS_Options_Wizard));
  1058. if (!UStrICmp(sWizard, L"security"))
  1059. bSkipSourceSid = TRUE;
  1060. else
  1061. bSkipSourceSid = FALSE;
  1062. nThreads = pVarSetIn->get(GET_BSTR(DCTVS_Options_MaxThreads));
  1063. text = pVarSetIn->get(GET_BSTR(DCTVS_Options_AppendToLogs));
  1064. if (! UStrICmp(text,GET_STRING(IDS_YES)) )
  1065. {
  1066. bAppend = 1;
  1067. }
  1068. log = pVarSetIn->get(GET_BSTR(DCTVS_Options_DispatchLog));
  1069. err.LogOpen((WCHAR*)log,bAppend);
  1070. // open the internal log "dispatcher.csv" and try to reserve enough
  1071. // disk space for it
  1072. // if we cannot, agent dispatching fails with an error
  1073. DWORD rc = ERROR_SUCCESS;
  1074. _bstr_t internalLog = pVarSetIn->get(GET_BSTR(DCTVS_Options_DispatchCSV));
  1075. BOOL bLogOpened = errLog.LogOpen((WCHAR*)internalLog,0,0,true);
  1076. if (!bLogOpened)
  1077. rc = GetLastError();
  1078. if (rc == ERROR_SUCCESS)
  1079. {
  1080. LONG lServers = pVarSetIn->get(GET_BSTR(DCTVS_Servers_NumItems));
  1081. DWORD dwNumOfBytes = sizeof(WCHAR) * (2 * (22 + MAX_PATH) // the first two lines
  1082. + (22 + 10) // the third line
  1083. + lServers * 2000 // 2000 WCHAR per server
  1084. );
  1085. //dwNumOfBytes = 1000000000;
  1086. rc = errLog.ExtendSize(dwNumOfBytes);
  1087. }
  1088. if (rc != ERROR_SUCCESS)
  1089. {
  1090. hr = HRESULT_FROM_WIN32(rc);
  1091. errLog.LogClose();
  1092. _bstr_t errMsg = errLog.GetMsgText(DCT_MSG_CANNOT_WRITE_INTERNAL_DISPATCH_LOG_D, hr);
  1093. err.SysMsgWrite(ErrE, HRESULT_CODE(hr),DCT_MSG_CANNOT_WRITE_INTERNAL_DISPATCH_LOG_D, hr);
  1094. return Error((WCHAR*) errMsg, GUID_NULL, hr);
  1095. }
  1096. // make sure this dispatcher.csv file is written starting from the current file pointer
  1097. errLog.SetWriteOnCurrentPosition(TRUE);
  1098. // write the log file into dispatcher.csv
  1099. errLog.DbgMsgWrite(0,L"%ls",(WCHAR*)log);
  1100. // default to 20 threads if the client doesn't specify
  1101. if ( ! nThreads )
  1102. {
  1103. nThreads = 20;
  1104. }
  1105. // Get the name of the local computer
  1106. DWORD dim = DIM(gComputerName);
  1107. GetComputerName(gComputerName,&dim);
  1108. m_pThreadPool = new TJobDispatcher(nThreads);
  1109. if (!m_pThreadPool)
  1110. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1111. // write the common result path into dispatcher.csv
  1112. _bstr_t bstrResultDir = pVarSetIn->get(GET_BSTR(DCTVS_Dispatcher_ResultPath));
  1113. errLog.DbgMsgWrite(0,L"%ls",(WCHAR*)bstrResultDir);
  1114. // check whether it is account reference report
  1115. BOOL bAccountReferenceReport = FALSE;
  1116. _bstr_t bstrGenerateReport = pVarSetIn->get(GET_BSTR(DCTVS_Reports_Generate));
  1117. _bstr_t bstrAccountRefReport = pVarSetIn->get(GET_BSTR(DCTVS_Reports_AccountReferences));
  1118. if (!UStrICmp(bstrGenerateReport, GET_STRING(IDS_YES))
  1119. && !UStrICmp(bstrAccountRefReport,GET_STRING(IDS_YES)))
  1120. bAccountReferenceReport = TRUE;
  1121. //
  1122. // only generate a new cache file if migration task is not the
  1123. // retry task as the retry task uses persisted cache file(s)
  1124. // a cache file is not necessary if it is account reference report
  1125. //
  1126. if (UStrICmp(sWizard, L"retry") != 0 && !bAccountReferenceReport)
  1127. {
  1128. // Build an input file for the ST cache, to send to each server
  1129. hr = BuildInputFile(pVarSetIn);
  1130. if ( FAILED(hr) )
  1131. {
  1132. err.SysMsgWrite(ErrE,HRESULT_CODE(hr),DCT_MSG_CACHE_CONSTRUCTION_FAILED);
  1133. bFatalError = TRUE;
  1134. }
  1135. }
  1136. // Split out the remotable tasks for each server
  1137. // Get the sids for the source and target domains
  1138. // note: if a sid mapping file is used, we do not need to get sids for source and target domains
  1139. //
  1140. // In case of account reference report, we need to set up the dctcache on the client machine
  1141. // as if we're using migrated object table because the account reference report relies on
  1142. // TRidCache::Lookup (called when MOT is used) which requires valid source and target sids.
  1143. // This logic is added because with a fresh install of database, DCTVS_AccountOptions_SecurityInputMOT
  1144. // is not set for account reference report.
  1145. _bstr_t bstrUseMOT = pVarSetIn->get(GET_BSTR(DCTVS_AccountOptions_SecurityInputMOT));
  1146. if (bAccountReferenceReport || !UStrICmp(bstrUseMOT,GET_STRING(IDS_YES)))
  1147. {
  1148. PSID pSidSrc = NULL;
  1149. PSID pSidTgt = NULL;
  1150. _bstr_t source = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1151. _bstr_t target = pVarSetIn->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1152. //if security translation, retrieve source sid and convert so
  1153. //that it can be convert back below
  1154. if (bSkipSourceSid)
  1155. {
  1156. _bstr_t sSid = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomainSid));
  1157. pSidSrc = SidFromString((WCHAR*)sSid);
  1158. }
  1159. else //else get the sid now
  1160. GetSidForDomain((WCHAR*)source,&pSidSrc);
  1161. GetSidForDomain((WCHAR*)target,&pSidTgt);
  1162. if ( pSidSrc && pSidTgt )
  1163. {
  1164. WCHAR txtSid[200];
  1165. DWORD lenTxt = DIM(txtSid);
  1166. if ( GetTextualSid(pSidSrc,txtSid,&lenTxt) )
  1167. {
  1168. pVarSetIn->put(GET_BSTR(DCTVS_Options_SourceDomainSid),txtSid);
  1169. }
  1170. lenTxt = DIM(txtSid);
  1171. if ( GetTextualSid(pSidTgt,txtSid,&lenTxt) )
  1172. {
  1173. pVarSetIn->put(GET_BSTR(DCTVS_Options_TargetDomainSid),txtSid);
  1174. }
  1175. FreeSid(pSidSrc);
  1176. FreeSid(pSidTgt);
  1177. }
  1178. else
  1179. {
  1180. if ( source.length() && ! pSidSrc )
  1181. {
  1182. err.MsgWrite(ErrE,DCT_MSG_DOMAIN_SID_NOT_FOUND_S,(WCHAR*)source);
  1183. bFatalError = TRUE;
  1184. }
  1185. else if ( target.length() && ! pSidTgt )
  1186. {
  1187. err.MsgWrite(ErrE,DCT_MSG_DOMAIN_SID_NOT_FOUND_S,(WCHAR*)target);
  1188. bFatalError = TRUE;
  1189. }
  1190. }
  1191. }
  1192. MergeServerList(pVarSetIn);
  1193. TNodeList * fileList = new TNodeList;
  1194. if (!fileList)
  1195. {
  1196. delete m_pThreadPool;
  1197. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1198. }
  1199. // Build list of files to install for plug-ins (if any)
  1200. // but for retry case, we should not build the plugin file list now
  1201. if (UStrICmp(sWizard, L"retry") != 0)
  1202. hr = BuildPlugInFileList(fileList,pVarSetIn);
  1203. if(!SUCCEEDED(hr))
  1204. {
  1205. delete m_pThreadPool;
  1206. delete fileList;
  1207. return hr;
  1208. }
  1209. // Make a copy of the varset with the server lists removed,
  1210. // so we don't have to copy the entire server list for each agent
  1211. gCS.Enter();
  1212. IVarSet * pTemp = NULL;
  1213. IVarSetPtr pVarSetTemp(CLSID_VarSet);
  1214. hr = pVarSetTemp->ImportSubTree(_bstr_t(L""),pVarSetIn);
  1215. if ( SUCCEEDED(hr) )
  1216. {
  1217. hr = pVarSetTemp->raw_getReference(SysAllocString(L"MigrateServers"),&pTemp);
  1218. if ( SUCCEEDED(hr) )
  1219. {
  1220. pTemp->Clear();
  1221. pTemp->Release();
  1222. pTemp = NULL;
  1223. }
  1224. hr = pVarSetTemp->raw_getReference(SysAllocString(L"Servers"),&pTemp);
  1225. if ( SUCCEEDED(hr) )
  1226. {
  1227. pTemp->Clear();
  1228. pTemp->Release();
  1229. pTemp = NULL;
  1230. }
  1231. }
  1232. else
  1233. {
  1234. bFatalError = TRUE;
  1235. }
  1236. gCS.Leave();
  1237. m_startFailedVector.clear();
  1238. // log the number of agents to be dispatched into dispatcher.csv
  1239. LONG nServerCount = pVarSetIn->get(GET_BSTR(DCTVS_Servers_NumItems));
  1240. if ( nServerCount && ! bFatalError )
  1241. {
  1242. err.MsgWrite(0,DCT_MSG_DISPATCH_SERVER_COUNT_D,nServerCount);
  1243. errLog.DbgMsgWrite(0,L"%ld",nServerCount);
  1244. }
  1245. else
  1246. {
  1247. // no agent will be dispatched
  1248. err.MsgWrite(0,DCT_MSG_DISPATCH_SERVER_COUNT_D,0);
  1249. errLog.DbgMsgWrite(0,L"%ld",0);
  1250. }
  1251. // if it is in test mode, there is no skipping
  1252. text = pVarSetIn->get(GET_BSTR(DCTVS_Options_NoChange));
  1253. BOOL bNoChange = (!UStrICmp(text, GET_STRING(IDS_YES))) ? TRUE : FALSE;
  1254. // reset the index for servers
  1255. nServers = 0;
  1256. while (nServers < nServerCount)
  1257. {
  1258. if ( bFatalError )
  1259. {
  1260. break;
  1261. }
  1262. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),nServers);
  1263. skip = pVarSetIn->get(key);
  1264. swprintf(key,GET_STRING(DCTVSFmt_Servers_D),nServers);
  1265. serverName = pVarSetIn->get(key);
  1266. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_DnsName_D),nServers);
  1267. serverNameDns = pVarSetIn->get(key);
  1268. // to test whether to skip, use the same logic as in mergeserverlist
  1269. if ((serverName.length())
  1270. && (bNoChange || !skip || !UStrICmp(skip,GET_STRING(IDS_No))))
  1271. {
  1272. IVarSetPtr pVS(CLSID_VarSet);
  1273. InstallJobInfo * pInfo = new InstallJobInfo;
  1274. if (!pInfo)
  1275. {
  1276. delete fileList;
  1277. delete m_pThreadPool;
  1278. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1279. }
  1280. if ( pVS == NULL )
  1281. {
  1282. return E_FAIL;
  1283. }
  1284. swprintf(key, L"Servers.%ld.JobFile", nServers);
  1285. _bstr_t file = pVarSetIn->get(key);
  1286. // Set up job structure
  1287. pInfo->pVarSetList = pVarSetIn;
  1288. pInfo->pVarSet = pVarSetTemp;
  1289. pInfo->serverName = serverName;
  1290. pInfo->serverNameDns = serverNameDns;
  1291. pInfo->ndx = nServers;
  1292. pInfo->pPlugInFileList = fileList;
  1293. pInfo->pStartFailedVector = &m_startFailedVector;
  1294. pInfo->pFailureDescVector = &m_failureDescVector;
  1295. pInfo->pStartedVector = &m_startedVector;
  1296. pInfo->pJobidVector = &m_jobidVector;
  1297. pInfo->hMutex = m_hMutex;
  1298. pInfo->nErrCount = 0;
  1299. if ( file.length() )
  1300. {
  1301. pInfo->jobfile = file;
  1302. }
  1303. err.MsgWrite(0,DCT_MSG_DISPATCHING_TO_SERVER_S,pInfo->GetServerName());
  1304. errLog.DbgMsgWrite(0,L"%ls\t%ls\t%ld",(WCHAR*)pInfo->GetServerName(),L"WillInstall",0);
  1305. m_pThreadPool->SubmitJob(&DoInstall,(void *)pInfo);
  1306. nServers++;
  1307. }
  1308. }
  1309. // launch a thread to wait for all jobs to finish, then clean up and exit
  1310. WaitInfo* wInfo = new WaitInfo;
  1311. if (!wInfo)
  1312. {
  1313. delete fileList;
  1314. delete m_pThreadPool;
  1315. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1316. }
  1317. wInfo->ppPool = &m_pThreadPool;
  1318. wInfo->pUnknown = NULL;
  1319. wInfo->pPlugInFileList = fileList;
  1320. QueryInterface(IID_IUnknown,(LPVOID*)&(wInfo->pUnknown));
  1321. DWORD id = 0;
  1322. HANDLE waitHandle = CreateThread(NULL,0,&Wait,(void *)wInfo,0,&id);
  1323. if(waitHandle)
  1324. {
  1325. CloseHandle(waitHandle);
  1326. }
  1327. return hr;
  1328. }
  1329. STDMETHODIMP CDCTDispatcher::AllAgentsStarted(long *bAllAgentsStarted)
  1330. {
  1331. *bAllAgentsStarted = m_pThreadPool == NULL;
  1332. return S_OK;
  1333. }
  1334. SAFEARRAY* MakeSafeArray(std::vector<CComBSTR>& stVector)
  1335. {
  1336. SAFEARRAYBOUND rgsabound[1];
  1337. rgsabound[0].lLbound = 0;
  1338. rgsabound[0].cElements = stVector.size();
  1339. SAFEARRAY FAR* psa = SafeArrayCreate(VT_BSTR, 1, rgsabound);
  1340. if (psa)
  1341. {
  1342. std::vector<CComBSTR>::iterator iter = stVector.begin();
  1343. for(long i=0; iter != stVector.end(); ++iter, ++i)
  1344. {
  1345. _ASSERTE(*iter && *iter != L"");
  1346. BSTR insert = (*iter).Copy();
  1347. if (insert == NULL || FAILED(SafeArrayPutElement(psa, &i, (void*)insert)))
  1348. {
  1349. if (insert)
  1350. SysFreeString(insert);
  1351. SafeArrayDestroy(psa);
  1352. psa = NULL;
  1353. break;
  1354. }
  1355. }
  1356. stVector.clear();
  1357. }
  1358. return psa;
  1359. }
  1360. STDMETHODIMP CDCTDispatcher::GetStartedAgentsInfo(long* bAllAgentsStarted, SAFEARRAY** ppbstrStartedAgents, SAFEARRAY** ppbstrJobid, SAFEARRAY** ppbstrFailedAgents, SAFEARRAY** ppbstrFailureDesc)
  1361. {
  1362. *bAllAgentsStarted = m_pThreadPool == NULL;
  1363. // DWORD res = ::WaitForSingleObject(m_hMutex, 30000);
  1364. ::WaitForSingleObject(m_hMutex, 30000);
  1365. *ppbstrFailedAgents = MakeSafeArray(m_startFailedVector);
  1366. *ppbstrFailureDesc = MakeSafeArray(m_failureDescVector);
  1367. _ASSERTE(m_startedVector.size() == m_jobidVector.size());
  1368. *ppbstrStartedAgents = MakeSafeArray(m_startedVector);
  1369. *ppbstrJobid = MakeSafeArray(m_jobidVector);
  1370. ::ReleaseMutex(m_hMutex);
  1371. return S_OK;
  1372. }