1 /* stats.c - statistics from the bus driver
2  *
3  * Copyright © 2011-2012 Nokia Corporation
4  * Copyright © 2012-2013 Collabora Ltd.
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23 
24 #include <config.h>
25 #include "stats.h"
26 
27 #include <dbus/dbus-asv-util.h>
28 #include <dbus/dbus-internals.h>
29 #include <dbus/dbus-connection-internal.h>
30 
31 #include "connection.h"
32 #include "driver.h"
33 #include "services.h"
34 #include "signals.h"
35 #include "utils.h"
36 
37 #ifdef DBUS_ENABLE_STATS
38 
39 dbus_bool_t
bus_stats_handle_get_stats(DBusConnection * connection,BusTransaction * transaction,DBusMessage * message,DBusError * error)40 bus_stats_handle_get_stats (DBusConnection *connection,
41                             BusTransaction *transaction,
42                             DBusMessage    *message,
43                             DBusError      *error)
44 {
45   BusContext *context;
46   BusConnections *connections;
47   DBusMessage *reply = NULL;
48   DBusMessageIter iter, arr_iter;
49   static dbus_uint32_t stats_serial = 0;
50   dbus_uint32_t in_use, in_free_list, allocated;
51 
52   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
53 
54   context = bus_transaction_get_context (transaction);
55   connections = bus_context_get_connections (context);
56 
57   reply = _dbus_asv_new_method_return (message, &iter, &arr_iter);
58 
59   if (reply == NULL)
60     goto oom;
61 
62   /* Globals */
63 
64   _dbus_list_get_stats (&in_use, &in_free_list, &allocated);
65 
66   if (!_dbus_asv_add_uint32 (&arr_iter, "Serial", stats_serial++) ||
67       !_dbus_asv_add_uint32 (&arr_iter, "ListMemPoolUsedBytes", in_use) ||
68       !_dbus_asv_add_uint32 (&arr_iter, "ListMemPoolCachedBytes", in_free_list) ||
69       !_dbus_asv_add_uint32 (&arr_iter, "ListMemPoolAllocatedBytes", allocated))
70     {
71       _dbus_asv_abandon (&iter, &arr_iter);
72       goto oom;
73     }
74 
75   /* Connections */
76 
77   if (!_dbus_asv_add_uint32 (&arr_iter, "ActiveConnections",
78         bus_connections_get_n_active (connections)) ||
79       !_dbus_asv_add_uint32 (&arr_iter, "IncompleteConnections",
80         bus_connections_get_n_incomplete (connections)) ||
81       !_dbus_asv_add_uint32 (&arr_iter, "MatchRules",
82         bus_connections_get_total_match_rules (connections)) ||
83       !_dbus_asv_add_uint32 (&arr_iter, "PeakMatchRules",
84         bus_connections_get_peak_match_rules (connections)) ||
85       !_dbus_asv_add_uint32 (&arr_iter, "PeakMatchRulesPerConnection",
86         bus_connections_get_peak_match_rules_per_conn (connections)) ||
87       !_dbus_asv_add_uint32 (&arr_iter, "BusNames",
88         bus_connections_get_total_bus_names (connections)) ||
89       !_dbus_asv_add_uint32 (&arr_iter, "PeakBusNames",
90         bus_connections_get_peak_bus_names (connections)) ||
91       !_dbus_asv_add_uint32 (&arr_iter, "PeakBusNamesPerConnection",
92         bus_connections_get_peak_bus_names_per_conn (connections)))
93     {
94       _dbus_asv_abandon (&iter, &arr_iter);
95       goto oom;
96     }
97 
98   /* end */
99 
100   if (!_dbus_asv_close (&iter, &arr_iter))
101     goto oom;
102 
103   if (!bus_transaction_send_from_driver (transaction, connection, reply))
104     goto oom;
105 
106   dbus_message_unref (reply);
107   return TRUE;
108 
109 oom:
110   if (reply != NULL)
111     dbus_message_unref (reply);
112 
113   BUS_SET_OOM (error);
114   return FALSE;
115 }
116 
117 dbus_bool_t
bus_stats_handle_get_connection_stats(DBusConnection * caller_connection,BusTransaction * transaction,DBusMessage * message,DBusError * error)118 bus_stats_handle_get_connection_stats (DBusConnection *caller_connection,
119                                        BusTransaction *transaction,
120                                        DBusMessage    *message,
121                                        DBusError      *error)
122 {
123   BusDriverFound found;
124   DBusMessage *reply = NULL;
125   DBusMessageIter iter, arr_iter;
126   static dbus_uint32_t stats_serial = 0;
127   dbus_uint32_t in_messages, in_bytes, in_fds, in_peak_bytes, in_peak_fds;
128   dbus_uint32_t out_messages, out_bytes, out_fds, out_peak_bytes, out_peak_fds;
129   DBusConnection *stats_connection;
130 
131   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
132 
133   found = bus_driver_get_conn_helper (caller_connection, message,
134                                       "statistics", NULL, &stats_connection,
135                                       error);
136 
137   switch (found)
138     {
139       case BUS_DRIVER_FOUND_SELF:
140         dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
141                         "GetConnectionStats is not meaningful for the "
142                         "message bus \"%s\" itself", DBUS_SERVICE_DBUS);
143         goto failed;
144 
145       case BUS_DRIVER_FOUND_PEER:
146         break;
147 
148       case BUS_DRIVER_FOUND_ERROR:
149         /* fall through */
150       default:
151         goto failed;
152     }
153 
154   _dbus_assert (stats_connection != NULL);
155 
156   reply = _dbus_asv_new_method_return (message, &iter, &arr_iter);
157 
158   if (reply == NULL)
159     goto oom;
160 
161   /* Bus daemon per-connection stats */
162 
163   if (!_dbus_asv_add_uint32 (&arr_iter, "Serial", stats_serial++) ||
164       !_dbus_asv_add_uint32 (&arr_iter, "MatchRules",
165         bus_connection_get_n_match_rules (stats_connection)) ||
166       !_dbus_asv_add_uint32 (&arr_iter, "PeakMatchRules",
167         bus_connection_get_peak_match_rules (stats_connection)) ||
168       !_dbus_asv_add_uint32 (&arr_iter, "BusNames",
169         bus_connection_get_n_services_owned (stats_connection)) ||
170       !_dbus_asv_add_uint32 (&arr_iter, "PeakBusNames",
171         bus_connection_get_peak_bus_names (stats_connection)) ||
172       !_dbus_asv_add_string (&arr_iter, "UniqueName",
173         bus_connection_get_name (stats_connection)))
174     {
175       _dbus_asv_abandon (&iter, &arr_iter);
176       goto oom;
177     }
178 
179   /* DBusConnection per-connection stats */
180 
181   _dbus_connection_get_stats (stats_connection,
182                               &in_messages, &in_bytes, &in_fds,
183                               &in_peak_bytes, &in_peak_fds,
184                               &out_messages, &out_bytes, &out_fds,
185                               &out_peak_bytes, &out_peak_fds);
186 
187   if (!_dbus_asv_add_uint32 (&arr_iter, "IncomingMessages", in_messages) ||
188       !_dbus_asv_add_uint32 (&arr_iter, "IncomingBytes", in_bytes) ||
189       !_dbus_asv_add_uint32 (&arr_iter, "IncomingFDs", in_fds) ||
190       !_dbus_asv_add_uint32 (&arr_iter, "PeakIncomingBytes", in_peak_bytes) ||
191       !_dbus_asv_add_uint32 (&arr_iter, "PeakIncomingFDs", in_peak_fds) ||
192       !_dbus_asv_add_uint32 (&arr_iter, "OutgoingMessages", out_messages) ||
193       !_dbus_asv_add_uint32 (&arr_iter, "OutgoingBytes", out_bytes) ||
194       !_dbus_asv_add_uint32 (&arr_iter, "OutgoingFDs", out_fds) ||
195       !_dbus_asv_add_uint32 (&arr_iter, "PeakOutgoingBytes", out_peak_bytes) ||
196       !_dbus_asv_add_uint32 (&arr_iter, "PeakOutgoingFDs", out_peak_fds))
197     {
198       _dbus_asv_abandon (&iter, &arr_iter);
199       goto oom;
200     }
201 
202   /* end */
203 
204   if (!_dbus_asv_close (&iter, &arr_iter))
205     goto oom;
206 
207   if (!bus_transaction_send_from_driver (transaction, caller_connection,
208                                          reply))
209     goto oom;
210 
211   dbus_message_unref (reply);
212   return TRUE;
213 
214 oom:
215   BUS_SET_OOM (error);
216   /* fall through */
217 failed:
218   if (reply != NULL)
219     dbus_message_unref (reply);
220 
221   return FALSE;
222 }
223 
224 
225 dbus_bool_t
bus_stats_handle_get_all_match_rules(DBusConnection * caller_connection,BusTransaction * transaction,DBusMessage * message,DBusError * error)226 bus_stats_handle_get_all_match_rules (DBusConnection *caller_connection,
227                                       BusTransaction *transaction,
228                                       DBusMessage    *message,
229                                       DBusError      *error)
230 {
231   BusContext *context;
232   DBusString bus_name_str;
233   DBusMessage *reply = NULL;
234   DBusMessageIter iter, hash_iter, entry_iter, arr_iter;
235   BusRegistry *registry;
236   char **services = NULL;
237   int services_len;
238   DBusConnection *conn_filter = NULL;
239   BusMatchmaker *matchmaker;
240   int i;
241 
242   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
243 
244   registry = bus_connection_get_registry (caller_connection);
245   context = bus_transaction_get_context (transaction);
246   matchmaker = bus_context_get_matchmaker (context);
247 
248   if (!bus_registry_list_services (registry, &services, &services_len))
249     return FALSE;
250 
251   reply = dbus_message_new_method_return (message);
252   if (reply == NULL)
253     goto oom;
254 
255   dbus_message_iter_init_append (reply, &iter);
256 
257   if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sas}",
258                                          &hash_iter))
259     goto oom;
260 
261   for (i = 0 ; i < services_len ; i++)
262     {
263       BusService *service;
264 
265       /* To avoid duplicate entries, only look for unique names */
266       if (services[i][0] != ':')
267         continue;
268 
269       _dbus_string_init_const (&bus_name_str, services[i]);
270       service = bus_registry_lookup (registry, &bus_name_str);
271       _dbus_assert (service != NULL);
272 
273       conn_filter = bus_service_get_primary_owners_connection (service);
274       _dbus_assert (conn_filter != NULL);
275 
276       if (!dbus_message_iter_open_container (&hash_iter, DBUS_TYPE_DICT_ENTRY, NULL,
277                                              &entry_iter))
278         {
279           dbus_message_iter_abandon_container (&iter, &hash_iter);
280           goto oom;
281         }
282 
283       if (!dbus_message_iter_append_basic (&entry_iter, DBUS_TYPE_STRING, &services[i]))
284         {
285           dbus_message_iter_abandon_container (&hash_iter, &entry_iter);
286           dbus_message_iter_abandon_container (&iter, &hash_iter);
287           goto oom;
288         }
289 
290       if (!dbus_message_iter_open_container (&entry_iter, DBUS_TYPE_ARRAY, "s",
291                                              &arr_iter))
292         {
293           dbus_message_iter_abandon_container (&hash_iter, &entry_iter);
294           dbus_message_iter_abandon_container (&iter, &hash_iter);
295           goto oom;
296         }
297 
298       if (!bus_match_rule_dump (matchmaker, conn_filter, &arr_iter))
299         {
300           dbus_message_iter_abandon_container (&entry_iter, &arr_iter);
301           dbus_message_iter_abandon_container (&hash_iter, &entry_iter);
302           dbus_message_iter_abandon_container (&iter, &hash_iter);
303           goto oom;
304         }
305 
306       if (!dbus_message_iter_close_container (&entry_iter, &arr_iter))
307         {
308           dbus_message_iter_abandon_container (&hash_iter, &entry_iter);
309           dbus_message_iter_abandon_container (&iter, &hash_iter);
310           goto oom;
311         }
312       if (!dbus_message_iter_close_container (&hash_iter, &entry_iter))
313         {
314           dbus_message_iter_abandon_container (&iter, &hash_iter);
315           goto oom;
316         }
317     }
318 
319   if (!dbus_message_iter_close_container (&iter, &hash_iter))
320     goto oom;
321 
322   if (!bus_transaction_send_from_driver (transaction, caller_connection,
323                                          reply))
324     goto oom;
325 
326   dbus_message_unref (reply);
327   dbus_free_string_array (services);
328   return TRUE;
329 
330 oom:
331   if (reply != NULL)
332     dbus_message_unref (reply);
333 
334   dbus_free_string_array (services);
335 
336   BUS_SET_OOM (error);
337   return FALSE;
338 }
339 
340 #endif
341