deploy.cpp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "deploy.hpp"
  15. #include "environment.hpp"
  16. #include "jptree.hpp"
  17. #include "jexcept.hpp"
  18. #include "jencrypt.hpp"
  19. #include "xslprocessor.hpp"
  20. #include "DeploymentEngine.hpp"
  21. #include "ThorDeploymentEngine.hpp"
  22. #include "EspDeploymentEngine.hpp"
  23. #include "dalideploymentengine.hpp"
  24. #include "RoxieDeploymentEngine.hpp"
  25. #include "configgenengine.hpp"
  26. #include "espconfiggenengine.hpp"
  27. #include "thorconfiggenengine.hpp"
  28. //---------------------------------------------------------------------------
  29. // CEnvironmentDeploymentEngine
  30. //---------------------------------------------------------------------------
  31. class CEnvironmentDeploymentEngine : public CInterface, implements IEnvDeploymentEngine
  32. {
  33. public:
  34. IMPLEMENT_IINTERFACE;
  35. //---------------------------------------------------------------------------
  36. // CEnvironmentDeploymentEngine
  37. //---------------------------------------------------------------------------
  38. CEnvironmentDeploymentEngine(IConstEnvironment &environment, IDeploymentCallback& callback,
  39. IPropertyTree* pSelectedComponents)
  40. : m_environment(environment),
  41. m_transform(NULL),
  42. m_abort(false),
  43. m_espModuleCount(0),
  44. m_tempFileCount(0),
  45. m_bLinuxDeployment(false),
  46. m_bInteractiveMode(true)
  47. {
  48. m_pCallback.set(&callback);
  49. if (pSelectedComponents)
  50. {
  51. initXML(pSelectedComponents);
  52. Owned<IPropertyTreeIterator> it = pSelectedComponents->getElements("*");
  53. ForEach(*it)
  54. {
  55. IPropertyTree* pComponent = &it->query();
  56. IDeploymentEngine* pEngine = addProcess(pComponent->queryName(), pComponent->queryProp("@name"));
  57. Owned<IPropertyTreeIterator> iter = pComponent->getElements("*");
  58. if (iter->first())
  59. {
  60. pEngine->resetInstances();
  61. ForEach(*iter)
  62. {
  63. IPropertyTree* pChild = &iter->query();
  64. const char* tagName = pChild->queryName();
  65. const char* instName = pChild->queryProp("@name");
  66. pEngine->addInstance(tagName, instName);
  67. //determine if this is linux deployment
  68. if (!m_bLinuxDeployment)
  69. {
  70. const char* computer = pChild->queryProp("@computer");
  71. IConstMachineInfo* pMachine = environment.getMachine(computer);
  72. if (pMachine->getOS() == MachineOsLinux)
  73. m_bLinuxDeployment = true;
  74. }
  75. }
  76. }
  77. else if (!m_bLinuxDeployment)//another previously added engine already does not have linux instance
  78. {
  79. //some components like thor and hole clusters don't show their instances in the
  80. //deployment wizard so detect if they have any linux instance.
  81. const IArrayOf<IPropertyTree>& instances = pEngine->getInstances();
  82. if (instances.ordinality() > 0)
  83. {
  84. Owned<IConstMachineInfo> machine = m_environment.getMachine(instances.item(0).queryProp("@computer"));
  85. if (machine && machine->getOS() == MachineOsLinux)
  86. m_bLinuxDeployment = true;
  87. }
  88. }
  89. }
  90. }
  91. #ifdef _WINDOWS
  92. EnumerateNetworkConnections();
  93. #endif
  94. }
  95. //---------------------------------------------------------------------------
  96. // ~CEnvironmentDeploymentEngine
  97. //---------------------------------------------------------------------------
  98. virtual ~CEnvironmentDeploymentEngine()
  99. {
  100. //delete all temporary files generated during deployemnt
  101. int count = m_tempFiles.length();
  102. int i;
  103. for (i = 0; i < count; i++)
  104. DeleteFile(m_tempFiles.item(i));
  105. count = m_tempDirs.length();
  106. for (i = 0; i < count; i++)
  107. deleteRecursive(m_tempDirs.item(i));
  108. m_processes.kill(); // must do this before destroying deplyCallback and deployLog
  109. m_pDeployLog.clear(); // this causes the log file to be written
  110. m_pCallback.clear();
  111. termXML();
  112. }
  113. void deleteRecursive(const char* path)
  114. {
  115. Owned<IFile> pDir = createIFile(path);
  116. if (pDir->exists())
  117. {
  118. if (pDir->isDirectory())
  119. {
  120. Owned<IDirectoryIterator> it = pDir->directoryFiles(NULL, false, true);
  121. ForEach(*it)
  122. {
  123. StringBuffer name;
  124. it->getName(name);
  125. StringBuffer childPath(path);
  126. childPath.append(PATHSEPCHAR);
  127. childPath.append(name);
  128. deleteRecursive(childPath.str());
  129. }
  130. }
  131. pDir->remove();
  132. }
  133. }
  134. //---------------------------------------------------------------------------
  135. // addProcess
  136. //---------------------------------------------------------------------------
  137. IDeploymentEngine* addProcess(const char* processType, const char* processName)
  138. {
  139. assertex(processType);
  140. assertex(processName);
  141. StringBuffer xpath;
  142. xpath.appendf("Software/%s[@name='%s']", processType, processName);
  143. Owned<IPropertyTree> tree = &m_environment.getPTree();
  144. IPropertyTree* pComponent = tree->queryPropTree(xpath.str());
  145. if (!pComponent)
  146. throw MakeStringException(0, "%s with name %s was not found!", processType, processName);
  147. IDeploymentEngine* deployEngine;
  148. if (strcmp(processType, "DaliServerProcess")==0)
  149. deployEngine = new CDaliDeploymentEngine(*this, *m_pCallback, *pComponent);
  150. else if (strcmp(processType, "ThorCluster")==0)
  151. deployEngine = new CThorDeploymentEngine(*this, *m_pCallback, *pComponent);
  152. else if (strcmp(processType, "RoxieCluster")==0)
  153. deployEngine = new CRoxieDeploymentEngine(*this, *m_pCallback, *pComponent);
  154. else if (strcmp(processType, "EspProcess")==0)
  155. deployEngine = new CEspDeploymentEngine(*this, *m_pCallback, *pComponent);
  156. else
  157. deployEngine = new CDeploymentEngine(*this, *m_pCallback, *pComponent, "Instance", true);
  158. assertex(deployEngine);
  159. deployEngine->setXsl(m_processor, m_transform);
  160. m_processes.append(*deployEngine); // array releases members when destroyed
  161. return deployEngine;
  162. }
  163. //---------------------------------------------------------------------------
  164. // setSshAccount
  165. //---------------------------------------------------------------------------
  166. void setSshAccount(const char* userid, const char* password)
  167. {
  168. m_sSshUserid = userid;
  169. m_sSshPassword = password;
  170. }
  171. //---------------------------------------------------------------------------
  172. // isLinuxDeployment
  173. //---------------------------------------------------------------------------
  174. bool isLinuxDeployment() const
  175. {
  176. return m_bLinuxDeployment;
  177. }
  178. //---------------------------------------------------------------------------
  179. // start
  180. //---------------------------------------------------------------------------
  181. void start()
  182. {
  183. ForEachItemIn(idx, m_processes)
  184. {
  185. m_processes.item(idx).start();
  186. }
  187. }
  188. //---------------------------------------------------------------------------
  189. // stop
  190. //---------------------------------------------------------------------------
  191. void stop()
  192. {
  193. ForEachItemInRev(idx, m_processes)
  194. {
  195. m_processes.item(idx).stop();
  196. }
  197. }
  198. //---------------------------------------------------------------------------
  199. // stripXsltMessage
  200. //---------------------------------------------------------------------------
  201. void stripXsltMessage(StringBuffer& msg)
  202. {
  203. //for better readability of msg, remove redundant prefix of "[XSLT warning: ", if present
  204. const char* pattern = "[ElemMessageTerminateException: ";
  205. const int len =sizeof("[ElemMessageTerminateException: ")-1;
  206. if (!strncmp(msg, pattern, len))
  207. msg.remove(0, len);
  208. //remove the excessive info about XSLT context when this was thrown
  209. const char* begin = msg.str();
  210. const char* end = strstr(begin, "(file:");
  211. if (end)
  212. msg.setLength(end-begin);
  213. }
  214. //---------------------------------------------------------------------------
  215. // check
  216. //---------------------------------------------------------------------------
  217. void check()
  218. {
  219. if (m_pCallback->getAbortStatus())
  220. throw MakeStringException(0, "User abort");
  221. bool valid = false;
  222. m_nValidationErrors = 0;
  223. StringBuffer outputXml;
  224. Owned<IXslFunction> externalFunction;
  225. externalFunction.setown(m_transform->createExternalFunction("validationMessage", validationMessageFromXSLT));
  226. m_transform->setExternalFunction(SEISINT_NAMESPACE, externalFunction.get(), true);
  227. m_transform->setXslSource("validateAll.xsl");
  228. m_transform->setUserData(this);
  229. try
  230. {
  231. m_transform->transform( outputXml );
  232. m_transform->closeResultTarget();
  233. const char* msg = m_transform->getMessages();
  234. if (msg && *msg)
  235. {
  236. /*
  237. //there may be multiple warnings messages bundled here so process each of them:
  238. StringArray msgs;
  239. DelimToStringArray(msg, msgs, "\n");
  240. ForEachItemIn(idx, msgs)
  241. {
  242. msg = msgs.item(idx);
  243. if (msg && *msg)
  244. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, msg);
  245. }
  246. */
  247. m_sValidationErrors.append(msg);
  248. m_nValidationErrors++;
  249. }
  250. if (!m_nValidationErrors)//this may get filled in by the external function
  251. valid = true;
  252. }
  253. catch (IException* e)
  254. {
  255. StringBuffer msg;
  256. e->errorMessage(msg);
  257. e->Release();
  258. stripXsltMessage(msg);
  259. m_sValidationErrors.append(msg);
  260. }
  261. catch (...)
  262. {
  263. m_sValidationErrors.appendf("Validation failed: Unspecified XSL error!");
  264. }
  265. m_transform->setExternalFunction(SEISINT_NAMESPACE, externalFunction.get(), false);
  266. m_transform->setUserData(NULL);
  267. if (!valid)
  268. {
  269. const char* errors = m_sValidationErrors.str();
  270. const char* caption = "Preliminary validation failed!";
  271. if (!errors || !*errors)
  272. errors = "Continue?";
  273. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, caption);
  274. while ( !m_pCallback->processException(NULL, NULL, NULL, NULL, errors, caption, NULL) )
  275. ;
  276. valid = true; //ignore validation errors
  277. }
  278. if (valid)
  279. {
  280. ForEachItemIn(idx, m_processes)
  281. m_processes.item(idx).check();
  282. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  283. }
  284. }
  285. //---------------------------------------------------------------------------
  286. // compare
  287. //---------------------------------------------------------------------------
  288. void compare(unsigned mode)
  289. {
  290. ForEachItemIn(idx, m_processes)
  291. {
  292. m_processes.item(idx).compare(mode);
  293. }
  294. }
  295. //---------------------------------------------------------------------------
  296. // deploy
  297. //---------------------------------------------------------------------------
  298. void deploy(unsigned flags, bool useTempDir)
  299. {
  300. m_tempFileCount = m_espModuleCount = 0;
  301. if (flags != DEFLAGS_NONE)
  302. {
  303. ForEachItemIn(idx, m_processes)
  304. {
  305. m_processes.item(idx).deploy(flags, useTempDir);
  306. }
  307. }
  308. }
  309. //---------------------------------------------------------------------------
  310. // deploy
  311. //---------------------------------------------------------------------------
  312. void deploy(unsigned flags, BackupMode backupMode, bool bStop, bool bStart)
  313. {
  314. switch (backupMode)
  315. {
  316. case DEBACKUP_NONE:
  317. if (bStop)
  318. stop();
  319. deploy(flags, false);
  320. if (bStart)
  321. start();
  322. break;
  323. case DEBACKUP_COPY:
  324. backupDirs();
  325. if (bStop)
  326. stop();
  327. deploy(flags, false);
  328. if (bStart)
  329. start();
  330. break;
  331. case DEBACKUP_RENAME:
  332. deploy(flags, true);
  333. if (bStop)
  334. stop();
  335. renameDirs();
  336. if (bStart)
  337. start();
  338. break;
  339. default:
  340. assertex(false);
  341. }
  342. }
  343. //---------------------------------------------------------------------------
  344. // renameDirs
  345. //---------------------------------------------------------------------------
  346. void renameDirs()
  347. {
  348. ForEachItemIn(idx, m_processes)
  349. {
  350. m_processes.item(idx).renameDirs();
  351. }
  352. }
  353. //---------------------------------------------------------------------------
  354. // backupDirs
  355. //---------------------------------------------------------------------------
  356. void backupDirs()
  357. {
  358. ForEachItemIn(idx, m_processes)
  359. {
  360. m_processes.item(idx).backupDirs();
  361. }
  362. }
  363. //---------------------------------------------------------------------------
  364. // abort
  365. //---------------------------------------------------------------------------
  366. void abort()
  367. {
  368. m_abort = true;
  369. ForEachItemIn(idx, m_processes)
  370. {
  371. m_processes.item(idx).abort();
  372. }
  373. }
  374. //---------------------------------------------------------------------------
  375. // archive
  376. //---------------------------------------------------------------------------
  377. void archive(const char* filename)
  378. {
  379. if (!filename || !*filename) return;
  380. if (m_abort)
  381. {
  382. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Aborted!");
  383. throw MakeStringException(0, "User abort");
  384. }
  385. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Archiving environment data to %s...", filename);
  386. Owned<IPropertyTree> tree = &m_environment.getPTree();
  387. StringBuffer xml;
  388. toXML(tree, xml);
  389. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Archive File", NULL, NULL, NULL, NULL,
  390. filename, "", "", "", false);
  391. task->createFile(xml.str());
  392. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
  393. if (task->getAbort())
  394. throw MakeStringException(0, "User abort");
  395. Owned<IFile> pFile = createIFile(filename);
  396. pFile->setReadOnly(true);
  397. }
  398. //---------------------------------------------------------------------------
  399. // setLog
  400. //---------------------------------------------------------------------------
  401. void setLog(const char* filename, const char* envname)
  402. {
  403. if (!filename || !*filename) return;
  404. m_pDeployLog.setown(createDeployLog(*m_pCallback, filename, envname));
  405. }
  406. //---------------------------------------------------------------------------
  407. // initXML
  408. //---------------------------------------------------------------------------
  409. void initXML(IPropertyTree* pSelectedComponents)
  410. {
  411. if (m_abort)
  412. throw MakeStringException(0, "User abort");
  413. m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Loading environment...");
  414. m_processor.setown(getXslProcessor());
  415. m_transform.setown(m_processor->createXslTransform());
  416. //decrypt external function is no longer used by any xslt
  417. //
  418. //m_externalFunction.setown(m_transform->createExternalFunction("decrypt", decrypt));
  419. //m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction.get(), true);
  420. Owned<IPropertyTree> tree = &m_environment.getPTree();
  421. IPropertyTree* pDeploy = tree->queryPropTree("DeployComponents");
  422. if (pDeploy)
  423. tree->removeTree(pDeploy);
  424. pDeploy = tree->addPropTree("DeployComponents", createPTreeFromIPT(pSelectedComponents));
  425. StringBuffer xml;
  426. toXML(tree, xml);
  427. tree->removeTree(pDeploy);
  428. if (m_transform->setXmlSource(xml.str(), xml.length()) != 0)
  429. throw MakeStringException(0, "Invalid environment XML string");
  430. }
  431. //---------------------------------------------------------------------------
  432. // termXML
  433. //---------------------------------------------------------------------------
  434. void termXML()
  435. {
  436. //decrypt external function is no longer used by any xslt
  437. //
  438. //m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction.get(), false);
  439. m_externalFunction.clear();
  440. m_transform.clear();
  441. m_processor.clear();
  442. }
  443. //---------------------------------------------------------------------------
  444. // incrementTempFileCount
  445. //---------------------------------------------------------------------------
  446. virtual int incrementTempFileCount()
  447. {
  448. return ++m_tempFileCount;
  449. }
  450. //---------------------------------------------------------------------------
  451. // incrementEspModuleCount
  452. //---------------------------------------------------------------------------
  453. virtual int incrementEspModuleCount()
  454. {
  455. return ++m_espModuleCount;
  456. }
  457. //---------------------------------------------------------------------------
  458. // validationMessageFromXSLT
  459. //---------------------------------------------------------------------------
  460. static void validationMessageFromXSLT(StringBuffer &ret, const char *in, IXslTransform* pTransform)
  461. {
  462. CEnvironmentDeploymentEngine* pEnvDepEngine = (CEnvironmentDeploymentEngine*) pTransform->getUserData();
  463. IDeploymentCallback* pCallback = pEnvDepEngine->m_pCallback;
  464. //input 'in' has format of the form [type]:[compType]:[compName]:[message]
  465. //type is either 'error' or 'warning' and any of the other parts may be empty strings
  466. //
  467. StringArray sArray;
  468. DelimToStringArray(in, sArray, ":");
  469. if (sArray.ordinality() != 4)
  470. {
  471. pCallback->printStatus(STATUS_ERROR, NULL, NULL, NULL, in);
  472. return;
  473. }
  474. const char* msgType = sArray.item(0);
  475. const char* compType = sArray.item(1);
  476. const char* compName = sArray.item(2);
  477. const char* msg = sArray.item(3);
  478. if (compType && !*compType)
  479. compType = NULL;
  480. if (compName && !*compName)
  481. compName = NULL;
  482. StatusType statusType;
  483. if (!stricmp(msgType, "error"))
  484. {
  485. statusType = STATUS_ERROR;
  486. pEnvDepEngine->m_nValidationErrors++;
  487. if (!compType || !compName)//if this error is not being reported under a particular component in tree
  488. pEnvDepEngine->m_sValidationErrors.append(msg);
  489. }
  490. else if (!strnicmp(msgType, "warn", 4))
  491. statusType = STATUS_WARN;
  492. else if (!stricmp(msgType, "OK"))
  493. statusType = STATUS_OK;
  494. else if (!strnicmp(msgType, "inc", 3))
  495. statusType = STATUS_INCOMPLETE;
  496. else statusType = !stricmp(msgType, "normal") ? STATUS_NORMAL : STATUS_ERROR;
  497. try
  498. {
  499. if (pCallback)
  500. pCallback->printStatus( statusType,
  501. compType,
  502. compName,
  503. NULL,
  504. msg);
  505. }
  506. catch (IException* e)
  507. {
  508. StringBuffer buf;
  509. e->errorMessage(buf);
  510. e->Release();
  511. pCallback->printStatus(STATUS_ERROR, NULL, NULL, NULL, buf.str());
  512. }
  513. catch(...)
  514. {
  515. pCallback->printStatus(STATUS_ERROR, NULL, NULL, NULL, "Unknown exception!");
  516. }
  517. }
  518. //---------------------------------------------------------------------------
  519. // getCallback
  520. //---------------------------------------------------------------------------
  521. virtual IDeploymentCallback& getCallback() const
  522. {
  523. return *m_pCallback;
  524. }
  525. //---------------------------------------------------------------------------
  526. // setInteractiveMode
  527. //---------------------------------------------------------------------------
  528. virtual void setInteractiveMode(bool bSet)
  529. {
  530. m_bInteractiveMode = bSet;
  531. }
  532. //---------------------------------------------------------------------------
  533. // getEnvironment
  534. //---------------------------------------------------------------------------
  535. IConstEnvironment& getEnvironment() const
  536. {
  537. return m_environment;
  538. }
  539. //---------------------------------------------------------------------------
  540. // getInteractiveMode
  541. //---------------------------------------------------------------------------
  542. virtual bool getInteractiveMode() const
  543. {
  544. return m_bInteractiveMode;
  545. }
  546. //---------------------------------------------------------------------------
  547. // getDeployLog
  548. //---------------------------------------------------------------------------
  549. virtual IDeployLog* getDeployLog()
  550. {
  551. return m_pDeployLog ? m_pDeployLog.getLink() : NULL;
  552. }
  553. //---------------------------------------------------------------------------
  554. // addTempFile
  555. //---------------------------------------------------------------------------
  556. virtual void addTempFile(const char* filePath)
  557. {
  558. if (filePath)
  559. m_tempFiles.append(filePath);
  560. }
  561. //---------------------------------------------------------------------------
  562. // addTempDirectory
  563. //---------------------------------------------------------------------------
  564. virtual void addTempDirectory(const char* dirPath)
  565. {
  566. if (dirPath)
  567. m_tempDirs.append(dirPath);
  568. }
  569. //---------------------------------------------------------------------------
  570. // setDeployToFolder
  571. //---------------------------------------------------------------------------
  572. virtual void setDeployToFolder(const char* path)
  573. {
  574. StringBuffer machineName;
  575. StringBuffer localPath;
  576. StringBuffer tail;
  577. StringBuffer ext;
  578. if (splitUNCFilename(path, &machineName, &localPath, &tail, &ext))
  579. {
  580. const char* hostName = machineName.str() + 2;
  581. Owned<IConstMachineInfo> machine = m_environment.getMachine(hostName);
  582. if (!machine)
  583. throw MakeStringException(-1, "The computer '%s' used for deployment folder is undefined!", hostName);
  584. StringAttr netAddress;
  585. StringAttrAdaptor adaptor(netAddress);
  586. machine->getNetAddress(adaptor);
  587. if (!netAddress.get() || !*netAddress.get())
  588. throw MakeStringException(-1,
  589. "The computer '%s' used for deployment folder does not have any network address defined!", hostName);
  590. StringBuffer uncPath(PATHSEPSTR PATHSEPSTR);
  591. uncPath.append( netAddress.get() );
  592. if (*localPath.str() != PATHSEPCHAR)
  593. uncPath.append( PATHSEPCHAR );
  594. uncPath.append( localPath );//note that the path ends with PATHSEPCHAR
  595. uncPath.append( tail );
  596. uncPath.append( ext );
  597. m_sDeployToFolder.set( uncPath.str() );
  598. getAccountInfo(hostName, m_sDeployToUser, m_sDeployToPswd);
  599. }
  600. else
  601. m_sDeployToFolder.set( path );
  602. /*
  603. Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Create Directory", NULL, NULL, NULL, NULL, m_sDeployToFolder.get());
  604. m_pCallback->printStatus(task);
  605. task->createDirectory();
  606. m_pCallback->printStatus(task);
  607. */
  608. }
  609. //---------------------------------------------------------------------------
  610. // getDeployToFolder
  611. //---------------------------------------------------------------------------
  612. virtual const char* getDeployToFolder() const
  613. {
  614. return m_sDeployToFolder.get();
  615. }
  616. //---------------------------------------------------------------------------
  617. // getDeployToAccountInfo
  618. //---------------------------------------------------------------------------
  619. virtual void getDeployToAccountInfo(const char*& user, const char*& pswd) const
  620. {
  621. user = m_sDeployToUser.get();
  622. pswd = m_sDeployToPswd.get();
  623. }
  624. //---------------------------------------------------------------------------
  625. // lookupNetAddress
  626. //---------------------------------------------------------------------------
  627. StringAttr& lookupNetAddress(StringAttr& str, const char* computer) const
  628. {
  629. Owned<IConstMachineInfo> machine = m_environment.getMachine(computer);
  630. if (machine)
  631. {
  632. StringAttrAdaptor adaptor(str);
  633. machine->getNetAddress(adaptor);
  634. }
  635. return str;
  636. }
  637. //---------------------------------------------------------------------------
  638. // lookupMachineOS
  639. //---------------------------------------------------------------------------
  640. EnvMachineOS lookupMachineOS(IPropertyTree& node) const
  641. {
  642. Owned<IConstMachineInfo> machine = m_environment.getMachine(node.queryProp("@computer"));
  643. return machine ? machine->getOS() : MachineOsUnknown;
  644. }
  645. //---------------------------------------------------------------------------
  646. // getAccountInfo
  647. //---------------------------------------------------------------------------
  648. void getAccountInfo(const char* computer, StringAttr& user, StringAttr& pwd) const
  649. {
  650. Owned<IConstMachineInfo> machine = m_environment.getMachine(computer);
  651. if (machine)
  652. {
  653. Owned<IConstDomainInfo> domain = machine->getDomain();
  654. if (!domain)
  655. throw MakeStringException(-1, "The computer '%s' does not have any domain information!", computer);
  656. StringBuffer x;
  657. if (machine->getOS() == MachineOsW2K)
  658. {
  659. domain->getName(StringBufferAdaptor(x));
  660. if (x.length())
  661. x.append(PATHSEPCHAR);
  662. }
  663. domain->getAccountInfo(StringBufferAdaptor(x), StringAttrAdaptor(pwd));
  664. user.set(x.str());
  665. }
  666. else
  667. throw MakeStringException(-1, "The computer '%s' is undefined!", computer);
  668. }
  669. //---------------------------------------------------------------------------
  670. // getSSHAccountInfo
  671. //---------------------------------------------------------------------------
  672. void getSSHAccountInfo(const char* computer, StringAttr& user, StringAttr& sshKeyFile, StringAttr& sshKeyPassphrase) const
  673. {
  674. Owned<IConstMachineInfo> machine = m_environment.getMachine(computer);
  675. if (machine)
  676. {
  677. Owned<IConstDomainInfo> domain = machine->getDomain();
  678. if (!domain)
  679. throw MakeStringException(-1, "The computer '%s' does not have any domain information!", computer);
  680. StringBuffer x;
  681. if (machine->getOS() == MachineOsW2K)
  682. {
  683. domain->getName(StringBufferAdaptor(x));
  684. if (x.length())
  685. x.append(PATHSEPCHAR);
  686. }
  687. domain->getSSHAccountInfo(StringBufferAdaptor(x), StringAttrAdaptor(sshKeyFile), StringAttrAdaptor(sshKeyPassphrase));
  688. user.set(x.str());
  689. }
  690. else
  691. throw MakeStringException(-1, "The computer '%s' is undefined!", computer);
  692. }
  693. virtual void setSourceDaliAddress( const char* addr )
  694. {
  695. m_sSrcDaliAddress.set( addr );
  696. }
  697. virtual const char* getSourceDaliAddress()
  698. {
  699. return m_sSrcDaliAddress.get();
  700. }
  701. virtual IArrayOf<IDeploymentEngine>& queryProcesses() { return m_processes; }
  702. #ifdef _WINDOWS
  703. void NetErrorHandler(DWORD dwResult)
  704. {
  705. StringBuffer out;
  706. formatSystemError(out, dwResult);
  707. out.insert(0, "Failed to enumerate existing network connections:\n");
  708. while ( !m_pCallback->processException(NULL, NULL, NULL, NULL, out, "Network Error", NULL) )
  709. ;
  710. }
  711. bool EnumerateNetworkConnections()
  712. {
  713. DWORD dwResult;
  714. HANDLE hEnum;
  715. DWORD cbBuffer = 16384; // 16K is a good size
  716. DWORD cEntries = -1; // enumerate all possible entries
  717. LPNETRESOURCE lpnr; // pointer to enumerated structures
  718. //
  719. // Call the WNetOpenEnum function to begin the enumeration.
  720. //
  721. dwResult = WNetOpenEnum(RESOURCE_CONNECTED, // connected network resources
  722. RESOURCETYPE_ANY,// all resources
  723. 0, // enumerate all resources
  724. NULL, // NULL first time the function is called
  725. &hEnum); // handle to the resource
  726. if (dwResult != NO_ERROR)
  727. {
  728. NetErrorHandler(dwResult);
  729. return false;
  730. }
  731. //
  732. // Call the GlobalAlloc function to allocate resources.
  733. //
  734. lpnr = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);
  735. do
  736. {
  737. ZeroMemory(lpnr, cbBuffer);
  738. // Call the WNetEnumResource function to continue
  739. // the enumeration.
  740. //
  741. dwResult = WNetEnumResource( hEnum, // resource handle
  742. &cEntries, // defined locally as -1
  743. lpnr, // LPNETRESOURCE
  744. &cbBuffer); // buffer size
  745. // If the call succeeds, loop through the structures.
  746. //
  747. if (dwResult == NO_ERROR)
  748. {
  749. StringBuffer networkPath;
  750. for (DWORD i = 0; i < cEntries; i++)
  751. if (lpnr[i].lpRemoteName)
  752. {
  753. // make a valid UNC path to connect to and see if we are not already connected
  754. if (CDeploymentEngine::stripTrailingDirsFromUNCPath(lpnr[i].lpRemoteName, networkPath.clear()) &&
  755. m_persistentConnections.find( networkPath.str() ) == m_persistentConnections.end())
  756. {
  757. //::MessageBox(NULL, networkPath.str(), lpnr[i].lpRemoteName, MB_OK);
  758. m_persistentConnections.insert( networkPath.str() );
  759. }
  760. }
  761. }
  762. else if (dwResult != ERROR_NO_MORE_ITEMS)
  763. {
  764. NetErrorHandler(dwResult);
  765. break;
  766. }
  767. }
  768. while (dwResult != ERROR_NO_MORE_ITEMS);
  769. GlobalFree((HGLOBAL)lpnr); // free the memory
  770. dwResult = WNetCloseEnum(hEnum); // end the enumeration
  771. if (dwResult != NO_ERROR)
  772. {
  773. NetErrorHandler(dwResult);
  774. return false;
  775. }
  776. return true;
  777. }
  778. #endif//WINDOWS
  779. virtual bool IsPersistentConnection(const char* networkPath) const
  780. {
  781. return m_persistentConnections.find( networkPath ) != m_persistentConnections.end();
  782. }
  783. protected:
  784. IArrayOf<IDeploymentEngine> m_processes;
  785. IConstEnvironment& m_environment;
  786. Owned<IDeploymentCallback> m_pCallback;
  787. Owned<IXslProcessor> m_processor;
  788. Owned<IXslTransform> m_transform;
  789. Owned<IXslFunction> m_externalFunction;
  790. Owned<IDeployLog> m_pDeployLog;
  791. set<string> m_persistentConnections;
  792. int m_espModuleCount;
  793. int m_tempFileCount;
  794. bool m_abort;
  795. bool m_bLinuxDeployment;
  796. bool m_bInteractiveMode;
  797. StringBuffer m_sSshUserid;
  798. StringBuffer m_sSshPassword;
  799. unsigned int m_nValidationErrors;
  800. StringBuffer m_sValidationErrors;
  801. StringArray m_tempFiles;
  802. StringArray m_tempDirs;
  803. StringAttr m_sDeployToFolder;
  804. StringAttr m_sDeployToUser;
  805. StringAttr m_sDeployToPswd;
  806. StringAttr m_sSrcDaliAddress;
  807. };
  808. class CConfigGenMgr : public CEnvironmentDeploymentEngine
  809. {
  810. public:
  811. IMPLEMENT_IINTERFACE;
  812. //---------------------------------------------------------------------------
  813. // CConfigGenMgr
  814. //---------------------------------------------------------------------------
  815. CConfigGenMgr(IConstEnvironment& environment, IDeploymentCallback& callback,
  816. IPropertyTree* pSelectedComponents, const char* inputDir, const char* outputDir, const char* compName, const char* compType, const char* ipAddr)
  817. : CEnvironmentDeploymentEngine(environment, callback, pSelectedComponents),
  818. m_inDir(inputDir),
  819. m_outDir(outputDir),
  820. m_compName(compName),
  821. m_compType(compType),
  822. m_hostIpAddr(ipAddr)
  823. {
  824. Owned<IPropertyTree> pSelComps;
  825. if (!pSelectedComponents)
  826. {
  827. Owned<IPropertyTree> pEnvTree = &m_environment.getPTree();
  828. pSelComps.setown(getInstances(pEnvTree, compName, compType, ipAddr));
  829. pSelectedComponents = pSelComps;
  830. }
  831. initXML(pSelectedComponents);
  832. {
  833. Owned<IPropertyTreeIterator> it = pSelectedComponents->getElements("*");
  834. ForEach(*it)
  835. {
  836. IPropertyTree* pComponent = &it->query();
  837. IDeploymentEngine* pEngine = addProcess(pComponent->queryName(), pComponent->queryProp("@name"));
  838. Owned<IPropertyTreeIterator> iter = pComponent->getElements("*");
  839. if (iter->first())
  840. {
  841. pEngine->resetInstances();
  842. ForEach(*iter)
  843. {
  844. IPropertyTree* pChild = &iter->query();
  845. const char* tagName = pChild->queryName();
  846. const char* instName = pChild->queryProp("@name");
  847. pEngine->addInstance(tagName, instName);
  848. //determine if this is linux deployment
  849. if (!m_bLinuxDeployment)
  850. {
  851. const char* computer = pChild->queryProp("@computer");
  852. Owned<IConstMachineInfo> pMachine = environment.getMachine(computer);
  853. if (!pMachine)
  854. throw MakeStringException(0, "Invalid Environment file. Instance '%s' of '%s' references a computer '%s' that has not been defined!", pChild->queryProp("@name"), pComponent->queryProp("@name"), computer);
  855. else if (pMachine->getOS() == MachineOsLinux)
  856. m_bLinuxDeployment = true;
  857. }
  858. }
  859. }
  860. else if (!m_bLinuxDeployment)//another previously added engine already does not have linux instance
  861. {
  862. //some components like thor and hole clusters don't show their instances in the
  863. //deployment wizard so detect if they have any linux instance.
  864. const IArrayOf<IPropertyTree>& instances = pEngine->getInstances();
  865. if (instances.ordinality() > 0)
  866. {
  867. Owned<IConstMachineInfo> machine;// = m_environment.getMachine(instances.item(0).queryProp("@computer"));
  868. if (machine && machine->getOS() == MachineOsLinux)
  869. m_bLinuxDeployment = true;
  870. }
  871. }
  872. }
  873. }
  874. }
  875. //---------------------------------------------------------------------------
  876. // addProcess
  877. //---------------------------------------------------------------------------
  878. IDeploymentEngine* addProcess(const char* processType, const char* processName)
  879. {
  880. assertex(processType);
  881. assertex(processName);
  882. StringBuffer xpath;
  883. xpath.appendf("Software/%s[@name='%s']", processType, processName);
  884. Owned<IPropertyTree> tree = &m_environment.getPTree();
  885. IPropertyTree* pComponent = tree->queryPropTree(xpath.str());
  886. if (!pComponent)
  887. throw MakeStringException(0, "%s with name %s was not found!", processType, processName);
  888. IDeploymentEngine* deployEngine;
  889. if (strcmp(processType, "RoxieCluster")==0)
  890. deployEngine = new CConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir, "*");
  891. else if (strcmp(processType, "ThorCluster")==0)
  892. deployEngine = new CThorConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir);
  893. else if (strcmp(processType, "EspProcess")==0)
  894. deployEngine = new CEspConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir);
  895. else
  896. deployEngine = new CConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir, "Instance", true);
  897. assertex(deployEngine);
  898. deployEngine->setXsl(m_processor, m_transform);
  899. m_processes.append(*deployEngine); // array releases members when destroyed
  900. return deployEngine;
  901. }
  902. private:
  903. StringBuffer m_inDir;
  904. StringBuffer m_outDir;
  905. StringBuffer m_compName;
  906. StringBuffer m_compType;
  907. StringBuffer m_hostIpAddr;
  908. };
  909. //---------------------------------------------------------------------------
  910. // Factory functions
  911. //---------------------------------------------------------------------------
  912. IEnvDeploymentEngine* createEnvDeploymentEngine(IConstEnvironment& environment,
  913. IDeploymentCallback& callback,
  914. IPropertyTree* pSelectedComponents)
  915. {
  916. try
  917. {
  918. return new CEnvironmentDeploymentEngine(environment, callback, pSelectedComponents);
  919. }
  920. catch (IException* e)
  921. {
  922. throw e;
  923. }
  924. catch(...)
  925. {
  926. throw MakeStringException(-1, "Unknown exception!");
  927. }
  928. }
  929. IEnvDeploymentEngine* createConfigGenMgr(IConstEnvironment& env,
  930. IDeploymentCallback& callback,
  931. IPropertyTree* pSelectedComponents,
  932. const char* inputDir,
  933. const char* outputDir,
  934. const char* compName,
  935. const char* compType,
  936. const char* ipAddr)
  937. {
  938. try
  939. {
  940. StringBuffer inDir(inputDir);
  941. if (inDir.length() && inDir.charAt(inDir.length() - 1) != PATHSEPCHAR)
  942. inDir.append(PATHSEPCHAR);
  943. StringBuffer outDir(outputDir);
  944. if (outDir.length() && outDir.charAt(outDir.length() - 1) != PATHSEPCHAR)
  945. outDir.append(PATHSEPCHAR);
  946. return new CConfigGenMgr(env, callback, pSelectedComponents, inDir.str(), outDir.str(), compName, compType, ipAddr);
  947. }
  948. catch (IException* e)
  949. {
  950. throw e;
  951. }
  952. catch(...)
  953. {
  954. throw MakeStringException(-1, "Unknown exception!");
  955. }
  956. }
  957. bool matchDeployAddress(const char *searchIP, const char *envIP)
  958. {
  959. if (searchIP && envIP && *searchIP && *envIP)
  960. {
  961. IpAddress ip(envIP);
  962. if (strcmp(searchIP, ".")==0)
  963. return ip.isLocal();
  964. else
  965. {
  966. IpAddress ip2(searchIP);
  967. return ip.ipequals(ip2);
  968. }
  969. }
  970. return false;
  971. }
  972. IPropertyTree* getInstances(const IPropertyTree* pEnvRoot, const char* compName,
  973. const char* compType, const char* ipAddr, bool listall)
  974. {
  975. Owned<IPropertyTree> pSelComps(createPTree("SelectedComponents"));
  976. Owned<IPropertyTreeIterator> iter = pEnvRoot->getElements("Software/*");
  977. const char* instanceNodeNames[] = { "Instance", "RoxieServerProcess", "RoxieSlaveProcess" };
  978. const char* logDirNames[] = { "@logDir", "@LogDir", "@dfuLogDir", "@eclLogDir" };
  979. ForEach(*iter)
  980. {
  981. IPropertyTree* pComponent = &iter->query();
  982. const char* type = pComponent->queryName();
  983. if (stricmp(type, "Topology")!=0 && stricmp(type, "Directories")!=0 &&
  984. ((!compName && !compType) || (compName && !strcmp(pComponent->queryProp("@name"), compName)) ||
  985. (!compName && compType && !strcmp(pComponent->queryProp("@buildSet"), compType))))
  986. {
  987. const char* name = pComponent->queryProp("@name");
  988. const char* build = pComponent->queryProp("@build");
  989. const char* buildSet= pComponent->queryProp("@buildSet");
  990. const char* logDir = NULL;
  991. if (listall)
  992. for (int i = 0; i < sizeof(logDirNames)/sizeof(char*); i++)
  993. {
  994. logDir = pComponent->queryProp(logDirNames[i]);
  995. if (logDir)
  996. break;
  997. }
  998. StringBuffer sXPath;
  999. sXPath.appendf("Programs/Build[@name='%s']/BuildSet[@name='%s']/@deployable", build, buildSet);
  1000. const char* deployable = pEnvRoot->queryProp(sXPath.str());
  1001. //either the @deployable does not exist or it is not one of 'no', 'false' or '0'
  1002. if (!deployable ||
  1003. (strcmp(deployable, "no") != 0 && strcmp(deployable, "false") != 0 && strcmp(deployable, "0") != 0))
  1004. {
  1005. IPropertyTree* pSelComp = NULL;
  1006. Owned<IPropertyTreeIterator> iterInst = pComponent->getElements("*", iptiter_sort);
  1007. bool bAdded = false;
  1008. ForEach(*iterInst)
  1009. {
  1010. IPropertyTree* pInst = &iterInst->query();
  1011. const char* computer = pInst->queryProp("@computer");
  1012. const char* netAddr = pInst->queryProp("@netAddress");
  1013. if (!computer || !*computer || !strcmp("Notes", pInst->queryName()))
  1014. continue;
  1015. if (!strcmp(buildSet, "thor"))
  1016. {
  1017. sXPath.clear().appendf("Hardware/Computer[@name=\"%s\"]", computer);
  1018. IPropertyTree* pComputer = pEnvRoot->queryPropTree(sXPath.str());
  1019. netAddr = pComputer->queryProp("@netAddress");
  1020. if (matchDeployAddress(ipAddr, netAddr) ||
  1021. (!ipAddr && netAddr && *netAddr))
  1022. {
  1023. if (!bAdded)
  1024. {
  1025. pSelComp = pSelComps->addPropTree(pComponent->queryName(), createPTree());
  1026. pSelComp->addProp("@name", name);
  1027. pSelComp->addProp("@buildSet", buildSet);
  1028. pSelComp->addProp("@logDir", logDir);
  1029. bAdded = true;
  1030. }
  1031. if (listall)
  1032. {
  1033. IPropertyTree* pInstance = pSelComp->addPropTree(pInst->queryName(), createPTree());
  1034. pInstance->addProp("@name", pInst->queryProp("@name"));
  1035. pInstance->addProp("@computer", computer);
  1036. pInstance->addProp("@netAddress", netAddr);
  1037. }
  1038. }
  1039. }
  1040. else if (matchDeployAddress(ipAddr, netAddr) ||
  1041. (!ipAddr && netAddr && *netAddr))
  1042. {
  1043. if (!bAdded)
  1044. {
  1045. pSelComp = pSelComps->addPropTree(pComponent->queryName(), createPTree());
  1046. pSelComp->addProp("@name", name);
  1047. pSelComp->addProp("@buildSet", buildSet);
  1048. pSelComp->addProp("@logDir", logDir);
  1049. bAdded = true;
  1050. }
  1051. StringBuffer sb(pInst->queryName());
  1052. for (UINT i=0; i<sizeof(instanceNodeNames) / sizeof(instanceNodeNames[0]); i++)
  1053. if (!strcmp(sb.str(), instanceNodeNames[i]))
  1054. {
  1055. //allow multiple instances but do not allow either roxie servers or slaves more than once per computer
  1056. if (listall || sb.str()[0] != 'R' || !pSelComp->queryPropTree(StringBuffer().appendf("*[@computer=\"%s\"]", computer)))
  1057. {
  1058. IPropertyTree* pInstance = pSelComp->addPropTree(sb.str(), createPTree());
  1059. pInstance->addProp("@name", pInst->queryProp("@name"));
  1060. pInstance->addProp("@computer", pInst->queryProp("@computer"));
  1061. pInstance->addProp("@port", pInst->queryProp("@port"));
  1062. pInstance->addProp("@netAddress", pInst->queryProp("@netAddress"));
  1063. const char* directory = pInst->queryProp(sb.str()[0]=='R' ? "@dataDirectory" : "@directory");
  1064. if (directory && *directory)
  1065. pInstance->addProp("@directory", directory);
  1066. }
  1067. break;
  1068. }
  1069. }
  1070. }
  1071. }
  1072. }
  1073. }
  1074. return pSelComps.getLink();
  1075. }
  1076. //---------------------------------------------------------------------------
  1077. // Module Globals
  1078. //---------------------------------------------------------------------------
  1079. const char* findFileExtension(const char* pszPath)
  1080. {
  1081. const char* lastSlash = pathTail(pszPath);
  1082. return strrchr(lastSlash ? lastSlash : pszPath, '.');
  1083. }
  1084. void removeTrailingPathSepChar(char* pszPath)
  1085. {
  1086. if (pszPath)
  1087. {
  1088. char* lastChar = pszPath + strlen(pszPath) - 1;
  1089. if (isPathSepChar(*lastChar))
  1090. *lastChar = '\0';
  1091. }
  1092. }
  1093. void stripNetAddr(const char* dir, StringBuffer& destpath, StringBuffer& destip, bool makeLinux)
  1094. {
  1095. destpath.clear().append(dir);
  1096. if (dir[0] == '\\' && dir[1] == '\\' && strlen(dir) > 2)
  1097. {
  1098. destip.clear().append(strchr(dir + 2, '\\') - (dir + 2), dir + 2);
  1099. destpath.clear().append(strchr(dir + 2, '\\'));
  1100. }
  1101. if (makeLinux)
  1102. destpath.replace('\\', '/');
  1103. }
  1104. //returns temp path that ends with path sep
  1105. //
  1106. #ifdef _WIN32
  1107. extern DWORD getLastError() { return ::GetLastError(); }
  1108. void getTempPath(char* tempPath, unsigned int bufsize, const char* subdir/*=NULL*/)
  1109. {
  1110. ::GetTempPath(bufsize, tempPath);
  1111. ::GetLongPathName(tempPath, tempPath, bufsize);
  1112. if (subdir && *subdir)
  1113. {
  1114. const int len = strlen(tempPath);
  1115. char* p = tempPath + len;
  1116. strcpy(p, subdir);
  1117. p += strlen(subdir);
  1118. *p++ = '\\';
  1119. *p = '\0';
  1120. }
  1121. }
  1122. #else//Linux specifics follow
  1123. extern DWORD getLastError() { return errno; }
  1124. void getTempPath(char* tempPath, unsigned int bufsize, const char* subdir/*=NULL*/)
  1125. {
  1126. assert(bufsize > 5);
  1127. strcpy(tempPath, "/tmp/");
  1128. if (subdir && *subdir)
  1129. {
  1130. strcat(tempPath, subdir);
  1131. strcat(tempPath, "/");
  1132. }
  1133. }
  1134. #endif