monitor.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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 <condition_variable>
  19. #include <mutex>
  20. #include <new>
  21. #include <climits>
  22. #include <cstdio>
  23. #include <cstdlib>
  24. #include <iostream>
  25. #include <csignal>
  26. #include <mapi.h>
  27. #include <mapix.h>
  28. #include <mapiutil.h>
  29. #include <mapidefs.h>
  30. #include <mapiguid.h>
  31. #include <kopano/ECScheduler.h>
  32. #include <kopano/lockhelper.hpp>
  33. #include <kopano/my_getopt.h>
  34. #include "ECMonitorDefs.h"
  35. #include "ECQuotaMonitor.h"
  36. #include <kopano/CommonUtil.h>
  37. #include <kopano/UnixUtil.h>
  38. #include <kopano/ecversion.h>
  39. #include "charset/localeutil.h"
  40. using namespace std;
  41. static void deleteThreadMonitor(ECTHREADMONITOR *lpThreadMonitor,
  42. bool base = false)
  43. {
  44. if(lpThreadMonitor == NULL)
  45. return;
  46. delete lpThreadMonitor->lpConfig;
  47. if(lpThreadMonitor->lpLogger)
  48. lpThreadMonitor->lpLogger->Release();
  49. if(base)
  50. delete lpThreadMonitor;
  51. }
  52. static ECTHREADMONITOR *m_lpThreadMonitor;
  53. static std::mutex m_hExitMutex;
  54. static std::condition_variable m_hExitSignal;
  55. static pthread_t mainthread;
  56. static HRESULT running_service(void)
  57. {
  58. HRESULT hr = hrSuccess;
  59. ECScheduler *lpECScheduler = nullptr;
  60. unsigned int ulInterval = 0;
  61. bool bMapiInit = false;
  62. ulock_normal l_exit(m_hExitMutex, std::defer_lock_t());
  63. hr = MAPIInitialize(nullptr);
  64. if (hr != hrSuccess) {
  65. m_lpThreadMonitor->lpLogger->Log(EC_LOGLEVEL_FATAL, "Unable to initialize MAPI");
  66. goto exit;
  67. }
  68. bMapiInit = true;
  69. lpECScheduler = new ECScheduler(m_lpThreadMonitor->lpLogger);
  70. ulInterval = atoi(m_lpThreadMonitor->lpConfig->GetSetting("quota_check_interval", nullptr, "15"));
  71. if (ulInterval == 0)
  72. ulInterval = 15;
  73. m_lpThreadMonitor->lpLogger->Log(EC_LOGLEVEL_ALWAYS, "Starting kopano-monitor version " PROJECT_VERSION_MONITOR_STR " (" PROJECT_SVN_REV_STR "), pid %d", getpid());
  74. // Add Quota monitor
  75. hr = lpECScheduler->AddSchedule(SCHEDULE_MINUTES, ulInterval, ECQuotaMonitor::Create, m_lpThreadMonitor);
  76. if (hr != hrSuccess) {
  77. m_lpThreadMonitor->lpLogger->Log(EC_LOGLEVEL_FATAL, "Unable to add quota monitor schedule");
  78. goto exit;
  79. }
  80. l_exit.lock();
  81. m_hExitSignal.wait(l_exit);
  82. l_exit.unlock();
  83. exit:
  84. delete lpECScheduler;
  85. if (bMapiInit)
  86. MAPIUninitialize();
  87. return hr;
  88. }
  89. static void sighandle(int sig)
  90. {
  91. // Win32 has Unix semantics and therefore requires us to reset the signal handler.
  92. signal(SIGTERM , sighandle);
  93. signal(SIGINT , sighandle); // CTRL+C
  94. if (m_lpThreadMonitor) {
  95. if (!m_lpThreadMonitor->bShutdown)
  96. /* do not log multimple shutdown messages */
  97. m_lpThreadMonitor->lpLogger->Log(EC_LOGLEVEL_NOTICE, "Termination requested, shutting down.");
  98. m_lpThreadMonitor->bShutdown = true;
  99. }
  100. m_hExitSignal.notify_one();
  101. }
  102. static void sighup(int signr)
  103. {
  104. // In Win32, the signal is sent in a separate, special signal thread. So this test is
  105. // not needed or required.
  106. if (pthread_equal(pthread_self(), mainthread)==0)
  107. return;
  108. if (m_lpThreadMonitor == NULL)
  109. return;
  110. if (m_lpThreadMonitor->lpConfig != NULL &&
  111. !m_lpThreadMonitor->lpConfig->ReloadSettings() &&
  112. m_lpThreadMonitor->lpLogger != NULL)
  113. m_lpThreadMonitor->lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to reload configuration file, continuing with current settings.");
  114. if (m_lpThreadMonitor->lpLogger == NULL)
  115. return;
  116. if (m_lpThreadMonitor->lpConfig) {
  117. const char *ll = m_lpThreadMonitor->lpConfig->GetSetting("log_level");
  118. int new_ll = ll ? atoi(ll) : EC_LOGLEVEL_WARNING;
  119. m_lpThreadMonitor->lpLogger->SetLoglevel(new_ll);
  120. }
  121. m_lpThreadMonitor->lpLogger->Reset();
  122. m_lpThreadMonitor->lpLogger->Log(EC_LOGLEVEL_WARNING, "Log connection was reset");
  123. }
  124. // SIGSEGV catcher
  125. static void sigsegv(int signr, siginfo_t *si, void *uc)
  126. {
  127. generic_sigsegv_handler(m_lpThreadMonitor->lpLogger, "Monitor",
  128. PROJECT_VERSION_MONITOR_STR, signr, si, uc);
  129. }
  130. static void print_help(const char *name)
  131. {
  132. cout << "Usage:\n" << endl;
  133. cout << name << " [-F] [-h|--host <serverpath>] [-c|--config <configfile>]" << endl;
  134. cout << " -F\t\tDo not run in the background" << endl;
  135. cout << " -h path\tUse alternate connect path (e.g. file:///var/run/socket).\n\t\tDefault: file:///var/run/kopano/server.sock" << endl;
  136. cout << " -c filename\tUse alternate config file (e.g. /etc/kopano-monitor.cfg)\n\t\tDefault: /etc/kopano/monitor.cfg" << endl;
  137. cout << endl;
  138. }
  139. int main(int argc, char *argv[]) {
  140. HRESULT hr = hrSuccess;
  141. const char *szConfig = ECConfig::GetDefaultPath("monitor.cfg");
  142. const char *szPath = NULL;
  143. int c;
  144. int daemonize = 1;
  145. bool bIgnoreUnknownConfigOptions = false;
  146. // Default settings
  147. static const configsetting_t lpDefaults[] = {
  148. { "smtp_server","localhost" },
  149. { "server_socket", "default:" },
  150. { "run_as_user", "kopano" },
  151. { "run_as_group", "kopano" },
  152. { "pid_file", "/var/run/kopano/monitor.pid" },
  153. { "running_path", "/var/lib/kopano" },
  154. { "log_method","file" },
  155. { "log_file","/var/log/kopano/monitor.log" },
  156. { "log_level", "3", CONFIGSETTING_RELOADABLE },
  157. { "log_timestamp","1" },
  158. { "log_buffer_size", "0" },
  159. { "sslkey_file", "" },
  160. { "sslkey_pass", "", CONFIGSETTING_EXACT },
  161. { "quota_check_interval", "15" },
  162. { "mailquota_resend_interval", "1", CONFIGSETTING_RELOADABLE },
  163. { "userquota_warning_template", "/etc/kopano/quotamail/userwarning.mail", CONFIGSETTING_RELOADABLE },
  164. { "userquota_soft_template", "", CONFIGSETTING_UNUSED },
  165. { "userquota_hard_template", "", CONFIGSETTING_UNUSED },
  166. { "companyquota_warning_template", "/etc/kopano/quotamail/companywarning.mail", CONFIGSETTING_RELOADABLE },
  167. { "companyquota_soft_template", "/etc/kopano/quotamail/companysoft.mail", CONFIGSETTING_RELOADABLE },
  168. { "companyquota_hard_template", "/etc/kopano/quotamail/companyhard.mail", CONFIGSETTING_RELOADABLE },
  169. { "servers", "" },
  170. { NULL, NULL },
  171. };
  172. enum {
  173. OPT_HELP = UCHAR_MAX + 1,
  174. OPT_HOST,
  175. OPT_CONFIG,
  176. OPT_FOREGROUND,
  177. OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS
  178. };
  179. static const struct option long_options[] = {
  180. { "help", 0, NULL, OPT_HELP },
  181. { "host", 1, NULL, OPT_HOST },
  182. { "config", 1, NULL, OPT_CONFIG },
  183. { "foreground", 1, NULL, OPT_FOREGROUND },
  184. { "ignore-unknown-config-options", 0, NULL, OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS },
  185. { NULL, 0, NULL, 0 }
  186. };
  187. if (!forceUTF8Locale(true))
  188. goto exit;
  189. while(1) {
  190. c = my_getopt_long_permissive(argc, argv, "c:h:iuFV", long_options, NULL);
  191. if(c == -1)
  192. break;
  193. switch(c) {
  194. case OPT_CONFIG:
  195. case 'c':
  196. szConfig = optarg;
  197. break;
  198. case OPT_HOST:
  199. case 'h':
  200. szPath = optarg;
  201. break;
  202. case 'i': // Install service
  203. case 'u': // Uninstall service
  204. break;
  205. case OPT_FOREGROUND:
  206. case 'F':
  207. daemonize = 0;
  208. break;
  209. case OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS:
  210. bIgnoreUnknownConfigOptions = true;
  211. break;
  212. case 'V':
  213. cout << "Product version:\t" << PROJECT_VERSION_MONITOR_STR << endl
  214. << "File version:\t\t" << PROJECT_SVN_REV_STR << endl;
  215. return 1;
  216. case OPT_HELP:
  217. default:
  218. print_help(argv[0]);
  219. return 1;
  220. }
  221. }
  222. m_lpThreadMonitor = new(std::nothrow) ECTHREADMONITOR;
  223. if (m_lpThreadMonitor == nullptr) {
  224. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  225. goto exit;
  226. }
  227. m_lpThreadMonitor->lpConfig = ECConfig::Create(lpDefaults);
  228. if (!m_lpThreadMonitor->lpConfig->LoadSettings(szConfig) ||
  229. m_lpThreadMonitor->lpConfig->ParseParams(argc - optind, &argv[optind]) < 0 ||
  230. (!bIgnoreUnknownConfigOptions && m_lpThreadMonitor->lpConfig->HasErrors())) {
  231. /* Create fatal logger without a timestamp to stderr. */
  232. m_lpThreadMonitor->lpLogger = new(std::nothrow) ECLogger_File(EC_LOGLEVEL_INFO, 0, "-", false);
  233. if (m_lpThreadMonitor->lpLogger == nullptr) {
  234. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  235. goto exit;
  236. }
  237. ec_log_set(m_lpThreadMonitor->lpLogger);
  238. LogConfigErrors(m_lpThreadMonitor->lpConfig);
  239. hr = E_FAIL;
  240. goto exit;
  241. }
  242. mainthread = pthread_self();
  243. // setup logging
  244. m_lpThreadMonitor->lpLogger = CreateLogger(m_lpThreadMonitor->lpConfig, argv[0], "Kopano-Monitor");
  245. ec_log_set(m_lpThreadMonitor->lpLogger);
  246. if ((bIgnoreUnknownConfigOptions && m_lpThreadMonitor->lpConfig->HasErrors()) || m_lpThreadMonitor->lpConfig->HasWarnings())
  247. LogConfigErrors(m_lpThreadMonitor->lpConfig);
  248. // set socket filename
  249. if (!szPath)
  250. szPath = m_lpThreadMonitor->lpConfig->GetSetting("server_socket");
  251. signal(SIGTERM, sighandle);
  252. signal(SIGINT, sighandle);
  253. signal(SIGHUP, sighup);
  254. // SIGSEGV backtrace support
  255. stack_t st;
  256. struct sigaction act;
  257. memset(&st, 0, sizeof(st));
  258. memset(&act, 0, sizeof(act));
  259. st.ss_sp = malloc(65536);
  260. st.ss_flags = 0;
  261. st.ss_size = 65536;
  262. act.sa_sigaction = sigsegv;
  263. act.sa_flags = SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
  264. sigemptyset(&act.sa_mask);
  265. sigaltstack(&st, NULL);
  266. sigaction(SIGSEGV, &act, NULL);
  267. sigaction(SIGBUS, &act, NULL);
  268. sigaction(SIGABRT, &act, NULL);
  269. // fork if needed and drop privileges as requested.
  270. // this must be done before we do anything with pthreads
  271. if (unix_runas(m_lpThreadMonitor->lpConfig))
  272. goto exit;
  273. if (daemonize && unix_daemonize(m_lpThreadMonitor->lpConfig))
  274. goto exit;
  275. if (!daemonize)
  276. setsid();
  277. if (unix_create_pidfile(argv[0], m_lpThreadMonitor->lpConfig, false) < 0)
  278. goto exit;
  279. // Init exit threads
  280. hr = running_service();
  281. exit:
  282. if(m_lpThreadMonitor)
  283. deleteThreadMonitor(m_lpThreadMonitor, true);
  284. return hr == hrSuccess ? 0 : 1;
  285. }