chiark / gitweb /
dbus: terminate mechanism daemons after a while
[elogind.git] / src / timedated.c
index 4bde0355a55e391949693ffe8598c88f4d72adec..b2a56e7cb0d7b1f39a0f9f527443b74602f115df 100644 (file)
@@ -29,6 +29,7 @@
 #include "strv.h"
 #include "dbus-common.h"
 #include "polkit.h"
+#include "def.h"
 
 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
@@ -37,6 +38,7 @@
         " <interface name=\"org.freedesktop.timedate1\">\n"             \
         "  <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n"  \
         "  <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n"  \
+        "  <property name=\"NTP\" type=\"b\" access=\"read\"/>\n"       \
         "  <method name=\"SetTime\">\n"                                 \
         "   <arg name=\"usec_utc\" type=\"x\" direction=\"in\"/>\n"     \
         "   <arg name=\"relative\" type=\"b\" direction=\"in\"/>\n"     \
         "   <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n"   \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
+        "  <method name=\"SetNTP\">\n"                                  \
+        "   <arg name=\"use_ntp\" type=\"b\" direction=\"in\"/>\n"      \
+        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
+        "  </method>\n"                                                 \
         " </interface>\n"
 
 #define INTROSPECTION                                                   \
@@ -70,6 +76,9 @@ const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
 
 static char *zone = NULL;
 static bool local_rtc = false;
+static int use_ntp = -1;
+
+static usec_t remain_until = 0;
 
 static void free_data(void) {
         free(zone);
@@ -271,6 +280,213 @@ static int write_data_local_rtc(void) {
         return r;
 }
 
+static int read_ntp(DBusConnection *bus) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *name = "ntpd.service", *s;
+        DBusError error;
+        int r;
+
+        assert(bus);
+
+        dbus_error_init(&error);
+
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "GetUnitFileState");
+
+        if (!m) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &name,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+        if (!reply) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_get_args(reply, &error,
+                                   DBUS_TYPE_STRING, &s,
+                                   DBUS_TYPE_INVALID)) {
+                log_error("Failed to parse reply: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        use_ntp =
+                streq(s, "enabled") ||
+                streq(s, "enabled-runtime");
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int start_ntp(DBusConnection *bus, DBusError *error) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *name = "ntpd.service", *mode = "replace";
+        int r;
+
+        assert(bus);
+        assert(error);
+
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        use_ntp ? "StartUnit" : "StopUnit");
+        if (!m) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &name,
+                                      DBUS_TYPE_STRING, &mode,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+        if (!reply) {
+                log_error("Failed to issue method call: %s", bus_error_message(error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return r;
+}
+
+static int enable_ntp(DBusConnection *bus, DBusError *error) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char * const names[] = { "ntpd.service", NULL };
+        int r;
+        DBusMessageIter iter;
+        dbus_bool_t f = FALSE, t = TRUE;
+
+        assert(bus);
+        assert(error);
+
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        use_ntp ? "EnableUnitFiles" : "DisableUnitFiles");
+
+        if (!m) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        dbus_message_iter_init_append(m, &iter);
+
+        r = bus_append_strv_iter(&iter, (char**) names);
+        if (r < 0) {
+                log_error("Failed to append unit files.");
+                goto finish;
+        }
+        /* send runtime bool */
+        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) {
+                log_error("Failed to append runtime boolean.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (use_ntp) {
+                /* send force bool */
+                if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) {
+                        log_error("Failed to append force boolean.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+        if (!reply) {
+                log_error("Failed to issue method call: %s", bus_error_message(error));
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_unref(m);
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "Reload");
+        if (!m) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        dbus_message_unref(reply);
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+        if (!reply) {
+                log_error("Failed to issue method call: %s", bus_error_message(error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return r;
+}
+
+static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) {
+        dbus_bool_t db;
+
+        assert(i);
+        assert(property);
+
+        db = use_ntp > 0;
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static DBusHandlerResult timedate_message_handler(
                 DBusConnection *connection,
                 DBusMessage *message,
@@ -279,6 +495,7 @@ static DBusHandlerResult timedate_message_handler(
         const BusProperty properties[] = {
                 { "org.freedesktop.timedate1", "Timezone", bus_property_append_string, "s", zone       },
                 { "org.freedesktop.timedate1", "LocalRTC", bus_property_append_bool,   "b", &local_rtc },
+                { "org.freedesktop.timedate1", "NTP",      property_append_ntp,        "b", NULL       },
                 { NULL, NULL, NULL, NULL, NULL }
         };
 
@@ -427,7 +644,7 @@ static DBusHandlerResult timedate_message_handler(
                                 hwclock_set_time(tm);
                         }
 
-                        log_error("RTC configured to %s time.", local_rtc ? "local" : "UTC");
+                        log_info("RTC configured to %s time.", local_rtc ? "local" : "UTC");
 
                         changed = bus_properties_changed_new(
                                         "/org/freedesktop/timedate1",
@@ -483,6 +700,43 @@ static DBusHandlerResult timedate_message_handler(
 
                         log_info("Changed local time to %s", ctime(&ts.tv_sec));
                 }
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) {
+                dbus_bool_t ntp;
+                dbus_bool_t interactive;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_BOOLEAN, &ntp,
+                                    DBUS_TYPE_BOOLEAN, &interactive,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                if (ntp != !!use_ntp) {
+
+                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        use_ntp = !!ntp;
+
+                        r = enable_ntp(connection, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        r = start_ntp(connection, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        log_info("Set NTP to %s", use_ntp ? "enabled" : "disabled");
+
+                        changed = bus_properties_changed_new(
+                                        "/org/freedesktop/timedate1",
+                                        "org.freedesktop.timedate1",
+                                        "NTP\0");
+                        if (!changed)
+                                goto oom;
+                }
 
         } else
                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
@@ -537,7 +791,10 @@ static int connect_bus(DBusConnection **_bus) {
                 goto fail;
         }
 
-        if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL)) {
+        dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+        if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) ||
+            !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
                 log_error("Not enough memory");
                 r = -ENOMEM;
                 goto fail;
@@ -573,6 +830,7 @@ fail:
 int main(int argc, char *argv[]) {
         int r;
         DBusConnection *bus = NULL;
+        bool exiting = false;
 
         log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
@@ -604,8 +862,23 @@ int main(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
-        while (dbus_connection_read_write_dispatch(bus, -1))
-                ;
+        r = read_ntp(bus);
+        if (r < 0) {
+                log_error("Failed to determine whether NTP is enabled: %s", strerror(-r));
+                goto finish;
+        }
+
+        remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
+        for (;;) {
+
+                if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
+                        break;
+
+                if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
+                        exiting = true;
+                        bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
+                }
+        }
 
         r = 0;