DeploymentEngine.cpp 102 KB

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