Add listener to collect events propagated by systemd via dbus
authorŁukasz Stelmach <l.stelmach@samsung.com>
Tue, 9 May 2017 09:49:46 +0000 (11:49 +0200)
committerKrzysztof Opasiak <k.opasiak@samsung.com>
Wed, 10 May 2017 18:49:58 +0000 (20:49 +0200)
This listener subscribes to DBUS signals generated by systemd
to catch services which entered a failed state.

Signed-off-by: Krzysztof Opasiak <k.opasiak@samsung.com>
Makefile.am
src/listeners/systemd.c [new file with mode: 0644]

index 22c8b692d0aa1f277c59d08400b68e9535f722a8..f5b2ca83f82858c3bef096f64653fb499d469501 100644 (file)
@@ -44,6 +44,7 @@ faultd_SOURCES = \
     src/event_types/service_failed_event.c \
     src/faultd.c \
     src/listeners/audit.c \
+    src/listeners/systemd.c \
     src/util/log.c \
     src/util/notify_queue.c
 faultd_LDADD = $(LIBSYSTEMD_LIBS) ${AUDIT_LIBS}
diff --git a/src/listeners/systemd.c b/src/listeners/systemd.c
new file mode 100644 (file)
index 0000000..9ddd6df
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * This file is part of faultd.
+ *
+ * Copyright © 2017 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+
+#include <poll.h>
+#include <stdio.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
+
+#include "event_processor.h"
+#include "log.h"
+#include "module.h"
+#include "service_failed_event.h"
+
+struct systemd_listener {
+       struct faultd_module module;
+       /* Put your data here */
+};
+
+#define to_systemd_listener(MOD)                                               \
+       container_of(MOD, struct systemd_listener, module)
+
+static int on_unit_properties_changed(sd_bus_message *m, void *userdata,
+                                      sd_bus_error *ret_error)
+{
+       int rc = 1;
+       const char *interface;
+       /* const char* path; */
+       /* path = NULL; */
+       uint8_t type;
+       struct sf_event_data sf_ev_data = {};
+       struct faultd_event *ev;
+
+       rc = sd_bus_message_read(m, "s", &interface);
+       if (rc < 0) {
+               log_error_errno(rc, "Invalid message format.");
+               goto finish;
+       }
+       if (strcmp("org.freedesktop.systemd1.Unit", interface) != 0) {
+               rc = 0;
+               goto finish;
+       }
+
+       rc = sd_bus_message_get_type(m, &type);
+       if (rc < 0) {
+               log_error_errno(rc, "Failed to get message type.");
+               goto finish;
+       }
+
+       log_debug("Received a message!");
+       log_debug("    Type: %s",
+                         type ==  SD_BUS_MESSAGE_METHOD_CALL ? "method call" :
+                         (type ==  SD_BUS_MESSAGE_METHOD_RETURN ? "method return" :
+                          (type ==  SD_BUS_MESSAGE_METHOD_ERROR ? "method error" :
+                               (type ==  SD_BUS_MESSAGE_SIGNAL ? "signal" : "INVALID"))));
+       log_debug("    Interface:  %s", sd_bus_message_get_interface(m));
+       log_debug("    Member:     %s", sd_bus_message_get_member(m));
+       log_debug("    Path:       %s", sd_bus_message_get_path(m));
+       log_debug("    Message If: %s", interface);
+
+       rc = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+       if (rc < 0) {
+               rc = 0;
+       }
+       log_debug("    Message dictionary:");
+       while ((rc = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+               const char *key;
+
+               rc = sd_bus_message_read(m, "s", &key);
+               if (rc < 0) {
+                       log_error_errno(rc, "Failed to read a DICT_ENTRY: %m.");
+                       rc = 0;
+                       goto finish;
+               }
+
+               if (strcmp("ActiveState", key) == 0) {
+                       const char *value;
+
+                       rc = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "s");
+                       if (rc < 0) {
+                               /* XXX */
+                               rc = 0;
+                               goto finish;
+                       }
+                       rc = sd_bus_message_read(m, "s", &value);
+                       if (rc < 0) {
+                               log_error_errno(rc, "Failed to read the AciveSate value: %m.");
+                               goto finish;
+                       }
+                       rc = sd_bus_message_exit_container(m);
+                       log_debug("        %s:%s", key, value);
+                       if (strcmp("failed", value) == 0) {
+                               ;       /* XXX */
+                       }
+
+               } else if (strncmp("ConditionTimestamp", key, 18) == 0) {
+                       uint64_t value;
+
+                       /* TODO: Fill detection_time properly */
+
+                       rc = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "t");
+                       if (rc < 0) {
+                               log_debug("failedenering ConditionTimestamp variant");
+                               /* XXX */
+                               rc = 0;
+                               goto finish;
+                       }
+                       rc = sd_bus_message_read(m, "t", &value);
+                       if (rc < 0) {
+                               log_error_errno(rc, "Failed to read the ConditionTimestamp value: %m.");
+                               goto finish;
+                       }
+                       rc = sd_bus_message_exit_container(m);
+                       log_debug("        %s:%ld", key, value);
+
+               } else {
+                       log_debug("        %s", key);
+                       rc = sd_bus_message_skip(m, "v");
+                       if (rc < 0) {
+                               fprintf(stderr, "Failed to skip a value.");
+                               rc = 0;
+                               goto finish;
+                       }
+               }
+
+               rc = sd_bus_message_exit_container(m);
+               if (rc < 0) {
+                       log_error_errno(rc, "Failed to exit a container: %m.");
+                       rc = 0;
+                       goto finish;
+               }
+       }
+
+       rc = sd_bus_message_exit_container(m);
+       if (rc < 0) {
+               rc = 0;
+       }
+
+       systemd_service_init(sd_bus_message_get_path(m), &sf_ev_data.service);
+
+       rc = faultd_event_create(SERVICE_FAILED_EVENT_ID, &sf_ev_data, &ev);
+       if (rc) {
+               log_error_errno(rc, "Unable to allocate an event: %m.");
+               systemd_service_cleanup(&sf_ev_data.service);
+               goto finish;
+       }
+
+       rc = event_processor_report_event(ev);
+       if (rc) {
+               log_error_errno(rc, "Unable to report event: %m");
+               faultd_event_unref(ev);
+       }
+finish:
+       return rc;
+}
+
+static int systemd_listener_init(struct faultd_module *module, sd_event* loop)
+{
+       sd_bus_error error = SD_BUS_ERROR_NULL;
+       sd_bus *bus = NULL;
+       int rc;
+
+       rc = sd_bus_default_system(&bus);
+       if (rc < 0) {
+               log_error_errno(rc, "Failed to acquire the defult system bus connection.");
+               return -1;
+       }
+
+       rc = sd_bus_attach_event(bus, loop, SD_EVENT_PRIORITY_NORMAL);
+       if (rc < 0) {
+               log_error_errno(rc, "Failed to attach the bus to the event loop.");
+               return -1;
+       }
+
+       rc = sd_bus_add_match(bus,
+                                                 NULL,
+                                                 "type='signal',sender='org.freedesktop.systemd1',"
+                                                 "interface='org.freedesktop.DBus.Properties',"
+                                                 "member='PropertiesChanged',"
+                                                 "path_namespace='/org/freedesktop/systemd1/unit'",
+                                                 on_unit_properties_changed,
+                                                 NULL);
+       if (rc < 0) {
+               log_error_errno(rc, "Failed to add match.");
+               return -1;
+       }
+
+       rc = sd_bus_call_method(bus,
+                                                       "org.freedesktop.systemd1",
+                                                       "/org/freedesktop/systemd1",
+                                                       "org.freedesktop.systemd1.Manager",
+                                                       "Subscribe",
+                                                       &error,
+                                                       NULL, NULL);
+       if (rc < 0) {
+               log_error_errno(rc, "Failed to subscribe.");
+               return -1;
+       }
+
+       return 0;
+}
+
+static void systemd_listener_cleanup(struct faultd_module *module)
+{
+       /* Nothing to do here at the moment. Anyway, don't use D-Bus here,
+          it's been closed already. */
+       ;
+}
+
+struct systemd_listener systemd_listener = {
+       .module = {
+               .name = "systemd_listener",
+               .type = FAULTD_MODULE_TYPE_LISTENER,
+
+               .init = systemd_listener_init,
+               .cleanup = systemd_listener_cleanup,
+               .node = LIST_HEAD_INIT(systemd_listener.module.node),
+       },
+};
+
+FAULTD_MODULE_REGISTER(&systemd_listener.module)