1 /*
2  * Copyright (c) 2002-2013 Balabit
3  * Copyright (c) 1998-2013 Balázs Scheidler
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * As an additional exemption you are allowed to compile & link against the
20  * OpenSSL libraries as published by the OpenSSL project. See the file
21  * COPYING for details.
22  *
23  */
24 #include "stats/stats-registry.h"
25 #include "stats/stats-query.h"
26 #include "cfg.h"
27 #include <string.h>
28 
29 typedef struct _StatsClusterContainer
30 {
31   GHashTable *static_clusters;
32   GHashTable *dynamic_clusters;
33 } StatsClusterContainer;
34 
35 static StatsClusterContainer stats_cluster_container;
36 
37 static guint
_number_of_dynamic_clusters(void)38 _number_of_dynamic_clusters(void)
39 {
40   return g_hash_table_size(stats_cluster_container.dynamic_clusters);
41 }
42 
43 static GMutex stats_mutex;
44 gboolean stats_locked;
45 
46 static void
_insert_cluster(StatsCluster * sc)47 _insert_cluster(StatsCluster *sc)
48 {
49   if (sc->dynamic)
50     g_hash_table_insert(stats_cluster_container.dynamic_clusters, &sc->key, sc);
51   else
52     g_hash_table_insert(stats_cluster_container.static_clusters, &sc->key, sc);
53 }
54 
55 void
stats_lock(void)56 stats_lock(void)
57 {
58   g_mutex_lock(&stats_mutex);
59   stats_locked = TRUE;
60 }
61 
62 void
stats_unlock(void)63 stats_unlock(void)
64 {
65   stats_locked = FALSE;
66   g_mutex_unlock(&stats_mutex);
67 }
68 
69 static StatsCluster *
_grab_dynamic_cluster(const StatsClusterKey * sc_key)70 _grab_dynamic_cluster(const StatsClusterKey *sc_key)
71 {
72   StatsCluster *sc;
73 
74   sc = g_hash_table_lookup(stats_cluster_container.dynamic_clusters, sc_key);
75   if (!sc)
76     {
77       if (!stats_check_dynamic_clusters_limit(_number_of_dynamic_clusters()))
78         return NULL;
79       sc = stats_cluster_dynamic_new(sc_key);
80       _insert_cluster(sc);
81       if ( !stats_check_dynamic_clusters_limit(_number_of_dynamic_clusters()))
82         {
83           msg_warning("Number of dynamic cluster limit has been reached.",
84                       evt_tag_int("allowed_clusters", stats_number_of_dynamic_clusters_limit()));
85         }
86     }
87 
88   return sc;
89 
90 }
91 
92 static StatsCluster *
_grab_static_cluster(const StatsClusterKey * sc_key)93 _grab_static_cluster(const StatsClusterKey *sc_key)
94 {
95   StatsCluster *sc;
96 
97   sc = g_hash_table_lookup(stats_cluster_container.static_clusters, sc_key);
98   if (!sc)
99     {
100       sc = stats_cluster_new(sc_key);
101       _insert_cluster(sc);
102     }
103 
104   return sc;
105 }
106 
107 static StatsCluster *
_grab_cluster(gint stats_level,const StatsClusterKey * sc_key,gboolean dynamic)108 _grab_cluster(gint stats_level, const StatsClusterKey *sc_key, gboolean dynamic)
109 {
110   if (!stats_check_level(stats_level))
111     return NULL;
112 
113   StatsCluster *sc = NULL;
114 
115   if (dynamic)
116     sc = _grab_dynamic_cluster(sc_key);
117   else
118     sc = _grab_static_cluster(sc_key);
119 
120   if (!sc)
121     return NULL;
122 
123   /* check that we are not overwriting a dynamic counter with a
124    * non-dynamic one or vica versa.  This could only happen if the same
125    * key is used for both a dynamic counter and a non-dynamic one, which
126    * is a programming error */
127 
128   g_assert(sc->dynamic == dynamic);
129   return sc;
130 }
131 
132 static StatsCluster *
_register_counter(gint stats_level,const StatsClusterKey * sc_key,gint type,gboolean dynamic,StatsCounterItem ** counter)133 _register_counter(gint stats_level, const StatsClusterKey *sc_key, gint type,
134                   gboolean dynamic, StatsCounterItem **counter)
135 {
136   StatsCluster *sc;
137 
138   g_assert(stats_locked);
139 
140   sc = _grab_cluster(stats_level, sc_key, dynamic);
141   if (sc)
142     {
143       StatsCounterItem *ctr = stats_cluster_get_counter(sc, type);
144       *counter = stats_cluster_track_counter(sc, type);
145       if (ctr && ctr->external)
146         return sc;
147       (*counter)->type = type;
148       (*counter)->external = FALSE;
149     }
150   else
151     {
152       *counter = NULL;
153     }
154   return sc;
155 }
156 
157 static void
_assert_when_internal_or_stores_different_ref(StatsCluster * sc,gint type,atomic_gssize * external_counter)158 _assert_when_internal_or_stores_different_ref(StatsCluster *sc, gint type, atomic_gssize *external_counter)
159 {
160   StatsCounterItem *counter = stats_cluster_get_counter(sc, type);
161   if (counter)
162     {
163       g_assert(counter->external && counter->value_ref == external_counter);
164     }
165 }
166 
167 static StatsCluster *
_register_external_counter(gint stats_level,const StatsClusterKey * sc_key,gint type,gboolean dynamic,atomic_gssize * external_counter)168 _register_external_counter(gint stats_level, const StatsClusterKey *sc_key, gint type,
169                            gboolean dynamic, atomic_gssize *external_counter)
170 {
171   StatsCluster *sc;
172 
173   g_assert(stats_locked);
174 
175   sc = _grab_cluster(stats_level, sc_key, dynamic);
176   if (sc)
177     {
178       _assert_when_internal_or_stores_different_ref(sc, type, external_counter);
179       StatsCounterItem *ctr = stats_cluster_track_counter(sc, type);
180       ctr->external = TRUE;
181       ctr->value_ref = external_counter;
182       ctr->type = type;
183     }
184 
185   return sc;
186 }
187 
188 
189 
190 /**
191  * stats_register_counter:
192  * @stats_level: the required statistics level to make this counter available
193  * @component: a reference to the syslog-ng component that this counter belongs to (SCS_*)
194  * @id: the unique identifier of the configuration item that this counter belongs to
195  * @instance: if a given configuration item manages multiple similar counters
196  *            this makes those unique (like destination filename in case macros are used)
197  * @type: the counter type (processed, dropped, etc)
198  * @counter: returned pointer to the counter
199  *
200  * This function registers a general purpose counter. Whenever multiple
201  * objects touch the same counter all of these should register the counter
202  * with the same name. Internally the stats subsystem counts the number of
203  * users of the same counter in this case, thus the counter will only be
204  * freed when all of these uses are unregistered.
205  **/
206 StatsCluster *
stats_register_counter(gint stats_level,const StatsClusterKey * sc_key,gint type,StatsCounterItem ** counter)207 stats_register_counter(gint stats_level, const StatsClusterKey *sc_key, gint type,
208                        StatsCounterItem **counter)
209 {
210   return _register_counter(stats_level, sc_key, type, FALSE, counter);
211 }
212 
213 StatsCluster *
stats_register_external_counter(gint stats_level,const StatsClusterKey * sc_key,gint type,atomic_gssize * external_counter)214 stats_register_external_counter(gint stats_level, const StatsClusterKey *sc_key, gint type,
215                                 atomic_gssize *external_counter)
216 {
217   return _register_external_counter(stats_level, sc_key, type, FALSE, external_counter);
218 }
219 
220 StatsCluster *
stats_register_alias_counter(gint level,const StatsClusterKey * sc_key,gint type,StatsCounterItem * aliased_counter)221 stats_register_alias_counter(gint level, const StatsClusterKey *sc_key, gint type, StatsCounterItem *aliased_counter)
222 {
223   return stats_register_external_counter(level, sc_key, type, &aliased_counter->value);
224 }
225 
226 StatsCluster *
stats_register_counter_and_index(gint stats_level,const StatsClusterKey * sc_key,gint type,StatsCounterItem ** counter)227 stats_register_counter_and_index(gint stats_level, const StatsClusterKey *sc_key, gint type,
228                                  StatsCounterItem **counter)
229 {
230   StatsCluster *cluster =  _register_counter(stats_level, sc_key, type, FALSE, counter);
231   if (cluster)
232     stats_query_index_counter(cluster, type);
233 
234   return cluster;
235 }
236 
237 StatsCluster *
stats_register_dynamic_counter(gint stats_level,const StatsClusterKey * sc_key,gint type,StatsCounterItem ** counter)238 stats_register_dynamic_counter(gint stats_level, const StatsClusterKey *sc_key,
239                                gint type, StatsCounterItem **counter)
240 {
241   return _register_counter(stats_level, sc_key, type, TRUE, counter);
242 }
243 
244 /*
245  * stats_instant_inc_dynamic_counter
246  * @timestamp: if non-negative, an associated timestamp will be created and set
247  *
248  * Instantly create (if not exists) and increment a dynamic counter.
249  */
250 void
stats_register_and_increment_dynamic_counter(gint stats_level,const StatsClusterKey * sc_key,time_t timestamp)251 stats_register_and_increment_dynamic_counter(gint stats_level, const StatsClusterKey *sc_key,
252                                              time_t timestamp)
253 {
254   StatsCounterItem *counter, *stamp;
255   StatsCluster *handle;
256 
257   g_assert(stats_locked);
258   handle = stats_register_dynamic_counter(stats_level, sc_key, SC_TYPE_PROCESSED, &counter);
259   if (!handle)
260     return;
261   stats_counter_inc(counter);
262   if (timestamp >= 0)
263     {
264       stats_register_associated_counter(handle, SC_TYPE_STAMP, &stamp);
265       stats_counter_set(stamp, timestamp);
266       stats_unregister_dynamic_counter(handle, SC_TYPE_STAMP, &stamp);
267     }
268   stats_unregister_dynamic_counter(handle, SC_TYPE_PROCESSED, &counter);
269 }
270 
271 /**
272  * stats_register_associated_counter:
273  * @sc: the dynamic counter that was registered with stats_register_dynamic_counter
274  * @type: the type that we want to use in the same StatsCluster instance
275  * @counter: the returned pointer to the counter itself
276  *
277  * This function registers another counter type in the same StatsCounter
278  * instance in order to avoid an unnecessary lookup.
279  **/
280 void
stats_register_associated_counter(StatsCluster * sc,gint type,StatsCounterItem ** counter)281 stats_register_associated_counter(StatsCluster *sc, gint type, StatsCounterItem **counter)
282 {
283   g_assert(stats_locked);
284 
285   *counter = NULL;
286   if (!sc)
287     return;
288   g_assert(sc->dynamic);
289 
290   *counter = stats_cluster_track_counter(sc, type);
291 }
292 
293 void
stats_unregister_counter(const StatsClusterKey * sc_key,gint type,StatsCounterItem ** counter)294 stats_unregister_counter(const StatsClusterKey *sc_key, gint type,
295                          StatsCounterItem **counter)
296 {
297   StatsCluster *sc;
298 
299   g_assert(stats_locked);
300 
301   if (*counter == NULL)
302     return;
303 
304   sc = g_hash_table_lookup(stats_cluster_container.static_clusters, sc_key);
305 
306   stats_cluster_untrack_counter(sc, type, counter);
307 }
308 
309 void
stats_unregister_external_counter(const StatsClusterKey * sc_key,gint type,atomic_gssize * external_counter)310 stats_unregister_external_counter(const StatsClusterKey *sc_key, gint type,
311                                   atomic_gssize *external_counter)
312 {
313   StatsCluster *sc;
314 
315   g_assert(stats_locked);
316 
317   sc = g_hash_table_lookup(stats_cluster_container.static_clusters, sc_key);
318   StatsCounterItem *ctr = stats_cluster_get_counter(sc, type);
319   g_assert(ctr->value_ref == external_counter);
320 
321   stats_cluster_untrack_counter(sc, type, &ctr);
322 }
323 
324 void
stats_unregister_alias_counter(const StatsClusterKey * sc_key,gint type,StatsCounterItem * aliased_counter)325 stats_unregister_alias_counter(const StatsClusterKey *sc_key, gint type, StatsCounterItem *aliased_counter)
326 {
327   stats_unregister_external_counter(sc_key, type, &aliased_counter->value);
328 }
329 
330 void
stats_unregister_dynamic_counter(StatsCluster * sc,gint type,StatsCounterItem ** counter)331 stats_unregister_dynamic_counter(StatsCluster *sc, gint type, StatsCounterItem **counter)
332 {
333   g_assert(stats_locked);
334   if (!sc)
335     return;
336   stats_cluster_untrack_counter(sc, type, counter);
337 }
338 
339 StatsCluster *
stats_get_cluster(const StatsClusterKey * sc_key)340 stats_get_cluster(const StatsClusterKey *sc_key)
341 {
342   g_assert(stats_locked);
343 
344   StatsCluster *sc = g_hash_table_lookup(stats_cluster_container.static_clusters, sc_key);
345 
346   if (!sc)
347     sc = g_hash_table_lookup(stats_cluster_container.dynamic_clusters, sc_key);
348 
349   return sc;
350 }
351 
352 gboolean
stats_contains_counter(const StatsClusterKey * sc_key,gint type)353 stats_contains_counter(const StatsClusterKey *sc_key, gint type)
354 {
355   g_assert(stats_locked);
356 
357   StatsCluster *sc = stats_get_cluster(sc_key);
358   if (!sc)
359     {
360       return FALSE;
361     }
362 
363   return stats_cluster_is_alive(sc, type);
364 }
365 
366 StatsCounterItem *
stats_get_counter(const StatsClusterKey * sc_key,gint type)367 stats_get_counter(const StatsClusterKey *sc_key, gint type)
368 {
369   g_assert(stats_locked);
370   StatsCluster *sc = stats_get_cluster(sc_key);
371 
372   if (!sc)
373     return NULL;
374 
375   return stats_cluster_get_counter(sc, type);
376 }
377 
378 static void
_foreach_cluster_helper(gpointer key,gpointer value,gpointer user_data)379 _foreach_cluster_helper(gpointer key, gpointer value, gpointer user_data)
380 {
381   gpointer *args = (gpointer *) user_data;
382   StatsForeachClusterFunc func = args[0];
383   gpointer func_data = args[1];
384   StatsCluster *sc = (StatsCluster *) value;
385 
386   func(sc, func_data);
387 }
388 
389 void
stats_foreach_cluster(StatsForeachClusterFunc func,gpointer user_data)390 stats_foreach_cluster(StatsForeachClusterFunc func, gpointer user_data)
391 {
392   gpointer args[] = { func, user_data };
393 
394   g_assert(stats_locked);
395   g_hash_table_foreach(stats_cluster_container.static_clusters, _foreach_cluster_helper, args);
396   g_hash_table_foreach(stats_cluster_container.dynamic_clusters, _foreach_cluster_helper, args);
397 }
398 
399 static gboolean
_foreach_cluster_remove_helper(gpointer key,gpointer value,gpointer user_data)400 _foreach_cluster_remove_helper(gpointer key, gpointer value, gpointer user_data)
401 {
402   gpointer *args = (gpointer *) user_data;
403   StatsForeachClusterRemoveFunc func = args[0];
404   gpointer func_data = args[1];
405   StatsCluster *sc = (StatsCluster *) value;
406 
407   gboolean should_be_removed = func(sc, func_data);
408 
409   if (should_be_removed)
410     stats_query_deindex_cluster(sc);
411 
412   return should_be_removed;
413 }
414 
415 void
stats_foreach_cluster_remove(StatsForeachClusterRemoveFunc func,gpointer user_data)416 stats_foreach_cluster_remove(StatsForeachClusterRemoveFunc func, gpointer user_data)
417 {
418   gpointer args[] = { func, user_data };
419   g_hash_table_foreach_remove(stats_cluster_container.static_clusters, _foreach_cluster_remove_helper, args);
420   g_hash_table_foreach_remove(stats_cluster_container.dynamic_clusters, _foreach_cluster_remove_helper, args);
421 }
422 
423 static void
_foreach_counter_helper(StatsCluster * sc,gpointer user_data)424 _foreach_counter_helper(StatsCluster *sc, gpointer user_data)
425 {
426   gpointer *args = (gpointer *) user_data;
427   StatsForeachCounterFunc func = args[0];
428   gpointer func_data = args[1];
429 
430   stats_cluster_foreach_counter(sc, func, func_data);
431 }
432 
433 void
stats_foreach_counter(StatsForeachCounterFunc func,gpointer user_data)434 stats_foreach_counter(StatsForeachCounterFunc func, gpointer user_data)
435 {
436   gpointer args[] = { func, user_data };
437 
438   g_assert(stats_locked);
439   stats_foreach_cluster(_foreach_counter_helper, args);
440 }
441 
442 void
stats_registry_init(void)443 stats_registry_init(void)
444 {
445   stats_cluster_container.static_clusters = g_hash_table_new_full((GHashFunc) stats_cluster_hash,
446                                             (GEqualFunc) stats_cluster_equal, NULL,
447                                             (GDestroyNotify) stats_cluster_free);
448   stats_cluster_container.dynamic_clusters = g_hash_table_new_full((GHashFunc) stats_cluster_hash,
449                                              (GEqualFunc) stats_cluster_equal, NULL,
450                                              (GDestroyNotify) stats_cluster_free);
451 
452   g_mutex_init(&stats_mutex);
453 }
454 
455 void
stats_registry_deinit(void)456 stats_registry_deinit(void)
457 {
458   g_hash_table_destroy(stats_cluster_container.static_clusters);
459   g_hash_table_destroy(stats_cluster_container.dynamic_clusters);
460   stats_cluster_container.static_clusters = NULL;
461   stats_cluster_container.dynamic_clusters = NULL;
462   g_mutex_clear(&stats_mutex);
463 }
464