You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cfg_utils.c 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. /*
  2. * Copyright (c) 2009-2012, Vsevolod Stakhov
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  17. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  18. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  20. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  22. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. #include "config.h"
  25. #include "cfg_file.h"
  26. #include "main.h"
  27. #include "filter.h"
  28. #include "settings.h"
  29. #include "classifiers/classifiers.h"
  30. #include "cfg_xml.h"
  31. #include "lua/lua_common.h"
  32. #include "kvstorage_config.h"
  33. #include "map.h"
  34. #include "dynamic_cfg.h"
  35. #define DEFAULT_SCORE 10.0
  36. #define DEFAULT_RLIMIT_NOFILE 2048
  37. #define DEFAULT_RLIMIT_MAXCORE 0
  38. #define DEFAULT_MAP_TIMEOUT 10
  39. gboolean
  40. parse_host_port_priority (memory_pool_t *pool, const gchar *str, gchar **addr, guint16 *port, guint *priority)
  41. {
  42. gchar **tokens, *err_str, *cur_tok;
  43. struct addrinfo hints, *res;
  44. guint port_parsed, priority_parsed, saved_errno = errno;
  45. gint r;
  46. union {
  47. struct sockaddr_in v4;
  48. struct sockaddr_in6 v6;
  49. } addr_holder;
  50. tokens = g_strsplit_set (str, ":", 0);
  51. if (!tokens || !tokens[0]) {
  52. return FALSE;
  53. }
  54. /* Now try to parse host and write address to ina */
  55. memset (&hints, 0, sizeof (hints));
  56. hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
  57. hints.ai_socktype = SOCK_STREAM; /* Type of the socket */
  58. hints.ai_flags = 0;
  59. hints.ai_protocol = 0; /* Any protocol */
  60. hints.ai_canonname = NULL;
  61. hints.ai_addr = NULL;
  62. hints.ai_next = NULL;
  63. if (strcmp (tokens[0], "*") == 0) {
  64. /* XXX: actually we still cannot listen on multiply protocols */
  65. if (pool != NULL) {
  66. *addr = memory_pool_alloc (pool, INET_ADDRSTRLEN + 1);
  67. }
  68. rspamd_strlcpy (*addr, "0.0.0.0", INET_ADDRSTRLEN + 1);
  69. goto port_parse;
  70. }
  71. else {
  72. cur_tok = tokens[0];
  73. }
  74. if ((r = getaddrinfo (cur_tok, NULL, &hints, &res)) == 0) {
  75. memcpy (&addr_holder, res->ai_addr, MIN (sizeof (addr_holder), res->ai_addrlen));
  76. if (res->ai_family == AF_INET) {
  77. if (pool != NULL) {
  78. *addr = memory_pool_alloc (pool, INET_ADDRSTRLEN + 1);
  79. }
  80. inet_ntop (res->ai_family, &addr_holder.v4.sin_addr, *addr, INET_ADDRSTRLEN + 1);
  81. }
  82. else {
  83. if (pool != NULL) {
  84. *addr = memory_pool_alloc (pool, INET6_ADDRSTRLEN + 1);
  85. }
  86. inet_ntop (res->ai_family, &addr_holder.v6.sin6_addr, *addr, INET6_ADDRSTRLEN + 1);
  87. }
  88. freeaddrinfo (res);
  89. }
  90. else {
  91. msg_err ("address resolution for %s failed: %s", tokens[0], gai_strerror (r));
  92. goto err;
  93. }
  94. port_parse:
  95. if (tokens[1] != NULL) {
  96. /* Port part */
  97. if (port != NULL) {
  98. errno = 0;
  99. port_parsed = strtoul (tokens[1], &err_str, 10);
  100. if (*err_str != '\0' || errno != 0) {
  101. msg_warn ("cannot parse port: %s, at symbol %c, error: %s", tokens[1], *err_str, strerror (errno));
  102. goto err;
  103. }
  104. if (port_parsed > G_MAXUINT16) {
  105. errno = ERANGE;
  106. msg_warn ("cannot parse port: %s, error: %s", tokens[1], *err_str, strerror (errno));
  107. goto err;
  108. }
  109. *port = port_parsed;
  110. }
  111. if (priority != NULL) {
  112. if (port != NULL) {
  113. cur_tok = tokens[2];
  114. }
  115. else {
  116. cur_tok = tokens[1];
  117. }
  118. if (cur_tok != NULL) {
  119. /* Priority part */
  120. errno = 0;
  121. priority_parsed = strtoul (cur_tok, &err_str, 10);
  122. if (*err_str != '\0' || errno != 0) {
  123. msg_warn ("cannot parse priority: %s, at symbol %c, error: %s", tokens[1], *err_str, strerror (errno));
  124. goto err;
  125. }
  126. *priority = priority_parsed;
  127. }
  128. }
  129. }
  130. /* Restore errno */
  131. errno = saved_errno;
  132. g_strfreev (tokens);
  133. return TRUE;
  134. err:
  135. errno = saved_errno;
  136. g_strfreev (tokens);
  137. return FALSE;
  138. }
  139. gboolean
  140. parse_host_port (memory_pool_t *pool, const gchar *str, gchar **addr, guint16 *port)
  141. {
  142. return parse_host_port_priority (pool, str, addr, port, NULL);
  143. }
  144. gboolean
  145. parse_host_priority (memory_pool_t *pool, const gchar *str, gchar **addr, guint *priority)
  146. {
  147. return parse_host_port_priority (pool, str, addr, NULL, priority);
  148. }
  149. gint
  150. parse_bind_line (struct config_file *cfg, struct worker_conf *cf, gchar *str)
  151. {
  152. gchar **host;
  153. guint16 *family, *port;
  154. gchar **addr;
  155. if (str == NULL)
  156. return 0;
  157. host = &cf->bind_host;
  158. port = &cf->bind_port;
  159. *port = DEFAULT_BIND_PORT;
  160. family = &cf->bind_family;
  161. addr = &cf->bind_addr;
  162. if (str[0] == '/' || str[0] == '.') {
  163. #ifdef HAVE_DIRNAME
  164. /* Try to check path of bind credit */
  165. struct stat st;
  166. gint fd;
  167. gchar *copy = memory_pool_strdup (cfg->cfg_pool, str);
  168. if (stat (copy, &st) == -1) {
  169. if (errno == ENOENT) {
  170. if ((fd = open (str, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) {
  171. msg_err ("cannot open path %s for making socket, %s", str, strerror (errno));
  172. return 0;
  173. }
  174. else {
  175. close (fd);
  176. unlink (str);
  177. }
  178. }
  179. else {
  180. msg_err ("cannot stat path %s for making socket, %s", str, strerror (errno));
  181. return 0;
  182. }
  183. }
  184. else {
  185. if (unlink (str) == -1) {
  186. msg_err ("cannot remove path %s for making socket, %s", str, strerror (errno));
  187. return 0;
  188. }
  189. }
  190. #endif
  191. *host = memory_pool_strdup (cfg->cfg_pool, str);
  192. *family = AF_UNIX;
  193. return 1;
  194. }
  195. else {
  196. if (parse_host_port (cfg->cfg_pool, str, addr, port)) {
  197. *host = memory_pool_strdup (cfg->cfg_pool, str);
  198. *family = AF_INET;
  199. return 1;
  200. }
  201. }
  202. return 0;
  203. }
  204. void
  205. init_defaults (struct config_file *cfg)
  206. {
  207. cfg->memcached_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
  208. cfg->memcached_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
  209. cfg->memcached_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
  210. cfg->memcached_protocol = TCP_TEXT;
  211. cfg->dns_timeout = 1000;
  212. cfg->dns_retransmits = 5;
  213. /* After 20 errors do throttling for 10 seconds */
  214. cfg->dns_throttling_errors = 20;
  215. cfg->dns_throttling_time = 10000;
  216. cfg->statfile_sync_interval = 60000;
  217. cfg->statfile_sync_timeout = 20000;
  218. /* 20 Kb */
  219. cfg->max_diff = 20480;
  220. cfg->max_statfile_size = DEFAULT_STATFILE_SIZE;
  221. cfg->modules_opts = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  222. cfg->modules_metas = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  223. cfg->variables = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  224. cfg->metrics = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  225. cfg->c_modules = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  226. cfg->composite_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  227. cfg->classifiers_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  228. cfg->cfg_params = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  229. cfg->metrics_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  230. cfg->map_timeout = DEFAULT_MAP_TIMEOUT;
  231. cfg->log_level = G_LOG_LEVEL_WARNING;
  232. cfg->log_extended = TRUE;
  233. init_settings (cfg);
  234. }
  235. void
  236. free_config (struct config_file *cfg)
  237. {
  238. GList *cur;
  239. struct symbols_group *gr;
  240. remove_all_maps (cfg);
  241. g_hash_table_remove_all (cfg->modules_opts);
  242. g_hash_table_unref (cfg->modules_opts);
  243. g_hash_table_unref (cfg->modules_metas);
  244. g_hash_table_remove_all (cfg->variables);
  245. g_hash_table_unref (cfg->variables);
  246. g_hash_table_remove_all (cfg->metrics);
  247. g_hash_table_unref (cfg->metrics);
  248. g_hash_table_remove_all (cfg->c_modules);
  249. g_hash_table_unref (cfg->c_modules);
  250. g_hash_table_remove_all (cfg->composite_symbols);
  251. g_hash_table_unref (cfg->composite_symbols);
  252. g_hash_table_remove_all (cfg->cfg_params);
  253. g_hash_table_unref (cfg->cfg_params);
  254. g_hash_table_destroy (cfg->metrics_symbols);
  255. g_hash_table_destroy (cfg->classifiers_symbols);
  256. /* Free symbols groups */
  257. cur = cfg->symbols_groups;
  258. while (cur) {
  259. gr = cur->data;
  260. if (gr->symbols) {
  261. g_list_free (gr->symbols);
  262. }
  263. cur = g_list_next (cur);
  264. }
  265. if (cfg->symbols_groups) {
  266. g_list_free (cfg->symbols_groups);
  267. }
  268. if (cfg->checksum) {
  269. g_free (cfg->checksum);
  270. }
  271. g_list_free (cfg->classifiers);
  272. g_list_free (cfg->metrics_list);
  273. memory_pool_delete (cfg->cfg_pool);
  274. }
  275. gchar *
  276. get_module_opt (struct config_file *cfg, gchar *module_name, gchar *opt_name)
  277. {
  278. GList *cur_opt;
  279. struct module_opt *cur;
  280. static gchar numbuf[64];
  281. cur_opt = g_hash_table_lookup (cfg->modules_opts, module_name);
  282. if (cur_opt == NULL) {
  283. return NULL;
  284. }
  285. while (cur_opt) {
  286. cur = cur_opt->data;
  287. if (strcmp (cur->param, opt_name) == 0) {
  288. /* Check if it is lua variable */
  289. if (! cur->is_lua) {
  290. /* Plain variable */
  291. return cur->value;
  292. }
  293. else {
  294. /* Check type */
  295. switch (cur->lua_type) {
  296. case LUA_VAR_NUM:
  297. /* numbuf is static, so it is safe to return it "as is" */
  298. snprintf (numbuf, sizeof (numbuf), "%f", *(double *)cur->actual_data);
  299. return numbuf;
  300. case LUA_VAR_BOOLEAN:
  301. snprintf (numbuf, sizeof (numbuf), "%s", *(gboolean *)cur->actual_data ? "yes" : "no");
  302. return numbuf;
  303. case LUA_VAR_STRING:
  304. return (gchar *)cur->actual_data;
  305. case LUA_VAR_FUNCTION:
  306. msg_info ("option %s is dynamic, so it cannot be aqquired statically", opt_name);
  307. return NULL;
  308. case LUA_VAR_UNKNOWN:
  309. msg_info ("option %s has unknown type, maybe there is error in LUA code", opt_name);
  310. return NULL;
  311. }
  312. }
  313. }
  314. cur_opt = g_list_next (cur_opt);
  315. }
  316. return NULL;
  317. }
  318. guint64
  319. parse_limit (const gchar *limit, guint len)
  320. {
  321. guint64 result = 0;
  322. const gchar *err_str;
  323. if (!limit || *limit == '\0' || len == 0) {
  324. return 0;
  325. }
  326. errno = 0;
  327. result = strtoull (limit, (gchar **)&err_str, 10);
  328. if (*err_str != '\0') {
  329. /* Megabytes */
  330. if (*err_str == 'm' || *err_str == 'M') {
  331. result *= 1048576L;
  332. }
  333. /* Kilobytes */
  334. else if (*err_str == 'k' || *err_str == 'K') {
  335. result *= 1024;
  336. }
  337. /* Gigabytes */
  338. else if (*err_str == 'g' || *err_str == 'G') {
  339. result *= 1073741824L;
  340. }
  341. else if (len > 0 && err_str - limit != (gint)len) {
  342. msg_warn ("invalid limit value '%s' at position '%s'", limit, err_str);
  343. result = 0;
  344. }
  345. }
  346. return result;
  347. }
  348. gdouble
  349. cfg_parse_time (const gchar *t, enum time_type default_type)
  350. {
  351. union {
  352. guint i;
  353. double d;
  354. } result;
  355. gboolean use_double = FALSE;
  356. gchar *err_str;
  357. if (!t || *t == '\0')
  358. return 0;
  359. errno = 0;
  360. result.i = strtoul (t, &err_str, 10);
  361. if (*err_str != '\0') {
  362. if (*err_str == '.') {
  363. /* Try to handle decimal point */
  364. errno = 0;
  365. result.d = strtod (t, &err_str);
  366. use_double = TRUE;
  367. }
  368. /* Seconds */
  369. if (*err_str == 's' || *err_str == 'S') {
  370. if (use_double) {
  371. result.d *= 1000.;
  372. }
  373. else {
  374. result.i *= 1000;
  375. }
  376. }
  377. /* Minutes */
  378. else if (*err_str == 'm' || *err_str == 'M') {
  379. /* Handle ms correctly */
  380. if (*(err_str + 1) != 's' && *(err_str + 1) != 'S') {
  381. if (use_double) {
  382. result.d *= 60. * 1000.;
  383. }
  384. else {
  385. result.i *= 60 * 1000;
  386. }
  387. }
  388. }
  389. /* Hours */
  390. else if (*err_str == 'h' || *err_str == 'H') {
  391. if (use_double) {
  392. result.d *= 60. * 60. * 1000.;
  393. }
  394. else {
  395. result.i *= 60 * 60 * 1000;
  396. }
  397. }
  398. /* Days */
  399. else if (*err_str == 'd' || *err_str == 'D') {
  400. if (use_double) {
  401. result.d *= 24. * 60. * 60. * 1000.;
  402. }
  403. else {
  404. result.i *= 24 * 60 * 60 * 1000;
  405. }
  406. }
  407. else {
  408. msg_warn ("invalid time value '%s' at position '%s'", t, err_str);
  409. if (use_double) {
  410. result.d = 0.;
  411. }
  412. else {
  413. result.i = 0;
  414. }
  415. }
  416. }
  417. else {
  418. /* Switch to default time multiplier */
  419. switch (default_type) {
  420. case TIME_HOURS:
  421. if (use_double) {
  422. result.d *= 60. * 60. * 1000.;
  423. }
  424. else {
  425. result.i *= 60 * 60 * 1000;
  426. }
  427. break;
  428. case TIME_MINUTES:
  429. if (use_double) {
  430. result.d *= 60. * 1000.;
  431. }
  432. else {
  433. result.i *= 60 * 1000;
  434. }
  435. break;
  436. case TIME_SECONDS:
  437. if (use_double) {
  438. result.d *= 1000.;
  439. }
  440. else {
  441. result.i *= 1000;
  442. }
  443. break;
  444. case TIME_MILLISECONDS:
  445. break;
  446. }
  447. }
  448. if (use_double) {
  449. return result.d;
  450. }
  451. else {
  452. return (gdouble)result.i;
  453. }
  454. }
  455. gchar
  456. parse_flag (const gchar *str)
  457. {
  458. guint len;
  459. gchar c;
  460. if (!str || !*str) {
  461. return -1;
  462. }
  463. len = strlen (str);
  464. switch (len) {
  465. case 1:
  466. c = g_ascii_tolower (*str);
  467. if (c == 'y' || c == '1') {
  468. return 1;
  469. }
  470. else if (c == 'n' || c == '0') {
  471. return 0;
  472. }
  473. break;
  474. case 2:
  475. if (g_ascii_strncasecmp (str, "no", len) == 0) {
  476. return 0;
  477. }
  478. else if (g_ascii_strncasecmp (str, "on", len) == 0) {
  479. return 1;
  480. }
  481. break;
  482. case 3:
  483. if (g_ascii_strncasecmp (str, "yes", len) == 0) {
  484. return 1;
  485. }
  486. else if (g_ascii_strncasecmp (str, "off", len) == 0) {
  487. return 0;
  488. }
  489. break;
  490. case 4:
  491. if (g_ascii_strncasecmp (str, "true", len) == 0) {
  492. return 1;
  493. }
  494. break;
  495. case 5:
  496. if (g_ascii_strncasecmp (str, "false", len) == 0) {
  497. return 0;
  498. }
  499. break;
  500. }
  501. return -1;
  502. }
  503. /*
  504. * Try to substitute all variables in given string
  505. * Return: newly allocated string with substituted variables (original string may be freed if variables are found)
  506. */
  507. gchar *
  508. substitute_variable (struct config_file *cfg, gchar *name, gchar *str, guchar recursive)
  509. {
  510. gchar *var, *new, *v_begin, *v_end, *p, t;
  511. gsize len;
  512. gboolean changed = FALSE;
  513. if (str == NULL) {
  514. msg_warn ("trying to substitute variable in NULL string");
  515. return NULL;
  516. }
  517. p = str;
  518. while ((v_begin = strstr (p, "${")) != NULL) {
  519. len = strlen (str);
  520. *v_begin = '\0';
  521. v_begin += 2;
  522. if ((v_end = strstr (v_begin, "}")) == NULL) {
  523. /* Not a variable, skip */
  524. p = v_begin;
  525. continue;
  526. }
  527. t = *v_end;
  528. *v_end = '\0';
  529. var = g_hash_table_lookup (cfg->variables, v_begin);
  530. if (var == NULL) {
  531. msg_warn ("variable %s is not defined", v_begin);
  532. *v_end = t;
  533. p = v_end + 1;
  534. continue;
  535. }
  536. else if (recursive) {
  537. var = substitute_variable (cfg, v_begin, var, recursive);
  538. }
  539. /* Allocate new string */
  540. new = memory_pool_alloc (cfg->cfg_pool, len - strlen (v_begin) + strlen (var) + 3);
  541. snprintf (new, len - strlen (v_begin) + strlen (var) + 3, "%s(%s)%s", str, var, v_end + 1);
  542. p = new;
  543. str = new;
  544. changed = TRUE;
  545. }
  546. if (changed && name != NULL) {
  547. g_hash_table_insert (cfg->variables, name, str);
  548. }
  549. return str;
  550. }
  551. static void
  552. substitute_module_variables (gpointer key, gpointer value, gpointer data)
  553. {
  554. struct config_file *cfg = (struct config_file *)data;
  555. GList *cur_opt = (GList *) value;
  556. struct module_opt *cur;
  557. while (cur_opt) {
  558. cur = cur_opt->data;
  559. if (cur->value) {
  560. cur->value = substitute_variable (cfg, NULL, cur->value, 1);
  561. }
  562. cur_opt = g_list_next (cur_opt);
  563. }
  564. }
  565. static void
  566. substitute_all_variables (gpointer key, gpointer value, gpointer data)
  567. {
  568. struct config_file *cfg = (struct config_file *)data;
  569. /* Do recursive substitution */
  570. (void)substitute_variable (cfg, (gchar *)key, (gchar *)value, 1);
  571. }
  572. /*
  573. * Place pointers to cfg_file structure to hash cfg_params
  574. */
  575. static void
  576. fill_cfg_params (struct config_file *cfg)
  577. {
  578. struct config_scalar *scalars;
  579. scalars = memory_pool_alloc (cfg->cfg_pool, 10 * sizeof (struct config_scalar));
  580. scalars[0].type = SCALAR_TYPE_STR;
  581. scalars[0].pointer = &cfg->cfg_name;
  582. g_hash_table_insert (cfg->cfg_params, "cfg_name", &scalars[0]);
  583. scalars[1].type = SCALAR_TYPE_STR;
  584. scalars[1].pointer = &cfg->pid_file;
  585. g_hash_table_insert (cfg->cfg_params, "pid_file", &scalars[1]);
  586. scalars[2].type = SCALAR_TYPE_STR;
  587. scalars[2].pointer = &cfg->temp_dir;
  588. g_hash_table_insert (cfg->cfg_params, "temp_dir", &scalars[2]);
  589. scalars[3].type = SCALAR_TYPE_SIZE;
  590. scalars[3].pointer = &cfg->max_statfile_size;
  591. g_hash_table_insert (cfg->cfg_params, "max_statfile_size", &scalars[3]);
  592. }
  593. gboolean
  594. get_config_checksum (struct config_file *cfg)
  595. {
  596. gint fd;
  597. void *map;
  598. struct stat st;
  599. /* Compute checksum for config file that should be used by xml dumper */
  600. if ((fd = open (cfg->cfg_name, O_RDONLY)) == -1) {
  601. msg_err ("config file %s is no longer available, cannot calculate checksum");
  602. return FALSE;
  603. }
  604. if (stat (cfg->cfg_name, &st) == -1) {
  605. msg_err ("cannot stat %s: %s", cfg->cfg_name, strerror (errno));
  606. return FALSE;
  607. }
  608. /* Now mmap this file to simplify reading process */
  609. if ((map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
  610. msg_err ("cannot mmap %s: %s", cfg->cfg_name, strerror (errno));
  611. close (fd);
  612. return FALSE;
  613. }
  614. close (fd);
  615. /* Get checksum for a file */
  616. cfg->checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, map, st.st_size);
  617. munmap (map, st.st_size);
  618. return TRUE;
  619. }
  620. /*
  621. * Perform post load actions
  622. */
  623. void
  624. post_load_config (struct config_file *cfg)
  625. {
  626. #ifdef HAVE_CLOCK_GETTIME
  627. struct timespec ts;
  628. #endif
  629. struct metric *def_metric;
  630. g_hash_table_foreach (cfg->variables, substitute_all_variables, cfg);
  631. g_hash_table_foreach (cfg->modules_opts, substitute_module_variables, cfg);
  632. fill_cfg_params (cfg);
  633. #ifdef HAVE_CLOCK_GETTIME
  634. #ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID
  635. clock_getres (CLOCK_PROCESS_CPUTIME_ID, &ts);
  636. # elif defined(HAVE_CLOCK_VIRTUAL)
  637. clock_getres (CLOCK_VIRTUAL, &ts);
  638. # else
  639. clock_getres (CLOCK_REALTIME, &ts);
  640. # endif
  641. cfg->clock_res = (gint)log10 (1000000 / ts.tv_nsec);
  642. if (cfg->clock_res < 0) {
  643. cfg->clock_res = 0;
  644. }
  645. if (cfg->clock_res > 3) {
  646. cfg->clock_res = 3;
  647. }
  648. #else
  649. /* For gettimeofday */
  650. cfg->clock_res = 1;
  651. #endif
  652. if ((def_metric = g_hash_table_lookup (cfg->metrics, DEFAULT_METRIC)) == NULL) {
  653. def_metric = check_metric_conf (cfg, NULL);
  654. def_metric->name = DEFAULT_METRIC;
  655. def_metric->required_score = DEFAULT_SCORE;
  656. def_metric->reject_score = DEFAULT_REJECT_SCORE;
  657. cfg->metrics_list = g_list_prepend (cfg->metrics_list, def_metric);
  658. g_hash_table_insert (cfg->metrics, DEFAULT_METRIC, def_metric);
  659. }
  660. cfg->default_metric = def_metric;
  661. /* Lua options */
  662. (void)lua_post_load_config (cfg);
  663. init_dynamic_config (cfg);
  664. }
  665. #if 0
  666. void
  667. parse_err (const gchar *fmt, ...)
  668. {
  669. va_list aq;
  670. gchar logbuf[BUFSIZ], readbuf[32];
  671. gint r;
  672. va_start (aq, fmt);
  673. rspamd_strlcpy (readbuf, yytext, sizeof (readbuf));
  674. r = snprintf (logbuf, sizeof (logbuf), "config file parse error! line: %d, text: %s, reason: ", yylineno, readbuf);
  675. r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
  676. va_end (aq);
  677. g_critical ("%s", logbuf);
  678. }
  679. void
  680. parse_warn (const gchar *fmt, ...)
  681. {
  682. va_list aq;
  683. gchar logbuf[BUFSIZ], readbuf[32];
  684. gint r;
  685. va_start (aq, fmt);
  686. rspamd_strlcpy (readbuf, yytext, sizeof (readbuf));
  687. r = snprintf (logbuf, sizeof (logbuf), "config file parse warning! line: %d, text: %s, reason: ", yylineno, readbuf);
  688. r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
  689. va_end (aq);
  690. g_warning ("%s", logbuf);
  691. }
  692. #endif
  693. void
  694. unescape_quotes (gchar *line)
  695. {
  696. gchar *c = line, *t;
  697. while (*c) {
  698. if (*c == '\\' && *(c + 1) == '"') {
  699. t = c;
  700. while (*t) {
  701. *t = *(t + 1);
  702. t++;
  703. }
  704. }
  705. c++;
  706. }
  707. }
  708. GList *
  709. parse_comma_list (memory_pool_t * pool, gchar *line)
  710. {
  711. GList *res = NULL;
  712. gchar *c, *p, *str;
  713. c = line;
  714. p = c;
  715. while (*p) {
  716. if (*p == ',' && *c != *p) {
  717. str = memory_pool_alloc (pool, p - c + 1);
  718. rspamd_strlcpy (str, c, p - c + 1);
  719. res = g_list_prepend (res, str);
  720. /* Skip spaces */
  721. while (g_ascii_isspace (*(++p)));
  722. c = p;
  723. continue;
  724. }
  725. p++;
  726. }
  727. if (res != NULL) {
  728. memory_pool_add_destructor (pool, (pool_destruct_func) g_list_free, res);
  729. }
  730. return res;
  731. }
  732. struct classifier_config *
  733. check_classifier_conf (struct config_file *cfg, struct classifier_config *c)
  734. {
  735. if (c == NULL) {
  736. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct classifier_config));
  737. }
  738. if (c->opts == NULL) {
  739. c->opts = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  740. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->opts);
  741. }
  742. if (c->labels == NULL) {
  743. c->labels = g_hash_table_new_full (rspamd_str_hash, rspamd_str_equal, NULL, (GDestroyNotify)g_list_free);
  744. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->labels);
  745. }
  746. return c;
  747. }
  748. struct statfile*
  749. check_statfile_conf (struct config_file *cfg, struct statfile *c)
  750. {
  751. if (c == NULL) {
  752. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct statfile));
  753. }
  754. if (c->opts == NULL) {
  755. c->opts = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  756. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->opts);
  757. }
  758. return c;
  759. }
  760. struct metric *
  761. check_metric_conf (struct config_file *cfg, struct metric *c)
  762. {
  763. if (c == NULL) {
  764. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
  765. c->action = METRIC_ACTION_REJECT;
  766. c->grow_factor = 1.0;
  767. c->symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  768. c->descriptions = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  769. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->symbols);
  770. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->descriptions);
  771. }
  772. return c;
  773. }
  774. struct worker_conf *
  775. check_worker_conf (struct config_file *cfg, struct worker_conf *c)
  776. {
  777. if (c == NULL) {
  778. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct worker_conf));
  779. c->params = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  780. c->active_workers = g_queue_new ();
  781. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_hash_table_destroy, c->params);
  782. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_queue_free, c->active_workers);
  783. #ifdef HAVE_SC_NPROCESSORS_ONLN
  784. c->count = sysconf (_SC_NPROCESSORS_ONLN);
  785. #else
  786. c->count = DEFAULT_WORKERS_NUM;
  787. #endif
  788. c->rlimit_nofile = DEFAULT_RLIMIT_NOFILE;
  789. c->rlimit_maxcore = DEFAULT_RLIMIT_MAXCORE;
  790. }
  791. return c;
  792. }
  793. static double
  794. internal_normalizer_func (struct config_file *cfg, long double score, void *data)
  795. {
  796. long double max = *(double *)data;
  797. if (score < 0) {
  798. return score;
  799. }
  800. #ifdef HAVE_TANHL
  801. return max * tanhl (score / max);
  802. #else
  803. /*
  804. * As some implementations of libm does not support tanhl, try to use
  805. * tanh
  806. */
  807. return max * tanh ((double) (score / max));
  808. #endif
  809. }
  810. static gboolean
  811. parse_internal_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
  812. {
  813. double *max;
  814. gchar *err;
  815. /* Line contains maximum value for internal normalizer */
  816. max = memory_pool_alloc (cfg->cfg_pool, sizeof (double));
  817. errno = 0;
  818. *max = strtod (line, &err);
  819. if (errno != 0 || *err != '\0') {
  820. msg_err ("cannot parse max number for internal normalizer");
  821. return FALSE;
  822. }
  823. st->normalizer = internal_normalizer_func;
  824. st->normalizer_data = (void *)max;
  825. return TRUE;
  826. }
  827. #ifdef WITH_LUA
  828. static gboolean
  829. parse_lua_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
  830. {
  831. gchar *code_begin;
  832. GList *params = NULL;
  833. gint len;
  834. code_begin = strchr (line, ':');
  835. if (code_begin == NULL) {
  836. /* Just function name without code */
  837. params = g_list_prepend (g_list_prepend (NULL, NULL), memory_pool_strdup (cfg->cfg_pool, line));
  838. }
  839. else {
  840. /* Postpone actual code load as lua libraries are not loaded */
  841. /* Put code to list */
  842. params = g_list_prepend (NULL, code_begin + 1);
  843. /* Put function name */
  844. len = code_begin - line;
  845. code_begin = memory_pool_alloc (cfg->cfg_pool, len + 1);
  846. rspamd_strlcpy (code_begin, line, len + 1);
  847. params = g_list_prepend (params, code_begin);
  848. }
  849. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_list_free, params);
  850. st->normalizer = lua_normalizer_func;
  851. st->normalizer_data = params;
  852. return TRUE;
  853. }
  854. #endif
  855. gboolean
  856. parse_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
  857. {
  858. gchar *params_begin;
  859. params_begin = strchr (line, ':');
  860. if (params_begin == NULL) {
  861. msg_err ("no parameters are specified for normalizer %s", line);
  862. return FALSE;
  863. }
  864. /* Try to guess normalizer */
  865. if (g_ascii_strncasecmp (line, "internal", sizeof ("points")) == 0) {
  866. return parse_internal_normalizer (cfg, st, params_begin + 1);
  867. }
  868. #ifdef WITH_LUA
  869. else if (g_ascii_strncasecmp (line, "points", sizeof ("points")) == 0) {
  870. return parse_lua_normalizer (cfg, st, params_begin + 1);
  871. }
  872. #endif
  873. msg_err ("unknown normalizer %s", line);
  874. return FALSE;
  875. }
  876. static GMarkupParser xml_parser = {
  877. .start_element = rspamd_xml_start_element,
  878. .end_element = rspamd_xml_end_element,
  879. .passthrough = NULL,
  880. .text = rspamd_xml_text,
  881. .error = rspamd_xml_error,
  882. };
  883. gboolean
  884. read_xml_config (struct config_file *cfg, const gchar *filename)
  885. {
  886. struct stat st;
  887. gint fd;
  888. gchar *data;
  889. gboolean res;
  890. GMarkupParseContext *ctx;
  891. GError *err = NULL;
  892. struct rspamd_xml_userdata ud;
  893. if (stat (filename, &st) == -1) {
  894. msg_err ("cannot stat %s: %s", filename, strerror (errno));
  895. return FALSE;
  896. }
  897. if ((fd = open (filename, O_RDONLY)) == -1) {
  898. msg_err ("cannot open %s: %s", filename, strerror (errno));
  899. return FALSE;
  900. }
  901. /* Now mmap this file to simplify reading process */
  902. if ((data = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
  903. msg_err ("cannot mmap %s: %s", filename, strerror (errno));
  904. close (fd);
  905. return FALSE;
  906. }
  907. close (fd);
  908. /* Prepare xml parser */
  909. ud.cfg = cfg;
  910. ud.state = XML_READ_START;
  911. ud.if_stack = g_queue_new ();
  912. ctx = g_markup_parse_context_new (&xml_parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &ud, NULL);
  913. init_kvstorage_config ();
  914. res = g_markup_parse_context_parse (ctx, data, st.st_size, &err);
  915. if (g_queue_get_length (ud.if_stack) != 0) {
  916. msg_err ("unexpected nesting for if arguments");
  917. res = FALSE;
  918. }
  919. munmap (data, st.st_size);
  920. return res;
  921. }
  922. static void
  923. modules_config_callback (gpointer key, gpointer value, gpointer ud)
  924. {
  925. extern GHashTable *module_options;
  926. GHashTable *cur_module;
  927. GList *cur;
  928. struct module_opt *opt;
  929. const gchar *mname = key;
  930. gboolean *res = ud;
  931. if ((cur_module = g_hash_table_lookup (module_options, mname)) == NULL) {
  932. msg_warn ("module %s has not registered any options but is presented in configuration", mname);
  933. *res = FALSE;
  934. return;
  935. }
  936. cur = value;
  937. while (cur) {
  938. opt = cur->data;
  939. if (!opt->is_lua && !check_module_option (mname, opt->param, opt->value)) {
  940. *res = FALSE;
  941. return;
  942. }
  943. cur = g_list_next (cur);
  944. }
  945. }
  946. gboolean
  947. check_modules_config (struct config_file *cfg)
  948. {
  949. gboolean res = TRUE;
  950. g_hash_table_foreach (cfg->modules_opts, modules_config_callback, &res);
  951. return res;
  952. }
  953. static void
  954. symbols_classifiers_callback (gpointer key, gpointer value, gpointer ud)
  955. {
  956. struct config_file *cfg = ud;
  957. register_virtual_symbol (&cfg->cache, key, 1.0);
  958. }
  959. void
  960. insert_classifier_symbols (struct config_file *cfg)
  961. {
  962. g_hash_table_foreach (cfg->classifiers_symbols, symbols_classifiers_callback, cfg);
  963. }
  964. struct classifier_config*
  965. find_classifier_conf (struct config_file *cfg, const gchar *name)
  966. {
  967. GList *cur;
  968. struct classifier_config *cf;
  969. if (name == NULL) {
  970. return NULL;
  971. }
  972. cur = cfg->classifiers;
  973. while (cur) {
  974. cf = cur->data;
  975. if (g_ascii_strcasecmp (cf->classifier->name, name) == 0) {
  976. return cf;
  977. }
  978. cur = g_list_next (cur);
  979. }
  980. return NULL;
  981. }
  982. gboolean
  983. check_classifier_statfiles (struct classifier_config *cf)
  984. {
  985. struct statfile *st;
  986. gboolean has_other = FALSE, res = FALSE, cur_class;
  987. GList *cur;
  988. /* First check classes directly */
  989. cur = cf->statfiles;
  990. while (cur) {
  991. st = cur->data;
  992. if (!has_other) {
  993. cur_class = st->is_spam;
  994. has_other = TRUE;
  995. }
  996. else {
  997. if (cur_class != st->is_spam) {
  998. return TRUE;
  999. }
  1000. }
  1001. cur = g_list_next (cur);
  1002. }
  1003. if (!has_other) {
  1004. /* We have only one statfile */
  1005. return FALSE;
  1006. }
  1007. /* We have not detected any statfile that has different class, so turn on euristic based on symbol's name */
  1008. has_other = FALSE;
  1009. cur = cf->statfiles;
  1010. while (cur) {
  1011. st = cur->data;
  1012. if (rspamd_strncasestr (st->symbol, "spam", -1) != NULL) {
  1013. st->is_spam = TRUE;
  1014. }
  1015. else if (rspamd_strncasestr (st->symbol, "ham", -1) != NULL) {
  1016. st->is_spam = FALSE;
  1017. }
  1018. if (!has_other) {
  1019. cur_class = st->is_spam;
  1020. has_other = TRUE;
  1021. }
  1022. else {
  1023. if (cur_class != st->is_spam) {
  1024. res = TRUE;
  1025. }
  1026. }
  1027. cur = g_list_next (cur);
  1028. }
  1029. return res;
  1030. }
  1031. /*
  1032. * vi:ts=4
  1033. */