summaryrefslogtreecommitdiff
path: root/src/rcl-timedate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rcl-timedate.c')
-rw-r--r--src/rcl-timedate.c898
1 files changed, 898 insertions, 0 deletions
diff --git a/src/rcl-timedate.c b/src/rcl-timedate.c
new file mode 100644
index 0000000..3478026
--- /dev/null
+++ b/src/rcl-timedate.c
@@ -0,0 +1,898 @@
+
+/*
+ * Copyright (C) 2023 Andrey V.Kosteltsev <kx@radix.pro>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <polkit/polkit.h>
+
+#include "rcl-timedate.h"
+#include "rcl-time-utils.h"
+#include "rcl-ntpd-utils.h"
+#include "rcl-zone-utils.h"
+
+struct RclDaemonPrivate
+{
+ gboolean debug;
+ gchar *timezone;
+ gboolean local_rtc;
+ gboolean can_ntp;
+ gboolean use_ntp;
+ PolkitAuthority *auth;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (RclDaemon, rcl_daemon, RCL_TYPE_TIMEDATE_DAEMON_SKELETON)
+
+#define RCL_DAEMON_ACTION_DELAY 20 /* seconds */
+#define RCL_INTERFACE_PREFIX "org.freedesktop.timedate1."
+
+
+/***************************************************************
+ Polkit functions:
+ ================
+ */
+static gboolean
+_check_polkit_for_action( RclDaemon *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *function )
+{
+ const gchar *action = g_strjoin( "", RCL_INTERFACE_PREFIX, function, NULL );
+ const gchar *sender;
+ GError *error;
+ PolkitSubject *subject;
+ PolkitAuthorizationResult *result;
+
+ error = NULL;
+
+ /* Check that caller is privileged */
+ sender = g_dbus_method_invocation_get_sender( invocation );
+ subject = polkit_system_bus_name_new( sender );
+
+ /********************************************************************************************
+ flag = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION causes a pop-up dialog box.
+ We have not to use any pop-up windows.
+ */
+ result = polkit_authority_check_authorization_sync( object->priv->auth,
+ subject,
+ action,
+ NULL,
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE,
+ NULL,
+ &error );
+ g_object_unref( G_OBJECT(subject) );
+
+ if( error )
+ {
+ g_dbus_method_invocation_return_gerror( invocation, error );
+ g_error_free( error );
+ g_free( (gpointer)action );
+
+ return FALSE;
+ }
+
+ if( !polkit_authorization_result_get_is_authorized( result ) )
+ {
+ error = g_error_new( RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_NOT_PRIVILEGED,
+ "User is not privileged for action %s", action );
+ g_dbus_method_invocation_return_gerror( invocation, error );
+
+ g_error_free( error );
+ g_object_unref( G_OBJECT(result) );
+ g_free( (gpointer)action );
+
+ return FALSE;
+ }
+
+ g_object_unref( G_OBJECT(result) );
+ g_free( (gpointer)action );
+
+ return TRUE;
+}
+
+
+/***************************************************************
+ DBus Properties:
+ ===============
+ */
+static gboolean get_ntpsynchronized( RclTimedateDaemon *object )
+{
+ gboolean ntp_synced = ntp_synchronized();
+
+ rcl_timedate_daemon_set_ntpsynchronized( object, ntp_synced );
+
+ return ntp_synced;
+}
+
+static guint64 get_rtctime_usec( RclTimedateDaemon *object )
+{
+ struct tm tm = {};
+ guint64 usec = 0;
+
+ if( !clock_get_hwclock( &tm ) )
+ {
+ g_debug( "get-ntpsynchronized: error: Cannot get RTC clock" );
+
+ rcl_timedate_daemon_set_rtctime_usec( object, (guint64)0 );
+
+ return (guint64)0;
+ }
+
+ usec = (guint64)timegm( &tm ) * USEC_PER_SEC;
+
+ rcl_timedate_daemon_set_rtctime_usec( object, usec );
+
+ return usec;
+}
+
+static guint64 get_time_usec( RclTimedateDaemon *object )
+{
+ guint64 usec;
+
+ usec = now( CLOCK_REALTIME );
+
+ rcl_timedate_daemon_set_time_usec( object, usec );
+
+ return usec;
+}
+
+void rcl_daemon_sync_dbus_properties( RclTimedateDaemon *object )
+{
+
+ /* Update NTPSynchronized */
+ (void)get_ntpsynchronized( object );
+
+ /* Update RTCTimeUSec */
+ (void)get_rtctime_usec( object );
+
+ /* Update TimeUSec */
+ (void)get_time_usec( object );
+}
+
+
+/***************************************************************
+ DBus Handlers:
+ =============
+ */
+gboolean handle_set_timezone( RclTimedateDaemon *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *timezone,
+ gboolean interactive,
+ RclDaemon *daemon )
+{
+ gboolean ret = TRUE;
+
+ if( !timezone_is_valid( timezone ) )
+ {
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_INVALID_TIMEZONE_FILE,
+ "Requested timezone '%s' is invalid", timezone );
+ return TRUE;
+ }
+
+ if( g_strcmp0( (const char *)daemon->priv->timezone, (const char *)timezone ) == 0 )
+ goto out;
+
+ if( !_check_polkit_for_action( daemon, invocation, "set-timezone" ) )
+ {
+ g_debug( "set-timezone: error: '%s'", "User is not privileged" );
+ return TRUE;
+ }
+
+ ret = set_system_timezone( timezone );
+ if( ret )
+ {
+ if( daemon->priv->local_rtc )
+ {
+ struct timespec ts;
+ struct tm tm;
+
+ /* Sync RTC from system clock, with the new delta */
+ if( clock_gettime(CLOCK_REALTIME, &ts) != 0 )
+ {
+ g_debug( "set-timezone: error: Sync RTC from system clock: '%s'", "clock_gettime(): failed" );
+ return TRUE;
+ }
+ if( !localtime_r( &ts.tv_sec, &tm ) )
+ {
+ g_debug( "set-timezone: error: Sync RTC from system clock: '%s'", "localtime_r(): failed" );
+ return TRUE;
+ }
+ if( !clock_set_hwclock( &tm ) )
+ {
+ g_debug( "set-timezone: error: Sync RTC from system clock: '%s'", "Cannot update '/dev/rtc' (ignoring)" );
+ }
+ }
+ }
+ else
+ {
+ g_debug( "set-timezone: error: Cannot set system timezone '%s'", timezone );
+ return TRUE;
+ }
+
+ g_free( (gpointer)daemon->priv->timezone );
+ daemon->priv->timezone = g_strdup( timezone );
+ rcl_timedate_daemon_set_timezone( object, (const gchar *)daemon->priv->timezone );
+
+out:
+ g_debug( "set-timezone: SetTimezone to '%s' returns successful status (interactive=%s)",
+ daemon->priv->timezone, (interactive) ? "true" : "false" );
+
+ rcl_timedate_daemon_complete_set_timezone( object, invocation );
+
+ return TRUE;
+}
+
+
+gboolean handle_set_local_rtc( RclTimedateDaemon *object,
+ GDBusMethodInvocation *invocation,
+ gboolean local_rtc,
+ gboolean fix_system,
+ gboolean interactive,
+ RclDaemon *daemon )
+{
+ struct timespec ts;
+ gboolean ret = TRUE;
+
+ if( daemon->priv->local_rtc == local_rtc && !fix_system )
+ goto out;
+
+ if( !_check_polkit_for_action( daemon, invocation, "set-local-rtc" ) )
+ {
+ g_debug( "set-local-rtc: error: '%s'", "User is not privileged" );
+ return TRUE;
+ }
+
+ if( daemon->priv->local_rtc != local_rtc )
+ {
+ daemon->priv->local_rtc = local_rtc;
+
+ /* Write new configuration files */
+ ret = write_data_local_rtc( daemon->priv->local_rtc );
+ if( !ret )
+ {
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "Cannot set LocalRTC" );
+ return TRUE;
+ }
+
+ }
+
+ /* Tell the kernel our timezone */
+ ret = clock_set_timezone( NULL );
+ if( !ret )
+ {
+ g_debug( "set-local-rtc: error: Cannot set timezone clock after SetLocal_RTC" );
+ return TRUE;
+ }
+
+ /* Synchronize clocks */
+ if( clock_gettime(CLOCK_REALTIME, &ts) != 0 )
+ {
+ g_debug( "set-local-rtc: error: Sync RTC from system clock after SetLocalRTC: '%s'", "clock_gettime(): failed" );
+ return TRUE;
+ }
+
+ if( fix_system )
+ {
+ struct tm tm;
+
+ /* Sync system clock from RTC; first, initialize the timezone fields of struct tm. */
+ localtime_or_gmtime_r( &ts.tv_sec, &tm, !daemon->priv->local_rtc);
+
+ /* Override the main fields of struct tm, but not the timezone fields */
+ ret = clock_get_hwclock( &tm );
+ if( !ret)
+ {
+ g_debug( "set-local-rtc: error: Failed to get hardware clock (ignoring)" );
+ }
+ else
+ {
+ /* And set the system clock with this */
+ ts.tv_sec = mktime_or_timegm( &tm, !daemon->priv->local_rtc );
+
+ if( clock_settime( CLOCK_REALTIME, &ts ) < 0 )
+ {
+ g_debug( "set-local-rtc: error: Failed to update system clock (ignoring)" );
+ }
+ }
+ }
+ else
+ {
+ struct tm tm;
+
+ /* Sync RTC from system clock */
+ localtime_or_gmtime_r( &ts.tv_sec, &tm, !daemon->priv->local_rtc );
+
+ ret = clock_set_hwclock( &tm );
+ if( !ret )
+ {
+ g_debug( "set-local-rtc: error: Failed to sync time to hardware clock (ignoring)" );
+ }
+ }
+
+ g_debug( "set-local-rtc: RTC configured to %s time", (daemon->priv->local_rtc) ? "localtime" : "UTC" );
+
+
+ rcl_timedate_daemon_set_local_rtc( object, daemon->priv->local_rtc );
+
+out:
+ g_debug( "set-local-rtc: SetLocalRTC to '%s' returns successful status (fix_sysrem=%s; interactive=%s)",
+ (daemon->priv->local_rtc) ? "localtime" : "UTC",
+ (fix_system) ? "true" : "false",
+ (interactive) ? "true" : "false" );
+
+ rcl_timedate_daemon_complete_set_local_rtc( object, invocation );
+
+ return TRUE;
+}
+
+
+gboolean handle_set_ntp( RclTimedateDaemon *object,
+ GDBusMethodInvocation *invocation,
+ gboolean use_ntp,
+ gboolean interactive,
+ RclDaemon *daemon )
+{
+ /* check CanNTP (in case NTPD was uninstalled while timedated running) */
+ if( !ntp_daemon_installed() )
+ {
+ daemon->priv->can_ntp = FALSE;
+ rcl_timedate_daemon_set_can_ntp( object, daemon->priv->can_ntp );
+ }
+
+ if( !daemon->priv->can_ntp )
+ {
+ daemon->priv->use_ntp = FALSE;
+ rcl_timedate_daemon_set_ntp( object, daemon->priv->use_ntp );
+ return TRUE;
+ }
+
+ if( !_check_polkit_for_action( daemon, invocation, "set-ntp" ) )
+ {
+ g_debug( "set-ntp: error: '%s'", "User is not privileged" );
+ return TRUE;
+ }
+
+ if( daemon->priv->use_ntp == use_ntp )
+ goto out;
+
+ if( use_ntp ) /* enable and start NTP daemon: */
+ {
+ if( ntp_daemon_enabled() )
+ {
+ if( ntp_daemon_status() )
+ {
+ g_debug( "set-ntp: The NTP Daemon already running" );
+ daemon->priv->use_ntp = TRUE;
+ /* SUCCESS */
+ }
+ else
+ {
+ if( !start_ntp_daemon() )
+ {
+ g_debug( "set-ntp: error: Cannot start NTPD daemon" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "Cannot start NTP Daemon" );
+ daemon->priv->use_ntp = FALSE;
+ /* FAILURE */
+ return TRUE;
+ }
+ else
+ {
+ g_debug( "set-ntp: The NTPD daemon started successful" );
+ daemon->priv->use_ntp = TRUE;
+ /* SUCCESS */
+ }
+ }
+ }
+ else
+ {
+ if( !enable_ntp_daemon() )
+ {
+ g_debug( "set-ntp: error: Cannot enable NTPD daemon" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "Cannot enable NTP Daemon" );
+ daemon->priv->use_ntp = FALSE;
+ /* FAILURE */
+ return TRUE;
+ }
+ else
+ {
+ if( !start_ntp_daemon() )
+ {
+ g_debug( "set-ntp: error: Cannot start NTPD daemon" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "Cannot start NTP Daemon" );
+ daemon->priv->use_ntp = FALSE;
+ /* FAILURE */
+ return TRUE;
+ }
+ else
+ {
+ g_debug( "set-ntp: The NTPD daemon started successful" );
+ daemon->priv->use_ntp = TRUE;
+ /* SUCCESS */
+ }
+ }
+ }
+ }
+ else /* stop and disable NTP daemon: */
+ {
+ if( ntp_daemon_enabled() )
+ {
+ if( ntp_daemon_status() )
+ {
+ /* daemon is running; stop it */
+ if( !stop_ntp_daemon() )
+ {
+ g_debug( "set-ntp: error: Cannot stop NTPD daemon" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "Cannot stop NTP Daemon" );
+ daemon->priv->use_ntp = TRUE;
+ /* FAILURE */
+ return TRUE;
+ }
+ else
+ {
+ g_debug( "set-ntp: The NTPD daemon stopped successful" );
+ if( !disable_ntp_daemon() )
+ {
+ g_debug( "set-ntp: Cannot disable NTPD daemon" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "Cannot disable NTP Daemon" );
+ daemon->priv->use_ntp = FALSE;
+ /* daemon stopped but not disabled (will start after reboot) */
+ /* SUCCESS */
+ }
+ else
+ {
+ g_debug( "set-ntp: The NTPD daemon disabled successful" );
+ daemon->priv->use_ntp = FALSE;
+ /* SUCCESS */
+ }
+ }
+ }
+ else
+ {
+ /* daemon is stopped; disable it */
+ if( !disable_ntp_daemon() )
+ {
+ g_debug( "set-ntp: error: Cannot disable NTPD daemon" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "Cannot disable NTP Daemon" );
+ daemon->priv->use_ntp = FALSE;
+ /* daemon stopped but not disabled (will start after reboot) */
+ /* SUCCESS */
+ }
+ else
+ {
+ g_debug( "set-ntp: The NTPD daemon disabled successful" );
+ daemon->priv->use_ntp = FALSE;
+ /* SUCCESS */
+ }
+ }
+ }
+ else
+ {
+ g_debug( "set-ntp: The NTPD daemon already disabled" );
+ daemon->priv->use_ntp = FALSE;
+ /* SUCCESS */
+ }
+ }
+
+ g_debug( "set-ntp: NTP configured to %s", (daemon->priv->use_ntp) ? "enabled" : "disabled" );
+
+ rcl_timedate_daemon_set_ntp( object, daemon->priv->use_ntp );
+ /* rcl_timedate_daemon_set_ntpsynchronized( object, daemon->priv->use_ntp ); */
+
+out:
+ g_debug( "set-ntp: SetNTP to '%s' returns successful status (interactive=%s)",
+ (daemon->priv->use_ntp) ? "true" : "false",
+ (interactive) ? "true" : "false" );
+
+ rcl_timedate_daemon_complete_set_ntp( object, invocation );
+
+ return TRUE;
+}
+
+
+gboolean handle_set_time( RclTimedateDaemon *object,
+ GDBusMethodInvocation *invocation,
+ gint64 usec_utc,
+ gboolean relative,
+ gboolean interactive,
+ RclDaemon *daemon )
+{
+ struct timespec ts;
+ struct tm tm;
+ guint64 start;
+
+ if( ntp_daemon_installed() && ntp_daemon_enabled() && ntp_daemon_status() )
+ {
+ /* NTP Daemon is running */
+ g_debug( "set-time: error: Automatic time synchronization is enabled" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "set-time: Automatic time synchronization is enabled" );
+ return TRUE;
+ }
+
+ start = now( CLOCK_MONOTONIC );
+
+ if( !relative && usec_utc <= 0 )
+ {
+ g_debug( "set-time: error: Invalid absolute time" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_INVALID_ARGS,
+ "set-time: Invalid absolute time" );
+ return TRUE;
+ }
+
+ if( relative && usec_utc == 0 )
+ {
+ /* Nothing to do */
+ goto out;
+ }
+
+ if( relative )
+ {
+ guint64 n, x;
+
+ n = now( CLOCK_REALTIME );
+ x = n + usec_utc;
+
+ if( (usec_utc > 0 && x < n) ||
+ (usec_utc < 0 && x > n) )
+ {
+ g_debug( "set-time: error: Time value overflow" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_INVALID_ARGS,
+ "set-time: Time value overflow" );
+ return TRUE;
+ }
+ timespec_store( &ts, x );
+ }
+ else
+ {
+ timespec_store( &ts, (guint64)usec_utc);
+ }
+
+ if( !_check_polkit_for_action( daemon, invocation, "set-time" ) )
+ {
+ g_debug( "set-time: error: '%s'", "User is not privileged" );
+ return TRUE;
+ }
+
+ timespec_store( &ts, timespec_load( &ts ) + (now( CLOCK_MONOTONIC ) - start) );
+
+ /* Set system clock */
+ if( clock_settime( CLOCK_REALTIME, &ts ) < 0 )
+ {
+ g_debug( "set-time: error: Failed to set local time" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "set-time: Failed to set local time" );
+ return TRUE;
+ }
+
+ /* Sync down to RTC */
+ localtime_or_gmtime_r( &ts.tv_sec, &tm, !daemon->priv->local_rtc );
+
+ if( !clock_set_hwclock( &tm ) )
+ {
+ g_debug( "set-time: error: Failed to update hardware clock (ignoring)" );
+ }
+
+ g_debug( "set-time: SetTime method returns successful status" );
+
+ /* Update RTCTimeUSec (by the way) */
+ (void)get_rtctime_usec( object );
+
+ rcl_timedate_daemon_set_time_usec( object, timespec_load( &ts ) );
+
+out:
+ g_debug( "set-time: SetTime to %ld returns successful status(relative=%s; interactive=%s)",
+ usec_utc,
+ (relative) ? "true" : "false",
+ (interactive) ? "true" : "false" );
+
+ rcl_timedate_daemon_complete_set_time( object, invocation );
+
+ return TRUE;
+}
+
+
+gboolean handle_list_timezones( RclTimedateDaemon *object,
+ GDBusMethodInvocation *invocation,
+ RclDaemon *daemon )
+{
+ gboolean ret;
+ const gchar *const *zones = { NULL };
+
+ ret = get_timezones( &zones );
+ if( !ret )
+ {
+ g_debug( "list-timezones: error: Failed to read list of time zones" );
+ g_dbus_method_invocation_return_error( invocation,
+ RCL_DAEMON_ERROR,
+ RCL_DAEMON_ERROR_GENERAL,
+ "list-timezones: Failed to read list of time zones" );
+ return TRUE;
+ }
+
+ g_debug( "list-timezones: ListTimesones returns successful status" );
+
+ rcl_timedate_daemon_complete_list_timezones( object, invocation, zones );
+
+ timezones_free( &zones );
+
+ return TRUE;
+}
+
+
+
+/***************************************************************
+ Daemon functions:
+ ================
+ */
+
+void
+rcl_daemon_set_debug( RclDaemon *daemon,
+ gboolean debug )
+{
+ daemon->priv->debug = debug;
+}
+
+gboolean
+rcl_daemon_get_debug( RclDaemon *daemon )
+{
+ return daemon->priv->debug;
+}
+
+
+/***************************************************************
+ rcl_daemon_register_timedate_daemon:
+ */
+static gboolean
+rcl_daemon_register_timedate_daemon( RclDaemon *daemon,
+ GDBusConnection *connection )
+{
+ GError *error = NULL;
+
+ daemon->priv->auth = polkit_authority_get_sync( NULL, &error );
+ if( daemon->priv->auth == NULL )
+ {
+ if( error != NULL )
+ {
+ g_critical ("timedated: error: Cannot get system bus: %s", error->message );
+ g_error_free( error );
+ }
+ return FALSE;
+ }
+
+ /* export our interface on the bus */
+ g_dbus_interface_skeleton_export( G_DBUS_INTERFACE_SKELETON( daemon ),
+ connection,
+ "/org/freedesktop/timedate1",
+ &error );
+
+ if( error != NULL )
+ {
+ g_critical( "timedated: error: Cannot register the daemon on system bus: %s", error->message );
+ g_error_free( error );
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/***************************************************************
+ rcl_daemon_startup:
+ */
+gboolean
+rcl_daemon_startup( RclDaemon *daemon,
+ GDBusConnection *connection )
+{
+ gboolean ret;
+
+ /* register on bus */
+ ret = rcl_daemon_register_timedate_daemon( daemon, connection );
+ if( !ret )
+ {
+ g_warning( "timedated: warning: Failed to register the daemon on bus" );
+ goto out;
+ }
+
+ g_debug( "Daemon now started" );
+
+out:
+ return ret;
+}
+
+/***************************************************************
+ rcl_daemon_shutdown:
+
+ Stop the daemon, release all resources.
+ */
+void
+rcl_daemon_shutdown( RclDaemon *daemon )
+{
+}
+
+
+/***************************************************************
+ rcl_daemon_init:
+ */
+static void
+rcl_daemon_init( RclDaemon *daemon )
+{
+ gboolean rtc = TRUE; /* default */
+ gboolean ntp = FALSE; /* default */
+
+ daemon->priv = rcl_daemon_get_instance_private( daemon );
+
+ /**********************
+ Get current Timezone:
+ */
+ if( !get_system_timezone( (gchar **)&daemon->priv->timezone ) )
+ {
+ daemon->priv->timezone = g_strdup( "Europe/Moscow" );
+ }
+
+ /******************
+ Init Properties:
+ */
+ /* Timezone: */
+ rcl_timedate_daemon_set_daemon_version( RCL_TIMEDATE_DAEMON( daemon ), PACKAGE_VERSION );
+ rcl_timedate_daemon_set_timezone( RCL_TIMEDATE_DAEMON( daemon ), (const gchar *)daemon->priv->timezone );
+
+ /* LocalRTC: */
+ (void)read_data_local_rtc( &rtc );
+ daemon->priv->local_rtc = rtc;
+ rcl_timedate_daemon_set_local_rtc( RCL_TIMEDATE_DAEMON( daemon ), daemon->priv->local_rtc );
+
+ /* CanNTP: */
+ ntp = ntp_daemon_installed();
+ daemon->priv->can_ntp = ntp;
+ rcl_timedate_daemon_set_can_ntp( RCL_TIMEDATE_DAEMON( daemon ), daemon->priv->can_ntp );
+
+ /* NTP: */
+ ntp = ( ntp_daemon_installed() && ntp_daemon_enabled() && ntp_daemon_status() );
+ daemon->priv->use_ntp = ntp;
+ rcl_timedate_daemon_set_ntp( RCL_TIMEDATE_DAEMON( daemon ), daemon->priv->use_ntp );
+
+ /* NTPSynchronized: */
+ rcl_timedate_daemon_set_ntpsynchronized( RCL_TIMEDATE_DAEMON( daemon ), ntp_synchronized() );
+
+
+ /******************
+ Handlers:
+ */
+ g_signal_connect( RCL_TIMEDATE_DAEMON( daemon ),
+ "handle-set-timezone",
+ G_CALLBACK( handle_set_timezone ),
+ daemon ); /* user_data */
+
+ g_signal_connect( RCL_TIMEDATE_DAEMON( daemon ),
+ "handle-set-local-rtc",
+ G_CALLBACK( handle_set_local_rtc ),
+ daemon ); /* user_data */
+
+ g_signal_connect( RCL_TIMEDATE_DAEMON( daemon ),
+ "handle-set-ntp",
+ G_CALLBACK( handle_set_ntp ),
+ daemon ); /* user_data */
+
+ g_signal_connect( RCL_TIMEDATE_DAEMON( daemon ),
+ "handle-set-time",
+ G_CALLBACK( handle_set_time ),
+ daemon ); /* user_data */
+
+ g_signal_connect( RCL_TIMEDATE_DAEMON( daemon ),
+ "handle-list-timezones",
+ G_CALLBACK( handle_list_timezones ),
+ daemon ); /* user_data */
+
+}
+
+
+static const GDBusErrorEntry rcl_daemon_error_entries[] = {
+ { RCL_DAEMON_ERROR_GENERAL, RCL_INTERFACE_PREFIX "GeneralError" },
+ { RCL_DAEMON_ERROR_NOT_PRIVILEGED, RCL_INTERFACE_PREFIX "NotPrivileged" },
+ { RCL_DAEMON_ERROR_INVALID_TIMEZONE_FILE, RCL_INTERFACE_PREFIX "InvalidTimezoneFile" },
+ { RCL_DAEMON_ERROR_INVALID_ARGS, RCL_INTERFACE_PREFIX "InvalidArguments" },
+ { RCL_DAEMON_ERROR_NOT_SUPPORTED, RCL_INTERFACE_PREFIX "NotSupported" },
+};
+
+/***************************************************************
+ rcl_daemon_error_quark:
+ */
+GQuark
+rcl_daemon_error_quark( void )
+{
+ static volatile gsize quark_volatile = 0;
+
+ g_dbus_error_register_error_domain( "rcl_timedated_error",
+ &quark_volatile,
+ rcl_daemon_error_entries,
+ G_N_ELEMENTS( rcl_daemon_error_entries ) );
+ return quark_volatile;
+}
+
+
+/***************************************************************
+ rcl_daemon_finalize:
+ */
+static void
+rcl_daemon_finalize( GObject *object )
+{
+ RclDaemon *daemon = RCL_DAEMON( object );
+
+ g_free( daemon->priv->timezone );
+ g_clear_object( &daemon->priv->auth );
+
+ G_OBJECT_CLASS( rcl_daemon_parent_class)->finalize( object );
+}
+
+/***************************************************************
+ rcl_daemon_class_init:
+ */
+static void
+rcl_daemon_class_init( RclDaemonClass *klass )
+{
+ GObjectClass *object_class = G_OBJECT_CLASS( klass );
+ object_class->finalize = rcl_daemon_finalize;
+}
+
+/***************************************************************
+ rcl_daemon_new:
+ */
+RclDaemon *
+rcl_daemon_new( void )
+{
+ return RCL_DAEMON( g_object_new( RCL_TYPE_DAEMON, NULL ) );
+}