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