| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "libnetdata/libnetdata.h" |
| 4 | #include "daemon-systemd-watcher.h" |
| 5 | #include "daemon-service.h" |
| 6 | #include "daemon-shutdown.h" |
| 7 | |
| 8 | #include <systemd/sd-bus.h> |
| 9 | |
| 10 | /* Callback function to handle the PrepareForShutdown signal. |
| 11 | * The signal sends a boolean: true indicates that shutdown is starting, |
| 12 | * false indicates that a previously initiated shutdown was canceled. |
| 13 | */ |
| 14 | static int shutdown_event_handler(sd_bus_message *m, void *userdata __maybe_unused, sd_bus_error *ret_error __maybe_unused) { |
| 15 | int shutdown; |
| 16 | int r = sd_bus_message_read(m, "b", &shutdown); |
| 17 | if (r < 0) { |
| 18 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 19 | "SYSTEMD DBUS: Failed to parse shutdown message: %s", |
| 20 | strerror(-r)); |
| 21 | return r; |
| 22 | } |
| 23 | |
| 24 | nd_log(NDLS_DAEMON, NDLP_NOTICE, |
| 25 | "SYSTEMD DBUS: Received PrepareForShutdown signal: shutdown=%s", |
| 26 | shutdown ? "true" : "false"); |
| 27 | |
| 28 | if(shutdown) |
| 29 | netdata_exit_gracefully(EXIT_REASON_SYSTEM_SHUTDOWN, true); |
| 30 | |
| 31 | return 0; |
| 32 | } |
| 33 | |
| 34 | /* Callback function to handle the PrepareForSleep signal. |
| 35 | * The signal sends a boolean: true indicates that the system is preparing to suspend, |
| 36 | * false indicates that a previous suspend was canceled (i.e. resuming). |
| 37 | */ |
| 38 | static int suspend_event_handler(sd_bus_message *m, void *userdata __maybe_unused, sd_bus_error *ret_error __maybe_unused) { |
| 39 | int suspend; |
| 40 | int r = sd_bus_message_read(m, "b", &suspend); |
| 41 | if (r < 0) { |
| 42 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 43 | "SYSTEMD DBUS: Failed to parse suspend message: %s", |
| 44 | strerror(-r)); |
| 45 | return r; |
| 46 | } |
| 47 | |
| 48 | nd_log(NDLS_DAEMON, NDLP_NOTICE, |
| 49 | "SYSTEMD DBUS: Received PrepareForSleep signal: suspend=%s\n", |
| 50 | suspend ? "true (suspending)" : "false (resuming)"); |
| 51 | |
| 52 | // Here you can trigger your suspend/resume logic. |
| 53 | return 0; |
| 54 | } |
| 55 | |
| 56 | /* Function that sets up the sd-bus listener for shutdown and suspend events. |
| 57 | * This function blocks in a loop processing bus events. |
| 58 | */ |
| 59 | static void listen_for_systemd_dbus_events(void) { |
| 60 | sd_bus *bus = NULL; |
| 61 | sd_bus_slot *shutdown_slot = NULL; |
| 62 | sd_bus_slot *suspend_slot = NULL; |
| 63 | int r; |
| 64 | |
| 65 | // Connect to the system bus. |
| 66 | r = sd_bus_open_system(&bus); |
| 67 | if (r < 0) { |
| 68 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 69 | "SYSTEMD DBUS: Failed to connect to system bus: %s", |
| 70 | strerror(-r)); |
| 71 | goto finish; |
| 72 | } |
| 73 | |
| 74 | // Add a match rule for the PrepareForShutdown signal on the login1 manager. |
| 75 | r = sd_bus_add_match( |
| 76 | bus, |
| 77 | &shutdown_slot, |
| 78 | "type='signal'," |
| 79 | "sender='org.freedesktop.login1'," |
| 80 | "interface='org.freedesktop.login1.Manager'," |
| 81 | "member='PrepareForShutdown'", |
| 82 | shutdown_event_handler, |
| 83 | NULL); |
| 84 | if (r < 0) { |
| 85 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 86 | "SYSTEMD DBUS: Failed to add signal match for shutdown: %s", |
| 87 | strerror(-r)); |
| 88 | goto finish; |
| 89 | } |
| 90 | |
| 91 | // Add a match rule for the PrepareForSleep signal on the login1 manager. |
| 92 | r = sd_bus_add_match( |
| 93 | bus, |
| 94 | &suspend_slot, |
| 95 | "type='signal'," |
| 96 | "sender='org.freedesktop.login1'," |
| 97 | "interface='org.freedesktop.login1.Manager'," |
| 98 | "member='PrepareForSleep'", |
| 99 | suspend_event_handler, |
| 100 | NULL); |
| 101 | if (r < 0) { |
| 102 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 103 | "SYSTEMD DBUS: Failed to add signal match for suspend: %s", |
| 104 | strerror(-r)); |
| 105 | goto finish; |
| 106 | } |
| 107 | |
| 108 | // Process incoming D-Bus messages. |
| 109 | while (service_running(SERVICE_SYSTEMD) && bus != NULL) { |
| 110 | // Process any pending messages. |
| 111 | r = sd_bus_process(bus, NULL); |
| 112 | if (r < 0) { |
| 113 | nd_log(NDLS_DAEMON, NDLP_ERR, |
| 114 | "SYSTEMD DBUS: Failed to process bus: %s", |
| 115 | strerror(-r)); |
| 116 | goto finish; |
| 117 | } |
| 118 | if (r > 0) // Message was processed; check for more. |
| 119 | continue; |
| 120 | |
| 121 | // Wait for the next signal. |
| 122 | do { |
| 123 | r = sd_bus_wait(bus, USEC_PER_SEC); |
| 124 | } while((r == 0 || r == -EINTR) && service_running(SERVICE_SYSTEMD)); |
| 125 | |
| 126 | if (r < 0) { |
| 127 | nd_log(NDLS_DAEMON, NDLP_ERR, "SYSTEMD DBUS: Failed to wait on bus: %s", strerror(-r)); |
| 128 | break; |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | finish: |
| 133 | sd_bus_slot_unref(shutdown_slot); |
| 134 | sd_bus_slot_unref(suspend_slot); |
| 135 | sd_bus_unref(bus); |
| 136 | } |
| 137 | |
| 138 | void systemd_watcher_thread(void *arg) { |
| 139 | struct netdata_static_thread *static_thread = arg; |
| 140 | |
| 141 | service_register(NULL, NULL, NULL); |
| 142 | |
| 143 | listen_for_systemd_dbus_events(); |
| 144 | |
| 145 | service_exits(); |
| 146 | worker_unregister(); |
| 147 | static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; |
| 148 | } |