DeployTask.cpp 69 KB

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