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-cluster.h"
25 #include "mainloop.h"
26 
27 #include <assert.h>
28 #include <string.h>
29 
30 GPtrArray *stats_types;
31 
32 void
stats_cluster_init(void)33 stats_cluster_init(void)
34 {
35   g_assert(!stats_types);
36   stats_types = g_ptr_array_new_with_free_func(g_free);
37 
38   g_assert(stats_register_type("none") == 0);
39   g_assert(stats_register_type("group") == SCS_GROUP);
40   g_assert(stats_register_type("global") == SCS_GLOBAL);
41   g_assert(stats_register_type("center") == SCS_CENTER);
42   g_assert(stats_register_type("host") == SCS_HOST);
43   g_assert(stats_register_type("sender") == SCS_SENDER);
44   g_assert(stats_register_type("program") == SCS_PROGRAM);
45   g_assert(stats_register_type("severity") == SCS_SEVERITY);
46   g_assert(stats_register_type("facility") == SCS_FACILITY);
47   g_assert(stats_register_type("tag") == SCS_TAG);
48   g_assert(stats_register_type("filter") == SCS_FILTER);
49   g_assert(stats_register_type("parser") == SCS_PARSER);
50 }
51 
52 gboolean
_types_equal(const gchar * a,const gchar * b)53 _types_equal(const gchar *a, const gchar *b)
54 {
55   return !strcmp(a, b);
56 };
57 
58 guint
stats_register_type(const gchar * type_name)59 stats_register_type(const gchar *type_name)
60 {
61   main_loop_assert_main_thread();
62   guint index_ = 0;
63   gboolean result = g_ptr_array_find_with_equal_func(stats_types, type_name, (GEqualFunc)_types_equal, &index_);
64   if (result)
65     return index_;
66 
67   g_ptr_array_add(stats_types, g_strdup(type_name));
68 
69   guint registered_number = stats_types->len - 1;
70   g_assert(registered_number <= SCS_SOURCE_MASK);
71   return registered_number;
72 };
73 
74 void
stats_cluster_deinit(void)75 stats_cluster_deinit(void)
76 {
77   g_ptr_array_free(stats_types, TRUE);
78   stats_types = NULL;
79 }
80 
81 StatsClusterKey *
stats_cluster_key_clone(StatsClusterKey * dst,const StatsClusterKey * src)82 stats_cluster_key_clone(StatsClusterKey *dst, const StatsClusterKey *src)
83 {
84   dst->component = src->component;
85   dst->id = g_strdup(src->id ? : "");
86   dst->instance = g_strdup(src->instance ? : "");
87 
88   if (src->counter_group_init.clone)
89     src->counter_group_init.clone(&dst->counter_group_init, &src->counter_group_init);
90   else
91     dst->counter_group_init = src->counter_group_init;
92 
93   return dst;
94 }
95 
96 void
stats_cluster_key_set(StatsClusterKey * self,guint16 component,const gchar * id,const gchar * instance,StatsCounterGroupInit counter_group_init)97 stats_cluster_key_set(StatsClusterKey *self, guint16 component, const gchar *id, const gchar *instance,
98                       StatsCounterGroupInit counter_group_init)
99 {
100   self->component = component;
101   self->id = (id?id:"");
102   self->instance = (instance?instance:"");
103   self->counter_group_init = counter_group_init;
104 }
105 
106 void
stats_cluster_key_cloned_free(StatsClusterKey * self)107 stats_cluster_key_cloned_free(StatsClusterKey *self)
108 {
109   g_free((gchar *)(self->id));
110   g_free((gchar *)(self->instance));
111 
112   if (self->counter_group_init.cloned_free)
113     self->counter_group_init.cloned_free(&self->counter_group_init);
114 }
115 
116 void
stats_cluster_foreach_counter(StatsCluster * self,StatsForeachCounterFunc func,gpointer user_data)117 stats_cluster_foreach_counter(StatsCluster *self, StatsForeachCounterFunc func, gpointer user_data)
118 {
119   gint type;
120 
121   for (type = 0; type < self->counter_group.capacity; type++)
122     {
123       if (self->live_mask & (1 << type))
124         {
125           func(self, type, &self->counter_group.counters[type], user_data);
126         }
127     }
128 }
129 
130 const gchar *
stats_cluster_get_type_name(StatsCluster * self,gint type)131 stats_cluster_get_type_name(StatsCluster *self, gint type)
132 {
133   if (type < self->counter_group.capacity)
134     return self->counter_group.counter_names[type];
135 
136   return "";
137 }
138 
139 static const gchar *
_get_module_name(gint source)140 _get_module_name(gint source)
141 {
142   gint type = source & SCS_SOURCE_MASK;
143   g_assert(type < stats_types->len);
144   return g_ptr_array_index(stats_types, type);
145 }
146 
147 
148 static const gchar *
_get_component_prefix(gint source)149 _get_component_prefix(gint source)
150 {
151   return (source & SCS_SOURCE ? "src." : (source & SCS_DESTINATION ? "dst." : ""));
152 }
153 
154 /* buf is a scratch area which is not always used, the return value is
155  * either a locally managed string or points to @buf.  */
156 const gchar *
stats_cluster_get_component_name(StatsCluster * self,gchar * buf,gsize buf_len)157 stats_cluster_get_component_name(StatsCluster *self, gchar *buf, gsize buf_len)
158 {
159   if ((self->key.component & SCS_SOURCE_MASK) == SCS_GROUP)
160     {
161       if (self->key.component & SCS_SOURCE)
162         return "source";
163       else if (self->key.component & SCS_DESTINATION)
164         return "destination";
165       else
166         g_assert_not_reached();
167     }
168   else
169     {
170       g_snprintf(buf, buf_len, "%s%s",
171                  _get_component_prefix(self->key.component),
172                  _get_module_name(self->key.component));
173       return buf;
174     }
175 }
176 
177 gboolean
stats_counter_group_init_equals(const StatsCounterGroupInit * self,const StatsCounterGroupInit * other)178 stats_counter_group_init_equals(const StatsCounterGroupInit *self, const StatsCounterGroupInit *other)
179 {
180   if (!self || !other)
181     return FALSE;
182 
183   if (self == other)
184     return TRUE;
185 
186   if (self->equals)
187     return self->equals(self, other);
188 
189   return (self->init == other->init) && (self->counter.names == other->counter.names);
190 }
191 
192 gboolean
stats_cluster_key_equal(const StatsClusterKey * key1,const StatsClusterKey * key2)193 stats_cluster_key_equal(const StatsClusterKey *key1, const StatsClusterKey *key2)
194 {
195   return key1->component == key2->component
196          && strcmp(key1->id, key2->id) == 0
197          && strcmp(key1->instance, key2->instance) == 0
198          && stats_counter_group_init_equals(&key1->counter_group_init, &key2->counter_group_init);
199 }
200 
201 gboolean
stats_cluster_equal(const StatsCluster * sc1,const StatsCluster * sc2)202 stats_cluster_equal(const StatsCluster *sc1, const StatsCluster *sc2)
203 {
204   return stats_cluster_key_equal(&sc1->key, &sc2->key);
205 }
206 
207 guint
stats_cluster_hash(const StatsCluster * self)208 stats_cluster_hash(const StatsCluster *self)
209 {
210   return g_str_hash(self->key.id) + g_str_hash(self->key.instance) + self->key.component;
211 }
212 
213 StatsCounterItem *
stats_cluster_track_counter(StatsCluster * self,gint type)214 stats_cluster_track_counter(StatsCluster *self, gint type)
215 {
216   gint type_mask = 1 << type;
217 
218   g_assert(type < self->counter_group.capacity);
219 
220   self->live_mask |= type_mask;
221   self->use_count++;
222   return &self->counter_group.counters[type];
223 }
224 
225 StatsCounterItem *
stats_cluster_get_counter(StatsCluster * self,gint type)226 stats_cluster_get_counter(StatsCluster *self, gint type)
227 {
228   gint type_mask = 1 << type;
229 
230   g_assert(type < self->counter_group.capacity);
231 
232   if (!(self->live_mask & type_mask))
233     return NULL;
234 
235   return &self->counter_group.counters[type];
236 }
237 
238 void
stats_cluster_untrack_counter(StatsCluster * self,gint type,StatsCounterItem ** counter)239 stats_cluster_untrack_counter(StatsCluster *self, gint type, StatsCounterItem **counter)
240 {
241   g_assert(self && (self->live_mask & (1 << type)) && &self->counter_group.counters[type] == (*counter));
242   g_assert(self->use_count > 0);
243   self->use_count--;
244 
245   if (self->use_count == 0 && (*counter)->external)
246     {
247       (*counter)->external = FALSE;
248       (*counter)->value_ref = NULL;
249       gint type_mask = 1 << type;
250       self->live_mask &= ~type_mask;
251     }
252 
253   *counter = NULL;
254 }
255 
256 static gchar *
_stats_build_query_key(StatsCluster * self)257 _stats_build_query_key(StatsCluster *self)
258 {
259   GString *key = g_string_new("");
260   gchar buffer[64] = {0};
261   const gchar *component_name = stats_cluster_get_component_name(self, buffer, sizeof(buffer));
262 
263   g_string_append(key, component_name);
264 
265   if (self->key.id && self->key.id[0])
266     {
267       g_string_append_printf(key, ".%s", self->key.id);
268     }
269   if (self->key.instance && self->key.instance[0])
270     {
271       g_string_append_printf(key, ".%s", self->key.instance);
272     }
273 
274   return g_string_free(key, FALSE);
275 }
276 
277 gboolean
stats_cluster_is_alive(StatsCluster * self,gint type)278 stats_cluster_is_alive(StatsCluster *self, gint type)
279 {
280   g_assert(type < self->counter_group.capacity);
281 
282   return !!((1<<type) & self->live_mask);
283 }
284 
285 gboolean
stats_cluster_is_indexed(StatsCluster * self,gint type)286 stats_cluster_is_indexed(StatsCluster *self, gint type)
287 {
288   g_assert(type < self->counter_group.capacity);
289 
290   return !!((1<<type) & self->indexed_mask);
291 }
292 
293 StatsCluster *
stats_cluster_new(const StatsClusterKey * key)294 stats_cluster_new(const StatsClusterKey *key)
295 {
296   StatsCluster *self = g_new0(StatsCluster, 1);
297 
298   stats_cluster_key_clone(&self->key, key);
299   self->use_count = 0;
300   self->query_key = _stats_build_query_key(self);
301   key->counter_group_init.init(&self->key.counter_group_init, &self->counter_group);
302   g_assert(self->counter_group.capacity <= sizeof(self->live_mask)*8);
303   return self;
304 }
305 
306 StatsCluster *
stats_cluster_dynamic_new(const StatsClusterKey * key)307 stats_cluster_dynamic_new(const StatsClusterKey *key)
308 {
309   StatsCluster *sc = stats_cluster_new(key);
310   sc->dynamic = TRUE;
311 
312   return sc;
313 }
314 
315 void
stats_counter_group_free(StatsCounterGroup * self)316 stats_counter_group_free(StatsCounterGroup *self)
317 {
318   if (self->free_fn)
319     self->free_fn(self);
320 }
321 
322 
323 static void
stats_cluster_free_counter(StatsCluster * self,gint type,StatsCounterItem * item,gpointer user_data)324 stats_cluster_free_counter(StatsCluster *self, gint type, StatsCounterItem *item, gpointer user_data)
325 {
326   stats_counter_free(item);
327 }
328 
329 void
stats_cluster_free(StatsCluster * self)330 stats_cluster_free(StatsCluster *self)
331 {
332   stats_cluster_foreach_counter(self, stats_cluster_free_counter, NULL);
333   stats_cluster_key_cloned_free(&self->key);
334   g_free(self->query_key);
335   stats_counter_group_free(&self->counter_group);
336   g_free(self);
337 }
338