DeploymentEngine.cpp 102 KB

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