jmisc.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  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. //#define MINIMALTRACE // a more readable version of the trace file (IMO)
  15. //#define LOGCLOCK
  16. #include "platform.h"
  17. #include "stdio.h"
  18. #include <time.h>
  19. #include "jmisc.hpp"
  20. #include "jmutex.hpp"
  21. #include "jexcept.hpp"
  22. #include "jstring.hpp"
  23. #include "jdebug.hpp"
  24. #ifndef _WIN32
  25. #include <sys/wait.h>
  26. #endif
  27. #ifdef LOGCLOCK
  28. #define MSGFIELD_PRINTLOG MSGFIELD_timeDate | MSGFIELD_msgID | MSGFIELD_process | MSGFIELD_thread | MSGFIELD_code | MSGFIELD_milliTime
  29. #else
  30. #define MSGFIELD_PRINTLOG MSGFIELD_timeDate | MSGFIELD_msgID | MSGFIELD_process | MSGFIELD_thread | MSGFIELD_code
  31. #endif
  32. #define FPRINTF(s) { fprintf(stdlog, "%s", s); \
  33. if (logFile) fprintf(logFile, "%s", s); }
  34. void _rev(size32_t len, void * _ptr)
  35. {
  36. byte * ptr = (byte *)_ptr;
  37. while (len > 1)
  38. {
  39. byte t = *ptr;
  40. *ptr = ptr[--len];
  41. ptr[len] = t;
  42. len--;
  43. ptr++;
  44. }
  45. }
  46. Mutex printMutex;
  47. FILE *logFile;
  48. FILE *stdlog = stderr;
  49. HiresTimer logTimer;
  50. class CStdLogIntercept: public ILogIntercept
  51. {
  52. bool nl;
  53. time_t lastt;
  54. unsigned lasttpid;
  55. unsigned detail;
  56. public:
  57. CStdLogIntercept()
  58. {
  59. lasttpid = 0;
  60. nl = true;
  61. detail = _LOG_TIME | _LOG_PID | _LOG_TID;
  62. #ifdef LOGCLOCK
  63. detail |= _LOG_CLOCK;
  64. #endif
  65. queryStderrLogMsgHandler()->setMessageFields(detail);
  66. }
  67. unsigned setLogDetail(unsigned i)
  68. {
  69. unsigned ret = detail;
  70. detail = i;
  71. return ret;
  72. }
  73. void print(const char *str)
  74. {
  75. if (nl) {
  76. char timeStamp[32];
  77. time_t tNow;
  78. time(&tNow);
  79. #ifdef _WIN32
  80. unsigned tpid = GetCurrentThreadId();
  81. #else
  82. unsigned tpid = getpid();
  83. #endif
  84. #ifdef _WIN32
  85. struct tm ltNow = *localtime(&tNow);
  86. #else
  87. struct tm ltNow;
  88. localtime_r(&tNow, &ltNow);
  89. #endif
  90. #ifdef MINIMALTRACE
  91. if (((detail & _LOG_TID) && tpid!=lasttpid)||((detail & _LOG_TIME) && memcmp(&tNow,&lastt,sizeof(tNow))!=0))
  92. {
  93. lasttpid = tpid;
  94. lastt = tNow;
  95. FPRINTF("[");
  96. if (detail & _LOG_TIME)
  97. {
  98. strftime(timeStamp, 32, "%H:%M:%S ", &ltNow);
  99. FPRINTF(timeStamp);
  100. }
  101. if (detail & _LOG_TID)
  102. {
  103. fprintf(stdlog, "TID=%d ", tpid);
  104. if (logFile)
  105. fprintf(logFile, "TID=%d ", tpid);
  106. }
  107. FPRINTF("]\n");
  108. }
  109. #else
  110. if (detail & _LOG_TIME)
  111. {
  112. strftime(timeStamp, 32, "%m/%d/%y %H:%M:%S ", &ltNow);
  113. FPRINTF(timeStamp);
  114. }
  115. if (detail & _LOG_CLOCK)
  116. {
  117. unsigned t=msTick();
  118. fprintf(stdlog, "%u ", t);
  119. if (logFile)
  120. fprintf(logFile, "%u ", t);
  121. }
  122. if (detail & _LOG_HIRESCLOCK)
  123. {
  124. unsigned clock = usTick();
  125. fprintf(stdlog, " TICK=%u ", clock);
  126. if (logFile)
  127. fprintf(logFile, " TICK=%u ", clock);
  128. }
  129. #if defined(_WIN32)
  130. if (detail & _LOG_TID)
  131. {
  132. fprintf(stdlog, "TID=%d ", GetCurrentThreadId());
  133. if (logFile)
  134. fprintf(logFile, "TID=%d ", GetCurrentThreadId());
  135. }
  136. if (detail & _LOG_PID)
  137. {
  138. fprintf(stdlog, "PID=%d ", GetCurrentProcessId());
  139. if (logFile)
  140. fprintf(logFile, "PID=%d ", GetCurrentProcessId());
  141. }
  142. #else
  143. if (detail & _LOG_PID)
  144. {
  145. fprintf(stdlog, "PID=%d ", getpid());
  146. if (logFile)
  147. fprintf(logFile, "PID=%d ", getpid());
  148. }
  149. #endif
  150. if (detail & (_LOG_PID|_LOG_TID|_LOG_TIME))
  151. {
  152. fprintf(stdlog, "- ");
  153. if (logFile)
  154. fprintf(logFile, "- ");
  155. }
  156. #endif
  157. }
  158. if (str && *str)
  159. {
  160. nl = str[strlen(str)-1]=='\n';
  161. FPRINTF(str);
  162. }
  163. else
  164. nl = false;
  165. fflush(stdlog);
  166. if (logFile)
  167. {
  168. fflush(logFile);
  169. }
  170. }
  171. void close()
  172. {
  173. if (logFile) {
  174. fclose(logFile);
  175. logFile = 0;
  176. }
  177. }
  178. };
  179. static ILogIntercept *logintercept = NULL;
  180. jlib_decl ILogIntercept* interceptLog(ILogIntercept *intercept)
  181. {
  182. ILogIntercept *old=logintercept;
  183. logintercept = intercept;
  184. return old;
  185. }
  186. jlib_decl void openLogFile(StringBuffer & resolvedFS, const char *filename, unsigned detail, bool enterQueueMode, bool append)
  187. {
  188. if(enterQueueMode)
  189. queryLogMsgManager()->enterQueueingMode();
  190. Owned<IComponentLogFileCreator> lf = createComponentLogFileCreator(".", 0);
  191. lf->setCreateAliasFile(false);
  192. lf->setLocal(true);
  193. lf->setRolling(false);
  194. lf->setAppend(append);
  195. lf->setCompleteFilespec(filename);//user specified log filespec
  196. lf->setMaxDetail(detail ? detail : DefaultDetail);
  197. lf->setMsgFields(MSGFIELD_timeDate | MSGFIELD_msgID | MSGFIELD_process | MSGFIELD_thread | MSGFIELD_code);
  198. lf->beginLogging();
  199. resolvedFS.set(lf->queryLogFileSpec());
  200. }
  201. jlib_decl void PrintLogDirect(const char *msg)
  202. {
  203. LOG(MClegacy, unknownJob, "%s", msg);
  204. }
  205. jlib_decl int PrintLog(const char *fmt, ...)
  206. {
  207. va_list args;
  208. va_start(args, fmt);
  209. VALOG(MClegacy, unknownJob, fmt, args);
  210. va_end(args);
  211. return 0;
  212. }
  213. jlib_decl void SPrintLog(const char *fmt, ...)
  214. {
  215. va_list args;
  216. va_start(args, fmt);
  217. VALOG(MClegacy, unknownJob, fmt, args);
  218. va_end(args);
  219. }
  220. StringBuffer &addFileTimestamp(StringBuffer &fname, bool daily)
  221. {
  222. time_t tNow;
  223. time(&tNow);
  224. char timeStamp[32];
  225. #ifdef _WIN32
  226. struct tm *ltNow;
  227. ltNow = localtime(&tNow);
  228. strftime(timeStamp, 32, daily ? ".%Y_%m_%d" : ".%Y_%m_%d_%H_%M_%S", ltNow);
  229. #else
  230. struct tm ltNow;
  231. localtime_r(&tNow, &ltNow);
  232. strftime(timeStamp, 32, daily ? ".%Y_%m_%d" : ".%Y_%m_%d_%H_%M_%S", &ltNow);
  233. #endif
  234. return fname.append(timeStamp);
  235. }
  236. #define MAX_TRY_SIZE 0x8000000 // 256 MB
  237. jlib_decl void PrintMemoryStatusLog()
  238. {
  239. #ifdef _WIN32
  240. MEMORYSTATUS mS;
  241. GlobalMemoryStatus(&mS);
  242. LOG(MCdebugInfo, unknownJob, "Available Physical Memory = %dK", (unsigned)(mS.dwAvailPhys/1024));
  243. #ifdef FRAGMENTATION_CHECK
  244. // see if fragmented
  245. size32_t sz = MAX_TRY_SIZE;
  246. while (sz) {
  247. if (sz<mS.dwAvailPhys/4) {
  248. void *p=malloc(sz);
  249. if (p) {
  250. free(p);
  251. break;
  252. }
  253. }
  254. sz /= 2;
  255. }
  256. sz *= 2;
  257. if ((sz<MAX_TRY_SIZE)&&(sz<mS.dwAvailPhys/4)) {
  258. LOG(MCdebugInfo, unknownJob, "WARNING: Could not allocate block size %d", sz);
  259. _HEAPINFO hinfo;
  260. int heapstatus;
  261. hinfo._pentry = NULL;
  262. size32_t max=0;
  263. unsigned fragments=0;
  264. while( ( heapstatus = _heapwalk( &hinfo ) ) == _HEAPOK ) {
  265. if (hinfo._useflag != _USEDENTRY) {
  266. if (hinfo._size>max)
  267. max = hinfo._size;
  268. fragments++;
  269. }
  270. }
  271. LOG(MCdebugInfo, unknownJob, "Largest unused fragment = %d", max);
  272. LOG(MCdebugInfo, unknownJob, "Number of fragments = %d", fragments);
  273. }
  274. #endif
  275. #endif
  276. }
  277. static class _init_jmisc_class
  278. {
  279. public:
  280. ~_init_jmisc_class()
  281. {
  282. if (logFile)
  283. fclose(logFile);
  284. }
  285. } _init_jmisc;
  286. FILE *xfopen(const char *path, const char *mode)
  287. {
  288. char *s, *e, *p;
  289. p = s = strdup(path);
  290. e = s+strlen(path);
  291. bool alt=false;
  292. for (; p<e; p++)
  293. {
  294. #ifdef _WIN32
  295. if (*p == '/')
  296. #else
  297. if (*p == '\\')
  298. #endif
  299. {
  300. alt = true;
  301. *p = PATHSEPCHAR;
  302. }
  303. }
  304. if (alt)
  305. printf("XFOPEN ALTERED FILENAME FROM:%s TO %s\n", path, s);
  306. FILE *file = ::fopen(s, mode);
  307. free(s);
  308. return file;
  309. }
  310. const char * queryCcLogName()
  311. {
  312. const char* logFileName = getenv("ECL_CCLOG");
  313. if (!logFileName)
  314. {
  315. logFileName = "cclog.txt";
  316. }
  317. return logFileName;
  318. //More does output get redirected here?
  319. }
  320. StringBuffer& queryCcLogName(const char* wuid, StringBuffer& logname)
  321. {
  322. if(!wuid || !*wuid)
  323. logname.append(queryCcLogName());
  324. else
  325. {
  326. const char* presetName = getenv("ECL_CCLOG");
  327. if (presetName && *presetName)
  328. logname.append(presetName);
  329. else
  330. logname.append(wuid).trim().append(".cclog.txt");
  331. }
  332. return logname;
  333. }
  334. jlib_decl char* readarg(char*& curptr)
  335. {
  336. char *cur = curptr;
  337. if(cur == NULL || *cur == 0)
  338. return NULL;
  339. while(*cur == ' ' || *cur == '\t') {
  340. cur++;
  341. if (!*cur) {
  342. curptr = cur;
  343. return NULL;
  344. }
  345. }
  346. char quote = 0;
  347. if(*cur == '\'' || *cur == '"')
  348. {
  349. quote = *cur;
  350. cur++;
  351. }
  352. char *ret = cur;
  353. if (quote)
  354. {
  355. while (*cur && *cur!=quote)
  356. cur++;
  357. }
  358. else
  359. {
  360. do {
  361. cur++;
  362. } while(*cur && *cur != ' ' && *cur != '\t');
  363. }
  364. if(*cur != 0)
  365. {
  366. *cur = 0;
  367. cur++;
  368. }
  369. curptr = cur;
  370. return ret;
  371. }
  372. #ifdef _WIN32
  373. bool invoke_program(const char *command_line, DWORD &runcode, bool wait, const char *outfile, HANDLE *rethandle, bool throwException)
  374. {
  375. runcode = 0;
  376. if (rethandle)
  377. *rethandle = 0;
  378. if(!command_line || !*command_line)
  379. return false;
  380. STARTUPINFO StartupInfo;
  381. SECURITY_ATTRIBUTES security;
  382. _clear(security);
  383. security.nLength = sizeof(security);
  384. security.bInheritHandle = TRUE;
  385. _clear(StartupInfo);
  386. StartupInfo.cb = sizeof(StartupInfo);
  387. HANDLE outh = NULL;
  388. if (outfile&&*outfile) {
  389. outh = CreateFile(outfile,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,&security,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  390. if (outh == INVALID_HANDLE_VALUE)
  391. {
  392. ERRLOG("Cannot create file '%s' error code %d",outfile,(int)GetLastError());
  393. return false;
  394. }
  395. }
  396. if (outh) {
  397. if (!DuplicateHandle(GetCurrentProcess(),outh,GetCurrentProcess(),&StartupInfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS) ||
  398. !DuplicateHandle(GetCurrentProcess(),outh,GetCurrentProcess(),&StartupInfo.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS))
  399. {
  400. ERRLOG("Execution of \"%s\" failed, DuplicateHandle error = %d",command_line, (int)GetLastError());
  401. CloseHandle(outh);
  402. return false;
  403. }
  404. StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  405. StartupInfo.dwFlags = STARTF_USESTDHANDLES;
  406. CloseHandle(outh);
  407. }
  408. PROCESS_INFORMATION ProcessInformation;
  409. bool ok = CreateProcess(NULL,(char *)command_line,NULL,NULL,TRUE,0,NULL,NULL,&StartupInfo,&ProcessInformation)!=0;
  410. if (ok) {
  411. if (wait)
  412. {
  413. WaitForSingleObject(ProcessInformation.hProcess,INFINITE);
  414. GetExitCodeProcess(ProcessInformation.hProcess, &runcode);
  415. }
  416. CloseHandle(ProcessInformation.hThread);
  417. if (rethandle) {
  418. *rethandle = (HANDLE)ProcessInformation.hProcess;
  419. if (wait)
  420. CloseHandle(ProcessInformation.hProcess);
  421. }
  422. else
  423. CloseHandle(ProcessInformation.hProcess);
  424. }
  425. else
  426. {
  427. int lastError = (int)GetLastError(); // for debugging
  428. //print out why create process failed
  429. ERRLOG("Execution of \"%s\" failed, error = %d",command_line,lastError);
  430. }
  431. if (outh)
  432. {
  433. CloseHandle(StartupInfo.hStdOutput);
  434. CloseHandle(StartupInfo.hStdError);
  435. }
  436. return ok;
  437. }
  438. bool wait_program(HANDLE handle,DWORD &runcode,bool block)
  439. {
  440. runcode = (DWORD)-1;
  441. if ((handle==NULL)||(handle==(HANDLE)-1))
  442. return true; // actually it failed
  443. int ret = WaitForSingleObject(handle,block?INFINITE:0);
  444. int err = GetLastError();
  445. if (ret==WAIT_OBJECT_0) {
  446. GetExitCodeProcess(handle, &runcode);
  447. CloseHandle(handle);
  448. return true;
  449. }
  450. return false;
  451. }
  452. jlib_decl bool interrupt_program(HANDLE handle,int signum)
  453. {
  454. if (signum==-9)
  455. return TerminateProcess(handle,1)!=FALSE;
  456. ERRLOG("interrupt_program signal %d not supported in windows",signum);
  457. return false;
  458. }
  459. void close_program(HANDLE handle)
  460. {
  461. CloseHandle(handle);
  462. }
  463. #else
  464. bool invoke_program(const char *command_line, DWORD &runcode, bool wait, const char *outfile, HANDLE *rethandle, bool throwException)
  465. {
  466. runcode = 0;
  467. if (rethandle)
  468. *rethandle = 0;
  469. if(!command_line || !*command_line)
  470. return false;
  471. pid_t pid = fork();
  472. if (pid == 0)
  473. {
  474. if (outfile&&*outfile) {
  475. int outh = open(outfile, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
  476. if(outh > 0)
  477. {
  478. dup2(outh, STDOUT_FILENO);
  479. dup2(outh, STDERR_FILENO);
  480. close(outh);
  481. }
  482. else {
  483. ERRLOG("invoke_program: could not open %s",outfile);
  484. return false;
  485. }
  486. }
  487. int size = 10;
  488. char **args;
  489. char **argptr = args = (char **) malloc(sizeof(args) * size);
  490. char **over = argptr+size;
  491. char *cl = strdup(command_line);
  492. char *curptr = cl;
  493. *argptr = readarg(curptr);
  494. while (*argptr != NULL)
  495. {
  496. argptr++;
  497. if (argptr==over)
  498. {
  499. args = (char **) realloc(args, sizeof(args) * size * 2);
  500. argptr = args+size;
  501. size *= 2;
  502. over = args+size;
  503. }
  504. *argptr = readarg(curptr);
  505. }
  506. // JCSMORE - do I not need to free args, if successful exec?
  507. if (execvp(*args, args))
  508. {
  509. //print out why create process failed
  510. int err = errno;
  511. ERRLOG("invoke_program(%s) failed with error(%d): %s",command_line,err,strerror(err));
  512. }
  513. assertex(0); // don't expect to get here!
  514. _exit(0);
  515. }
  516. else if (pid < 0)
  517. {
  518. StringBuffer s("fork of \"");
  519. s.append(command_line).append("\" failed. fork() returned:");
  520. if (errno == EAGAIN)
  521. s.append("EAGAIN");
  522. else if (errno == ENOMEM)
  523. s.append("ENOMEM");
  524. else
  525. s.append(errno);
  526. ERRLOG("%s",s.toCharArray());
  527. if(throwException)
  528. throw MakeStringException(-1, "%s", s.str());
  529. return false;
  530. }
  531. if (rethandle)
  532. *rethandle = (HANDLE)pid;
  533. if (wait)
  534. {
  535. int retv;
  536. if (waitpid(pid, &retv, 0) != pid) {
  537. ERRLOG("invoke_program(%s): wait failed (%d)",command_line,(int)pid);
  538. return false;
  539. }
  540. if (!WIFEXITED(retv)) //did not exit normally
  541. {
  542. int err = errno;
  543. ERRLOG("invoke_program(%s): failed.",command_line);
  544. ERRLOG("The process was killed by signal %d%s.",(int)WTERMSIG(retv),WCOREDUMP(retv)?" - core dumped":"");
  545. ERRLOG("Last system error is %s",strerror(err));
  546. }
  547. runcode = WEXITSTATUS(retv);
  548. }
  549. return true; // it did run even if signalled
  550. }
  551. bool wait_program(HANDLE handle,DWORD &runcode,bool block)
  552. {
  553. pid_t pid = (pid_t)handle;
  554. runcode = (DWORD)-1;
  555. if ((int)pid<=0)
  556. return true; // actually it failed
  557. int retv;
  558. pid_t ret = waitpid(pid, &retv, block?0:WNOHANG);
  559. if (ret == (pid_t)-1) {
  560. ERRLOG("wait_program: wait failed (%d)",errno);
  561. return true; // actually it failed but assume finished
  562. }
  563. else if (ret==0)
  564. return false;
  565. runcode = WEXITSTATUS(retv);
  566. return true;
  567. }
  568. bool interrupt_program(HANDLE handle,int signum)
  569. {
  570. pid_t pid = (pid_t)handle;
  571. if ((int)pid<=0)
  572. return false;
  573. return (kill(pid, signum)==0);
  574. }
  575. void close_program(HANDLE handle)
  576. {
  577. // not needed in linux
  578. }
  579. #endif
  580. #ifndef _WIN32
  581. bool CopyFile(const char *file, const char *newfile, bool fail)
  582. {
  583. struct stat s;
  584. if ((fail) && (0 == stat((char *)newfile, &s))) return false;
  585. FILE *in=fopen(file,"rb"), *out=fopen(newfile,"wb");
  586. try
  587. {
  588. if (!in)
  589. throw MakeStringException(-1, "failed to open %s for copy",file);
  590. if (!out)
  591. throw MakeStringException(-1, "failed to create %s",newfile);
  592. char b[1024];
  593. while (true)
  594. {
  595. int c=fread(b,1,sizeof(b),in);
  596. if (!c) break;
  597. if (!fwrite(b,c,1,out)) throw MakeStringException(-1, "failed to copy file %s to %s",file,newfile);
  598. }
  599. fclose(in);
  600. fclose(out);
  601. stat((char *)file, &s);
  602. chmod(newfile, s.st_mode);
  603. } catch (...)
  604. {
  605. if (in) fclose(in);
  606. if (out) fclose(out);
  607. return false;
  608. }
  609. return true;
  610. }
  611. #endif
  612. //========================================================================================================================
  613. static bool hadAbortSignal = false;
  614. static bool handlerInstalled = false;
  615. CriticalSection abortCrit;
  616. class AbortHandlerInfo : public CInterface
  617. {
  618. public:
  619. ThreadId installer;
  620. AbortHandler handler;
  621. IAbortHandler *ihandler;
  622. AbortHandlerInfo(AbortHandler _handler)
  623. {
  624. handler = _handler;
  625. ihandler = NULL;
  626. installer = GetCurrentThreadId();
  627. }
  628. AbortHandlerInfo(IAbortHandler *_ihandler)
  629. {
  630. handler = NULL;
  631. ihandler = _ihandler;
  632. installer = GetCurrentThreadId();
  633. }
  634. bool handle()
  635. {
  636. #ifndef _WIN32
  637. if (installer == GetCurrentThreadId())
  638. #endif
  639. {
  640. // DBGLOG("handle abort %x", GetCurrentThreadId());
  641. if (handler)
  642. return handler();
  643. else
  644. return ihandler->onAbort();
  645. }
  646. #ifndef _WIN32
  647. else
  648. return false;
  649. #endif
  650. }
  651. };
  652. CIArrayOf<AbortHandlerInfo> handlers;
  653. bool notifyOnAbort()
  654. {
  655. // DBGLOG("notifyOnAbort %x", GetCurrentThreadId());
  656. // CriticalBlock c(abortCrit); You would think that this was needed, but it locks up.
  657. // If it needs to be threadsafe, have to clone the list or something
  658. bool doExit = false;
  659. ForEachItemInRev(idx, handlers)
  660. {
  661. if (handlers.item(idx).handle())
  662. doExit = true;
  663. }
  664. // DBGLOG("notifyOnAbort returning %d", (int) doExit);
  665. return doExit;
  666. }
  667. #ifdef _WIN32
  668. BOOL WINAPI WindowsAbortHandler ( DWORD dwCtrlType )
  669. {
  670. switch( dwCtrlType )
  671. {
  672. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  673. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  674. case CTRL_CLOSE_EVENT:
  675. {
  676. hadAbortSignal = true;
  677. bool doExit = notifyOnAbort();
  678. return !doExit;
  679. }
  680. case CTRL_LOGOFF_EVENT:
  681. case CTRL_SHUTDOWN_EVENT:
  682. hadAbortSignal = true;
  683. notifyOnAbort();
  684. return FALSE;
  685. }
  686. return FALSE;
  687. }
  688. BOOL WINAPI ModuleExitHandler ( 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. case CTRL_LOGOFF_EVENT:
  696. case CTRL_SHUTDOWN_EVENT:
  697. ExitModuleObjects();
  698. }
  699. return FALSE;
  700. }
  701. #else
  702. static void UnixAbortHandler(int sig)
  703. {
  704. hadAbortSignal = true;
  705. if (handlers.length()==0 || notifyOnAbort())
  706. {
  707. _exit(0);
  708. }
  709. }
  710. #endif
  711. void queryInstallAbortHandler()
  712. {
  713. if (handlerInstalled)
  714. return;
  715. #if defined(_WIN32)
  716. SetConsoleCtrlHandler( WindowsAbortHandler, TRUE );
  717. #elif defined(__linux__)
  718. struct sigaction action;
  719. sigemptyset(&action.sa_mask);
  720. action.sa_flags = SA_RESTART;
  721. action.sa_handler = (void(*)(int))UnixAbortHandler;
  722. if (sigaction(SIGINT, &action, NULL) == -1 ||
  723. sigaction(SIGQUIT, &action, NULL) == -1 ||
  724. sigaction(SIGTERM, &action, NULL) == -1)
  725. {
  726. perror("sigaction in queryInstallAbortHandler failed");
  727. }
  728. #endif
  729. handlerInstalled = true;
  730. }
  731. void queryUninstallAbortHandler()
  732. {
  733. if (handlers.ordinality())
  734. return;
  735. #if defined(_WIN32)
  736. if (handlerInstalled)
  737. {
  738. SetConsoleCtrlHandler( WindowsAbortHandler, FALSE);
  739. handlerInstalled = false;
  740. }
  741. #else
  742. // Don't uninstall - we always want one for the module exit support
  743. #endif
  744. }
  745. MODULE_INIT(INIT_PRIORITY_JMISC2)
  746. {
  747. #if defined(_WIN32)
  748. // NOTE: handlers are called in LIFO order and hence any handler that returns false
  749. // (e.g CTRL-C not wanting to abort)) will stop this handler being called also (correctly).
  750. SetConsoleCtrlHandler( ModuleExitHandler, TRUE);
  751. #elif defined(__linux__)
  752. queryInstallAbortHandler();
  753. #endif
  754. return true;
  755. }
  756. void addAbortHandler(AbortHandler handler)
  757. {
  758. CriticalBlock c(abortCrit);
  759. queryInstallAbortHandler();
  760. handlers.append(*new AbortHandlerInfo(handler));
  761. }
  762. void addAbortHandler(IAbortHandler & handler)
  763. {
  764. CriticalBlock c(abortCrit);
  765. queryInstallAbortHandler();
  766. handlers.append(*new AbortHandlerInfo(&handler));
  767. }
  768. void removeAbortHandler(AbortHandler handler)
  769. {
  770. CriticalBlock c(abortCrit);
  771. ForEachItemInRev(idx, handlers)
  772. {
  773. if (handlers.item(idx).handler == handler)
  774. {
  775. handlers.remove(idx);
  776. break;
  777. }
  778. }
  779. queryUninstallAbortHandler();
  780. }
  781. void removeAbortHandler(IAbortHandler & handler)
  782. {
  783. CriticalBlock c(abortCrit);
  784. ForEachItemInRev(idx, handlers)
  785. {
  786. if (handlers.item(idx).ihandler == &handler)
  787. {
  788. handlers.remove(idx);
  789. break;
  790. }
  791. }
  792. queryUninstallAbortHandler();
  793. }
  794. bool isAborting()
  795. {
  796. return hadAbortSignal;
  797. }
  798. void throwAbortException()
  799. {
  800. throw MakeStringException(JLIBERR_UserAbort, "Operation aborted by user");
  801. }
  802. void throwExceptionIfAborting()
  803. {
  804. if (isAborting())
  805. throwAbortException();
  806. }
  807. //========================================================================================================================
  808. StringBuffer & hexdump2string(byte const * in, size32_t inSize, StringBuffer & out)
  809. {
  810. out.append("[");
  811. byte last;
  812. unsigned seq = 1;
  813. for(unsigned i=0; i<inSize; ++i)
  814. {
  815. if((i>0) && (in[i]==last))
  816. {
  817. ++seq;
  818. }
  819. else
  820. {
  821. if(seq>1)
  822. {
  823. if(seq==2)
  824. out.appendf(" %02X", last);
  825. else
  826. out.appendf("x%u", seq);
  827. seq = 1;
  828. }
  829. out.appendf(" %02X", in[i]);
  830. last = in[i];
  831. }
  832. }
  833. if(seq>1)
  834. out.appendf("x%u", seq);
  835. out.append(" ]");
  836. return out;
  837. }