/* * Copyright 2024 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RRD_H_ #define RRD_H_ #include "config.h" /** * This file contains basic structure and functions to operate with round-robin databases */ #define RRD_COOKIE "RRD" #define RRD_VERSION "0003" #define RRD_FLOAT_COOKIE ((double) 8.642135E130) #ifdef __cplusplus extern "C" { #endif typedef union { unsigned long lv; double dv; } rrd_value_t; struct rrd_file_head { /* Data Base Identification Section ** */ char cookie[4]; /* RRD */ char version[5]; /* version of the format */ double float_cookie; /* is it the correct double representation ? */ /* Data Base Structure Definition **** */ gulong ds_cnt; /* how many different ds provide input to the rrd */ gulong rra_cnt; /* how many rras will be maintained in the rrd */ gulong pdp_step; /* pdp interval in seconds */ rrd_value_t par[10]; /* global parameters ... unused at the moment */ }; enum rrd_dst_type { RRD_DST_INVALID = -1, RRD_DST_COUNTER = 0, /* data source types available */ RRD_DST_ABSOLUTE, RRD_DST_GAUGE, RRD_DST_DERIVE, RRD_DST_CDEF }; enum rrd_ds_param { RRD_DS_mrhb_cnt = 0, /* minimum required heartbeat */ RRD_DS_min_val, /* the processed input of a ds must */ RRD_DS_max_val, /* be between max_val and min_val * both can be set to UNKNOWN if you * do not care. Data outside the limits * set to UNKNOWN */ RRD_DS_cdef = RRD_DS_mrhb_cnt }; /* pointer to encoded rpn expression only applies to DST_CDEF */ /* The magic number here is one less than DS_NAM_SIZE */ #define RRD_DS_NAM_SIZE 20 #define RRD_DST_SIZE 20 struct rrd_ds_def { char ds_nam[RRD_DS_NAM_SIZE]; /* Name of the data source (null terminated) */ char dst[RRD_DST_SIZE]; /* Type of data source (null terminated) */ rrd_value_t par[10]; /* index of this array see ds_param_en */ }; /* RRA definition */ enum rrd_cf_type { RRD_CF_INVALID = -1, RRD_CF_AVERAGE = 0, /* data consolidation functions */ RRD_CF_MINIMUM, RRD_CF_MAXIMUM, RRD_CF_LAST, }; #define MAX_RRA_PAR_EN 10 enum rrd_rra_param { RRA_cdp_xff_val = 0, /* what part of the consolidated * datapoint must be known, to produce a * valid entry in the rra */ }; #define RRD_CF_NAM_SIZE 20 struct rrd_rra_def { char cf_nam[RRD_CF_NAM_SIZE]; /* consolidation function (null term) */ gulong row_cnt; /* number of entries in the store */ gulong pdp_cnt; /* how many primary data points are * required for a consolidated data point?*/ rrd_value_t par[MAX_RRA_PAR_EN]; /* index see rra_param_en */ }; struct rrd_live_head { time_t last_up; /* when was rrd last updated */ glong last_up_usec; /* micro seconds part of the update timestamp. Always >= 0 */ }; #define RRD_LAST_DS_LEN 30 enum rrd_pdp_param { PDP_unkn_sec_cnt = 0, /* how many seconds of the current * pdp value is unknown data? */ PDP_val }; /* current value of the pdp. this depends on dst */ struct rrd_pdp_prep { char last_ds[RRD_LAST_DS_LEN]; /* the last reading from the data * source. this is stored in ASCII * to cater for very large counters * we might encounter in connection * with SNMP. */ rrd_value_t scratch[10]; /* contents according to pdp_par_en */ }; #define RRD_MAX_CDP_PAR_EN 10 #define RRD_MAX_CDP_FAILURES_IDX 8 /* max CDP scratch entries avail to record violations for a FAILURES RRA */ #define RRD_MAX_FAILURES_WINDOW_LEN 28 enum rrd_cdp_param { CDP_val = 0, /* the base_interval is always an * average */ CDP_unkn_pdp_cnt, /* how many unknown pdp were * integrated. This and the cdp_xff * will decide if this is going to * be a UNKNOWN or a valid value */ CDP_hw_intercept, /* Current intercept coefficient for the Holt-Winters * prediction algorithm. */ CDP_hw_last_intercept, /* Last iteration intercept coefficient for the Holt-Winters * prediction algorithm. */ CDP_hw_slope, /* Current slope coefficient for the Holt-Winters * prediction algorithm. */ CDP_hw_last_slope, /* Last iteration slope coefficient. */ CDP_null_count, /* Number of sequential Unknown (DNAN) values + 1 preceding * the current prediction. * */ CDP_last_null_count, /* Last iteration count of Unknown (DNAN) values. */ CDP_primary_val = 8, /* optimization for bulk updates: the value of the first CDP * value to be written in the bulk update. */ CDP_secondary_val = 9, /* optimization for bulk updates: the value of subsequent * CDP values to be written in the bulk update. */ CDP_hw_seasonal = CDP_hw_intercept, /* Current seasonal coefficient for the Holt-Winters * prediction algorithm. This is stored in CDP prep to avoid * redundant seek operations. */ CDP_hw_last_seasonal = CDP_hw_last_intercept, /* Last iteration seasonal coefficient. */ CDP_seasonal_deviation = CDP_hw_intercept, CDP_last_seasonal_deviation = CDP_hw_last_intercept, CDP_init_seasonal = CDP_null_count }; struct rrd_cdp_prep { rrd_value_t scratch[RRD_MAX_CDP_PAR_EN]; /* contents according to cdp_par_en * * init state should be NAN */ }; struct rrd_rra_ptr { gulong cur_row; /* current row in the rra */ }; /* Final rrd file structure */ struct rspamd_rrd_file { struct rrd_file_head *stat_head; /* the static header */ struct rrd_ds_def *ds_def; /* list of data source definitions */ struct rrd_rra_def *rra_def; /* list of round robin archive def */ struct rrd_live_head *live_head; /* rrd v >= 3 last_up with us */ struct rrd_pdp_prep *pdp_prep; /* pdp data prep area */ struct rrd_cdp_prep *cdp_prep; /* cdp prep area */ struct rrd_rra_ptr *rra_ptr; /* list of rra pointers */ double *rrd_value; /* list of rrd values */ char *filename; uint8_t *map; /* mmapped area */ gsize size; /* its size */ gboolean finalized; char *id; int fd; }; /* Public API */ /** * Open (and mmap) existing RRD file * @param filename path * @param err error pointer * @return rrd file structure */ struct rspamd_rrd_file *rspamd_rrd_open(const char *filename, GError **err); /** * Create basic header for rrd file * @param filename file path * @param ds_count number of data sources * @param rra_count number of round robin archives * @param pdp_step step of primary data points * @param err error pointer * @return TRUE if file has been created */ struct rspamd_rrd_file *rspamd_rrd_create(const char *filename, gulong ds_count, gulong rra_count, gulong pdp_step, double initial_ticks, GError **err); /** * Add data sources to rrd file * @param filename path to file * @param ds array of struct rrd_ds_def * @param err error pointer * @return TRUE if data sources were added */ gboolean rspamd_rrd_add_ds(struct rspamd_rrd_file *file, GArray *ds, GError **err); /** * Add round robin archives to rrd file * @param filename path to file * @param ds array of struct rrd_rra_def * @param err error pointer * @return TRUE if archives were added */ gboolean rspamd_rrd_add_rra(struct rspamd_rrd_file *file, GArray *rra, GError **err); /** * Finalize rrd file header and initialize all RRA in the file * @param filename file path * @param err error pointer * @return TRUE if rrd file is ready for use */ gboolean rspamd_rrd_finalize(struct rspamd_rrd_file *file, GError **err); /** * Add record to rrd file * @param file rrd file object * @param points points (must be row suitable for this RRA, depending on ds count) * @param err error pointer * @return TRUE if a row has been added */ gboolean rspamd_rrd_add_record(struct rspamd_rrd_file *file, GArray *points, double ticks, GError **err); /** * Close rrd file * @param file * @return */ int rspamd_rrd_close(struct rspamd_rrd_file *file); /* * Conversion functions */ /** * Convert rrd dst type from string to numeric value */ enum rrd_dst_type rrd_dst_from_string(const char *str); /** * Convert numeric presentation of dst to string */ const char *rrd_dst_to_string(enum rrd_dst_type type); /** * Convert rrd consolidation function type from string to numeric value */ enum rrd_cf_type rrd_cf_from_string(const char *str); /** * Convert numeric presentation of cf to string */ const char *rrd_cf_to_string(enum rrd_cf_type type); /* Default RRA and DS */ /** * Create default RRA */ void rrd_make_default_rra(const char *cf_name, gulong pdp_cnt, gulong rows, struct rrd_rra_def *rra); /** * Create default DS */ void rrd_make_default_ds(const char *name, const char *type, gulong pdp_step, struct rrd_ds_def *ds); /** * Open or create the default rspamd rrd file */ struct rspamd_rrd_file *rspamd_rrd_file_default(const char *path, GError **err); /** * Returned by querying rrd database */ struct rspamd_rrd_query_result { gulong rra_rows; gulong pdp_per_cdp; gulong ds_count; double last_update; gulong cur_row; const double *data; }; /** * Return RRA data * @param file rrd file * @param rra_num number of rra to return data for * @return query result structure, that should be freed (using g_slice_free1) after usage */ struct rspamd_rrd_query_result *rspamd_rrd_query(struct rspamd_rrd_file *file, gulong rra_num); #ifdef __cplusplus } #endif #endif /* RRD_H_ */