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

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