ECLogger.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <kopano/ECLogger.h>
  19. #include <kopano/lockhelper.hpp>
  20. #include <mutex>
  21. #include <cassert>
  22. #include <clocale>
  23. #include <pthread.h>
  24. #include <cstdarg>
  25. #include <csignal>
  26. #include <zlib.h>
  27. #include <kopano/stringutil.h>
  28. #include "charset/localeutil.h"
  29. #include "config.h"
  30. #include <poll.h>
  31. #if HAVE_SYSLOG_H
  32. #include <syslog.h>
  33. #endif
  34. #include <grp.h>
  35. #include <libgen.h>
  36. #include <pwd.h>
  37. #include <sys/resource.h>
  38. #include <sys/time.h>
  39. #include <sys/types.h>
  40. #include <sys/utsname.h>
  41. #include <kopano/ECConfig.h>
  42. using namespace std;
  43. namespace KC {
  44. static const char *const ll_names[] = {
  45. " notice",
  46. "crit ",
  47. "error ",
  48. "warning",
  49. "notice ",
  50. "info ",
  51. "debug "
  52. };
  53. static ECLogger_File ec_log_fallback_target(EC_LOGLEVEL_WARNING, false, "-", false);
  54. static ECLogger *ec_log_target = &ec_log_fallback_target;
  55. ECLogger::ECLogger(int max_ll) {
  56. max_loglevel = max_ll;
  57. // get system locale for time, NULL is returned if locale was not found.
  58. timelocale = createlocale(LC_TIME, "C");
  59. datalocale = createUTF8Locale();
  60. prefix = LP_NONE;
  61. m_ulRef = 1;
  62. }
  63. ECLogger::~ECLogger() {
  64. if (ec_log_target == this)
  65. ec_log_set(NULL);
  66. if (timelocale)
  67. freelocale(timelocale);
  68. if (datalocale)
  69. freelocale(datalocale);
  70. }
  71. void ECLogger::SetLoglevel(unsigned int max_ll) {
  72. max_loglevel = max_ll;
  73. }
  74. std::string ECLogger::MakeTimestamp() {
  75. time_t now = time(NULL);
  76. tm local;
  77. localtime_r(&now, &local);
  78. char buffer[_LOG_TSSIZE];
  79. if (timelocale)
  80. strftime_l(buffer, sizeof buffer, "%c", &local, timelocale);
  81. else
  82. strftime(buffer, sizeof buffer, "%c", &local);
  83. return buffer;
  84. }
  85. bool ECLogger::Log(unsigned int loglevel) {
  86. unsigned int ext_bits = loglevel & EC_LOGLEVEL_EXTENDED_MASK;
  87. unsigned int level = loglevel & EC_LOGLEVEL_MASK;
  88. unsigned int max_loglevel_only = max_loglevel & EC_LOGLEVEL_MASK;
  89. unsigned int allowed_extended_bits_only = max_loglevel & EC_LOGLEVEL_EXTENDED_MASK;
  90. // any extended bits used? then only match those
  91. if (ext_bits) {
  92. // are any of the extended bits that were selected in the max_loglevel
  93. // set in the loglevel?
  94. if (ext_bits & allowed_extended_bits_only)
  95. return true;
  96. return false;
  97. }
  98. // continue if the maximum level of logging (so not the bits)
  99. // is not 0
  100. if (max_loglevel_only == EC_LOGLEVEL_NONE)
  101. return false;
  102. if (level == EC_LOGLEVEL_ALWAYS)
  103. return true;
  104. // is the parameter log-level <= max_loglevel?
  105. return level <= max_loglevel_only;
  106. }
  107. void ECLogger::SetLogprefix(logprefix lp) {
  108. prefix = lp;
  109. }
  110. unsigned int ECLogger::AddRef(void)
  111. {
  112. scoped_lock locker(m_mutex);
  113. assert(m_ulRef < UINT_MAX);
  114. return ++m_ulRef;
  115. }
  116. unsigned ECLogger::Release() {
  117. ulock_normal locker(m_mutex);
  118. unsigned int ulRef = --m_ulRef;
  119. locker.unlock();
  120. if (ulRef == 0)
  121. delete this;
  122. return ulRef;
  123. }
  124. int ECLogger::snprintf(char *str, size_t size, const char *format, ...) {
  125. va_list va;
  126. int len = 0;
  127. va_start(va, format);
  128. len = _vsnprintf_l(str, size, format, datalocale, va);
  129. va_end(va);
  130. return len;
  131. }
  132. ECLogger_Null::ECLogger_Null() : ECLogger(EC_LOGLEVEL_NONE) {}
  133. void ECLogger_Null::Reset() {}
  134. void ECLogger_Null::Log(unsigned int loglevel, const string &message) {}
  135. void ECLogger_Null::Log(unsigned int loglevel, const char *format, ...) {}
  136. void ECLogger_Null::LogVA(unsigned int loglevel, const char *format, va_list& va) {}
  137. /**
  138. * @param[in] max_ll max loglevel passed to ECLogger
  139. * @param[in] add_timestamp true if a timestamp before the logmessage is wanted
  140. * @param[in] filename filename of log in current locale
  141. */
  142. ECLogger_File::ECLogger_File(const unsigned int max_ll, const bool add_timestamp, const char *const filename, const bool compress) : ECLogger(max_ll) {
  143. logname = filename;
  144. timestamp = add_timestamp;
  145. if (logname == "-") {
  146. init_for_stderr();
  147. } else {
  148. if (compress)
  149. init_for_gzfile();
  150. else
  151. init_for_file();
  152. log = fnOpen(logname.c_str(), szMode);
  153. if (log == nullptr) {
  154. init_for_stderr();
  155. fnPrintf(log, "Unable to open logfile %s: %s. Logging to stderr.\n",
  156. logname.c_str(), strerror(errno));
  157. }
  158. }
  159. reinit_buffer(0);
  160. // read/write is for the handle, not the f*-calls
  161. // so read is because we're only reading the handle ('log')
  162. // so only Reset() will do a write-lock
  163. prevcount = 0;
  164. prevmsg.clear();
  165. prevloglevel = 0;
  166. }
  167. ECLogger_File::~ECLogger_File() {
  168. // not required at this stage but only added here for consistency
  169. KC::shared_lock<KC::shared_mutex> lh(handle_lock);
  170. if (prevcount > 1)
  171. fnPrintf(log, "%sPrevious message logged %d times\n", DoPrefix().c_str(), prevcount);
  172. if (log && fnClose)
  173. fnClose(log);
  174. }
  175. void ECLogger_File::init_for_stderr(void)
  176. {
  177. log = stderr;
  178. fnOpen = nullptr;
  179. fnClose = nullptr;
  180. fnPrintf = reinterpret_cast<printf_func>(&fprintf);
  181. fnFileno = reinterpret_cast<fileno_func>(&fileno);
  182. szMode = nullptr;
  183. }
  184. void ECLogger_File::init_for_file(void)
  185. {
  186. fnOpen = reinterpret_cast<open_func>(&fopen);
  187. fnClose = reinterpret_cast<close_func>(&fclose);
  188. fnPrintf = reinterpret_cast<printf_func>(&fprintf);
  189. fnFileno = reinterpret_cast<fileno_func>(&fileno);
  190. szMode = "a";
  191. }
  192. void ECLogger_File::init_for_gzfile(void)
  193. {
  194. fnOpen = reinterpret_cast<open_func>(&gzopen);
  195. fnClose = reinterpret_cast<close_func>(&gzclose);
  196. fnPrintf = reinterpret_cast<printf_func>(&gzprintf);
  197. fnFileno = nullptr;
  198. szMode = "wb";
  199. }
  200. void ECLogger_File::reinit_buffer(size_t size)
  201. {
  202. if (size == 0)
  203. setvbuf(static_cast<FILE *>(log), NULL, _IOLBF, size);
  204. else
  205. setvbuf(static_cast<FILE *>(log), NULL, _IOFBF, size);
  206. /* Store value for Reset() to re-invoke this function after reload */
  207. buffer_size = size;
  208. }
  209. void ECLogger_File::Reset() {
  210. std::lock_guard<KC::shared_mutex> lh(handle_lock);
  211. if (log == stderr || fnClose == nullptr || fnOpen == nullptr)
  212. return;
  213. if (log)
  214. fnClose(log);
  215. /*
  216. * The fnOpen call cannot be reordered before fnClose in all cases —
  217. * like compressed files, as the data stream may not be
  218. * finalized.
  219. */
  220. log = fnOpen(logname.c_str(), szMode);
  221. if (log == nullptr) {
  222. init_for_stderr();
  223. fnPrintf(log, "%s%sECLogger reset issued, but cannot (re-)open %s: %s. Logging to stderr.\n",
  224. DoPrefix().c_str(), EmitLevel(EC_LOGLEVEL_ERROR).c_str(),
  225. logname.c_str(), strerror(errno));
  226. return;
  227. }
  228. reinit_buffer(buffer_size);
  229. }
  230. int ECLogger_File::GetFileDescriptor() {
  231. KC::shared_lock<KC::shared_mutex> lh(handle_lock);
  232. if (log && fnFileno)
  233. return fnFileno(log);
  234. return -1;
  235. }
  236. std::string ECLogger_File::EmitLevel(const unsigned int loglevel) {
  237. if (loglevel == EC_LOGLEVEL_ALWAYS)
  238. return format("[%s] ", ll_names[0]);
  239. else if (loglevel <= EC_LOGLEVEL_DEBUG)
  240. return format("[%s] ", ll_names[loglevel]);
  241. return format("[%7x] ", loglevel);
  242. }
  243. /**
  244. * Prints the optional timestamp and prefix to the log.
  245. */
  246. std::string ECLogger_File::DoPrefix() {
  247. std::string out;
  248. if (timestamp)
  249. out += MakeTimestamp() + ": ";
  250. if (prefix == LP_TID) {
  251. #ifdef HAVE_PTHREAD_GETNAME_NP
  252. pthread_t th = pthread_self();
  253. char name[32] = { 0 };
  254. if (pthread_getname_np(th, name, sizeof name))
  255. out += format("[0x%lx] ", kc_threadid());
  256. else
  257. out += format("[%s|0x%lx] ", name, kc_threadid());
  258. #else
  259. out += format("[0x%lx] ", kc_threadid());
  260. #endif
  261. }
  262. else if (prefix == LP_PID) {
  263. out += format("[%5d] ", getpid());
  264. }
  265. return out;
  266. }
  267. /**
  268. * Check if the ECLogger_File instance is logging to stderr.
  269. * @retval true This instance is logging to stderr.
  270. * @retval false This instance is not logging to stderr.
  271. */
  272. bool ECLogger_File::IsStdErr() {
  273. return logname == "-";
  274. }
  275. bool ECLogger_File::DupFilter(const unsigned int loglevel, const std::string &message) {
  276. bool exit_with_true = false;
  277. KC::shared_lock<KC::shared_mutex> lr_dup(dupfilter_lock);
  278. if (prevmsg == message) {
  279. ++prevcount;
  280. if (prevcount < 100)
  281. exit_with_true = true;
  282. }
  283. lr_dup.unlock();
  284. if (exit_with_true)
  285. return true;
  286. if (prevcount > 1) {
  287. KC::shared_lock<KC::shared_mutex> lr_handle(handle_lock);
  288. fnPrintf(log, "%s%sPrevious message logged %d times\n", DoPrefix().c_str(), EmitLevel(prevloglevel).c_str(), prevcount);
  289. }
  290. std::lock_guard<KC::shared_mutex> lw_dup(dupfilter_lock);
  291. prevloglevel = loglevel;
  292. prevmsg = message;
  293. prevcount = 0;
  294. return false;
  295. }
  296. void ECLogger_File::Log(unsigned int loglevel, const string &message) {
  297. if (!ECLogger::Log(loglevel))
  298. return;
  299. if (DupFilter(loglevel, message))
  300. return;
  301. KC::shared_lock<KC::shared_mutex> lh(handle_lock);
  302. if (log == nullptr)
  303. return;
  304. fnPrintf(log, "%s%s%s\n", DoPrefix().c_str(), EmitLevel(loglevel).c_str(), message.c_str());
  305. /*
  306. * If IOLBF was set (buffer_size==0), the previous
  307. * print call already flushed it. Do not flush again
  308. * in that case.
  309. */
  310. if (buffer_size > 0 && (loglevel <= EC_LOGLEVEL_WARNING || loglevel == EC_LOGLEVEL_ALWAYS))
  311. fflush((FILE *)log);
  312. }
  313. void ECLogger_File::Log(unsigned int loglevel, const char *format, ...) {
  314. va_list va;
  315. if (ECLogger::Log(loglevel)) {
  316. va_start(va, format);
  317. LogVA(loglevel, format, va);
  318. va_end(va);
  319. }
  320. }
  321. void ECLogger_File::LogVA(unsigned int loglevel, const char *format, va_list& va) {
  322. char msgbuffer[_LOG_BUFSIZE];
  323. _vsnprintf_l(msgbuffer, sizeof msgbuffer, format, datalocale, va);
  324. Log(loglevel, std::string(msgbuffer));
  325. }
  326. const int ECLogger_Syslog::levelmap[16] = {
  327. /* EC_LOGLEVEL_NONE */ LOG_DEBUG,
  328. /* EC_LOGLEVEL_CRIT */ LOG_CRIT,
  329. /* EC_LOGLEVEL_ERROR */ LOG_WARNING,
  330. /* EC_LOGLEVEL_WARNING */ LOG_WARNING,
  331. /* EC_LOGLEVEL_NOTICE */ LOG_NOTICE,
  332. /* EC_LOGLEVEL_INFO */ LOG_INFO,
  333. /* EC_LOGLEVEL_DEBUG */ LOG_DEBUG,
  334. /* 7-14 */ 0,0,0,0,0,0,0,0,
  335. /* EC_LOGLEVEL_ALWAYS */ LOG_ALERT,
  336. };
  337. ECLogger_Syslog::ECLogger_Syslog(unsigned int max_ll, const char *ident, int facility) : ECLogger(max_ll) {
  338. /*
  339. * Because @ident can go away, and openlog(3) does not make a copy for
  340. * itself >:-((, we have to do it.
  341. */
  342. if (ident == NULL) {
  343. m_ident = NULL;
  344. openlog(ident, LOG_PID, facility);
  345. } else {
  346. m_ident = strdup(ident);
  347. openlog(m_ident, LOG_PID, facility);
  348. }
  349. }
  350. ECLogger_Syslog::~ECLogger_Syslog() {
  351. closelog();
  352. free(m_ident);
  353. }
  354. void ECLogger_Syslog::Reset() {
  355. // not needed.
  356. }
  357. void ECLogger_Syslog::Log(unsigned int loglevel, const string &message) {
  358. if (!ECLogger::Log(loglevel))
  359. return;
  360. syslog(levelmap[loglevel & EC_LOGLEVEL_MASK], "%s", message.c_str());
  361. }
  362. void ECLogger_Syslog::Log(unsigned int loglevel, const char *format, ...) {
  363. va_list va;
  364. if (!ECLogger::Log(loglevel))
  365. return;
  366. va_start(va, format);
  367. LogVA(loglevel, format, va);
  368. va_end(va);
  369. }
  370. void ECLogger_Syslog::LogVA(unsigned int loglevel, const char *format, va_list& va) {
  371. #if HAVE_VSYSLOG
  372. vsyslog(levelmap[loglevel & EC_LOGLEVEL_MASK], format, va);
  373. #else
  374. char msgbuffer[_LOG_BUFSIZE];
  375. _vsnprintf_l(msgbuffer, sizeof msgbuffer, format, datalocale, va);
  376. syslog(levelmap[loglevel & EC_LOGLEVEL_MASK], "%s", msgbuffer);
  377. #endif
  378. }
  379. ECLogger_Tee::ECLogger_Tee(): ECLogger(EC_LOGLEVEL_DEBUG) {
  380. }
  381. /**
  382. * The destructor calls Release on each attached logger so
  383. * they'll be deleted if it was the last reference.
  384. */
  385. ECLogger_Tee::~ECLogger_Tee(void)
  386. {
  387. for (auto log : m_loggers)
  388. log->Release();
  389. }
  390. /**
  391. * Reset all loggers attached to this logger.
  392. */
  393. void ECLogger_Tee::Reset(void)
  394. {
  395. for (auto log : m_loggers)
  396. log->Reset();
  397. }
  398. /**
  399. * Check if anything would be logged with the requested loglevel.
  400. * Effectively this call is delegated to all attached loggers until
  401. * one logger is found that returns true.
  402. *
  403. * @param[in] loglevel The loglevel to test.
  404. *
  405. * @retval true when at least one of the attached loggers would produce output
  406. */
  407. bool ECLogger_Tee::Log(unsigned int loglevel)
  408. {
  409. bool bResult = false;
  410. for (auto log : m_loggers)
  411. bResult = log->Log(loglevel);
  412. return bResult;
  413. }
  414. /**
  415. * Log a message at the reuiqred loglevel to all attached loggers.
  416. *
  417. * @param[in] loglevel The requierd loglevel
  418. * @param[in] message The message to log
  419. */
  420. void ECLogger_Tee::Log(unsigned int loglevel, const std::string &message)
  421. {
  422. for (auto log : m_loggers)
  423. log->Log(loglevel, message);
  424. }
  425. /**
  426. * Log a formatted message (printf style) to all attached loggers.
  427. *
  428. * @param[in] loglevel The required loglevel
  429. * @param[in] format The format string.
  430. */
  431. void ECLogger_Tee::Log(unsigned int loglevel, const char *format, ...) {
  432. va_list va;
  433. va_start(va, format);
  434. LogVA(loglevel, format, va);
  435. va_end(va);
  436. }
  437. void ECLogger_Tee::LogVA(unsigned int loglevel, const char *format, va_list &va)
  438. {
  439. char msgbuffer[_LOG_BUFSIZE];
  440. _vsnprintf_l(msgbuffer, sizeof msgbuffer, format, datalocale, va);
  441. for (auto log : m_loggers)
  442. log->Log(loglevel, std::string(msgbuffer));
  443. }
  444. /**
  445. * Add a logger to the list of loggers to log to.
  446. * @note The passed loggers reference will be increased, so
  447. * make sure to release the logger in the caller function
  448. * if it's going to be 'forgotten' there.
  449. *
  450. * @param[in] lpLogger The logger to attach.
  451. */
  452. void ECLogger_Tee::AddLogger(ECLogger *lpLogger) {
  453. if (lpLogger == nullptr)
  454. return;
  455. lpLogger->AddRef();
  456. m_loggers.push_back(lpLogger);
  457. }
  458. ECLogger_Pipe::ECLogger_Pipe(int fd, pid_t childpid, int loglevel) :
  459. ECLogger(loglevel), m_fd(fd), m_childpid(childpid)
  460. {
  461. }
  462. ECLogger_Pipe::~ECLogger_Pipe() {
  463. close(m_fd); // this will make the log child exit
  464. if (m_childpid)
  465. waitpid(m_childpid, NULL, 0); // wait for the child if we're the one that forked it
  466. }
  467. void ECLogger_Pipe::Reset() {
  468. // send the log process HUP signal again
  469. kill(m_childpid, SIGHUP);
  470. }
  471. void ECLogger_Pipe::Log(unsigned int loglevel, const std::string &message) {
  472. int len = 0;
  473. int off = 0;
  474. char msgbuffer[_LOG_BUFSIZE];
  475. msgbuffer[0] = loglevel;
  476. off += 1;
  477. if (prefix == LP_TID)
  478. len = snprintf(msgbuffer + off, sizeof(msgbuffer) - off,
  479. "[0x%lx] ", kc_threadid());
  480. else if (prefix == LP_PID)
  481. len = snprintf(msgbuffer + off, sizeof msgbuffer - off, "[%5d] ", getpid());
  482. if (len < 0)
  483. len = 0;
  484. else
  485. off += len;
  486. len = min((int)message.length(), (int)sizeof msgbuffer - (off + 1));
  487. if (len < 0)
  488. len = 0;
  489. else {
  490. memcpy(msgbuffer+off, message.c_str(), len);
  491. off += len;
  492. }
  493. msgbuffer[off] = '\0';
  494. ++off;
  495. /*
  496. * Write as one block to get it to the real logger.
  497. * (Atomicity actually only guaranteed up to PIPE_BUF number of bytes.)
  498. */
  499. write(m_fd, msgbuffer, off);
  500. }
  501. void ECLogger_Pipe::Log(unsigned int loglevel, const char *format, ...) {
  502. va_list va;
  503. va_start(va, format);
  504. LogVA(loglevel, format, va);
  505. va_end(va);
  506. }
  507. void ECLogger_Pipe::LogVA(unsigned int loglevel, const char *format, va_list& va) {
  508. int len = 0;
  509. int off = 0;
  510. char msgbuffer[_LOG_BUFSIZE];
  511. msgbuffer[0] = loglevel;
  512. off += 1;
  513. if (prefix == LP_TID)
  514. len = snprintf(msgbuffer + off, sizeof(msgbuffer) - off, "[0x%lx] ", kc_threadid());
  515. else if (prefix == LP_PID)
  516. len = snprintf(msgbuffer + off, sizeof msgbuffer - off, "[%5d] ", getpid());
  517. if (len < 0)
  518. len = 0;
  519. else
  520. off += len;
  521. // return value is what WOULD have been written if enough space were available in the buffer
  522. len = _vsnprintf_l(msgbuffer + off, sizeof msgbuffer - off - 1, format, datalocale, va);
  523. // -1 can be returned on formatting error (eg. %ls in C locale)
  524. if (len < 0)
  525. len = 0;
  526. len = min(len, (int)sizeof msgbuffer - off - 2); // yes, -2, otherwise we could have 2 \0 at the end of the buffer
  527. off += len;
  528. msgbuffer[off] = '\0';
  529. ++off;
  530. /*
  531. * Write as one block to get it to the real logger.
  532. * (Atomicity actually only guaranteed up to PIPE_BUF number of bytes.)
  533. */
  534. write(m_fd, msgbuffer, off);
  535. }
  536. /**
  537. * Make sure we do not close the log process when this object is cleaned.
  538. */
  539. void ECLogger_Pipe::Disown()
  540. {
  541. m_childpid = 0;
  542. }
  543. namespace PrivatePipe {
  544. ECLogger_File *m_lpFileLogger;
  545. ECConfig *m_lpConfig;
  546. pthread_t signal_thread;
  547. sigset_t signal_mask;
  548. static void sighup(int)
  549. {
  550. if (m_lpConfig) {
  551. const char *ll;
  552. m_lpConfig->ReloadSettings();
  553. ll = m_lpConfig->GetSetting("log_level");
  554. if (ll)
  555. m_lpFileLogger->SetLoglevel(atoi(ll));
  556. }
  557. m_lpFileLogger->Reset();
  558. m_lpFileLogger->Log(EC_LOGLEVEL_INFO, "[%5d] Log process received sighup", getpid());
  559. }
  560. static int PipePassLoop(int readfd, ECLogger_File *lpFileLogger,
  561. ECConfig *lpConfig)
  562. {
  563. ssize_t ret;
  564. char buffer[_LOG_BUFSIZE] = {0};
  565. std::string complete;
  566. const char *p = NULL;
  567. int s;
  568. int l;
  569. m_lpConfig = lpConfig;
  570. m_lpFileLogger = lpFileLogger;
  571. m_lpFileLogger->AddRef();
  572. struct sigaction act;
  573. memset(&act, 0, sizeof(act));
  574. act.sa_handler = sighup;
  575. act.sa_flags = SA_RESTART | SA_ONSTACK;
  576. sigemptyset(&act.sa_mask);
  577. sigaction(SIGHUP, &act, nullptr);
  578. signal(SIGPIPE, SIG_IGN);
  579. // ignore stop signals to keep logging until the very end
  580. signal(SIGTERM, SIG_IGN);
  581. signal(SIGINT, SIG_IGN);
  582. // close signals we don't want to see from the main program anymore
  583. signal(SIGCHLD, SIG_IGN);
  584. signal(SIGUSR1, SIG_IGN);
  585. signal(SIGUSR2, SIG_IGN);
  586. // We want the prefix of each individual thread/fork, so don't add that of the Pipe version.
  587. m_lpFileLogger->SetLogprefix(LP_NONE);
  588. struct pollfd fds = {readfd, POLLIN, 0};
  589. while(true) {
  590. // blocking wait, returns on error or data waiting to log
  591. fds.revents = 0;
  592. ret = poll(&fds, 1, -1);
  593. if (ret == -1) {
  594. if (errno == EINTR)
  595. continue; // logger received SIGHUP, which wakes the select
  596. break;
  597. }
  598. if (ret == 0)
  599. continue;
  600. complete.clear();
  601. do {
  602. // if we don't read anything from the fd, it was the end
  603. ret = read(readfd, buffer, sizeof buffer);
  604. if (ret <= 0)
  605. break;
  606. complete.append(buffer,ret);
  607. } while (ret == sizeof buffer);
  608. if (ret <= 0)
  609. break;
  610. p = complete.data();
  611. ret = complete.size();
  612. while (ret && p) {
  613. // first char in buffer is loglevel
  614. l = *p++;
  615. --ret;
  616. s = strlen(p); // find string in read buffer
  617. if (s == 0) {
  618. p = NULL;
  619. continue;
  620. }
  621. lpFileLogger->Log(l, string(p, s));
  622. ++s; // add \0
  623. p += s; // skip string
  624. ret -= s;
  625. }
  626. }
  627. // we need to stop fetching signals
  628. kill(getpid(), SIGPIPE);
  629. m_lpFileLogger->Log(EC_LOGLEVEL_INFO, "[%5d] Log process is done", getpid());
  630. m_lpFileLogger->Release();
  631. return ret;
  632. }
  633. }
  634. /**
  635. * Starts a new process when needed for forked model programs. Only
  636. * actually replaces your ECLogger object if it's logging to a file.
  637. *
  638. * @param[in] lpConfig Pointer to your ECConfig object. Cannot be NULL.
  639. * @param[in] lpLogger Pointer to your current ECLogger object.
  640. * @return ECLogger Returns the same or new ECLogger object to use in your program.
  641. */
  642. ECLogger* StartLoggerProcess(ECConfig* lpConfig, ECLogger* lpLogger) {
  643. auto lpFileLogger = dynamic_cast<ECLogger_File *>(lpLogger);
  644. ECLogger_Pipe *lpPipeLogger = NULL;
  645. int filefd;
  646. int pipefds[2];
  647. pid_t child = 0;
  648. if (lpFileLogger == NULL)
  649. return lpLogger;
  650. filefd = lpFileLogger->GetFileDescriptor();
  651. child = pipe(pipefds);
  652. if (child < 0)
  653. return NULL;
  654. child = fork();
  655. if (child < 0)
  656. return NULL;
  657. if (child == 0) {
  658. // close all files except the read pipe and the logfile
  659. int t = getdtablesize();
  660. for (int i = 3; i < t; ++i) {
  661. if (i == pipefds[0] || i == filefd) continue;
  662. close(i);
  663. }
  664. PrivatePipe::PipePassLoop(pipefds[0], lpFileLogger, lpConfig);
  665. close(pipefds[0]);
  666. delete lpFileLogger;
  667. delete lpConfig;
  668. _exit(0);
  669. }
  670. // this is the main fork, which doesn't log anything anymore, except through the pipe version.
  671. // we should release the lpFileLogger
  672. unsigned int refs = lpLogger->Release();
  673. if (refs > 0)
  674. /*
  675. * The object must have had a refcount of 1 (the caller);
  676. * if not, this means that some other class is carrying a
  677. * pointer. This is not critical but is ill-favored.
  678. */
  679. lpLogger->Log(EC_LOGLEVEL_WARNING, "StartLoggerProcess called with large refcount %u", refs + 1);
  680. close(pipefds[0]);
  681. lpPipeLogger = new ECLogger_Pipe(pipefds[1], child, atoi(lpConfig->GetSetting("log_level"))); // let destructor wait on child
  682. lpPipeLogger->SetLogprefix(LP_PID);
  683. lpPipeLogger->Log(EC_LOGLEVEL_INFO, "Logger process started on pid %d", child);
  684. return lpPipeLogger;
  685. }
  686. /**
  687. * Create ECLogger object from configuration.
  688. *
  689. * @param[in] lpConfig ECConfig object with config settings from config file. Must have all log_* options.
  690. * @param argv0 name of the logger
  691. * @param lpszServiceName service name for windows event logger
  692. * @param bAudit prepend "audit_" before log settings to create an audit logger (kopano-server)
  693. *
  694. * @return Log object, or NULL on error
  695. */
  696. ECLogger* CreateLogger(ECConfig *lpConfig, const char *argv0,
  697. const char *lpszServiceName, bool bAudit)
  698. {
  699. ECLogger *lpLogger = NULL;
  700. string prepend;
  701. int loglevel = 0;
  702. int syslog_facility = LOG_MAIL;
  703. if (bAudit) {
  704. #if 1 /* change to ifdef HAVE_LOG_AUTHPRIV */
  705. if (!parseBool(lpConfig->GetSetting("audit_log_enabled")))
  706. return NULL;
  707. prepend = "audit_";
  708. syslog_facility = LOG_AUTHPRIV;
  709. #else
  710. return NULL; // No auditing in Windows, apparently.
  711. #endif
  712. }
  713. loglevel = strtol(lpConfig->GetSetting((prepend+"log_level").c_str()), NULL, 0);
  714. if (strcasecmp(lpConfig->GetSetting((prepend+"log_method").c_str()), "syslog") == 0) {
  715. char *argzero = strdup(argv0);
  716. lpLogger = new ECLogger_Syslog(loglevel, basename(argzero), syslog_facility);
  717. free(argzero);
  718. } else if (strcasecmp(lpConfig->GetSetting((prepend+"log_method").c_str()), "eventlog") == 0) {
  719. fprintf(stderr, "eventlog logging is only available on windows.\n");
  720. } else if (strcasecmp(lpConfig->GetSetting((prepend+"log_method").c_str()), "file") == 0) {
  721. int ret = 0;
  722. const struct passwd *pw = NULL;
  723. const struct group *gr = NULL;
  724. if (strcmp(lpConfig->GetSetting((prepend+"log_file").c_str()), "-")) {
  725. if (lpConfig->GetSetting("run_as_user") && strcmp(lpConfig->GetSetting("run_as_user"),""))
  726. pw = getpwnam(lpConfig->GetSetting("run_as_user"));
  727. else
  728. pw = getpwuid(getuid());
  729. if (lpConfig->GetSetting("run_as_group") && strcmp(lpConfig->GetSetting("run_as_group"),""))
  730. gr = getgrnam(lpConfig->GetSetting("run_as_group"));
  731. else
  732. gr = getgrgid(getgid());
  733. // see if we can open the file as the user we're supposed to run as
  734. if (pw || gr) {
  735. ret = fork();
  736. if (ret == 0) {
  737. // client test program
  738. setgroups(0, NULL);
  739. if (gr)
  740. setgid(gr->gr_gid);
  741. if (pw)
  742. setuid(pw->pw_uid);
  743. FILE *test = fopen(lpConfig->GetSetting((prepend+"log_file").c_str()), "a");
  744. if (!test) {
  745. fprintf(stderr, "Unable to open logfile '%s' as user '%s'\n",
  746. lpConfig->GetSetting((prepend+"log_file").c_str()), pw ? pw->pw_name : "???");
  747. _exit(1);
  748. }
  749. else {
  750. fclose(test);
  751. }
  752. // free known alloced memory in parent before exiting, keep valgrind from complaining
  753. delete lpConfig;
  754. _exit(0);
  755. }
  756. if (ret > 0) { // correct parent, (fork != -1)
  757. wait(&ret);
  758. ret = WEXITSTATUS(ret);
  759. }
  760. }
  761. }
  762. if (ret == 0) {
  763. bool logtimestamp = parseBool(lpConfig->GetSetting((prepend + "log_timestamp").c_str()));
  764. size_t log_buffer_size = 0;
  765. const char *log_buffer_size_str = lpConfig->GetSetting("log_buffer_size");
  766. if (log_buffer_size_str)
  767. log_buffer_size = strtoul(log_buffer_size_str, NULL, 0);
  768. auto log = new ECLogger_File(loglevel, logtimestamp, lpConfig->GetSetting((prepend + "log_file").c_str()), false);
  769. log->reinit_buffer(log_buffer_size);
  770. lpLogger = log;
  771. // chown file
  772. if (pw || gr) {
  773. uid_t uid = -1;
  774. gid_t gid = -1;
  775. if (pw)
  776. uid = pw->pw_uid;
  777. if (gr)
  778. gid = gr->gr_gid;
  779. chown(lpConfig->GetSetting((prepend+"log_file").c_str()), uid, gid);
  780. }
  781. } else {
  782. fprintf(stderr, "Not enough permissions to append logfile '%s'. Reverting to stderr.\n", lpConfig->GetSetting((prepend+"log_file").c_str()));
  783. bool logtimestamp = parseBool(lpConfig->GetSetting((prepend + "log_timestamp").c_str()));
  784. lpLogger = new ECLogger_File(loglevel, logtimestamp, "-", false);
  785. }
  786. }
  787. if (!lpLogger) {
  788. fprintf(stderr, "Incorrect logging method selected. Reverting to stderr.\n");
  789. bool logtimestamp = parseBool(lpConfig->GetSetting((prepend + "log_timestamp").c_str()));
  790. lpLogger = new ECLogger_File(loglevel, logtimestamp, "-", false);
  791. }
  792. return lpLogger;
  793. }
  794. int DeleteLogger(ECLogger *lpLogger) {
  795. if (lpLogger)
  796. lpLogger->Release();
  797. return 0;
  798. }
  799. void LogConfigErrors(ECConfig *lpConfig)
  800. {
  801. if (lpConfig == NULL)
  802. return;
  803. for (const auto &i : *lpConfig->GetWarnings())
  804. ec_log_warn("Config warning: " + i);
  805. for (const auto &i : *lpConfig->GetErrors())
  806. ec_log_crit("Config error: " + i);
  807. }
  808. void generic_sigsegv_handler(ECLogger *lpLogger, const char *app_name,
  809. const char *version_string, int signr, const siginfo_t *si, const void *uc)
  810. {
  811. ECLogger_Syslog localLogger(EC_LOGLEVEL_DEBUG, app_name, LOG_MAIL);
  812. if (lpLogger == NULL)
  813. lpLogger = &localLogger;
  814. lpLogger->Log(EC_LOGLEVEL_FATAL, "----------------------------------------------------------------------");
  815. lpLogger->Log(EC_LOGLEVEL_FATAL, "Fatal error detected. Please report all following information.");
  816. lpLogger->Log(EC_LOGLEVEL_FATAL, "Application %s version: %s", app_name, version_string);
  817. struct utsname buf;
  818. if (uname(&buf) == -1)
  819. lpLogger->Log(EC_LOGLEVEL_FATAL, "uname() failed: %s", strerror(errno));
  820. else
  821. lpLogger->Log(EC_LOGLEVEL_FATAL, "OS: %s, release: %s, version: %s, hardware: %s", buf.sysname, buf.release, buf.version, buf.machine);
  822. #ifdef HAVE_PTHREAD_GETNAME_NP
  823. char name[32] = { 0 };
  824. int rc = pthread_getname_np(pthread_self(), name, sizeof name);
  825. if (rc)
  826. lpLogger->Log(EC_LOGLEVEL_FATAL, "pthread_getname_np failed: %s", strerror(rc));
  827. else
  828. lpLogger->Log(EC_LOGLEVEL_FATAL, "Thread name: %s", name);
  829. #endif
  830. struct rusage rusage;
  831. if (getrusage(RUSAGE_SELF, &rusage) == -1)
  832. lpLogger->Log(EC_LOGLEVEL_FATAL, "getrusage() failed: %s", strerror(errno));
  833. else
  834. lpLogger->Log(EC_LOGLEVEL_FATAL, "Peak RSS: %ld", rusage.ru_maxrss);
  835. switch (signr) {
  836. case SIGSEGV:
  837. lpLogger->Log(EC_LOGLEVEL_FATAL, "Pid %d caught SIGSEGV (%d), traceback:", getpid(), signr);
  838. break;
  839. case SIGBUS:
  840. lpLogger->Log(EC_LOGLEVEL_FATAL, "Pid %d caught SIGBUS (%d), possible invalid mapped memory access, traceback:", getpid(), signr);
  841. break;
  842. case SIGABRT:
  843. lpLogger->Log(EC_LOGLEVEL_FATAL, "Pid %d caught SIGABRT (%d), out of memory or unhandled exception, traceback:", getpid(), signr);
  844. break;
  845. }
  846. ec_log_bt(EC_LOGLEVEL_CRIT, "Backtrace:");
  847. ec_log_crit("Signal errno: %s, signal code: %d", strerror(si->si_errno), si->si_code);
  848. ec_log_crit("Sender pid: %d, sender uid: %d, si_status: %d", si->si_pid, si->si_uid, si->si_status);
  849. ec_log_crit("User time: %ld, system time: %ld, signal value: %d", si->si_utime, si->si_stime, si->si_value.sival_int);
  850. ec_log_crit("Faulting address: %p, affected fd: %d", si->si_addr, si->si_fd);
  851. lpLogger->Log(EC_LOGLEVEL_FATAL, "When reporting this traceback, please include Linux distribution name (and version), system architecture and Kopano version.");
  852. /* Reset to DFL to avoid recursion */
  853. if (signal(signr, SIG_DFL) < 0)
  854. ec_log_warn("Cannot reset signal handler: %s", strerror(errno));
  855. else if (kill(getpid(), signr) <= 0)
  856. ec_log_warn("Killing self with signal had no effect. Error code: %s", strerror(errno));
  857. ec_log_warn("Regular exit without coredump.");
  858. _exit(1);
  859. }
  860. /**
  861. * The expectation here is that ec_log_set is only called when the program is
  862. * single-threaded, i.e. during startup or at shutdown. As a consequence, all
  863. * of this is written without locks and without shared_ptr.
  864. *
  865. * This function gets called from destructors, so you must not invoke
  866. * ec_log_target->anyfunction at this point any more.
  867. */
  868. void ec_log_set(ECLogger *logger)
  869. {
  870. if (logger == NULL)
  871. logger = &ec_log_fallback_target;
  872. ec_log_target = logger;
  873. }
  874. ECLogger *ec_log_get(void)
  875. {
  876. return ec_log_target;
  877. }
  878. bool ec_log_has_target(void)
  879. {
  880. return ec_log_target != &ec_log_fallback_target;
  881. }
  882. void ec_log(unsigned int level, const char *fmt, ...)
  883. {
  884. if (!ec_log_target->Log(level))
  885. return;
  886. va_list argp;
  887. va_start(argp, fmt);
  888. ec_log_target->LogVA(level, fmt, argp);
  889. va_end(argp);
  890. }
  891. void ec_log(unsigned int level, const std::string &msg)
  892. {
  893. ec_log_target->Log(level, msg);
  894. }
  895. void ec_log_bt(unsigned int level, const char *fmt, ...)
  896. {
  897. if (!ec_log_target->Log(level))
  898. return;
  899. va_list argp;
  900. va_start(argp, fmt);
  901. ec_log_target->LogVA(level, fmt, argp);
  902. va_end(argp);
  903. static bool notified = false;
  904. std::vector<std::string> bt = get_backtrace();
  905. if (!bt.empty()) {
  906. for (size_t i = 0; i < bt.size(); ++i)
  907. ec_log(level, "#%zu. %s", i, bt[i].c_str());
  908. } else if (!notified) {
  909. ec_log_info("Backtrace not available");
  910. notified = true;
  911. }
  912. }
  913. } /* namespace */