change 'rcctl' invocations to /etc/rc.d/foo
[systembsd.git] / src / interfaces / timedated / timedated.c
index 61a48cb4873c47afcccf6c721cb8638a21aa4537..4de0a2326561d4b289c59d14eb768fbd60be39f3 100644 (file)
 
 #include <sys/types.h>
 #include <sys/time.h>
+#include <sys/stat.h>
+#include <errno.h>
 #include <time.h>
 #include <string.h>
+#include <tzfile.h>
 
 #include <glib/gprintf.h>
 #include <glib-unix.h>
@@ -33,6 +36,8 @@
 
 #include "../../util.h"
 
+#define TZNAME_MAX PATH_MAX
+
 GPtrArray *timedated_freeable;
 Timedate1 *timedated_interf;
 
@@ -162,7 +167,102 @@ on_handle_set_timezone(Timedate1 *td1_passed_interf,
                    GDBusMethodInvocation *invoc,
                    const gchar *greet,
                    gpointer data) {
-    return FALSE;
+
+    GVariant *params;
+    gchar *proposed_tz;
+    const gchar *bus_name;
+    gboolean policykit_auth;
+    check_auth_result is_authed;
+
+    gchar *tz_target_path;
+    struct stat *statbuf;
+    extern int errno;
+
+    params = g_dbus_method_invocation_get_parameters(invoc);
+    g_variant_get(params, "(sb)", &proposed_tz, &policykit_auth);
+    bus_name = g_dbus_method_invocation_get_sender(invoc);
+
+    is_authed = polkit_try_auth(bus_name, "org.freedesktop.timedate1.set-timezone", policykit_auth);
+
+    switch(is_authed) {
+
+        case AUTHORIZED_NATIVELY:
+        case AUTHORIZED_BY_PROMPT:
+            break;
+
+        case UNAUTHORIZED_NATIVELY:
+        case UNAUTHORIZED_FAILED_PROMPT:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EACCES", "Insufficient permissions to set timezone.");
+            return FALSE;
+
+        case ERROR_BADBUS:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EFAULT", "Provided bus name is invalid.");
+            return FALSE;
+
+        case ERROR_BADACTION:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EFAULT", "Provided action ID is invalid.");
+            return FALSE;
+
+        case ERROR_GENERIC:
+        default:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.ECANCELED", "Failed to set timezone for unknown reasons.");
+            return FALSE;
+    }
+
+    statbuf        = (struct stat*) calloc(1, sizeof(struct stat));
+    tz_target_path = (gchar *) calloc(1, TZNAME_MAX);
+
+    g_ptr_array_add(timedated_freeable, statbuf);
+    g_ptr_array_add(timedated_freeable, tz_target_path);
+
+    strlcat(tz_target_path, TZDIR, TZNAME_MAX);
+    strlcat(tz_target_path, "/", TZNAME_MAX);
+    strlcat(tz_target_path, proposed_tz, TZNAME_MAX);
+
+    if(strstr(tz_target_path, "../")) {
+
+        g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EBADF", "Provided timezone is invalid.");
+        return FALSE;
+    }
+
+    if(!statbuf)
+        return FALSE;
+
+    if(lstat(tz_target_path, statbuf)) {
+
+        switch(errno) {
+
+            case ENOENT:
+                g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.ENOENT", "Specified timezone does not exist.");
+                break;
+
+            default:
+                g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EBADF", "Specified timezone is invalid.");
+                break;
+        }
+
+        return FALSE;
+    }
+    
+    if(!S_ISREG(statbuf->st_mode)) {
+
+        g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EBADF", "Specified path is of an inappropriate type.");
+        return FALSE;
+    }
+
+    memset(statbuf, 0, sizeof statbuf);
+
+    if(!lstat(TZDEFAULT, statbuf))
+        if(remove(TZDEFAULT))
+            return FALSE;
+
+    if(symlink(tz_target_path, TZDEFAULT))
+        return FALSE;
+
+    
+    timedate1_complete_set_timezone(td1_passed_interf, invoc);
+
+    return TRUE;
 }
 
 static gboolean
@@ -170,7 +270,9 @@ on_handle_set_local_rtc(Timedate1 *td1_passed_interf,
                         GDBusMethodInvocation *invoc,
                         const gchar *greet,
                         gpointer data) {
-    return FALSE;
+
+    g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.ENODEV", "Unix RTC must be in UTC.");
+    return TRUE;
 }
 
 static gboolean
@@ -178,7 +280,80 @@ on_handle_set_ntp(Timedate1 *td1_passed_interf,
                   GDBusMethodInvocation *invoc,
                   const gchar *greet,
                   gpointer data) {
-    return FALSE;
+
+    GVariant *params;
+    const gchar *bus_name;
+    gboolean policykit_auth;
+    check_auth_result is_authed;
+
+                                            /* revert to rcctl when 5.7 rolls around */
+    gint ntpd_notrunning, ntpd_notenabled; /* this logic flip is due to rcctl returning 0 on success, 
+                                             * in this case an error means ntpd is not running or not enabled */
+    gboolean proposed_ntpstate;
+    GError *sh_errors;
+
+    extern int errno;
+
+    params = g_dbus_method_invocation_get_parameters(invoc);
+    g_variant_get(params, "(bb)", &proposed_ntpstate, &policykit_auth);
+    bus_name = g_dbus_method_invocation_get_sender(invoc);
+
+    is_authed = polkit_try_auth(bus_name, "org.freedesktop.timedate1.set-ntp", policykit_auth);
+
+    switch(is_authed) {
+
+        case AUTHORIZED_NATIVELY:
+        case AUTHORIZED_BY_PROMPT:
+            break;
+
+        case UNAUTHORIZED_NATIVELY:
+        case UNAUTHORIZED_FAILED_PROMPT:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EACCES", "Insufficient permissions to toggle the NTP daemon.");
+            return FALSE;
+
+        case ERROR_BADBUS:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EFAULT", "Provided bus name is invalid.");
+            return FALSE;
+
+        case ERROR_BADACTION:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.EFAULT", "Provided action ID is invalid.");
+            return FALSE;
+
+        case ERROR_GENERIC:
+        default:
+            g_dbus_method_invocation_return_dbus_error(invoc, "org.freedesktop.timedate1.Error.ECANCELED", "Failed to toggle the NTP daemon for unknown reasons.");
+            return FALSE;
+    }
+
+    ntpd_notrunning = 0;   /* GLib does not bother asserting the passed return value int to zero */
+    ntpd_notenabled = 0;   /* if the program's exit status is also zero, hence this decl.        */
+
+    if((ntpd_notrunning = system("/etc/rc.d/ntpd check > /dev/null 2>&1")) == -1)
+        return FALSE;
+
+    if((ntpd_notenabled = system("/etc/rc.d/ntpd status > /dev/null 2>&1")) == -1)
+        return FALSE;
+
+    if(proposed_ntpstate) {
+
+        if(ntpd_notrunning)
+            system("/etc/rc.d/ntpd -f start > /dev/null 2>&1");
+
+        if(ntpd_notenabled)
+            system("/etc/rc.d/ntpd enable > /dev/null 2>&1");
+
+    } else {
+
+        if(!ntpd_notrunning)
+            system("/etc/rc.d/ntpd stop > /dev/null 2>&1");
+
+        if(!ntpd_notenabled)
+            system("/etc/rc.d/ntpd disable > /dev/null 2>&1");
+    }
+    timedate1_complete_set_ntp(td1_passed_interf, invoc);
+
+    return TRUE; 
 }
 
 const gchar *
@@ -222,10 +397,11 @@ our_get_timezone() {
         if(hash_to_match)
             g_free(hash_to_match);
     }
-
     return ret;
 }
 
+/* Unix time must be in UTC. */
 gboolean
 our_get_local_rtc() { 
 
@@ -245,9 +421,18 @@ our_get_can_ntp() {
 gboolean
 our_get_ntp() {
  
-    const gboolean ret = FALSE;
+    int system_ret;
 
-    return ret;
+    if((system_ret = system("/etc/rc.d/ntpd check > /dev/null 2>&1")) == -1) {
+
+        g_printf("failed to check NTP\n");
+        return FALSE;
+    }
+
+    if(system_ret)
+        return FALSE;
+
+    return TRUE;
 }
 
 gboolean