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.

преди 8 години
преди 8 години
преди 8 години
преди 11 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 11 години
преди 8 години
преди 10 години
преди 11 години
преди 8 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 10 години
преди 8 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 10 години
преди 8 години
преди 11 години
преди 10 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 10 години
преди 11 години
преди 6 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 6 години
преди 11 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 8 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 6 години
преди 11 години
преди 11 години
преди 8 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 8 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 8 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 11 години
преди 8 години
преди 10 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 11 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 8 години
преди 11 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 8 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 8 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 8 години
преди 6 години
преди 11 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 7 години
преди 7 години
преди 8 години
преди 6 години
преди 8 години
преди 8 години
преди 8 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506
  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 (head.float_cookie != RRD_FLOAT_COOKIE) {
  211. g_set_error (err,
  212. rrd_error_quark (), EINVAL, "rrd head error: another architecture "
  213. "(file cookie %g != our cookie %g)",
  214. head.float_cookie, RRD_FLOAT_COOKIE);
  215. close (fd);
  216. return FALSE;
  217. }
  218. /* Check for other params */
  219. if (head.ds_cnt <= 0 || head.rra_cnt <= 0) {
  220. g_set_error (err,
  221. rrd_error_quark (), EINVAL, "rrd head cookies error: bad rra or ds count");
  222. close (fd);
  223. return FALSE;
  224. }
  225. /* Now we can calculate the overall size of rrd */
  226. head_size = sizeof (struct rrd_file_head) +
  227. sizeof (struct rrd_ds_def) * head.ds_cnt +
  228. sizeof (struct rrd_rra_def) * head.rra_cnt +
  229. sizeof (struct rrd_live_head) +
  230. sizeof (struct rrd_pdp_prep) * head.ds_cnt +
  231. sizeof (struct rrd_cdp_prep) * head.ds_cnt * head.rra_cnt +
  232. sizeof (struct rrd_rra_ptr) * head.rra_cnt;
  233. if (st.st_size < (goffset)head_size) {
  234. g_set_error (err,
  235. rrd_error_quark (), errno, "rrd file seems to have stripped header: %d",
  236. head_size);
  237. close (fd);
  238. return FALSE;
  239. }
  240. if (need_data) {
  241. /* Now check rra */
  242. if (lseek (fd, sizeof (struct rrd_ds_def) * head.ds_cnt,
  243. SEEK_CUR) == -1) {
  244. g_set_error (err,
  245. rrd_error_quark (), errno, "rrd head lseek error: %s",
  246. strerror (errno));
  247. close (fd);
  248. return FALSE;
  249. }
  250. for (i = 0; i < (gint)head.rra_cnt; i++) {
  251. if (read (fd, &rra, sizeof (rra)) != sizeof (rra)) {
  252. g_set_error (err,
  253. rrd_error_quark (), errno, "rrd read rra error: %s",
  254. strerror (errno));
  255. close (fd);
  256. return FALSE;
  257. }
  258. head_size += rra.row_cnt * head.ds_cnt * sizeof (gdouble);
  259. }
  260. if (st.st_size != head_size) {
  261. g_set_error (err,
  262. rrd_error_quark (), EINVAL, "rrd file seems to have incorrect size: %d, must be %d",
  263. (gint)st.st_size, head_size);
  264. close (fd);
  265. return FALSE;
  266. }
  267. }
  268. close (fd);
  269. return TRUE;
  270. }
  271. /**
  272. * Adjust pointers in mmapped rrd file
  273. * @param file
  274. */
  275. static void
  276. rspamd_rrd_adjust_pointers (struct rspamd_rrd_file *file, gboolean completed)
  277. {
  278. guint8 *ptr;
  279. ptr = file->map;
  280. file->stat_head = (struct rrd_file_head *)ptr;
  281. ptr += sizeof (struct rrd_file_head);
  282. file->ds_def = (struct rrd_ds_def *)ptr;
  283. ptr += sizeof (struct rrd_ds_def) * file->stat_head->ds_cnt;
  284. file->rra_def = (struct rrd_rra_def *)ptr;
  285. ptr += sizeof (struct rrd_rra_def) * file->stat_head->rra_cnt;
  286. file->live_head = (struct rrd_live_head *)ptr;
  287. ptr += sizeof (struct rrd_live_head);
  288. file->pdp_prep = (struct rrd_pdp_prep *)ptr;
  289. ptr += sizeof (struct rrd_pdp_prep) * file->stat_head->ds_cnt;
  290. file->cdp_prep = (struct rrd_cdp_prep *)ptr;
  291. ptr += sizeof (struct rrd_cdp_prep) * file->stat_head->rra_cnt *
  292. file->stat_head->ds_cnt;
  293. file->rra_ptr = (struct rrd_rra_ptr *)ptr;
  294. if (completed) {
  295. ptr += sizeof (struct rrd_rra_ptr) * file->stat_head->rra_cnt;
  296. file->rrd_value = (gdouble *)ptr;
  297. }
  298. else {
  299. file->rrd_value = NULL;
  300. }
  301. }
  302. static void
  303. rspamd_rrd_calculate_checksum (struct rspamd_rrd_file *file)
  304. {
  305. guchar sigbuf[rspamd_cryptobox_HASHBYTES];
  306. struct rrd_ds_def *ds;
  307. guint i;
  308. rspamd_cryptobox_hash_state_t st;
  309. if (file->finalized) {
  310. rspamd_cryptobox_hash_init (&st, NULL, 0);
  311. rspamd_cryptobox_hash_update (&st, file->filename, strlen (file->filename));
  312. for (i = 0; i < file->stat_head->ds_cnt; i ++) {
  313. ds = &file->ds_def[i];
  314. rspamd_cryptobox_hash_update (&st, ds->ds_nam, sizeof (ds->ds_nam));
  315. }
  316. rspamd_cryptobox_hash_final (&st, sigbuf);
  317. file->id = rspamd_encode_base32 (sigbuf, sizeof (sigbuf));
  318. }
  319. }
  320. static int
  321. rspamd_rrd_open_exclusive (const gchar *filename)
  322. {
  323. struct timespec sleep_ts = {
  324. .tv_sec = 0,
  325. .tv_nsec = 1000000
  326. };
  327. gint fd;
  328. fd = open (filename, O_RDWR);
  329. if (fd == -1) {
  330. return -1;
  331. }
  332. for (;;) {
  333. if (rspamd_file_lock (fd, TRUE) == -1) {
  334. if (errno == EAGAIN || errno == EWOULDBLOCK) {
  335. nanosleep (&sleep_ts, NULL);
  336. continue;
  337. }
  338. else {
  339. close (fd);
  340. return -1;
  341. }
  342. }
  343. else {
  344. break;
  345. }
  346. }
  347. return fd;
  348. };
  349. /**
  350. * Open completed or incompleted rrd file
  351. * @param filename
  352. * @param completed
  353. * @param err
  354. * @return
  355. */
  356. static struct rspamd_rrd_file *
  357. rspamd_rrd_open_common (const gchar *filename, gboolean completed, GError **err)
  358. {
  359. struct rspamd_rrd_file *file;
  360. gint fd;
  361. struct stat st;
  362. if (!rspamd_rrd_check_file (filename, completed, err)) {
  363. return NULL;
  364. }
  365. file = g_malloc0 (sizeof (struct rspamd_rrd_file));
  366. /* Open file */
  367. fd = rspamd_rrd_open_exclusive (filename);
  368. if (fd == -1) {
  369. g_set_error (err,
  370. rrd_error_quark (), errno, "rrd open error: %s", strerror (errno));
  371. g_free (file);
  372. return FALSE;
  373. }
  374. if (fstat (fd, &st) == -1) {
  375. g_set_error (err,
  376. rrd_error_quark (), errno, "rrd stat error: %s", strerror (errno));
  377. rspamd_file_unlock (fd, FALSE);
  378. g_free (file);
  379. close (fd);
  380. return FALSE;
  381. }
  382. /* Mmap file */
  383. file->size = st.st_size;
  384. if ((file->map =
  385. mmap (NULL, st.st_size, PROT_READ | PROT_WRITE,
  386. MAP_SHARED, fd, 0)) == MAP_FAILED) {
  387. rspamd_file_unlock (fd, FALSE);
  388. close (fd);
  389. g_set_error (err,
  390. rrd_error_quark (), ENOMEM, "mmap failed: %s", strerror (errno));
  391. g_free (file);
  392. return NULL;
  393. }
  394. file->fd = fd;
  395. /* Adjust pointers */
  396. rspamd_rrd_adjust_pointers (file, completed);
  397. /* Mark it as finalized */
  398. file->finalized = completed;
  399. file->filename = g_strdup (filename);
  400. rspamd_rrd_calculate_checksum (file);
  401. return file;
  402. }
  403. /**
  404. * Open (and mmap) existing RRD file
  405. * @param filename path
  406. * @param err error pointer
  407. * @return rrd file structure
  408. */
  409. struct rspamd_rrd_file *
  410. rspamd_rrd_open (const gchar *filename, GError **err)
  411. {
  412. struct rspamd_rrd_file *file;
  413. if ((file = rspamd_rrd_open_common (filename, TRUE, err))) {
  414. msg_info_rrd ("rrd file opened: %s", filename);
  415. }
  416. return file;
  417. }
  418. /**
  419. * Create basic header for rrd file
  420. * @param filename file path
  421. * @param ds_count number of data sources
  422. * @param rra_count number of round robin archives
  423. * @param pdp_step step of primary data points
  424. * @param err error pointer
  425. * @return TRUE if file has been created
  426. */
  427. struct rspamd_rrd_file *
  428. rspamd_rrd_create (const gchar *filename,
  429. gulong ds_count,
  430. gulong rra_count,
  431. gulong pdp_step,
  432. gdouble initial_ticks,
  433. GError **err)
  434. {
  435. struct rspamd_rrd_file *new;
  436. struct rrd_file_head head;
  437. struct rrd_ds_def ds;
  438. struct rrd_rra_def rra;
  439. struct rrd_live_head lh;
  440. struct rrd_pdp_prep pdp;
  441. struct rrd_cdp_prep cdp;
  442. struct rrd_rra_ptr rra_ptr;
  443. gint fd;
  444. guint i, j;
  445. /* Open file */
  446. fd = open (filename, O_RDWR | O_CREAT | O_EXCL, 0644);
  447. if (fd == -1) {
  448. g_set_error (err,
  449. rrd_error_quark (), errno, "rrd create error: %s",
  450. strerror (errno));
  451. return NULL;
  452. }
  453. rspamd_file_lock (fd, FALSE);
  454. /* Fill header */
  455. memset (&head, 0, sizeof (head));
  456. head.rra_cnt = rra_count;
  457. head.ds_cnt = ds_count;
  458. head.pdp_step = pdp_step;
  459. memcpy (head.cookie, RRD_COOKIE, sizeof (head.cookie));
  460. memcpy (head.version, RRD_VERSION, sizeof (head.version));
  461. head.float_cookie = RRD_FLOAT_COOKIE;
  462. if (write (fd, &head, sizeof (head)) != sizeof (head)) {
  463. rspamd_file_unlock (fd, FALSE);
  464. close (fd);
  465. g_set_error (err,
  466. rrd_error_quark (), errno, "rrd write error: %s", strerror (errno));
  467. return NULL;
  468. }
  469. /* Fill DS section */
  470. memset (&ds, 0, sizeof (ds));
  471. memset (&ds.ds_nam, 0, sizeof (ds.ds_nam));
  472. memcpy (&ds.dst, "COUNTER", sizeof ("COUNTER"));
  473. memset (&ds.par, 0, sizeof (ds.par));
  474. for (i = 0; i < ds_count; i++) {
  475. if (write (fd, &ds, sizeof (ds)) != sizeof (ds)) {
  476. rspamd_file_unlock (fd, FALSE);
  477. close (fd);
  478. g_set_error (err,
  479. rrd_error_quark (), errno, "rrd write error: %s",
  480. strerror (errno));
  481. return NULL;
  482. }
  483. }
  484. /* Fill RRA section */
  485. memset (&rra, 0, sizeof (rra));
  486. memcpy (&rra.cf_nam, "AVERAGE", sizeof ("AVERAGE"));
  487. rra.pdp_cnt = 1;
  488. memset (&rra.par, 0, sizeof (rra.par));
  489. for (i = 0; i < rra_count; i++) {
  490. if (write (fd, &rra, sizeof (rra)) != sizeof (rra)) {
  491. rspamd_file_unlock (fd, FALSE);
  492. close (fd);
  493. g_set_error (err,
  494. rrd_error_quark (), errno, "rrd write error: %s",
  495. strerror (errno));
  496. return NULL;
  497. }
  498. }
  499. /* Fill live header */
  500. memset (&lh, 0, sizeof (lh));
  501. lh.last_up = (glong)initial_ticks;
  502. lh.last_up_usec = (glong)((initial_ticks - lh.last_up) * 1e6f);
  503. if (write (fd, &lh, sizeof (lh)) != sizeof (lh)) {
  504. rspamd_file_unlock (fd, FALSE);
  505. close (fd);
  506. g_set_error (err,
  507. rrd_error_quark (), errno, "rrd write error: %s", strerror (errno));
  508. return NULL;
  509. }
  510. /* Fill pdp prep */
  511. memset (&pdp, 0, sizeof (pdp));
  512. memcpy (&pdp.last_ds, "U", sizeof ("U"));
  513. memset (&pdp.scratch, 0, sizeof (pdp.scratch));
  514. pdp.scratch[PDP_val].dv = NAN;
  515. pdp.scratch[PDP_unkn_sec_cnt].lv = 0;
  516. for (i = 0; i < ds_count; i++) {
  517. if (write (fd, &pdp, sizeof (pdp)) != sizeof (pdp)) {
  518. rspamd_file_unlock (fd, FALSE);
  519. close (fd);
  520. g_set_error (err,
  521. rrd_error_quark (), errno, "rrd write error: %s",
  522. strerror (errno));
  523. return NULL;
  524. }
  525. }
  526. /* Fill cdp prep */
  527. memset (&cdp, 0, sizeof (cdp));
  528. memset (&cdp.scratch, 0, sizeof (cdp.scratch));
  529. cdp.scratch[CDP_val].dv = NAN;
  530. cdp.scratch[CDP_unkn_pdp_cnt].lv = 0;
  531. for (i = 0; i < rra_count; i++) {
  532. for (j = 0; j < ds_count; j++) {
  533. if (write (fd, &cdp, sizeof (cdp)) != sizeof (cdp)) {
  534. rspamd_file_unlock (fd, FALSE);
  535. close (fd);
  536. g_set_error (err,
  537. rrd_error_quark (), errno, "rrd write error: %s",
  538. strerror (errno));
  539. return NULL;
  540. }
  541. }
  542. }
  543. /* Set row pointers */
  544. memset (&rra_ptr, 0, sizeof (rra_ptr));
  545. for (i = 0; i < rra_count; i++) {
  546. if (write (fd, &rra_ptr, sizeof (rra_ptr)) != sizeof (rra_ptr)) {
  547. rspamd_file_unlock (fd, FALSE);
  548. close (fd);
  549. g_set_error (err,
  550. rrd_error_quark (), errno, "rrd write error: %s",
  551. strerror (errno));
  552. return NULL;
  553. }
  554. }
  555. rspamd_file_unlock (fd, FALSE);
  556. close (fd);
  557. new = rspamd_rrd_open_common (filename, FALSE, err);
  558. return new;
  559. }
  560. /**
  561. * Add data sources to rrd file
  562. * @param filename path to file
  563. * @param ds array of struct rrd_ds_def
  564. * @param err error pointer
  565. * @return TRUE if data sources were added
  566. */
  567. gboolean
  568. rspamd_rrd_add_ds (struct rspamd_rrd_file *file, GArray *ds, GError **err)
  569. {
  570. if (file == NULL || file->stat_head->ds_cnt * sizeof (struct rrd_ds_def) !=
  571. ds->len) {
  572. g_set_error (err,
  573. rrd_error_quark (), EINVAL, "rrd add ds failed: wrong arguments");
  574. return FALSE;
  575. }
  576. /* Straightforward memcpy */
  577. memcpy (file->ds_def, ds->data, ds->len);
  578. return TRUE;
  579. }
  580. /**
  581. * Add round robin archives to rrd file
  582. * @param filename path to file
  583. * @param ds array of struct rrd_rra_def
  584. * @param err error pointer
  585. * @return TRUE if archives were added
  586. */
  587. gboolean
  588. rspamd_rrd_add_rra (struct rspamd_rrd_file *file, GArray *rra, GError **err)
  589. {
  590. if (file == NULL || file->stat_head->rra_cnt *
  591. sizeof (struct rrd_rra_def) != rra->len) {
  592. g_set_error (err,
  593. rrd_error_quark (), EINVAL, "rrd add rra failed: wrong arguments");
  594. return FALSE;
  595. }
  596. /* Straightforward memcpy */
  597. memcpy (file->rra_def, rra->data, rra->len);
  598. return TRUE;
  599. }
  600. /**
  601. * Finalize rrd file header and initialize all RRA in the file
  602. * @param filename file path
  603. * @param err error pointer
  604. * @return TRUE if rrd file is ready for use
  605. */
  606. gboolean
  607. rspamd_rrd_finalize (struct rspamd_rrd_file *file, GError **err)
  608. {
  609. gint fd;
  610. guint i;
  611. gint count = 0;
  612. gdouble vbuf[1024];
  613. struct stat st;
  614. if (file == NULL || file->filename == NULL || file->fd == -1) {
  615. g_set_error (err,
  616. rrd_error_quark (), EINVAL, "rrd add rra failed: wrong arguments");
  617. return FALSE;
  618. }
  619. fd = file->fd;
  620. if (lseek (fd, 0, SEEK_END) == -1) {
  621. g_set_error (err,
  622. rrd_error_quark (), errno, "rrd seek error: %s", strerror (errno));
  623. close (fd);
  624. return FALSE;
  625. }
  626. /* Adjust CDP */
  627. for (i = 0; i < file->stat_head->rra_cnt; i++) {
  628. file->cdp_prep->scratch[CDP_unkn_pdp_cnt].lv = 0;
  629. /* Randomize row pointer (disabled) */
  630. /* file->rra_ptr->cur_row = g_random_int () % file->rra_def[i].row_cnt; */
  631. file->rra_ptr->cur_row = file->rra_def[i].row_cnt - 1;
  632. /* Calculate values count */
  633. count += file->rra_def[i].row_cnt * file->stat_head->ds_cnt;
  634. }
  635. munmap (file->map, file->size);
  636. /* Write values */
  637. for (i = 0; i < G_N_ELEMENTS (vbuf); i++) {
  638. vbuf[i] = NAN;
  639. }
  640. while (count > 0) {
  641. /* Write values in buffered matter */
  642. if (write (fd, vbuf,
  643. MIN ((gint)G_N_ELEMENTS (vbuf), count) * sizeof (gdouble)) == -1) {
  644. g_set_error (err,
  645. rrd_error_quark (), errno, "rrd write error: %s",
  646. strerror (errno));
  647. close (fd);
  648. return FALSE;
  649. }
  650. count -= G_N_ELEMENTS (vbuf);
  651. }
  652. if (fstat (fd, &st) == -1) {
  653. g_set_error (err,
  654. rrd_error_quark (), errno, "rrd stat error: %s", strerror (errno));
  655. close (fd);
  656. return FALSE;
  657. }
  658. /* Mmap again */
  659. file->size = st.st_size;
  660. if ((file->map =
  661. mmap (NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
  662. 0)) == MAP_FAILED) {
  663. close (fd);
  664. g_set_error (err,
  665. rrd_error_quark (), ENOMEM, "mmap failed: %s", strerror (errno));
  666. g_free (file);
  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. }