summaryrefslogtreecommitdiff
path: root/src/nhm-systemd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nhm-systemd.c')
-rw-r--r--src/nhm-systemd.c930
1 files changed, 930 insertions, 0 deletions
diff --git a/src/nhm-systemd.c b/src/nhm-systemd.c
new file mode 100644
index 0000000..1170631
--- /dev/null
+++ b/src/nhm-systemd.c
@@ -0,0 +1,930 @@
+/* NHM - NodeHealthMonitor
+ *
+ * Copyright (C) 2013 Continental Automotive Systems, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Author: Jean-Pierre Bogler <Jean-Pierre.Bogler@continental-corporation.com>
+ */
+
+/**
+ * SECTION:nhm-systemd
+ * @title: NodeHealthMonitor (NHM) systemd observation
+ * @short_description: Use systemd to check for failed units
+ *
+ * This section is for the observation of systemd. The code will monitor the
+ * "ActiveState" of unit objects on their dbus interface. Based on the
+ * transitions of the "ActiveState", failing units can be identified.
+ */
+
+
+/*******************************************************************************
+*
+* Header includes
+*
+*******************************************************************************/
+
+/* System header files */
+#include <stdio.h> /* NULL */
+#include <gio/gio.h> /* Use gdbus */
+#include <dlt/dlt.h> /* DLT traces */
+
+/* Component header files */
+#include "nhm-systemd.h" /* Own header */
+#include "nhm-helper.h" /* NHM helper functions */
+
+
+/*******************************************************************************
+*
+* Constants, types and defines
+*
+*******************************************************************************/
+
+#define NHM_SYSTEMD_BUS_NAME "org.freedesktop.systemd1"
+#define NHM_SYSTEMD_MNGR_IF "org.freedesktop.systemd1.Manager"
+#define NHM_SYSTEMD_UNIT_IF "org.freedesktop.systemd1.Unit"
+#define NHM_SYSTEMD_PROP_IF "org.freedesktop.DBus.Properties"
+#define NHM_SYSTEMD_OBJ_PATH "/org/freedesktop/systemd1"
+
+
+/**
+ * NhmActiveState:
+ * @NHM_ACTIVE_STATE_UNKNOWN: Init. val. and to handle systemd updates
+ * @NHM_ACTIVE_STATE_ACTIVE: Unit state is active
+ * @NHM_ACTIVE_STATE_RELOADING: Unit state is reloading
+ * @NHM_ACTIVE_STATE_INACTIVE: Unit is inactive
+ * @NHM_ACTIVE_STATE_FAILED: Unit is in a failed state
+ * @NHM_ACTIVE_STATE_ACTIVATING: Unit is currently being activated
+ * @NHM_ACTIVE_STATE_DEACTIVATING: Unit is currently being deactivated
+ * @NHM_ACTIVE_STATE_LAST: Last value of enumeration
+ *
+ * The enumeration is used to convert string values, which systemd delivers for
+ * the ActiveState, to numeric values.
+ */
+typedef enum
+{
+ NHM_ACTIVE_STATE_UNKNOWN,
+ NHM_ACTIVE_STATE_ACTIVE,
+ NHM_ACTIVE_STATE_RELOADING,
+ NHM_ACTIVE_STATE_INACTIVE,
+ NHM_ACTIVE_STATE_FAILED,
+ NHM_ACTIVE_STATE_ACTIVATING,
+ NHM_ACTIVE_STATE_DEACTIVATING,
+ NHM_ACTIVE_STATE_LAST
+} NhmActiveState;
+
+
+/**
+ * NhmSystemdUnit:
+ * @name: Name of the unit
+ * @path: Path to find unit on dbus
+ * @active_state: Active state of the unit
+ * @sig_sub_id: Subscription ID for PropertiesChanged signal
+ *
+ * The structure is used to create a list of observed units.
+ */
+typedef struct
+{
+ gchar *name;
+ gchar *path;
+ NhmActiveState active_state;
+ guint sig_sub_id;
+} NhmSystemdUnit;
+
+
+/**
+ * NhmSystemdAppStatusChange:
+ * @do_callback: Defines if 'app_status' should be send
+ * @next_status: Status to which app changed.
+ *
+ * The structure is used in 'nhm_systemd_active_state_map' to define the new
+ * app_status after a transition and whether a callback should be made.
+ */
+typedef struct
+{
+ gboolean do_callback;
+ NhmAppStatus_e next_status;
+} NhmSystemdAppStatusChange;
+
+
+/* Array defines new 'app_status' after a transition of the 'active_state' */
+static const
+NhmSystemdAppStatusChange
+nhm_systemd_active_state_map[NHM_ACTIVE_STATE_LAST][NHM_ACTIVE_STATE_LAST] =
+{
+ /* NHM_ACTIVE_STATE_UNKNOWN -> New state */
+ /* NHM_ACTIVE_STATE_UNKNOWN */ {{ FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVE */ { TRUE, NhmAppStatus_Ok },
+ /* NHM_ACTIVE_STATE_RELOADING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_INACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_FAILED */ { TRUE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVATING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_DEACTIVATING */ { FALSE, NhmAppStatus_Failed }},
+
+ /* NHM_ACTIVE_STATE_ACTIVE -> New state */
+ /* NHM_ACTIVE_STATE_UNKNOWN */ {{ FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_RELOADING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_INACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_FAILED */ { TRUE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVATING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_DEACTIVATING */ { FALSE, NhmAppStatus_Failed }},
+
+ /* NHM_ACTIVE_STATE_RELOADING -> New state */
+ /* NHM_ACTIVE_STATE_UNKNOWN */ {{ FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVE */ { TRUE, NhmAppStatus_Ok },
+ /* NHM_ACTIVE_STATE_RELOADING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_INACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_FAILED */ { TRUE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVATING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_DEACTIVATING */ { FALSE, NhmAppStatus_Failed }},
+
+ /* NHM_ACTIVE_STATE_INACTIVE -> New state */
+ /* NHM_ACTIVE_STATE_UNKNOWN */ {{ FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVE */ { TRUE, NhmAppStatus_Ok },
+ /* NHM_ACTIVE_STATE_RELOADING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_INACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_FAILED */ { TRUE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVATING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_DEACTIVATING */ { FALSE, NhmAppStatus_Failed }},
+
+ /* NHM_ACTIVE_STATE_FAILED -> New state */
+ /* NHM_ACTIVE_STATE_UNKNOWN */ {{ FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVE */ { TRUE, NhmAppStatus_Ok },
+ /* NHM_ACTIVE_STATE_RELOADING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_INACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_FAILED */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVATING */ { TRUE, NhmAppStatus_Restarting },
+ /* NHM_ACTIVE_STATE_DEACTIVATING */ { FALSE, NhmAppStatus_Failed }},
+
+ /* NHM_ACTIVE_STATE_ACTIVATING -> New state */
+ /* NHM_ACTIVE_STATE_UNKNOWN */ {{ FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVE */ { TRUE, NhmAppStatus_Ok },
+ /* NHM_ACTIVE_STATE_RELOADING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_INACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_FAILED */ { TRUE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVATING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_DEACTIVATING */ { FALSE, NhmAppStatus_Failed }},
+
+ /* NHM_ACTIVE_STATE_DEACTIVATING -> New state */
+ /* NHM_ACTIVE_STATE_UNKNOWN */ {{ FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_RELOADING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_INACTIVE */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_FAILED */ { TRUE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_ACTIVATING */ { FALSE, NhmAppStatus_Failed },
+ /* NHM_ACTIVE_STATE_DEACTIVATING */ { FALSE, NhmAppStatus_Failed }}
+};
+
+
+/*******************************************************************************
+*
+* Prototypes for file local functions (see implementation for description)
+*
+*******************************************************************************/
+
+/* Helper functions */
+static NhmActiveState nhm_systemd_active_state_string_to_enum (const gchar *string);
+static void nhm_systemd_unit_active_state_changed (NhmSystemdUnit *unit,
+ NhmActiveState new_state);
+static gint nhm_systemd_find_unit_by_name (gconstpointer u1,
+ gconstpointer u2);
+static void nhm_systemd_free_unit (gpointer unit);
+
+static NhmActiveState nhm_systemd_unit_get_active_state (NhmSystemdUnit *unit);
+
+/* Signal registration functions */
+static guint nhm_systemd_subscribe_properties_changed (const NhmSystemdUnit *unit);
+
+/* Signal handler functions */
+static void nhm_systemd_unit_active_state_changed (NhmSystemdUnit *unit,
+ NhmActiveState new_state);
+static void nhm_systemd_unit_properties_changed (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data);
+static void nhm_systemd_unit_added (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data);
+static void nhm_systemd_unit_removed (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data);
+static void nhm_systemd_unit_properties_changed (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data);
+
+
+/*******************************************************************************
+*
+* Local variables and constants
+*
+*******************************************************************************/
+
+/* Bus connection and callback set on observation start */
+static NhmSystemdAppStatusCb nhm_systemd_app_status_cb = NULL;
+static GDBusConnection *nhm_systemd_conn = NULL;
+
+/* List of units known by us (based on NhmSystemdUnit) */
+static GSList *nhm_systemd_observed_units = NULL;
+
+/* Signals registered at systemd */
+static guint nhm_systemd_unit_add_sig_id = 0;
+static guint nhm_systemd_unit_rem_sig_id = 0;
+static gboolean nhm_systemd_events_subscribed = FALSE;
+
+
+/*******************************************************************************
+*
+* Local (static) functions
+*
+*******************************************************************************/
+
+
+/**
+ * nhm_systemd_active_state_string_to_enum:
+ * @string: String representing 'active_state' of a systemd unit
+ * @return: Enumerated equivalent for 'active_state' string.
+ *
+ * The function converts the passed 'active_state' string to the equivalent
+ * enumerated value. If the string isn't a known ActiveState,
+ * 'NHM_ACTIVE_STATE_UNKNOWN' is returned.
+ */
+static NhmActiveState
+nhm_systemd_active_state_string_to_enum(const gchar *string)
+{
+ NhmActiveState state = NHM_ACTIVE_STATE_UNKNOWN;
+
+ if(g_strcmp0(string, "active") == 0)
+ {
+ state = NHM_ACTIVE_STATE_ACTIVE;
+ }
+ else if(g_strcmp0(string, "inactive") == 0)
+ {
+ state = NHM_ACTIVE_STATE_INACTIVE;
+ }
+ else if(g_strcmp0(string, "activating") == 0)
+ {
+ state = NHM_ACTIVE_STATE_ACTIVATING;
+ }
+ else if(g_strcmp0(string, "deactivating") == 0)
+ {
+ state = NHM_ACTIVE_STATE_DEACTIVATING;
+ }
+ else if(g_strcmp0(string, "failed") == 0)
+ {
+ state = NHM_ACTIVE_STATE_FAILED;
+ }
+ else if(g_strcmp0(string, "reloading") == 0)
+ {
+ state = NHM_ACTIVE_STATE_RELOADING;
+ }
+ else
+ {
+ state = NHM_ACTIVE_STATE_UNKNOWN;
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to convert 'ActiveState'.");
+ DLT_STRING("Error: Unknown string.");
+ DLT_STRING("String:"); DLT_STRING(string));
+ }
+
+ return state;
+}
+
+
+/**
+ * nhm_systemd_subscribe_properties_changed:
+ * @unit: Unit for which properties changed should be processed
+ * @return: Subscription ID. Necessary to unsubscribe.
+ *
+ * The function registers for the PropertiesChanged signal of the passed unit.
+ */
+static guint
+nhm_systemd_subscribe_properties_changed(const NhmSystemdUnit *unit)
+{
+ guint rval = 0;
+
+ rval =
+ g_dbus_connection_signal_subscribe(nhm_systemd_conn,
+ NHM_SYSTEMD_BUS_NAME,
+ NHM_SYSTEMD_PROP_IF,
+ "PropertiesChanged",
+ unit->path,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ &nhm_systemd_unit_properties_changed,
+ (gpointer) unit,
+ NULL);
+ return rval;
+}
+
+
+/**
+ * nhm_systemd_find_unit_by_name:
+ * @u1: Pointer to unit item from list.
+ * @u2: Pointer to searched unit item.
+ * @return: 0, if unit names equal.
+ *
+ * The function is used by 'custom finds' to find a unit based on it name.
+ */
+static gint
+nhm_systemd_find_unit_by_name(gconstpointer u1, gconstpointer u2)
+{
+ return g_strcmp0(((NhmSystemdUnit*) u1)->name,
+ ((NhmSystemdUnit*) u2)->name);
+}
+
+
+/**
+ * nhm_systemd_free_unit:
+ * @unit: Pointer to unit item.
+ *
+ * The function frees the memory occupied by a unit object and its members.
+ * It also unregisters for the PropertiesChnaged signal of the unit.
+ */
+static void
+nhm_systemd_free_unit(gpointer unit)
+{
+ NhmSystemdUnit *u = (NhmSystemdUnit*) unit;
+
+ g_free(u->name);
+ g_free(u->path);
+
+ g_dbus_connection_signal_unsubscribe(nhm_systemd_conn, u->sig_sub_id);
+
+ g_free(u);
+}
+
+
+/**
+ * nhm_systemd_unit_active_state_changed:
+ * @unit: Pointer to unit item, whose 'ActiveState' changed.
+ * @new_state: New 'ActiveState' of the unit.
+ *
+ * The function is called when the 'ActiveState' property of a unit changed.
+ */
+static void
+nhm_systemd_unit_active_state_changed(NhmSystemdUnit *unit,
+ NhmActiveState new_state)
+{
+ const NhmSystemdAppStatusChange *status_change = NULL;
+
+ status_change = &nhm_systemd_active_state_map[unit->active_state][new_state];
+
+ if(status_change->do_callback == TRUE)
+ {
+ nhm_systemd_app_status_cb(unit->name, status_change->next_status);
+ }
+
+ unit->active_state = new_state;
+}
+
+
+/**
+ * nhm_systemd_unit_get_active_state:
+ * @unit: Pointer to unit item, whose 'ActiveState' property should be read.
+ * @return: ActiveState read from unit properties and converted to the
+ * equivalent enumeration.
+ *
+ * The function is called to retrieve value of the 'ActiveState' property of a
+ * unit.
+ */
+static NhmActiveState
+nhm_systemd_unit_get_active_state(NhmSystemdUnit *unit)
+{
+ GError *error = NULL;
+ GVariant *propval = NULL;
+ GVariant *child = NULL;
+ const gchar *state = NULL;
+ NhmActiveState retval = NHM_ACTIVE_STATE_UNKNOWN;
+
+ propval = g_dbus_connection_call_sync(nhm_systemd_conn,
+ NHM_SYSTEMD_BUS_NAME,
+ unit->path,
+ NHM_SYSTEMD_PROP_IF,
+ "Get",
+ g_variant_new("(ss)",
+ NHM_SYSTEMD_UNIT_IF,
+ "ActiveState"),
+ (GVariantType*) "(v)",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if(error == NULL)
+ {
+ child = g_variant_get_child_value(propval, 0);
+ state = g_variant_get_string(child, NULL);
+ retval = nhm_systemd_active_state_string_to_enum(state);
+ g_variant_unref(propval);
+ }
+ else
+ {
+ retval = NHM_ACTIVE_STATE_UNKNOWN;
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to get unit property 'ActiveState'.");
+ DLT_STRING("Error: D-Bus communication failed.");
+ DLT_STRING("Reason:"); DLT_STRING(error->message));
+ g_error_free(error);
+ }
+
+ return retval;
+}
+
+
+/**
+ * nhm_systemd_unit_added:
+ * @connection: Connection on which signal occurred.
+ * @sender_name: (Unique) Sender name of signal.
+ * @object_path: Object that send the signal.
+ * @interface_name: Interface from where signal comes from.
+ * @signal_name: Name of the signal.
+ * @parameters: Parameters of dbus signal.
+ * @user_data: Optional user data (not used)
+ *
+ * Called when the "UnitAdded" signal from org.freedesktop.systemd1
+ * arrives. The new unit will be added to the internal unit list and
+ * its initial state will be retrieved.
+ */
+static void
+nhm_systemd_unit_added(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ NhmSystemdUnit *unit = NULL;
+ const gchar *param_type = NULL;
+ GVariant *child = NULL;
+ NhmSystemdUnit search_unit;
+ GSList *list_item = NULL;
+
+ param_type = g_variant_get_type_string(parameters);
+
+ if(g_strcmp0(param_type, "(so)") == 0)
+ {
+ child = g_variant_get_child_value(parameters, 0);
+ search_unit.name = (gchar*) g_variant_get_string(child, NULL);
+
+ if(g_str_has_suffix(search_unit.name, ".service") == TRUE)
+ {
+ list_item = g_slist_find_custom(nhm_systemd_observed_units,
+ &search_unit,
+ &nhm_systemd_find_unit_by_name);
+ if(list_item == NULL)
+ {
+ unit = g_new(NhmSystemdUnit, 1);
+ unit->name = g_strdup(search_unit.name);
+
+ child = g_variant_get_child_value(parameters, 1);
+ unit->path = g_variant_dup_string(child, NULL);
+
+ unit->active_state = nhm_systemd_unit_get_active_state(unit);
+ unit->sig_sub_id = nhm_systemd_subscribe_properties_changed(unit);
+
+ nhm_systemd_observed_units = g_slist_prepend(nhm_systemd_observed_units,
+ unit);
+
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_INFO,
+ DLT_STRING("NHM: Systemd unit added.");
+ DLT_STRING("Name:"); DLT_STRING(unit->name));
+ }
+ }
+ }
+ else
+ {
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to process 'UnitAdded' signal.");
+ DLT_STRING("Error: Invalid parameter type.");
+ DLT_STRING("Type:"); DLT_STRING(param_type));
+ }
+}
+
+
+/**
+ * nhm_systemd_unit_removed:
+ * @connection: Connection on which signal occurred.
+ * @sender_name: (Unique) Sender name of signal.
+ * @object_path: Object that send the signal.
+ * @interface_name: Interface from where signal comes from.
+ * @signal_name: Name of the signal.
+ * @parameters: Parameters of dbus signal.
+ * @user_data: Optional user data (not used)
+ *
+ * Called when the "UnitRemoved" signal from org.freedesktop.systemd1
+ * arrives. The unit will be removed from the internal unit list.
+ */
+static void
+nhm_systemd_unit_removed(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ GSList *list_item = NULL;
+ const gchar *param_type = NULL;
+ GVariant *child = NULL;
+ NhmSystemdUnit search_unit;
+
+ param_type = g_variant_get_type_string(parameters);
+
+ if(g_strcmp0(param_type, "(so)") == 0)
+ {
+ child = g_variant_get_child_value(parameters, 0);
+ search_unit.name = (gchar*) g_variant_get_string(child, NULL);
+
+ if(g_str_has_suffix(search_unit.name, ".service") == TRUE)
+ {
+ list_item = g_slist_find_custom(nhm_systemd_observed_units,
+ &search_unit,
+ &nhm_systemd_find_unit_by_name);
+ if(list_item != NULL)
+ {
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_INFO,
+ DLT_STRING("NHM: Systemd unit removed.");
+ DLT_STRING("Name:"); DLT_STRING(search_unit.name));
+
+ nhm_systemd_free_unit(list_item->data);
+ nhm_systemd_observed_units = g_slist_remove(nhm_systemd_observed_units,
+ list_item->data);
+ }
+ }
+ }
+ else
+ {
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to process 'UnitRemoved' signal.");
+ DLT_STRING("Error: Invalid parameter type.");
+ DLT_STRING("Type:"); DLT_STRING(param_type));
+ }
+}
+
+
+/**
+ * nhm_systemd_unit_properties_changed:
+ * @connection: Connection on which signal occurred.
+ * @sender_name: (Unique) Sender name of signal.
+ * @object_path: Object that send the signal.
+ * @interface_name: Interface from where signal comes from.
+ * @signal_name: Name of the signal.
+ * @parameters: Parameters of dbus signal.
+ * @user_data: User data. Pointer to unit for which properties changed
+ * signal had been registered.
+ *
+ * Called when the "PropertiesChanged" signal from the "Properties" interface
+ * of a systemd unit arrives. Depending on the changed property, the change
+ * will be processed further in the related sub functions.
+ */
+static void
+nhm_systemd_unit_properties_changed(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ NhmSystemdUnit *unit = (NhmSystemdUnit*) user_data;
+ const gchar **inv_props = NULL;
+ NhmActiveState active_state = NHM_ACTIVE_STATE_UNKNOWN;
+ const gchar *param_type = NULL;
+
+ param_type = g_variant_get_type_string(parameters);
+
+ if(g_strcmp0(param_type, "(sa{sv}as)") == 0)
+ {
+ inv_props = g_variant_get_strv(g_variant_get_child_value(parameters, 2),
+ NULL);
+
+ if(nhm_helper_str_in_strv("ActiveState", (gchar**) inv_props) == TRUE)
+ {
+ active_state = nhm_systemd_unit_get_active_state(unit);
+
+ if(active_state != unit->active_state)
+ {
+ nhm_systemd_unit_active_state_changed(unit, active_state);
+ }
+ }
+
+ g_free(inv_props);
+ }
+ else
+ {
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to process 'PropertiesChanged' signal.");
+ DLT_STRING("Error: Invalid parameter type.");
+ DLT_STRING("Type:"); DLT_STRING(param_type));
+ }
+}
+
+
+/*******************************************************************************
+*
+* Interfaces. Exported functions. See Header for detailed description.
+*
+*******************************************************************************/
+
+/**
+ * nhm_systemd_connect:
+ * @app_status_cb: Callback that should be called, if the "ActiveState" of
+ * a systemd Service changes between valid and invalid states.
+ *
+ * The NHM main process can start the systemd observation whith this function.
+ */
+gboolean
+nhm_systemd_connect(NhmSystemdAppStatusCb app_status_cb)
+{
+ GVariantIter iter;
+ GError *error = NULL;
+ GVariant *manager_return = NULL;
+ GVariant *unit_array = NULL;
+ GVariant *unit = NULL;
+ gboolean retval = FALSE;
+ gchar *unit_name = NULL;
+ const gchar *active_state = NULL;
+ NhmSystemdUnit *new_unit = NULL;
+
+ /* Initialize local variables */
+ nhm_systemd_app_status_cb = NULL;
+ nhm_systemd_conn = NULL;
+ nhm_systemd_observed_units = NULL;
+ nhm_systemd_events_subscribed = FALSE;
+ nhm_systemd_unit_add_sig_id = 0;
+ nhm_systemd_unit_rem_sig_id = 0;
+
+ /* Step 1: Save function to call if app status changes. */
+ if(app_status_cb != NULL)
+ {
+ retval = TRUE;
+ nhm_systemd_app_status_cb = app_status_cb;
+ }
+ else
+ {
+ retval = FALSE;
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to connect to systemd dbus.");
+ DLT_STRING("Error: Invalid callback passed."));
+ }
+
+ /* Step 2: Connect to the system bus */
+ if(retval == TRUE)
+ {
+ nhm_systemd_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+
+ if(error == NULL)
+ {
+ retval = TRUE;
+ }
+ else
+ {
+ retval = FALSE;
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to connect to systemd dbus.");
+ DLT_STRING("Error: D-Bus connection failed.");
+ DLT_STRING("Reason:"); DLT_STRING(error->message));
+ g_error_free(error);
+ }
+ }
+
+ /* Step 3: Register for signals, if units are removed/added */
+ if(retval == TRUE)
+ {
+ nhm_systemd_unit_add_sig_id =
+ g_dbus_connection_signal_subscribe(nhm_systemd_conn,
+ NHM_SYSTEMD_BUS_NAME,
+ NHM_SYSTEMD_MNGR_IF,
+ "UnitNew",
+ NHM_SYSTEMD_OBJ_PATH,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ &nhm_systemd_unit_added,
+ NULL,
+ NULL);
+
+ nhm_systemd_unit_rem_sig_id =
+ g_dbus_connection_signal_subscribe(nhm_systemd_conn,
+ NHM_SYSTEMD_BUS_NAME,
+ NHM_SYSTEMD_MNGR_IF,
+ "UnitRemoved",
+ NHM_SYSTEMD_OBJ_PATH,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ &nhm_systemd_unit_removed,
+ NULL,
+ NULL);
+ }
+
+ /* Step 4: Subscribe. Without, PropertiesChanged isn't send */
+ if(retval == TRUE)
+ {
+ manager_return = g_dbus_connection_call_sync(nhm_systemd_conn,
+ NHM_SYSTEMD_BUS_NAME,
+ NHM_SYSTEMD_OBJ_PATH,
+ NHM_SYSTEMD_MNGR_IF,
+ "Subscribe",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if(error == NULL)
+ {
+ retval = TRUE;
+ nhm_systemd_events_subscribed = TRUE;
+ g_variant_unref(manager_return);
+ }
+ else
+ {
+ retval = FALSE;
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to subscribe to systemd signals.");
+ DLT_STRING("Error: D-Bus connection failed.");
+ DLT_STRING("Reason:"); DLT_STRING(error->message));
+ g_error_free(error);
+ }
+ }
+
+ /* Step 5: Retrieve all currently known service units from systemd. */
+ if(retval == TRUE)
+ {
+ manager_return =
+ g_dbus_connection_call_sync(nhm_systemd_conn,
+ NHM_SYSTEMD_BUS_NAME,
+ NHM_SYSTEMD_OBJ_PATH,
+ NHM_SYSTEMD_MNGR_IF,
+ "ListUnits",
+ NULL,
+ (GVariantType*) "(a(ssssssouso))",
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if(error == NULL)
+ {
+ retval = TRUE;
+
+ unit_array = g_variant_get_child_value(manager_return, 0);
+ g_variant_iter_init (&iter, unit_array);
+
+ while((unit = g_variant_iter_next_value(&iter)))
+ {
+ /* Return for a unit is of type '(ssssssouso)' with member #:
+ * 0: Unit name. 3: Active state. 6: Object path */
+ g_variant_get_child(unit, 0, "s", &unit_name);
+
+ if(g_str_has_suffix(unit_name, ".service") == TRUE)
+ {
+ new_unit = g_new(NhmSystemdUnit, 1);
+ new_unit->name = unit_name;
+
+ active_state =
+ g_variant_get_string(g_variant_get_child_value(unit, 3), NULL);
+
+ new_unit->active_state =
+ nhm_systemd_active_state_string_to_enum(active_state);
+
+ g_variant_get_child(unit, 6, "o", &new_unit->path);
+
+ new_unit->sig_sub_id =
+ nhm_systemd_subscribe_properties_changed(new_unit);
+
+ nhm_systemd_observed_units =
+ g_slist_append(nhm_systemd_observed_units, new_unit);
+ }
+ else
+ {
+ g_free(unit_name);
+ }
+ }
+
+ g_variant_unref(manager_return);
+ }
+ else
+ {
+ retval = FALSE;
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to retrieve unit list from systemd.");
+ DLT_STRING("Error: D-Bus communication failed.");
+ DLT_STRING("Reason:"); DLT_STRING(error->message));
+ g_error_free(error);
+ }
+ }
+
+ /* Step 6: If there was a problem, destroy what has been created */
+ if(retval == FALSE)
+ {
+ nhm_systemd_disconnect();
+ }
+
+ return retval;
+}
+
+
+/**
+ * nhm_systemd_disconnect:
+ *
+ * The NHM main process should call this function, when systemd observation is
+ * no longer needed.
+ */
+void
+nhm_systemd_disconnect(void)
+{
+ GVariant *manager_return = NULL;
+ GError *error = NULL;
+
+ /* Reset callback */
+ nhm_systemd_app_status_cb = NULL;
+
+ /* Unsubscribe from systemd signals */
+ if(nhm_systemd_events_subscribed == TRUE)
+ {
+ manager_return =
+ g_dbus_connection_call_sync(nhm_systemd_conn,
+ NHM_SYSTEMD_BUS_NAME,
+ NHM_SYSTEMD_OBJ_PATH,
+ NHM_SYSTEMD_MNGR_IF,
+ "Unsubscribe",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if(error == NULL)
+ {
+ nhm_systemd_events_subscribed = FALSE;
+ g_variant_unref(manager_return);
+ }
+ else
+ {
+ DLT_LOG(nhm_helper_trace_ctx,
+ DLT_LOG_ERROR,
+ DLT_STRING("NHM: Failed to Unsubscribe from systemd.");
+ DLT_STRING("Error: D-Bus communication failed.");
+ DLT_STRING("Reason:"); DLT_STRING(error->message));
+ g_error_free(error);
+ }
+ }
+
+ /* Unregister unit add and remove signals */
+ if(nhm_systemd_unit_add_sig_id != 0)
+ {
+ g_dbus_connection_signal_unsubscribe(nhm_systemd_conn,
+ nhm_systemd_unit_add_sig_id);
+ nhm_systemd_unit_add_sig_id = 0;
+ }
+
+ if(nhm_systemd_unit_rem_sig_id != 0)
+ {
+ g_dbus_connection_signal_unsubscribe(nhm_systemd_conn,
+ nhm_systemd_unit_rem_sig_id);
+ nhm_systemd_unit_rem_sig_id = 0;
+ }
+
+ /* Destroy list of units (incl. unregister prop. changed) */
+ if(nhm_systemd_observed_units != NULL)
+ {
+ g_slist_free_full(nhm_systemd_observed_units, &nhm_systemd_free_unit);
+ nhm_systemd_observed_units = NULL;
+ }
+
+ if(nhm_systemd_conn != NULL)
+ {
+ g_object_unref(nhm_systemd_conn);
+ nhm_systemd_conn = NULL;
+ }
+}