1 /*
2 * Copyright (c) 2021 One Identity
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * As an additional exemption you are allowed to compile & link against the
19 * OpenSSL libraries as published by the OpenSSL project. See the file
20 * COPYING for details.
21 *
22 */
23
24 #include "stats/aggregator/stats-aggregator-registry.h"
25 #include "stats/stats-registry.h"
26 #include "stats/stats-query.h"
27 #include "cfg.h"
28 #include "iv.h"
29 #include "timeutils/cache.h"
30 #include "mainloop.h"
31 #include "messages.h"
32
33 #define FREQUENCY_OF_UPDATE 60
34
35 typedef struct
36 {
37 GHashTable *aggregators;
38 struct iv_timer update_timer;
39 } StatsAggregatorContainer;
40
41 static StatsAggregatorContainer stats_container;
42
43 static GMutex stats_aggregator_mutex;
44 static gboolean stats_aggregator_locked;
45
46 static void
_update_func(gpointer _key,gpointer _value,gpointer _user_data)47 _update_func (gpointer _key, gpointer _value, gpointer _user_data)
48 {
49 StatsAggregator *self = (StatsAggregator *) _value;
50
51 if (!stats_aggregator_is_orphaned(self))
52 stats_aggregator_aggregate(self);
53 }
54
55 static void
_start_timer(void)56 _start_timer(void)
57 {
58 main_loop_assert_main_thread();
59 iv_validate_now();
60 stats_container.update_timer.expires = iv_now;
61 stats_container.update_timer.expires.tv_sec += FREQUENCY_OF_UPDATE;
62
63 iv_timer_register(&stats_container.update_timer);
64 }
65
66 static void
_update(void * cookie)67 _update(void *cookie)
68 {
69 msg_trace("stats-aggregator-registry update");
70 g_hash_table_foreach(stats_container.aggregators, _update_func, NULL);
71
72 if(g_hash_table_size(stats_container.aggregators) > 0
73 && !iv_timer_registered(&stats_container.update_timer))
74 _start_timer();
75 }
76
77 static void
_init_timer(void)78 _init_timer(void)
79 {
80 IV_TIMER_INIT(&stats_container.update_timer);
81 stats_container.update_timer.cookie = NULL;
82 stats_container.update_timer.handler = _update;
83 }
84
85 static void
_stop_timer(void)86 _stop_timer(void)
87 {
88 main_loop_assert_main_thread();
89 if (iv_timer_registered(&stats_container.update_timer))
90 iv_timer_unregister(&stats_container.update_timer);
91 }
92
93 static void
_deinit_timer(void)94 _deinit_timer(void)
95 {
96 _stop_timer();
97 }
98
99 void
stats_aggregator_lock(void)100 stats_aggregator_lock(void)
101 {
102 g_mutex_lock(&stats_aggregator_mutex);
103 stats_aggregator_locked = TRUE;
104 }
105
106 void
stats_aggregator_unlock(void)107 stats_aggregator_unlock(void)
108 {
109 stats_aggregator_locked = FALSE;
110 g_mutex_unlock(&stats_aggregator_mutex);
111 }
112
113 static void
_free_aggregator(StatsAggregator * self)114 _free_aggregator(StatsAggregator *self)
115 {
116 stats_aggregator_free(self);
117 }
118
119 static gboolean
_remove_orphaned_helper(gpointer _key,gpointer _value,gpointer _user_data)120 _remove_orphaned_helper(gpointer _key, gpointer _value, gpointer _user_data)
121 {
122 StatsAggregator *self = (StatsAggregator *) _value;
123 if (stats_aggregator_is_orphaned(self))
124 {
125 _free_aggregator(self);
126 return TRUE;
127 }
128 return FALSE;
129 }
130
131 void
stats_aggregator_remove_orphaned_stats(void)132 stats_aggregator_remove_orphaned_stats(void)
133 {
134 g_assert(stats_aggregator_locked);
135
136 g_hash_table_foreach_remove(stats_container.aggregators, _remove_orphaned_helper, NULL);
137 }
138
139 static gboolean
_remove_helper(gpointer _key,gpointer _value,gpointer _user_data)140 _remove_helper(gpointer _key, gpointer _value, gpointer _user_data)
141 {
142 StatsAggregator *self = (StatsAggregator *) _value;
143 if (!stats_aggregator_is_orphaned(self))
144 stats_aggregator_unregister(self);
145
146 _free_aggregator(self);
147 return TRUE;
148 }
149
150 static void
stats_aggregator_remove_stats(void)151 stats_aggregator_remove_stats(void)
152 {
153 g_assert(stats_aggregator_locked);
154
155 g_hash_table_foreach_remove(stats_container.aggregators, _remove_helper, NULL);
156 }
157
158 static guint
_stats_cluster_key_hash(const StatsClusterKey * self)159 _stats_cluster_key_hash(const StatsClusterKey *self)
160 {
161 return g_str_hash(self->id) + g_str_hash(self->instance) + self->component;
162 }
163
164 void
stats_aggregator_registry_init(void)165 stats_aggregator_registry_init(void)
166 {
167 stats_container.aggregators = g_hash_table_new_full((GHashFunc) _stats_cluster_key_hash,
168 (GEqualFunc) stats_cluster_key_equal, NULL, NULL);
169 _init_timer();
170 g_mutex_init(&stats_aggregator_mutex);
171 }
172
173 void
stats_aggregator_registry_deinit(void)174 stats_aggregator_registry_deinit(void)
175 {
176 stats_aggregator_lock();
177 stats_aggregator_remove_stats();
178 stats_aggregator_unlock();
179 g_hash_table_destroy(stats_container.aggregators);
180 stats_container.aggregators = NULL;
181 g_mutex_clear(&stats_aggregator_mutex);
182 _deinit_timer();
183 }
184
185 static void
_insert_to_table(StatsAggregator * value)186 _insert_to_table(StatsAggregator *value)
187 {
188 g_hash_table_insert(stats_container.aggregators, &value->key, value);
189
190 if (!iv_timer_registered(&stats_container.update_timer))
191 _start_timer();
192 }
193
194 static gboolean
_is_in_table(StatsClusterKey * sc_key)195 _is_in_table(StatsClusterKey *sc_key)
196 {
197 return g_hash_table_lookup(stats_container.aggregators, sc_key) != NULL;
198 }
199
200 static StatsAggregator *
_get_from_table(StatsClusterKey * sc_key)201 _get_from_table(StatsClusterKey *sc_key)
202 {
203 return g_hash_table_lookup(stats_container.aggregators, sc_key);
204 }
205
206 void
stats_register_aggregator_maximum(gint level,StatsClusterKey * sc_key,StatsAggregator ** s)207 stats_register_aggregator_maximum(gint level, StatsClusterKey *sc_key, StatsAggregator **s)
208 {
209 g_assert(stats_aggregator_locked);
210
211 if (!stats_check_level(level))
212 {
213 *s = NULL;
214 return;
215 }
216
217 if (!_is_in_table(sc_key))
218 {
219 *s = stats_aggregator_maximum_new(level, sc_key);
220 _insert_to_table(*s);
221 }
222 else
223 {
224 *s = _get_from_table(sc_key);
225 }
226
227 stats_aggregator_track_counter(*s);
228 }
229
230 void
stats_unregister_aggregator_maximum(StatsAggregator ** s)231 stats_unregister_aggregator_maximum(StatsAggregator **s)
232 {
233 g_assert(stats_aggregator_locked);
234 stats_aggregator_untrack_counter(*s);
235 *s = NULL;
236 }
237
238 void
stats_register_aggregator_average(gint level,StatsClusterKey * sc_key,StatsAggregator ** s)239 stats_register_aggregator_average(gint level, StatsClusterKey *sc_key, StatsAggregator **s)
240 {
241 g_assert(stats_aggregator_locked);
242
243 if (!stats_check_level(level))
244 {
245 *s = NULL;
246 return;
247 }
248
249 if (!_is_in_table(sc_key))
250 {
251 *s = stats_aggregator_average_new(level, sc_key);
252 _insert_to_table(*s);
253 }
254 else
255 {
256 *s = _get_from_table(sc_key);
257 }
258
259 stats_aggregator_track_counter(*s);
260 }
261
262 void
stats_unregister_aggregator_average(StatsAggregator ** s)263 stats_unregister_aggregator_average(StatsAggregator **s)
264 {
265 g_assert(stats_aggregator_locked);
266 stats_aggregator_untrack_counter(*s);
267 *s = NULL;
268 }
269
270 void
stats_register_aggregator_cps(gint level,StatsClusterKey * sc_key,StatsClusterKey * sc_key_input,gint stats_type,StatsAggregator ** s)271 stats_register_aggregator_cps(gint level, StatsClusterKey *sc_key, StatsClusterKey *sc_key_input, gint stats_type,
272 StatsAggregator **s)
273 {
274 g_assert(stats_aggregator_locked);
275
276 if (!stats_check_level(level))
277 {
278 *s = NULL;
279 return;
280 }
281
282 if (!_is_in_table(sc_key))
283 {
284 *s = stats_aggregator_cps_new(level, sc_key, sc_key_input, stats_type);
285 _insert_to_table(*s);
286 }
287 else
288 {
289 *s = _get_from_table(sc_key);
290 }
291
292 stats_aggregator_track_counter(*s);
293 }
294
295 void
stats_unregister_aggregator_cps(StatsAggregator ** s)296 stats_unregister_aggregator_cps(StatsAggregator **s)
297 {
298 g_assert(stats_aggregator_locked);
299 stats_aggregator_untrack_counter(*s);
300 *s = NULL;
301 }
302
303 static void
_reset_func(gpointer _key,gpointer _value,gpointer _user_data)304 _reset_func (gpointer _key, gpointer _value, gpointer _user_data)
305 {
306 StatsAggregator *self = (StatsAggregator *) _value;
307 stats_aggregator_reset(self);
308 }
309
310 void
stats_aggregator_registry_reset(void)311 stats_aggregator_registry_reset(void)
312 {
313 g_assert(stats_aggregator_locked);
314
315 g_hash_table_foreach(stats_container.aggregators, _reset_func, NULL);
316 }
317