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