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.

monitored.c 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  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 <contrib/librdns/rdns.h>
  17. #include "rdns.h"
  18. #include "mem_pool.h"
  19. #include "cfg_file.h"
  20. #include "cryptobox.h"
  21. #include "logger.h"
  22. #include "contrib/uthash/utlist.h"
  23. static const gdouble default_monitoring_interval = 60.0;
  24. static const guint default_max_errors = 2;
  25. static const gdouble default_max_monitored_mult = 32;
  26. static const gdouble default_min_monitored_mult = 0.1;
  27. static const gdouble default_initial_monitored_mult = default_min_monitored_mult;
  28. static const gdouble default_offline_monitored_mult = 8.0;
  29. struct rspamd_monitored_methods {
  30. void *(*monitored_config)(struct rspamd_monitored *m,
  31. struct rspamd_monitored_ctx *ctx,
  32. const ucl_object_t *opts);
  33. gboolean (*monitored_update)(struct rspamd_monitored *m,
  34. struct rspamd_monitored_ctx *ctx, gpointer ud);
  35. void (*monitored_dtor)(struct rspamd_monitored *m,
  36. struct rspamd_monitored_ctx *ctx, gpointer ud);
  37. gpointer ud;
  38. };
  39. struct rspamd_monitored_ctx {
  40. struct rspamd_config *cfg;
  41. struct rdns_resolver *resolver;
  42. struct ev_loop *event_loop;
  43. GPtrArray *elts;
  44. GHashTable *helts;
  45. mon_change_cb change_cb;
  46. gpointer ud;
  47. gdouble monitoring_interval;
  48. gdouble max_monitored_mult;
  49. gdouble min_monitored_mult;
  50. gdouble initial_monitored_mult;
  51. gdouble offline_monitored_mult;
  52. guint max_errors;
  53. gboolean initialized;
  54. };
  55. struct rspamd_monitored {
  56. gchar *url;
  57. gdouble monitoring_mult;
  58. gdouble offline_time;
  59. gdouble total_offline_time;
  60. gdouble latency;
  61. guint nchecks;
  62. guint max_errors;
  63. guint cur_errors;
  64. gboolean alive;
  65. enum rspamd_monitored_type type;
  66. enum rspamd_monitored_flags flags;
  67. struct rspamd_monitored_ctx *ctx;
  68. struct rspamd_monitored_methods proc;
  69. ev_timer periodic;
  70. gchar tag[RSPAMD_MONITORED_TAG_LEN];
  71. };
  72. #define msg_err_mon(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  73. "monitored", m->tag, \
  74. RSPAMD_LOG_FUNC, \
  75. __VA_ARGS__)
  76. #define msg_warn_mon(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING, \
  77. "monitored", m->tag, \
  78. RSPAMD_LOG_FUNC, \
  79. __VA_ARGS__)
  80. #define msg_info_mon(...) rspamd_default_log_function(G_LOG_LEVEL_INFO, \
  81. "monitored", m->tag, \
  82. RSPAMD_LOG_FUNC, \
  83. __VA_ARGS__)
  84. #define msg_notice_mon(...) rspamd_default_log_function(G_LOG_LEVEL_MESSAGE, \
  85. "monitored", m->tag, \
  86. RSPAMD_LOG_FUNC, \
  87. __VA_ARGS__)
  88. #define msg_debug_mon(...) rspamd_conditional_debug_fast(NULL, NULL, \
  89. rspamd_monitored_log_id, "monitored", m->tag, \
  90. RSPAMD_LOG_FUNC, \
  91. __VA_ARGS__)
  92. INIT_LOG_MODULE(monitored)
  93. static inline void
  94. rspamd_monitored_propagate_error(struct rspamd_monitored *m,
  95. const gchar *error)
  96. {
  97. if (m->alive) {
  98. if (m->cur_errors < m->max_errors) {
  99. m->cur_errors++;
  100. /* Reduce timeout */
  101. rspamd_monitored_stop(m);
  102. if (m->monitoring_mult > m->ctx->min_monitored_mult) {
  103. if (m->monitoring_mult < 1.0) {
  104. m->monitoring_mult = 1.0;
  105. }
  106. else {
  107. m->monitoring_mult /= 2.0;
  108. }
  109. }
  110. msg_debug_mon("%s on resolving %s, %d retries left; next check in %.2f",
  111. error, m->url, m->max_errors - m->cur_errors,
  112. m->ctx->monitoring_interval * m->monitoring_mult);
  113. rspamd_monitored_start(m);
  114. }
  115. else {
  116. msg_notice_mon("%s on resolving %s, disable object",
  117. error, m->url);
  118. m->alive = FALSE;
  119. m->offline_time = rspamd_get_calendar_ticks();
  120. rspamd_monitored_stop(m);
  121. m->monitoring_mult = 2.0;
  122. rspamd_monitored_start(m);
  123. if (m->ctx->change_cb) {
  124. m->ctx->change_cb(m->ctx, m, FALSE, m->ctx->ud);
  125. }
  126. }
  127. }
  128. else {
  129. if (m->monitoring_mult < m->ctx->offline_monitored_mult) {
  130. /* Increase timeout */
  131. rspamd_monitored_stop(m);
  132. m->monitoring_mult *= 2.0;
  133. rspamd_monitored_start(m);
  134. }
  135. else {
  136. rspamd_monitored_stop(m);
  137. m->monitoring_mult = m->ctx->offline_monitored_mult;
  138. rspamd_monitored_start(m);
  139. }
  140. }
  141. }
  142. static inline void
  143. rspamd_monitored_propagate_success(struct rspamd_monitored *m, gdouble lat)
  144. {
  145. gdouble t;
  146. m->cur_errors = 0;
  147. if (!m->alive) {
  148. m->monitoring_mult = 1.0;
  149. t = rspamd_get_calendar_ticks();
  150. m->total_offline_time += t - m->offline_time;
  151. m->alive = TRUE;
  152. msg_notice_mon("restoring %s after %.1f seconds of downtime, "
  153. "total downtime: %.1f",
  154. m->url, t - m->offline_time, m->total_offline_time);
  155. m->offline_time = 0;
  156. m->nchecks = 1;
  157. m->latency = lat;
  158. rspamd_monitored_stop(m);
  159. rspamd_monitored_start(m);
  160. if (m->ctx->change_cb) {
  161. m->ctx->change_cb(m->ctx, m, TRUE, m->ctx->ud);
  162. }
  163. }
  164. else {
  165. /* Increase monitored interval */
  166. if (m->monitoring_mult < m->ctx->max_monitored_mult) {
  167. if (m->monitoring_mult < 1.0) {
  168. /* Upgrade fast from the initial mult */
  169. m->monitoring_mult = 1.0;
  170. }
  171. else {
  172. m->monitoring_mult *= 2.0;
  173. }
  174. }
  175. else {
  176. m->monitoring_mult = m->ctx->max_monitored_mult;
  177. }
  178. m->latency = (lat + m->latency * m->nchecks) / (m->nchecks + 1);
  179. m->nchecks++;
  180. }
  181. }
  182. static void
  183. rspamd_monitored_periodic(EV_P_ ev_timer *w, int revents)
  184. {
  185. struct rspamd_monitored *m = (struct rspamd_monitored *) w->data;
  186. gdouble jittered;
  187. gboolean ret = FALSE;
  188. if (m->proc.monitored_update) {
  189. ret = m->proc.monitored_update(m, m->ctx, m->proc.ud);
  190. }
  191. jittered = rspamd_time_jitter(m->ctx->monitoring_interval * m->monitoring_mult,
  192. 0.0);
  193. if (ret) {
  194. m->periodic.repeat = jittered;
  195. ev_timer_again(EV_A_ & m->periodic);
  196. }
  197. }
  198. struct rspamd_dns_monitored_conf {
  199. enum rdns_request_type rt;
  200. GString *request;
  201. radix_compressed_t *expected;
  202. struct rspamd_monitored *m;
  203. gint expected_code;
  204. gdouble check_tm;
  205. };
  206. static void
  207. rspamd_monitored_dns_random(struct rspamd_monitored *m,
  208. struct rspamd_dns_monitored_conf *conf)
  209. {
  210. gchar random_prefix[32];
  211. const gchar dns_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
  212. gint len;
  213. len = rspamd_random_uint64_fast() % sizeof(random_prefix);
  214. if (len < 8) {
  215. len = 8;
  216. }
  217. for (guint i = 0; i < len; i++) {
  218. guint idx = rspamd_random_uint64_fast() % (sizeof(dns_chars) - 1);
  219. random_prefix[i] = dns_chars[idx];
  220. }
  221. conf->request->len = 0;
  222. rspamd_printf_gstring(conf->request, "%*.s.%s", len, random_prefix,
  223. m->url);
  224. }
  225. static void *
  226. rspamd_monitored_dns_conf(struct rspamd_monitored *m,
  227. struct rspamd_monitored_ctx *ctx,
  228. const ucl_object_t *opts)
  229. {
  230. struct rspamd_dns_monitored_conf *conf;
  231. const ucl_object_t *elt;
  232. gint rt;
  233. GString *req = g_string_sized_new(127);
  234. conf = g_malloc0(sizeof(*conf));
  235. conf->rt = RDNS_REQUEST_A;
  236. conf->m = m;
  237. conf->expected_code = -1;
  238. if (opts) {
  239. elt = ucl_object_lookup(opts, "type");
  240. if (elt) {
  241. rt = rdns_type_fromstr(ucl_object_tostring(elt));
  242. if (rt != -1) {
  243. conf->rt = rt;
  244. }
  245. else {
  246. msg_err_mon("invalid resolve type: %s",
  247. ucl_object_tostring(elt));
  248. }
  249. }
  250. if (!(m->flags & RSPAMD_MONITORED_RANDOM)) {
  251. /* Prefix is useless for random monitored */
  252. elt = ucl_object_lookup(opts, "prefix");
  253. if (elt && ucl_object_type(elt) == UCL_STRING) {
  254. rspamd_printf_gstring(req, "%s.", ucl_object_tostring(elt));
  255. }
  256. }
  257. elt = ucl_object_lookup(opts, "ipnet");
  258. if (elt) {
  259. if (ucl_object_type(elt) == UCL_STRING) {
  260. radix_add_generic_iplist(ucl_object_tostring(elt),
  261. &conf->expected, FALSE, NULL);
  262. }
  263. else if (ucl_object_type(elt) == UCL_ARRAY) {
  264. const ucl_object_t *cur;
  265. ucl_object_iter_t it = NULL;
  266. while ((cur = ucl_object_iterate(elt, &it, true)) != NULL) {
  267. radix_add_generic_iplist(ucl_object_tostring(elt),
  268. &conf->expected, FALSE, NULL);
  269. }
  270. }
  271. }
  272. elt = ucl_object_lookup(opts, "rcode");
  273. if (elt) {
  274. rt = rdns_rcode_fromstr(ucl_object_tostring(elt));
  275. if (rt != -1) {
  276. conf->expected_code = rt;
  277. }
  278. else {
  279. msg_err_mon("invalid resolve rcode: %s",
  280. ucl_object_tostring(elt));
  281. }
  282. }
  283. }
  284. if (!(m->flags & RSPAMD_MONITORED_RANDOM)) {
  285. rspamd_printf_gstring(req, "%s", m->url);
  286. }
  287. conf->request = req;
  288. return conf;
  289. }
  290. static void
  291. rspamd_monitored_dns_cb(struct rdns_reply *reply, void *arg)
  292. {
  293. struct rspamd_dns_monitored_conf *conf = arg;
  294. struct rspamd_monitored *m;
  295. struct rdns_reply_entry *cur;
  296. gboolean is_special_reply = FALSE;
  297. gdouble lat;
  298. m = conf->m;
  299. lat = rspamd_get_calendar_ticks() - conf->check_tm;
  300. conf->check_tm = 0;
  301. msg_debug_mon("dns callback for %s in %.2f: %s", m->url, lat,
  302. rdns_strerror(reply->code));
  303. if (reply->code == RDNS_RC_TIMEOUT) {
  304. rspamd_monitored_propagate_error(m, "timeout");
  305. }
  306. else if (reply->code == RDNS_RC_SERVFAIL) {
  307. rspamd_monitored_propagate_error(m, "servfail");
  308. }
  309. else if (reply->code == RDNS_RC_REFUSED) {
  310. rspamd_monitored_propagate_error(m, "refused");
  311. }
  312. else {
  313. if (conf->expected_code != -1) {
  314. if (reply->code != conf->expected_code) {
  315. if (reply->code == RDNS_RC_NOREC &&
  316. conf->expected_code == RDNS_RC_NXDOMAIN) {
  317. rspamd_monitored_propagate_success(m, lat);
  318. }
  319. else {
  320. LL_FOREACH(reply->entries, cur)
  321. {
  322. if (cur->type == RDNS_REQUEST_A) {
  323. if ((uint32_t) cur->content.a.addr.s_addr ==
  324. htonl(INADDR_LOOPBACK)) {
  325. is_special_reply = TRUE;
  326. }
  327. }
  328. }
  329. if (is_special_reply) {
  330. msg_notice_mon("DNS query blocked on %s "
  331. "(127.0.0.1 returned), "
  332. "possibly due to high volume",
  333. m->url);
  334. }
  335. else {
  336. msg_notice_mon("DNS reply returned '%s' for %s while '%s' "
  337. "was expected when querying for '%s'"
  338. "(likely DNS spoofing or BL internal issues)",
  339. rdns_strerror(reply->code),
  340. m->url,
  341. rdns_strerror(conf->expected_code),
  342. conf->request->str);
  343. }
  344. rspamd_monitored_propagate_error(m, "invalid return");
  345. }
  346. }
  347. else {
  348. rspamd_monitored_propagate_success(m, lat);
  349. }
  350. }
  351. else if (conf->expected) {
  352. /* We also need to check IP */
  353. if (reply->code != RDNS_RC_NOERROR) {
  354. rspamd_monitored_propagate_error(m, "no record");
  355. }
  356. else {
  357. rspamd_inet_addr_t *addr;
  358. addr = rspamd_inet_address_from_rnds(reply->entries);
  359. if (!addr) {
  360. rspamd_monitored_propagate_error(m,
  361. "unreadable address");
  362. }
  363. else if (radix_find_compressed_addr(conf->expected, addr)) {
  364. msg_notice_mon("bad address %s is returned when monitoring %s",
  365. rspamd_inet_address_to_string(addr),
  366. conf->request->str);
  367. rspamd_monitored_propagate_error(m,
  368. "invalid address");
  369. rspamd_inet_address_free(addr);
  370. }
  371. else {
  372. rspamd_monitored_propagate_success(m, lat);
  373. rspamd_inet_address_free(addr);
  374. }
  375. }
  376. }
  377. else {
  378. rspamd_monitored_propagate_success(m, lat);
  379. }
  380. }
  381. }
  382. static gboolean
  383. rspamd_monitored_dns_mon(struct rspamd_monitored *m,
  384. struct rspamd_monitored_ctx *ctx, gpointer ud)
  385. {
  386. struct rspamd_dns_monitored_conf *conf = ud;
  387. if (m->flags & RSPAMD_MONITORED_RANDOM) {
  388. rspamd_monitored_dns_random(m, conf);
  389. }
  390. if (!rdns_make_request_full(ctx->resolver, rspamd_monitored_dns_cb,
  391. conf, ctx->cfg->dns_timeout, ctx->cfg->dns_retransmits,
  392. 1, conf->request->str, conf->rt)) {
  393. msg_notice_mon("cannot make request to resolve %s (%s monitored url)",
  394. conf->request->str, conf->m->url);
  395. m->cur_errors++;
  396. rspamd_monitored_propagate_error(m, "failed to make DNS request");
  397. return FALSE;
  398. }
  399. else {
  400. conf->check_tm = rspamd_get_calendar_ticks();
  401. }
  402. return TRUE;
  403. }
  404. void rspamd_monitored_dns_dtor(struct rspamd_monitored *m,
  405. struct rspamd_monitored_ctx *ctx, gpointer ud)
  406. {
  407. struct rspamd_dns_monitored_conf *conf = ud;
  408. g_string_free(conf->request, TRUE);
  409. if (conf->expected) {
  410. radix_destroy_compressed(conf->expected);
  411. }
  412. g_free(conf);
  413. }
  414. struct rspamd_monitored_ctx *
  415. rspamd_monitored_ctx_init(void)
  416. {
  417. struct rspamd_monitored_ctx *ctx;
  418. ctx = g_malloc0(sizeof(*ctx));
  419. ctx->monitoring_interval = default_monitoring_interval;
  420. ctx->max_errors = default_max_errors;
  421. ctx->offline_monitored_mult = default_offline_monitored_mult;
  422. ctx->initial_monitored_mult = default_initial_monitored_mult;
  423. ctx->max_monitored_mult = default_max_monitored_mult;
  424. ctx->min_monitored_mult = default_min_monitored_mult;
  425. ctx->elts = g_ptr_array_new();
  426. ctx->helts = g_hash_table_new(g_str_hash, g_str_equal);
  427. return ctx;
  428. }
  429. void rspamd_monitored_ctx_config(struct rspamd_monitored_ctx *ctx,
  430. struct rspamd_config *cfg,
  431. struct ev_loop *ev_base,
  432. struct rdns_resolver *resolver,
  433. mon_change_cb change_cb,
  434. gpointer ud)
  435. {
  436. struct rspamd_monitored *m;
  437. guint i;
  438. g_assert(ctx != NULL);
  439. ctx->event_loop = ev_base;
  440. ctx->resolver = resolver;
  441. ctx->cfg = cfg;
  442. ctx->initialized = TRUE;
  443. ctx->change_cb = change_cb;
  444. ctx->ud = ud;
  445. if (cfg->monitored_interval != 0) {
  446. ctx->monitoring_interval = cfg->monitored_interval;
  447. }
  448. /* Start all events */
  449. for (i = 0; i < ctx->elts->len; i++) {
  450. m = g_ptr_array_index(ctx->elts, i);
  451. m->monitoring_mult = ctx->initial_monitored_mult;
  452. rspamd_monitored_start(m);
  453. m->monitoring_mult = 1.0;
  454. }
  455. }
  456. struct ev_loop *
  457. rspamd_monitored_ctx_get_ev_base(struct rspamd_monitored_ctx *ctx)
  458. {
  459. return ctx->event_loop;
  460. }
  461. struct rspamd_monitored *
  462. rspamd_monitored_create_(struct rspamd_monitored_ctx *ctx,
  463. const gchar *line,
  464. enum rspamd_monitored_type type,
  465. enum rspamd_monitored_flags flags,
  466. const ucl_object_t *opts,
  467. const gchar *loc)
  468. {
  469. struct rspamd_monitored *m;
  470. rspamd_cryptobox_hash_state_t st;
  471. gchar *cksum_encoded, cksum[rspamd_cryptobox_HASHBYTES];
  472. g_assert(ctx != NULL);
  473. m = g_malloc0(sizeof(*m));
  474. m->type = type;
  475. m->flags = flags;
  476. m->url = g_strdup(line);
  477. m->ctx = ctx;
  478. m->monitoring_mult = ctx->initial_monitored_mult;
  479. m->max_errors = ctx->max_errors;
  480. m->alive = TRUE;
  481. if (type == RSPAMD_MONITORED_DNS) {
  482. m->proc.monitored_update = rspamd_monitored_dns_mon;
  483. m->proc.monitored_config = rspamd_monitored_dns_conf;
  484. m->proc.monitored_dtor = rspamd_monitored_dns_dtor;
  485. }
  486. else {
  487. g_free(m);
  488. return NULL;
  489. }
  490. if (opts) {
  491. const ucl_object_t *rnd_obj;
  492. rnd_obj = ucl_object_lookup(opts, "random");
  493. if (rnd_obj && ucl_object_type(rnd_obj) == UCL_BOOLEAN) {
  494. if (ucl_object_toboolean(rnd_obj)) {
  495. m->flags |= RSPAMD_MONITORED_RANDOM;
  496. }
  497. }
  498. }
  499. m->proc.ud = m->proc.monitored_config(m, ctx, opts);
  500. if (m->proc.ud == NULL) {
  501. g_free(m);
  502. return NULL;
  503. }
  504. /* Create a persistent tag */
  505. rspamd_cryptobox_hash_init(&st, NULL, 0);
  506. rspamd_cryptobox_hash_update(&st, m->url, strlen(m->url));
  507. rspamd_cryptobox_hash_update(&st, loc, strlen(loc));
  508. rspamd_cryptobox_hash_final(&st, cksum);
  509. cksum_encoded = rspamd_encode_base32(cksum, sizeof(cksum), RSPAMD_BASE32_DEFAULT);
  510. rspamd_strlcpy(m->tag, cksum_encoded, sizeof(m->tag));
  511. if (g_hash_table_lookup(ctx->helts, m->tag) != NULL) {
  512. msg_err("monitored error: tag collision detected for %s; "
  513. "url: %s",
  514. m->tag, m->url);
  515. }
  516. else {
  517. g_hash_table_insert(ctx->helts, m->tag, m);
  518. }
  519. g_free(cksum_encoded);
  520. g_ptr_array_add(ctx->elts, m);
  521. if (ctx->event_loop) {
  522. rspamd_monitored_start(m);
  523. }
  524. return m;
  525. }
  526. gboolean
  527. rspamd_monitored_alive(struct rspamd_monitored *m)
  528. {
  529. g_assert(m != NULL);
  530. return m->alive;
  531. }
  532. gboolean
  533. rspamd_monitored_set_alive(struct rspamd_monitored *m, gboolean alive)
  534. {
  535. gboolean st;
  536. g_assert(m != NULL);
  537. st = m->alive;
  538. m->alive = alive;
  539. return st;
  540. }
  541. gdouble
  542. rspamd_monitored_offline_time(struct rspamd_monitored *m)
  543. {
  544. g_assert(m != NULL);
  545. if (m->offline_time > 0) {
  546. return rspamd_get_calendar_ticks() - m->offline_time;
  547. }
  548. return 0;
  549. }
  550. gdouble
  551. rspamd_monitored_total_offline_time(struct rspamd_monitored *m)
  552. {
  553. g_assert(m != NULL);
  554. if (m->offline_time > 0) {
  555. return rspamd_get_calendar_ticks() - m->offline_time + m->total_offline_time;
  556. }
  557. return m->total_offline_time;
  558. }
  559. gdouble
  560. rspamd_monitored_latency(struct rspamd_monitored *m)
  561. {
  562. g_assert(m != NULL);
  563. return m->latency;
  564. }
  565. void rspamd_monitored_stop(struct rspamd_monitored *m)
  566. {
  567. g_assert(m != NULL);
  568. ev_timer_stop(m->ctx->event_loop, &m->periodic);
  569. }
  570. void rspamd_monitored_start(struct rspamd_monitored *m)
  571. {
  572. gdouble jittered;
  573. g_assert(m != NULL);
  574. jittered = rspamd_time_jitter(m->ctx->monitoring_interval * m->monitoring_mult,
  575. 0.0);
  576. msg_debug_mon("started monitored object %s in %.2f seconds", m->url, jittered);
  577. if (ev_can_stop(&m->periodic)) {
  578. ev_timer_stop(m->ctx->event_loop, &m->periodic);
  579. }
  580. m->periodic.data = m;
  581. ev_timer_init(&m->periodic, rspamd_monitored_periodic, jittered, 0.0);
  582. ev_timer_start(m->ctx->event_loop, &m->periodic);
  583. }
  584. void rspamd_monitored_ctx_destroy(struct rspamd_monitored_ctx *ctx)
  585. {
  586. struct rspamd_monitored *m;
  587. guint i;
  588. g_assert(ctx != NULL);
  589. for (i = 0; i < ctx->elts->len; i++) {
  590. m = g_ptr_array_index(ctx->elts, i);
  591. rspamd_monitored_stop(m);
  592. m->proc.monitored_dtor(m, m->ctx, m->proc.ud);
  593. g_free(m->url);
  594. g_free(m);
  595. }
  596. g_ptr_array_free(ctx->elts, TRUE);
  597. g_hash_table_unref(ctx->helts);
  598. g_free(ctx);
  599. }
  600. struct rspamd_monitored *
  601. rspamd_monitored_by_tag(struct rspamd_monitored_ctx *ctx,
  602. guchar tag[RSPAMD_MONITORED_TAG_LEN])
  603. {
  604. struct rspamd_monitored *res;
  605. gchar rtag[RSPAMD_MONITORED_TAG_LEN];
  606. rspamd_strlcpy(rtag, tag, sizeof(rtag));
  607. res = g_hash_table_lookup(ctx->helts, rtag);
  608. return res;
  609. }
  610. void rspamd_monitored_get_tag(struct rspamd_monitored *m,
  611. guchar tag_out[RSPAMD_MONITORED_TAG_LEN])
  612. {
  613. g_assert(m != NULL);
  614. rspamd_strlcpy(tag_out, m->tag, RSPAMD_MONITORED_TAG_LEN);
  615. }