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