ECServer.cpp 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369
  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 <new>
  18. #include "config.h"
  19. #include <kopano/zcdefs.h>
  20. #include <kopano/platform.h>
  21. #include <kopano/ecversion.h>
  22. #include <kopano/stringutil.h>
  23. #include "soapH.h"
  24. #include "ECDatabase.h"
  25. #include "ECDatabaseFactory.h"
  26. #include "ECDatabaseUpdate.h"
  27. #include "ECDatabaseUtils.h"
  28. #include <kopano/ECLogger.h>
  29. #include <kopano/ECConfig.h>
  30. #include "ECPluginFactory.h"
  31. #include "ECNotificationManager.h"
  32. #include "ECSessionManager.h"
  33. #include "ECStatsCollector.h"
  34. #include "ECStatsTables.h"
  35. #include <climits>
  36. #include <csignal>
  37. #include <kopano/UnixUtil.h>
  38. #include <pwd.h>
  39. #include <sys/stat.h>
  40. #include <kopano/ECScheduler.h>
  41. #include <kopano/kcodes.h>
  42. #include <kopano/my_getopt.h>
  43. #include "cmd.hpp"
  44. #include "ECServerEntrypoint.h"
  45. #include "SSLUtil.h"
  46. #include "ECSoapServerConnection.h"
  47. #include <libintl.h>
  48. #include <map>
  49. #include <kopano/charset/convstring.h>
  50. #include <unicode/uclean.h>
  51. #include "TmpPath.h"
  52. #include "ECICS.h"
  53. #include <openssl/ssl.h>
  54. // The following value is based on:
  55. // http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_thread_stack
  56. // Since the remote MySQL server can be 32 or 64 bit we'll just go with the value specified
  57. // for 64-bit architectures.
  58. // We could use the 'version_compile_machine' variable, but I'm not sure if 32-bit versions
  59. // will ever be build on 64-bit machines and what that variable does. Plus we would need a
  60. // list of all possible 32-bit architectures because if the architecture is unknown we'll
  61. // have to go with the safe value which is for 64-bit.
  62. #define MYSQL_MIN_THREAD_STACK (256*1024)
  63. const char upgrade_lock_file[] = "/tmp/kopano-upgrade-lock";
  64. // Reports information on the current state of the license
  65. void* ReportLicense(void *);
  66. static int running_server(char *, const char *, int, char **, int, char **);
  67. int g_Quit = 0;
  68. int daemonize = 1;
  69. int restart_searches = 0;
  70. bool m_bIgnoreDatabaseVersionConflict = false;
  71. bool m_bIgnoreAttachmentStorageConflict = false;
  72. bool m_bIgnoreDistributedKopanoConflict = false;
  73. bool m_bForceDatabaseUpdate = false;
  74. bool m_bIgnoreUnknownConfigOptions = false;
  75. bool m_bIgnoreDbThreadStackSize = false;
  76. pthread_t mainthread;
  77. ECConfig* g_lpConfig = NULL;
  78. ECLogger* g_lpLogger = NULL;
  79. ECLogger* g_lpAudit = NULL;
  80. ECScheduler* g_lpScheduler = NULL;
  81. ECSoapServerConnection* g_lpSoapServerConn = NULL;
  82. bool m_bDatabaseUpdateIgnoreSignals = false;
  83. // This is the callback function for libserver/* so that it can notify that a delayed soap
  84. // request has been handled.
  85. static void kcsrv_notify_done(struct soap *soap)
  86. {
  87. g_lpSoapServerConn->NotifyDone(soap);
  88. }
  89. // Called from ECStatsTables to get server stats
  90. static void kcsrv_get_server_stats(unsigned int *lpulQueueLength,
  91. double *lpdblAge, unsigned int *lpulThreadCount,
  92. unsigned int *lpulIdleThreads)
  93. {
  94. g_lpSoapServerConn->GetStats(lpulQueueLength, lpdblAge, lpulThreadCount, lpulIdleThreads);
  95. }
  96. static void process_signal(int sig)
  97. {
  98. ec_log_debug("Received signal %d", sig);
  99. ZLOG_AUDIT(g_lpAudit, "server signalled sig=%d", sig);
  100. if (m_bDatabaseUpdateIgnoreSignals) {
  101. ec_log_notice("WARNING: Database upgrade is taking place.");
  102. ec_log_notice(" Please be patient, and do not try to kill the server process.");
  103. ec_log_notice(" It may leave your database in an inconsistent state.");
  104. return;
  105. }
  106. const char *ll;
  107. int new_ll;
  108. if(g_Quit == 1)
  109. return; // already in exit state!
  110. switch (sig) {
  111. case SIGINT:
  112. case SIGTERM:
  113. ec_log_warn("Shutting down");
  114. if (g_lpSoapServerConn)
  115. g_lpSoapServerConn->ShutDown();
  116. g_Quit = 1;
  117. break;
  118. case SIGHUP:
  119. // g_lpSessionManager only present when kopano_init is called (last init function), signals are initialized much earlier
  120. if (g_lpSessionManager == NULL)
  121. return;
  122. if (!g_lpConfig->ReloadSettings())
  123. ec_log_warn("Unable to reload configuration file, continuing with current settings.");
  124. if (g_lpConfig->HasErrors())
  125. ec_log_err("Failed to reload configuration file");
  126. g_lpSessionManager->GetPluginFactory()->SignalPlugins(sig);
  127. ll = g_lpConfig->GetSetting("log_level");
  128. new_ll = ll ? strtol(ll, NULL, 0) : EC_LOGLEVEL_WARNING;
  129. g_lpLogger->SetLoglevel(new_ll);
  130. g_lpLogger->Reset();
  131. ec_log_warn("Log connection was reset");
  132. if (g_lpAudit) {
  133. ll = g_lpConfig->GetSetting("audit_log_level");
  134. new_ll = ll ? strtol(ll, NULL, 0) : 1;
  135. g_lpAudit->SetLoglevel(new_ll);
  136. g_lpAudit->Reset();
  137. }
  138. g_lpStatsCollector->SetTime(SCN_SERVER_LAST_CONFIGRELOAD, time(NULL));
  139. g_lpSoapServerConn->DoHUP();
  140. break;
  141. default:
  142. ec_log_debug("Unknown signal %d received", sig);
  143. break;
  144. }
  145. }
  146. // SIGSEGV catcher
  147. #include <execinfo.h>
  148. static void sigsegv(int signr, siginfo_t *si, void *uc)
  149. {
  150. generic_sigsegv_handler(g_lpLogger, "Server",
  151. PROJECT_VERSION_SERVER_STR, signr, si, uc);
  152. }
  153. static ECRESULT check_database_innodb(ECDatabase *lpDatabase)
  154. {
  155. ECRESULT er = erSuccess;
  156. #ifndef EMBEDDED_MYSQL
  157. string strQuery;
  158. DB_RESULT lpResult;
  159. DB_ROW lpRow = NULL;
  160. // Only supported from mysql 5.0
  161. er = lpDatabase->DoSelect("SHOW TABLE STATUS WHERE engine != 'InnoDB'", &lpResult);
  162. if (er != erSuccess)
  163. return er;
  164. while( (lpRow = lpDatabase->FetchRow(lpResult)) ) {
  165. ec_log_crit("Database table '%s' not in InnoDB format: %s", lpRow[0] ? lpRow[0] : "unknown table", lpRow[1] ? lpRow[1] : "unknown engine");
  166. er = KCERR_DATABASE_ERROR;
  167. }
  168. if (er != erSuccess) {
  169. ec_log_crit("Your database was incorrectly created. Please upgrade all tables to the InnoDB format using this query:");
  170. ec_log_crit(" ALTER TABLE <table name> ENGINE='InnoDB';");
  171. ec_log_crit("This process may take a very long time, depending on the size of your database.");
  172. }
  173. #endif
  174. return er;
  175. }
  176. static ECRESULT check_database_attachments(ECDatabase *lpDatabase)
  177. {
  178. ECRESULT er = erSuccess;
  179. string strQuery;
  180. DB_RESULT lpResult;
  181. DB_ROW lpRow = NULL;
  182. er = lpDatabase->DoSelect("SELECT value FROM settings WHERE name = 'attachment_storage'", &lpResult);
  183. if (er != erSuccess) {
  184. ec_log_crit("Unable to read from database");
  185. return er;
  186. }
  187. lpRow = lpDatabase->FetchRow(lpResult);
  188. if (lpRow != nullptr && lpRow[0] != nullptr &&
  189. // check if the mode is the same as last time
  190. strcmp(lpRow[0], g_lpConfig->GetSetting("attachment_storage")) != 0) {
  191. if (!m_bIgnoreAttachmentStorageConflict) {
  192. ec_log_err("Attachments are stored with option '%s', but '%s' is selected.", lpRow[0], g_lpConfig->GetSetting("attachment_storage"));
  193. return KCERR_DATABASE_ERROR;
  194. }
  195. ec_log_warn("Ignoring attachment storing conflict as requested. Attachments are now stored with option '%s'", g_lpConfig->GetSetting("attachment_storage"));
  196. }
  197. // first time we start, set the database to the selected mode
  198. strQuery = (string)"REPLACE INTO settings VALUES ('attachment_storage', '" + g_lpConfig->GetSetting("attachment_storage") + "')";
  199. er = lpDatabase->DoInsert(strQuery);
  200. if (er != erSuccess) {
  201. ec_log_err("Unable to update database settings");
  202. return er;
  203. }
  204. // Create attachment directories
  205. if (strcmp(g_lpConfig->GetSetting("attachment_storage"), "files") != 0)
  206. return erSuccess;
  207. // These values are hard coded .. if they change, the hash algorithm will fail, and you'll be FUCKED.
  208. for (int i = 0; i < ATTACH_PATHDEPTH_LEVEL1; ++i)
  209. for (int j = 0; j < ATTACH_PATHDEPTH_LEVEL2; ++j) {
  210. string path = (string)g_lpConfig->GetSetting("attachment_path") + PATH_SEPARATOR + stringify(i) + PATH_SEPARATOR + stringify(j);
  211. CreatePath(path.c_str());
  212. }
  213. return erSuccess;
  214. }
  215. static ECRESULT check_distributed_kopano(ECDatabase *lpDatabase)
  216. {
  217. ECRESULT er = erSuccess;
  218. string strQuery;
  219. DB_RESULT lpResult;
  220. DB_ROW lpRow = NULL;
  221. bool bConfigEnabled = parseBool(g_lpConfig->GetSetting("enable_distributed_kopano"));
  222. er = lpDatabase->DoSelect("SELECT value FROM settings WHERE name = 'lock_distributed_kopano'", &lpResult);
  223. if (er != erSuccess) {
  224. ec_log_err("Unable to read from database");
  225. return er;
  226. }
  227. lpRow = lpDatabase->FetchRow(lpResult);
  228. // If no value is found in the database any setting is valid
  229. if (lpRow == NULL || lpRow[0] == NULL)
  230. return er;
  231. // If any value is found, distributed is not allowed. The value specifies the reason.
  232. if (bConfigEnabled) {
  233. if (!m_bIgnoreDistributedKopanoConflict) {
  234. ec_log_crit("Multiserver mode is locked, reason: '%s'. Contact Kopano for support.", lpRow[0]);
  235. return KCERR_DATABASE_ERROR;
  236. }
  237. ec_log_warn("Ignoring multiserver mode lock as requested.");
  238. }
  239. return erSuccess;
  240. }
  241. static ECRESULT check_attachment_storage_permissions(void)
  242. {
  243. ECRESULT er = erSuccess;
  244. FILE *tmpfile = NULL;
  245. string strtestpath;
  246. if (strcmp(g_lpConfig->GetSetting("attachment_storage"), "files") == 0) {
  247. strtestpath = g_lpConfig->GetSetting("attachment_path");
  248. strtestpath += "/testfile";
  249. tmpfile = fopen(strtestpath.c_str(), "w");
  250. if (!tmpfile) {
  251. ec_log_err("Unable to write attachments to the directory '%s' - %s. Please check the directory and sub directories.", g_lpConfig->GetSetting("attachment_path"), strerror(errno));
  252. er = KCERR_NO_ACCESS;
  253. goto exit;
  254. }
  255. }
  256. exit:
  257. if (tmpfile) {
  258. fclose(tmpfile);
  259. unlink(strtestpath.c_str());
  260. }
  261. return er;
  262. }
  263. static ECRESULT check_database_tproperties_key(ECDatabase *lpDatabase)
  264. {
  265. ECRESULT er = erSuccess;
  266. string strQuery, strTable;
  267. string::size_type start, end;
  268. DB_RESULT lpResult;
  269. DB_ROW lpRow = NULL;
  270. strQuery = "SHOW CREATE TABLE `tproperties`";
  271. er = lpDatabase->DoSelect(strQuery, &lpResult);
  272. if (er != erSuccess) {
  273. ec_log_err("Unable to read from database");
  274. return er;
  275. }
  276. er = KCERR_DATABASE_ERROR;
  277. lpRow = lpDatabase->FetchRow(lpResult);
  278. if (!lpRow || !lpRow[1]) {
  279. ec_log_crit("No tproperties table definition found");
  280. return er;
  281. }
  282. strTable = lpRow[1];
  283. start = strTable.find("PRIMARY KEY");
  284. if (start == string::npos) {
  285. ec_log_crit("No primary key found in tproperties table");
  286. return er;
  287. }
  288. end = strTable.find(")", start);
  289. if (end == string::npos) {
  290. ec_log_crit("No end of primary key found in tproperties table");
  291. return er;
  292. }
  293. strTable.erase(end, string::npos);
  294. strTable.erase(0, start);
  295. // correct:
  296. // PRIMARY KEY (`folderid`,`tag`,`hierarchyid`,`type`),
  297. // incorrect:
  298. // PRIMARY KEY `ht` (`folderid`,`tag`,`type`,`hierarchyid`)
  299. // `ht` part seems to be optional
  300. start = strTable.find_first_of(',');
  301. if (start != string::npos)
  302. start = strTable.find_first_of(',', start+1);
  303. if (start == string::npos) {
  304. ec_log_warn("Primary key of tproperties table incorrect, trying: %s", strTable.c_str());
  305. return er;
  306. }
  307. // start+1:end == `type`,`hierarchyid`
  308. strTable.erase(0, start+1);
  309. // if not correct...
  310. if (strTable.compare("`hierarchyid`,`type`") != 0) {
  311. ec_log_warn("**** WARNING: Installation is not optimal! ****");
  312. ec_log_warn(" The primary key of the tproperties table is incorrect.");
  313. ec_log_warn(" Since updating the primary key on a large table is slow, the server will not automatically update this for you.");
  314. }
  315. return erSuccess;
  316. }
  317. static ECRESULT check_database_thread_stack(ECDatabase *lpDatabase)
  318. {
  319. ECRESULT er = erSuccess;
  320. string strQuery;
  321. DB_RESULT lpResult;
  322. DB_ROW lpRow = NULL;
  323. unsigned ulThreadStack = 0;
  324. // only required when procedures are used
  325. if (!parseBool(g_lpConfig->GetSetting("enable_sql_procedures")))
  326. return er;
  327. strQuery = "SHOW VARIABLES LIKE 'thread_stack'";
  328. er = lpDatabase->DoSelect(strQuery, &lpResult);
  329. if (er != erSuccess) {
  330. ec_log_err("Unable to read from database");
  331. return er;
  332. }
  333. lpRow = lpDatabase->FetchRow(lpResult);
  334. if (!lpRow || !lpRow[1]) {
  335. ec_log_err("No thread_stack variable returned");
  336. return er;
  337. }
  338. ulThreadStack = atoui(lpRow[1]);
  339. if (ulThreadStack < MYSQL_MIN_THREAD_STACK) {
  340. ec_log_warn("MySQL thread_stack is set to %u, which is too small", ulThreadStack);
  341. ec_log_warn("Please set thread_stack to %uK or higher in your MySQL configuration", MYSQL_MIN_THREAD_STACK / 1024);
  342. if (!m_bIgnoreDbThreadStackSize)
  343. return KCERR_DATABASE_ERROR;
  344. ec_log_warn("MySQL thread_stack setting ignored. Please reconsider when 'Thread stack overrun' errors appear in the log.");
  345. }
  346. return erSuccess;
  347. }
  348. /**
  349. * Checks the server_hostname value of the configuration, and if
  350. * empty, gets the current FQDN through DNS lookups, and updates the
  351. * server_hostname value in the config object.
  352. */
  353. static ECRESULT check_server_fqdn(void)
  354. {
  355. ECRESULT er = erSuccess;
  356. int rc;
  357. char hostname[256] = {0};
  358. struct sockaddr_in saddr = {0};
  359. struct addrinfo hints = {0};
  360. struct addrinfo *aiResult = NULL;
  361. const char *option;
  362. // If admin has set the option, we're not using DNS to check the name
  363. option = g_lpConfig->GetSetting("server_hostname");
  364. if (option && option[0] != '\0')
  365. goto exit;
  366. rc = gethostname(hostname, sizeof(hostname));
  367. if (rc != 0) {
  368. er = KCERR_NOT_FOUND;
  369. goto exit;
  370. }
  371. // if we exit hereon after, hostname will always contain a correct hostname, which we can set in the config.
  372. rc = getaddrinfo(hostname, NULL, &hints, &aiResult);
  373. if (rc != 0) {
  374. er = KCERR_NOT_FOUND;
  375. goto exit;
  376. }
  377. // no need to set other contents of saddr struct, we're just intrested in the DNS lookup.
  378. saddr = *((sockaddr_in*)aiResult->ai_addr);
  379. // Name lookup is required, so set that flag
  380. rc = getnameinfo((const sockaddr*)&saddr, sizeof(saddr), hostname, sizeof(hostname), NULL, 0, NI_NAMEREQD);
  381. if (rc != 0) {
  382. er = KCERR_NOT_FOUND;
  383. goto exit;
  384. }
  385. exit:
  386. if (hostname[0] != '\0')
  387. g_lpConfig->AddSetting("server_hostname", hostname);
  388. if (aiResult)
  389. freeaddrinfo(aiResult);
  390. return er;
  391. }
  392. /**
  393. * Checks config options for sane multi-server environments and
  394. * updates some SSO options if detauls may be inadequate.
  395. *
  396. * @return always returns erSuccess
  397. */
  398. static ECRESULT check_server_configuration(void)
  399. {
  400. ECRESULT er = erSuccess;
  401. bool bHaveErrors = false;
  402. bool bCheck = false;
  403. std::string strServerName;
  404. ECSession *lpecSession = NULL;
  405. serverdetails_t sServerDetails;
  406. unsigned ulPort = 0;
  407. // Upgrade 'enable_sso_ntlmauth' to 'enable_sso'
  408. bCheck = parseBool(g_lpConfig->GetSetting("enable_sso_ntlmauth"));
  409. if (bCheck)
  410. g_lpConfig->AddSetting("enable_sso", g_lpConfig->GetSetting("enable_sso_ntlmauth"));
  411. // Find FQDN if Kerberos is enabled (remove check if we're using 'server_hostname' for other purposes)
  412. bCheck = parseBool(g_lpConfig->GetSetting("enable_sso"));
  413. if (bCheck && check_server_fqdn() != erSuccess)
  414. ec_log_err("WARNING: Unable to find FQDN, please specify in 'server_hostname'. Now using '%s'.", g_lpConfig->GetSetting("server_hostname"));
  415. // all other checks are only required for multi-server environments
  416. bCheck = parseBool(g_lpConfig->GetSetting("enable_distributed_kopano"));
  417. if (!bCheck)
  418. goto exit;
  419. strServerName = g_lpConfig->GetSetting("server_name");
  420. if (strServerName.empty()) {
  421. ec_log_crit("ERROR: No 'server_name' specified while operating in multiserver mode.");
  422. er = KCERR_INVALID_PARAMETER;
  423. // unable to check any other server details if we have no name, skip other tests
  424. goto exit;
  425. }
  426. er = g_lpSessionManager->CreateSessionInternal(&lpecSession);
  427. if (er != erSuccess) {
  428. ec_log_crit("Internal error 0x%08x while checking distributed configuration", er);
  429. goto exit;
  430. }
  431. lpecSession->Lock();
  432. er = lpecSession->GetUserManagement()->GetServerDetails(strServerName, &sServerDetails);
  433. if (er != erSuccess) {
  434. ec_log_crit("ERROR: Unable to find server information on LDAP for '%s', error 0x%08X. Check your server name.", strServerName.c_str(), er);
  435. // unable to check anything else if we have no details, skip other tests
  436. goto exit;
  437. }
  438. // Check the various connection parameters for consistency
  439. if (parseBool(g_lpConfig->GetSetting("server_pipe_enabled")) == true) {
  440. if (sServerDetails.GetFilePath().empty()) {
  441. ec_log_warn("WARNING: 'server_pipe_enabled' is set, but LDAP returns nothing");
  442. bHaveErrors = true;
  443. }
  444. if (sServerDetails.GetFilePath().compare((std::string)"file://" + g_lpConfig->GetSetting("server_pipe_name")) != 0) {
  445. ec_log_warn("WARNING: 'server_pipe_name' is set to '%s', but LDAP returns '%s'", g_lpConfig->GetSetting("server_pipe_name"), sServerDetails.GetFilePath().c_str());
  446. bHaveErrors = true;
  447. }
  448. } else if (!sServerDetails.GetFilePath().empty()) {
  449. ec_log_warn("WARNING: 'server_pipe_enabled' is unset, but LDAP returns '%s'", sServerDetails.GetFilePath().c_str());
  450. bHaveErrors = true;
  451. }
  452. if (parseBool(g_lpConfig->GetSetting("server_tcp_enabled")) == true) {
  453. if (sServerDetails.GetHttpPath().empty()) {
  454. ec_log_warn("WARNING: 'server_tcp_enabled' is set, but LDAP returns nothing");
  455. bHaveErrors = true;
  456. }
  457. ulPort = atoui(g_lpConfig->GetSetting("server_tcp_port"));
  458. if (sServerDetails.GetHttpPort() != ulPort) {
  459. ec_log_warn("WARNING: 'server_tcp_port' is set to '%u', but LDAP returns '%u'", ulPort, sServerDetails.GetHttpPort());
  460. bHaveErrors = true;
  461. }
  462. } else if (!sServerDetails.GetHttpPath().empty()) {
  463. ec_log_warn("WARNING: 'server_tcp_enabled' is unset, but LDAP returns '%s'", sServerDetails.GetHttpPath().c_str());
  464. bHaveErrors = true;
  465. }
  466. if (parseBool(g_lpConfig->GetSetting("server_ssl_enabled")) == true) {
  467. if (sServerDetails.GetSslPath().empty()) {
  468. ec_log_warn("WARNING: 'server_ssl_enabled' is set, but LDAP returns nothing");
  469. bHaveErrors = true;
  470. }
  471. ulPort = atoui(g_lpConfig->GetSetting("server_ssl_port"));
  472. if (sServerDetails.GetSslPort() != ulPort) {
  473. ec_log_warn("WARNING: 'server_ssl_port' is set to '%u', but LDAP returns '%u'", ulPort, sServerDetails.GetSslPort());
  474. bHaveErrors = true;
  475. }
  476. } else if (!sServerDetails.GetSslPath().empty()) {
  477. ec_log_warn("WARNING: 'server_ssl_enabled' is unset, but LDAP returns '%s'", sServerDetails.GetSslPath().c_str());
  478. bHaveErrors = true;
  479. }
  480. exit:
  481. if (lpecSession) {
  482. lpecSession->Unlock();
  483. g_lpSessionManager->RemoveSessionInternal(lpecSession);
  484. }
  485. // we could return an error when bHaveErrors is set, but we currently find this not fatal as a sysadmin might be smarter than us.
  486. if (bHaveErrors)
  487. ec_log_warn("WARNING: Inconsistencies detected between local and LDAP based configuration.");
  488. // we do return er, since if that is set GetServerDetails() does not work and that is quite vital to work in distributed systems.
  489. return er;
  490. }
  491. /**
  492. * Restart the program with a new preloaded library.
  493. * @argv: full argv of current invocation
  494. * @lib: library to load via LD_PRELOAD
  495. *
  496. * As every program under Linux is linked to libc and symbol resolution is done
  497. * breadth-first, having just libkcserver.so linked to the alternate allocator
  498. * is not enough to ensure the allocator is being used in favor of libc malloc.
  499. *
  500. * A program built against glibc will have a record for e.g.
  501. * "malloc@GLIBC_2.2.5". The use of LD_PRELOAD appears to relax the version
  502. * requirement, though; the benefit would be that libtcmalloc's malloc will
  503. * take over _all_ malloc calls.
  504. */
  505. static int kc_reexec_with_allocator(char **argv, const char *lib)
  506. {
  507. if (lib == NULL || *lib == '\0')
  508. return 0;
  509. const char *s = getenv("KC_ALLOCATOR_DONE");
  510. if (s != NULL)
  511. /* avoid repeatedly reexecing ourselves */
  512. return 0;
  513. s = getenv("LD_PRELOAD");
  514. if (s == NULL)
  515. setenv("LD_PRELOAD", lib, true);
  516. else if (strstr(s, "/valgrind/") != NULL)
  517. /*
  518. * Within vg, everything is a bit different — since it catches
  519. * execve itself. Execing /proc/self/exe therefore won't work,
  520. * we would need to use argv[0]. But… don't bother.
  521. */
  522. return 0;
  523. else
  524. setenv("LD_PRELOAD", (std::string(s) + ":" + lib).c_str(), true);
  525. void *handle = dlopen(lib, RTLD_LAZY | RTLD_LOCAL);
  526. if (handle == NULL)
  527. /*
  528. * Ignore libraries that won't load anyway. This avoids
  529. * ld.so emitting a scary warning if we did re-exec.
  530. */
  531. return 0;
  532. dlclose(handle);
  533. setenv("KC_ALLOCATOR_DONE", lib, true);
  534. /* Resolve "exe" symlink before exec to please the sysadmin */
  535. std::vector<char> linkbuf(16);
  536. ssize_t linklen;
  537. while (true) {
  538. linklen = readlink("/proc/self/exe", &linkbuf[0], linkbuf.size());
  539. if (linklen < 0 || static_cast<size_t>(linklen) < linkbuf.size())
  540. break;
  541. linkbuf.resize(linkbuf.size() * 2);
  542. }
  543. if (linklen < 0) {
  544. int ret = -errno;
  545. ec_log_warn("kc_reexec_with_allocator: readlink: %s", strerror(errno));
  546. return ret;
  547. }
  548. linkbuf[linklen] = '\0';
  549. ec_log_debug("Reexecing %s with %s", &linkbuf[0], lib);
  550. execv(&linkbuf[0], argv);
  551. int ret = -errno;
  552. ec_log_info("Failed to reexec self: %s. Continuing with standard allocator.", strerror(errno));
  553. return ret;
  554. }
  555. int main(int argc, char* argv[])
  556. {
  557. int nReturn = 0;
  558. const char *config = ECConfig::GetDefaultPath("server.cfg");
  559. const char *default_config = config;
  560. enum {
  561. OPT_HELP = UCHAR_MAX + 1,
  562. OPT_CONFIG,
  563. OPT_RESTART_SEARCHES,
  564. OPT_IGNORE_DATABASE_VERSION_CONFLICT,
  565. OPT_IGNORE_ATTACHMENT_STORAGE_CONFLICT,
  566. OPT_OVERRIDE_DISTRIBUTED_LOCK,
  567. OPT_FORCE_DATABASE_UPGRADE,
  568. OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS,
  569. OPT_IGNORE_DB_THREAD_STACK_SIZE
  570. };
  571. static const struct option long_options[] = {
  572. { "help", 0, NULL, OPT_HELP }, // help text
  573. { "config", 1, NULL, OPT_CONFIG }, // config file
  574. { "restart-searches", 0, NULL, OPT_RESTART_SEARCHES },
  575. { "ignore-database-version-conflict", 0, NULL, OPT_IGNORE_DATABASE_VERSION_CONFLICT },
  576. { "ignore-attachment-storage-conflict", 0, NULL, OPT_IGNORE_ATTACHMENT_STORAGE_CONFLICT },
  577. { "override-multiserver-lock", 0, NULL, OPT_OVERRIDE_DISTRIBUTED_LOCK },
  578. { "force-database-upgrade", 0, NULL, OPT_FORCE_DATABASE_UPGRADE },
  579. { "ignore-unknown-config-options", 0, NULL, OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS },
  580. { "ignore-db-thread-stack-size", 0, NULL, OPT_IGNORE_DB_THREAD_STACK_SIZE },
  581. { NULL, 0, NULL, 0 }
  582. };
  583. //FIXME: By start as service current path is the system32 dir ??? <-- use '-c' option in service to be sure?
  584. // check for configfile
  585. while (1) {
  586. int c = my_getopt_long_permissive(argc, argv, "c:VFiuR", long_options, NULL);
  587. if(c == -1)
  588. break;
  589. switch (c) {
  590. case 'c':
  591. case OPT_CONFIG:
  592. config = optarg;
  593. break;
  594. case OPT_HELP:
  595. cout << "Kopano " PROJECT_VERSION_SERVER_STR " ";
  596. #ifdef EMBEDDED_MYSQL
  597. cout << "with embedded SQL server";
  598. #endif
  599. cout << endl;
  600. cout << argv[0] << " [options...]" << endl;
  601. cout << " -c --config=FILE Set new config file location. Default: " << default_config << endl;
  602. cout << " -F Do not start in the background." << endl;
  603. cout << " -V Print version info." << endl;
  604. cout << " -R --restart-searches Rebuild searchfolders." << endl;
  605. cout << " --ignore-database-version-conflict Start even if database version conflict with server version" << endl;
  606. cout << " --ignore-attachment-storage-conflict Start even if the attachment_storage config option changed" << endl;
  607. cout << " --override-multiserver-lock Start in multiserver mode even if multiserver mode is locked" << endl;
  608. cout << " --force-database-upgrade Start upgrade from 6.x database and continue running if upgrade is complete" << endl;
  609. cout << " --ignore-db-thread-stack-size Start even if the thread_stack setting for MySQL is too low" << endl;
  610. return 0;
  611. case 'V':
  612. cout << "Product version:\t" << PROJECT_VERSION_SERVER_STR << endl
  613. << "File version:\t\t" << PROJECT_SVN_REV_STR << endl;
  614. return 0;
  615. case 'F':
  616. daemonize = 0;
  617. break;
  618. case 'R':
  619. case OPT_RESTART_SEARCHES:
  620. restart_searches = 1;
  621. break;
  622. case OPT_IGNORE_DATABASE_VERSION_CONFLICT:
  623. m_bIgnoreDatabaseVersionConflict = true;
  624. break;
  625. case OPT_IGNORE_ATTACHMENT_STORAGE_CONFLICT:
  626. m_bIgnoreAttachmentStorageConflict = true;
  627. break;
  628. case OPT_FORCE_DATABASE_UPGRADE:
  629. m_bForceDatabaseUpdate = true;
  630. break;
  631. case OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS:
  632. m_bIgnoreUnknownConfigOptions = true;
  633. break;
  634. case OPT_IGNORE_DB_THREAD_STACK_SIZE:
  635. m_bIgnoreDbThreadStackSize = true;
  636. break;
  637. };
  638. }
  639. nReturn = running_server(argv[0], config, argc, argv,
  640. argc - optind, &argv[optind]);
  641. return nReturn;
  642. }
  643. #define KOPANO_SERVER_PIPE "/var/run/kopano/server.sock"
  644. #define KOPANO_SERVER_PRIO "/var/run/kopano/prio.sock"
  645. static void InitBindTextDomain(void)
  646. {
  647. // Set gettext codeset, used for generated folder name translations
  648. bind_textdomain_codeset("kopano", "UTF-8");
  649. }
  650. static int running_server(char *szName, const char *szConfig,
  651. int argc, char **argv, int trim_argc, char **trim_argv)
  652. {
  653. int retval = -1;
  654. ECRESULT er = erSuccess;
  655. ECDatabaseFactory* lpDatabaseFactory = NULL;
  656. ECDatabase* lpDatabase = NULL;
  657. std::string dbError;
  658. // Connections
  659. bool bSSLEnabled = false;
  660. bool bPipeEnabled = false;
  661. bool bTCPEnabled = false;
  662. bool hosted = false;
  663. bool distributed = false;
  664. // SIGSEGV backtrace support
  665. stack_t st = {0};
  666. struct sigaction act;
  667. int tmplock = -1;
  668. struct stat dir = {0};
  669. struct passwd *runasUser = NULL;
  670. const configsetting_t lpDefaults[] = {
  671. // Aliases
  672. { "server_port", "server_tcp_port", CONFIGSETTING_ALIAS },
  673. { "unix_socket", "server_pipe_name", CONFIGSETTING_ALIAS },
  674. // Default settings
  675. { "enable_hosted_kopano", "false" }, // Will only be checked when license allows hosted
  676. { "enable_distributed_kopano", "false" },
  677. { "server_name", "" }, // used by multi-server
  678. { "server_hostname", "" }, // used by kerberos, if empty, gethostbyname is used
  679. // server connections
  680. { "server_bind", "" },
  681. { "server_tcp_port", "236" },
  682. { "server_tcp_enabled", "yes" },
  683. { "server_pipe_enabled", "yes" },
  684. { "server_pipe_name", KOPANO_SERVER_PIPE },
  685. { "server_pipe_priority", KOPANO_SERVER_PRIO },
  686. { "server_recv_timeout", "5", CONFIGSETTING_RELOADABLE }, // timeout before reading next XML request
  687. { "server_read_timeout", "60", CONFIGSETTING_RELOADABLE }, // timeout during reading of XML request
  688. { "server_send_timeout", "60", CONFIGSETTING_RELOADABLE },
  689. { "server_max_keep_alive_requests", "100" },
  690. { "thread_stacksize", "512" },
  691. { "allow_local_users", "yes", CONFIGSETTING_RELOADABLE }, // allow any user connect through the Unix socket
  692. { "local_admin_users", "root", CONFIGSETTING_RELOADABLE }, // this local user is admin
  693. { "run_as_user", "kopano" }, // drop root privileges, and run as this user/group
  694. { "run_as_group", "kopano" },
  695. { "pid_file", "/var/run/kopano/server.pid" },
  696. { "running_path", "/var/lib/kopano" },
  697. {"allocator_library", "libtcmalloc_minimal.so.4"},
  698. { "coredump_enabled", "yes" },
  699. { "license_path", "/etc/kopano/license", CONFIGSETTING_UNUSED },
  700. { "license_socket", "/var/run/kopano/licensed.sock" },
  701. { "license_timeout", "10", CONFIGSETTING_RELOADABLE},
  702. { "system_email_address", "postmaster@localhost", CONFIGSETTING_RELOADABLE },
  703. { "server_ssl_enabled", "no" },
  704. { "server_ssl_port", "237" },
  705. {"server_ssl_key_file", "/etc/kopano/ssl/server.pem", CONFIGSETTING_RELOADABLE},
  706. {"server_ssl_key_pass", "server", CONFIGSETTING_EXACT | CONFIGSETTING_RELOADABLE},
  707. {"server_ssl_ca_file", "/etc/kopano/ssl/cacert.pem", CONFIGSETTING_RELOADABLE},
  708. {"server_ssl_ca_path", "", CONFIGSETTING_RELOADABLE},
  709. #ifdef SSL_TXT_SSLV2
  710. {"server_ssl_protocols", "!SSLv2", CONFIGSETTING_RELOADABLE},
  711. #else
  712. {"server_ssl_protocols", "", CONFIGSETTING_RELOADABLE},
  713. #endif
  714. {"server_ssl_ciphers", "ALL:!LOW:!SSLv2:!EXP:!aNULL", CONFIGSETTING_RELOADABLE},
  715. {"server_ssl_prefer_server_ciphers", "no", CONFIGSETTING_RELOADABLE},
  716. { "sslkeys_path", "/etc/kopano/sslkeys" }, // login keys
  717. // Database options
  718. { "database_engine", "mysql" },
  719. // MySQL Settings
  720. { "mysql_host", "localhost" },
  721. { "mysql_port", "3306" },
  722. { "mysql_user", "root" },
  723. { "mysql_password", "", CONFIGSETTING_EXACT },
  724. { "mysql_database", "kopano" },
  725. { "mysql_socket", "" },
  726. #if defined(EMBEDDED_MYSQL)
  727. { "mysql_database_path", "/var/kopano/data" },
  728. { "mysql_config_file", "/etc/kopano/my.cnf" },
  729. #endif
  730. { "attachment_storage", "database" },
  731. #ifdef HAVE_LIBS3_H
  732. {"attachment_s3_hostname", ""},
  733. {"attachment_s3_protocol", "https"},
  734. {"attachment_s3_uristyle", "virtualhost"},
  735. {"attachment_s3_accesskeyid", ""},
  736. {"attachment_s3_secretaccesskey", ""},
  737. {"attachment_s3_bucketname", ""},
  738. {"attachment_s3_region", ""},
  739. #endif
  740. { "attachment_path", "Kopano Data" },
  741. { "attachment_compression", "6" },
  742. // Log options
  743. { "log_method", "file" },
  744. { "log_file", "-" },
  745. { "log_level", "3", CONFIGSETTING_RELOADABLE },
  746. { "log_timestamp", "1" },
  747. { "log_buffer_size", "0" },
  748. // security log options
  749. { "audit_log_enabled", "no" },
  750. { "audit_log_method", "syslog" },
  751. { "audit_log_file", "-" },
  752. { "audit_log_level", "1", CONFIGSETTING_RELOADABLE },
  753. { "audit_log_timestamp", "0" },
  754. // user plugin
  755. { "plugin_path", PKGLIBDIR },
  756. { "user_plugin", "db" },
  757. { "user_plugin_config", "/etc/kopano/ldap.cfg" },
  758. { "createuser_script", "/etc/kopano/userscripts/createuser", CONFIGSETTING_RELOADABLE },
  759. { "deleteuser_script", "/etc/kopano/userscripts/deleteuser", CONFIGSETTING_RELOADABLE },
  760. { "creategroup_script", "/etc/kopano/userscripts/creategroup", CONFIGSETTING_RELOADABLE },
  761. { "deletegroup_script", "/etc/kopano/userscripts/deletegroup", CONFIGSETTING_RELOADABLE},
  762. { "createcompany_script", "/etc/kopano/userscripts/createcompany", CONFIGSETTING_RELOADABLE },
  763. { "deletecompany_script", "/etc/kopano/userscripts/deletecompany", CONFIGSETTING_RELOADABLE },
  764. { "user_safe_mode", "no", CONFIGSETTING_RELOADABLE },
  765. // Storename format
  766. { "storename_format", "%f" },
  767. { "loginname_format", "%u" },
  768. // internal server contols
  769. { "softdelete_lifetime", "0", CONFIGSETTING_RELOADABLE }, // time expressed in days, 0 == never delete anything
  770. { "cache_cell_size", "16M", CONFIGSETTING_SIZE }, // default 16 Mb, default in config 256M
  771. { "cache_object_size", "16M", CONFIGSETTING_SIZE },
  772. { "cache_indexedobject_size", "32M", CONFIGSETTING_SIZE },
  773. { "cache_quota_size", "1M", CONFIGSETTING_SIZE }, // 1Mb
  774. { "cache_quota_lifetime", "1" }, // 1 minute
  775. { "cache_user_size", "1M", CONFIGSETTING_SIZE }, // 48 bytes per struct, can hold 21k+ users, allocated 2x (user and ueid cache)
  776. { "cache_userdetails_size", "25M", CONFIGSETTING_SIZE }, // 120 bytes per struct, can hold 21k+ users (was 2.5Mb, no float in size)
  777. { "cache_userdetails_lifetime", "5" }, // 5 minutes
  778. { "cache_acl_size", "1M", CONFIGSETTING_SIZE }, // 1Mb, acl table cache
  779. { "cache_store_size", "1M", CONFIGSETTING_SIZE }, // 1Mb, store table cache (storeid, storeguid), 40 bytes
  780. { "cache_server_size", "1M", CONFIGSETTING_SIZE }, // 1Mb
  781. { "cache_server_lifetime", "30" }, // 30 minutes
  782. // default no quota's. Note: quota values are in Mb, and thus have no size flag.
  783. { "quota_warn", "0", CONFIGSETTING_RELOADABLE },
  784. { "quota_soft", "0", CONFIGSETTING_RELOADABLE },
  785. { "quota_hard", "0", CONFIGSETTING_RELOADABLE },
  786. { "companyquota_warn", "0", CONFIGSETTING_RELOADABLE },
  787. { "companyquota_soft", "0", CONFIGSETTING_UNUSED },
  788. { "companyquota_hard", "0", CONFIGSETTING_UNUSED },
  789. { "session_timeout", "300", CONFIGSETTING_RELOADABLE }, // 5 minutes
  790. { "sync_lifetime", "365", CONFIGSETTING_RELOADABLE }, // 1 year
  791. { "sync_log_all_changes", "default" }, // Log All ICS changes
  792. { "auth_method", "plugin", CONFIGSETTING_RELOADABLE }, // plugin (default), pam, kerberos
  793. { "pam_service", "passwd", CONFIGSETTING_RELOADABLE }, // pam service, found in /etc/pam.d/
  794. { "enable_sso_ntlmauth", "no", CONFIGSETTING_UNUSED }, // default disables ntlm_auth, so we don't log errors on useless things
  795. { "enable_sso", "no", CONFIGSETTING_RELOADABLE }, // autodetect between Kerberos and NTLM
  796. { "session_ip_check", "yes", CONFIGSETTING_RELOADABLE }, // check session id comes from same ip address (or not)
  797. { "hide_everyone", "yes", CONFIGSETTING_RELOADABLE }, // whether internal group Everyone should be removed for users
  798. { "hide_system", "yes", CONFIGSETTING_RELOADABLE }, // whether internal user SYSTEM should be removed for users
  799. { "enable_gab", "yes", CONFIGSETTING_RELOADABLE }, // whether the GAB is enabled
  800. { "enable_enhanced_ics", "yes", CONFIGSETTING_RELOADABLE }, // (dis)allow enhanced ICS operations (stream and notifications)
  801. { "enable_sql_procedures", "no" }, // (dis)allow SQL procedures (requires mysql config stack adjustment), not reloadable because in the middle of the streaming flip
  802. { "report_path", "/etc/kopano/report", CONFIGSETTING_RELOADABLE },
  803. { "report_ca_path", "/etc/kopano/report-ca", CONFIGSETTING_RELOADABLE },
  804. { "cache_sortkey_size", "0", CONFIGSETTING_UNUSED }, // Option not support, only for backward compatibility of all configurations under the 6.20
  805. { "client_update_enabled", "no" },
  806. { "client_update_log_level", "1", CONFIGSETTING_RELOADABLE },
  807. { "client_update_path", "/var/lib/kopano/client", CONFIGSETTING_RELOADABLE },
  808. { "client_update_log_path", "/var/log/kopano/autoupdate", CONFIGSETTING_RELOADABLE },
  809. { "index_services_enabled", "", CONFIGSETTING_UNUSED },
  810. { "index_services_path", "", CONFIGSETTING_UNUSED },
  811. { "index_services_search_timeout", "", CONFIGSETTING_UNUSED },
  812. { "search_enabled", "yes", CONFIGSETTING_RELOADABLE },
  813. { "search_socket", "file:///var/run/kopano/search.sock", CONFIGSETTING_RELOADABLE },
  814. { "search_timeout", "10", CONFIGSETTING_RELOADABLE },
  815. { "threads", "8", CONFIGSETTING_RELOADABLE },
  816. { "watchdog_max_age", "500", CONFIGSETTING_RELOADABLE },
  817. { "watchdog_frequency", "1", CONFIGSETTING_RELOADABLE },
  818. { "folder_max_items", "1000000", CONFIGSETTING_RELOADABLE },
  819. { "default_sort_locale_id", "en_US", CONFIGSETTING_RELOADABLE },
  820. { "sync_gab_realtime", "yes", CONFIGSETTING_RELOADABLE },
  821. { "max_deferred_records", "0", CONFIGSETTING_RELOADABLE },
  822. { "max_deferred_records_folder", "20", CONFIGSETTING_RELOADABLE },
  823. { "enable_test_protocol", "no", CONFIGSETTING_RELOADABLE },
  824. { "disabled_features", "imap pop3", CONFIGSETTING_RELOADABLE },
  825. { "counter_reset", "yes", CONFIGSETTING_RELOADABLE },
  826. { "mysql_group_concat_max_len", "21844", CONFIGSETTING_RELOADABLE },
  827. { "restrict_admin_permissions", "no", 0 },
  828. { "embedded_attachment_limit", "20", CONFIGSETTING_RELOADABLE },
  829. { "proxy_header", "", CONFIGSETTING_RELOADABLE },
  830. { "owner_auto_full_access", "true" },
  831. { "attachment_files_fsync", "false", 0 },
  832. { "tmp_path", "/tmp" },
  833. { NULL, NULL },
  834. };
  835. // Init random generator
  836. rand_init();
  837. #if GSOAP_VERSION >= 20839
  838. /*
  839. * Init translations according to environment variables.
  840. * It also changes things like decimal separator, which gsoap < 2.8.39
  841. * fails to cope with properly.
  842. */
  843. setlocale(LC_ALL, "");
  844. #endif
  845. #if GSOAP_VERSION == 20841
  846. # error This gsoap version suffers from sf.net/p/gsoap2/bugs/1095
  847. #endif
  848. InitBindTextDomain();
  849. // Load settings
  850. g_lpConfig = ECConfig::Create(lpDefaults);
  851. if (!g_lpConfig->LoadSettings(szConfig) ||
  852. g_lpConfig->ParseParams(trim_argc, trim_argv) < 0 ||
  853. (!m_bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors()) ) {
  854. /* Create info logger without a timestamp to stderr. */
  855. g_lpLogger = new(std::nothrow) ECLogger_File(EC_LOGLEVEL_INFO, 0, "-", false);
  856. if (g_lpLogger == nullptr) {
  857. er = MAPI_E_NOT_ENOUGH_MEMORY;
  858. goto exit;
  859. }
  860. ec_log_set(g_lpLogger);
  861. LogConfigErrors(g_lpConfig);
  862. er = MAPI_E_UNCONFIGURED;
  863. goto exit;
  864. }
  865. kc_reexec_with_allocator(argv, g_lpConfig->GetSetting("allocator_library"));
  866. // setup logging
  867. g_lpLogger = CreateLogger(g_lpConfig, szName, "KopanoServer");
  868. if (!g_lpLogger) {
  869. fprintf(stderr, "Error in log configuration, unable to resume.\n");
  870. er = MAPI_E_UNCONFIGURED;
  871. goto exit;
  872. }
  873. ec_log_set(g_lpLogger);
  874. if (m_bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors())
  875. LogConfigErrors(g_lpConfig);
  876. if (!TmpPath::getInstance() -> OverridePath(g_lpConfig))
  877. ec_log_err("Ignoring invalid path-setting!");
  878. g_lpAudit = CreateLogger(g_lpConfig, szName, "KopanoServer", true);
  879. if (g_lpAudit)
  880. g_lpAudit->Log(EC_LOGLEVEL_NOTICE, "server startup uid=%d", getuid());
  881. else
  882. ec_log_info("Audit logging not enabled.");
  883. ec_log_always("Starting server version " PROJECT_VERSION_SERVER_STR ", pid %d", getpid());
  884. if (g_lpConfig->HasWarnings())
  885. LogConfigErrors(g_lpConfig);
  886. if (strcmp(g_lpConfig->GetSetting("sync_log_all_changes"), "default") != 0)
  887. ec_log_warn("sync_log_all_changes is deprecated, please remove from your config");
  888. if (strcmp(g_lpConfig->GetSetting("attachment_storage"), "files") == 0) {
  889. // directory will be created using startup (probably root) and then chowned to the new 'runas' username
  890. if (CreatePath(g_lpConfig->GetSetting("attachment_path")) != 0) {
  891. ec_log_err("Unable to create attachment directory '%s'", g_lpConfig->GetSetting("attachment_path"));
  892. er = KCERR_DATABASE_ERROR;
  893. goto exit;
  894. }
  895. if (stat(g_lpConfig->GetSetting("attachment_path"), &dir) != 0) {
  896. ec_log_err("Unable to stat attachment directory '%s', error: %s", g_lpConfig->GetSetting("attachment_path"), strerror(errno));
  897. er = KCERR_DATABASE_ERROR;
  898. goto exit;
  899. }
  900. runasUser = getpwnam(g_lpConfig->GetSetting("run_as_user","","root"));
  901. if (runasUser == NULL) {
  902. ec_log_err("Fatal: run_as_user '%s' is unknown", g_lpConfig->GetSetting("run_as_user","","root"));
  903. er = MAPI_E_UNCONFIGURED;
  904. goto exit;
  905. }
  906. if (runasUser->pw_uid != dir.st_uid) {
  907. if (unix_chown(g_lpConfig->GetSetting("attachment_path"), g_lpConfig->GetSetting("run_as_user"), g_lpConfig->GetSetting("run_as_group")) != 0) {
  908. ec_log_err("Unable to change ownership for attachment directory '%s'", g_lpConfig->GetSetting("attachment_path"));
  909. er = KCERR_DATABASE_ERROR;
  910. goto exit;
  911. }
  912. }
  913. #ifdef HAVE_LIBS3_H
  914. } else if (strcmp(g_lpConfig->GetSetting("attachment_storage"), "s3") == 0) {
  915. // @todo check S3 settings and connectivity
  916. ec_log_info("Attachment storage set to S3 Storage");
  917. #endif
  918. } else if (strcmp(g_lpConfig->GetSetting("attachment_storage"), "database") != 0) {
  919. ec_log_err("Unknown attachment_storage option '%s', reverting to default 'database' method.", g_lpConfig->GetSetting("attachment_storage"));
  920. g_lpConfig->AddSetting("attachment_storage", "database");
  921. }
  922. if (strcasecmp(g_lpConfig->GetSetting("user_plugin"), "db") == 0 && parseBool(g_lpConfig->GetSetting("sync_gab_realtime")) == false) {
  923. ec_log_info("Unsupported sync_gab_realtime = no when using DB plugin. Enabling sync_gab_realtime.");
  924. g_lpConfig->AddSetting("sync_gab_realtime", "yes");
  925. }
  926. kopano_notify_done = kcsrv_notify_done;
  927. kopano_get_server_stats = kcsrv_get_server_stats;
  928. kopano_initlibrary(g_lpConfig->GetSetting("mysql_database_path"), g_lpConfig->GetSetting("mysql_config_file"));
  929. if(!strcmp(g_lpConfig->GetSetting("server_pipe_enabled"), "yes"))
  930. bPipeEnabled = true;
  931. else
  932. bPipeEnabled = false;
  933. if(!strcmp(g_lpConfig->GetSetting("server_tcp_enabled"), "yes"))
  934. bTCPEnabled = true;
  935. else
  936. bTCPEnabled = false;
  937. if(!strcmp(g_lpConfig->GetSetting("server_ssl_enabled"), "yes"))
  938. bSSLEnabled = true;
  939. else
  940. bSSLEnabled = false;
  941. soap_ssl_init(); // Always call this in the main thread once!
  942. ssl_threading_setup();
  943. // setup connection handler
  944. g_lpSoapServerConn = new ECSoapServerConnection(g_lpConfig);
  945. // Setup a TCP connection
  946. if (bTCPEnabled)
  947. {
  948. er = g_lpSoapServerConn->ListenTCP(g_lpConfig->GetSetting("server_bind"), atoi(g_lpConfig->GetSetting("server_tcp_port")),
  949. parseBool(g_lpConfig->GetSetting("client_update_enabled")));
  950. if (er != erSuccess)
  951. goto exit;
  952. }
  953. // Setup SSL connection
  954. if (bSSLEnabled) {
  955. er = g_lpSoapServerConn->ListenSSL(g_lpConfig->GetSetting("server_bind"), // servername
  956. atoi(g_lpConfig->GetSetting("server_ssl_port")), // sslPort
  957. parseBool(g_lpConfig->GetSetting("client_update_enabled")), // HTTP GET command is enabled
  958. g_lpConfig->GetSetting("server_ssl_key_file","",NULL), // key file
  959. g_lpConfig->GetSetting("server_ssl_key_pass","",NULL), // key password
  960. g_lpConfig->GetSetting("server_ssl_ca_file","",NULL), // CA certificate file which signed clients
  961. g_lpConfig->GetSetting("server_ssl_ca_path","",NULL) // CA certificate path of thrusted sources
  962. );
  963. if (er != erSuccess)
  964. goto exit;
  965. }
  966. struct rlimit limit;
  967. limit.rlim_cur = KC_DESIRED_FILEDES;
  968. limit.rlim_max = KC_DESIRED_FILEDES;
  969. if(setrlimit(RLIMIT_NOFILE, &limit) < 0) {
  970. ec_log_warn("setrlimit(RLIMIT_NOFILE, %d) failed, you will only be able to connect up to %d sockets.", KC_DESIRED_FILEDES, getdtablesize());
  971. ec_log_warn("WARNING: Either start the process as root, or increase user limits for open file descriptors.");
  972. }
  973. if (parseBool(g_lpConfig->GetSetting("coredump_enabled")))
  974. unix_coredump_enable();
  975. if (unix_runas(g_lpConfig)) {
  976. er = MAPI_E_CALL_FAILED;
  977. goto exit;
  978. }
  979. // Priority queue is always enabled, create as first socket, so this socket is returned first too on activity
  980. er = g_lpSoapServerConn->ListenPipe(g_lpConfig->GetSetting("server_pipe_priority"), true);
  981. if (er != erSuccess)
  982. goto exit;
  983. // Setup a pipe connection
  984. if (bPipeEnabled) {
  985. er = g_lpSoapServerConn->ListenPipe(g_lpConfig->GetSetting("server_pipe_name"));
  986. if (er != erSuccess)
  987. goto exit;
  988. }
  989. // Test database settings
  990. lpDatabaseFactory = new ECDatabaseFactory(g_lpConfig);
  991. // open database
  992. er = lpDatabaseFactory->CreateDatabaseObject(&lpDatabase, dbError);
  993. if(er == KCERR_DATABASE_NOT_FOUND) {
  994. er = lpDatabaseFactory->CreateDatabase();
  995. if (er != erSuccess)
  996. goto exit;
  997. er = lpDatabaseFactory->CreateDatabaseObject(&lpDatabase, dbError);
  998. }
  999. if(er != erSuccess) {
  1000. ec_log_crit("Unable to connect to database: %s", dbError.c_str());
  1001. goto exit;
  1002. }
  1003. ec_log_notice("Connection to database '%s' succeeded", g_lpConfig->GetSetting("mysql_database"));
  1004. hosted = parseBool(g_lpConfig->GetSetting("enable_hosted_kopano"));
  1005. distributed = parseBool(g_lpConfig->GetSetting("enable_distributed_kopano"));
  1006. // fork if needed and drop privileges as requested.
  1007. // this must be done before we do anything with pthreads
  1008. if (daemonize && unix_daemonize(g_lpConfig)) {
  1009. er = MAPI_E_CALL_FAILED;
  1010. goto exit;
  1011. }
  1012. if (!daemonize)
  1013. setsid();
  1014. unix_create_pidfile(szName, g_lpConfig);
  1015. mainthread = pthread_self();
  1016. // SIGSEGV backtrace support
  1017. memset(&st, 0, sizeof(st));
  1018. memset(&act, 0, sizeof(act));
  1019. st.ss_sp = malloc(65536);
  1020. st.ss_flags = 0;
  1021. st.ss_size = 65536;
  1022. act.sa_sigaction = sigsegv;
  1023. act.sa_flags = SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
  1024. sigemptyset(&act.sa_mask);
  1025. sigaltstack(&st, NULL);
  1026. sigaction(SIGSEGV, &act, NULL);
  1027. sigaction(SIGBUS , &act, NULL);
  1028. sigaction(SIGABRT, &act, NULL);
  1029. // normally ignore these signals
  1030. signal(SIGUSR1, SIG_IGN);
  1031. signal(SIGUSR2, SIG_IGN);
  1032. signal(SIGPIPE, SIG_IGN);
  1033. act.sa_handler = process_signal;
  1034. act.sa_flags = SA_ONSTACK | SA_RESTART;
  1035. sigemptyset(&act.sa_mask);
  1036. sigaction(SIGINT, &act, nullptr);
  1037. sigaction(SIGHUP, &act, nullptr);
  1038. sigaction(SIGTERM, &act, nullptr);
  1039. // ignore ignorable signals that might stop the server during database upgrade
  1040. // all these signals will be reset after the database upgrade part.
  1041. m_bDatabaseUpdateIgnoreSignals = true;
  1042. // add a lock file to disable the /etc/init.d scripts
  1043. tmplock = open(upgrade_lock_file, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
  1044. if (tmplock == -1)
  1045. ec_log_warn("WARNING: Unable to place upgrade lockfile: %s", strerror(errno));
  1046. #ifdef EMBEDDED_MYSQL
  1047. {
  1048. unsigned int ulResult = 0;
  1049. // setting upgrade_tables
  1050. // 1 = upgrade from mysql 4.1.23 to 5.22
  1051. if(GetDatabaseSettingAsInteger(lpDatabase, "upgrade_tables", &ulResult) != erSuccess || ulResult == 0) {
  1052. er = lpDatabase->ValidateTables();
  1053. if (er != erSuccess) {
  1054. ec_log_err("Unable to validate the database.");
  1055. goto exit;
  1056. }
  1057. SetDatabaseSetting(lpDatabase, "upgrade_tables", 1);
  1058. }
  1059. }
  1060. #endif
  1061. // perform database upgrade .. may take a very long time
  1062. er = lpDatabaseFactory->UpdateDatabase(m_bForceDatabaseUpdate, dbError);
  1063. // remove lock file
  1064. if (tmplock != -1) {
  1065. if (unlink(upgrade_lock_file) == -1)
  1066. ec_log_warn("WARNING: Unable to delete upgrade lockfile (%s): %s", upgrade_lock_file, strerror(errno));
  1067. close(tmplock);
  1068. }
  1069. if(er == KCERR_INVALID_VERSION) {
  1070. ec_log_warn("WARNING: %s", dbError.c_str());
  1071. if(m_bIgnoreDatabaseVersionConflict == false) {
  1072. ec_log_warn(" You can force the server to start with --ignore-database-version-conflict");
  1073. ec_log_warn(" Warning, you can lose data! If you don't know what you're doing, you shouldn't be using this option!");
  1074. goto exit;
  1075. }
  1076. }else if(er != erSuccess) {
  1077. if (er != KCERR_USER_CANCEL)
  1078. ec_log_err("Can't update the database: %s", dbError.c_str());
  1079. goto exit;
  1080. }
  1081. er = lpDatabase->InitializeDBState();
  1082. if(er != erSuccess) {
  1083. ec_log_err("Can't initialize database settings");
  1084. goto exit;
  1085. }
  1086. m_bDatabaseUpdateIgnoreSignals = false;
  1087. if(searchfolder_restart_required) {
  1088. ec_log_warn("Update requires searchresult folders to be rebuilt. This may take some time. You can restart this process with the --restart-searches option");
  1089. restart_searches = 1;
  1090. }
  1091. // check database for MyISAM tables
  1092. er = check_database_innodb(lpDatabase);
  1093. if (er != erSuccess)
  1094. goto exit;
  1095. // check attachment database started with, and maybe reject startup
  1096. er = check_database_attachments(lpDatabase);
  1097. if (er != erSuccess)
  1098. goto exit;
  1099. // check you can write into the file attachment storage
  1100. er = check_attachment_storage_permissions();
  1101. if (er != erSuccess)
  1102. goto exit;
  1103. // check upgrade problem with wrong sequence in tproperties table primary key
  1104. er = check_database_tproperties_key(lpDatabase);
  1105. if (er != erSuccess)
  1106. goto exit;
  1107. // check distributed mode started with, and maybe reject startup
  1108. er = check_distributed_kopano(lpDatabase);
  1109. if (er != erSuccess)
  1110. goto exit;
  1111. // check whether the thread_stack is large enough.
  1112. er = check_database_thread_stack(lpDatabase);
  1113. if (er != erSuccess)
  1114. goto exit;
  1115. //Init the main system, now you can use the values like session manager
  1116. // This also starts several threads, like SessionCleaner, NotificationThread and TPropsPurge.
  1117. er = kopano_init(g_lpConfig, g_lpAudit, hosted, distributed);
  1118. if (er != erSuccess) { // create SessionManager
  1119. ec_log_err("Unable to initialize kopano session manager");
  1120. goto exit;
  1121. }
  1122. // check for conflicting settings in local config and LDAP, after kopano_init since this needs the sessionmanager.
  1123. er = check_server_configuration();
  1124. if (er != erSuccess)
  1125. goto exit;
  1126. // Load search folders from disk
  1127. ec_log_notice("Loading searchfolders");
  1128. er = g_lpSessionManager->GetSearchFolders()->LoadSearchFolders();
  1129. if (er != erSuccess) {
  1130. ec_log_err("Unable to load searchfolders");
  1131. goto exit;
  1132. }
  1133. if (restart_searches) // restart_searches if specified
  1134. g_lpSessionManager->GetSearchFolders()->RestartSearches();
  1135. // Create scheduler system
  1136. g_lpScheduler = new ECScheduler(g_lpLogger);
  1137. // Add a task on the scheduler
  1138. g_lpScheduler->AddSchedule(SCHEDULE_HOUR, 00, &SoftDeleteRemover, (void*)&g_Quit);
  1139. g_lpScheduler->AddSchedule(SCHEDULE_HOUR, 15, &CleanupSyncsTable);
  1140. g_lpScheduler->AddSchedule(SCHEDULE_HOUR, 16, &CleanupSyncedMessagesTable);
  1141. // high loglevel to always see when server is started.
  1142. ec_log_notice("Startup succeeded on pid %d", getpid() );
  1143. g_lpStatsCollector->SetTime(SCN_SERVER_STARTTIME, time(NULL));
  1144. // Enter main accept loop
  1145. retval = 0;
  1146. while(!g_Quit) {
  1147. // Select on the sockets
  1148. er = g_lpSoapServerConn->MainLoop();
  1149. if(er == KCERR_NETWORK_ERROR) {
  1150. retval = -1;
  1151. break; // Exit loop
  1152. } else if (er != erSuccess) {
  1153. continue;
  1154. }
  1155. }
  1156. // Close All sessions
  1157. kopano_removeallsessions();
  1158. exit:
  1159. if (er != erSuccess) {
  1160. auto msg = format("An error occurred (%x).", er);
  1161. if (g_lpConfig)
  1162. msg += format(" Please check %s for details.", g_lpConfig->GetSetting("log_file"));
  1163. else
  1164. msg += " Please check logfile for details.";
  1165. fprintf(stderr, "\n%s\n\n", msg.c_str());
  1166. }
  1167. if (g_lpAudit)
  1168. g_lpAudit->Log(EC_LOGLEVEL_ALWAYS, "server shutdown in progress");
  1169. delete g_lpSoapServerConn;
  1170. delete g_lpScheduler;
  1171. free(st.ss_sp);
  1172. delete lpDatabase;
  1173. delete lpDatabaseFactory;
  1174. kopano_exit();
  1175. ssl_threading_cleanup();
  1176. SSL_library_cleanup(); //cleanup memory so valgrind is happy
  1177. kopano_unloadlibrary();
  1178. rand_free();
  1179. delete g_lpConfig;
  1180. if (g_lpLogger) {
  1181. ec_log_always("Server shutdown complete.");
  1182. g_lpLogger->Release();
  1183. }
  1184. if (g_lpAudit)
  1185. g_lpAudit->Release();
  1186. // cleanup ICU data so valgrind is happy
  1187. u_cleanup();
  1188. return retval;
  1189. }