/* stats.c - statistics from the bus driver * * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include "stats.h" #include #include #include "connection.h" #include "services.h" #include "utils.h" #ifdef DBUS_ENABLE_STATS static DBusMessage * new_asv_reply (DBusMessage *message, DBusMessageIter *iter, DBusMessageIter *arr_iter) { DBusMessage *reply = dbus_message_new_method_return (message); if (reply == NULL) return NULL; dbus_message_iter_init_append (reply, iter); if (!dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, "{sv}", arr_iter)) { dbus_message_unref (reply); return NULL; } return reply; } static dbus_bool_t open_asv_entry (DBusMessageIter *arr_iter, DBusMessageIter *entry_iter, const char *key, const char *type, DBusMessageIter *var_iter) { if (!dbus_message_iter_open_container (arr_iter, DBUS_TYPE_DICT_ENTRY, NULL, entry_iter)) return FALSE; if (!dbus_message_iter_append_basic (entry_iter, DBUS_TYPE_STRING, &key)) { dbus_message_iter_abandon_container (arr_iter, entry_iter); return FALSE; } if (!dbus_message_iter_open_container (entry_iter, DBUS_TYPE_VARIANT, type, var_iter)) { dbus_message_iter_abandon_container (arr_iter, entry_iter); return FALSE; } return TRUE; } static dbus_bool_t close_asv_entry (DBusMessageIter *arr_iter, DBusMessageIter *entry_iter, DBusMessageIter *var_iter) { if (!dbus_message_iter_close_container (entry_iter, var_iter)) { dbus_message_iter_abandon_container (arr_iter, entry_iter); return FALSE; } if (!dbus_message_iter_close_container (arr_iter, entry_iter)) return FALSE; return TRUE; } static dbus_bool_t close_asv_reply (DBusMessageIter *iter, DBusMessageIter *arr_iter) { return dbus_message_iter_close_container (iter, arr_iter); } static void abandon_asv_entry (DBusMessageIter *arr_iter, DBusMessageIter *entry_iter, DBusMessageIter *var_iter) { dbus_message_iter_abandon_container (entry_iter, var_iter); dbus_message_iter_abandon_container (arr_iter, entry_iter); } static void abandon_asv_reply (DBusMessageIter *iter, DBusMessageIter *arr_iter) { dbus_message_iter_abandon_container (iter, arr_iter); } static dbus_bool_t asv_add_uint32 (DBusMessageIter *iter, DBusMessageIter *arr_iter, const char *key, dbus_uint32_t value) { DBusMessageIter entry_iter, var_iter; if (!open_asv_entry (arr_iter, &entry_iter, key, DBUS_TYPE_UINT32_AS_STRING, &var_iter)) goto oom; if (!dbus_message_iter_append_basic (&var_iter, DBUS_TYPE_UINT32, &value)) { abandon_asv_entry (arr_iter, &entry_iter, &var_iter); goto oom; } if (!close_asv_entry (arr_iter, &entry_iter, &var_iter)) goto oom; return TRUE; oom: abandon_asv_reply (iter, arr_iter); return FALSE; } static dbus_bool_t asv_add_string (DBusMessageIter *iter, DBusMessageIter *arr_iter, const char *key, const char *value) { DBusMessageIter entry_iter, var_iter; if (!open_asv_entry (arr_iter, &entry_iter, key, DBUS_TYPE_STRING_AS_STRING, &var_iter)) goto oom; if (!dbus_message_iter_append_basic (&var_iter, DBUS_TYPE_STRING, &value)) { abandon_asv_entry (arr_iter, &entry_iter, &var_iter); goto oom; } if (!close_asv_entry (arr_iter, &entry_iter, &var_iter)) goto oom; return TRUE; oom: abandon_asv_reply (iter, arr_iter); return FALSE; } dbus_bool_t bus_stats_handle_get_stats (DBusConnection *connection, BusTransaction *transaction, DBusMessage *message, DBusError *error) { BusConnections *connections; DBusMessage *reply = NULL; DBusMessageIter iter, arr_iter; static dbus_uint32_t stats_serial = 0; dbus_uint32_t in_use, in_free_list, allocated; _DBUS_ASSERT_ERROR_IS_CLEAR (error); connections = bus_transaction_get_connections (transaction); reply = new_asv_reply (message, &iter, &arr_iter); if (reply == NULL) goto oom; /* Globals */ if (!asv_add_uint32 (&iter, &arr_iter, "Serial", stats_serial++)) goto oom; _dbus_list_get_stats (&in_use, &in_free_list, &allocated); if (!asv_add_uint32 (&iter, &arr_iter, "ListMemPoolUsedBytes", in_use) || !asv_add_uint32 (&iter, &arr_iter, "ListMemPoolCachedBytes", in_free_list) || !asv_add_uint32 (&iter, &arr_iter, "ListMemPoolAllocatedBytes", allocated)) goto oom; /* Connections */ if (!asv_add_uint32 (&iter, &arr_iter, "ActiveConnections", bus_connections_get_n_active (connections)) || !asv_add_uint32 (&iter, &arr_iter, "IncompleteConnections", bus_connections_get_n_incomplete (connections)) || !asv_add_uint32 (&iter, &arr_iter, "MatchRules", bus_connections_get_total_match_rules (connections)) || !asv_add_uint32 (&iter, &arr_iter, "PeakMatchRules", bus_connections_get_peak_match_rules (connections)) || !asv_add_uint32 (&iter, &arr_iter, "PeakMatchRulesPerConnection", bus_connections_get_peak_match_rules_per_conn (connections)) || !asv_add_uint32 (&iter, &arr_iter, "BusNames", bus_connections_get_total_bus_names (connections)) || !asv_add_uint32 (&iter, &arr_iter, "PeakBusNames", bus_connections_get_peak_bus_names (connections)) || !asv_add_uint32 (&iter, &arr_iter, "PeakBusNamesPerConnection", bus_connections_get_peak_bus_names_per_conn (connections))) goto oom; /* end */ if (!close_asv_reply (&iter, &arr_iter)) goto oom; if (!bus_transaction_send_from_driver (transaction, connection, reply)) goto oom; dbus_message_unref (reply); return TRUE; oom: if (reply != NULL) dbus_message_unref (reply); BUS_SET_OOM (error); return FALSE; } dbus_bool_t bus_stats_handle_get_connection_stats (DBusConnection *caller_connection, BusTransaction *transaction, DBusMessage *message, DBusError *error) { const char *bus_name = NULL; DBusString bus_name_str; DBusMessage *reply = NULL; DBusMessageIter iter, arr_iter; static dbus_uint32_t stats_serial = 0; dbus_uint32_t in_messages, in_bytes, in_fds, in_peak_bytes, in_peak_fds; dbus_uint32_t out_messages, out_bytes, out_fds, out_peak_bytes, out_peak_fds; BusRegistry *registry; BusService *service; DBusConnection *stats_connection; _DBUS_ASSERT_ERROR_IS_CLEAR (error); registry = bus_connection_get_registry (caller_connection); if (! dbus_message_get_args (message, error, DBUS_TYPE_STRING, &bus_name, DBUS_TYPE_INVALID)) return FALSE; _dbus_string_init_const (&bus_name_str, bus_name); service = bus_registry_lookup (registry, &bus_name_str); if (service == NULL) { dbus_set_error (error, DBUS_ERROR_NAME_HAS_NO_OWNER, "Bus name '%s' has no owner", bus_name); return FALSE; } stats_connection = bus_service_get_primary_owners_connection (service); _dbus_assert (stats_connection != NULL); reply = new_asv_reply (message, &iter, &arr_iter); if (reply == NULL) goto oom; /* Bus daemon per-connection stats */ if (!asv_add_uint32 (&iter, &arr_iter, "Serial", stats_serial++) || !asv_add_uint32 (&iter, &arr_iter, "MatchRules", bus_connection_get_n_match_rules (stats_connection)) || !asv_add_uint32 (&iter, &arr_iter, "PeakMatchRules", bus_connection_get_peak_match_rules (stats_connection)) || !asv_add_uint32 (&iter, &arr_iter, "BusNames", bus_connection_get_n_services_owned (stats_connection)) || !asv_add_uint32 (&iter, &arr_iter, "PeakBusNames", bus_connection_get_peak_bus_names (stats_connection)) || !asv_add_string (&iter, &arr_iter, "UniqueName", bus_connection_get_name (stats_connection))) goto oom; /* DBusConnection per-connection stats */ _dbus_connection_get_stats (stats_connection, &in_messages, &in_bytes, &in_fds, &in_peak_bytes, &in_peak_fds, &out_messages, &out_bytes, &out_fds, &out_peak_bytes, &out_peak_fds); if (!asv_add_uint32 (&iter, &arr_iter, "IncomingMessages", in_messages) || !asv_add_uint32 (&iter, &arr_iter, "IncomingBytes", in_bytes) || !asv_add_uint32 (&iter, &arr_iter, "IncomingFDs", in_fds) || !asv_add_uint32 (&iter, &arr_iter, "PeakIncomingBytes", in_peak_bytes) || !asv_add_uint32 (&iter, &arr_iter, "PeakIncomingFDs", in_peak_fds) || !asv_add_uint32 (&iter, &arr_iter, "OutgoingMessages", out_messages) || !asv_add_uint32 (&iter, &arr_iter, "OutgoingBytes", out_bytes) || !asv_add_uint32 (&iter, &arr_iter, "OutgoingFDs", out_fds) || !asv_add_uint32 (&iter, &arr_iter, "PeakOutgoingBytes", out_peak_bytes) || !asv_add_uint32 (&iter, &arr_iter, "PeakOutgoingFDs", out_peak_fds)) goto oom; /* end */ if (!close_asv_reply (&iter, &arr_iter)) goto oom; if (!bus_transaction_send_from_driver (transaction, caller_connection, reply)) goto oom; dbus_message_unref (reply); return TRUE; oom: if (reply != NULL) dbus_message_unref (reply); BUS_SET_OOM (error); return FALSE; } #endif