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 #ifndef RRD_H_
17 #define RRD_H_
18 
19 #include "config.h"
20 
21 /**
22  * This file contains basic structure and functions to operate with round-robin databases
23  */
24 
25 #define RRD_COOKIE    "RRD"
26 #define RRD_VERSION   "0003"
27 #define RRD_FLOAT_COOKIE  ((double)8.642135E130)
28 
29 #ifdef  __cplusplus
30 extern "C" {
31 #endif
32 
33 typedef union {
34 	unsigned long lv;
35 	double dv;
36 } rrd_value_t;
37 
38 struct rrd_file_head {
39 	/* Data Base Identification Section ** */
40 	gchar cookie[4];         /* RRD */
41 	gchar version[5];        /* version of the format */
42 	gdouble float_cookie;    /* is it the correct double representation ?  */
43 
44 	/* Data Base Structure Definition **** */
45 	gulong ds_cnt;   /* how many different ds provid input to the rrd */
46 	gulong rra_cnt;  /* how many rras will be maintained in the rrd */
47 	gulong pdp_step; /* pdp interval in seconds */
48 
49 	rrd_value_t par[10];    /* global parameters ... unused
50 	                           at the moment */
51 };
52 
53 enum rrd_dst_type {
54 	RRD_DST_INVALID = -1,
55 	RRD_DST_COUNTER = 0,  /* data source types available */
56 	RRD_DST_ABSOLUTE,
57 	RRD_DST_GAUGE,
58 	RRD_DST_DERIVE,
59 	RRD_DST_CDEF
60 };
61 enum rrd_ds_param {
62 	RRD_DS_mrhb_cnt = 0, /* minimum required heartbeat */
63 	RRD_DS_min_val,         /* the processed input of a ds must */
64 	RRD_DS_max_val,         /* be between max_val and min_val
65 	                         * both can be set to UNKNOWN if you
66 	                         * do not care. Data outside the limits
67 	                         * set to UNKNOWN */
68 	RRD_DS_cdef = RRD_DS_mrhb_cnt
69 };                      /* pointer to encoded rpn expression only applies to DST_CDEF */
70 
71 
72 /* The magic number here is one less than DS_NAM_SIZE */
73 #define RRD_DS_NAM_SIZE   20
74 
75 #define RRD_DST_SIZE   20
76 
77 struct rrd_ds_def {
78 	gchar ds_nam[RRD_DS_NAM_SIZE];  /* Name of the data source (null terminated) */
79 	gchar dst[RRD_DST_SIZE];    /* Type of data source (null terminated) */
80 	rrd_value_t par[10];  /* index of this array see ds_param_en */
81 };
82 
83 /* RRA definition */
84 
85 enum rrd_cf_type {
86 	RRD_CF_INVALID = -1,
87 	RRD_CF_AVERAGE = 0,    /* data consolidation functions */
88 	RRD_CF_MINIMUM,
89 	RRD_CF_MAXIMUM,
90 	RRD_CF_LAST,
91 };
92 
93 
94 #define MAX_RRA_PAR_EN 10
95 
96 enum rrd_rra_param {
97 	RRA_cdp_xff_val = 0,  /* what part of the consolidated
98 	                       * datapoint must be known, to produce a
99 	                       * valid entry in the rra */
100 };
101 
102 
103 #define RRD_CF_NAM_SIZE   20
104 
105 struct rrd_rra_def {
106 	gchar cf_nam[RRD_CF_NAM_SIZE];   /* consolidation function (null term) */
107 	gulong row_cnt;  /* number of entries in the store */
108 	gulong pdp_cnt;  /* how many primary data points are
109 	                  * required for a consolidated data point?*/
110 	rrd_value_t par[MAX_RRA_PAR_EN];  /* index see rra_param_en */
111 
112 };
113 
114 struct rrd_live_head {
115 	time_t last_up;  /* when was rrd last updated */
116 	glong last_up_usec; /* micro seconds part of the update timestamp. Always >= 0 */
117 };
118 
119 #define RRD_LAST_DS_LEN 30
120 
121 enum rrd_pdp_param {
122 	PDP_unkn_sec_cnt = 0, /* how many seconds of the current
123 	                       * pdp value is unknown data? */
124 	PDP_val
125 };                      /* current value of the pdp.
126                            this depends on dst */
127 
128 struct rrd_pdp_prep {
129 	gchar last_ds[RRD_LAST_DS_LEN]; /* the last reading from the data
130 	                                 * source.  this is stored in ASCII
131 	                                 * to cater for very large counters
132 	                                 * we might encounter in connection
133 	                                 * with SNMP. */
134 	rrd_value_t scratch[10];  /* contents according to pdp_par_en */
135 };
136 
137 #define RRD_MAX_CDP_PAR_EN 10
138 #define RRD_MAX_CDP_FAILURES_IDX 8
139 /* max CDP scratch entries avail to record violations for a FAILURES RRA */
140 #define RRD_MAX_FAILURES_WINDOW_LEN 28
141 
142 enum rrd_cdp_param {
143 	CDP_val = 0,
144 	/* the base_interval is always an
145 	 * average */
146 			CDP_unkn_pdp_cnt,
147 	/* how many unknown pdp were
148 	 * integrated. This and the cdp_xff
149 	 * will decide if this is going to
150 	 * be a UNKNOWN or a valid value */
151 			CDP_hw_intercept,
152 	/* Current intercept coefficient for the Holt-Winters
153 	 * prediction algorithm. */
154 			CDP_hw_last_intercept,
155 	/* Last iteration intercept coefficient for the Holt-Winters
156 	 * prediction algorihtm. */
157 			CDP_hw_slope,
158 	/* Current slope coefficient for the Holt-Winters
159 	 * prediction algorithm. */
160 			CDP_hw_last_slope,
161 	/* Last iteration slope coeffient. */
162 			CDP_null_count,
163 	/* Number of sequential Unknown (DNAN) values + 1 preceding
164 	 * the current prediction.
165 	 * */
166 			CDP_last_null_count,
167 	/* Last iteration count of Unknown (DNAN) values. */
168 			CDP_primary_val = 8,
169 	/* optimization for bulk updates: the value of the first CDP
170 	 * value to be written in the bulk update. */
171 			CDP_secondary_val = 9,
172 	/* optimization for bulk updates: the value of subsequent
173 	 * CDP values to be written in the bulk update. */
174 			CDP_hw_seasonal = CDP_hw_intercept,
175 	/* Current seasonal coefficient for the Holt-Winters
176 	 * prediction algorithm. This is stored in CDP prep to avoid
177 	 * redundant seek operations. */
178 			CDP_hw_last_seasonal = CDP_hw_last_intercept,
179 	/* Last iteration seasonal coefficient. */
180 			CDP_seasonal_deviation = CDP_hw_intercept,
181 	CDP_last_seasonal_deviation = CDP_hw_last_intercept,
182 	CDP_init_seasonal = CDP_null_count
183 };
184 
185 struct rrd_cdp_prep {
186 	rrd_value_t scratch[RRD_MAX_CDP_PAR_EN];
187 	/* contents according to cdp_par_en *
188 	 * init state should be NAN */
189 };
190 
191 struct rrd_rra_ptr {
192 	gulong cur_row;  /* current row in the rra */
193 };
194 
195 /* Final rrd file structure */
196 struct rspamd_rrd_file {
197 	struct rrd_file_head *stat_head; /* the static header */
198 	struct rrd_ds_def *ds_def;   /* list of data source definitions */
199 	struct rrd_rra_def *rra_def; /* list of round robin archive def */
200 	struct rrd_live_head *live_head; /* rrd v >= 3 last_up with us */
201 	struct rrd_pdp_prep *pdp_prep;   /* pdp data prep area */
202 	struct rrd_cdp_prep *cdp_prep;   /* cdp prep area */
203 	struct rrd_rra_ptr *rra_ptr; /* list of rra pointers */
204 	gdouble *rrd_value; /* list of rrd values */
205 
206 	gchar *filename;
207 	guint8 *map; /* mmapped area */
208 	gsize size; /* its size */
209 	gboolean finalized;
210 	gchar *id;
211 	gint fd;
212 };
213 
214 
215 /* Public API */
216 
217 /**
218  * Open (and mmap) existing RRD file
219  * @param filename path
220  * @param err error pointer
221  * @return rrd file structure
222  */
223 struct rspamd_rrd_file *rspamd_rrd_open (const gchar *filename, GError **err);
224 
225 /**
226  * Create basic header for rrd file
227  * @param filename file path
228  * @param ds_count number of data sources
229  * @param rra_count number of round robin archives
230  * @param pdp_step step of primary data points
231  * @param err error pointer
232  * @return TRUE if file has been created
233  */
234 struct rspamd_rrd_file *rspamd_rrd_create (const gchar *filename,
235 										   gulong ds_count,
236 										   gulong rra_count,
237 										   gulong pdp_step,
238 										   gdouble initial_ticks,
239 										   GError **err);
240 
241 /**
242  * Add data sources to rrd file
243  * @param filename path to file
244  * @param ds array of struct rrd_ds_def
245  * @param err error pointer
246  * @return TRUE if data sources were added
247  */
248 gboolean rspamd_rrd_add_ds (struct rspamd_rrd_file *file,
249 							GArray *ds,
250 							GError **err);
251 
252 /**
253  * Add round robin archives to rrd file
254  * @param filename path to file
255  * @param ds array of struct rrd_rra_def
256  * @param err error pointer
257  * @return TRUE if archives were added
258  */
259 gboolean rspamd_rrd_add_rra (struct rspamd_rrd_file *file,
260 							 GArray *rra,
261 							 GError **err);
262 
263 /**
264  * Finalize rrd file header and initialize all RRA in the file
265  * @param filename file path
266  * @param err error pointer
267  * @return TRUE if rrd file is ready for use
268  */
269 gboolean rspamd_rrd_finalize (struct rspamd_rrd_file *file, GError **err);
270 
271 /**
272  * Add record to rrd file
273  * @param file rrd file object
274  * @param points points (must be row suitable for this RRA, depending on ds count)
275  * @param err error pointer
276  * @return TRUE if a row has been added
277  */
278 gboolean rspamd_rrd_add_record (struct rspamd_rrd_file *file,
279 								GArray *points,
280 								gdouble ticks,
281 								GError **err);
282 
283 /**
284  * Close rrd file
285  * @param file
286  * @return
287  */
288 gint rspamd_rrd_close (struct rspamd_rrd_file *file);
289 
290 /*
291  * Conversion functions
292  */
293 
294 /**
295  * Convert rrd dst type from string to numeric value
296  */
297 enum rrd_dst_type rrd_dst_from_string (const gchar *str);
298 
299 /**
300  * Convert numeric presentation of dst to string
301  */
302 const gchar *rrd_dst_to_string (enum rrd_dst_type type);
303 
304 /**
305  * Convert rrd consolidation function type from string to numeric value
306  */
307 enum rrd_cf_type rrd_cf_from_string (const gchar *str);
308 
309 /**
310  * Convert numeric presentation of cf to string
311  */
312 const gchar *rrd_cf_to_string (enum rrd_cf_type type);
313 
314 /* Default RRA and DS */
315 
316 /**
317  * Create default RRA
318  */
319 void rrd_make_default_rra (const gchar *cf_name,
320 						   gulong pdp_cnt,
321 						   gulong rows,
322 						   struct rrd_rra_def *rra);
323 
324 /**
325  * Create default DS
326  */
327 void rrd_make_default_ds (const gchar *name,
328 						  const gchar *type,
329 						  gulong pdp_step,
330 						  struct rrd_ds_def *ds);
331 
332 /**
333  * Open or create the default rspamd rrd file
334  */
335 struct rspamd_rrd_file *rspamd_rrd_file_default (const gchar *path,
336 												 GError **err);
337 
338 /**
339  * Returned by querying rrd database
340  */
341 struct rspamd_rrd_query_result {
342 	gulong rra_rows;
343 	gulong pdp_per_cdp;
344 	gulong ds_count;
345 	gdouble last_update;
346 	gulong cur_row;
347 	const gdouble *data;
348 };
349 
350 /**
351  * Return RRA data
352  * @param file rrd file
353  * @param rra_num number of rra to return data for
354  * @return query result structure, that should be freed (using g_slice_free1) after usage
355  */
356 struct rspamd_rrd_query_result *rspamd_rrd_query (struct rspamd_rrd_file *file,
357 												  gulong rra_num);
358 
359 #ifdef  __cplusplus
360 }
361 #endif
362 
363 #endif /* RRD_H_ */
364