jmisc.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  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. //#define MINIMALTRACE // a more readable version of the trace file (IMO)
  14. //#define LOGCLOCK
  15. #include "platform.h"
  16. #include "stdio.h"
  17. #include <time.h>
  18. #include "jmisc.hpp"
  19. #include "jmutex.hpp"
  20. #include "jexcept.hpp"
  21. #include "jstring.hpp"
  22. #include "jdebug.hpp"
  23. #ifdef _WIN32
  24. #include <direct.h>
  25. #else
  26. #include <sys/wait.h>
  27. #include <pwd.h>
  28. #endif
  29. #ifdef _WIN32
  30. #include <stdlib.h>
  31. #elif defined(__linux__) || defined(__FreeBSD__)
  32. extern char **environ;
  33. #elif defined(__APPLE__)
  34. #include <crt_externs.h>
  35. #endif
  36. #ifdef LOGCLOCK
  37. #define MSGFIELD_PRINTLOG MSGFIELD_timeDate | MSGFIELD_msgID | MSGFIELD_process | MSGFIELD_thread | MSGFIELD_code | MSGFIELD_milliTime
  38. #else
  39. #define MSGFIELD_PRINTLOG MSGFIELD_timeDate | MSGFIELD_msgID | MSGFIELD_process | MSGFIELD_thread | MSGFIELD_code
  40. #endif
  41. #define FPRINTF(s) { fprintf(stdlog, "%s", s); \
  42. if (logFile) fprintf(logFile, "%s", s); }
  43. void _rev(size32_t len, void * _ptr)
  44. {
  45. byte * ptr = (byte *)_ptr;
  46. while (len > 1)
  47. {
  48. byte t = *ptr;
  49. *ptr = ptr[--len];
  50. ptr[len] = t;
  51. len--;
  52. ptr++;
  53. }
  54. }
  55. Mutex printMutex;
  56. FILE *logFile;
  57. FILE *stdlog = stderr;
  58. HiresTimer logTimer;
  59. class CStdLogIntercept: public ILogIntercept
  60. {
  61. bool nl;
  62. #ifdef MINIMALTRACE
  63. time_t lastt;
  64. unsigned lasttpid;
  65. #endif
  66. unsigned detail;
  67. public:
  68. CStdLogIntercept()
  69. {
  70. #ifdef MINIMALTRACE
  71. lasttpid = 0;
  72. lastt = 0;
  73. #endif
  74. nl = true;
  75. detail = _LOG_TIME | _LOG_PID | _LOG_TID;
  76. #ifdef LOGCLOCK
  77. detail |= _LOG_CLOCK;
  78. #endif
  79. queryStderrLogMsgHandler()->setMessageFields(detail);
  80. }
  81. unsigned setLogDetail(unsigned i)
  82. {
  83. unsigned ret = detail;
  84. detail = i;
  85. return ret;
  86. }
  87. void print(const char *str)
  88. {
  89. if (nl) {
  90. char timeStamp[32];
  91. time_t tNow;
  92. time(&tNow);
  93. #ifdef _WIN32
  94. struct tm ltNow = *localtime(&tNow);
  95. #else
  96. struct tm ltNow;
  97. localtime_r(&tNow, &ltNow);
  98. #endif
  99. #ifdef MINIMALTRACE
  100. unsigned tpid = GetCurrentThreadId();
  101. if (((detail & _LOG_TID) && tpid!=lasttpid)||((detail & _LOG_TIME) && memcmp(&tNow,&lastt,sizeof(tNow))!=0))
  102. {
  103. lasttpid = tpid;
  104. lastt = tNow;
  105. FPRINTF("[");
  106. if (detail & _LOG_TIME)
  107. {
  108. strftime(timeStamp, 32, "%H:%M:%S ", &ltNow);
  109. FPRINTF(timeStamp);
  110. }
  111. if (detail & _LOG_TID)
  112. {
  113. fprintf(stdlog, "TID=%d ", tpid);
  114. if (logFile)
  115. fprintf(logFile, "TID=%d ", tpid);
  116. }
  117. FPRINTF("]\n");
  118. }
  119. #else
  120. if (detail & _LOG_TIME)
  121. {
  122. strftime(timeStamp, 32, "%m/%d/%y %H:%M:%S ", &ltNow);
  123. FPRINTF(timeStamp);
  124. }
  125. if (detail & _LOG_CLOCK)
  126. {
  127. unsigned t=msTick();
  128. fprintf(stdlog, "%u ", t);
  129. if (logFile)
  130. fprintf(logFile, "%u ", t);
  131. }
  132. if (detail & _LOG_HIRESCLOCK)
  133. {
  134. unsigned clock = usTick();
  135. fprintf(stdlog, " TICK=%u ", clock);
  136. if (logFile)
  137. fprintf(logFile, " TICK=%u ", clock);
  138. }
  139. #if defined(_WIN32)
  140. if (detail & _LOG_TID)
  141. {
  142. fprintf(stdlog, "TID=%d ", GetCurrentThreadId());
  143. if (logFile)
  144. fprintf(logFile, "TID=%d ", GetCurrentThreadId());
  145. }
  146. if (detail & _LOG_PID)
  147. {
  148. fprintf(stdlog, "PID=%d ", GetCurrentProcessId());
  149. if (logFile)
  150. fprintf(logFile, "PID=%d ", GetCurrentProcessId());
  151. }
  152. #else
  153. if (detail & _LOG_PID)
  154. {
  155. fprintf(stdlog, "PID=%d ", getpid());
  156. if (logFile)
  157. fprintf(logFile, "PID=%d ", getpid());
  158. }
  159. #endif
  160. if (detail & (_LOG_PID|_LOG_TID|_LOG_TIME))
  161. {
  162. fprintf(stdlog, "- ");
  163. if (logFile)
  164. fprintf(logFile, "- ");
  165. }
  166. #endif
  167. }
  168. if (str && *str)
  169. {
  170. nl = str[strlen(str)-1]=='\n';
  171. FPRINTF(str);
  172. }
  173. else
  174. nl = false;
  175. fflush(stdlog);
  176. if (logFile)
  177. {
  178. fflush(logFile);
  179. }
  180. }
  181. void close()
  182. {
  183. if (logFile) {
  184. fclose(logFile);
  185. logFile = 0;
  186. }
  187. }
  188. };
  189. static ILogIntercept *logintercept = NULL;
  190. jlib_decl ILogIntercept* interceptLog(ILogIntercept *intercept)
  191. {
  192. ILogIntercept *old=logintercept;
  193. logintercept = intercept;
  194. return old;
  195. }
  196. jlib_decl void openLogFile(StringBuffer & resolvedFS, const char *filename, unsigned detail, bool enterQueueMode, bool append)
  197. {
  198. if(enterQueueMode)
  199. queryLogMsgManager()->enterQueueingMode();
  200. Owned<IComponentLogFileCreator> lf = createComponentLogFileCreator(".", 0);
  201. lf->setCreateAliasFile(false);
  202. lf->setLocal(true);
  203. lf->setRolling(false);
  204. lf->setAppend(append);
  205. lf->setCompleteFilespec(filename);//user specified log filespec
  206. lf->setMaxDetail(detail ? detail : DefaultDetail);
  207. lf->setMsgFields(MSGFIELD_STANDARD);
  208. lf->beginLogging();
  209. resolvedFS.set(lf->queryLogFileSpec());
  210. }
  211. StringBuffer &addFileTimestamp(StringBuffer &fname, bool daily)
  212. {
  213. time_t tNow;
  214. time(&tNow);
  215. char timeStamp[32];
  216. #ifdef _WIN32
  217. struct tm *ltNow;
  218. ltNow = localtime(&tNow);
  219. strftime(timeStamp, 32, daily ? ".%Y_%m_%d" : ".%Y_%m_%d_%H_%M_%S", ltNow);
  220. #else
  221. struct tm ltNow;
  222. localtime_r(&tNow, &ltNow);
  223. strftime(timeStamp, 32, daily ? ".%Y_%m_%d" : ".%Y_%m_%d_%H_%M_%S", &ltNow);
  224. #endif
  225. return fname.append(timeStamp);
  226. }
  227. #define MAX_TRY_SIZE 0x8000000 // 256 MB
  228. jlib_decl void PrintMemoryStatusLog()
  229. {
  230. #ifdef _WIN32
  231. MEMORYSTATUS mS;
  232. GlobalMemoryStatus(&mS);
  233. LOG(MCdebugInfo, unknownJob, "Available Physical Memory = %dK", (unsigned)(mS.dwAvailPhys/1024));
  234. #ifdef FRAGMENTATION_CHECK
  235. // see if fragmented
  236. size32_t sz = MAX_TRY_SIZE;
  237. while (sz) {
  238. if (sz<mS.dwAvailPhys/4) {
  239. void *p=malloc(sz);
  240. if (p) {
  241. free(p);
  242. break;
  243. }
  244. }
  245. sz /= 2;
  246. }
  247. sz *= 2;
  248. if ((sz<MAX_TRY_SIZE)&&(sz<mS.dwAvailPhys/4)) {
  249. LOG(MCdebugInfo, unknownJob, "WARNING: Could not allocate block size %d", sz);
  250. _HEAPINFO hinfo;
  251. int heapstatus;
  252. hinfo._pentry = NULL;
  253. size32_t max=0;
  254. unsigned fragments=0;
  255. while( ( heapstatus = _heapwalk( &hinfo ) ) == _HEAPOK ) {
  256. if (hinfo._useflag != _USEDENTRY) {
  257. if (hinfo._size>max)
  258. max = hinfo._size;
  259. fragments++;
  260. }
  261. }
  262. LOG(MCdebugInfo, unknownJob, "Largest unused fragment = %d", max);
  263. LOG(MCdebugInfo, unknownJob, "Number of fragments = %d", fragments);
  264. }
  265. #endif
  266. #endif
  267. }
  268. static class _init_jmisc_class
  269. {
  270. public:
  271. ~_init_jmisc_class()
  272. {
  273. if (logFile)
  274. fclose(logFile);
  275. }
  276. } _init_jmisc;
  277. FILE *xfopen(const char *path, const char *mode)
  278. {
  279. char *s, *e, *p;
  280. p = s = strdup(path);
  281. e = s+strlen(path);
  282. bool alt=false;
  283. for (; p<e; p++)
  284. {
  285. #ifdef _WIN32
  286. if (*p == '/')
  287. #else
  288. if (*p == '\\')
  289. #endif
  290. {
  291. alt = true;
  292. *p = PATHSEPCHAR;
  293. }
  294. }
  295. if (alt)
  296. printf("XFOPEN ALTERED FILENAME FROM:%s TO %s\n", path, s);
  297. FILE *file = ::fopen(s, mode);
  298. free(s);
  299. return file;
  300. }
  301. const char * queryCcLogName()
  302. {
  303. const char* logFileName = getenv("ECL_CCLOG");
  304. if (!logFileName)
  305. {
  306. logFileName = "cclog.txt";
  307. }
  308. return logFileName;
  309. //More does output get redirected here?
  310. }
  311. StringBuffer& queryCcLogName(const char* wuid, StringBuffer& logname)
  312. {
  313. if(!wuid || !*wuid)
  314. logname.append(queryCcLogName());
  315. else
  316. {
  317. const char* presetName = getenv("ECL_CCLOG");
  318. if (presetName && *presetName)
  319. logname.append(presetName);
  320. else
  321. logname.append(wuid).trim().append(".cclog.txt");
  322. }
  323. return logname;
  324. }
  325. jlib_decl char* readarg(char*& curptr)
  326. {
  327. char *cur = curptr;
  328. if(cur == NULL || *cur == 0)
  329. return NULL;
  330. while(*cur == ' ' || *cur == '\t') {
  331. cur++;
  332. if (!*cur) {
  333. curptr = cur;
  334. return NULL;
  335. }
  336. }
  337. char quote = 0;
  338. if(*cur == '\'' || *cur == '"')
  339. {
  340. quote = *cur;
  341. cur++;
  342. }
  343. char *ret = cur;
  344. if (quote)
  345. {
  346. while (*cur && *cur!=quote)
  347. cur++;
  348. }
  349. else
  350. {
  351. do {
  352. cur++;
  353. } while(*cur && *cur != ' ' && *cur != '\t');
  354. }
  355. if(*cur != 0)
  356. {
  357. *cur = 0;
  358. cur++;
  359. }
  360. curptr = cur;
  361. return ret;
  362. }
  363. #ifdef _WIN32
  364. bool invoke_program(const char *command_line, DWORD &runcode, bool wait, const char *outfile, HANDLE *rethandle, bool throwException, bool newProcessGroup)
  365. {
  366. runcode = 0;
  367. if (rethandle)
  368. *rethandle = 0;
  369. if(!command_line || !*command_line)
  370. return false;
  371. STARTUPINFO StartupInfo;
  372. SECURITY_ATTRIBUTES security;
  373. _clear(security);
  374. security.nLength = sizeof(security);
  375. security.bInheritHandle = TRUE;
  376. _clear(StartupInfo);
  377. StartupInfo.cb = sizeof(StartupInfo);
  378. HANDLE outh = NULL;
  379. if (outfile&&*outfile) {
  380. outh = CreateFile(outfile,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,&security,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  381. if (outh == INVALID_HANDLE_VALUE)
  382. {
  383. OERRLOG("Cannot create file '%s' error code %d",outfile,(int)GetLastError());
  384. return false;
  385. }
  386. }
  387. if (outh) {
  388. if (!DuplicateHandle(GetCurrentProcess(),outh,GetCurrentProcess(),&StartupInfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS) ||
  389. !DuplicateHandle(GetCurrentProcess(),outh,GetCurrentProcess(),&StartupInfo.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS))
  390. {
  391. OERRLOG("Execution of \"%s\" failed, DuplicateHandle error = %d",command_line, (int)GetLastError());
  392. CloseHandle(outh);
  393. return false;
  394. }
  395. StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  396. StartupInfo.dwFlags = STARTF_USESTDHANDLES;
  397. CloseHandle(outh);
  398. }
  399. PROCESS_INFORMATION ProcessInformation;
  400. bool ok = CreateProcess(NULL,(char *)command_line,NULL,NULL,TRUE,0,NULL,NULL,&StartupInfo,&ProcessInformation)!=0;
  401. if (ok) {
  402. if (wait)
  403. {
  404. WaitForSingleObject(ProcessInformation.hProcess,INFINITE);
  405. GetExitCodeProcess(ProcessInformation.hProcess, &runcode);
  406. }
  407. CloseHandle(ProcessInformation.hThread);
  408. if (rethandle) {
  409. *rethandle = (HANDLE)ProcessInformation.hProcess;
  410. if (wait)
  411. CloseHandle(ProcessInformation.hProcess);
  412. }
  413. else
  414. CloseHandle(ProcessInformation.hProcess);
  415. }
  416. else
  417. {
  418. int lastError = (int)GetLastError(); // for debugging
  419. //print out why create process failed
  420. OERRLOG("Execution of \"%s\" failed, error = %d",command_line,lastError);
  421. }
  422. if (outh)
  423. {
  424. CloseHandle(StartupInfo.hStdOutput);
  425. CloseHandle(StartupInfo.hStdError);
  426. }
  427. return ok;
  428. }
  429. bool wait_program(HANDLE handle,DWORD &runcode,bool block)
  430. {
  431. runcode = (DWORD)-1;
  432. if ((handle==NULL)||(handle==(HANDLE)-1))
  433. return true; // actually it failed
  434. int ret = WaitForSingleObject(handle,block?INFINITE:0);
  435. int err = GetLastError();
  436. if (ret==WAIT_OBJECT_0) {
  437. GetExitCodeProcess(handle, &runcode);
  438. CloseHandle(handle);
  439. return true;
  440. }
  441. return false;
  442. }
  443. jlib_decl bool interrupt_program(HANDLE handle, bool stopChildren, int signum)
  444. {
  445. if (signum==0)
  446. return TerminateProcess(handle,1)!=FALSE;
  447. OERRLOG("interrupt_program signal %d not supported in windows",signum);
  448. return false;
  449. }
  450. void close_program(HANDLE handle)
  451. {
  452. CloseHandle(handle);
  453. }
  454. #else
  455. bool invoke_program(const char *command_line, DWORD &runcode, bool wait, const char *outfile, HANDLE *rethandle, bool throwException, bool newProcessGroup)
  456. {
  457. runcode = 0;
  458. if (rethandle)
  459. *rethandle = 0;
  460. if(!command_line || !*command_line)
  461. return false;
  462. pid_t pid = fork();
  463. if (pid == 0)
  464. {
  465. //Force the child process into its own process group, so we can terminate it and its children.
  466. if (newProcessGroup)
  467. setpgid(0,0);
  468. if (outfile&&*outfile) {
  469. int outh = open(outfile, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
  470. if(outh >= 0)
  471. {
  472. dup2(outh, STDOUT_FILENO);
  473. dup2(outh, STDERR_FILENO);
  474. close(outh);
  475. }
  476. else {
  477. OERRLOG("invoke_program: could not open %s",outfile);
  478. return false;
  479. }
  480. }
  481. int size = 10;
  482. char **args;
  483. char **argptr = args = (char **) malloc(sizeof(args) * size);
  484. char **over = argptr+size;
  485. char *cl = strdup(command_line);
  486. char *curptr = cl;
  487. *argptr = readarg(curptr);
  488. while (*argptr != NULL)
  489. {
  490. argptr++;
  491. if (argptr==over)
  492. {
  493. args = (char **) realloc(args, sizeof(args) * size * 2);
  494. argptr = args+size;
  495. size *= 2;
  496. over = args+size;
  497. }
  498. *argptr = readarg(curptr);
  499. }
  500. // JCSMORE - do I not need to free args, if successful exec?
  501. if (execvp(*args, args))
  502. {
  503. //print out why create process failed
  504. int err = errno;
  505. OERRLOG("invoke_program(%s) failed with error(%d): %s",command_line,err,strerror(err));
  506. }
  507. assertex(0); // don't expect to get here!
  508. _exit(0);
  509. }
  510. else if (pid < 0)
  511. {
  512. StringBuffer s("fork of \"");
  513. s.append(command_line).append("\" failed. fork() returned:");
  514. if (errno == EAGAIN)
  515. s.append("EAGAIN");
  516. else if (errno == ENOMEM)
  517. s.append("ENOMEM");
  518. else
  519. s.append(errno);
  520. OERRLOG("%s",s.str());
  521. if(throwException)
  522. throw MakeStringExceptionDirect(-1, s.str());
  523. return false;
  524. }
  525. if (rethandle)
  526. *rethandle = (HANDLE)pid;
  527. if (wait)
  528. {
  529. int retv;
  530. while (1)
  531. {
  532. auto wpid = waitpid(pid, &retv, 0);
  533. if (wpid == pid)
  534. break;
  535. if (errno != EINTR)
  536. {
  537. OERRLOG("invoke_program(%s): wait failed (%d, %d, %d)",command_line,(int) wpid, retv, errno);
  538. return false;
  539. }
  540. }
  541. if (!WIFEXITED(retv)) //did not exit normally
  542. {
  543. int err = errno;
  544. OERRLOG("invoke_program(%s): failed.",command_line);
  545. OERRLOG("The process was killed by signal %d%s.",(int)WTERMSIG(retv),WCOREDUMP(retv)?" - core dumped":"");
  546. OERRLOG("Last system error is %s",strerror(err));
  547. }
  548. runcode = WEXITSTATUS(retv);
  549. }
  550. return true; // it did run even if signalled
  551. }
  552. bool wait_program(HANDLE handle,DWORD &runcode,bool block)
  553. {
  554. pid_t pid = (pid_t)handle;
  555. runcode = (DWORD)-1;
  556. if ((int)pid<=0)
  557. return true; // actually it failed
  558. int retv;
  559. pid_t ret = waitpid(pid, &retv, block?0:WNOHANG);
  560. if (ret == (pid_t)-1) {
  561. IERRLOG("wait_program: wait failed (%d)",errno);
  562. return true; // actually it failed but assume finished
  563. }
  564. else if (ret==0)
  565. return false;
  566. runcode = WEXITSTATUS(retv);
  567. return true;
  568. }
  569. bool interrupt_program(HANDLE handle, bool stopChildren, int signum)
  570. {
  571. if (signum == 0)
  572. signum = SIGINT;
  573. pid_t pid = (pid_t)handle;
  574. if ((int)pid<=0)
  575. return false;
  576. //If we need to also stop child processes then kill the process group (same as the pid)
  577. //Note: This will not apply to grand-children started by the children by calling invoke_program()
  578. //since they will have a different process group
  579. if (stopChildren)
  580. pid = -pid;
  581. return (kill(pid, signum)==0);
  582. }
  583. void close_program(HANDLE handle)
  584. {
  585. // not needed in linux
  586. }
  587. #endif
  588. #ifndef _WIN32
  589. bool CopyFile(const char *file, const char *newfile, bool fail)
  590. {
  591. struct stat s;
  592. if ((fail) && (0 == stat((char *)newfile, &s))) return false;
  593. FILE *in=fopen(file,"rb"), *out=fopen(newfile,"wb");
  594. try
  595. {
  596. if (!in)
  597. throw MakeStringException(-1, "failed to open %s for copy",file);
  598. if (!out)
  599. throw MakeStringException(-1, "failed to create %s",newfile);
  600. char b[1024];
  601. while (true)
  602. {
  603. int c=fread(b,1,sizeof(b),in);
  604. if (!c) break;
  605. if (!fwrite(b,c,1,out)) throw MakeStringException(-1, "failed to copy file %s to %s",file,newfile);
  606. }
  607. fclose(in);
  608. fclose(out);
  609. stat((char *)file, &s);
  610. chmod(newfile, s.st_mode);
  611. } catch (...)
  612. {
  613. if (in) fclose(in);
  614. if (out) fclose(out);
  615. return false;
  616. }
  617. return true;
  618. }
  619. #endif
  620. //========================================================================================================================
  621. static bool hadAbortSignal = false;
  622. static bool handlerInstalled = false;
  623. CriticalSection abortCrit;
  624. class AbortHandlerInfo : public CInterface
  625. {
  626. public:
  627. ThreadId installer;
  628. AbortHandler handler;
  629. SimpleAbortHandler shandler;
  630. IAbortHandler *ihandler;
  631. AbortHandlerInfo(AbortHandler _handler)
  632. {
  633. handler = _handler;
  634. shandler = NULL;
  635. ihandler = NULL;
  636. installer = GetCurrentThreadId();
  637. }
  638. AbortHandlerInfo(SimpleAbortHandler _handler)
  639. {
  640. handler = NULL;
  641. shandler = _handler;
  642. ihandler = NULL;
  643. installer = GetCurrentThreadId();
  644. }
  645. AbortHandlerInfo(IAbortHandler *_ihandler)
  646. {
  647. handler = NULL;
  648. shandler = NULL;
  649. ihandler = _ihandler;
  650. installer = GetCurrentThreadId();
  651. }
  652. bool handle(ahType type)
  653. {
  654. #ifndef _WIN32
  655. if (installer == GetCurrentThreadId())
  656. #endif
  657. {
  658. // DBGLOG("handle abort %x", GetCurrentThreadId());
  659. if (handler)
  660. return handler(type);
  661. else if (shandler)
  662. return shandler();
  663. else
  664. return ihandler->onAbort();
  665. }
  666. #ifndef _WIN32
  667. else
  668. return false;
  669. #endif
  670. }
  671. };
  672. CIArrayOf<AbortHandlerInfo> handlers;
  673. bool notifyOnAbort(ahType type)
  674. {
  675. // DBGLOG("notifyOnAbort %x", GetCurrentThreadId());
  676. // CriticalBlock c(abortCrit); You would think that this was needed, but it locks up.
  677. // If it needs to be threadsafe, have to clone the list or something
  678. bool doExit = false;
  679. ForEachItemInRev(idx, handlers)
  680. {
  681. if (handlers.item(idx).handle(type))
  682. doExit = true;
  683. }
  684. // DBGLOG("notifyOnAbort returning %d", (int) doExit);
  685. return doExit;
  686. }
  687. #ifdef _WIN32
  688. BOOL WINAPI WindowsAbortHandler ( DWORD dwCtrlType )
  689. {
  690. switch( dwCtrlType )
  691. {
  692. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  693. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  694. case CTRL_CLOSE_EVENT:
  695. {
  696. hadAbortSignal = true;
  697. bool doExit = notifyOnAbort(ahInterrupt);
  698. return !doExit;
  699. }
  700. case CTRL_LOGOFF_EVENT:
  701. case CTRL_SHUTDOWN_EVENT:
  702. hadAbortSignal = true;
  703. notifyOnAbort(ahTerminate);
  704. return FALSE;
  705. }
  706. return FALSE;
  707. }
  708. BOOL WINAPI ModuleExitHandler ( DWORD dwCtrlType )
  709. {
  710. switch( dwCtrlType )
  711. {
  712. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  713. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  714. case CTRL_CLOSE_EVENT:
  715. case CTRL_LOGOFF_EVENT:
  716. case CTRL_SHUTDOWN_EVENT:
  717. ExitModuleObjects();
  718. }
  719. return FALSE;
  720. }
  721. #elif defined(__linux__) || defined(__APPLE__)
  722. static void UnixAbortHandler(int signo)
  723. {
  724. ahType type = ahInterrupt;
  725. if (SIGTERM == signo)
  726. type = ahTerminate;
  727. hadAbortSignal = true;
  728. if (handlers.length()==0 || notifyOnAbort(type))
  729. {
  730. _exit(0);
  731. }
  732. }
  733. #endif
  734. void queryInstallAbortHandler()
  735. {
  736. if (handlerInstalled)
  737. return;
  738. #if defined(_WIN32)
  739. SetConsoleCtrlHandler( WindowsAbortHandler, TRUE );
  740. #elif defined(__linux__) || defined(__APPLE__)
  741. struct sigaction action;
  742. sigemptyset(&action.sa_mask);
  743. action.sa_flags = SA_RESTART;
  744. action.sa_handler = (void(*)(int))UnixAbortHandler;
  745. if (sigaction(SIGINT, &action, NULL) == -1 ||
  746. sigaction(SIGQUIT, &action, NULL) == -1 ||
  747. sigaction(SIGTERM, &action, NULL) == -1)
  748. {
  749. perror("sigaction in queryInstallAbortHandler failed");
  750. }
  751. #endif
  752. handlerInstalled = true;
  753. }
  754. void queryUninstallAbortHandler()
  755. {
  756. if (handlers.ordinality())
  757. return;
  758. #if defined(_WIN32)
  759. if (handlerInstalled)
  760. {
  761. SetConsoleCtrlHandler( WindowsAbortHandler, FALSE);
  762. handlerInstalled = false;
  763. }
  764. #else
  765. // Don't uninstall - we always want one for the module exit support
  766. #endif
  767. }
  768. MODULE_INIT(INIT_PRIORITY_JMISC2)
  769. {
  770. #if defined(_WIN32)
  771. // NOTE: handlers are called in LIFO order and hence any handler that returns false
  772. // (e.g CTRL-C not wanting to abort)) will stop this handler being called also (correctly).
  773. SetConsoleCtrlHandler( ModuleExitHandler, TRUE);
  774. #elif defined(__linux__)
  775. queryInstallAbortHandler();
  776. #endif
  777. return true;
  778. }
  779. void addAbortHandler(AbortHandler handler)
  780. {
  781. CriticalBlock c(abortCrit);
  782. queryInstallAbortHandler();
  783. handlers.append(*new AbortHandlerInfo(handler));
  784. }
  785. void addAbortHandler(SimpleAbortHandler handler)
  786. {
  787. CriticalBlock c(abortCrit);
  788. queryInstallAbortHandler();
  789. handlers.append(*new AbortHandlerInfo(handler));
  790. }
  791. void addAbortHandler(IAbortHandler & handler)
  792. {
  793. CriticalBlock c(abortCrit);
  794. queryInstallAbortHandler();
  795. handlers.append(*new AbortHandlerInfo(&handler));
  796. }
  797. void removeAbortHandler(AbortHandler handler)
  798. {
  799. CriticalBlock c(abortCrit);
  800. ForEachItemInRev(idx, handlers)
  801. {
  802. if (handlers.item(idx).handler == handler)
  803. {
  804. handlers.remove(idx);
  805. break;
  806. }
  807. }
  808. queryUninstallAbortHandler();
  809. }
  810. void removeAbortHandler(SimpleAbortHandler handler)
  811. {
  812. CriticalBlock c(abortCrit);
  813. ForEachItemInRev(idx, handlers)
  814. {
  815. if (handlers.item(idx).shandler == handler)
  816. {
  817. handlers.remove(idx);
  818. break;
  819. }
  820. }
  821. queryUninstallAbortHandler();
  822. }
  823. void removeAbortHandler(IAbortHandler & handler)
  824. {
  825. CriticalBlock c(abortCrit);
  826. ForEachItemInRev(idx, handlers)
  827. {
  828. if (handlers.item(idx).ihandler == &handler)
  829. {
  830. handlers.remove(idx);
  831. break;
  832. }
  833. }
  834. queryUninstallAbortHandler();
  835. }
  836. bool isAborting()
  837. {
  838. return hadAbortSignal;
  839. }
  840. void throwAbortException()
  841. {
  842. throw MakeStringException(JLIBERR_UserAbort, "Operation aborted by user");
  843. }
  844. void throwExceptionIfAborting()
  845. {
  846. if (isAborting())
  847. throwAbortException();
  848. }
  849. //========================================================================================================================
  850. StringBuffer & hexdump2string(byte const * in, size32_t inSize, StringBuffer & out)
  851. {
  852. out.append("[");
  853. byte last = 0;
  854. unsigned seq = 1;
  855. for(unsigned i=0; i<inSize; ++i)
  856. {
  857. if((i>0) && (in[i]==last))
  858. {
  859. ++seq;
  860. }
  861. else
  862. {
  863. if(seq>1)
  864. {
  865. if(seq==2)
  866. out.appendf(" %02X", last);
  867. else
  868. out.appendf("x%u", seq);
  869. seq = 1;
  870. }
  871. out.appendf(" %02X", in[i]);
  872. last = in[i];
  873. }
  874. }
  875. if(seq>1)
  876. out.appendf("x%u", seq);
  877. out.append(" ]");
  878. return out;
  879. }
  880. jlib_decl bool getHomeDir(StringBuffer & homepath)
  881. {
  882. #ifdef _WIN32
  883. const char *home = getenv("APPDATA");
  884. // Not the 'official' way - which changes with every windows version
  885. // but should work well enough for us (and avoids sorting out windows include mess)
  886. #else
  887. const char *home = getenv("HOME");
  888. if (!home)
  889. {
  890. struct passwd *pw = getpwuid(getuid());
  891. home = pw->pw_dir;
  892. }
  893. #endif
  894. if (!home)
  895. return false;
  896. homepath.append(home);
  897. return true;
  898. }
  899. #ifdef _WIN32
  900. char *mkdtemp(char *_template)
  901. {
  902. if (!_template || strlen(_template) < 6 || !streq(_template+strlen(_template)-6, "XXXXXX"))
  903. {
  904. errno = EINVAL;
  905. return nullptr;
  906. }
  907. char * tail = _template + strlen(_template) - 6;
  908. for (int i = 0; i < 100; i++)
  909. {
  910. snprintf(tail, 7, "%06d", fastRand());
  911. if (!_mkdir(_template))
  912. return _template;
  913. if (errno != EEXIST)
  914. return nullptr;
  915. }
  916. errno = EINVAL;
  917. return nullptr;
  918. }
  919. #endif
  920. jlib_decl char **getSystemEnv()
  921. {
  922. #ifdef _WIN32
  923. return _environ;
  924. #elif defined (__APPLE__)
  925. return *_NSGetEnviron();
  926. #else
  927. return environ;
  928. #endif
  929. }
  930. #ifdef _CONTAINERIZED
  931. // NB: will fire an exception if command fails (returns non-zero exit code)
  932. void runKubectlCommand(const char *title, const char *cmd, const char *input, StringBuffer *output)
  933. {
  934. StringBuffer _output, error;
  935. if (!output)
  936. output = &_output;
  937. unsigned ret = runExternalCommand(title, *output, error, cmd, input, ".", nullptr);
  938. if (output->length())
  939. MLOG(MCExtraneousInfo, unknownJob, "%s: ret=%u, stdout=%s", cmd, ret, output->trimRight().str());
  940. if (error.length())
  941. MLOG(MCinternalError, unknownJob, "%s: ret=%u, stderr=%s", cmd, ret, error.trimRight().str());
  942. if (ret)
  943. {
  944. if (input)
  945. MLOG(MCinternalError, unknownJob, "Using input %s", input);
  946. throw makeStringExceptionV(0, "Failed to run %s: error %u: %s", cmd, ret, error.str());
  947. }
  948. }
  949. static CTimeLimitedCache<std::string, std::pair<std::string, unsigned>> externalServiceCache;
  950. static CriticalSection externalServiceCacheCrit;
  951. std::pair<std::string, unsigned> getExternalService(const char *serviceName)
  952. {
  953. {
  954. CriticalBlock b(externalServiceCacheCrit);
  955. std::pair<std::string, unsigned> cachedExternalSevice;
  956. if (externalServiceCache.get(serviceName, cachedExternalSevice))
  957. return cachedExternalSevice;
  958. }
  959. StringBuffer output;
  960. try
  961. {
  962. VStringBuffer getServiceCmd("kubectl get svc --selector=server=%s --output=jsonpath={.items[0].status.loadBalancer.ingress[0].hostname},{.items[0].status.loadBalancer.ingress[0].ip},{.items[0].spec.ports[0].port}", serviceName);
  963. runKubectlCommand("get-external-service", getServiceCmd, nullptr, &output);
  964. }
  965. catch (IException *e)
  966. {
  967. EXCLOG(e);
  968. VStringBuffer exceptionText("Failed to get external service for '%s'. Error: [%d, ", serviceName, e->errorCode());
  969. e->errorMessage(exceptionText).append("]");
  970. e->Release();
  971. throw makeStringException(-1, exceptionText);
  972. }
  973. StringArray fields;
  974. fields.appendList(output, ",");
  975. // NB: add even if no result, want non-result to be cached too
  976. std::string host, port;
  977. if (fields.ordinality() == 3) // hostname,ip,port. NB: hostname may be missing, but still present as a blank field
  978. {
  979. host = fields.item(0); // hostname
  980. if (0 == host.length())
  981. host = fields.item(1); // ip
  982. port = fields.item(2);
  983. }
  984. auto servicePair = std::make_pair(host, atoi(port.c_str()));
  985. externalServiceCache.add(serviceName, servicePair);
  986. return servicePair;
  987. }
  988. std::pair<std::string, unsigned> getDafileServiceFromConfig(const char *application)
  989. {
  990. /* NB: For now expect 1 dafilesrv in configuration only
  991. * We could have multiple dafilesrv services with e.g. different specs./replicas etc. that
  992. * serviced different planes. At the moment dafilesrv mounts all data planes.
  993. */
  994. VStringBuffer serviceXPath("services[@type='%s']", application);
  995. Owned<IPropertyTreeIterator> dafilesrvServices = getGlobalConfigSP()->getElements(serviceXPath);
  996. if (!dafilesrvServices->first())
  997. throw makeStringException(-1, "dafilesrv service not defined");
  998. const IPropertyTree &dafilesrv = dafilesrvServices->query();
  999. if (!dafilesrv.getPropBool("@public"))
  1000. throw makeStringException(-1, "dafilesrv service has no public service defined");
  1001. StringBuffer dafilesrvName;
  1002. dafilesrv.getProp("@name", dafilesrvName);
  1003. auto externalService = getExternalService(dafilesrvName);
  1004. if (externalService.first.empty())
  1005. throw makeStringExceptionV(-1, "dafilesrv '%s': external service not found", dafilesrvName.str());
  1006. if (0 == externalService.second)
  1007. throw makeStringExceptionV(-1, "dafilesrv '%s': external service port not defined", dafilesrvName.str());
  1008. return externalService;
  1009. }
  1010. #endif