DeployTask.cpp 69 KB

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