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.

hs_helper.c 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "libutil/util.h"
  18. #include "libserver/cfg_file.h"
  19. #include "libserver/cfg_rcl.h"
  20. #include "libserver/worker_util.h"
  21. #include "libserver/rspamd_control.h"
  22. #include "unix-std.h"
  23. #ifdef HAVE_GLOB_H
  24. #include <glob.h>
  25. #endif
  26. static gpointer init_hs_helper (struct rspamd_config *cfg);
  27. static void start_hs_helper (struct rspamd_worker *worker);
  28. worker_t hs_helper_worker = {
  29. "hs_helper", /* Name */
  30. init_hs_helper, /* Init function */
  31. start_hs_helper, /* Start function */
  32. FALSE, /* No socket */
  33. TRUE, /* Unique */
  34. FALSE, /* Non threaded */
  35. TRUE, /* Killable */
  36. SOCK_STREAM /* TCP socket */
  37. };
  38. static const gdouble default_max_time = 1.0;
  39. static const gdouble default_recompile_time = 60.0;
  40. /*
  41. * Worker's context
  42. */
  43. struct hs_helper_ctx {
  44. gchar *hs_dir;
  45. gboolean loaded;
  46. gdouble max_time;
  47. gdouble recompile_time;
  48. struct rspamd_config *cfg;
  49. struct event recompile_timer;
  50. struct event_base *ev_base;
  51. };
  52. static gpointer
  53. init_hs_helper (struct rspamd_config *cfg)
  54. {
  55. struct hs_helper_ctx *ctx;
  56. GQuark type;
  57. type = g_quark_try_string ("hs_helper");
  58. ctx = g_malloc0 (sizeof (*ctx));
  59. ctx->cfg = cfg;
  60. ctx->hs_dir = RSPAMD_DBDIR "/";
  61. ctx->max_time = default_max_time;
  62. ctx->recompile_time = default_recompile_time;
  63. rspamd_rcl_register_worker_option (cfg,
  64. type,
  65. "cache_dir",
  66. rspamd_rcl_parse_struct_string,
  67. ctx,
  68. G_STRUCT_OFFSET (struct hs_helper_ctx, hs_dir),
  69. 0,
  70. "Directory where to save hyperscan compiled expressions");
  71. rspamd_rcl_register_worker_option (cfg,
  72. type,
  73. "max_time",
  74. rspamd_rcl_parse_struct_time,
  75. ctx,
  76. G_STRUCT_OFFSET (struct hs_helper_ctx, max_time),
  77. RSPAMD_CL_FLAG_TIME_FLOAT,
  78. "Maximum time to wait for compilation of a single expression");
  79. rspamd_rcl_register_worker_option (cfg,
  80. type,
  81. "recompile",
  82. rspamd_rcl_parse_struct_time,
  83. ctx,
  84. G_STRUCT_OFFSET (struct hs_helper_ctx, recompile_time),
  85. RSPAMD_CL_FLAG_TIME_FLOAT,
  86. "Time between recompilation checks");
  87. rspamd_rcl_register_worker_option (cfg,
  88. type,
  89. "timeout",
  90. rspamd_rcl_parse_struct_time,
  91. ctx,
  92. G_STRUCT_OFFSET (struct hs_helper_ctx, max_time),
  93. RSPAMD_CL_FLAG_TIME_FLOAT,
  94. "Maximum time to wait for compilation of a single expression");
  95. return ctx;
  96. }
  97. /**
  98. * Clean
  99. */
  100. static gboolean
  101. rspamd_hs_helper_cleanup_dir (struct hs_helper_ctx *ctx, gboolean forced)
  102. {
  103. struct stat st;
  104. glob_t globbuf;
  105. guint len, i;
  106. gint rc;
  107. gchar *pattern;
  108. gboolean ret = TRUE;
  109. if (stat (ctx->hs_dir, &st) == -1) {
  110. msg_err ("cannot stat path %s, %s",
  111. ctx->hs_dir,
  112. strerror (errno));
  113. return FALSE;
  114. }
  115. globbuf.gl_offs = 0;
  116. len = strlen (ctx->hs_dir) + 1 + sizeof ("*.hs");
  117. pattern = g_malloc (len);
  118. rspamd_snprintf (pattern, len, "%s%c%s", ctx->hs_dir, G_DIR_SEPARATOR, "*.hs");
  119. if ((rc = glob (pattern, GLOB_DOOFFS, NULL, &globbuf)) == 0) {
  120. for (i = 0; i < globbuf.gl_pathc; i++) {
  121. if (forced ||
  122. !rspamd_re_cache_is_valid_hyperscan_file (ctx->cfg->re_cache,
  123. globbuf.gl_pathv[i], TRUE)) {
  124. if (unlink (globbuf.gl_pathv[i]) == -1) {
  125. msg_err ("cannot unlink %s: %s", globbuf.gl_pathv[i],
  126. strerror (errno));
  127. ret = FALSE;
  128. }
  129. }
  130. }
  131. }
  132. else if (rc != GLOB_NOMATCH) {
  133. msg_err ("glob %s failed: %s", pattern, strerror (errno));
  134. ret = FALSE;
  135. }
  136. globfree (&globbuf);
  137. g_free (pattern);
  138. return ret;
  139. }
  140. static gboolean
  141. rspamd_rs_compile (struct hs_helper_ctx *ctx, struct rspamd_worker *worker,
  142. gboolean forced)
  143. {
  144. GError *err = NULL;
  145. static struct rspamd_srv_command srv_cmd;
  146. gint ncompiled;
  147. if (!rspamd_hs_helper_cleanup_dir (ctx, forced)) {
  148. msg_warn ("cannot cleanup cache dir '%s'", ctx->hs_dir);
  149. }
  150. if ((ncompiled = rspamd_re_cache_compile_hyperscan (ctx->cfg->re_cache,
  151. ctx->hs_dir, ctx->max_time, !forced,
  152. &err)) == -1) {
  153. msg_err ("failed to compile re cache: %e", err);
  154. g_error_free (err);
  155. return FALSE;
  156. }
  157. if (ncompiled > 0) {
  158. msg_info ("compiled %d regular expressions to the hyperscan tree",
  159. ncompiled);
  160. forced = TRUE;
  161. }
  162. /*
  163. * Do not send notification unless all other workers are started
  164. * XXX: now we just sleep for 5 seconds to ensure that
  165. */
  166. if (!ctx->loaded) {
  167. sleep (5);
  168. ctx->loaded = TRUE;
  169. }
  170. srv_cmd.type = RSPAMD_SRV_HYPERSCAN_LOADED;
  171. srv_cmd.cmd.hs_loaded.cache_dir = ctx->hs_dir;
  172. srv_cmd.cmd.hs_loaded.forced = forced;
  173. rspamd_srv_send_command (worker, ctx->ev_base, &srv_cmd, NULL, NULL);
  174. return TRUE;
  175. }
  176. static gboolean
  177. rspamd_hs_helper_reload (struct rspamd_main *rspamd_main,
  178. struct rspamd_worker *worker, gint fd,
  179. struct rspamd_control_command *cmd,
  180. gpointer ud)
  181. {
  182. struct rspamd_control_reply rep;
  183. struct hs_helper_ctx *ctx = ud;
  184. msg_info ("recompiling hyperscan expressions after receiving reload command");
  185. memset (&rep, 0, sizeof (rep));
  186. rep.type = RSPAMD_CONTROL_RECOMPILE;
  187. rep.reply.recompile.status = 0;
  188. /* We write reply before actual recompilation as it takes a lot of time */
  189. if (write (fd, &rep, sizeof (rep)) != sizeof (rep)) {
  190. msg_err ("cannot write reply to the control socket: %s",
  191. strerror (errno));
  192. }
  193. rspamd_rs_compile (ctx, worker, TRUE);
  194. return TRUE;
  195. }
  196. static void
  197. rspamd_hs_helper_timer (gint fd, short what, gpointer ud)
  198. {
  199. struct rspamd_worker *worker = ud;
  200. struct hs_helper_ctx *ctx;
  201. struct timeval tv;
  202. double tim;
  203. ctx = worker->ctx;
  204. tim = rspamd_time_jitter (ctx->recompile_time, 0);
  205. double_to_tv (tim, &tv);
  206. event_del (&ctx->recompile_timer);
  207. rspamd_rs_compile (ctx, worker, FALSE);
  208. event_add (&ctx->recompile_timer, &tv);
  209. }
  210. static void
  211. start_hs_helper (struct rspamd_worker *worker)
  212. {
  213. struct hs_helper_ctx *ctx = worker->ctx;
  214. struct timeval tv;
  215. double tim;
  216. ctx->ev_base = rspamd_prepare_worker (worker,
  217. "hs_helper",
  218. NULL);
  219. if (!rspamd_rs_compile (ctx, worker, FALSE)) {
  220. /* Tell main not to respawn more workers */
  221. exit (EXIT_SUCCESS);
  222. }
  223. rspamd_control_worker_add_cmd_handler (worker, RSPAMD_CONTROL_RECOMPILE,
  224. rspamd_hs_helper_reload, ctx);
  225. event_set (&ctx->recompile_timer, -1, EV_TIMEOUT, rspamd_hs_helper_timer,
  226. worker);
  227. event_base_set (ctx->ev_base, &ctx->recompile_timer);
  228. tim = rspamd_time_jitter (ctx->recompile_time, 0);
  229. double_to_tv (tim, &tv);
  230. event_add (&ctx->recompile_timer, &tv);
  231. event_base_loop (ctx->ev_base, 0);
  232. rspamd_worker_block_signals ();
  233. rspamd_log_close (worker->srv->logger);
  234. exit (EXIT_SUCCESS);
  235. }