DeployTask.cpp 70 KB

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