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.

rrd.c 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515
  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 "rrd.h"
  18. #include "util.h"
  19. #include "logger.h"
  20. #include "unix-std.h"
  21. #include "cryptobox.h"
  22. #include <math.h>
  23. #define RSPAMD_RRD_DS_COUNT METRIC_ACTION_MAX
  24. #define RSPAMD_RRD_OLD_DS_COUNT 4
  25. #define RSPAMD_RRD_RRA_COUNT 4
  26. #define msg_err_rrd(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \
  27. "rrd", file->id, \
  28. G_STRFUNC, \
  29. __VA_ARGS__)
  30. #define msg_warn_rrd(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  31. "rrd", file->id, \
  32. G_STRFUNC, \
  33. __VA_ARGS__)
  34. #define msg_info_rrd(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  35. "rrd", file->id, \
  36. G_STRFUNC, \
  37. __VA_ARGS__)
  38. #define msg_debug_rrd(...) rspamd_conditional_debug_fast (NULL, NULL, \
  39. rspamd_rrd_log_id, "rrd", file->id, \
  40. G_STRFUNC, \
  41. __VA_ARGS__)
  42. INIT_LOG_MODULE(rrd)
  43. static GQuark
  44. rrd_error_quark (void)
  45. {
  46. return g_quark_from_static_string ("rrd-error");
  47. }
  48. /**
  49. * Convert rrd dst type from string to numeric value
  50. */
  51. enum rrd_dst_type
  52. rrd_dst_from_string (const gchar *str)
  53. {
  54. if (g_ascii_strcasecmp (str, "counter") == 0) {
  55. return RRD_DST_COUNTER;
  56. }
  57. else if (g_ascii_strcasecmp (str, "absolute") == 0) {
  58. return RRD_DST_ABSOLUTE;
  59. }
  60. else if (g_ascii_strcasecmp (str, "gauge") == 0) {
  61. return RRD_DST_GAUGE;
  62. }
  63. else if (g_ascii_strcasecmp (str, "cdef") == 0) {
  64. return RRD_DST_CDEF;
  65. }
  66. else if (g_ascii_strcasecmp (str, "derive") == 0) {
  67. return RRD_DST_DERIVE;
  68. }
  69. return RRD_DST_INVALID;
  70. }
  71. /**
  72. * Convert numeric presentation of dst to string
  73. */
  74. const gchar *
  75. rrd_dst_to_string (enum rrd_dst_type type)
  76. {
  77. switch (type) {
  78. case RRD_DST_COUNTER:
  79. return "COUNTER";
  80. case RRD_DST_ABSOLUTE:
  81. return "ABSOLUTE";
  82. case RRD_DST_GAUGE:
  83. return "GAUGE";
  84. case RRD_DST_CDEF:
  85. return "CDEF";
  86. case RRD_DST_DERIVE:
  87. return "DERIVE";
  88. default:
  89. return "U";
  90. }
  91. return "U";
  92. }
  93. /**
  94. * Convert rrd consolidation function type from string to numeric value
  95. */
  96. enum rrd_cf_type
  97. rrd_cf_from_string (const gchar *str)
  98. {
  99. if (g_ascii_strcasecmp (str, "average") == 0) {
  100. return RRD_CF_AVERAGE;
  101. }
  102. else if (g_ascii_strcasecmp (str, "minimum") == 0) {
  103. return RRD_CF_MINIMUM;
  104. }
  105. else if (g_ascii_strcasecmp (str, "maximum") == 0) {
  106. return RRD_CF_MAXIMUM;
  107. }
  108. else if (g_ascii_strcasecmp (str, "last") == 0) {
  109. return RRD_CF_LAST;
  110. }
  111. /* XXX: add other CF functions supported by rrd */
  112. return RRD_CF_INVALID;
  113. }
  114. /**
  115. * Convert numeric presentation of cf to string
  116. */
  117. const gchar *
  118. rrd_cf_to_string (enum rrd_cf_type type)
  119. {
  120. switch (type) {
  121. case RRD_CF_AVERAGE:
  122. return "AVERAGE";
  123. case RRD_CF_MINIMUM:
  124. return "MINIMUM";
  125. case RRD_CF_MAXIMUM:
  126. return "MAXIMUM";
  127. case RRD_CF_LAST:
  128. return "LAST";
  129. default:
  130. return "U";
  131. }
  132. /* XXX: add other CF functions supported by rrd */
  133. return "U";
  134. }
  135. void
  136. rrd_make_default_rra (const gchar *cf_name,
  137. gulong pdp_cnt,
  138. gulong rows,
  139. struct rrd_rra_def *rra)
  140. {
  141. g_assert (cf_name != NULL);
  142. g_assert (rrd_cf_from_string (cf_name) != RRD_CF_INVALID);
  143. rra->pdp_cnt = pdp_cnt;
  144. rra->row_cnt = rows;
  145. rspamd_strlcpy (rra->cf_nam, cf_name, sizeof (rra->cf_nam));
  146. memset (rra->par, 0, sizeof (rra->par));
  147. rra->par[RRA_cdp_xff_val].dv = 0.5;
  148. }
  149. void
  150. rrd_make_default_ds (const gchar *name,
  151. const gchar *type,
  152. gulong pdp_step,
  153. struct rrd_ds_def *ds)
  154. {
  155. g_assert (name != NULL);
  156. g_assert (type != NULL);
  157. g_assert (rrd_dst_from_string (type) != RRD_DST_INVALID);
  158. rspamd_strlcpy (ds->ds_nam, name, sizeof (ds->ds_nam));
  159. rspamd_strlcpy (ds->dst, type, sizeof (ds->dst));
  160. memset (ds->par, 0, sizeof (ds->par));
  161. ds->par[RRD_DS_mrhb_cnt].lv = pdp_step * 2;
  162. ds->par[RRD_DS_min_val].dv = NAN;
  163. ds->par[RRD_DS_max_val].dv = NAN;
  164. }
  165. /**
  166. * Check rrd file for correctness (size, cookies, etc)
  167. */
  168. static gboolean
  169. rspamd_rrd_check_file (const gchar *filename, gboolean need_data, GError **err)
  170. {
  171. gint fd, i;
  172. struct stat st;
  173. struct rrd_file_head head;
  174. struct rrd_rra_def rra;
  175. gint head_size;
  176. fd = open (filename, O_RDWR);
  177. if (fd == -1) {
  178. g_set_error (err,
  179. rrd_error_quark (), errno, "rrd open error: %s", strerror (errno));
  180. return FALSE;
  181. }
  182. if (fstat (fd, &st) == -1) {
  183. g_set_error (err,
  184. rrd_error_quark (), errno, "rrd stat error: %s", strerror (errno));
  185. close (fd);
  186. return FALSE;
  187. }
  188. if (st.st_size < (goffset)sizeof (struct rrd_file_head)) {
  189. /* We have trimmed file */
  190. g_set_error (err, rrd_error_quark (), EINVAL, "rrd size is bad: %ud",
  191. (guint)st.st_size);
  192. close (fd);
  193. return FALSE;
  194. }
  195. /* Try to read header */
  196. if (read (fd, &head, sizeof (head)) != sizeof (head)) {
  197. g_set_error (err,
  198. rrd_error_quark (), errno, "rrd read head error: %s",
  199. strerror (errno));
  200. close (fd);
  201. return FALSE;
  202. }
  203. /* Check magic */
  204. if (memcmp (head.version, RRD_VERSION, sizeof (head.version)) != 0) {
  205. g_set_error (err,
  206. rrd_error_quark (), EINVAL, "rrd head error: bad cookie");
  207. close (fd);
  208. return FALSE;
  209. }
  210. if (memcmp (head.version, RRD_VERSION, sizeof (head.version)) != 0) {
  211. g_set_error (err,
  212. rrd_error_quark (), EINVAL, "rrd head error: invalid version");
  213. close (fd);
  214. return FALSE;
  215. }
  216. if (head.float_cookie != RRD_FLOAT_COOKIE) {
  217. g_set_error (err,
  218. rrd_error_quark (), EINVAL, "rrd head error: another architecture "
  219. "(file cookie %g != our cookie %g)",
  220. head.float_cookie, RRD_FLOAT_COOKIE);
  221. close (fd);
  222. return FALSE;
  223. }
  224. /* Check for other params */
  225. if (head.ds_cnt <= 0 || head.rra_cnt <= 0) {
  226. g_set_error (err,
  227. rrd_error_quark (), EINVAL, "rrd head cookies error: bad rra or ds count");
  228. close (fd);
  229. return FALSE;
  230. }
  231. /* Now we can calculate the overall size of rrd */
  232. head_size = sizeof (struct rrd_file_head) +
  233. sizeof (struct rrd_ds_def) * head.ds_cnt +
  234. sizeof (struct rrd_rra_def) * head.rra_cnt +
  235. sizeof (struct rrd_live_head) +
  236. sizeof (struct rrd_pdp_prep) * head.ds_cnt +
  237. sizeof (struct rrd_cdp_prep) * head.ds_cnt * head.rra_cnt +
  238. sizeof (struct rrd_rra_ptr) * head.rra_cnt;
  239. if (st.st_size < (goffset)head_size) {
  240. g_set_error (err,
  241. rrd_error_quark (), errno, "rrd file seems to have stripped header: %d",
  242. head_size);
  243. close (fd);
  244. return FALSE;
  245. }
  246. if (need_data) {
  247. /* Now check rra */
  248. if (lseek (fd, sizeof (struct rrd_ds_def) * head.ds_cnt,
  249. SEEK_CUR) == -1) {
  250. g_set_error (err,
  251. rrd_error_quark (), errno, "rrd head lseek error: %s",
  252. strerror (errno));
  253. close (fd);
  254. return FALSE;
  255. }
  256. for (i = 0; i < (gint)head.rra_cnt; i++) {
  257. if (read (fd, &rra, sizeof (rra)) != sizeof (rra)) {
  258. g_set_error (err,
  259. rrd_error_quark (), errno, "rrd read rra error: %s",
  260. strerror (errno));
  261. close (fd);
  262. return FALSE;
  263. }
  264. head_size += rra.row_cnt * head.ds_cnt * sizeof (gdouble);
  265. }
  266. if (st.st_size != head_size) {
  267. g_set_error (err,
  268. rrd_error_quark (), EINVAL, "rrd file seems to have incorrect size: %d, must be %d",
  269. (gint)st.st_size, head_size);
  270. close (fd);
  271. return FALSE;
  272. }
  273. }
  274. close (fd);
  275. return TRUE;
  276. }
  277. /**
  278. * Adjust pointers in mmapped rrd file
  279. * @param file
  280. */
  281. static void
  282. rspamd_rrd_adjust_pointers (struct rspamd_rrd_file *file, gboolean completed)
  283. {
  284. guint8 *ptr;
  285. ptr = file->map;
  286. file->stat_head = (struct rrd_file_head *)ptr;
  287. ptr += sizeof (struct rrd_file_head);
  288. file->ds_def = (struct rrd_ds_def *)ptr;
  289. ptr += sizeof (struct rrd_ds_def) * file->stat_head->ds_cnt;
  290. file->rra_def = (struct rrd_rra_def *)ptr;
  291. ptr += sizeof (struct rrd_rra_def) * file->stat_head->rra_cnt;
  292. file->live_head = (struct rrd_live_head *)ptr;
  293. ptr += sizeof (struct rrd_live_head);
  294. file->pdp_prep = (struct rrd_pdp_prep *)ptr;
  295. ptr += sizeof (struct rrd_pdp_prep) * file->stat_head->ds_cnt;
  296. file->cdp_prep = (struct rrd_cdp_prep *)ptr;
  297. ptr += sizeof (struct rrd_cdp_prep) * file->stat_head->rra_cnt *
  298. file->stat_head->ds_cnt;
  299. file->rra_ptr = (struct rrd_rra_ptr *)ptr;
  300. if (completed) {
  301. ptr += sizeof (struct rrd_rra_ptr) * file->stat_head->rra_cnt;
  302. file->rrd_value = (gdouble *)ptr;
  303. }
  304. else {
  305. file->rrd_value = NULL;
  306. }
  307. }
  308. static void
  309. rspamd_rrd_calculate_checksum (struct rspamd_rrd_file *file)
  310. {
  311. guchar sigbuf[rspamd_cryptobox_HASHBYTES];
  312. struct rrd_ds_def *ds;
  313. guint i;
  314. rspamd_cryptobox_hash_state_t st;
  315. if (file->finalized) {
  316. rspamd_cryptobox_hash_init (&st, NULL, 0);
  317. rspamd_cryptobox_hash_update (&st, file->filename, strlen (file->filename));
  318. for (i = 0; i < file->stat_head->ds_cnt; i ++) {
  319. ds = &file->ds_def[i];
  320. rspamd_cryptobox_hash_update (&st, ds->ds_nam, sizeof (ds->ds_nam));
  321. }
  322. rspamd_cryptobox_hash_final (&st, sigbuf);
  323. file->id = rspamd_encode_base32 (sigbuf, sizeof (sigbuf));
  324. }
  325. }
  326. static int
  327. rspamd_rrd_open_exclusive (const gchar *filename)
  328. {
  329. struct timespec sleep_ts = {
  330. .tv_sec = 0,
  331. .tv_nsec = 1000000
  332. };
  333. gint fd;
  334. fd = open (filename, O_RDWR);
  335. if (fd == -1) {
  336. return -1;
  337. }
  338. for (;;) {
  339. if (rspamd_file_lock (fd, TRUE) == -1) {
  340. if (errno == EAGAIN || errno == EWOULDBLOCK) {
  341. nanosleep (&sleep_ts, NULL);
  342. continue;
  343. }
  344. else {
  345. close (fd);
  346. return -1;
  347. }
  348. }
  349. else {
  350. break;
  351. }
  352. }
  353. return fd;
  354. };
  355. /**
  356. * Open completed or incompleted rrd file
  357. * @param filename
  358. * @param completed
  359. * @param err
  360. * @return
  361. */
  362. static struct rspamd_rrd_file *
  363. rspamd_rrd_open_common (const gchar *filename, gboolean completed, GError **err)
  364. {
  365. struct rspamd_rrd_file *file;
  366. gint fd;
  367. struct stat st;
  368. if (!rspamd_rrd_check_file (filename, completed, err)) {
  369. return NULL;
  370. }
  371. file = g_malloc0 (sizeof (struct rspamd_rrd_file));
  372. if (file == NULL) {
  373. g_set_error (err, rrd_error_quark (), ENOMEM, "not enough memory");
  374. return NULL;
  375. }
  376. /* Open file */
  377. fd = rspamd_rrd_open_exclusive (filename);
  378. if (fd == -1) {
  379. g_set_error (err,
  380. rrd_error_quark (), errno, "rrd open error: %s", strerror (errno));
  381. return FALSE;
  382. }
  383. if (fstat (fd, &st) == -1) {
  384. g_set_error (err,
  385. rrd_error_quark (), errno, "rrd stat error: %s", strerror (errno));
  386. rspamd_file_unlock (fd, FALSE);
  387. close (fd);
  388. return FALSE;
  389. }
  390. /* Mmap file */
  391. file->size = st.st_size;
  392. if ((file->map =
  393. mmap (NULL, st.st_size, PROT_READ | PROT_WRITE,
  394. MAP_SHARED, fd, 0)) == MAP_FAILED) {
  395. rspamd_file_unlock (fd, FALSE);
  396. close (fd);
  397. g_set_error (err,
  398. rrd_error_quark (), ENOMEM, "mmap failed: %s", strerror (errno));
  399. g_free (file);
  400. return NULL;
  401. }
  402. file->fd = fd;
  403. /* Adjust pointers */
  404. rspamd_rrd_adjust_pointers (file, completed);
  405. /* Mark it as finalized */
  406. file->finalized = completed;
  407. file->filename = g_strdup (filename);
  408. rspamd_rrd_calculate_checksum (file);
  409. return file;
  410. }
  411. /**
  412. * Open (and mmap) existing RRD file
  413. * @param filename path
  414. * @param err error pointer
  415. * @return rrd file structure
  416. */
  417. struct rspamd_rrd_file *
  418. rspamd_rrd_open (const gchar *filename, GError **err)
  419. {
  420. struct rspamd_rrd_file *file;
  421. if ((file = rspamd_rrd_open_common (filename, TRUE, err))) {
  422. msg_info_rrd ("rrd file opened: %s", filename);
  423. }
  424. return file;
  425. }
  426. /**
  427. * Create basic header for rrd file
  428. * @param filename file path
  429. * @param ds_count number of data sources
  430. * @param rra_count number of round robin archives
  431. * @param pdp_step step of primary data points
  432. * @param err error pointer
  433. * @return TRUE if file has been created
  434. */
  435. struct rspamd_rrd_file *
  436. rspamd_rrd_create (const gchar *filename,
  437. gulong ds_count,
  438. gulong rra_count,
  439. gulong pdp_step,
  440. gdouble initial_ticks,
  441. GError **err)
  442. {
  443. struct rspamd_rrd_file *new;
  444. struct rrd_file_head head;
  445. struct rrd_ds_def ds;
  446. struct rrd_rra_def rra;
  447. struct rrd_live_head lh;
  448. struct rrd_pdp_prep pdp;
  449. struct rrd_cdp_prep cdp;
  450. struct rrd_rra_ptr rra_ptr;
  451. gint fd;
  452. guint i, j;
  453. /* Open file */
  454. fd = open (filename, O_RDWR | O_CREAT | O_EXCL, 0644);
  455. if (fd == -1) {
  456. g_set_error (err,
  457. rrd_error_quark (), errno, "rrd create error: %s",
  458. strerror (errno));
  459. return NULL;
  460. }
  461. rspamd_file_lock (fd, FALSE);
  462. /* Fill header */
  463. memset (&head, 0, sizeof (head));
  464. head.rra_cnt = rra_count;
  465. head.ds_cnt = ds_count;
  466. head.pdp_step = pdp_step;
  467. memcpy (head.cookie, RRD_COOKIE, sizeof (head.cookie));
  468. memcpy (head.version, RRD_VERSION, sizeof (head.version));
  469. head.float_cookie = RRD_FLOAT_COOKIE;
  470. if (write (fd, &head, sizeof (head)) != sizeof (head)) {
  471. rspamd_file_unlock (fd, FALSE);
  472. close (fd);
  473. g_set_error (err,
  474. rrd_error_quark (), errno, "rrd write error: %s", strerror (errno));
  475. return NULL;
  476. }
  477. /* Fill DS section */
  478. memset (&ds, 0, sizeof (ds));
  479. memset (&ds.ds_nam, 0, sizeof (ds.ds_nam));
  480. memcpy (&ds.dst, "COUNTER", sizeof ("COUNTER"));
  481. memset (&ds.par, 0, sizeof (ds.par));
  482. for (i = 0; i < ds_count; i++) {
  483. if (write (fd, &ds, sizeof (ds)) != sizeof (ds)) {
  484. rspamd_file_unlock (fd, FALSE);
  485. close (fd);
  486. g_set_error (err,
  487. rrd_error_quark (), errno, "rrd write error: %s",
  488. strerror (errno));
  489. return NULL;
  490. }
  491. }
  492. /* Fill RRA section */
  493. memset (&rra, 0, sizeof (rra));
  494. memcpy (&rra.cf_nam, "AVERAGE", sizeof ("AVERAGE"));
  495. rra.pdp_cnt = 1;
  496. memset (&rra.par, 0, sizeof (rra.par));
  497. for (i = 0; i < rra_count; i++) {
  498. if (write (fd, &rra, sizeof (rra)) != sizeof (rra)) {
  499. rspamd_file_unlock (fd, FALSE);
  500. close (fd);
  501. g_set_error (err,
  502. rrd_error_quark (), errno, "rrd write error: %s",
  503. strerror (errno));
  504. return NULL;
  505. }
  506. }
  507. /* Fill live header */
  508. memset (&lh, 0, sizeof (lh));
  509. lh.last_up = (glong)initial_ticks;
  510. lh.last_up_usec = (glong)((initial_ticks - lh.last_up) * 1e6f);
  511. if (write (fd, &lh, sizeof (lh)) != sizeof (lh)) {
  512. rspamd_file_unlock (fd, FALSE);
  513. close (fd);
  514. g_set_error (err,
  515. rrd_error_quark (), errno, "rrd write error: %s", strerror (errno));
  516. return NULL;
  517. }
  518. /* Fill pdp prep */
  519. memset (&pdp, 0, sizeof (pdp));
  520. memcpy (&pdp.last_ds, "U", sizeof ("U"));
  521. memset (&pdp.scratch, 0, sizeof (pdp.scratch));
  522. pdp.scratch[PDP_val].dv = NAN;
  523. pdp.scratch[PDP_unkn_sec_cnt].lv = 0;
  524. for (i = 0; i < ds_count; i++) {
  525. if (write (fd, &pdp, sizeof (pdp)) != sizeof (pdp)) {
  526. rspamd_file_unlock (fd, FALSE);
  527. close (fd);
  528. g_set_error (err,
  529. rrd_error_quark (), errno, "rrd write error: %s",
  530. strerror (errno));
  531. return NULL;
  532. }
  533. }
  534. /* Fill cdp prep */
  535. memset (&cdp, 0, sizeof (cdp));
  536. memset (&cdp.scratch, 0, sizeof (cdp.scratch));
  537. cdp.scratch[CDP_val].dv = NAN;
  538. cdp.scratch[CDP_unkn_pdp_cnt].lv = 0;
  539. for (i = 0; i < rra_count; i++) {
  540. for (j = 0; j < ds_count; j++) {
  541. if (write (fd, &cdp, sizeof (cdp)) != sizeof (cdp)) {
  542. rspamd_file_unlock (fd, FALSE);
  543. close (fd);
  544. g_set_error (err,
  545. rrd_error_quark (), errno, "rrd write error: %s",
  546. strerror (errno));
  547. return NULL;
  548. }
  549. }
  550. }
  551. /* Set row pointers */
  552. memset (&rra_ptr, 0, sizeof (rra_ptr));
  553. for (i = 0; i < rra_count; i++) {
  554. if (write (fd, &rra_ptr, sizeof (rra_ptr)) != sizeof (rra_ptr)) {
  555. rspamd_file_unlock (fd, FALSE);
  556. close (fd);
  557. g_set_error (err,
  558. rrd_error_quark (), errno, "rrd write error: %s",
  559. strerror (errno));
  560. return NULL;
  561. }
  562. }
  563. rspamd_file_unlock (fd, FALSE);
  564. close (fd);
  565. new = rspamd_rrd_open_common (filename, FALSE, err);
  566. return new;
  567. }
  568. /**
  569. * Add data sources to rrd file
  570. * @param filename path to file
  571. * @param ds array of struct rrd_ds_def
  572. * @param err error pointer
  573. * @return TRUE if data sources were added
  574. */
  575. gboolean
  576. rspamd_rrd_add_ds (struct rspamd_rrd_file *file, GArray *ds, GError **err)
  577. {
  578. if (file == NULL || file->stat_head->ds_cnt * sizeof (struct rrd_ds_def) !=
  579. ds->len) {
  580. g_set_error (err,
  581. rrd_error_quark (), EINVAL, "rrd add ds failed: wrong arguments");
  582. return FALSE;
  583. }
  584. /* Straightforward memcpy */
  585. memcpy (file->ds_def, ds->data, ds->len);
  586. return TRUE;
  587. }
  588. /**
  589. * Add round robin archives to rrd file
  590. * @param filename path to file
  591. * @param ds array of struct rrd_rra_def
  592. * @param err error pointer
  593. * @return TRUE if archives were added
  594. */
  595. gboolean
  596. rspamd_rrd_add_rra (struct rspamd_rrd_file *file, GArray *rra, GError **err)
  597. {
  598. if (file == NULL || file->stat_head->rra_cnt *
  599. sizeof (struct rrd_rra_def) != rra->len) {
  600. g_set_error (err,
  601. rrd_error_quark (), EINVAL, "rrd add rra failed: wrong arguments");
  602. return FALSE;
  603. }
  604. /* Straightforward memcpy */
  605. memcpy (file->rra_def, rra->data, rra->len);
  606. return TRUE;
  607. }
  608. /**
  609. * Finalize rrd file header and initialize all RRA in the file
  610. * @param filename file path
  611. * @param err error pointer
  612. * @return TRUE if rrd file is ready for use
  613. */
  614. gboolean
  615. rspamd_rrd_finalize (struct rspamd_rrd_file *file, GError **err)
  616. {
  617. gint fd;
  618. guint i;
  619. gint count = 0;
  620. gdouble vbuf[1024];
  621. struct stat st;
  622. if (file == NULL || file->filename == NULL || file->fd == -1) {
  623. g_set_error (err,
  624. rrd_error_quark (), EINVAL, "rrd add rra failed: wrong arguments");
  625. return FALSE;
  626. }
  627. fd = file->fd;
  628. if (lseek (fd, 0, SEEK_END) == -1) {
  629. g_set_error (err,
  630. rrd_error_quark (), errno, "rrd seek error: %s", strerror (errno));
  631. close (fd);
  632. return FALSE;
  633. }
  634. /* Adjust CDP */
  635. for (i = 0; i < file->stat_head->rra_cnt; i++) {
  636. file->cdp_prep->scratch[CDP_unkn_pdp_cnt].lv = 0;
  637. /* Randomize row pointer (disabled) */
  638. /* file->rra_ptr->cur_row = g_random_int () % file->rra_def[i].row_cnt; */
  639. file->rra_ptr->cur_row = file->rra_def[i].row_cnt - 1;
  640. /* Calculate values count */
  641. count += file->rra_def[i].row_cnt * file->stat_head->ds_cnt;
  642. }
  643. munmap (file->map, file->size);
  644. /* Write values */
  645. for (i = 0; i < G_N_ELEMENTS (vbuf); i++) {
  646. vbuf[i] = NAN;
  647. }
  648. while (count > 0) {
  649. /* Write values in buffered matter */
  650. if (write (fd, vbuf,
  651. MIN ((gint)G_N_ELEMENTS (vbuf), count) * sizeof (gdouble)) == -1) {
  652. g_set_error (err,
  653. rrd_error_quark (), errno, "rrd write error: %s",
  654. strerror (errno));
  655. close (fd);
  656. return FALSE;
  657. }
  658. count -= G_N_ELEMENTS (vbuf);
  659. }
  660. if (fstat (fd, &st) == -1) {
  661. g_set_error (err,
  662. rrd_error_quark (), errno, "rrd stat error: %s", strerror (errno));
  663. close (fd);
  664. return FALSE;
  665. }
  666. /* Mmap again */
  667. file->size = st.st_size;
  668. if ((file->map =
  669. mmap (NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
  670. 0)) == MAP_FAILED) {
  671. close (fd);
  672. g_set_error (err,
  673. rrd_error_quark (), ENOMEM, "mmap failed: %s", strerror (errno));
  674. g_free (file);
  675. return FALSE;
  676. }
  677. /* Adjust pointers */
  678. rspamd_rrd_adjust_pointers (file, TRUE);
  679. file->finalized = TRUE;
  680. rspamd_rrd_calculate_checksum (file);
  681. msg_info_rrd ("rrd file created: %s", file->filename);
  682. return TRUE;
  683. }
  684. /**
  685. * Update pdp_prep data
  686. * @param file rrd file
  687. * @param vals new values
  688. * @param pdp_new new pdp array
  689. * @param interval time elapsed from the last update
  690. * @return
  691. */
  692. static gboolean
  693. rspamd_rrd_update_pdp_prep (struct rspamd_rrd_file *file,
  694. gdouble *vals,
  695. gdouble *pdp_new,
  696. gdouble interval)
  697. {
  698. guint i;
  699. enum rrd_dst_type type;
  700. for (i = 0; i < file->stat_head->ds_cnt; i++) {
  701. type = rrd_dst_from_string (file->ds_def[i].dst);
  702. if (file->ds_def[i].par[RRD_DS_mrhb_cnt].lv < interval) {
  703. rspamd_strlcpy (file->pdp_prep[i].last_ds, "U",
  704. sizeof (file->pdp_prep[i].last_ds));
  705. pdp_new[i] = NAN;
  706. msg_debug_rrd ("adding unknown point interval %.3f is less than heartbeat %l",
  707. interval, file->ds_def[i].par[RRD_DS_mrhb_cnt].lv);
  708. }
  709. else {
  710. switch (type) {
  711. case RRD_DST_COUNTER:
  712. case RRD_DST_DERIVE:
  713. if (file->pdp_prep[i].last_ds[0] == 'U') {
  714. pdp_new[i] = NAN;
  715. msg_debug_rrd ("last point is NaN for point %ud", i);
  716. }
  717. else {
  718. pdp_new[i] = vals[i] - strtod (file->pdp_prep[i].last_ds,
  719. NULL);
  720. msg_debug_rrd ("new PDP %ud, %.3f", i, pdp_new[i]);
  721. }
  722. break;
  723. case RRD_DST_GAUGE:
  724. pdp_new[i] = vals[i] * interval;
  725. msg_debug_rrd ("new PDP %ud, %.3f", i, pdp_new[i]);
  726. break;
  727. case RRD_DST_ABSOLUTE:
  728. pdp_new[i] = vals[i];
  729. msg_debug_rrd ("new PDP %ud, %.3f", i, pdp_new[i]);
  730. break;
  731. default:
  732. return FALSE;
  733. }
  734. }
  735. /* Copy value to the last_ds */
  736. if (!isnan (vals[i])) {
  737. rspamd_snprintf (file->pdp_prep[i].last_ds,
  738. sizeof (file->pdp_prep[i].last_ds), "%.4f", vals[i]);
  739. }
  740. else {
  741. file->pdp_prep[i].last_ds[0] = 'U';
  742. file->pdp_prep[i].last_ds[1] = '\0';
  743. }
  744. }
  745. return TRUE;
  746. }
  747. /**
  748. * Update step for this pdp
  749. * @param file
  750. * @param pdp_new new pdp array
  751. * @param pdp_temp temp pdp array
  752. * @param interval time till last update
  753. * @param pre_int pre interval
  754. * @param post_int post intervall
  755. * @param pdp_diff time till last pdp update
  756. */
  757. static void
  758. rspamd_rrd_update_pdp_step (struct rspamd_rrd_file *file,
  759. gdouble *pdp_new,
  760. gdouble *pdp_temp,
  761. gdouble interval,
  762. gulong pdp_diff)
  763. {
  764. guint i;
  765. rrd_value_t *scratch;
  766. gulong heartbeat;
  767. for (i = 0; i < file->stat_head->ds_cnt; i++) {
  768. scratch = file->pdp_prep[i].scratch;
  769. heartbeat = file->ds_def[i].par[RRD_DS_mrhb_cnt].lv;
  770. if (!isnan (pdp_new[i])) {
  771. if (isnan (scratch[PDP_val].dv)) {
  772. scratch[PDP_val].dv = 0;
  773. }
  774. }
  775. /* Check interval value for heartbeat for this DS */
  776. if ((interval > heartbeat) ||
  777. (file->stat_head->pdp_step / 2.0 < scratch[PDP_unkn_sec_cnt].lv)) {
  778. pdp_temp[i] = NAN;
  779. }
  780. else {
  781. pdp_temp[i] = scratch[PDP_val].dv /
  782. ((double) (pdp_diff - scratch[PDP_unkn_sec_cnt].lv));
  783. }
  784. if (isnan (pdp_new[i])) {
  785. scratch[PDP_unkn_sec_cnt].lv = interval;
  786. scratch[PDP_val].dv = NAN;
  787. } else {
  788. scratch[PDP_unkn_sec_cnt].lv = 0;
  789. scratch[PDP_val].dv = pdp_new[i] / interval;
  790. }
  791. msg_debug_rrd ("new temp PDP %ud, %.3f -> %.3f, scratch: %3f",
  792. i, pdp_new[i], pdp_temp[i],
  793. scratch[PDP_val].dv);
  794. }
  795. }
  796. /**
  797. * Update CDP for this rra
  798. * @param file rrd file
  799. * @param pdp_steps how much pdp steps elapsed from the last update
  800. * @param pdp_offset offset from pdp
  801. * @param rra_steps how much steps must be updated for this rra
  802. * @param rra_index index of desired rra
  803. * @param pdp_temp temporary pdp points
  804. */
  805. static void
  806. rspamd_rrd_update_cdp (struct rspamd_rrd_file *file,
  807. gdouble pdp_steps,
  808. gdouble pdp_offset,
  809. gulong *rra_steps,
  810. gulong rra_index,
  811. gdouble *pdp_temp)
  812. {
  813. guint i;
  814. struct rrd_rra_def *rra;
  815. rrd_value_t *scratch;
  816. enum rrd_cf_type cf;
  817. gdouble last_cdp = INFINITY, cur_cdp = INFINITY;
  818. gulong pdp_in_cdp;
  819. rra = &file->rra_def[rra_index];
  820. cf = rrd_cf_from_string (rra->cf_nam);
  821. /* Iterate over all DS for this RRA */
  822. for (i = 0; i < file->stat_head->ds_cnt; i++) {
  823. /* Get CDP for this RRA and DS */
  824. scratch =
  825. file->cdp_prep[rra_index * file->stat_head->ds_cnt + i].scratch;
  826. if (rra->pdp_cnt > 1) {
  827. /* Do we have any CDP to update for this rra ? */
  828. if (rra_steps[rra_index] > 0) {
  829. if (isnan (pdp_temp[i])) {
  830. /* New pdp is nan */
  831. /* Increment unknown points count */
  832. scratch[CDP_unkn_pdp_cnt].lv += pdp_offset;
  833. /* Reset secondary value */
  834. scratch[CDP_secondary_val].dv = NAN;
  835. }
  836. else {
  837. scratch[CDP_secondary_val].dv = pdp_temp[i];
  838. }
  839. /* Check XFF for this rra */
  840. if (scratch[CDP_unkn_pdp_cnt].lv > rra->pdp_cnt *
  841. rra->par[RRA_cdp_xff_val].lv) {
  842. /* XFF is reached */
  843. scratch[CDP_primary_val].dv = NAN;
  844. }
  845. else {
  846. /* Need to initialize CDP using specified consolidation */
  847. switch (cf) {
  848. case RRD_CF_AVERAGE:
  849. last_cdp =
  850. isnan (scratch[CDP_val].dv) ? 0.0 : scratch[CDP_val]
  851. .dv;
  852. cur_cdp = isnan (pdp_temp[i]) ? 0.0 : pdp_temp[i];
  853. scratch[CDP_primary_val].dv =
  854. (last_cdp + cur_cdp *
  855. pdp_offset) /
  856. (rra->pdp_cnt - scratch[CDP_unkn_pdp_cnt].lv);
  857. break;
  858. case RRD_CF_MAXIMUM:
  859. last_cdp =
  860. isnan (scratch[CDP_val].dv) ? -INFINITY : scratch[
  861. CDP_val].dv;
  862. cur_cdp = isnan (pdp_temp[i]) ? -INFINITY : pdp_temp[i];
  863. scratch[CDP_primary_val].dv = MAX (last_cdp, cur_cdp);
  864. break;
  865. case RRD_CF_MINIMUM:
  866. last_cdp =
  867. isnan (scratch[CDP_val].dv) ? INFINITY : scratch[
  868. CDP_val].dv;
  869. cur_cdp = isnan (pdp_temp[i]) ? INFINITY : pdp_temp[i];
  870. scratch[CDP_primary_val].dv = MIN (last_cdp, cur_cdp);
  871. break;
  872. case RRD_CF_LAST:
  873. default:
  874. scratch[CDP_primary_val].dv = pdp_temp[i];
  875. last_cdp = INFINITY;
  876. break;
  877. }
  878. }
  879. /* Init carry of this CDP */
  880. pdp_in_cdp = (pdp_steps - pdp_offset) / rra->pdp_cnt;
  881. if (pdp_in_cdp == 0 || isnan (pdp_temp[i])) {
  882. /* Set overflow */
  883. switch (cf) {
  884. case RRD_CF_AVERAGE:
  885. scratch[CDP_val].dv = 0;
  886. break;
  887. case RRD_CF_MAXIMUM:
  888. scratch[CDP_val].dv = -INFINITY;
  889. break;
  890. case RRD_CF_MINIMUM:
  891. scratch[CDP_val].dv = INFINITY;
  892. break;
  893. default:
  894. scratch[CDP_val].dv = NAN;
  895. break;
  896. }
  897. }
  898. else {
  899. /* Special carry for average */
  900. if (cf == RRD_CF_AVERAGE) {
  901. scratch[CDP_val].dv = pdp_temp[i] * pdp_in_cdp;
  902. }
  903. else {
  904. scratch[CDP_val].dv = pdp_temp[i];
  905. }
  906. }
  907. scratch[CDP_unkn_pdp_cnt].lv = 0;
  908. msg_debug_rrd ("update cdp for DS %d with value %.3f, "
  909. "stored value: %.3f, carry: %.3f",
  910. i, last_cdp,
  911. scratch[CDP_primary_val].dv, scratch[CDP_val].dv);
  912. }
  913. /* In this case we just need to update cdp_prep for this RRA */
  914. else {
  915. if (isnan (pdp_temp[i])) {
  916. /* Just increase undefined zone */
  917. scratch[CDP_unkn_pdp_cnt].lv += pdp_steps;
  918. }
  919. else {
  920. /* Calculate cdp value */
  921. last_cdp = scratch[CDP_val].dv;
  922. switch (cf) {
  923. case RRD_CF_AVERAGE:
  924. if (isnan (last_cdp)) {
  925. scratch[CDP_val].dv = pdp_temp[i] * pdp_steps;
  926. }
  927. else {
  928. scratch[CDP_val].dv = last_cdp + pdp_temp[i] *
  929. pdp_steps;
  930. }
  931. break;
  932. case RRD_CF_MAXIMUM:
  933. scratch[CDP_val].dv = MAX (last_cdp, pdp_temp[i]);
  934. break;
  935. case RRD_CF_MINIMUM:
  936. scratch[CDP_val].dv = MIN (last_cdp, pdp_temp[i]);
  937. break;
  938. case RRD_CF_LAST:
  939. scratch[CDP_val].dv = pdp_temp[i];
  940. break;
  941. default:
  942. scratch[CDP_val].dv = NAN;
  943. break;
  944. }
  945. }
  946. msg_debug_rrd ("aggregate cdp %d with pdp %.3f, "
  947. "stored value: %.3f",
  948. i, pdp_temp[i], scratch[CDP_val].dv);
  949. }
  950. }
  951. else {
  952. /* We have nothing to consolidate, but we may miss some pdp */
  953. if (pdp_steps > 2) {
  954. /* Just write PDP value */
  955. scratch[CDP_primary_val].dv = pdp_temp[i];
  956. scratch[CDP_secondary_val].dv = pdp_temp[i];
  957. }
  958. }
  959. }
  960. }
  961. /**
  962. * Update RRA in a file
  963. * @param file rrd file
  964. * @param rra_steps steps for each rra
  965. * @param now current time
  966. */
  967. void
  968. rspamd_rrd_write_rra (struct rspamd_rrd_file *file, gulong *rra_steps)
  969. {
  970. guint i, j, ds_cnt;
  971. struct rrd_rra_def *rra;
  972. struct rrd_cdp_prep *cdp;
  973. gdouble *rra_row = file->rrd_value, *cur_row;
  974. ds_cnt = file->stat_head->ds_cnt;
  975. /* Iterate over all RRA */
  976. for (i = 0; i < file->stat_head->rra_cnt; i++) {
  977. rra = &file->rra_def[i];
  978. if (rra_steps[i] > 0) {
  979. /* Move row ptr */
  980. if (++file->rra_ptr[i].cur_row >= rra->row_cnt) {
  981. file->rra_ptr[i].cur_row = 0;
  982. }
  983. /* Calculate seek */
  984. cdp = &file->cdp_prep[ds_cnt * i];
  985. cur_row = rra_row + ds_cnt * file->rra_ptr[i].cur_row;
  986. /* Iterate over DS */
  987. for (j = 0; j < ds_cnt; j++) {
  988. cur_row[j] = cdp[j].scratch[CDP_primary_val].dv;
  989. msg_debug_rrd ("write cdp %d: %.3f", j, cur_row[j]);
  990. }
  991. }
  992. rra_row += rra->row_cnt * ds_cnt;
  993. }
  994. }
  995. /**
  996. * Add record to rrd file
  997. * @param file rrd file object
  998. * @param points points (must be row suitable for this RRA, depending on ds count)
  999. * @param err error pointer
  1000. * @return TRUE if a row has been added
  1001. */
  1002. gboolean
  1003. rspamd_rrd_add_record (struct rspamd_rrd_file *file,
  1004. GArray *points,
  1005. gdouble ticks,
  1006. GError **err)
  1007. {
  1008. gdouble interval, *pdp_new, *pdp_temp;
  1009. guint i;
  1010. glong seconds, microseconds;
  1011. gulong pdp_steps, cur_pdp_count, prev_pdp_step, cur_pdp_step,
  1012. prev_pdp_age, cur_pdp_age, *rra_steps, pdp_offset;
  1013. if (file == NULL || file->stat_head->ds_cnt * sizeof (gdouble) !=
  1014. points->len) {
  1015. g_set_error (err,
  1016. rrd_error_quark (), EINVAL,
  1017. "rrd add points failed: wrong arguments");
  1018. return FALSE;
  1019. }
  1020. /* Get interval */
  1021. seconds = (glong)ticks;
  1022. microseconds = (glong)((ticks - seconds) * 1000000.);
  1023. interval = ticks - ((gdouble)file->live_head->last_up +
  1024. file->live_head->last_up_usec / 1000000.);
  1025. msg_debug_rrd ("update rrd record after %.3f seconds", interval);
  1026. /* Update PDP preparation values */
  1027. pdp_new = g_malloc0 (sizeof (gdouble) * file->stat_head->ds_cnt);
  1028. pdp_temp = g_malloc0 (sizeof (gdouble) * file->stat_head->ds_cnt);
  1029. /* How much steps need to be updated in each RRA */
  1030. rra_steps = g_malloc0 (sizeof (gulong) * file->stat_head->rra_cnt);
  1031. if (!rspamd_rrd_update_pdp_prep (file, (gdouble *)points->data, pdp_new,
  1032. interval)) {
  1033. g_set_error (err,
  1034. rrd_error_quark (), EINVAL,
  1035. "rrd update pdp failed: wrong arguments");
  1036. g_free (pdp_new);
  1037. g_free (pdp_temp);
  1038. g_free (rra_steps);
  1039. return FALSE;
  1040. }
  1041. /* Calculate elapsed steps */
  1042. /* Age in seconds for previous pdp store */
  1043. prev_pdp_age = file->live_head->last_up % file->stat_head->pdp_step;
  1044. /* Time in seconds for last pdp update */
  1045. prev_pdp_step = file->live_head->last_up - prev_pdp_age;
  1046. /* Age in seconds from current time to required pdp time */
  1047. cur_pdp_age = seconds % file->stat_head->pdp_step;
  1048. /* Time of desired pdp step */
  1049. cur_pdp_step = seconds - cur_pdp_age;
  1050. cur_pdp_count = cur_pdp_step / file->stat_head->pdp_step;
  1051. pdp_steps = (cur_pdp_step - prev_pdp_step) / file->stat_head->pdp_step;
  1052. if (pdp_steps == 0) {
  1053. /* Simple update of pdp prep */
  1054. for (i = 0; i < file->stat_head->ds_cnt; i++) {
  1055. if (isnan (pdp_new[i])) {
  1056. /* Increment unknown period */
  1057. file->pdp_prep[i].scratch[PDP_unkn_sec_cnt].lv += floor (
  1058. interval);
  1059. }
  1060. else {
  1061. if (isnan (file->pdp_prep[i].scratch[PDP_val].dv)) {
  1062. /* Reset pdp to the current value */
  1063. file->pdp_prep[i].scratch[PDP_val].dv = pdp_new[i];
  1064. }
  1065. else {
  1066. /* Increment pdp value */
  1067. file->pdp_prep[i].scratch[PDP_val].dv += pdp_new[i];
  1068. }
  1069. }
  1070. }
  1071. }
  1072. else {
  1073. /* Complex update of PDP, CDP and RRA */
  1074. /* Update PDP for this step */
  1075. rspamd_rrd_update_pdp_step (file,
  1076. pdp_new,
  1077. pdp_temp,
  1078. interval,
  1079. pdp_steps * file->stat_head->pdp_step);
  1080. /* Update CDP points for each RRA*/
  1081. for (i = 0; i < file->stat_head->rra_cnt; i++) {
  1082. /* Calculate pdp offset for this RRA */
  1083. pdp_offset = file->rra_def[i].pdp_cnt - cur_pdp_count %
  1084. file->rra_def[i].pdp_cnt;
  1085. /* How much steps we got for this RRA */
  1086. if (pdp_offset <= pdp_steps) {
  1087. rra_steps[i] =
  1088. (pdp_steps - pdp_offset) / file->rra_def[i].pdp_cnt + 1;
  1089. }
  1090. else {
  1091. /* This rra have not passed enough pdp steps */
  1092. rra_steps[i] = 0;
  1093. }
  1094. msg_debug_rrd ("cdp: %ud, rra steps: %ul(%ul), pdp steps: %ul",
  1095. i, rra_steps[i], pdp_offset, pdp_steps);
  1096. /* Update this specific CDP */
  1097. rspamd_rrd_update_cdp (file,
  1098. pdp_steps,
  1099. pdp_offset,
  1100. rra_steps,
  1101. i,
  1102. pdp_temp);
  1103. }
  1104. /* Write RRA */
  1105. rspamd_rrd_write_rra (file, rra_steps);
  1106. }
  1107. file->live_head->last_up = seconds;
  1108. file->live_head->last_up_usec = microseconds;
  1109. /* Sync and invalidate */
  1110. msync (file->map, file->size, MS_ASYNC | MS_INVALIDATE);
  1111. g_free (pdp_new);
  1112. g_free (pdp_temp);
  1113. g_free (rra_steps);
  1114. return TRUE;
  1115. }
  1116. /**
  1117. * Close rrd file
  1118. * @param file
  1119. * @return
  1120. */
  1121. gint
  1122. rspamd_rrd_close (struct rspamd_rrd_file * file)
  1123. {
  1124. if (file == NULL) {
  1125. errno = EINVAL;
  1126. return -1;
  1127. }
  1128. munmap (file->map, file->size);
  1129. close (file->fd);
  1130. g_free (file->filename);
  1131. g_free (file->id);
  1132. g_free (file);
  1133. return 0;
  1134. }
  1135. static struct rspamd_rrd_file *
  1136. rspamd_rrd_create_file (const gchar *path, gboolean finalize, GError **err)
  1137. {
  1138. struct rspamd_rrd_file *file;
  1139. struct rrd_ds_def ds[RSPAMD_RRD_DS_COUNT];
  1140. struct rrd_rra_def rra[RSPAMD_RRD_RRA_COUNT];
  1141. gint i;
  1142. GArray ar;
  1143. /* Try to create new rrd file */
  1144. file = rspamd_rrd_create (path, RSPAMD_RRD_DS_COUNT, RSPAMD_RRD_RRA_COUNT,
  1145. 1, rspamd_get_calendar_ticks (), err);
  1146. if (file == NULL) {
  1147. return NULL;
  1148. }
  1149. /* Create DS and RRA */
  1150. for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
  1151. rrd_make_default_ds (rspamd_action_to_str (i),
  1152. rrd_dst_to_string (RRD_DST_COUNTER), 1, &ds[i]);
  1153. }
  1154. ar.data = (gchar *)ds;
  1155. ar.len = sizeof (ds);
  1156. if (!rspamd_rrd_add_ds (file, &ar, err)) {
  1157. rspamd_rrd_close (file);
  1158. return NULL;
  1159. }
  1160. /* Once per minute for 1 day */
  1161. rrd_make_default_rra (rrd_cf_to_string (RRD_CF_AVERAGE),
  1162. 60, 24 * 60, &rra[0]);
  1163. /* Once per 5 minutes for 1 week */
  1164. rrd_make_default_rra (rrd_cf_to_string (RRD_CF_AVERAGE),
  1165. 5 * 60, 7 * 24 * 60 / 5, &rra[1]);
  1166. /* Once per 10 mins for 1 month */
  1167. rrd_make_default_rra (rrd_cf_to_string (RRD_CF_AVERAGE),
  1168. 60 * 10, 30 * 24 * 6, &rra[2]);
  1169. /* Once per hour for 1 year */
  1170. rrd_make_default_rra (rrd_cf_to_string (RRD_CF_AVERAGE),
  1171. 60 * 60, 365 * 24, &rra[3]);
  1172. ar.data = (gchar *)rra;
  1173. ar.len = sizeof (rra);
  1174. if (!rspamd_rrd_add_rra (file, &ar, err)) {
  1175. rspamd_rrd_close (file);
  1176. return NULL;
  1177. }
  1178. if (finalize && !rspamd_rrd_finalize (file, err)) {
  1179. rspamd_rrd_close (file);
  1180. return NULL;
  1181. }
  1182. return file;
  1183. }
  1184. static void
  1185. rspamd_rrd_convert_ds (struct rspamd_rrd_file *old,
  1186. struct rspamd_rrd_file *cur, gint idx_old, gint idx_new)
  1187. {
  1188. struct rrd_pdp_prep *pdp_prep_old, *pdp_prep_new;
  1189. struct rrd_cdp_prep *cdp_prep_old, *cdp_prep_new;
  1190. gdouble *val_old, *val_new;
  1191. gulong rra_cnt, i, j, points_cnt, old_ds, new_ds;
  1192. rra_cnt = old->stat_head->rra_cnt;
  1193. pdp_prep_old = &old->pdp_prep[idx_old];
  1194. pdp_prep_new = &cur->pdp_prep[idx_new];
  1195. memcpy (pdp_prep_new, pdp_prep_old, sizeof (*pdp_prep_new));
  1196. val_old = old->rrd_value;
  1197. val_new = cur->rrd_value;
  1198. old_ds = old->stat_head->ds_cnt;
  1199. new_ds = cur->stat_head->ds_cnt;
  1200. for (i = 0; i < rra_cnt; i++) {
  1201. cdp_prep_old = &old->cdp_prep[i * old_ds] + idx_old;
  1202. cdp_prep_new = &cur->cdp_prep[i * new_ds] + idx_new;
  1203. memcpy (cdp_prep_new, cdp_prep_old, sizeof (*cdp_prep_new));
  1204. points_cnt = old->rra_def[i].row_cnt;
  1205. for (j = 0; j < points_cnt; j ++) {
  1206. val_new[j * new_ds + idx_new] = val_old[j * old_ds + idx_old];
  1207. }
  1208. val_new += points_cnt * new_ds;
  1209. val_old += points_cnt * old_ds;
  1210. }
  1211. }
  1212. static struct rspamd_rrd_file *
  1213. rspamd_rrd_convert (const gchar *path, struct rspamd_rrd_file *old,
  1214. GError **err)
  1215. {
  1216. struct rspamd_rrd_file *rrd;
  1217. gchar tpath[PATH_MAX];
  1218. g_assert (old != NULL);
  1219. rspamd_snprintf (tpath, sizeof (tpath), "%s.new", path);
  1220. rrd = rspamd_rrd_create_file (tpath, TRUE, err);
  1221. if (rrd) {
  1222. /* Copy old data */
  1223. memcpy (rrd->live_head, old->live_head, sizeof (*rrd->live_head));
  1224. memcpy (rrd->rra_ptr, old->rra_ptr,
  1225. sizeof (*old->rra_ptr) * rrd->stat_head->rra_cnt);
  1226. /*
  1227. * Old DSes:
  1228. * 0 - spam -> reject
  1229. * 1 - probable spam -> add header
  1230. * 2 - greylist -> greylist
  1231. * 3 - ham -> ham
  1232. */
  1233. rspamd_rrd_convert_ds (old, rrd, 0, METRIC_ACTION_REJECT);
  1234. rspamd_rrd_convert_ds (old, rrd, 1, METRIC_ACTION_ADD_HEADER);
  1235. rspamd_rrd_convert_ds (old, rrd, 2, METRIC_ACTION_GREYLIST);
  1236. rspamd_rrd_convert_ds (old, rrd, 3, METRIC_ACTION_NOACTION);
  1237. if (unlink (path) == -1) {
  1238. g_set_error (err, rrd_error_quark (), errno, "cannot unlink old rrd file %s: %s",
  1239. path, strerror (errno));
  1240. unlink (tpath);
  1241. rspamd_rrd_close (rrd);
  1242. return NULL;
  1243. }
  1244. if (rename (tpath, path) == -1) {
  1245. g_set_error (err, rrd_error_quark (), errno, "cannot rename old rrd file %s: %s",
  1246. path, strerror (errno));
  1247. unlink (tpath);
  1248. rspamd_rrd_close (rrd);
  1249. return NULL;
  1250. }
  1251. }
  1252. return rrd;
  1253. }
  1254. struct rspamd_rrd_file *
  1255. rspamd_rrd_file_default (const gchar *path,
  1256. GError **err)
  1257. {
  1258. struct rspamd_rrd_file *file, *nf;
  1259. g_assert (path != NULL);
  1260. if (access (path, R_OK) != -1) {
  1261. /* We can open rrd file */
  1262. file = rspamd_rrd_open (path, err);
  1263. if (file == NULL) {
  1264. return NULL;
  1265. }
  1266. if (file->stat_head->rra_cnt != RSPAMD_RRD_RRA_COUNT) {
  1267. msg_err_rrd ("rrd file is not suitable for rspamd: it has "
  1268. "%ul ds and %ul rra", file->stat_head->ds_cnt,
  1269. file->stat_head->rra_cnt);
  1270. g_set_error (err, rrd_error_quark (), EINVAL, "bad rrd file");
  1271. rspamd_rrd_close (file);
  1272. return NULL;
  1273. }
  1274. else if (file->stat_head->ds_cnt == RSPAMD_RRD_OLD_DS_COUNT) {
  1275. /* Old rrd, need to convert */
  1276. msg_info_rrd ("rrd file %s is not suitable for rspamd, convert it",
  1277. path);
  1278. nf = rspamd_rrd_convert (path, file, err);
  1279. rspamd_rrd_close (file);
  1280. return nf;
  1281. }
  1282. else if (file->stat_head->ds_cnt == RSPAMD_RRD_DS_COUNT) {
  1283. return file;
  1284. }
  1285. else {
  1286. msg_err_rrd ("rrd file is not suitable for rspamd: it has "
  1287. "%ul ds and %ul rra", file->stat_head->ds_cnt,
  1288. file->stat_head->rra_cnt);
  1289. g_set_error (err, rrd_error_quark (), EINVAL, "bad rrd file");
  1290. rspamd_rrd_close (file);
  1291. return NULL;
  1292. }
  1293. }
  1294. file = rspamd_rrd_create_file (path, TRUE, err);
  1295. return file;
  1296. }
  1297. struct rspamd_rrd_query_result *
  1298. rspamd_rrd_query (struct rspamd_rrd_file *file,
  1299. gulong rra_num)
  1300. {
  1301. struct rspamd_rrd_query_result *res;
  1302. struct rrd_rra_def *rra;
  1303. const gdouble *rra_offset = NULL;
  1304. guint i;
  1305. g_assert (file != NULL);
  1306. if (rra_num > file->stat_head->rra_cnt) {
  1307. msg_err_rrd ("requested unexisting rra: %l", rra_num);
  1308. return NULL;
  1309. }
  1310. res = g_malloc0 (sizeof (*res));
  1311. res->ds_count = file->stat_head->ds_cnt;
  1312. res->last_update = (gdouble)file->live_head->last_up +
  1313. ((gdouble)file->live_head->last_up_usec / 1e6f);
  1314. res->pdp_per_cdp = file->rra_def[rra_num].pdp_cnt;
  1315. res->rra_rows = file->rra_def[rra_num].row_cnt;
  1316. rra_offset = file->rrd_value;
  1317. for (i = 0; i < file->stat_head->rra_cnt; i++) {
  1318. rra = &file->rra_def[i];
  1319. if (i == rra_num) {
  1320. res->cur_row = file->rra_ptr[i].cur_row % rra->row_cnt;
  1321. break;
  1322. }
  1323. rra_offset += rra->row_cnt * res->ds_count;
  1324. }
  1325. res->data = rra_offset;
  1326. return res;
  1327. }