DeployTask.cpp 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include <string>
  14. #include "deploy.hpp"
  15. #include "jcrc.hpp"
  16. #include "jexcept.hpp"
  17. #include "jfile.hpp"
  18. #include "jptree.hpp"
  19. #include "jstring.hpp"
  20. #include "jmutex.hpp"
  21. #include "xslprocessor.hpp"
  22. offset_t getDirSize(const char* path, bool subdirs = true)
  23. {
  24. offset_t size = 0;
  25. if (!path || !*path)
  26. return size;
  27. Owned<IFile> pFile = createIFile(path);
  28. if (pFile->exists())
  29. {
  30. if (pFile->isDirectory() == foundYes)
  31. {
  32. Owned<IDirectoryIterator> it = pFile->directoryFiles(NULL, false, true);
  33. ForEach(*it)
  34. {
  35. if (it->isDir())
  36. {
  37. if (subdirs)
  38. {
  39. StringBuffer subdir;
  40. it->getName(subdir);
  41. StringBuffer dir(path);
  42. dir.append(PATHSEPCHAR).append(subdir);
  43. size += getDirSize(dir.str(), subdirs);
  44. }
  45. }
  46. else
  47. size += it->getFileSize();
  48. }
  49. }
  50. else
  51. size = pFile->size();
  52. }
  53. return size;
  54. }
  55. class CDeployTask : public CInterface, implements IDeployTask
  56. {
  57. public:
  58. IMPLEMENT_IINTERFACE;
  59. //---------------------------------------------------------------------------
  60. // CDeployTask
  61. //---------------------------------------------------------------------------
  62. CDeployTask(IDeploymentCallback& callback,
  63. const char* caption, const char* processType, const char* comp,
  64. const char* instance, const char* source, const char* target,
  65. const char* sshUser, const char* sshKeyFile, const char* sshKeyPassphrase,
  66. bool useSsh, EnvMachineOS os, const char* processName=NULL)
  67. : m_caption(caption), m_compName(comp),
  68. m_instanceName(instance), m_processed(false), m_errorCode(0),
  69. m_abort(false), m_dummy(false), m_machineOS(os), m_flags(0),
  70. m_processType(processType),
  71. m_processName(processName),
  72. m_targetFileWithWrongCase(false),
  73. m_sshUser(sshUser),
  74. m_sshKeyFile(sshKeyFile),
  75. m_sshKeyPassphrase(sshKeyPassphrase),
  76. m_useSsh(useSsh),
  77. m_updateProgress(true)
  78. {
  79. m_pCallback.set(&callback);
  80. m_fileSpec[DT_SOURCE].set(source ? source : "");
  81. m_fileSpec[DT_TARGET].set(target ? target : "");
  82. }
  83. //---------------------------------------------------------------------------
  84. // getCaption
  85. //---------------------------------------------------------------------------
  86. const char* getCaption() const
  87. {
  88. return m_caption;
  89. }
  90. //---------------------------------------------------------------------------
  91. // getCaption
  92. //---------------------------------------------------------------------------
  93. void setCaption(const char* caption)
  94. {
  95. m_caption.set(caption);
  96. }
  97. //---------------------------------------------------------------------------
  98. // getCompName
  99. //---------------------------------------------------------------------------
  100. const char* getCompName() const
  101. {
  102. return m_compName;
  103. }
  104. //---------------------------------------------------------------------------
  105. // getInstanceName
  106. //---------------------------------------------------------------------------
  107. const char* getInstanceName() const
  108. {
  109. return m_instanceName;
  110. }
  111. //---------------------------------------------------------------------------
  112. // getFileSpec
  113. //---------------------------------------------------------------------------
  114. const char* getFileSpec(int idx) const
  115. {
  116. assertex(idx >= 0 && idx < 2);
  117. return m_fileSpec[idx];
  118. }
  119. //---------------------------------------------------------------------------
  120. // getFileName
  121. //---------------------------------------------------------------------------
  122. const char* getFileName(int idx) const
  123. {
  124. assertex(idx >= 0 && idx < 2);
  125. return pathTail(m_fileSpec[idx]);
  126. }
  127. //---------------------------------------------------------------------------
  128. // getFileExt
  129. //---------------------------------------------------------------------------
  130. const char* getFileExt(int idx) const
  131. {
  132. return findFileExtension(m_fileSpec[idx]);
  133. }
  134. //---------------------------------------------------------------------------
  135. // getFilePath
  136. //---------------------------------------------------------------------------
  137. StringAttr getFilePath(int idx) const
  138. {
  139. assertex(idx >= 0 && idx < 2);
  140. StringBuffer dir;
  141. splitDirTail(m_fileSpec[idx], dir);
  142. unsigned int dirLen = dir.length();
  143. if (dirLen-- && isPathSepChar(dir.charAt(dirLen)))//remove trailing \ or /
  144. dir.setLength(dirLen);
  145. return dir.str();
  146. }
  147. //---------------------------------------------------------------------------
  148. // getFileExists
  149. //---------------------------------------------------------------------------
  150. bool getFileExists(int idx) const
  151. {
  152. assertex(idx >= 0 && idx < 2);
  153. if (m_machineOS == MachineOsLinux && m_sshUser.length() && m_sshKeyFile.length())
  154. return checkSSHFileExists(m_fileSpec[idx]);
  155. else
  156. {
  157. Owned<IFile> pFile = createIFile(m_fileSpec[idx]);
  158. return pFile->exists();
  159. }
  160. }
  161. //---------------------------------------------------------------------------
  162. // getFileSize
  163. //---------------------------------------------------------------------------
  164. offset_t getFileSize(int idx) const
  165. {
  166. assertex(idx >= 0 && idx < 2);
  167. Owned<IFile> pFile = createIFile(m_fileSpec[idx]);
  168. return pFile->size();
  169. }
  170. EnvMachineOS getMachineOS() const
  171. {
  172. return m_machineOS;
  173. }
  174. const char* getSSHUser() const
  175. {
  176. return m_sshUser.str();
  177. }
  178. const char* getSSHKeyFile() const
  179. {
  180. return m_sshKeyFile.str();
  181. }
  182. const char* getSSHKeyPassphrase() const
  183. {
  184. return m_sshKeyPassphrase.str();
  185. }
  186. bool checkSSHFileExists(const char* filename) const
  187. {
  188. bool flag = false;
  189. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Ensure Path", m_processType, m_compName,
  190. m_instanceName, filename, NULL, m_sshUser.str(), m_sshKeyFile.str(), m_sshKeyPassphrase.str(),
  191. true, m_machineOS);
  192. try
  193. {
  194. StringBuffer destpath, destip, cmd, output, tmp, err;
  195. stripNetAddr(filename, destpath, destip);
  196. tmp.appendf("%d", msTick());
  197. cmd.clear().appendf("[ -e %s ] && echo %s", destpath.str(), tmp.str());
  198. task->execSSHCmd(destip.str(), cmd, output, err);
  199. flag = strstr(output.str(), tmp.str()) == output.str();
  200. }
  201. catch(IException* e)
  202. {
  203. throw e;
  204. }
  205. return flag;
  206. }
  207. void setFileSpec(int idx, const char* file)
  208. {
  209. assertex(idx >= 0 && idx < 2);
  210. m_fileSpec[idx].set(file && *file ? file : "");
  211. }
  212. //---------------------------------------------------------------------------
  213. // isProcessed
  214. //---------------------------------------------------------------------------
  215. bool isProcessed() const
  216. {
  217. return m_processed;
  218. }
  219. //---------------------------------------------------------------------------
  220. // getErrorCode
  221. //---------------------------------------------------------------------------
  222. DWORD getErrorCode() const
  223. {
  224. return m_errorCode;
  225. }
  226. //---------------------------------------------------------------------------
  227. // getErrorString
  228. //---------------------------------------------------------------------------
  229. const char* getErrorString() const
  230. {
  231. return m_errorString.str();
  232. }
  233. //---------------------------------------------------------------------------
  234. // getWarnings
  235. //---------------------------------------------------------------------------
  236. const char* getWarnings() const
  237. {
  238. return m_warnings.str();
  239. }
  240. //---------------------------------------------------------------------------
  241. // getAbort
  242. //---------------------------------------------------------------------------
  243. virtual bool getAbort() const
  244. {
  245. return m_abort;
  246. }
  247. //---------------------------------------------------------------------------
  248. // getFlags
  249. //---------------------------------------------------------------------------
  250. unsigned getFlags() const
  251. {
  252. return m_flags;
  253. }
  254. //---------------------------------------------------------------------------
  255. // setFlags
  256. //---------------------------------------------------------------------------
  257. void setFlags(unsigned flags)
  258. {
  259. m_flags = flags;
  260. }
  261. void setUpdateProgress(bool flag)
  262. {
  263. m_updateProgress = flag;
  264. }
  265. //---------------------------------------------------------------------------
  266. // createFile
  267. //---------------------------------------------------------------------------
  268. bool createFile(const char* text)
  269. {
  270. const char* szOpenMode = m_machineOS == MachineOsLinux ? "wb" : "wt";
  271. m_processed = true;
  272. assertex(text);
  273. const char* target = getFileSpec(DT_TARGET);
  274. while (true)
  275. {
  276. m_errorCode = 0;
  277. m_warnings.clear();
  278. m_errorString.clear();
  279. if (m_dummy) break;
  280. FILE* fp = fopen(target, szOpenMode);
  281. if (fp)
  282. {
  283. DWORD bytesWritten = 0;
  284. bytesWritten = fwrite(text, 1, strlen(text), fp);
  285. if (bytesWritten == strlen(text))
  286. {
  287. fflush(fp);
  288. fclose(fp);
  289. break;
  290. }
  291. }
  292. // Prompt to retry on error
  293. m_errorCode = getLastError();
  294. m_errorString.appendf("Cannot create %s: ", target);
  295. if (fp) fclose(fp); // do this after retrieving last error
  296. if (showErrorMsg("Error creating file")) //ignore or abort?
  297. break;
  298. }
  299. return (m_errorCode == 0);
  300. }
  301. //---------------------------------------------------------------------------
  302. // transformFile
  303. //---------------------------------------------------------------------------
  304. bool transformFile(IXslProcessor& processor, IXslTransform& transform, const char* tempPath)
  305. {
  306. m_processed = true;
  307. const char* xsl = getFileSpec(DT_SOURCE);
  308. const char* target = getFileSpec(DT_TARGET);
  309. while (true)
  310. {
  311. try
  312. {
  313. m_errorCode = 0;
  314. m_warnings.clear();
  315. m_errorString.clear();
  316. const char* processName = m_processName ? m_processName : m_compName;
  317. transform.setParameter("process", StringBuffer("'").append(processName).append("'").str());
  318. transform.setParameter("outputFilePath", StringBuffer("'").append(target).append("'").str());
  319. transform.setParameter("tempPath", StringBuffer("'").append(tempPath).append("'").str());
  320. if (m_machineOS == MachineOsLinux)
  321. transform.setParameter("isLinuxInstance", "1");//set optional parameter
  322. if (m_instanceName.length())
  323. transform.setParameter("instance", StringBuffer("'").append(m_instanceName).append("'").str());
  324. transform.loadXslFromFile(xsl);
  325. if (!m_dummy)
  326. {
  327. if(m_machineOS != MachineOsLinux)
  328. {
  329. transform.setResultTarget(target);
  330. Owned<IFile> pTargetFile = createIFile(target);
  331. if (pTargetFile->exists() && pTargetFile->isReadOnly())
  332. pTargetFile->setReadOnly(false);
  333. pTargetFile.clear();
  334. processor.execute(&transform);
  335. }
  336. else
  337. {
  338. char tempfile[_MAX_PATH];
  339. StringBuffer sb;
  340. StringBuffer sbtmp;
  341. sbtmp.appendf("%d", msTick());
  342. bool flag = false;
  343. {
  344. Owned<IFile> pTargetFile;
  345. StringBuffer tmpoutbuf;
  346. transform.transform(tmpoutbuf);
  347. tmpoutbuf.replaceString("\r\n", "\n");
  348. if (m_useSsh && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
  349. {
  350. StringBuffer destpath, ip;
  351. stripNetAddr(target, destpath,ip);
  352. if (ip.length())
  353. {
  354. getTempPath(tempfile, sizeof(tempfile), m_compName);
  355. sb.append(tempfile).append(sbtmp);
  356. pTargetFile.setown(createIFile(sb.str()));
  357. flag = true;
  358. }
  359. }
  360. if (!flag)
  361. pTargetFile.setown(createIFile(target));
  362. if (pTargetFile->exists() && pTargetFile->isReadOnly())
  363. pTargetFile->setReadOnly(false);
  364. Owned<IFileIO> pTargetFileIO = pTargetFile->openShared(IFOcreate, IFSHfull);
  365. pTargetFileIO->write( 0, tmpoutbuf.length(), tmpoutbuf.str());
  366. }
  367. if (flag)
  368. {
  369. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Copy File",
  370. m_processType, m_compName, m_instanceName, sb.str(), target, m_sshUser.str(),
  371. m_sshKeyFile.str(), m_sshKeyPassphrase.str(), true, m_machineOS);
  372. try
  373. {
  374. task->setUpdateProgress(false);
  375. task->copyFile(DEFLAGS_CONFIGFILES);
  376. }
  377. catch(IException* e)
  378. {
  379. if (!DeleteFile(sb.str()))
  380. WARNLOG("Couldn't delete file %s", sb.str());
  381. throw e;
  382. }
  383. if (!DeleteFile(sb.str()))
  384. WARNLOG("Couldn't delete file %s", sb.str());
  385. }
  386. }
  387. m_warnings.clear().append(transform.getMessages());
  388. transform.closeResultTarget();
  389. #ifdef _WIN32
  390. //Samba maps the Windows archive, system, and hidden file attributes to the owner, group, and world
  391. //execute bits of the file respectively. So if we are deploying to a Linux box then make the file
  392. //executable by setting its archive bit.
  393. //
  394. if (m_machineOS == MachineOsLinux && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty())
  395. ::SetFileAttributes(target, FILE_ATTRIBUTE_ARCHIVE);
  396. #else
  397. //UNIMPLEMENTED;
  398. #endif
  399. }
  400. break;
  401. }
  402. catch (IException* e)
  403. {
  404. StringBuffer warning;
  405. e->errorMessage(warning);
  406. //for better readability of warning, remove redundant prefix of "[XSLT warning: ", if present
  407. const char* pattern = "[XSLT warning: ";
  408. int len = strlen(pattern);
  409. if (!strncmp(warning, pattern, len))
  410. warning.remove(0, len);
  411. //remove the excessive info about XSLT context when this was thrown
  412. const char* begin = warning.str();
  413. const char* end = strstr(begin, ", style tree node:");
  414. if (end)
  415. warning.setLength(end-begin);
  416. m_errorString.appendf("Cannot create %s\nusing XSL transform %s\n\n%s", target, xsl, warning.str());
  417. e->Release();
  418. //remove incomplete (invalid) output file produced thus far
  419. if (!DeleteFile(target))
  420. WARNLOG("Couldn't delete file %s", target);
  421. }
  422. catch (...)
  423. {
  424. m_errorString.appendf("Cannot create %s\nusing XSL transform %s\n\n", target, xsl);
  425. m_errorString.append("Unspecified XSL error");
  426. //remove incomplete (invalid) output file produced thus far
  427. if (!DeleteFile(target))
  428. WARNLOG("Couldn't delete file %s", target);
  429. }
  430. // Prompt to retry on error
  431. m_errorCode = (DWORD) -1;//don't format m_errorString based on last error
  432. if (showErrorMsg("Error transforming file"))//ignore or abort?
  433. break;
  434. }
  435. return (m_errorCode == 0);
  436. }
  437. //---------------------------------------------------------------------------
  438. // copyFile
  439. //---------------------------------------------------------------------------
  440. bool copyFile(unsigned mode)
  441. {
  442. if (m_updateProgress)
  443. m_pCallback->printStatus(this);
  444. m_errorCode = 0;
  445. const char* source = getFileSpec(DT_SOURCE);
  446. const char* target = getFileSpec(DT_TARGET);
  447. m_msgBoxOwner = false; //this thread hasn't shown msg box yet
  448. while (true)
  449. {
  450. m_warnings.clear();
  451. if (m_pCallback->getAbortStatus())
  452. break;
  453. m_errorCode = 0;
  454. m_errorString.clear();
  455. if (m_dummy)
  456. break;
  457. unsigned dtcFlags = mode >> 2; //gets DTC_TIME | DTC_SIZE | DTC_CRC
  458. if (dtcFlags)//deploy only if different checked ??
  459. {
  460. if (compareFile(dtcFlags | DTC_DEL_WRONG_CASE) || m_pCallback->getAbortStatus()) //flags specified and files match - ignore
  461. {
  462. Owned<IFile> f = createIFile(source);
  463. m_pCallback->fileSizeCopied(f->size(),true);
  464. break;
  465. }
  466. else if (m_targetFileWithWrongCase)
  467. break;
  468. }
  469. else if (m_machineOS != MachineOsW2K && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty() &&
  470. (targetFileExistsWithWrongCaseSensitivity(true) || m_pCallback->getAbortStatus()))
  471. {
  472. break;
  473. }
  474. //clear file comparison failure errors generated by compareFile above
  475. m_processed = false;
  476. m_errorString.clear();
  477. m_errorCode = 0;
  478. Owned<IFile> pSrcFile;
  479. Owned<IFile> pDstFile;
  480. bool bCopyRC = false;
  481. try
  482. {
  483. pSrcFile.setown(createIFile(source));
  484. pDstFile.setown(createIFile(target));
  485. class CCopyFileProgress : implements ICopyFileProgress
  486. {
  487. public:
  488. CCopyFileProgress(): m_percentDone(0),m_sizeReported(0){}
  489. CCopyFileProgress(CDeployTask* pTask)
  490. : m_percentDone(0),m_sizeReported(0)
  491. {
  492. m_pTask.set(pTask);
  493. m_pCallback.set(&pTask->getCallback());
  494. m_originalCaption.append( pTask->getCaption() );
  495. }
  496. virtual CFPmode onProgress(unsigned __int64 sizeDone, unsigned __int64 totalSize)
  497. {
  498. CFPmode rc = m_pCallback->getAbortStatus() ? CFPcancel : CFPcontinue;
  499. StringBuffer newCaption;
  500. newCaption.append(m_originalCaption);
  501. if (sizeDone && sizeDone != totalSize)
  502. {
  503. unsigned int percentDone = (unsigned int) ((sizeDone*100)/totalSize);
  504. const unsigned int DELTA = 25;
  505. const unsigned int rounded = (percentDone / DELTA)*DELTA;
  506. if (rounded > m_percentDone )
  507. m_percentDone = rounded;
  508. else
  509. return rc;
  510. newCaption.appendf(" [%d%%]", m_percentDone);
  511. }
  512. if (strcmp(newCaption.str(), m_pTask->getCaption()))
  513. {
  514. m_pTask->setCaption(newCaption.str());
  515. m_pCallback->printStatus(m_pTask);
  516. }
  517. // report size copied
  518. if (sizeDone==totalSize)
  519. m_pCallback->fileSizeCopied(sizeDone-m_sizeReported, true);
  520. else if (sizeDone - m_sizeReported>1024) // 1k
  521. {
  522. m_pCallback->fileSizeCopied(sizeDone-m_sizeReported, false);
  523. m_sizeReported = sizeDone;
  524. }
  525. return rc;
  526. }
  527. virtual ~CCopyFileProgress()
  528. {
  529. if (m_pTask)
  530. if (0 != strcmp(m_originalCaption.str(), m_pTask->getCaption()))
  531. {
  532. m_pTask->setCaption(m_originalCaption);
  533. m_pCallback->printStatus(m_pTask);
  534. }
  535. }
  536. virtual void setDeployTask(CDeployTask* pTask)
  537. {
  538. m_pTask.set(pTask);
  539. m_pCallback.set(&pTask->getCallback());
  540. m_originalCaption.append( pTask->getCaption() );
  541. }
  542. private:
  543. Owned<CDeployTask> m_pTask;
  544. Owned<IDeploymentCallback> m_pCallback;
  545. StringBuffer m_originalCaption;
  546. unsigned int m_percentDone;
  547. offset_t m_sizeReported;
  548. };
  549. CCopyFileProgress copyProgress;
  550. if (m_updateProgress)
  551. copyProgress.setDeployTask(this);
  552. if (m_useSsh && m_machineOS == MachineOsLinux && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
  553. {
  554. int retcode;
  555. StringBuffer outbuf, cmdline, errbuf;
  556. StringBuffer destpath,ip;
  557. StringBuffer passphr;
  558. getKeyPassphrase(passphr);
  559. StringBuffer sb(source);
  560. bool flag = (sb.charAt(sb.length() - 1) == '*');
  561. if (flag)
  562. sb.setLength(sb.length() - 1);
  563. offset_t dirsize = getDirSize(sb.str());
  564. if (flag && m_updateProgress)
  565. copyProgress.onProgress(0, dirsize);
  566. stripNetAddr(target, destpath, ip);
  567. cmdline.appendf("pscp -p -noagent -q %s -i %s -l %s %s \"%s\" %s:%s", flag?"-r":"",
  568. m_sshKeyFile.str(), m_sshUser.str(), passphr.str(), source, ip.str(), destpath.str());
  569. retcode = pipeSSHCmd(cmdline.str(), outbuf, errbuf);
  570. if (retcode)
  571. {
  572. m_errorCode = -1;
  573. String err(errbuf.str());
  574. int index = err.indexOf('\n');
  575. String* perr = err.substring(0, index > 0? index : err.length());
  576. m_errorString.clear().appendf("%s", perr->str());
  577. delete perr;
  578. bCopyRC = false;
  579. }
  580. else
  581. {
  582. if (flag && m_updateProgress)
  583. copyProgress.onProgress(dirsize, dirsize);
  584. else if (m_updateProgress)
  585. copyProgress.onProgress(pSrcFile->size(), pSrcFile->size());
  586. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Chmod", m_processType, m_compName, m_instanceName,
  587. destpath.str(), NULL, m_sshUser.str(), m_sshKeyFile.str(), m_sshKeyPassphrase.str(), true, m_machineOS);
  588. try
  589. {
  590. StringBuffer cmd;
  591. cmd.clear().appendf("chmod -R 755 %s", destpath.str());
  592. task->execSSHCmd(ip.str(), cmd, outbuf, errbuf);
  593. }
  594. catch(IException* e)
  595. {
  596. throw e;
  597. }
  598. bCopyRC = true;
  599. }
  600. }
  601. else
  602. {
  603. ::copyFile(pDstFile, pSrcFile, 0x100000, &copyProgress);
  604. bCopyRC = true;
  605. }
  606. }
  607. catch (IException* e)
  608. {
  609. e->Release();
  610. bCopyRC = false;
  611. m_errorCode = getLastError();
  612. }
  613. catch (...)
  614. {
  615. bCopyRC = false;
  616. m_errorCode = getLastError();
  617. }
  618. //if copy has failed due to access denied error, then change destination
  619. //file's attributes to normal (reset read only flag, if set) and retry
  620. if (m_machineOS != MachineOsLinux && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty())
  621. {
  622. if (!bCopyRC && (pDstFile.get() != NULL) && pDstFile->exists() && pDstFile->isReadOnly()/*&& m_machineOS != MachineOsLinux*/)
  623. {
  624. try
  625. {
  626. //BUG#48891 Handle the exception here to allow the user to retry or ignore.
  627. pDstFile->setReadOnly(false);
  628. continue;
  629. }
  630. catch(IException* e)
  631. {
  632. e->Release();
  633. }
  634. }
  635. }
  636. /* This method is invoked from multiple threads concurrently and implements the
  637. following logic. There are global retry and abort flags that can be set by
  638. any thread and are shared among threads to coordinate file copying process.
  639. The first thread that has an error and grabs the mutex pops up the error
  640. message box to the user. If the user selects "abort", the global abort flag is
  641. set and the thread exits. Other threads with or without errors check this
  642. abort flag and exit if it is set. If the user selected "ignore", the thread
  643. simply exits with error condition and other threads operate unaffected. However,
  644. if the user selected "retry" then we don't want other pending threads with error
  645. conditions to keep popping up error messages asking for abort/retry/ignore. The
  646. first thread becomes a "master retry thread" for lack of a better term. This
  647. thread dominates popping of the message boxes and other threads abide by user's
  648. wishes for the master retry thread. The idea is that the user should not be asked
  649. for every single file copy error since the cause of error in most scenarios could
  650. simply be of a global nature, for instance, that the target machine is not powered
  651. on or the target files are locked since the process is already running and needs
  652. to be stopped. An event semaphore is used by master retry thread to wake up all
  653. other threads with errors, which sense the retry flag and silently retry operation.
  654. */
  655. if (!bCopyRC)//file copy failed
  656. {
  657. // Prompt to retry on error
  658. m_errorString.appendf("Cannot copy %s to %s: ", source, target);
  659. synchronized block(s_monitor);
  660. if (m_pCallback->getAbortStatus())//has some other thread set the global abort flag?
  661. break; //go back to beginning of loop where we exit on abort
  662. enum responseType { r_abort = 1, r_retry, r_ignore };
  663. responseType rc;
  664. if (!s_msgBoxActive || m_msgBoxOwner) //no other thread has an active message box
  665. {
  666. //block other threads if they encounter error state until user responds
  667. s_msgBoxActive = true;
  668. m_msgBoxOwner = true;
  669. if (showErrorMsg("Error copying file")) //ignore or abort?
  670. {
  671. setRetryStatus(false);
  672. if (m_pCallback->getAbortStatus())
  673. {
  674. m_errorCode = (DWORD) -1;
  675. m_errorString.append("Aborted");
  676. rc = r_abort;
  677. }
  678. else
  679. rc = r_ignore;
  680. }
  681. else
  682. {
  683. setRetryStatus(true);
  684. rc = r_retry;
  685. }
  686. }
  687. else
  688. {
  689. //some other thread has an active message box
  690. //wait for that thread to signal us after it either successfully retries,
  691. //or if the user ignores that error or aborts deployment
  692. //
  693. s_monitor.wait();
  694. //respect user's wish to retry all queued failed operations
  695. rc = m_pCallback->getAbortStatus() ? r_abort : getRetryStatus() ? r_retry : r_ignore;
  696. }
  697. if (r_retry != rc)
  698. break;
  699. }
  700. else
  701. {
  702. //copy was successful
  703. if (m_machineOS != MachineOsLinux && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty())
  704. {
  705. pDstFile->setReadOnly(false);
  706. #ifdef _WIN32
  707. //Samba maps the Windows archive, system, and hidden file attributes to the owner, group, and world
  708. //execute bits of the file respectively. So if we are deploying to a Linux box then make the file
  709. //executable by setting its archive bit.
  710. //
  711. if (m_machineOS == MachineOsLinux)
  712. ::SetFileAttributes(target, FILE_ATTRIBUTE_ARCHIVE);
  713. #else
  714. //UNIMPLEMENTED;
  715. #endif
  716. }
  717. break;
  718. }
  719. }//while
  720. if (m_msgBoxOwner)//did this thread show the message box in last iteration of this loop?
  721. {
  722. synchronized block(s_monitor);
  723. s_msgBoxActive = false;
  724. m_msgBoxOwner = false;//up for grabs by other threads
  725. s_monitor.notifyAll();
  726. }
  727. else if (m_pCallback->getAbortStatus())
  728. {
  729. m_errorCode = (DWORD) -1;
  730. m_errorString.append("Aborted");
  731. }
  732. m_processed = true;
  733. return (m_errorCode == 0);
  734. }
  735. //---------------------------------------------------------------------------
  736. // renameFile
  737. //---------------------------------------------------------------------------
  738. bool renameFile()
  739. {
  740. m_processed = true;
  741. //IFile::move fails if you rename "\\ip\path\dir1" to "\\ip\path\dir2"
  742. //so try to rename to "dir2" if both source and dest have same path prefix
  743. //
  744. const char* src = getFileSpec(DT_SOURCE);
  745. const char* dst = getFileSpec(DT_TARGET);
  746. StringBuffer dstPath;
  747. bool linremote=(memcmp(src,"//",2)==0);
  748. if (linremote || !memcmp(src,"\\\\",2)) // see if remote path
  749. {
  750. RemoteFilename rfn;
  751. rfn.setRemotePath(dst);
  752. if (rfn.isLocal())
  753. {
  754. rfn.getLocalPath(dstPath);
  755. if (m_machineOS == MachineOsLinux)
  756. {
  757. dstPath.replace('\\', '/');
  758. dstPath.replace(':', '$');
  759. }
  760. else
  761. {
  762. dstPath.replace('/', '\\');
  763. dstPath.replace('$', ':');
  764. }
  765. dst = dstPath.str();
  766. }
  767. }
  768. Owned<IFile> pFile = createIFile(src);
  769. while (true)
  770. {
  771. m_errorCode = 0;
  772. m_warnings.clear();
  773. m_errorString.clear();
  774. try
  775. {
  776. if (m_dummy)
  777. break;
  778. pFile->move(dst);
  779. break;
  780. }
  781. catch(IException* e)
  782. {
  783. e->Release();
  784. }
  785. catch(...)
  786. {
  787. }
  788. // Prompt to retry on error
  789. m_errorString.clear().appendf("Cannot rename %s to %s: ", src, dst);
  790. if (showErrorMsg("Error renaming file"))//ignore or abort?
  791. break;
  792. }
  793. return (m_errorCode == 0);
  794. }
  795. //---------------------------------------------------------------------------
  796. // deleteFile
  797. //---------------------------------------------------------------------------
  798. bool deleteFile()
  799. {
  800. m_processed = true;
  801. const char* target = getFileSpec(DT_TARGET);
  802. m_errorCode = 0;
  803. m_warnings.clear();
  804. m_errorString.clear();
  805. bool rc = m_dummy || DeleteFile(target)==TRUE;
  806. m_errorCode = rc ? 0 : (DWORD) -1;
  807. return rc;
  808. }
  809. bool getFileAttributes(IFile* pFile, CDateTime& dt, offset_t& size, unsigned mode)
  810. {
  811. bool rc = true;
  812. try
  813. {
  814. if (mode & DTC_SIZE)
  815. size = pFile->size();
  816. if (mode & DTC_TIME)
  817. rc = pFile->getTime(NULL, &dt, NULL);
  818. }
  819. catch(IException* e)
  820. {
  821. e->Release();
  822. rc = false;
  823. }
  824. catch (...)
  825. {
  826. rc = false;
  827. }
  828. if (!rc)
  829. {
  830. m_errorCode = getLastError();
  831. m_errorString.clear().appendf("File %s does not exist or cannot be accessed: ", pFile->queryFilename());
  832. formatSystemError(m_errorString, m_errorCode);
  833. }
  834. return rc;
  835. }
  836. bool targetFileExistsWithWrongCaseSensitivity(bool bDeleteIfFound)
  837. {
  838. const char* target = getFileSpec(DT_TARGET);
  839. StringBuffer targetPath;
  840. const char* targetFileName = splitDirTail(target, targetPath);
  841. bool bLoop;
  842. bool rc = false;
  843. do
  844. {
  845. bLoop = false;
  846. Owned<IDirectoryIterator> pDirIter = createDirectoryIterator( targetPath, targetFileName );
  847. ForEach(*pDirIter)
  848. {
  849. StringBuffer existingFileName;
  850. pDirIter->getName(existingFileName);
  851. if (0 != strcmp(targetFileName, existingFileName))
  852. {
  853. StringBuffer msg;
  854. msg.appendf("File '%s' exists as '%s' and may interfere!", targetFileName, existingFileName.str());
  855. m_errorString.clear().append(msg);
  856. m_errorCode = (DWORD)-1;
  857. if (bDeleteIfFound)
  858. msg.append(" Would you like to delete the file and try again?");
  859. try
  860. {
  861. bool bIgnore = m_pCallback->processException( m_processType.get(), m_compName.get(), m_instanceName.get(), NULL,
  862. msg.str(), "File name conflict!", this);
  863. if (bIgnore)
  864. {
  865. m_processed = true;
  866. rc = true;
  867. }
  868. else
  869. {
  870. if (m_pCallback->getAbortStatus())
  871. {
  872. m_abort = true;
  873. rc = true;
  874. }
  875. else
  876. {
  877. if (bDeleteIfFound)
  878. {
  879. StringBuffer path;
  880. path.append(targetPath).append(existingFileName);
  881. Owned<IFile> pDstFile = createIFile(path.str());
  882. pDstFile->remove();
  883. }
  884. m_errorString.clear();
  885. m_errorCode = 0;
  886. bLoop = true;
  887. rc = false;
  888. }
  889. }
  890. }
  891. catch (IException* e)
  892. {
  893. e->Release();
  894. m_abort = m_pCallback->getAbortStatus();
  895. }
  896. catch (...)
  897. {
  898. m_abort = m_pCallback->getAbortStatus();
  899. }
  900. break; //found conflict so don't iterate directory any more
  901. }
  902. }//ForEach
  903. } while (bLoop);
  904. if (rc)
  905. m_targetFileWithWrongCase = true;
  906. return rc;
  907. }
  908. //---------------------------------------------------------------------------
  909. // compareFile
  910. //---------------------------------------------------------------------------
  911. bool compareFile(unsigned mode)
  912. {
  913. m_errorCode = 0;
  914. m_warnings.clear();
  915. if (m_dummy)
  916. {
  917. m_processed = true;
  918. return true;
  919. }
  920. const char* source = getFileSpec(DT_SOURCE);
  921. const char* target = getFileSpec(DT_TARGET);
  922. if (m_machineOS != MachineOsW2K)
  923. {
  924. if (m_useSsh && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
  925. {
  926. char digestStr[33];
  927. char* pStr = getMD5Checksum(source, digestStr);
  928. int retcode;
  929. StringBuffer outbuf, cmdline, errbuf;
  930. StringBuffer destpath,ip;
  931. stripNetAddr(target, destpath, ip);
  932. StringBuffer passphr;
  933. getKeyPassphrase(passphr);
  934. cmdline.appendf("plink -i %s -l %s %s %s md5sum %s", m_sshKeyFile.str(), m_sshUser.str(), passphr.str(), ip.str(), destpath.str());
  935. retcode = pipeSSHCmd(cmdline, outbuf, errbuf);
  936. m_processed = true;
  937. if (retcode == 0)
  938. {
  939. if (strncmp(outbuf.str(), pStr, 32))
  940. {
  941. m_errorCode = (DWORD) -1;
  942. m_errorString.clear().append("md5sum values do not compare");
  943. return false;
  944. }
  945. m_errorCode = 0;
  946. return true;
  947. }
  948. else
  949. {
  950. m_errorCode = (DWORD) -1;
  951. String err(errbuf.str());
  952. int index = err.indexOf('\n');
  953. String* perr = err.substring(0, index > 0? index : err.length());
  954. m_errorString.clear().appendf("%s", perr->str());
  955. delete perr;
  956. return false;
  957. }
  958. }
  959. const bool bDeleteIfFound = (mode & DTC_DEL_WRONG_CASE) != 0;
  960. if (!m_useSsh && targetFileExistsWithWrongCaseSensitivity(bDeleteIfFound))
  961. return false;
  962. }
  963. Owned<IFile> pSrcFile = createIFile(source);
  964. Owned<IFile> pDstFile = createIFile(target);
  965. CDateTime dtSrc;
  966. CDateTime dtDst;
  967. offset_t szSrc;
  968. offset_t szDst;
  969. if (!getFileAttributes(pSrcFile, dtSrc, szSrc, mode) ||
  970. !getFileAttributes(pDstFile, dtDst, szDst, mode))
  971. {
  972. m_processed = true;
  973. return false;
  974. }
  975. // Compare size
  976. if (mode & DTC_SIZE)
  977. {
  978. if (szSrc != szDst)
  979. {
  980. m_errorCode = (DWORD) -1;
  981. m_errorString.clear().append("File sizes do not compare");
  982. m_processed = true;
  983. return false;
  984. }
  985. }
  986. // Compare timestamp
  987. if (mode & DTC_TIME)
  988. {
  989. if (dtSrc.compare(dtDst))
  990. {
  991. m_errorCode = (DWORD) -1;
  992. m_errorString.clear().append("File timestamps do not compare");
  993. m_processed = true;
  994. return false;
  995. }
  996. }
  997. // Compare CRC
  998. if (mode & DTC_CRC)
  999. {
  1000. unsigned x = getFileCRC(source);
  1001. unsigned y = getFileCRC(target);
  1002. if (x != y)
  1003. {
  1004. m_errorCode = (DWORD) -1;
  1005. m_errorString.clear().append("File CRC values do not compare");
  1006. m_processed = true;
  1007. return false;
  1008. }
  1009. }
  1010. // Everything checked out
  1011. m_processed = true;
  1012. return true;
  1013. }
  1014. bool createDirectoryRecursive(const char* path)
  1015. {
  1016. StringBuffer machine;
  1017. StringBuffer localpath;
  1018. StringBuffer tail;
  1019. StringBuffer ext;
  1020. if (splitUNCFilename(path, &machine, &localpath, &tail, &ext))
  1021. {
  1022. if (machine.length() && localpath.length())
  1023. {
  1024. int len = localpath.length();
  1025. if (len && isPathSepChar(localpath[--len]))
  1026. localpath.setLength(len);
  1027. if (len == 0)
  1028. return true;
  1029. StringBuffer path2;
  1030. path2.append(machine).append(localpath);
  1031. if (createDirectoryRecursive(path2))
  1032. {
  1033. if (m_useSsh && m_machineOS == MachineOsLinux && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
  1034. {
  1035. int retcode;
  1036. Owned<IPipeProcess> pipe = createPipeProcess();
  1037. StringBuffer outbuf, cmdline, errbuf;
  1038. StringBuffer destpath,ip;
  1039. stripNetAddr(path, destpath, ip);
  1040. StringBuffer passphr;
  1041. getKeyPassphrase(passphr);
  1042. cmdline.appendf("plink -i %s -l %s %s %s %s %s", m_sshKeyFile.str(),
  1043. m_sshUser.str(), passphr.str(), ip.str(), "mkdir -p", destpath.str());
  1044. retcode = pipeSSHCmd(cmdline.str(), outbuf, errbuf);
  1045. if (retcode && retcode != 1)
  1046. {
  1047. m_errorCode = retcode;
  1048. return false;
  1049. }
  1050. else
  1051. return true;
  1052. }
  1053. else
  1054. {
  1055. // Check if directory already exists
  1056. Owned<IFile> pFile = createIFile(path);
  1057. if (pFile->exists())
  1058. {
  1059. if (!pFile->isDirectory())
  1060. throw MakeStringException(-1, "%s exists and is not a directory!", path);
  1061. }
  1062. else
  1063. pFile->createDirectory();//throws
  1064. return true;
  1065. }
  1066. }
  1067. }
  1068. return false;
  1069. }
  1070. else
  1071. return recursiveCreateDirectory(path);
  1072. }
  1073. //---------------------------------------------------------------------------
  1074. // createDirectory
  1075. //---------------------------------------------------------------------------
  1076. bool createDirectory()
  1077. {
  1078. m_processed = true;
  1079. m_errorCode = 0;
  1080. const char* target = getFileSpec(DT_TARGET);
  1081. while (true)
  1082. {
  1083. m_errorCode = 0;
  1084. m_warnings.clear();
  1085. m_errorString.clear();
  1086. if (m_dummy)
  1087. break;
  1088. StringBuffer path(target);
  1089. int len = path.length();
  1090. if (len && isPathSepChar(path[--len]))
  1091. path.setLength(len);
  1092. if (createDirectoryRecursive(path.str()))
  1093. break;
  1094. // Prompt to retry on error
  1095. m_errorString.clear().appendf("Cannot create directory %s: ", path.str());
  1096. if (showErrorMsg("Error creating directory"))//ignore or abort?
  1097. break;
  1098. }
  1099. return (m_errorCode == 0);
  1100. }
  1101. void copyRecursive(IFile& src, IFile& dest) //can throw IException
  1102. {
  1103. const char* srcPath = src.queryFilename();
  1104. const char* dstPath = dest.queryFilename();
  1105. while (true)
  1106. {
  1107. try
  1108. {
  1109. if (m_pCallback->getAbortStatus())
  1110. {
  1111. m_errorCode = (DWORD) -1;
  1112. m_errorString.append("Aborted");
  1113. break;
  1114. }
  1115. m_errorCode = 0;
  1116. m_errorString.clear();
  1117. if (m_dummy)
  1118. break;
  1119. if (src.isDirectory())
  1120. {
  1121. if (!dest.exists() && !dest.createDirectory())
  1122. throw MakeStringException(-1, "Failed to create directory %s", dest.queryFilename());
  1123. Owned<IDirectoryIterator> iSrcDirEntry = src.directoryFiles();
  1124. ForEach(*iSrcDirEntry)
  1125. {
  1126. IFile& srcDirEntry = iSrcDirEntry->query();
  1127. const char* dirEntryName = pathTail(srcDirEntry.queryFilename());
  1128. StringBuffer path;
  1129. path.append(dstPath).append(PATHSEPCHAR).append(dirEntryName);
  1130. Owned<IFile> destDirEntry = createIFile(path.str());
  1131. copyRecursive(srcDirEntry, *destDirEntry);
  1132. }
  1133. }
  1134. else
  1135. ::copyFile(&dest, &src);
  1136. break;
  1137. }
  1138. catch(IException* e)
  1139. {
  1140. e->Release();
  1141. }
  1142. catch(...)
  1143. {
  1144. }
  1145. m_errorString.appendf("Cannot copy %s to %s: ", srcPath, dstPath);
  1146. // Prompt to retry on error
  1147. if (showErrorMsg("Error copying file/directory"))//ignore or abort?
  1148. break;
  1149. }//while
  1150. }
  1151. //---------------------------------------------------------------------------
  1152. // copyDirectory
  1153. //---------------------------------------------------------------------------
  1154. bool copyDirectory()
  1155. {
  1156. m_processed = true;
  1157. m_warnings.clear();
  1158. Owned<IFile> pSrcFile = createIFile(getFileSpec(DT_SOURCE));
  1159. Owned<IFile> pDstFile = createIFile(getFileSpec(DT_TARGET));
  1160. copyRecursive(*pSrcFile, *pDstFile);
  1161. return (m_errorCode == 0);
  1162. }
  1163. //---------------------------------------------------------------------------
  1164. // createProcess
  1165. //---------------------------------------------------------------------------
  1166. bool createProcess(bool wait, const char* user, const char* pwd)
  1167. {
  1168. m_processed = true;
  1169. m_errorCode = 0;
  1170. #ifdef _WINDOWS
  1171. const char* target = getFileSpec(DT_TARGET);
  1172. std::string cmdLine = target;
  1173. std::string displayLine;
  1174. // Make sure target file exists
  1175. char cmdPath[_MAX_PATH];
  1176. strcpy(cmdPath, target);
  1177. char* pchSpace = strchr(cmdPath, ' ');// find command line parameters
  1178. if (pchSpace)
  1179. *pchSpace = '\0';// remove command line parameters
  1180. if (m_machineOS == MachineOsLinux)
  1181. {
  1182. const char* extension = findFileExtension(cmdPath);
  1183. if (extension && (!stricmp(extension, ".bat") || !stricmp(extension, ".exe")))
  1184. cmdPath[extension-cmdPath] = '\0';//rename programs like starup.bat to startup
  1185. }
  1186. Owned<IFile> pFile = createIFile(cmdPath);
  1187. while (!pFile->exists())
  1188. {
  1189. // Prompt to retry
  1190. m_errorCode = (DWORD) -1; //don't format m_errorString based on last error
  1191. m_warnings.clear();
  1192. m_errorString.clear().appendf("File not found: '%s'", cmdPath);
  1193. if (showErrorMsg("Error creating process"))//ignore or abort?
  1194. return false;
  1195. }
  1196. char modulePath[_MAX_PATH+1] = "";
  1197. //find out where configenv is running from and remove file name 'Configenv.exe'
  1198. #ifdef _WIN32
  1199. if (GetModuleFileName(NULL, modulePath, _MAX_PATH))
  1200. {
  1201. char* pch = strrchr(modulePath, '\\');
  1202. if (pch)
  1203. *++pch = '\0';
  1204. }
  1205. #endif
  1206. // Determine if process is remote or local
  1207. if (strlen(cmdPath)>2 && cmdPath[0]=='\\' && cmdPath[1]=='\\')
  1208. {
  1209. // Parse target into computer, dir, and cmd
  1210. char computer[_MAX_PATH];
  1211. strcpy(computer, cmdPath+2);
  1212. char* pchSlash = strchr(computer, '\\');
  1213. assert(pchSlash);
  1214. char* dir = pchSlash+1;
  1215. char* cmd = (char*)pathTail(dir);
  1216. assertex(computer && (computer < dir) && (dir < cmd));
  1217. *(dir-1) = '\0';
  1218. *(cmd-1) = '\0';
  1219. std::string sUser = user;
  1220. StringBuffer sPswd(pwd);
  1221. if (m_machineOS != MachineOsLinux)
  1222. {
  1223. // Replace '$' with ':' in dir part
  1224. char* x = dir;
  1225. while (x = strchr(x, '$')) *x++ = ':';
  1226. }
  1227. // Use psexec as default remote control program
  1228. #ifdef _WIN32
  1229. if (m_machineOS != MachineOsLinux)
  1230. {
  1231. if (!checkFileExists(".\\psexec.exe"))
  1232. throw MakeStringException(-1, "Configenv cannot find psexec.exe to execute the remote program!");
  1233. cmdLine = modulePath;
  1234. cmdLine.append("psexec.exe \\\\%computer -u %user -p %pwd -i %dir\\%cmd %dir");
  1235. }
  1236. else
  1237. {
  1238. if (!checkFileExists(".\\plink.exe"))
  1239. throw MakeStringException(-1, "Configenv cannot find plink.exe to execute the remote program!");
  1240. sUser = pathTail(user); //if user name is domain\user1 then just get user1
  1241. //replace all '\\' by '/'
  1242. char* x = dir;
  1243. while (x = strchr(x, '\\'))
  1244. *x++ = '/';
  1245. /* note that if we use plink (cmd line ssh client) for the first time with a computer,
  1246. it generates the following message:
  1247. The server's host key is not cached in the registry. You have no guarantee that the
  1248. server is the computer you think it is. The server's key fingerprint is:
  1249. 1024 aa:bb:cc:dd:ee:ff:gg:hh:ii:jj:kk:ll:mm:nn:oo:pp
  1250. If you trust this host, enter "y" to add the key to
  1251. PuTTY's cache and carry on connecting. If you want to carry on connecting just once,
  1252. without adding the key to the cache, enter "n".If you do not trust this host, press
  1253. Return to abandon the connection.
  1254. To get around this, we pipe "n" to plink without using its -batch parameter (since
  1255. that simply aborts the connection attempt). We need help from cmd.exe to do this though...
  1256. */
  1257. /* fix for bug# 6590: Error when trying to start/stop components from configenv
  1258. if the command being invoked is using cmd.exe and if the configenv has been invoked
  1259. with a UNC path (like \\machine\dir1\...\configenv.exe) then this would fail since
  1260. cmd.exe does not like to invoke commands with UNC names.
  1261. So we cannot use cmd.exe with UNC paths. Redirecting input file with 'n' does not
  1262. work either. So we cannot inhibit the prompt as shown above in this case and the
  1263. user must enter 'y' or 'n' when asked to cache the key. However, hitting enter
  1264. aborts the connection with return code 0 with the result that the user is notified
  1265. that command succeeded since plink returns success even though it aborted the connection.
  1266. Find out how configenv was invoked and use the following commands respectively:
  1267. if UNC : cmd /c \"echo y | .\\plink.exe -ssh -l %user -pw %pwd %computer /%dir/%cmd /%dir\"
  1268. otherwise: .\\plink.exe -ssh -l %user -pw %pwd %computer /%dir/%cmd /%dir
  1269. */
  1270. cmdLine.erase();
  1271. cmdLine = "cmd /c \"echo y | \"";
  1272. cmdLine.append(modulePath).append("plink\" -ssh -l %user -pw %pwd %computer sudo bash -c '/%dir/%cmd /%dir'\"");
  1273. StringBuffer sshUserid;
  1274. m_pCallback->getSshAccountInfo(sshUserid, sPswd);
  1275. sUser = sshUserid.str();
  1276. }//linux
  1277. #else
  1278. //TODO
  1279. #endif
  1280. // Replace known tokens except the password
  1281. const char* const tokens[] = { "%component", "%computer", "%user", "%dir", "%cmd" };
  1282. const char* values[] = { m_compName, computer, sUser.c_str(), dir, cmd };
  1283. const int count = sizeof(tokens) / sizeof(char*);
  1284. std::string::size_type pos;
  1285. for (int i = 0; i < count; i++)
  1286. {
  1287. while ((pos = cmdLine.find(tokens[i])) != std::string::npos)
  1288. cmdLine.replace(pos, strlen(tokens[i]), values[i] ? values[i] : "");
  1289. }
  1290. //replace %pwd by pwd in cmdLine but by <password> in displayLine
  1291. displayLine = cmdLine;
  1292. if ((pos = cmdLine.find("%pwd")) != std::string::npos)
  1293. cmdLine.replace(pos, strlen("%pwd"), sPswd.str() ? sPswd.str() : "" );
  1294. if ((pos = displayLine.find("%pwd")) != std::string::npos)
  1295. displayLine.replace(pos, strlen("%pwd"), "<password>");
  1296. }
  1297. bool rc = false;
  1298. const bool bCD = modulePath[0] == '\\' && modulePath[1] == '\\';
  1299. try
  1300. {
  1301. if (bCD)
  1302. {
  1303. char winDir[_MAX_PATH+1];
  1304. GetWindowsDirectory(winDir, _MAX_PATH);
  1305. _chdir(winDir);
  1306. }
  1307. rc = createProcess(cmdLine.c_str(), displayLine.c_str(), wait, m_machineOS == MachineOsLinux);
  1308. }
  1309. catch (IException* e)
  1310. {
  1311. if (bCD)
  1312. _chdir(modulePath);
  1313. throw e;
  1314. }
  1315. catch (...)
  1316. {
  1317. if (bCD)
  1318. _chdir(modulePath);
  1319. throw MakeStringException(-1, "Invalid exception!");
  1320. }
  1321. if (bCD)
  1322. _chdir(modulePath);
  1323. return rc;
  1324. #else
  1325. return true;
  1326. #endif
  1327. }
  1328. //---------------------------------------------------------------------------
  1329. // createProcess
  1330. //---------------------------------------------------------------------------
  1331. bool createProcess(const char* cmdLine, const char* displayLine, bool wait, bool captureOutput)
  1332. {
  1333. #ifdef _WINDOWS
  1334. char tempfile[_MAX_PATH];
  1335. const char* processName = m_processName ? m_processName : m_compName;
  1336. getTempPath(tempfile, sizeof(tempfile), processName);
  1337. strcat(tempfile, "rexec");
  1338. // Make sure file name is unique - at least during this session
  1339. Owned<IEnvDeploymentEngine> pEnvDepEngine = m_pCallback->getEnvDeploymentEngine();
  1340. sprintf(&tempfile[strlen(tempfile)], "%d.out", pEnvDepEngine->incrementTempFileCount());
  1341. while (true)
  1342. {
  1343. m_errorCode = 0;
  1344. m_warnings.clear();
  1345. m_errorString.clear();
  1346. // Launch the process
  1347. if (m_dummy)
  1348. break;
  1349. DeleteFile( tempfile );
  1350. if (invoke_program(cmdLine, m_errorCode, wait, captureOutput ? tempfile : NULL))
  1351. {
  1352. if (m_errorCode)
  1353. {
  1354. m_errorString.appendf("Process '%s' returned exit code of %d: ", displayLine, m_errorCode);
  1355. m_errorCode = -1; //so showErrorMsg() does not reformat m_errorString
  1356. }
  1357. StringBuffer& outputStr = m_errorCode ? m_errorString : m_warnings;
  1358. Owned<IFile> pFile = createIFile( tempfile );
  1359. if (pFile->exists())
  1360. {
  1361. Owned<IFileIO> pFileIO = pFile->open(IFOread);
  1362. offset_t sz = pFile->size();
  1363. if (sz)
  1364. {
  1365. if (m_errorCode)
  1366. m_errorString.append("\n\n");
  1367. unsigned int len = outputStr.length();
  1368. outputStr.ensureCapacity(len+sz);
  1369. if (sz == pFileIO->read(0, sz, (void*)(outputStr.str() + len)))
  1370. outputStr.setLength(sz+len);
  1371. }
  1372. }
  1373. DeleteFile( tempfile );
  1374. }
  1375. else
  1376. m_errorString.appendf("Cannot create process '%s': ", displayLine);
  1377. if (!m_errorString.length() || showErrorMsg("Error executing process"))//ignore or abort?
  1378. break;
  1379. }
  1380. return (m_errorCode == 0);
  1381. #else
  1382. return true;
  1383. #endif
  1384. }
  1385. virtual bool execSSHCmd(const char* ip, const char* cmd, StringBuffer& output, StringBuffer& errmsg)
  1386. {
  1387. if (!ip || !*ip || !cmd || !*cmd || m_sshUser.isEmpty() || m_sshKeyFile.isEmpty())
  1388. {
  1389. errmsg.append("Invalid SSH params");
  1390. return false;
  1391. }
  1392. int retcode;
  1393. Owned<IPipeProcess> pipe = createPipeProcess();
  1394. StringBuffer cmdline;
  1395. StringBuffer passphr;
  1396. getKeyPassphrase(passphr);
  1397. cmdline.appendf("plink -i %s -l %s %s %s %s", m_sshKeyFile.str(), m_sshUser.str(), passphr.str(), ip, cmd);
  1398. retcode = pipeSSHCmd(cmdline.str(), output, errmsg);
  1399. m_processed = true;
  1400. if (retcode && retcode != 1)
  1401. {
  1402. m_errorCode = retcode;
  1403. String err(errmsg.str());
  1404. int index = err.indexOf('\n');
  1405. String* perr = err.substring(0, index > 0? index : err.length());
  1406. m_errorString.clear().appendf("%s", perr->str());
  1407. delete perr;
  1408. return false;
  1409. }
  1410. else
  1411. return true;
  1412. }
  1413. int pipeSSHCmd(const char* cmdline, StringBuffer& outbuf, StringBuffer& errbuf)
  1414. {
  1415. int retcode = 1;
  1416. Owned<IPipeProcess> pipe = createPipeProcess();
  1417. StringBuffer workdir("");
  1418. if (pipe->run("ConfigEnv", cmdline, workdir, FALSE, true, true))
  1419. {
  1420. byte buf[4096*2];
  1421. size32_t read = pipe->read(sizeof(buf), buf);
  1422. outbuf.append(read,(const char *)buf);
  1423. if (strstr(outbuf.str(), "Passphrase for key "))
  1424. {
  1425. retcode = 5;
  1426. errbuf.clear().append("Invalid or missing key passphrase. ");
  1427. pipe->abort();
  1428. return retcode;
  1429. }
  1430. retcode = pipe->wait();
  1431. read = pipe->readError(sizeof(buf), buf);
  1432. errbuf.append(read,(const char *)buf);
  1433. if (strstr(errbuf.str(), "Wrong passphrase"))
  1434. {
  1435. retcode = -1;
  1436. errbuf.clear().append("Invalid or missing key passphrase");
  1437. }
  1438. else if (strstr(errbuf.str(), "The server's host key is not cached in the registry."))
  1439. {
  1440. Owned<IPipeProcess> pipe1 = createPipeProcess();
  1441. StringBuffer cmd;
  1442. cmd.appendf("cmd /c \" echo y | %s \"", cmdline);
  1443. if (pipe1->run("ConfigEnv",cmd.str(),workdir,FALSE,true,true))
  1444. {
  1445. size32_t read = pipe1->read(sizeof(buf), buf);
  1446. outbuf.append(read,(const char *)buf);
  1447. retcode = pipe1->wait();
  1448. read = pipe->readError(sizeof(buf), buf);
  1449. if (read)
  1450. {
  1451. errbuf.append(" ").append(read,(const char *)buf);
  1452. retcode = 7;
  1453. }
  1454. else
  1455. errbuf.clear();
  1456. }
  1457. }
  1458. else if (errbuf.length())
  1459. {
  1460. const char* psz = strstr(errbuf.str(), "Authenticating with public key \"");
  1461. if (!psz || psz != errbuf.str())
  1462. retcode = 2;
  1463. else
  1464. {
  1465. const char* psz1 = psz + strlen("Authenticating with public key \"");
  1466. const char* psz2 = strstr(psz1, "\"\r\n");
  1467. psz1 = psz2 + strlen("\"\r\n");
  1468. if (strlen(psz1))
  1469. retcode = 2;
  1470. }
  1471. }
  1472. }
  1473. return retcode;
  1474. }
  1475. void DisconnectNetworkConnection(const char* remoteNameOrIp)
  1476. {
  1477. #ifdef _WINDOWS
  1478. IpAddress ip;
  1479. if (!ip.ipset(remoteNameOrIp))
  1480. throw MakeStringException(-1, "Cannot resolve %s", remoteNameOrIp);
  1481. StringBuffer remoteIP;
  1482. ip.getIpText(remoteIP);
  1483. HANDLE hEnum;
  1484. DWORD dwResult = WNetOpenEnum( RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL, &hEnum );
  1485. if (dwResult != NO_ERROR)
  1486. throw MakeStringException(-1, "Cannot enumerate existing network connections!" );
  1487. else
  1488. {
  1489. do
  1490. {
  1491. DWORD cbBuffer = 16384;
  1492. LPNETRESOURCE lpnrDrv = (LPNETRESOURCE) GlobalAlloc( GPTR, cbBuffer );
  1493. DWORD cEntries = 0xFFFFFFFF;
  1494. dwResult = WNetEnumResource( hEnum, &cEntries, lpnrDrv, &cbBuffer);
  1495. if (dwResult == NO_ERROR)
  1496. {
  1497. for(DWORD i = 0; i < cEntries; i++ )
  1498. {
  1499. char nameOrIp[MAX_PATH];
  1500. strcpy(nameOrIp, lpnrDrv[i].lpRemoteName+2);
  1501. char* pch = strchr(nameOrIp, '\\');
  1502. if (pch)
  1503. *pch = '\0';
  1504. if (!ip.ipset(nameOrIp))
  1505. {
  1506. GlobalFree( (HGLOBAL) lpnrDrv );
  1507. WNetCloseEnum(hEnum);
  1508. throw MakeStringException(-1, "Cannot resolve host %s", nameOrIp);
  1509. }
  1510. StringBuffer ipAddr;
  1511. ip.getIpText(ipAddr);
  1512. if (!stricmp(remoteIP.str(), ipAddr.str()))
  1513. {
  1514. //we are already connected to this network resource with another user id
  1515. //so disconnect from it...and attempt to reconnect
  1516. //
  1517. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Disconnecting from %s...", lpnrDrv[i].lpRemoteName);
  1518. m_pCallback->printStatus(this);
  1519. dwResult = ::WNetCancelConnection2(lpnrDrv[i].lpRemoteName, CONNECT_UPDATE_PROFILE, FALSE);
  1520. if (dwResult != NO_ERROR)
  1521. {
  1522. m_errorCode = dwResult;
  1523. m_errorString.appendf("Error disconnecting from %s: ", lpnrDrv[i].lpRemoteName);
  1524. formatSystemError(m_errorString, m_errorCode);
  1525. throw MakeStringException(-1, m_errorString.str());
  1526. }
  1527. m_pCallback->printStatus(this);
  1528. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  1529. dwResult = ERROR_NO_MORE_ITEMS; //to break out of outer loop
  1530. break;//only disconnect from the first one since this method will be invoked again if more exist
  1531. }
  1532. }//for
  1533. }
  1534. else
  1535. if( dwResult != ERROR_NO_MORE_ITEMS )
  1536. {
  1537. GlobalFree( (HGLOBAL) lpnrDrv );
  1538. WNetCloseEnum(hEnum);
  1539. throw MakeStringException(-1, "Cannot complete enumeration for connections to \\%s", remoteNameOrIp);
  1540. }
  1541. GlobalFree( (HGLOBAL) lpnrDrv );
  1542. } while( dwResult != ERROR_NO_MORE_ITEMS );
  1543. WNetCloseEnum(hEnum);
  1544. }
  1545. #endif
  1546. }
  1547. //---------------------------------------------------------------------------
  1548. // connectTarget
  1549. //---------------------------------------------------------------------------
  1550. bool connectTarget(const char* user, const char* pwd, bool bInteractiveMode/*=true*/)
  1551. {
  1552. m_processed = true;
  1553. #if defined( _WIN32) && !defined (_DEBUG)
  1554. if (getenv("LN_CFG_SkipConnectTarget") != NULL)
  1555. {
  1556. m_errorCode = 0;
  1557. m_errorString.clear();
  1558. m_warnings.clear();
  1559. return (m_errorCode == 0);
  1560. }
  1561. NETRESOURCE netRes;
  1562. memset(&netRes, 0, sizeof(netRes));
  1563. netRes.dwType = RESOURCETYPE_DISK;
  1564. StringBuffer uncPath(getFileSpec(DT_TARGET));
  1565. unsigned int uncPathLen = uncPath.length();
  1566. if (uncPathLen-- && isPathSepChar(uncPath.charAt(uncPathLen)))//remove trailing \ or /
  1567. uncPath.setLength(uncPathLen);
  1568. netRes.lpRemoteName = (char*) uncPath.str();
  1569. while (true)
  1570. {
  1571. m_errorCode = 0;
  1572. m_errorString.clear();
  1573. m_warnings.clear();
  1574. if (m_dummy) return true;
  1575. HWND hCallback = (HWND) m_pCallback->getWindowHandle();
  1576. DWORD errCode = ::WNetAddConnection3(hCallback, &netRes, pwd, user, bInteractiveMode ? CONNECT_INTERACTIVE : 0);
  1577. if (errCode != NO_ERROR)
  1578. {
  1579. if (errCode == ERROR_SESSION_CREDENTIAL_CONFLICT)
  1580. {
  1581. char hostNameOrIp[MAX_PATH];
  1582. strcpy(hostNameOrIp, netRes.lpRemoteName+2);
  1583. char* pch = strchr(hostNameOrIp, '\\');
  1584. if (pch)
  1585. *pch = '\0';
  1586. try {
  1587. DisconnectNetworkConnection(hostNameOrIp);
  1588. }
  1589. catch (IException* e) {
  1590. e->Release();
  1591. m_errorCode = (DWORD) -1;
  1592. }
  1593. catch (...) {
  1594. m_errorCode = (DWORD) -1;
  1595. }
  1596. if (m_errorCode)
  1597. {
  1598. StringBuffer caption;
  1599. caption.appendf("Error connecting to %s: ", netRes.lpRemoteName);
  1600. m_errorString.clear().append("The target machine is already connected as a different user name.\n"
  1601. "Please disconnect any mapped network drive or active sessions.");
  1602. if (showErrorMsg(caption.str()))//ignore or abort?
  1603. break;
  1604. }
  1605. if (m_pCallback->getAbortStatus())
  1606. {
  1607. m_errorCode = (DWORD) -1;
  1608. m_errorString.append("Aborted");
  1609. break;
  1610. }
  1611. continue;
  1612. }
  1613. m_errorCode = errCode;
  1614. m_errorString.clear().appendf("Error connecting to %s: ", netRes.lpRemoteName);
  1615. if (showErrorMsg("Error connecting to target"))//ignore or abort?
  1616. break;
  1617. }
  1618. else
  1619. break;
  1620. }
  1621. #else
  1622. m_errorCode = 0;
  1623. m_errorString.clear();
  1624. m_warnings.clear();
  1625. #endif
  1626. return (m_errorCode == 0);
  1627. }
  1628. //---------------------------------------------------------------------------
  1629. // disconnectTarget
  1630. //---------------------------------------------------------------------------
  1631. bool disconnectTarget()
  1632. {
  1633. m_processed = true;
  1634. m_errorCode = 0;
  1635. m_errorString.clear();
  1636. m_warnings.clear();
  1637. #ifdef _WIN32
  1638. if (m_dummy) return true;
  1639. DWORD errCode = ::WNetCancelConnection2(getFileSpec(DT_TARGET), CONNECT_UPDATE_PROFILE, FALSE);
  1640. if (errCode != NO_ERROR)
  1641. {
  1642. m_errorCode = errCode;
  1643. m_errorString.appendf("Error disconnecting from %s: ", getFileSpec(DT_TARGET));
  1644. formatSystemError(m_errorString, m_errorCode);
  1645. //don't display error since deployment of one component (like esp) may involve
  1646. //deploying multiple others (like esp service modules) and we would get disconnection
  1647. //failures as a result in any case.
  1648. }
  1649. #endif
  1650. return (m_errorCode == 0);
  1651. }
  1652. //returns true if the caller needs to ignore or abort error; false to retry
  1653. //
  1654. bool showErrorMsg(const char* szTitle)
  1655. {
  1656. if (m_machineOS != MachineOsLinux || (m_machineOS == MachineOsLinux && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty()))
  1657. {
  1658. if (m_errorCode == 0)
  1659. m_errorCode = getLastError();
  1660. if (m_errorCode != (DWORD) -1)
  1661. formatSystemError(m_errorString, m_errorCode);
  1662. }
  1663. bool rc = true;//ignore
  1664. try
  1665. {
  1666. rc = m_pCallback->processException( m_processType.get(), m_compName.get(), m_instanceName.get(), NULL,
  1667. m_errorString, szTitle, this);
  1668. }
  1669. catch (IException* e)
  1670. {
  1671. e->Release();
  1672. m_abort = m_pCallback->getAbortStatus();
  1673. }
  1674. catch (...)
  1675. {
  1676. m_abort = m_pCallback->getAbortStatus();
  1677. }
  1678. return rc;
  1679. }
  1680. virtual void setErrorCode(DWORD code) { m_errorCode = code; }
  1681. virtual void setErrorString(const char* msg) { m_errorString = msg; }
  1682. virtual void setWarnings(const char* warnings) { m_warnings = warnings; }
  1683. virtual void setProcessed(bool bProcessed = true){ m_processed = bProcessed; }
  1684. static bool getRetryStatus() { return s_retry; }
  1685. static void setRetryStatus(bool status) { s_retry = status; }
  1686. virtual IDeploymentCallback& getCallback() const { return *m_pCallback; }
  1687. private:
  1688. char* getMD5Checksum(StringBuffer filename, char* digestStr)
  1689. {
  1690. if (filename.length() < 1)
  1691. return NULL;
  1692. if (!checkFileExists(filename.str()))
  1693. return NULL;
  1694. OwnedIFile ifile = createIFile(filename);
  1695. if (!ifile)
  1696. return NULL;
  1697. OwnedIFileIO ifileio = ifile->open(IFOread);
  1698. if (!ifileio)
  1699. return NULL;
  1700. size32_t len = (size32_t) ifileio->size();
  1701. if (len < 1)
  1702. return NULL;
  1703. char * buff = new char[1+len];
  1704. size32_t len0 = ifileio->read(0, len, buff);
  1705. buff[len0] = 0;
  1706. md5_state_t md5;
  1707. md5_byte_t digest[16];
  1708. md5_init(&md5);
  1709. md5_append(&md5, (const md5_byte_t *)buff, len0);
  1710. md5_finish(&md5, digest);
  1711. for (int i = 0; i < 16; i++)
  1712. sprintf(&digestStr[i*2],"%02x", digest[i]);
  1713. delete[] buff;
  1714. return digestStr;
  1715. }
  1716. void getKeyPassphrase(StringBuffer& passphr)
  1717. {
  1718. passphr.clear().append("-pw ");
  1719. if (m_sshKeyPassphrase.isEmpty())
  1720. passphr.clear();
  1721. else
  1722. {
  1723. StringBuffer sb;
  1724. decrypt(sb, m_sshKeyPassphrase.str());
  1725. passphr.append(sb.str());
  1726. }
  1727. }
  1728. private:
  1729. Owned<IDeploymentCallback> m_pCallback;
  1730. StringAttr m_caption;
  1731. StringAttr m_compName;
  1732. StringAttr m_instanceName;
  1733. StringAttr m_fileSpec[2];
  1734. StringAttr m_processName;
  1735. StringAttr m_processType;
  1736. StringAttr m_sshUser;
  1737. StringAttr m_sshKeyFile;
  1738. StringAttr m_sshKeyPassphrase;
  1739. int m_func;
  1740. bool m_processed;
  1741. DWORD m_errorCode;
  1742. StringBuffer m_errorString;
  1743. StringBuffer m_warnings;
  1744. bool m_targetFileWithWrongCase;
  1745. bool m_abort;
  1746. bool m_msgBoxOwner;//global flag set (used for multithreaded copying only)
  1747. bool m_dummy;
  1748. bool m_updateProgress;
  1749. bool m_useSsh;
  1750. EnvMachineOS m_machineOS;
  1751. unsigned m_flags;
  1752. static Monitor s_monitor;
  1753. static bool s_retry;
  1754. static bool s_msgBoxActive;
  1755. };
  1756. /*static*/ Monitor CDeployTask::s_monitor;
  1757. /*static*/ bool CDeployTask::s_retry = false;
  1758. /*static*/ bool CDeployTask::s_msgBoxActive = false;
  1759. //the following class implements a thread that asynchronously
  1760. //copies a given file. The current implementation of IDeployTask
  1761. //does not seemlessly lend itself to polymorphism since each task
  1762. //type has its own characteristic parameters and the caller must
  1763. //specifically invoke different methods like copyFile based on
  1764. //task type. Ideally, there needs to be only one worker method.
  1765. //
  1766. class CDeployTaskThread : public CInterface,
  1767. implements IDeployTaskThread
  1768. {
  1769. public:
  1770. IMPLEMENT_IINTERFACE;
  1771. CDeployTaskThread()
  1772. {
  1773. }
  1774. virtual ~CDeployTaskThread()
  1775. {
  1776. }
  1777. void init(void *startInfo)
  1778. {
  1779. m_pTask.set((IDeployTask*)startInfo);
  1780. }
  1781. void main()
  1782. {
  1783. m_pTask->copyFile( m_pTask->getFlags() );
  1784. static Mutex m;
  1785. m.lock();
  1786. try
  1787. {
  1788. m_pTask->getCallback().printStatus(m_pTask);
  1789. }
  1790. catch(IException* e)
  1791. {
  1792. e->Release();
  1793. }
  1794. m.unlock();
  1795. if (m_pTask && m_pTask->getAbort())
  1796. {
  1797. m_pTask->getCallback().printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Aborting, please wait...");
  1798. throw MakeStringException(0, "Abort");
  1799. }
  1800. }
  1801. bool canReuse()
  1802. {
  1803. return true;
  1804. }
  1805. bool stop()
  1806. {
  1807. return true;
  1808. }
  1809. virtual IDeployTask* getTask () const { return m_pTask; }
  1810. virtual void setTask (IDeployTask* pTask) { m_pTask.set(pTask); }
  1811. virtual bool getAbort() const { return s_abort; }
  1812. virtual void setAbort(bool bAbort) { s_abort = bAbort; }
  1813. private:
  1814. Owned<IDeployTask> m_pTask;
  1815. static bool s_abort;
  1816. };
  1817. class CDeployTaskThreadFactory : public CInterface, public IThreadFactory
  1818. {
  1819. public:
  1820. IMPLEMENT_IINTERFACE;
  1821. IPooledThread *createNew()
  1822. {
  1823. return new CDeployTaskThread();
  1824. }
  1825. };
  1826. bool CDeployTaskThread::s_abort = false;
  1827. //---------------------------------------------------------------------------
  1828. // Factory functions
  1829. //---------------------------------------------------------------------------
  1830. IDeployTask* createDeployTask(IDeploymentCallback& callback, const char* caption,
  1831. const char* processType, const char* comp,
  1832. const char* instance, const char* source,
  1833. const char* target, const char* sshUser, const char* sshKeyFile,
  1834. const char* sshKeyPassphrase, bool useSsh, EnvMachineOS os/* = MachineOsUnknown*/,
  1835. const char* processName/*=NULL*/)
  1836. {
  1837. return new CDeployTask(callback, caption, processType, comp, instance, source, target, sshUser, sshKeyFile,
  1838. sshKeyPassphrase, useSsh, os, processName);
  1839. }
  1840. IThreadFactory* createDeployTaskThreadFactory()
  1841. {
  1842. return new CDeployTaskThreadFactory();
  1843. }
  1844. void initializeMultiThreadedCopying()
  1845. {
  1846. CDeployTask::setRetryStatus(false);
  1847. }