DeploymentEngine.cpp 103 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #pragma warning(disable : 4786)
  15. #include <functional>
  16. #include <algorithm>
  17. #include "deploy.hpp"
  18. #include "environment.hpp"
  19. #include "DeploymentEngine.hpp"
  20. #include "jexcept.hpp"
  21. #include "jfile.hpp"
  22. #include "jmisc.hpp"
  23. #include "jptree.hpp"
  24. #include "jmutex.hpp"
  25. #include "xslprocessor.hpp"
  26. #include "securesocket.hpp"
  27. #ifndef _WIN32
  28. #include <unistd.h>
  29. #endif
  30. /*static*/ CInstallFileList CDeploymentEngine::s_dynamicFileList;
  31. /*static*/ CDeploymentEngine* CDeploymentEngine::s_xsltDepEngine = NULL;//deployment engine context for XSLT
  32. /*static*/ bool CDeploymentEngine::s_bCacheableDynFile = false;
  33. //---------------------------------------------------------------------------
  34. // CDeploymentEngine
  35. //---------------------------------------------------------------------------
  36. CDeploymentEngine::CDeploymentEngine(IEnvDeploymentEngine& envDepEngine,
  37. IDeploymentCallback& callback,
  38. IPropertyTree &process,
  39. const char *instanceType,
  40. bool createIni)
  41. : m_envDepEngine(envDepEngine),
  42. m_environment(envDepEngine.getEnvironment()),
  43. m_process(process),
  44. m_instanceType(instanceType),
  45. m_abort(false),
  46. m_startable(unknown),
  47. m_stoppable(unknown),
  48. m_createIni(createIni),
  49. m_curInstance(NULL),
  50. m_instanceCheck(true)
  51. {
  52. m_pCallback.set(&callback);
  53. m_installFiles.setDeploymentEngine(*this);
  54. m_name.set(m_process.queryProp("@name"));
  55. m_rootNode.setown(&m_environment.getPTree());
  56. m_useSSHIfDefined = true;
  57. assertex(m_rootNode);
  58. // Get instances
  59. if (m_instanceType.length()==0)
  60. m_instances.append(OLINK(m_process));
  61. else
  62. {
  63. Owned<IPropertyTreeIterator> iter = m_process.getElements(m_instanceType);
  64. if (!iter->first())
  65. throw MakeStringException(0, "Process %s has no instances defined", m_name.get());
  66. for (iter->first(); iter->isValid(); iter->next())
  67. m_instances.append(iter->get());
  68. }
  69. // Get name to use for INI file - use buildset name
  70. if (m_createIni)
  71. m_iniFile.set(StringBuffer(m_process.queryProp("@buildSet")).append(".ini").str());
  72. }
  73. //---------------------------------------------------------------------------
  74. // ~CDeploymentEngine
  75. //---------------------------------------------------------------------------
  76. CDeploymentEngine::~CDeploymentEngine()
  77. {
  78. // Do disconnects
  79. set<string>::const_iterator iEnd = m_connections.end();
  80. set<string>::const_iterator i;
  81. for (i=m_connections.begin(); i!=iEnd; i++)
  82. {
  83. const char* path = (*i).c_str();
  84. if (!m_envDepEngine.IsPersistentConnection(path))
  85. disconnectHost( path );
  86. }
  87. if (m_externalFunction)
  88. m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction.get(), false);
  89. if (m_externalFunction2)
  90. m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction2.get(), false);
  91. }
  92. //---------------------------------------------------------------------------
  93. // addInstance
  94. //---------------------------------------------------------------------------
  95. void CDeploymentEngine::addInstance(const char* tagName, const char* name)
  96. {
  97. if (m_instanceType.length() == 0)
  98. throw MakeStringException(-1, "%s: Specification of individual instances is not allowed!", m_name.get());
  99. StringBuffer xpath;
  100. xpath.appendf("%s[@name='%s']", tagName, name);
  101. Owned<IPropertyTree> pInstance = m_process.getPropTree(xpath.str());
  102. if (!pInstance)
  103. throw MakeStringException(-1, "%s: Instance '%s' cannot be found!", m_name.get(), name);
  104. m_instances.append(*pInstance.getLink());
  105. }
  106. //---------------------------------------------------------------------------
  107. // getInstallFileCount
  108. //---------------------------------------------------------------------------
  109. // whether a method is trackable for progress stats purpose
  110. static bool isMethodTrackable(const char* method)
  111. {
  112. return strieq(method,"copy") || startsWith(method,"xsl"); //|| strieq(method,"esp_service_module");
  113. }
  114. int CDeploymentEngine::getInstallFileCount()
  115. {
  116. const CInstallFileList& files = getInstallFiles().getInstallFileList();
  117. // debug untrackable files
  118. if (0)
  119. {
  120. StringBuffer s;
  121. for (CInstallFileList::const_iterator it=files.begin(); it!=files.end(); ++it)
  122. {
  123. const StlLinked<CInstallFile>& f = *it;
  124. if (!isMethodTrackable(f->getMethod().c_str()) || startsWith(f->getMethod().c_str(),"xsl"))
  125. s.append(f->getMethod().c_str()).append(": ").append(f->getSrcPath().c_str())
  126. .append(" --> ").append(f->getDestPath().c_str()).newline();
  127. }
  128. Owned<IFile> f = createIFile("c:\\temp\\files.txt");
  129. Owned<IFileIO> fio = f->open(IFOwrite);
  130. fio->write(0,s.length(),s.str());
  131. }
  132. // This includes all files, such as, esp_service_module, esp_plugins and custom
  133. //return getInstallFiles().getInstallFileList().size();
  134. // Only count these we can handle properly
  135. int count = 0, xslcount = 0, total = 0;
  136. for (CInstallFileList::const_iterator it=files.begin(); it!=files.end(); ++it)
  137. {
  138. const StlLinked<CInstallFile>& f = *it;
  139. const char* method = f->getMethod().c_str();
  140. if (strieq(method,"copy"))
  141. count++;
  142. else if (startsWith(method,"xsl"))
  143. xslcount++;
  144. }
  145. bool isCached = m_instances.length() > 1;
  146. total = isCached ? count : 0;
  147. const char* depToFolder = m_envDepEngine.getDeployToFolder();
  148. ForEachItemIn(idx, m_instances)
  149. {
  150. IPropertyTree& instance = m_instances.item(idx);
  151. StringAttr curSSHUser, curSSHKeyFile, curSSHKeyPassphrase;
  152. m_envDepEngine.getSSHAccountInfo(instance.queryProp("@computer"),
  153. curSSHUser, curSSHKeyFile, curSSHKeyPassphrase);
  154. total += xslcount;
  155. if (m_useSSHIfDefined && !curSSHKeyFile.isEmpty() &&
  156. !curSSHUser.isEmpty() && !(depToFolder && *depToFolder))
  157. {
  158. total += 1;
  159. if (!isCached)
  160. {
  161. isCached = true;
  162. total += count;
  163. }
  164. }
  165. else
  166. total += count;
  167. }
  168. return total;
  169. }
  170. //---------------------------------------------------------------------------
  171. // getInstallFileSize
  172. //---------------------------------------------------------------------------
  173. offset_t CDeploymentEngine::getInstallFileSize()
  174. {
  175. const CInstallFileList& files = getInstallFiles().getInstallFileList();
  176. offset_t fileSize = 0, xslSize = 0, total = 0;
  177. for (CInstallFileList::const_iterator it=files.begin(); it!=files.end(); ++it)
  178. {
  179. const StlLinked<CInstallFile>& f = *it;
  180. const char* method = f->getMethod().c_str();
  181. if (strieq(method,"copy"))
  182. fileSize += f->getSrcSize();
  183. else if (startsWith(method,"xsl"))
  184. xslSize += f->getSrcSize();
  185. /* debug
  186. if (f->getSrcSize()==24331)
  187. {
  188. VStringBuffer s("%s : %s -> %s", f->getMethod().c_str(), f->getSrcPath().c_str(), f->getDestPath().c_str());
  189. ::MessageBox((HWND)m_pCallback->getWindowHandle(), s.str(), "UNCOPIED",MB_OK);
  190. }*/
  191. }
  192. bool isCached = m_instances.length() > 1;
  193. total = isCached ? fileSize : 0;
  194. const char* depToFolder = m_envDepEngine.getDeployToFolder();
  195. ForEachItemIn(idx, m_instances)
  196. {
  197. total += fileSize + xslSize;
  198. if (!isCached)
  199. {
  200. IPropertyTree& instance = m_instances.item(idx);
  201. StringAttr curSSHUser, curSSHKeyFile, curSSHKeyPassphrase;
  202. m_envDepEngine.getSSHAccountInfo(instance.queryProp("@computer"), curSSHUser,
  203. curSSHKeyFile, curSSHKeyPassphrase);
  204. if (!curSSHKeyFile.isEmpty() && !m_curSSHUser.isEmpty() &&
  205. !(depToFolder && *depToFolder))
  206. {
  207. isCached = true;
  208. total += fileSize;
  209. }
  210. }
  211. }
  212. return total;
  213. }
  214. //---------------------------------------------------------------------------
  215. // start
  216. //---------------------------------------------------------------------------
  217. void CDeploymentEngine::start()
  218. {
  219. //some components may not have defined startup.bat or stop.bat scripts
  220. //in their installset since those actions may not be relevant (for e.g.
  221. //dfu) so ignore startup/stop commands in that case
  222. checkBuild();
  223. if (m_startable == unknown)
  224. m_startable = searchDeployMap("startup", ".bat") ? yes : no;
  225. if (m_startable == yes)
  226. {
  227. ForEachItemIn(idx, m_instances)
  228. {
  229. checkAbort();
  230. IPropertyTree& instance = m_instances.item(idx);
  231. m_curInstance = instance.queryProp("@name");
  232. setSSHVars(instance);
  233. try
  234. {
  235. char tempPath[_MAX_PATH];
  236. getTempPath(tempPath, sizeof(tempPath), m_name);
  237. ensurePath(tempPath);
  238. m_envDepEngine.addTempDirectory( tempPath );
  239. startInstance(instance);
  240. }
  241. catch (IException* e)
  242. {
  243. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, e))//retry ?
  244. idx--;
  245. }
  246. catch (...)
  247. {
  248. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, NULL))//retry ?
  249. idx--;
  250. }
  251. }//for
  252. m_curInstance = NULL;
  253. clearSSHVars();
  254. }
  255. }
  256. //---------------------------------------------------------------------------
  257. // startInstance
  258. //---------------------------------------------------------------------------
  259. void CDeploymentEngine::startInstance(IPropertyTree& node, const char* fileName/*="startup"*/)
  260. {
  261. EnvMachineOS os = m_envDepEngine.lookupMachineOS(node);
  262. StringAttr hostDir(getHostDir(node).str());
  263. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL,
  264. "Starting %s process on %s", m_name.get(), hostDir.get());
  265. Owned<IDeployTask> task;
  266. if (m_useSSHIfDefined)
  267. {
  268. const char* computer = node.queryProp("@computer");
  269. if (!computer || !*computer)
  270. return;
  271. const char* dir = hostDir.sget();
  272. StringBuffer destpath, destip;
  273. stripNetAddr(dir, destpath, destip);
  274. StringBuffer cmd, output, err, destdir;
  275. destdir.append(destpath.length() - 1, destpath.str());
  276. cmd.clear().appendf("%s%s %s", destpath.str(), fileName, destdir.str());
  277. task.set(createDeployTask(*m_pCallback, "Start Instance", m_process.queryName(), m_name.get(), m_curInstance, fileName, dir, m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os));
  278. m_pCallback->printStatus(task);
  279. bool flag = task->execSSHCmd(destip.str(), cmd, output, err);
  280. }
  281. else
  282. {
  283. StringBuffer startCmd;
  284. startCmd.append(hostDir).append(fileName);
  285. if (os == MachineOsW2K)
  286. startCmd.append(".bat");
  287. StringAttr user, pwd;
  288. m_envDepEngine.getAccountInfo(node.queryProp("@computer"), user, pwd);
  289. // Spawn start process
  290. connectToHost(node);
  291. task.set(createDeployTask(*m_pCallback, "Start Instance", m_process.queryName(),
  292. m_name.get(), m_curInstance, NULL, startCmd.str(),
  293. m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os));
  294. m_pCallback->printStatus(task);
  295. task->createProcess(true, user, pwd);
  296. }
  297. m_pCallback->printStatus(task);
  298. checkAbort(task);
  299. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  300. }
  301. //---------------------------------------------------------------------------
  302. // stop
  303. //---------------------------------------------------------------------------
  304. void CDeploymentEngine::stop()
  305. {
  306. //some components may not have defined startup.bat or stop.bat scripts
  307. //in their installset since those actions may not be relevant (for e.g.
  308. //dfu) so ignore startup/stop commands in that case
  309. checkBuild();
  310. if (m_stoppable == unknown)
  311. m_stoppable = searchDeployMap("stop", ".bat") ? yes : no;
  312. if (m_stoppable == yes)
  313. {
  314. ForEachItemIn(idx, m_instances)
  315. {
  316. checkAbort();
  317. IPropertyTree& instance = m_instances.item(idx);
  318. m_curInstance = instance.queryProp("@name");
  319. setSSHVars(instance);
  320. try
  321. {
  322. char tempPath[_MAX_PATH];
  323. getTempPath(tempPath, sizeof(tempPath), m_name);
  324. ensurePath(tempPath);
  325. m_envDepEngine.addTempDirectory( tempPath );
  326. stopInstance(instance);
  327. }
  328. catch (IException* e)
  329. {
  330. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, e))//retry ?
  331. idx--;
  332. }
  333. catch (...)
  334. {
  335. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, NULL))//retry ?
  336. idx--;
  337. }
  338. }
  339. m_curInstance = NULL;
  340. clearSSHVars();
  341. }
  342. }
  343. //---------------------------------------------------------------------------
  344. // stopInstance
  345. //---------------------------------------------------------------------------
  346. void CDeploymentEngine::stopInstance(IPropertyTree& node, const char* fileName/*="stop"*/)
  347. {
  348. EnvMachineOS os = m_envDepEngine.lookupMachineOS(node);
  349. StringAttr hostDir(getHostDir(node).str());
  350. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL,
  351. "Stopping %s process on %s", m_name.get(), hostDir.get());
  352. Owned<IDeployTask> task;
  353. if (m_useSSHIfDefined)
  354. {
  355. const char* computer = node.queryProp("@computer");
  356. if (!computer || !*computer)
  357. return;
  358. const char* dir = hostDir.sget();
  359. StringBuffer destpath, destip;
  360. stripNetAddr(dir, destpath, destip);
  361. StringBuffer cmd, output, err, destdir;
  362. destdir.append(destpath.length() - 1, destpath.str());
  363. cmd.clear().appendf("%s%s %s", destpath.str(), fileName, destdir.str());
  364. task.set(createDeployTask(*m_pCallback, "Stop Instance", m_process.queryName(), m_name.get(), m_curInstance, fileName, dir, m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os));
  365. m_pCallback->printStatus(task);
  366. bool flag = task->execSSHCmd(destip.str(), cmd, output, err);
  367. }
  368. else
  369. {
  370. StringBuffer stopCmd;
  371. StringAttr user, pwd;
  372. stopCmd.append(hostDir).append(fileName);
  373. m_envDepEngine.getAccountInfo(node.queryProp("@computer"), user, pwd);
  374. EnvMachineOS os = m_envDepEngine.lookupMachineOS(node);
  375. if (os == MachineOsW2K)
  376. stopCmd.append(".bat");
  377. // Spawn stop process
  378. connectToHost(node);
  379. task.set(createDeployTask(*m_pCallback, "Stop Instance", m_process.queryName(), m_name.get(),
  380. m_curInstance, NULL, stopCmd.str(), m_curSSHUser.sget(),
  381. m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os));
  382. m_pCallback->printStatus(task);
  383. task->createProcess(true, user, pwd);
  384. }
  385. m_pCallback->printStatus(task);
  386. checkAbort(task);
  387. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  388. }
  389. //---------------------------------------------------------------------------
  390. // check
  391. //---------------------------------------------------------------------------
  392. void CDeploymentEngine::check()
  393. {
  394. checkBuild();
  395. if (m_instances.empty())
  396. throw MakeStringException(0, "Process %s has no instances defined. Nothing to do!", m_name.get());
  397. if (m_instanceCheck)
  398. {
  399. ForEachItemIn(idx, m_instances)
  400. {
  401. checkAbort();
  402. IPropertyTree& instance = m_instances.item(idx);
  403. m_curInstance = instance.queryProp("@name");
  404. checkInstance(instance);
  405. }
  406. }
  407. m_curInstance = NULL;
  408. clearSSHVars();
  409. }
  410. //---------------------------------------------------------------------------
  411. // queryDirectory
  412. //---------------------------------------------------------------------------
  413. const char *CDeploymentEngine::queryDirectory(IPropertyTree& node, StringBuffer& sDir) const
  414. {
  415. const char *pszDir = node.queryProp("@directory");
  416. if (!pszDir)
  417. pszDir = m_process.queryProp("@directory");
  418. sDir.clear();
  419. if (pszDir)
  420. sDir.append(pszDir).replace('/', '\\').replace(':', '$'); //make UNC path
  421. return pszDir ? sDir.str() : NULL;
  422. }
  423. //---------------------------------------------------------------------------
  424. // checkInstance
  425. //---------------------------------------------------------------------------
  426. void CDeploymentEngine::checkInstance(IPropertyTree& node) const
  427. {
  428. // Check for valid net address
  429. StringAttr sAttr;
  430. if (m_envDepEngine.lookupNetAddress(sAttr, node.queryProp("@computer")).length()==0)
  431. throw MakeStringException(0, "Process %s has invalid computer net address", m_name.get());
  432. // Check for valid directory
  433. StringBuffer directory;
  434. queryDirectory(node, directory);
  435. if (directory.length()==0)
  436. throw MakeStringException(0, "Process %s has invalid directory", m_name.get());
  437. }
  438. //---------------------------------------------------------------------------
  439. // checkBuild
  440. //---------------------------------------------------------------------------
  441. void CDeploymentEngine::checkBuild() const
  442. {
  443. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL,
  444. "Checking builds for process %s", m_name.get());
  445. // Make sure build and buildset are defined
  446. StringAttr build(m_process.queryProp("@build"));
  447. StringAttr buildset(m_process.queryProp("@buildSet"));
  448. if (build.length()==0 || buildset.length()==0)
  449. throw MakeStringException(0, "Process %s has no build or buildSet defined", m_name.get());
  450. // Make sure build is valid
  451. StringBuffer path;
  452. path.appendf("./Programs/Build[@name=\"%s\"]", build.get());
  453. IPropertyTree* buildNode = m_rootNode->queryPropTree(path.str());
  454. if (!buildNode)
  455. throw MakeStringException(0, "Process %s has invalid build", m_name.get());
  456. // Make sure buildset is valid
  457. path.clear().appendf("./BuildSet[@name=\"%s\"]", buildset.get());
  458. IPropertyTree* buildsetNode = buildNode->queryPropTree(path.str());
  459. if (!buildsetNode)
  460. throw MakeStringException(0, "Process %s has invalid buildSet", m_name.get());
  461. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  462. }
  463. //---------------------------------------------------------------------------
  464. // compare
  465. //---------------------------------------------------------------------------
  466. void CDeploymentEngine::compare(unsigned flags)
  467. {
  468. m_compare = true;
  469. m_deployFlags = flags;
  470. _deploy(false);
  471. }
  472. //---------------------------------------------------------------------------
  473. // deploy
  474. //---------------------------------------------------------------------------
  475. void CDeploymentEngine::deploy(unsigned flags, bool useTempDir)
  476. {
  477. m_compare = false;
  478. m_deployFlags = flags;
  479. _deploy(useTempDir);
  480. }
  481. //---------------------------------------------------------------------------
  482. // beforeDeploy
  483. //---------------------------------------------------------------------------
  484. void CDeploymentEngine::beforeDeploy()
  485. {
  486. m_installFiles.clear();
  487. determineInstallFiles(m_process, m_installFiles);
  488. getCallback().installFileListChanged();
  489. char tempPath[_MAX_PATH];
  490. getTempPath(tempPath, sizeof(tempPath), m_name);
  491. ensurePath(tempPath);
  492. bool cacheFiles = false;
  493. const char* depToFolder = m_envDepEngine.getDeployToFolder();
  494. if (!m_compare && m_instances.ordinality() == 1 && !(depToFolder && *depToFolder))
  495. {
  496. IPropertyTree& instanceNode = m_instances.item(0);
  497. const char* curInstance = instanceNode.queryProp("@name");
  498. StringAttr sbSSHUser, sbSSHKeyFile, sbKeyPassphrase;
  499. m_envDepEngine.getSSHAccountInfo(instanceNode.queryProp("@computer"),
  500. sbSSHUser, sbSSHKeyFile, sbKeyPassphrase);
  501. cacheFiles = !sbSSHKeyFile.isEmpty();
  502. }
  503. if (m_instances.ordinality() > 1 || cacheFiles)
  504. {
  505. strcat(tempPath, "Cache");
  506. char* pszEnd = tempPath + strlen(tempPath);
  507. Owned<IFile> pFile = createIFile(tempPath);
  508. int i = 1;
  509. while (pFile->exists()) { //dir/file exists
  510. itoa(++i, pszEnd, 10);
  511. pFile.setown( createIFile(tempPath) );
  512. }
  513. m_envDepEngine.addTempDirectory( tempPath );
  514. strcat(tempPath, PATHSEPSTR);
  515. m_cachePath.set( tempPath );
  516. EnvMachineOS os = m_envDepEngine.lookupMachineOS( m_instances.item(0) );
  517. m_curInstance = "Cache";
  518. clearSSHVars();
  519. copyInstallFiles("Cache", -1, tempPath, os);
  520. }
  521. else
  522. m_cachePath.set( tempPath );
  523. }
  524. //---------------------------------------------------------------------------
  525. // _deploy
  526. //---------------------------------------------------------------------------
  527. void CDeploymentEngine::_deploy(bool useTempDir)
  528. {
  529. m_renameDirList.kill();
  530. beforeDeploy();
  531. m_curSSHUser.clear();
  532. m_curSSHKeyFile.clear();
  533. m_curSSHKeyPassphrase.clear();
  534. ForEachItemIn(idx, m_instances)
  535. {
  536. checkAbort();
  537. IPropertyTree& instanceNode = m_instances.item(idx);
  538. m_curInstance = instanceNode.queryProp("@name");
  539. setSSHVars(instanceNode);
  540. try
  541. {
  542. deployInstance(instanceNode, useTempDir);
  543. }
  544. catch (IException* e)
  545. {
  546. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, e))//retry ?
  547. idx--;
  548. }
  549. catch (...)
  550. {
  551. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, NULL))//retry ?
  552. idx--;
  553. }
  554. }
  555. m_curInstance = NULL;
  556. clearSSHVars();
  557. afterDeploy();
  558. }
  559. //---------------------------------------------------------------------------
  560. // deployInstance
  561. //---------------------------------------------------------------------------
  562. void CDeploymentEngine::deployInstance(IPropertyTree& instanceNode, bool useTempDir)
  563. {
  564. StringAttr hostDir(getHostDir(instanceNode).str());
  565. StringAttr destDir(useTempDir ? getDeployDir(instanceNode).str() : hostDir.get());
  566. ensurePath(destDir);
  567. const char* pszHostDir = hostDir.get();
  568. if (pszHostDir && *pszHostDir==PATHSEPCHAR && *(pszHostDir+1)==PATHSEPCHAR && m_envDepEngine.lookupMachineOS(instanceNode) != MachineOsLinux)
  569. connectToHost(instanceNode);
  570. beforeDeployInstance(instanceNode, destDir);
  571. copyInstallFiles(instanceNode, destDir);
  572. afterDeployInstance(instanceNode, destDir);
  573. if (!m_compare && useTempDir)
  574. {
  575. checkAbort();
  576. EnvMachineOS os= m_envDepEngine.lookupMachineOS(instanceNode);
  577. renameDir(hostDir, NULL, os);
  578. renameDir(destDir, hostDir, os);
  579. }
  580. }
  581. //---------------------------------------------------------------------------
  582. // renameDirs
  583. //---------------------------------------------------------------------------
  584. void CDeploymentEngine::renameDirs()
  585. {
  586. StringBuffer xpath, err;
  587. bool flag = false;
  588. while (m_renameDirList.length())
  589. {
  590. IDeployTask* task = &m_renameDirList.item(0);
  591. m_pCallback->printStatus(task);
  592. if (task->getMachineOS() == MachineOsLinux && strlen(task->getSSHKeyFile()) && strlen(task->getSSHUser()))
  593. {
  594. StringBuffer fromPath, toPath, err, destip;
  595. const char* source = task->getFileSpec(DT_SOURCE);
  596. stripNetAddr(source, fromPath, destip);
  597. stripNetAddr(task->getFileSpec(DT_TARGET), toPath, destip);
  598. StringBuffer cmd, output;
  599. cmd.clear().appendf("mv %s %s", fromPath.str(), toPath.str());
  600. flag = task->execSSHCmd(destip.str(), cmd, output, err);
  601. }
  602. else
  603. task->renameFile();
  604. m_pCallback->printStatus(task);
  605. checkAbort(task);
  606. m_renameDirList.remove(0);
  607. }
  608. }
  609. //---------------------------------------------------------------------------
  610. // deleteFile
  611. //---------------------------------------------------------------------------
  612. void CDeploymentEngine::deleteFile(const char* target, const char* instanceName, EnvMachineOS os)
  613. {
  614. checkAbort();
  615. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Delete File", m_process.queryName(),
  616. m_name.get(), instanceName, NULL, target, m_curSSHUser.sget(), m_curSSHKeyFile.sget(),
  617. m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os);
  618. task->deleteFile();
  619. //only display status info for successful attempts since some temp files may be attempted to be
  620. //deleted more than one time, for instance, due to multiple esp bindings
  621. if (task->getErrorCode() == 0)
  622. m_pCallback->printStatus(task);
  623. }
  624. //---------------------------------------------------------------------------
  625. // backupDirs
  626. //---------------------------------------------------------------------------
  627. void CDeploymentEngine::backupDirs()
  628. {
  629. ForEachItemIn(idx, m_instances)
  630. {
  631. checkAbort();
  632. IPropertyTree& instance = m_instances.item(idx);
  633. m_curInstance = instance.queryProp("@name");
  634. setSSHVars(instance);
  635. try
  636. {
  637. EnvMachineOS os = m_envDepEngine.lookupMachineOS(instance);
  638. if (os == MachineOsLinux && !m_curSSHUser.isEmpty() && !m_curSSHKeyFile.isEmpty())
  639. {
  640. StringAttr hostDir(getHostDir(instance).str());
  641. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL,
  642. "Backing up directory %s", hostDir.get());
  643. StringBuffer bkPath;
  644. getBackupDirName(hostDir.sget(), bkPath);
  645. if (bkPath.length() == 0)
  646. return;
  647. StringBuffer fromPath, toPath, err, fromip, toip;
  648. stripNetAddr(hostDir.sget(), fromPath, fromip);
  649. stripNetAddr(bkPath.str(), toPath, toip);
  650. StringBuffer cmd, output;
  651. StringBuffer tmp;
  652. tmp.appendf("%d", msTick());
  653. cmd.clear().appendf("cp -r %s %s", fromPath.str(), toPath.str());
  654. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Backup Directory", m_process.queryName(), m_name.get(),
  655. m_curInstance, fromPath.str(), toPath.str(), m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(),
  656. m_useSSHIfDefined, os);
  657. m_pCallback->printStatus(task);
  658. bool flag = task->execSSHCmd(fromip.str(), cmd, output, err);
  659. m_pCallback->printStatus(task);
  660. checkAbort(task);
  661. }
  662. else
  663. {
  664. connectToHost(instance);
  665. StringAttr hostDir(getHostDir(instance).str());
  666. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL,
  667. "Backing up directory %s", hostDir.get());
  668. backupDir(hostDir);
  669. }
  670. }
  671. catch (IException* e)
  672. {
  673. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, e))//retry ?
  674. idx--;
  675. }
  676. catch (...)
  677. {
  678. if (!m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, NULL))//retry ?
  679. idx--;
  680. }
  681. }
  682. m_curInstance = NULL;
  683. clearSSHVars();
  684. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  685. }
  686. //---------------------------------------------------------------------------
  687. // abort
  688. //---------------------------------------------------------------------------
  689. void CDeploymentEngine::abort()
  690. {
  691. m_pCallback->printStatus(STATUS_NORMAL, m_process.queryName(), m_name.get(), m_curInstance, "Aborted!");
  692. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Aborted!");
  693. m_abort = true;
  694. }
  695. //---------------------------------------------------------------------------
  696. // checkAbort
  697. //---------------------------------------------------------------------------
  698. void CDeploymentEngine::checkAbort(IDeployTask* task) const
  699. {
  700. if (m_abort || m_pCallback->getAbortStatus() || (task && task->getAbort()))
  701. throw MakeStringException(0, "User abort");
  702. }
  703. //---------------------------------------------------------------------------
  704. // xslTransform
  705. //---------------------------------------------------------------------------
  706. void CDeploymentEngine::xslTransform(const char *xslFilePath, const char *outputFilePath,
  707. const char* instanceName,
  708. EnvMachineOS os/*=MachineOsUnknown*/,
  709. const char* processName/*=NULL*/,
  710. bool isEspModuleOrPlugin)
  711. {
  712. m_createIni = false;
  713. // Skip if not processing config files
  714. if (!(m_deployFlags & DEFLAGS_CONFIGFILES)) return;
  715. checkAbort();
  716. bool useSSH = true;
  717. if (m_compare)
  718. {
  719. useSSH = false;
  720. outputFilePath = setCompare(outputFilePath);
  721. }
  722. else
  723. ensurePath(outputFilePath);
  724. m_transform->setParameter("processType", StringBuffer("'").append(m_process.queryName()).append("'").str());
  725. s_xsltDepEngine = this; //this is used in external function to get back to deployment engine instance
  726. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "XSL Transform", m_process.queryName(),
  727. m_name.get(), instanceName, xslFilePath, outputFilePath, m_curSSHUser.sget(), m_curSSHKeyFile.sget(),
  728. m_curSSHKeyPassphrase.sget(), useSSH ? m_useSSHIfDefined : useSSH, os, processName);
  729. m_pCallback->printStatus(task);
  730. if (os == MachineOsLinux)
  731. {
  732. char tempPath[_MAX_PATH];
  733. getTempPath(tempPath, sizeof(tempPath), m_name.get());
  734. ensurePath(tempPath);
  735. m_envDepEngine.addTempDirectory( tempPath );
  736. }
  737. task->transformFile(*m_processor, *m_transform, m_cachePath.get());
  738. m_pCallback->printStatus(task);
  739. checkAbort(task);
  740. if (!isEspModuleOrPlugin)
  741. {
  742. try {
  743. Owned<IFile> file = createIFile(xslFilePath);
  744. m_pCallback->fileSizeCopied(file->size(),true);
  745. } catch (...) {
  746. m_pCallback->fileSizeCopied(0,true);
  747. }
  748. }
  749. if (m_compare)
  750. compareFiles(os);
  751. }
  752. //---------------------------------------------------------------------------
  753. // setCompare
  754. //---------------------------------------------------------------------------
  755. const char* CDeploymentEngine::setCompare(const char *filename)
  756. {
  757. assertex(m_compareOld.length()==0 && m_compareNew.length()==0);
  758. m_compareOld.set(filename);
  759. char tempfile[_MAX_PATH];
  760. getTempPath(tempfile, sizeof(tempfile), m_name);
  761. ensurePath(tempfile);
  762. strcat(tempfile, "compare");
  763. // Make sure file name is unique - at least during this session
  764. sprintf(&tempfile[strlen(tempfile)], "%d", m_envDepEngine.incrementTempFileCount());
  765. // Add same extension as filename for use with shell functions
  766. const char* ext = findFileExtension(filename);
  767. if (ext)
  768. strcat(tempfile, ext);
  769. m_envDepEngine.addTempFile(tempfile);
  770. m_compareNew.set(tempfile);
  771. return m_compareNew;
  772. }
  773. //---------------------------------------------------------------------------
  774. // compareFiles
  775. //---------------------------------------------------------------------------
  776. void CDeploymentEngine::compareFiles(EnvMachineOS os)
  777. {
  778. compareFiles(m_compareNew, m_compareOld, os);
  779. //remove(m_compareNew); // let's keep the files around for later compares
  780. m_compareOld.clear();
  781. m_compareNew.clear();
  782. }
  783. //---------------------------------------------------------------------------
  784. // compareFiles
  785. //---------------------------------------------------------------------------
  786. void CDeploymentEngine::compareFiles(const char *newFile, const char *oldFile, EnvMachineOS os)
  787. {
  788. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Compare File",
  789. m_process.queryName(), m_name.get(),
  790. m_curInstance, newFile, oldFile, m_curSSHUser.sget(),
  791. m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os);
  792. m_pCallback->printStatus(task);
  793. task->compareFile(DTC_CRC | DTC_SIZE);
  794. task->setFileSpec(DT_SOURCE, newFile);
  795. m_pCallback->printStatus(task);
  796. checkAbort(task);
  797. }
  798. //---------------------------------------------------------------------------
  799. // writeFile
  800. //---------------------------------------------------------------------------
  801. void CDeploymentEngine::writeFile(const char* filename, const char* str, EnvMachineOS os)
  802. {
  803. // Skip if not processing config files
  804. if (!(m_deployFlags & DEFLAGS_CONFIGFILES)) return;
  805. checkAbort();
  806. bool useSSH = true;
  807. if (m_compare)
  808. {
  809. useSSH = false;
  810. filename = setCompare(filename);
  811. }
  812. else
  813. ensurePath(filename);
  814. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Create File", m_process.queryName(), m_name.get(),
  815. m_curInstance, NULL, filename, m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(),
  816. useSSH?m_useSSHIfDefined:useSSH, os);
  817. m_pCallback->printStatus(task);
  818. if (useSSH?m_useSSHIfDefined:useSSH)
  819. {
  820. StringBuffer cmd, output, err, destpath, ip;
  821. stripNetAddr(filename, destpath, ip);
  822. cmd.clear().appendf("echo '%s' > %s; chmod 644 %s", str, destpath.str(), destpath.str());
  823. bool flag = task->execSSHCmd(ip.str(), cmd, output, err);
  824. if (!flag)
  825. {
  826. String errmsg(err.str());
  827. int index = errmsg.indexOf('\n');
  828. String* perr = errmsg.substring(0, index > 0? index : errmsg.length());
  829. output.clear().appendf("%s", perr->toCharArray());
  830. delete perr;
  831. throw MakeStringException(-1, "%s", output.str());
  832. }
  833. }
  834. else
  835. task->createFile(str);
  836. m_pCallback->printStatus(task);
  837. checkAbort(task);
  838. if (m_compare) compareFiles(os);
  839. }
  840. //---------------------------------------------------------------------------
  841. // lookupProcess
  842. //---------------------------------------------------------------------------
  843. IPropertyTree *CDeploymentEngine::lookupProcess(const char* type, const char* name) const
  844. {
  845. if (name && *name && type && *type)
  846. {
  847. StringBuffer path;
  848. path.appendf("Software/%s[@name=\"%s\"]", type, name);
  849. Owned<IPropertyTreeIterator> iter = m_rootNode->getElements(path.str());
  850. if (iter->first() && iter->isValid())
  851. {
  852. return &iter->query();
  853. }
  854. }
  855. return NULL;
  856. }
  857. //---------------------------------------------------------------------------
  858. // lookupTable
  859. //---------------------------------------------------------------------------
  860. IPropertyTree* CDeploymentEngine::lookupTable(IPropertyTree* modelTree, const char *table) const
  861. {
  862. StringBuffer xpath;
  863. xpath.appendf("./*[@name='%s']", table);
  864. IPropertyTree *ret = modelTree->queryPropTree(xpath.str());
  865. if (ret)
  866. return ret;
  867. else
  868. throw MakeStringException(0, "Table %s could not be found", table);
  869. }
  870. //---------------------------------------------------------------------------
  871. // getEndPoints
  872. //---------------------------------------------------------------------------
  873. StringBuffer& CDeploymentEngine::getEndPoints(const char* path, const char* delimiter, StringBuffer& endPoints) const
  874. {
  875. Owned<IPropertyTreeIterator> iter = m_rootNode->getElements(path);
  876. for (iter->first(); iter->isValid(); iter->next())
  877. {
  878. Owned<IConstMachineInfo> machine = m_environment.getMachine(iter->query().queryProp("@computer"));
  879. if (machine)
  880. {
  881. if (endPoints.length())
  882. endPoints.append(delimiter);
  883. SCMStringBuffer scmSBuf;
  884. endPoints.append(machine->getNetAddress(scmSBuf).str());
  885. const char* port = iter->query().queryProp("@port");
  886. if (port)
  887. endPoints.append(":").append(port);
  888. }
  889. }
  890. return endPoints;
  891. }
  892. //---------------------------------------------------------------------------
  893. // getDaliServers
  894. //---------------------------------------------------------------------------
  895. StringBuffer& CDeploymentEngine::getDaliServers(StringBuffer& daliServers) const
  896. {
  897. daliServers.clear();
  898. const char* name = m_process.queryProp("@daliServer");
  899. if (!name)
  900. name = m_process.queryProp("@daliServers");
  901. if (name)
  902. {
  903. StringBuffer path;
  904. path.appendf("./Software/DaliServerProcess[@name=\"%s\"]/Instance", name);
  905. getEndPoints(path.str(), ", ", daliServers);
  906. }
  907. return daliServers;
  908. }
  909. //---------------------------------------------------------------------------
  910. // getHostRoot
  911. //---------------------------------------------------------------------------
  912. StringBuffer CDeploymentEngine::getHostRoot(const char* computer, const char* dir, bool bIgnoreDepToFolder/*=false*/) const
  913. {
  914. StringBuffer hostRoot;
  915. StringAttr netAddress;
  916. if (m_envDepEngine.lookupNetAddress(netAddress, computer).length() > 0)
  917. {
  918. const char* depToFolder = m_envDepEngine.getDeployToFolder();
  919. if (depToFolder && !bIgnoreDepToFolder)
  920. hostRoot.append(depToFolder);
  921. else
  922. hostRoot.append(PATHSEPCHAR).append(PATHSEPCHAR);
  923. hostRoot.append(netAddress).append(PATHSEPCHAR);
  924. //for Linux support, allow directories starting with '\' character
  925. if (dir)
  926. {
  927. if (isPathSepChar(*dir))
  928. dir++;
  929. while (*dir && !isPathSepChar(*dir))
  930. hostRoot.append(*dir++);
  931. }
  932. }
  933. return hostRoot;
  934. }
  935. //---------------------------------------------------------------------------
  936. // getHostDir
  937. //---------------------------------------------------------------------------
  938. StringBuffer CDeploymentEngine::getHostDir(IPropertyTree& node, bool bIgnoreDepToFolder/*=false*/)
  939. {
  940. StringBuffer hostDir;
  941. StringAttr netAddress;
  942. if (m_envDepEngine.lookupNetAddress(netAddress, node.queryProp("@computer")).length() > 0)
  943. {
  944. const char* depToFolder = m_envDepEngine.getDeployToFolder();
  945. if (depToFolder && !bIgnoreDepToFolder)
  946. {
  947. hostDir.append(depToFolder);
  948. m_useSSHIfDefined = false;
  949. }
  950. else
  951. hostDir.append(PATHSEPCHAR).append(PATHSEPCHAR);
  952. hostDir.append(netAddress).append(PATHSEPCHAR);
  953. StringBuffer directory;
  954. const char* dir = queryDirectory(node, directory);
  955. //for Linux support, allow directories starting with '\' character
  956. if (dir)
  957. {
  958. if (isPathSepChar(*dir))
  959. dir++;
  960. hostDir.append(dir).append(PATHSEPCHAR);
  961. }
  962. }
  963. return hostDir;
  964. }
  965. //---------------------------------------------------------------------------
  966. // getDeployDir
  967. //---------------------------------------------------------------------------
  968. StringBuffer CDeploymentEngine::getDeployDir(IPropertyTree& node)
  969. {
  970. char deployDir[_MAX_PATH];
  971. strcpy(deployDir, getHostDir(node).str());
  972. removeTrailingPathSepChar(deployDir);
  973. strcat(deployDir, "_deploy" PATHSEPSTR);
  974. return deployDir;
  975. }
  976. //---------------------------------------------------------------------------
  977. // getLocalDir - returns @directory with '$' replaced by ':'
  978. //---------------------------------------------------------------------------
  979. StringBuffer CDeploymentEngine::getLocalDir(IPropertyTree& node) const
  980. {
  981. StringBuffer localDir;
  982. queryDirectory(node, localDir);
  983. if (m_envDepEngine.lookupMachineOS(node) == MachineOsLinux)
  984. {
  985. localDir.replace(':', '$');
  986. localDir.replace('\\', '/');
  987. }
  988. else
  989. {
  990. localDir.replace('$', ':');
  991. localDir.replace('/', '\\');
  992. }
  993. return localDir;
  994. }
  995. //---------------------------------------------------------------------------
  996. // connectToHost
  997. //---------------------------------------------------------------------------
  998. void CDeploymentEngine::connectToHost(IPropertyTree& node, const char* dir/*=NULL*/)
  999. {
  1000. const char* computer = node.queryProp("@computer");
  1001. StringBuffer directory;
  1002. if (!dir)
  1003. dir = queryDirectory(node, directory);
  1004. StringAttr user;
  1005. StringAttr pswd;
  1006. m_envDepEngine.getAccountInfo(computer, user, pswd);
  1007. connectToNetworkPath( getHostRoot(computer, dir).str(), user, pswd );
  1008. }
  1009. //---------------------------------------------------------------------------
  1010. // static splitUNCPath
  1011. //---------------------------------------------------------------------------
  1012. bool CDeploymentEngine::stripTrailingDirsFromUNCPath(const char* uncPath, StringBuffer& netPath)
  1013. {
  1014. const char* p = uncPath;
  1015. if (p && *p && isPathSepChar(*p++) && *p && isPathSepChar(*p++))//is it really a network path starting with \\ or //
  1016. {
  1017. netPath.append(PATHSEPSTR PATHSEPSTR);
  1018. //remove trailing directories like dir2, dir3 etc. from paths like \\ip\dir1\dir2\dir3
  1019. while (*p && !isPathSepChar(*p))
  1020. netPath.append(*p++);
  1021. if (*p && isPathSepChar(*p++))
  1022. {
  1023. netPath.append( PATHSEPCHAR );
  1024. if (*p && !isPathSepChar(*p))
  1025. {
  1026. while (*p && !isPathSepChar(*p))
  1027. netPath.append(*p++);
  1028. netPath.append( PATHSEPCHAR );
  1029. }
  1030. return true;
  1031. }
  1032. }
  1033. return false;
  1034. }
  1035. //---------------------------------------------------------------------------
  1036. // connectToHost
  1037. //---------------------------------------------------------------------------
  1038. void CDeploymentEngine::connectToNetworkPath(const char* uncPath, const char* user, const char* pswd)
  1039. {
  1040. StringBuffer path;
  1041. // make a valid UNC path to connect to and see if we are not already connected
  1042. if (!stripTrailingDirsFromUNCPath(uncPath, path) || m_connections.find( path.str() ) != m_connections.end())
  1043. return;
  1044. if (m_envDepEngine.getDeployToFolder())
  1045. m_envDepEngine.getDeployToAccountInfo(user, pswd);
  1046. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Connect", m_process.queryName(), m_name.get(),
  1047. m_curInstance, NULL, path.str(), "", "", "", false);
  1048. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Connecting to %s...", path.str());
  1049. m_pCallback->printStatus(task);
  1050. task->connectTarget(user, pswd, m_envDepEngine.getInteractiveMode());
  1051. m_pCallback->printStatus(task);
  1052. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  1053. // No sense in continuing if connection falied
  1054. if (task->getErrorCode() != 0)
  1055. throw MakeStringException(0, "%s", ""); //message already displayed!
  1056. // Save connections for disconnecting during destructor
  1057. m_connections.insert( path.str() );
  1058. }
  1059. //---------------------------------------------------------------------------
  1060. // disconnectHost
  1061. //---------------------------------------------------------------------------
  1062. void CDeploymentEngine::disconnectHost(const char* uncPath)
  1063. {
  1064. // Create log of directory
  1065. IDeployLog* pDeployLog = m_envDepEngine.getDeployLog();
  1066. if (pDeployLog)
  1067. pDeployLog->addDirList(m_name, uncPath);
  1068. // Disconnect
  1069. bool disc = m_pCallback->onDisconnect(uncPath);
  1070. if (disc)
  1071. {
  1072. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Disconnect", m_process.queryName(), m_name.get(),
  1073. m_curInstance, NULL, uncPath, "", "", "", false);
  1074. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Disconnecting from %s...", uncPath);
  1075. m_pCallback->printStatus(task);
  1076. task->disconnectTarget();
  1077. m_pCallback->printStatus(task);
  1078. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  1079. }
  1080. }
  1081. struct string_compare : public std::binary_function<const char*, const char*, bool>
  1082. {
  1083. bool operator()(const char* x, const char* y) const { return stricmp(x, y)==0; }
  1084. };
  1085. //---------------------------------------------------------------------------
  1086. // copyAttributes
  1087. //---------------------------------------------------------------------------
  1088. void CDeploymentEngine::copyAttributes(IPropertyTree *dst, IPropertyTree *src, const char** begin, const char** end)
  1089. {
  1090. Owned<IAttributeIterator> attrs = src->getAttributes();
  1091. for(attrs->first(); attrs->isValid(); attrs->next())
  1092. {
  1093. if(std::find_if(begin, end, std::bind1st(string_compare(), attrs->queryName()+1)) != end)
  1094. dst->addProp(attrs->queryName(), attrs->queryValue());
  1095. }
  1096. }
  1097. //---------------------------------------------------------------------------
  1098. // copyUnknownAttributes
  1099. //---------------------------------------------------------------------------
  1100. void CDeploymentEngine::copyUnknownAttributes(IPropertyTree *dst, IPropertyTree *src, const char** begin, const char** end)
  1101. {
  1102. Owned<IAttributeIterator> attrs = src->getAttributes();
  1103. for(attrs->first(); attrs->isValid(); attrs->next())
  1104. {
  1105. if(std::find_if(begin, end, std::bind1st(string_compare(), attrs->queryName()+1)) == end)
  1106. dst->addProp(attrs->queryName(), attrs->queryValue());
  1107. }
  1108. }
  1109. //---------------------------------------------------------------------------
  1110. // ensurePath
  1111. //---------------------------------------------------------------------------
  1112. void CDeploymentEngine::ensurePath(const char* filespec) const
  1113. {
  1114. // Check if directory already exists
  1115. StringBuffer dir;
  1116. splitDirTail(filespec, dir);
  1117. bool flag = true;
  1118. EnvMachineOS os = MachineOsW2K;
  1119. if (m_curInstance && m_curSSHUser.length() && m_curSSHKeyFile.length())
  1120. {
  1121. StringBuffer xpath, err;
  1122. xpath.appendf("./Instance[@name='%s']", m_curInstance);
  1123. IPropertyTree* pInstanceNode = m_process.queryPropTree(xpath.str());
  1124. if (!pInstanceNode)
  1125. {
  1126. StringBuffer destpath, ip;
  1127. stripNetAddr(dir, destpath, ip);
  1128. if (!ip.length())
  1129. flag = true;
  1130. else
  1131. {
  1132. IConstMachineInfo* pInfo = m_envDepEngine.getEnvironment().getMachineByAddress(ip.str());
  1133. os = pInfo->getOS();
  1134. flag = !checkSSHFileExists(dir);
  1135. }
  1136. }
  1137. else
  1138. {
  1139. os = m_envDepEngine.lookupMachineOS(*pInstanceNode);
  1140. flag = !checkSSHFileExists(dir);
  1141. }
  1142. }
  1143. if (flag)
  1144. {
  1145. Owned<IFile> pIFile = createIFile(dir.str());
  1146. if ((m_curInstance && m_curSSHUser.length() && m_curSSHKeyFile.length()) || !pIFile->exists() || !pIFile->isDirectory())
  1147. {
  1148. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Create Directory", m_process.queryName(), m_name.get(),
  1149. m_curInstance, NULL, dir.str(), m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(),
  1150. m_useSSHIfDefined, os);
  1151. m_pCallback->printStatus(task);
  1152. task->createDirectory();
  1153. m_pCallback->printStatus(task);
  1154. checkAbort(task);
  1155. }
  1156. }
  1157. }
  1158. //---------------------------------------------------------------------------
  1159. // renameDir
  1160. //---------------------------------------------------------------------------
  1161. void CDeploymentEngine::renameDir(const char* from, const char* to, EnvMachineOS os)
  1162. {
  1163. assertex(!m_compare);
  1164. assertex(from && *from);
  1165. // If old path doesn't exist, then nothing to rename
  1166. char oldPath[_MAX_PATH];
  1167. strcpy(oldPath, from);
  1168. removeTrailingPathSepChar(oldPath);
  1169. if (!checkFileExists(oldPath))
  1170. return;
  1171. char newPath[_MAX_PATH];
  1172. if (to && *to)
  1173. {
  1174. // Use destination provided
  1175. assertex(strcmp(from, to) != 0);
  1176. strcpy(newPath, to);
  1177. removeTrailingPathSepChar(newPath);
  1178. }
  1179. else
  1180. {
  1181. // Create new path name with date suffix
  1182. time_t t = time(NULL);
  1183. struct tm* now = localtime(&t);
  1184. sprintf(newPath, "%s_%02d_%02d", oldPath, now->tm_mon + 1, now->tm_mday);
  1185. while (checkFileExists(newPath))
  1186. {
  1187. size32_t end = strlen(newPath) - 1;
  1188. char ch = newPath[end];
  1189. if (ch >= 'a' && ch < 'z')
  1190. newPath[end] = ch + 1;
  1191. else
  1192. strcat(newPath, "a");
  1193. }
  1194. }
  1195. // Save rename task
  1196. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Rename", m_process.queryName(), m_name.get(),
  1197. m_curInstance, oldPath, newPath, m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os);
  1198. m_renameDirList.append(*task.getLink());
  1199. }
  1200. //---------------------------------------------------------------------------
  1201. // backupDir
  1202. //---------------------------------------------------------------------------
  1203. void CDeploymentEngine::backupDir(const char* from)
  1204. {
  1205. assertex(from && *from);
  1206. // If from path doesn't exist, then nothing to backup
  1207. char fromPath[_MAX_PATH];
  1208. strcpy(fromPath, from);
  1209. removeTrailingPathSepChar(fromPath);
  1210. if (!checkFileExists(fromPath)) return;
  1211. StringBuffer toPath;
  1212. getBackupDirName(from, toPath);
  1213. // Copy directory
  1214. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Backup Directory", m_process.queryName(), m_name.get(),
  1215. m_curInstance, fromPath, toPath.str(), m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined);
  1216. m_pCallback->printStatus(task);
  1217. task->copyDirectory();
  1218. m_pCallback->printStatus(task);
  1219. checkAbort(task);
  1220. }
  1221. void CDeploymentEngine::getBackupDirName(const char* from, StringBuffer& to)
  1222. {
  1223. // If from path doesn't exist, then nothing to backup
  1224. char fromPath[_MAX_PATH];
  1225. strcpy(fromPath, from);
  1226. removeTrailingPathSepChar(fromPath);
  1227. if (!checkFileExists(fromPath)) return;
  1228. // Create to path name with date suffix
  1229. char toPath[_MAX_PATH];
  1230. time_t t = time(NULL);
  1231. struct tm* now = localtime(&t);
  1232. sprintf(toPath, "%s_%02d_%02d", fromPath, now->tm_mon + 1, now->tm_mday);
  1233. while (checkFileExists(toPath))
  1234. {
  1235. size32_t end = strlen(toPath) - 1;
  1236. char ch = toPath[end];
  1237. if (ch >= 'a' && ch < 'z')
  1238. toPath[end] = ch + 1;
  1239. else
  1240. strcat(toPath, "a");
  1241. }
  1242. to.clear().append(toPath);
  1243. }
  1244. //---------------------------------------------------------------------------
  1245. // copyInstallFiles
  1246. //---------------------------------------------------------------------------
  1247. void CDeploymentEngine::copyInstallFiles(IPropertyTree& instanceNode, const char* destPath)
  1248. {
  1249. EnvMachineOS os = m_envDepEngine.lookupMachineOS(instanceNode);
  1250. int instanceIndex = m_instances.find(instanceNode);
  1251. const char* instanceName = instanceNode.queryProp("@name");
  1252. copyInstallFiles(instanceName, instanceIndex, destPath, os);
  1253. }
  1254. void CDeploymentEngine::copyInstallFiles(const char* instanceName, int instanceIndex, const char* destPath, EnvMachineOS os)
  1255. {
  1256. bool bCacheFiles = instanceIndex == -1 && !strcmp(instanceName, "Cache");
  1257. s_dynamicFileList.clear();
  1258. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL,
  1259. m_compare ? "Comparing install files for %s with %s..." : "Copying install files for %s to %s...",
  1260. m_name.get(), destPath);
  1261. if (m_threadPool == NULL)
  1262. {
  1263. IThreadFactory* pThreadFactory = createDeployTaskThreadFactory();
  1264. m_threadPool.setown(createThreadPool("Deploy Task Thread Pool", pThreadFactory, this, DEPLOY_THREAD_POOL_SIZE));
  1265. pThreadFactory->Release();
  1266. }
  1267. else
  1268. {
  1269. int nThreads = m_threadPool->runningCount();
  1270. if (nThreads > 0)
  1271. throw MakeOsException(-1, "Unfinished threads detected!");
  1272. }
  1273. bool bCompare = m_compare;//save
  1274. try
  1275. {
  1276. m_pCallback->setAbortStatus(false);
  1277. initializeMultiThreadedCopying();
  1278. const CInstallFileList& fileList = m_installFiles.getInstallFileList();
  1279. int nItems = fileList.size();
  1280. int n;
  1281. bool recCopyDone = false;
  1282. for (n=0; n<nItems; n++)
  1283. {
  1284. CInstallFile& installFile = *fileList[n];
  1285. const bool bCacheable = installFile.isCacheable();
  1286. if (!bCacheFiles || bCacheable)
  1287. {
  1288. const char* method = installFile.getMethod().c_str();
  1289. const char* source = installFile.getSrcPath().c_str();
  1290. const char* params = installFile.getParams().c_str();
  1291. string dest = installFile.getDestPath().c_str();
  1292. std::string::size_type pos;
  1293. if ((pos = dest.find("@temp" PATHSEPSTR)) != std::string::npos)
  1294. {
  1295. dest.replace(pos, strlen("@temp" PATHSEPSTR), m_cachePath.get());
  1296. //a temp file should not be copied over itself so ignore
  1297. if (!bCacheFiles && !stricmp(source, dest.c_str()))
  1298. continue;
  1299. }
  1300. else
  1301. dest.insert(0, destPath);//note that destPath is always terminated by PATHSEPCHAR
  1302. if (params && !*params)
  1303. params = NULL;
  1304. if (m_useSSHIfDefined && !bCacheFiles && !m_compare &&
  1305. !strcmp(method, "copy") && strcmp(instanceName, "Cache"))
  1306. {
  1307. if (!recCopyDone)
  1308. {
  1309. StringBuffer sbsrc(source);
  1310. if (strrchr(source, PATHSEPCHAR))
  1311. sbsrc.setLength(strrchr(source, PATHSEPCHAR) - source + 1);
  1312. sbsrc.append("*");
  1313. StringBuffer sbdst(dest.c_str());
  1314. if (strrchr(sbdst.str(), PATHSEPCHAR))
  1315. sbdst.setLength(strrchr(sbdst.str(), PATHSEPCHAR) - sbdst.str() + 1);
  1316. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Copy Directory", m_process.queryName(), m_name.get(),
  1317. m_curInstance, sbsrc.str(), sbdst.str(), m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os);
  1318. task->setFlags(m_deployFlags & DCFLAGS_ALL);
  1319. m_threadPool->start(task);
  1320. recCopyDone = true;
  1321. }
  1322. continue;
  1323. }
  1324. if (!processInstallFile(m_process, instanceName, method, source, dest.c_str(), os, bCacheable, params))
  1325. break;
  1326. if (bCacheFiles && bCacheable)
  1327. {
  1328. if (0 != stricmp(method, "copy"))
  1329. installFile.setMethod("copy");
  1330. installFile.setSrcPath(dest.c_str());
  1331. m_envDepEngine.addTempFile(dest.c_str());
  1332. }
  1333. }
  1334. }//for
  1335. //now process any dynamically added files (via xslt's external function)
  1336. nItems = s_dynamicFileList.size();
  1337. for (n=0; n<nItems; n++)
  1338. {
  1339. const CInstallFile& installFile = *s_dynamicFileList[n];
  1340. //const bool bCacheable = installFile.isCacheable();
  1341. //if (bCacheable && instanceIndex>0)
  1342. // continue;
  1343. const char* method = installFile.getMethod().c_str();
  1344. //if we are not deploying build files and method is copy then ignore this file
  1345. if (!(m_deployFlags & DEFLAGS_BUILDFILES) && (!method || !stricmp(method, "copy")))
  1346. continue;
  1347. const char* source = installFile.getSrcPath().c_str();
  1348. const char* params = installFile.getParams().c_str();//only supported for dynamically added XSLT files
  1349. bool bCacheable = installFile.isCacheable();
  1350. string dest = installFile.getDestPath();
  1351. StringBuffer src(source);
  1352. if (params && !*params)
  1353. params = NULL;
  1354. if (dest.empty())
  1355. dest += pathTail( installFile.getSrcPath().c_str() );
  1356. //any dynamically generated paths are generated with '\\'
  1357. //In configenv, remote copying takes care of the paths
  1358. //if this is configgen, and we are on linux, replace
  1359. //paths with right separator
  1360. if (PATHSEPCHAR == '/' && os == MachineOsLinux)
  1361. src.replace('\\', '/');
  1362. //resolve conflicts if method is not schema or del
  1363. //find all occurrences of this destination file in the map and resove any conflicts
  1364. //like size mismatch etc.
  1365. bool bAddToFileMap = m_installFiles.resolveConflicts(m_process, method, src.str(), dest.c_str(),
  1366. m_name, m_curInstance, params);
  1367. if (bAddToFileMap)
  1368. {
  1369. CInstallFile* pInstallFileAdded = m_installFiles.addInstallFile(method, src.str(), dest.c_str(), bCacheable, params);
  1370. std::string::size_type pos;
  1371. if ((pos = dest.find("@temp" PATHSEPSTR)) != std::string::npos)
  1372. dest.replace(pos, strlen("@temp" PATHSEPSTR), m_cachePath.get());
  1373. else
  1374. dest.insert(0, destPath);//note that destPath is always terminated by PATHSEPCHAR
  1375. if (!processInstallFile(m_process, instanceName, method, src.str(), dest.c_str(), os, bCacheable, params))
  1376. break;
  1377. if (bCacheFiles && bCacheable)
  1378. {
  1379. if (0 != stricmp(method, "copy"))
  1380. pInstallFileAdded->setMethod("copy");
  1381. pInstallFileAdded->setSrcPath(dest.c_str());
  1382. m_envDepEngine.addTempFile(dest.c_str());
  1383. }
  1384. }
  1385. }//for
  1386. m_threadPool->joinAll();
  1387. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  1388. // if (m_createIni)
  1389. // createIniFile(destPath, os);
  1390. }
  1391. catch (IException* e)
  1392. {
  1393. m_compare = bCompare;
  1394. m_threadPool->joinAll();
  1395. throw e;
  1396. }
  1397. catch (...)
  1398. {
  1399. m_compare = bCompare;
  1400. m_threadPool->joinAll();
  1401. throw MakeErrnoException("Error deploying %s", m_name.get());
  1402. }
  1403. }
  1404. bool CDeploymentEngine::processInstallFile(IPropertyTree& processNode, const char* instanceName,
  1405. const char* method, const char* source, const char* dest,
  1406. EnvMachineOS os, bool bCacheable, const char* params/*=NULL*/)
  1407. {
  1408. while (true)
  1409. {
  1410. try
  1411. {
  1412. checkAbort();
  1413. if (m_pCallback->getAbortStatus())
  1414. return false;
  1415. bool bCompare = m_compare;//save
  1416. //if we are creating a temporary file then disable comparing - deploy
  1417. //since the previously generated temporary file would have been deleted by now
  1418. if (m_compare)
  1419. {
  1420. char tempfile[_MAX_PATH];
  1421. getTempPath(tempfile, sizeof(tempfile), m_name);
  1422. if (!strncmp(dest, tempfile, strlen(tempfile)))
  1423. m_compare = false;
  1424. }
  1425. // Skip if method is copy and we are not copying files
  1426. if (!strnicmp(method, "copy", 4))
  1427. {
  1428. if (m_compare)
  1429. {
  1430. compareFiles(source, dest, os );
  1431. Owned<IFile> f = createIFile(source);
  1432. getCallback().fileSizeCopied(f->size(),true);
  1433. }
  1434. else
  1435. {
  1436. // Copy the file
  1437. ensurePath(dest);
  1438. if (!stricmp(method+4, "_block_until_done")) //copy_block_until_done
  1439. {
  1440. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Copy File", m_process.queryName(), m_name.get(),
  1441. m_curInstance, source, dest, m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os);
  1442. task->setFlags(m_deployFlags & DCFLAGS_ALL);
  1443. task->copyFile( m_deployFlags & (DCFLAGS_ALL | DTC_DEL_WRONG_CASE));
  1444. m_pCallback->printStatus(task);
  1445. if (task && task->getAbort())
  1446. throw MakeStringException(0, "User abort");
  1447. }
  1448. else
  1449. {
  1450. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Copy File", m_process.queryName(), m_name.get(),
  1451. m_curInstance, source, dest, m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined, os);
  1452. task->setFlags(m_deployFlags & DCFLAGS_ALL);
  1453. m_threadPool->start(task);//start a thread for this task
  1454. }
  1455. }
  1456. }
  1457. else if (!strnicmp(method, "xsl", 3)) //allow xsl, xslt or any other string starting with xsl
  1458. {
  1459. unsigned deployFlags = m_deployFlags;
  1460. if (!strcmp(method, "xslt_deployment"))
  1461. m_deployFlags = deployFlags | DEFLAGS_CONFIGFILES;
  1462. s_bCacheableDynFile = bCacheable;
  1463. xslTransform(source, dest, instanceName, os, params);//params only supported for dynamically added xslt files
  1464. s_bCacheableDynFile = false;
  1465. m_deployFlags = deployFlags;
  1466. }
  1467. else if (!stricmp(method, "esp_service_module")) //processed from CEspDeploymentEngine::xslTransform
  1468. ;
  1469. else if (!stricmp(method, "esp_plugin")) //processed from CEspDeploymentEngine::xslTransform
  1470. ;
  1471. else if (!stricmp(method, "model"))
  1472. {
  1473. //extract name of model from dest file path
  1474. StringBuffer dir;
  1475. const char* pszFileName = splitDirTail(dest, dir);
  1476. const char* pszExtension= strchr(pszFileName, '.');
  1477. if (pszExtension == NULL)
  1478. pszExtension = pszFileName + strlen(pszFileName);
  1479. StringBuffer modelName;
  1480. modelName.append('\'').append(pszExtension - pszFileName, pszFileName).append('\'');
  1481. m_transform->setParameter("modelName", modelName.str());
  1482. xslTransform(source, dest, instanceName, os);
  1483. }
  1484. /* -- unsupported now since this was deemed security hole --
  1485. else if (!stricmp(method, "exec"))
  1486. {
  1487. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Executing %s", dest);
  1488. StringBuffer xpath;
  1489. xpath.appendf("Instance[@name='%s']", instanceName);
  1490. IPropertyTree* pInstanceNode = processNode.queryPropTree(xpath.str());
  1491. StringAttr user, pwd;
  1492. m_envDepEngine.getAccountInfo(pInstanceNode->queryProp("@computer"), user, pwd);
  1493. // Spawn start process
  1494. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Remote Execution", m_name.get(), m_curInstance, NULL, dest, os);
  1495. task->createProcess(true, user, pwd);
  1496. m_pCallback->printStatus(task);
  1497. checkAbort(task);
  1498. }
  1499. else if (!strnicmp(method, "dxsl", 4)) //allow dxsl, dxslt or any other string starting with dxsl
  1500. {
  1501. StringBuffer t(source);
  1502. t.append(".xsl");
  1503. xslTransform(source, t.str(), instanceName, os);
  1504. xslTransform(t.str(), dest, instanceName, os);
  1505. remove(t.str());
  1506. }
  1507. */
  1508. else
  1509. processCustomMethod(method, source, dest, instanceName, os);
  1510. m_compare = bCompare;
  1511. break;
  1512. }
  1513. catch (IException* e)
  1514. {
  1515. if (m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, e))//ignore ?
  1516. break;
  1517. }
  1518. catch (...)
  1519. {
  1520. if (m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, NULL))//ignore ?
  1521. break;
  1522. }
  1523. }
  1524. return true;
  1525. }
  1526. //---------------------------------------------------------------------------
  1527. // beforeDeployInstance
  1528. //---------------------------------------------------------------------------
  1529. void CDeploymentEngine::beforeDeployInstance(IPropertyTree& instanceNode, const char* destPath)
  1530. {
  1531. }
  1532. //---------------------------------------------------------------------------
  1533. // checkFileExists
  1534. //---------------------------------------------------------------------------
  1535. bool CDeploymentEngine::checkFileExists(const char* filename) const
  1536. {
  1537. StringBuffer xpath, err;
  1538. bool flag = false;
  1539. xpath.appendf("./Instance[@name='%s']", m_curInstance);
  1540. IPropertyTree* pInstanceNode = m_process.queryPropTree(xpath.str());
  1541. EnvMachineOS os = MachineOsW2K;
  1542. if (!pInstanceNode)
  1543. {
  1544. StringBuffer destpath, ip;
  1545. stripNetAddr(filename, destpath, ip);
  1546. if (ip.length())
  1547. {
  1548. IConstMachineInfo* pInfo = m_envDepEngine.getEnvironment().getMachineByAddress(ip.str());
  1549. os = pInfo->getOS();
  1550. }
  1551. }
  1552. else
  1553. os = m_envDepEngine.lookupMachineOS(*pInstanceNode);
  1554. if (os == MachineOsLinux && m_curSSHUser.length() && m_curSSHKeyFile.length())
  1555. return checkSSHFileExists(filename);
  1556. else
  1557. {
  1558. Owned<IFile> pIFile = createIFile(filename);
  1559. return pIFile->exists();
  1560. }
  1561. }
  1562. //---------------------------------------------------------------------------
  1563. // setXsl
  1564. //---------------------------------------------------------------------------
  1565. void CDeploymentEngine::setXsl(IXslProcessor* processor, IXslTransform* transform)
  1566. {
  1567. m_processor = processor;
  1568. m_transform = transform;
  1569. m_externalFunction.setown(m_transform->createExternalFunction("addDeploymentFile", addDeploymentFile));
  1570. m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction.get(), true);
  1571. m_externalFunction2.setown(m_transform->createExternalFunction("siteCertificate", siteCertificateFunction));
  1572. m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction2.get(), true);
  1573. }
  1574. //---------------------------------------------------------------------------
  1575. // createIniFile
  1576. //---------------------------------------------------------------------------
  1577. void CDeploymentEngine::createIniFile(const char* destPath, EnvMachineOS os)
  1578. {
  1579. // Check if INI file needs to be created
  1580. if (m_iniFile.length() == 0) return;
  1581. // Output all attributes - except certain ones
  1582. const char* ignore[] = {"name", "description", "build", "buildSet" };
  1583. const char** begin = &ignore[0];
  1584. const char** end = &ignore[sizeof(ignore)/sizeof(*ignore)];
  1585. StringBuffer str;
  1586. str.append("# INI file generated by CDeploymentEngine\n\n");
  1587. Owned<IAttributeIterator> aiter = m_process.getAttributes();
  1588. for (aiter->first(); aiter->isValid(); aiter->next())
  1589. {
  1590. // Only output attribute if its value is non-blank
  1591. const char* name = &aiter->queryName()[1];
  1592. const char* val = aiter->queryValue();
  1593. if (val && *val)
  1594. {
  1595. if (std::find_if(begin, end, std::bind1st(string_compare(), name)) == end)
  1596. {
  1597. str.appendf("%s=%s\n", name, val);
  1598. }
  1599. }
  1600. }
  1601. writeFile(StringBuffer(destPath).append(m_iniFile).str(), str.str(), os);
  1602. }
  1603. //---------------------------------------------------------------------------
  1604. // getBuildSetNode
  1605. //---------------------------------------------------------------------------
  1606. IPropertyTree* CDeploymentEngine::queryBuildSetNode(IPropertyTree& processNode,
  1607. IPropertyTree*& buildNode) const
  1608. {
  1609. // Get build node for process
  1610. StringBuffer xpath("Programs/Build[@name='");
  1611. xpath.appendf("%s']", processNode.queryProp("@build"));
  1612. buildNode = m_rootNode->queryPropTree(xpath.str());
  1613. assertex(buildNode);
  1614. // Get buildSet node for process
  1615. xpath.clear();
  1616. xpath.appendf("BuildSet[@name=\"%s\"]", processNode.queryProp("@buildSet"));
  1617. IPropertyTree* buildSetNode = buildNode->queryPropTree(xpath.str());
  1618. assertex(buildSetNode);
  1619. return buildSetNode;
  1620. }
  1621. IPropertyTree* CDeploymentEngine::getDeployMapNode(IPropertyTree* buildNode, IPropertyTree* buildSetNode) const
  1622. {
  1623. // Get some useful attributes
  1624. const char* url = buildNode->queryProp("@url");
  1625. const char* path = buildSetNode->queryProp("@path");
  1626. const char* installSet = buildSetNode->queryProp("@installSet");
  1627. // Workout name for deploy map file
  1628. StringBuffer deployFile(url);
  1629. if (path && *path)
  1630. deployFile.append(PATHSEPCHAR).append(path);
  1631. if (installSet && *installSet)
  1632. deployFile.append(PATHSEPCHAR).append(installSet);
  1633. else
  1634. deployFile.append("\\deploy_map.xml");
  1635. // Read in deploy map file and process file elements
  1636. IPropertyTree* deployNode = createPTreeFromXMLFile(deployFile.str(), ipt_caseInsensitive);
  1637. assertex(deployNode);
  1638. return deployNode;
  1639. }
  1640. bool CDeploymentEngine::searchDeployMap(const char* fileName, const char* optionalFileExt) const
  1641. {
  1642. IPropertyTree* buildNode;
  1643. IPropertyTree* buildSetNode = queryBuildSetNode(m_process, buildNode);
  1644. Owned<IPropertyTree> deployNode = getDeployMapNode(buildNode, buildSetNode);
  1645. StringBuffer xpath;
  1646. xpath.appendf("File[@name='%s']", fileName);
  1647. bool bFound = false;
  1648. Owned<IPropertyTreeIterator> iter = deployNode->getElements(xpath.str());
  1649. if (iter->first() && iter->isValid())
  1650. bFound = true;
  1651. else
  1652. {
  1653. xpath.clear().appendf("File[@name='%s%s']", fileName, optionalFileExt);
  1654. iter.setown( deployNode->getElements(xpath.str()) );
  1655. if (iter->first() && iter->isValid())
  1656. bFound = true;
  1657. }
  1658. return bFound;
  1659. }
  1660. //---------------------------------------------------------------------------
  1661. // determineInstallFiles
  1662. //---------------------------------------------------------------------------
  1663. int CDeploymentEngine::determineInstallFiles(IPropertyTree& processNode, CInstallFiles& installFiles) const
  1664. {
  1665. try
  1666. {
  1667. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL,
  1668. "Determining files to install for %s", processNode.queryProp("@name"));
  1669. IPropertyTree* buildNode;
  1670. IPropertyTree* buildSetNode = queryBuildSetNode(processNode, buildNode);
  1671. Owned<IPropertyTree> deployNode = getDeployMapNode(buildNode, buildSetNode);
  1672. StringBuffer srcFilePath;
  1673. srcFilePath.ensureCapacity(_MAX_PATH);
  1674. const char* url = buildNode->queryProp("@url");
  1675. const char* path = buildSetNode->queryProp("@path");
  1676. const bool bFindStartable = &m_process == &processNode && m_startable == unknown;
  1677. const bool bFindStoppable = &m_process == &processNode && m_stoppable == unknown;
  1678. Owned<IPropertyTreeIterator> iter = deployNode->getElements("File");
  1679. ForEach(*iter)
  1680. {
  1681. IPropertyTree* pFile = &iter->query();
  1682. // Get some useful attributes
  1683. const char* name = pFile->queryProp("@name");
  1684. //if this file is an installset (deploy_map.xml) then ignore it (don't deploy)
  1685. //
  1686. if (!stricmp(name, "deploy_map.xml"))
  1687. continue;
  1688. if (bFindStartable && !strnicmp(name, "startup", sizeof("startup")-1))
  1689. m_startable = yes;
  1690. if (bFindStoppable && !strnicmp(name, "stop", sizeof("stop")-1))
  1691. m_stoppable = yes;
  1692. const char* method = pFile->queryProp("@method");
  1693. if (method && !stricmp(method, "schema"))
  1694. continue;// ignore - could validate against it if we felt brave!
  1695. //if we are not deploying build files and method is copy then ignore this file
  1696. if (!(m_deployFlags & DEFLAGS_BUILDFILES) && (!method || !stricmp(method, "copy")))
  1697. continue;
  1698. const char* srcPath = pFile->queryProp("@srcPath");
  1699. const char* destPath= pFile->queryProp("@destPath");
  1700. const char* destName= pFile->queryProp("@destName");
  1701. bool bCacheable = pFile->getPropBool("@cache", false);
  1702. // Get source filespec
  1703. if (srcPath && !strcmp(srcPath, "@temp"))
  1704. {
  1705. char tempfile[_MAX_PATH];
  1706. getTempPath(tempfile, sizeof(tempfile), m_name);
  1707. srcFilePath.clear().append(tempfile).append(name);
  1708. }
  1709. else
  1710. {
  1711. srcFilePath.clear().append(url).append(PATHSEPCHAR);
  1712. if (path && *path)
  1713. srcFilePath.append(path).append(PATHSEPCHAR);
  1714. if (srcPath && 0!=strcmp(srcPath, "."))
  1715. {
  1716. if (!strncmp(srcPath, "..", 2) && (*(srcPath+2)=='/' || *(srcPath+2)=='\\'))
  1717. {
  1718. StringBuffer reldir(srcPath);
  1719. reldir.replace('/', '\\');
  1720. while (!strncmp(reldir.str(), "..\\", 3))
  1721. {
  1722. srcFilePath.setLength( srcFilePath.length() - 1 ); //remove last char PATHSEPCHAR
  1723. const char* tail = pathTail(srcFilePath.str());
  1724. srcFilePath.setLength( tail - srcFilePath.str() );
  1725. reldir.remove(0, 3);
  1726. }
  1727. srcFilePath.append(reldir).append(PATHSEPCHAR);
  1728. }
  1729. else
  1730. srcFilePath.append(srcPath).append(PATHSEPCHAR);
  1731. }
  1732. srcFilePath.append(name);
  1733. }
  1734. std::string sDestName;
  1735. if (method && (!stricmp(method, "esp_service_module") || !stricmp(method, "esp_plugin")))
  1736. {
  1737. //if this is xsl transformation and we are not generating config files then ignore
  1738. //
  1739. if (!(m_deployFlags & DEFLAGS_CONFIGFILES) && !stricmp(method, "esp_service_module"))
  1740. continue;
  1741. //if this file is an esp service module, encode name of service in the dest file name
  1742. //so the esp deployment can figure out which service this file belongs to
  1743. //
  1744. const char* serviceName = processNode.queryProp("@name");
  1745. //if destination name is specified then use it otherwise use <service-name>[index of module].xml
  1746. sDestName = serviceName;
  1747. if (destName)
  1748. {
  1749. sDestName += '_';
  1750. sDestName += destName;
  1751. }
  1752. else
  1753. {
  1754. int espServiceModules = m_envDepEngine.incrementEspModuleCount();
  1755. if (espServiceModules > 1)
  1756. {
  1757. char achNum[16];
  1758. itoa(espServiceModules, achNum, 10);
  1759. sDestName += achNum;
  1760. }
  1761. sDestName += ".xml";
  1762. }
  1763. //encode name of service herein - this is needed by and removed by CEspDeploymentEngine::processServiceModules()
  1764. sDestName += '+';
  1765. sDestName += processNode.queryProp("@name");//encode the name of service
  1766. }
  1767. else if (method && (!stricmp(method, "xsl") || !stricmp(method, "xslt")) && !(m_deployFlags & DEFLAGS_CONFIGFILES))
  1768. continue;//ignore xsl transformations if we are not generating config files
  1769. else
  1770. {
  1771. if (!method || !*method)
  1772. method = "copy";
  1773. // Get destination filespec
  1774. if (destName && *destName)
  1775. {
  1776. //we now support attribute names within the destination file names like delimted by @ and + (optional)
  1777. //for e.g. segment_@attrib1+_file_@attrib2 would produce segment_attribval1_file_attrib2value
  1778. //+ not necessary if the attribute name ends with the word, for e.g. file_@attrib1
  1779. //for instnace, suite_@eclServer+.bat would expand to suite_myeclserver.bat
  1780. //if this process has an @eclServer with value "myeclserver"
  1781. //
  1782. if (strchr(destName, '@') || strchr(destName, '+'))
  1783. {
  1784. char* pszParts = strdup(destName);
  1785. char *saveptr;
  1786. const char* pszPart = strtok_r(pszParts, "+", &saveptr);
  1787. while (pszPart)
  1788. {
  1789. const char* p = pszPart;
  1790. if (*p)
  1791. {
  1792. if (strchr(p, '@'))//xpath for an attribute?
  1793. {
  1794. // find name of attribute and replace it with its value
  1795. const char* value = m_process.queryProp( p );
  1796. if (value)
  1797. sDestName.append(value);
  1798. }
  1799. else
  1800. sDestName.append(p); //no attribute so copy verbatim
  1801. }
  1802. pszPart = strtok_r(NULL, "+", &saveptr);
  1803. }
  1804. free(pszParts);
  1805. }
  1806. else
  1807. sDestName = destName;
  1808. if (sDestName.empty())
  1809. throw MakeStringException(-1, "The destination file name '%s' for source file '%s' "
  1810. "translates to an empty string!", destName, name);
  1811. }
  1812. }
  1813. StringBuffer destFilePath;
  1814. destFilePath.ensureCapacity(_MAX_PATH);
  1815. bool bTempFile = (destPath && !stricmp(destPath, "@temp")) ||
  1816. !strnicmp(name, "@temp", 5); //@name starts with @temp or @tmp
  1817. if (bTempFile)
  1818. {
  1819. if (sDestName.empty())//dest name not specified
  1820. {
  1821. if (!strcmp(method, "copy"))
  1822. sDestName = name;
  1823. else
  1824. {
  1825. StringBuffer dir;
  1826. const char* pszFileName = splitDirTail(name, dir);
  1827. const char* pExt = findFileExtension(pszFileName);
  1828. if (pExt)
  1829. sDestName.append(pszFileName, pExt-pszFileName);
  1830. else
  1831. sDestName.append(pszFileName);
  1832. char index[16];
  1833. itoa(m_envDepEngine.incrementTempFileCount(), index, 10);
  1834. sDestName.append(index);
  1835. if (pExt)
  1836. sDestName.append(pExt);
  1837. }
  1838. }
  1839. destFilePath.append("@temp" PATHSEPSTR);
  1840. }
  1841. else
  1842. {
  1843. if (destPath && *destPath)
  1844. {
  1845. destFilePath.append(destPath);
  1846. if (destPath[strlen(destPath)-1] != PATHSEPCHAR)
  1847. destFilePath.append(PATHSEPCHAR);
  1848. }
  1849. if (sDestName.empty())
  1850. sDestName = name;
  1851. }
  1852. destFilePath.append(sDestName.c_str());
  1853. //find all occurrences of this destination file in the map and resove any conflicts
  1854. //like size mismatch etc.
  1855. bool bAddToFileMap = installFiles.resolveConflicts(processNode, method, srcFilePath.str(), destFilePath.str(),
  1856. m_name, m_curInstance, NULL);
  1857. //resolve conflicts if method is not schema or exec
  1858. if (0 != stricmp(method, "schema") && 0 != stricmp(method, "exec") && 0 != strnicmp(method, "del", 3))
  1859. {
  1860. }
  1861. else if (!strnicmp(method, "del", 3))//treat files to be deleted as temp files - to be deleted AFTER we are done!
  1862. {
  1863. bTempFile = true;
  1864. bAddToFileMap = false;
  1865. m_envDepEngine.addTempFile(destFilePath.str());
  1866. }
  1867. if (bAddToFileMap)
  1868. {
  1869. if (bTempFile)
  1870. m_envDepEngine.addTempFile(destFilePath.str());
  1871. //enable caching for files to be copied unless expressly asked not to do so
  1872. //
  1873. if (!bCacheable && !strcmp(method, "copy"))
  1874. bCacheable = pFile->getPropBool("@cache", true);
  1875. installFiles.addInstallFile(method, srcFilePath.str(), destFilePath.str(), bCacheable, NULL);
  1876. }
  1877. }
  1878. }
  1879. catch (IException* e)
  1880. {
  1881. StringBuffer msg;
  1882. e->errorMessage(msg);
  1883. e->Release();
  1884. throw MakeStringException(0, "Error creating file list for process %s: %s", m_name.get(), msg.str());
  1885. }
  1886. catch (...)
  1887. {
  1888. throw MakeErrnoException("Error creating file list for process %s", m_name.get());
  1889. }
  1890. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, NULL);
  1891. return installFiles.getInstallFileList().size();
  1892. }
  1893. //---------------------------------------------------------------------------
  1894. // resolveConflicts
  1895. //---------------------------------------------------------------------------
  1896. bool CInstallFileMap::resolveConflicts(IPropertyTree& processNode, const char* method, const char* srcPath,
  1897. const char* destPath, const char* compName,
  1898. const char* instanceName, const char* params)
  1899. {
  1900. bool rc = true;//no unresolved conflicts so add to file map
  1901. //DBGLOG("Resolving conflicts for %s from %s\n", srcPath, destPath);
  1902. //resolve conflicts if method is not schema or del
  1903. if (0 != stricmp(method, "schema") && 0 != stricmp(method, "del"))
  1904. {
  1905. //find all occurrences of this destination file in the map and resove any conflicts
  1906. //like size mismatch etc.
  1907. const_iterator iLower = lower_bound(destPath);
  1908. const_iterator iUpper = upper_bound(destPath);
  1909. offset_t srcSz = 0;
  1910. unsigned srcCRC;
  1911. for (const_iterator it=iLower; it != iUpper; it++)
  1912. {
  1913. rc = false;//don't add to file map
  1914. CInstallFile* pInstallFile = (*it).second;
  1915. const char* method2 = pInstallFile->getMethod().c_str();
  1916. const char* srcPath2= pInstallFile->getSrcPath().c_str();
  1917. const char* params2 = pInstallFile->getParams().c_str();
  1918. //if file at destPath is being generated using same method and src file
  1919. //anyway then don't warn - just don't add to file map
  1920. //
  1921. if ((!stricmp(method, method2) && !stricmp(srcPath, srcPath2)) || pInstallFile->isDuplicateSrcFile(srcPath))
  1922. {
  1923. //if method is xslt and params are different, then add to file map
  1924. if (!stricmp(method, "xslt") && ((params == NULL && params2!= NULL) ||
  1925. (params != NULL && params2 == NULL) ||
  1926. (0 != stricmp(params, params2))))
  1927. rc = true;
  1928. break;
  1929. }
  1930. bool rc2=true;
  1931. if (srcSz == 0)
  1932. {
  1933. try
  1934. {
  1935. srcCRC = getFileCRC(srcPath);
  1936. srcSz = filesize(srcPath);
  1937. }
  1938. catch(IException* e)
  1939. {
  1940. rc2 = false;
  1941. StringBuffer msg;
  1942. e->errorMessage(msg);
  1943. m_pDepEngine->getCallback().printStatus(STATUS_WARN, processNode.queryName(),
  1944. processNode.queryProp("@name"), instanceName, "%s", msg.str());
  1945. }
  1946. }
  1947. offset_t srcSz2;
  1948. unsigned srcCRC2;
  1949. if (rc2)
  1950. {
  1951. try {
  1952. srcSz2 = pInstallFile->getSrcSize();
  1953. srcCRC2 = pInstallFile->getSrcCRC();;
  1954. } catch(IException* e) {
  1955. rc2 = false;
  1956. StringBuffer msg;
  1957. e->errorMessage(msg);
  1958. m_pDepEngine->getCallback().printStatus(STATUS_WARN, processNode.queryName(),
  1959. processNode.queryProp("@name"), instanceName, "%s", msg.str());
  1960. }
  1961. }
  1962. if (rc2)
  1963. {
  1964. if (srcSz == srcSz2 && srcCRC == srcCRC2)
  1965. pInstallFile->addDuplicateSrcFile( srcPath );
  1966. else
  1967. {
  1968. //for starters, just display an error on every conflict even if this is a
  1969. //redeployment of the same file
  1970. //
  1971. const char* fileName = pathTail(destPath);
  1972. if (!fileName)
  1973. fileName = destPath;
  1974. StringBuffer path1;
  1975. const char* srcFileName1 = splitDirTail(srcPath, path1);
  1976. if (0 != strcmp(srcFileName1, fileName))//src file name is not same as dest file name
  1977. path1.clear().append(srcPath);
  1978. else
  1979. path1.remove( path1.length()-1, 1);
  1980. StringBuffer path2;
  1981. const char* srcFileName2 = splitDirTail(srcPath2, path2);
  1982. if (0 != strcmp(srcFileName2, fileName))//src file name is not same as dest file name
  1983. path2.clear().append(srcPath2);
  1984. else
  1985. path2.remove( path2.length()-1, 1);
  1986. bool bDiffMethods = 0 != strcmp(method, method2);
  1987. StringBuffer msg;
  1988. msg.appendf("File skipped: The file %s to be deployed from %s ", fileName, path1.str());
  1989. if (bDiffMethods)
  1990. msg.appendf("by method '%s' ", method);
  1991. msg.appendf("conflicts with another file from %s", path2.str());
  1992. if (bDiffMethods)
  1993. msg.appendf(" using method '%s'", method2);
  1994. msg.append('.');
  1995. m_pDepEngine->getCallback().printStatus(STATUS_WARN, processNode.queryName(),
  1996. processNode.queryProp("@name"), instanceName,
  1997. "%s", msg.str());
  1998. }
  1999. break;
  2000. }
  2001. }
  2002. }
  2003. return rc;
  2004. }
  2005. //---------------------------------------------------------------------------
  2006. // addDeploymentFile
  2007. //---------------------------------------------------------------------------
  2008. /*static*/
  2009. void CDeploymentEngine::addDeploymentFile(StringBuffer &ret, const char *in, IXslTransform*)
  2010. {
  2011. //input is of the format <method>+<file name>+<source dir>+<dest filename>[+<dest subdir>]
  2012. StringArray tokens;
  2013. DelimToStringArray(in, tokens, "+");
  2014. int len = tokens.length();
  2015. if (len < 4)
  2016. throw MakeStringException(0, "Invalid format for external function parameter!");
  2017. const char* method = tokens.item(0);
  2018. const char* name = tokens.item(1);
  2019. const char* srcPath = tokens.item(2);
  2020. const char* destName= tokens.item(3);
  2021. StringBuffer srcPath2(srcPath);
  2022. srcPath2.append(name);
  2023. StringBuffer destPath;
  2024. if (len > 4)
  2025. destPath.append(tokens.item(4));
  2026. destPath.append(destName);
  2027. Owned<CInstallFile> pInstallFile = new CInstallFile(method, srcPath2.str(), destPath.str(), s_bCacheableDynFile);
  2028. if (len > 5)
  2029. pInstallFile->setParams(tokens.item(5));
  2030. s_dynamicFileList.push_back(StlLinkedFilePtr(pInstallFile.get()));
  2031. }
  2032. //---------------------------------------------------------------------------
  2033. // siteCertificate
  2034. //---------------------------------------------------------------------------
  2035. /*static*/
  2036. void CDeploymentEngine::siteCertificateFunction(StringBuffer &ret, const char *in, IXslTransform*)
  2037. {
  2038. //input is of the format <processType>+<process name>+<instance name>+<output path>
  2039. StringArray tokens;
  2040. DelimToStringArray(in, tokens, "+");
  2041. int len = tokens.length();
  2042. if (len < 4)
  2043. throw MakeStringException(0, "Invalid format for external function parameter!");
  2044. const char* processType = tokens.item(0);
  2045. const char* processName = tokens.item(1);
  2046. const char* instanceName= tokens.item(2);
  2047. const char* outputFile = tokens.item(3);
  2048. if (!processType || !*processType || !processName || !*processName ||
  2049. !instanceName || !*instanceName || !outputFile || !*outputFile)
  2050. {
  2051. throw MakeStringException(0, "Invalid parameters for siteCertificate method call!");
  2052. }
  2053. IPropertyTree* pProcess = s_xsltDepEngine->lookupProcess(processType, processName);
  2054. if (!pProcess)
  2055. throw MakeStringException(0, "%s with name %s is not defined!", processType, processName);
  2056. s_xsltDepEngine->siteCertificate( *pProcess, instanceName, outputFile );
  2057. }
  2058. //---------------------------------------------------------------------------
  2059. // processCustomMethod
  2060. //---------------------------------------------------------------------------
  2061. void CDeploymentEngine::processCustomMethod(const char* method, const char *source, const char *outputFile,
  2062. const char *instanceName, EnvMachineOS os)
  2063. {
  2064. //we only recognize ssl_certificate as the custom method so if any other method is sought
  2065. //then throw exception
  2066. StringBuffer dir;
  2067. const char* fileName = splitDirTail(source, dir);
  2068. if (0 != stricmp(method, "ssl_certificate"))
  2069. throw MakeStringException(0, "Process '%s': invalid method '%s' specified for file '%s'",
  2070. m_name.get(), method, fileName);
  2071. siteCertificate(m_process, instanceName, outputFile);
  2072. }
  2073. //---------------------------------------------------------------------------
  2074. // processCustomMethod
  2075. //---------------------------------------------------------------------------
  2076. void CDeploymentEngine::siteCertificate(IPropertyTree& process, const char *instanceName, const char *outputFile)
  2077. {
  2078. const char* pszCertFile;
  2079. const char* pszPrivFile;
  2080. StringBuffer sPrivKey;
  2081. StringBuffer sCertificate;
  2082. bool rc;
  2083. Owned<IDeployTask> task = createDeployTask( *m_pCallback, "Site Certificate", process.queryName(),
  2084. m_name.get(), m_curInstance, NULL, NULL, "", "", "", false);
  2085. m_pCallback->printStatus(task);
  2086. task->setProcessed();
  2087. while (true)
  2088. {
  2089. try
  2090. {
  2091. //generate SSL certificate and private key, if they have not already been generated
  2092. //and save them in the environment under the instance nodes. Note that if the
  2093. //environment is read-only then these are not written out and are lost after close.
  2094. //
  2095. //todo: mark env modified so it gets saved!
  2096. //
  2097. StringBuffer xpath;
  2098. xpath.appendf("Instance[@name='%s']", instanceName);
  2099. IPropertyTree* pInstanceNode = process.queryPropTree(xpath.str());
  2100. IPropertyTree* pHttps = process.queryPropTree("HTTPS");
  2101. if (!pHttps)
  2102. {
  2103. if (!strcmp(process.queryName(), "EspProcess"))
  2104. throw MakeStringExceptionDirect(-1, "Cannot generate SSL certificate.\nNo HTTPS information was specified.");
  2105. else
  2106. pHttps = &process;
  2107. }
  2108. pszCertFile = pHttps->queryProp("@certificateFileName");
  2109. pszPrivFile = pHttps->queryProp("@privateKeyFileName");
  2110. if (!pszCertFile || !*pszCertFile || !pszPrivFile || !*pszPrivFile)
  2111. throw MakeStringExceptionDirect(-1, "Cannot generate SSL certificate.\nName for certificate or private key file was not specified.");
  2112. IPropertyTree* pCertNode = pInstanceNode->queryPropTree("Certificate");
  2113. if (!pCertNode)
  2114. pCertNode = pInstanceNode->addPropTree("Certificate", createPTree());
  2115. else
  2116. sCertificate.append( pCertNode->queryProp(NULL) );
  2117. IPropertyTree* pPrivKeyNode = pInstanceNode->queryPropTree("PrivateKey" );
  2118. if (!pPrivKeyNode)
  2119. pPrivKeyNode = pInstanceNode->addPropTree("PrivateKey", createPTree());
  2120. else
  2121. sPrivKey.append( pPrivKeyNode->queryProp(NULL) );
  2122. IPropertyTree* pCsrNode = pInstanceNode->queryPropTree("CSR" );
  2123. StringBuffer sCSR;
  2124. if (!pCsrNode)
  2125. pCsrNode = pInstanceNode->addPropTree("CSR", createPTree());
  2126. else
  2127. sCSR.append( pCsrNode->queryProp(NULL) );
  2128. bool bRegenerateCSR = pHttps->getPropBool("@regenerateCredentials", false);
  2129. if (sCertificate.length()==0 || sPrivKey.length()==0 || sCSR.length()==0 || bRegenerateCSR)
  2130. {
  2131. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Generating SSL certificate and private key files ...");
  2132. const char* pszOrgUnit = pHttps->queryProp("@organizationalUnit");
  2133. const char* pszOrg = pHttps->queryProp("@organization");
  2134. const char* pszCity = pHttps->queryProp("@city");
  2135. const char* pszState = pHttps->queryProp("@state");
  2136. const char* pszCountry = pHttps->queryProp("@country");
  2137. const char* pszPassPhrase= pHttps->queryProp("@passphrase");
  2138. const char* pszFQDN = pInstanceNode->queryProp("@FQDN");
  2139. const char* pszIpAddress = pInstanceNode->queryProp("@netAddress");
  2140. int daysValid = pHttps->getPropInt("@daysValid", -1);
  2141. if (!pszOrgUnit || !*pszOrgUnit || !pszOrg || !*pszOrg)
  2142. throw MakeStringExceptionDirect(-1, "Cannot generate SSL certificate.\nOrganizational unit or organization was not specified.");
  2143. if (!pszCity || !*pszCity || !pszState || !*pszState || !pszCountry || !*pszCountry)
  2144. throw MakeStringExceptionDirect(-1, "Cannot generate SSL certificate.\nCity, state or country was not specified.");
  2145. if (!pszPassPhrase || !*pszPassPhrase)
  2146. throw MakeStringExceptionDirect(-1, "Cannot generate SSL certificate.\nPass phrase was not specified.");
  2147. if (daysValid < -1)
  2148. throw MakeStringExceptionDirect(-1, "Cannot generate SSL certificate.\nNumber of days the certificate needs to be valid was not specified.");
  2149. //call secure socket method to generate the certificate and private key into string buffers
  2150. //
  2151. Owned<ICertificate> cc = createCertificate();
  2152. cc->setCountry(pszCountry);
  2153. cc->setState (pszState);
  2154. cc->setCity (pszCity);
  2155. cc->setOrganization(pszOrg);
  2156. cc->setOrganizationalUnit(pszOrgUnit);
  2157. cc->setDestAddr( pszFQDN && *pszFQDN ? pszFQDN : pszIpAddress); //use FQDN if available, ip address otherwise
  2158. cc->setDays (daysValid);
  2159. StringBuffer pwbuf;
  2160. decrypt(pwbuf, pszPassPhrase);
  2161. cc->setPassphrase(pwbuf.str());
  2162. cc->generate(sCertificate.clear(), sPrivKey.clear());//throws exception!
  2163. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Generating Certificate Signing Request (CSR) ...");
  2164. cc->generateCSR(sPrivKey.str(), sCSR.clear());
  2165. if (bRegenerateCSR)
  2166. pHttps->setProp("@regenerateCredentials", "false");
  2167. //set these generated values in the environment so we don't have to regenerate them later
  2168. //
  2169. if (!m_environment.isConstEnvironment())
  2170. {
  2171. pCertNode->setProp(NULL, sCertificate.str());
  2172. pPrivKeyNode->setProp(NULL, sPrivKey.str());
  2173. pCsrNode->setProp(NULL, sCSR.str());
  2174. m_pCallback->setEnvironmentUpdated();
  2175. }
  2176. }
  2177. rc = true;
  2178. break;
  2179. }
  2180. catch (IException* e)
  2181. {
  2182. StringBuffer msg;
  2183. e->errorMessage(msg);
  2184. task->setErrorString(msg.str());
  2185. task->setErrorCode((DWORD)-1);
  2186. if (m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, e, NULL, NULL, task))//ignore ?
  2187. {
  2188. rc = false;
  2189. break;
  2190. }
  2191. task->setErrorCode(0);
  2192. task->setErrorString("");
  2193. }
  2194. catch (...)
  2195. {
  2196. task->setErrorString("Unknown exception!");
  2197. task->setErrorCode((DWORD)-1);
  2198. if (m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, NULL, NULL, NULL, task))//ignore ?
  2199. {
  2200. rc = false;
  2201. break;
  2202. }
  2203. task->setErrorCode(0);
  2204. task->setErrorString("");
  2205. }
  2206. }//while
  2207. m_pCallback->printStatus(task);
  2208. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  2209. //write the string buffers out to temp files
  2210. //
  2211. if (rc && (m_deployFlags & DEFLAGS_BUILDFILES))
  2212. {
  2213. //Handle certificate first ------------------------------
  2214. //
  2215. //create temp file path to save certificate
  2216. char tempfile[_MAX_PATH];
  2217. getTempPath(tempfile, sizeof(tempfile), m_name);
  2218. char* pTempFile = tempfile + strlen(tempfile);
  2219. strcpy(pTempFile, pszCertFile);
  2220. DeleteFile(tempfile);
  2221. //write certificate in this temp file
  2222. Owned<IFile> pFile = createIFile(tempfile);
  2223. IFileIO* pFileIO = pFile->open(IFOcreate);
  2224. pFileIO->write( 0, sCertificate.length(), sCertificate.str());
  2225. pFileIO->Release();
  2226. m_envDepEngine.addTempFile(tempfile);
  2227. //add this file copy operation to our todo list
  2228. Owned<CInstallFile> pInstallFile = new CInstallFile("copy", tempfile, pszCertFile);
  2229. s_dynamicFileList.push_back(StlLinkedFilePtr(pInstallFile.get()));
  2230. //Now handle private key ------------------------------
  2231. //create temp file path to save private key
  2232. strcpy(pTempFile, pszPrivFile);
  2233. DeleteFile(tempfile);
  2234. //write private key in this temp file
  2235. pFile.set( createIFile(tempfile) );
  2236. pFileIO = pFile->open(IFOcreate);
  2237. pFileIO->write( 0, sPrivKey.length(), sPrivKey.str());
  2238. pFileIO->Release();
  2239. m_envDepEngine.addTempFile(tempfile);
  2240. //add this file copy operation to our todo list
  2241. Owned<CInstallFile> pInstallFile2 = new CInstallFile("copy", tempfile, pszPrivFile);
  2242. s_dynamicFileList.push_back(StlLinkedFilePtr(pInstallFile2.get()));
  2243. }
  2244. }
  2245. bool CDeploymentEngine::fireException(IException *e)
  2246. {
  2247. StringBuffer msg;
  2248. e->errorMessage(msg);
  2249. // don't release e since that is done by our caller
  2250. //don't process abort exception since processException will throw another exception
  2251. if (strcmp(msg.str(), "Abort") != 0)
  2252. {
  2253. try
  2254. {
  2255. //BUG#48891: m_pCallback->processException() releases the exception, so bump the link count
  2256. //and let the JThread handleException release the exception
  2257. //Also note, if control comes here and we display the
  2258. //the abort, retry and ignore message box, there is no route to go back to the
  2259. //DeployTask and retry the operation. Therefore, any exception that come here
  2260. //need to be fixed to be caught and handled in the DeployTask
  2261. e->Link();
  2262. m_pCallback->processException(m_process.queryName(), m_name, m_curInstance, e);
  2263. }
  2264. catch (IException *e1)
  2265. {
  2266. // smeda: 29299
  2267. // do not rethrow exceptions from processException (as we are already in the
  2268. // middle of handling an exception). If rethrown, the jthread's parent.notifyStopped()
  2269. // is not called which in turn will keep the ThreadPool's
  2270. // joinwait() hanging forever for this thread to stop.
  2271. EXCLOG(e1,"CDeploymentEngine::fireException");
  2272. return false; // didn't handle the exception
  2273. }
  2274. }
  2275. return true; //handled the exception
  2276. }
  2277. bool CDeploymentEngine::checkSSHFileExists(const char* dir) const
  2278. {
  2279. bool flag = false;
  2280. StringBuffer destpath, destip, cmd, output, tmp, err;
  2281. stripNetAddr(dir, destpath, destip);
  2282. if (destip.length() && m_curSSHUser.length() && m_curSSHKeyFile.length())
  2283. {
  2284. tmp.appendf("%d", msTick());
  2285. cmd.clear().appendf("[ -e %s ] && echo %s", destpath.str(), tmp.str());
  2286. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Ensure Path", m_process.queryName(), m_name.get(),
  2287. m_curInstance, NULL, NULL, m_curSSHUser.sget(), m_curSSHKeyFile.sget(), m_curSSHKeyPassphrase.sget(), m_useSSHIfDefined);
  2288. task->execSSHCmd(destip.str(), cmd, output, err);
  2289. flag = strstr(output.str(), tmp.str()) == output.str();
  2290. checkAbort(task);
  2291. }
  2292. return flag;
  2293. }
  2294. void CDeploymentEngine::setSSHVars(IPropertyTree& instance)
  2295. {
  2296. EnvMachineOS os = m_envDepEngine.lookupMachineOS(instance);
  2297. m_curSSHUser.clear();
  2298. m_curSSHKeyFile.clear();
  2299. m_curSSHKeyPassphrase.clear();
  2300. m_envDepEngine.getSSHAccountInfo(instance.queryProp("@computer"), m_curSSHUser, m_curSSHKeyFile, m_curSSHKeyPassphrase);
  2301. m_useSSHIfDefined = !m_curSSHKeyFile.isEmpty() && !m_curSSHUser.isEmpty() && os == MachineOsLinux;
  2302. const char* depToFolder = m_envDepEngine.getDeployToFolder();
  2303. if (depToFolder && *depToFolder)
  2304. m_useSSHIfDefined = false;
  2305. }
  2306. void CDeploymentEngine::clearSSHVars()
  2307. {
  2308. m_curSSHUser.clear();
  2309. m_curSSHKeyFile.clear();
  2310. m_curSSHKeyPassphrase.clear();
  2311. }