From c5f7eedd8605cc228ab72bc3d9c38981d269a14f Mon Sep 17 00:00:00 2001 From: kx Date: Sat, 23 Dec 2023 14:15:03 +0300 Subject: Support the interactive parameter for asynchronous polkit authorization --- README.md | 12 +- src/rcl-timedate.c | 723 ++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 521 insertions(+), 214 deletions(-) diff --git a/README.md b/README.md index 4724bd1..2c5abae 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ This is the replacement of *systemd* service that control the **org.freedesktop. You can find specification at: [**Freedesktop.org**](https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.timedate1.html) -**TimeDate** Daemon does not support interactive parameter which can be used to control -whether *PolKit* should interactively ask the user for authentication credentials if required. -Instead of interactive way users permissions can be set by *PolKit* rules in the -*/usr/share/polkit-1/rules.d/org.freedesktop.timedate1.rules* file. For example, -a system administrator can add Desktop-users into **wheel** group to give them rights -to access the **org.freedesktop.timedate1** *D-Bus interface*. +**TimeDate** Daemon supports interactive parameter which can be used to control +whether *PolKit* should interactively ask the user for authentication credentials +if required. But also, if interactive way is not applicable, users permissions can +be set by *PolKit* rules in the */usr/share/polkit-1/rules.d/org.freedesktop.timedate1.rules* +file. For example, a system administrator can add Desktop-users into **wheel** group +to give them rights to access the **org.freedesktop.timedate1** *D-Bus interface*. ## Requirements: diff --git a/src/rcl-timedate.c b/src/rcl-timedate.c index 7bd2f62..d6387af 100644 --- a/src/rcl-timedate.c +++ b/src/rcl-timedate.c @@ -53,66 +53,156 @@ G_DEFINE_TYPE_WITH_PRIVATE (RclDaemon, rcl_daemon, RCL_TYPE_TIMEDATE_DAEMON_SKEL /*************************************************************** - Polkit functions: - ================ + Polkit data and functions: + ========================= */ +struct check_polkit_data +{ + const gchar *unique_name; + const gchar *action_id; + gboolean user_interaction; + GAsyncReadyCallback callback; + gpointer user_data; + + PolkitAuthority *authority; + PolkitSubject *subject; +}; + +static void +check_polkit_data_free( struct check_polkit_data *data ) +{ + if( data == NULL ) + return; + + if( data->action_id != NULL ) + g_free( (gpointer)data->action_id ); + + if( data->subject != NULL ) + g_object_unref( data->subject ); + if( data->authority != NULL ) + g_object_unref( data->authority ); + + g_free( data ); +} + static gboolean -_check_polkit_for_action( RclDaemon *object, - GDBusMethodInvocation *invocation, - const gchar *function ) +check_polkit_finish( GAsyncResult *result, + GError **error ) { - const gchar *action = g_strjoin( "", RCL_INTERFACE_PREFIX, function, NULL ); - const gchar *sender; - GError *error; - PolkitSubject *subject; - PolkitAuthorizationResult *result; + g_return_val_if_fail( g_task_is_valid( result, NULL ), FALSE ); + + return g_task_propagate_boolean( G_TASK( result ), error ); +} - error = NULL; +static void +_check_polkit_for_action_async( GDBusMethodInvocation *invocation, + const gchar *function, + const gboolean interactive, + GAsyncReadyCallback callback, + gpointer user_data ); - /* 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 ); +static void +_check_polkit_authorization_callback( GObject *source_object, + GAsyncResult *res, + gpointer _data) +{ + struct check_polkit_data *data; + PolkitAuthorizationResult *result; + GTask *task; + GError *error = NULL; - return FALSE; + data = (struct check_polkit_data *)_data; + if( (result = polkit_authority_check_authorization_finish( data->authority, res, &error)) == NULL ) + { + g_task_report_error( NULL, data->callback, data->user_data, NULL, error ); + goto out; } - + 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_task_report_new_error( NULL, + data->callback, + data->user_data, + NULL, + POLKIT_ERROR, + POLKIT_ERROR_NOT_AUTHORIZED, + "Authorization for '%s': user is not authorized", data->action_id ); + goto out; + } + task = g_task_new( NULL, NULL, data->callback, data->user_data ); + g_task_set_source_tag( task, _check_polkit_for_action_async ); + g_task_return_boolean( task, TRUE ); + g_clear_object ( &task ); - g_error_free( error ); - g_object_unref( G_OBJECT(result) ); - g_free( (gpointer)action ); +out: + check_polkit_data_free( data ); + if( result != NULL ) + g_object_unref( result ); +} - return FALSE; +static void +_check_polkit_authority_callback( GObject *source_object, + GAsyncResult *result, + gpointer _data ) +{ + struct check_polkit_data *data; + GError *error = NULL; + + data = (struct check_polkit_data *)_data; + + if( (data->authority = polkit_authority_get_finish( result, &error )) == NULL) + { + g_task_report_error( NULL, data->callback, data->user_data, NULL, error ); + check_polkit_data_free( data ); + return; + } + if( data->unique_name == NULL || + data->action_id == NULL || + (data->subject = polkit_system_bus_name_new( data->unique_name )) == NULL ) + { + g_task_report_new_error( NULL, + data->callback, + data->user_data, + NULL, + POLKIT_ERROR, + POLKIT_ERROR_FAILED, + "Authorization for '%s': failed", data->action_id ); + check_polkit_data_free( data ); + return; } - g_object_unref( G_OBJECT(result) ); - g_free( (gpointer)action ); + polkit_authority_check_authorization( data->authority, + data->subject, + data->action_id, + NULL, + (PolkitCheckAuthorizationFlags)data->user_interaction, + NULL, + _check_polkit_authorization_callback, + data ); +} - return TRUE; +static void +_check_polkit_for_action_async( GDBusMethodInvocation *invocation, + const gchar *function, + const gboolean interactive, + GAsyncReadyCallback callback, + gpointer user_data ) +{ + const gchar *action = g_strjoin( "", RCL_INTERFACE_PREFIX, function, NULL ); + struct check_polkit_data *data; + const gchar *sender; + + sender = g_dbus_method_invocation_get_sender( invocation ); + + data = g_new0( struct check_polkit_data, 1 ); + data->unique_name = sender; + data->action_id = action; + data->user_interaction = interactive; + data->callback = callback; + data->user_data = user_data; + + polkit_authority_get_async( NULL, _check_polkit_authority_callback, data ); } @@ -179,36 +269,53 @@ void rcl_daemon_sync_dbus_properties( RclTimedateDaemon *object ) DBus Handlers: ============= */ -gboolean handle_set_timezone( RclTimedateDaemon *object, - GDBusMethodInvocation *invocation, - const gchar *timezone, - gboolean interactive, - RclDaemon *daemon ) + +/******************************* + SetTimezone: + ----------- + */ +struct set_timezone_data { - gboolean ret = TRUE; + RclTimedateDaemon *object; + GDBusMethodInvocation *invocation; + const gchar *timezone; + gboolean interactive; + RclDaemon *daemon; +}; - 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; - } +static void +set_timezone_data_free( struct set_timezone_data * data ) +{ + if( data == NULL ) + return; - if( g_strcmp0( (const char *)daemon->priv->timezone, (const char *)timezone ) == 0 ) - goto out; + if( data->timezone != NULL ) + g_free( (gpointer)data->timezone ); + + g_free( data ); +} - if( !_check_polkit_for_action( daemon, invocation, "set-timezone" ) ) + +static void +set_timezone_authorized_callback( GObject *source_object, + GAsyncResult *result, + gpointer user_data ) +{ + GError *error = NULL; + struct set_timezone_data *data = (struct set_timezone_data *)user_data; + gboolean ret = TRUE; + + if( !check_polkit_finish( result, &error ) ) { g_debug( "set-timezone: error: '%s'", "User is not privileged" ); - return TRUE; + set_timezone_data_free( data ); + return; } - ret = set_system_timezone( timezone ); + ret = set_system_timezone( data->timezone ); if( ret ) { - if( daemon->priv->local_rtc ) + if( data->daemon->priv->local_rtc ) { struct timespec ts; struct tm tm; @@ -217,12 +324,14 @@ gboolean handle_set_timezone( RclTimedateDaemon *object, if( clock_gettime(CLOCK_REALTIME, &ts) != 0 ) { g_debug( "set-timezone: error: Sync RTC from system clock: '%s'", "clock_gettime(): failed" ); - return TRUE; + set_timezone_data_free( data ); + return; } if( !localtime_r( &ts.tv_sec, &tm ) ) { g_debug( "set-timezone: error: Sync RTC from system clock: '%s'", "localtime_r(): failed" ); - return TRUE; + set_timezone_data_free( data ); + return; } if( !clock_set_hwclock( &tm ) ) { @@ -232,56 +341,115 @@ gboolean handle_set_timezone( RclTimedateDaemon *object, } else { - g_debug( "set-timezone: error: Cannot set system timezone '%s'", timezone ); - return TRUE; + g_debug( "set-timezone: error: Cannot set system timezone '%s'", data->timezone ); + set_timezone_data_free( data ); + return; } - g_free( (gpointer)daemon->priv->timezone ); - daemon->priv->timezone = g_strdup( timezone ); - rcl_timedate_daemon_set_timezone( object, (const gchar *)daemon->priv->timezone ); + g_free( (gpointer)data->daemon->priv->timezone ); + data->daemon->priv->timezone = g_strdup( data->timezone ); + rcl_timedate_daemon_set_timezone( data->object, (const gchar *)data->daemon->priv->timezone ); + -out: g_debug( "set-timezone: SetTimezone to '%s' returns successful status (interactive=%s)", - daemon->priv->timezone, (interactive) ? "true" : "false" ); + data->daemon->priv->timezone, (data->interactive) ? "true" : "false" ); + + rcl_timedate_daemon_complete_set_timezone( data->object, data->invocation ); + + set_timezone_data_free( data ); +} + +gboolean handle_set_timezone( RclTimedateDaemon *object, + GDBusMethodInvocation *invocation, + const gchar *timezone, + gboolean interactive, + RclDaemon *daemon ) +{ + struct set_timezone_data *data; - rcl_timedate_daemon_complete_set_timezone( object, invocation ); + 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; + data = g_new0( struct set_timezone_data, 1 ); + data->object = object; + data->invocation = invocation; + data->timezone = (const gchar *)g_strdup( timezone ); + data->interactive = interactive; + data->daemon = daemon; + + _check_polkit_for_action_async( invocation, + "set-timezone", + interactive, + set_timezone_authorized_callback, + data ); +out: return TRUE; } -gboolean handle_set_local_rtc( RclTimedateDaemon *object, - GDBusMethodInvocation *invocation, - gboolean local_rtc, - gboolean fix_system, - gboolean interactive, - RclDaemon *daemon ) +/******************************* + SetLocalRTC: + ----------- + */ +struct set_local_rtc_data { - struct timespec ts; - gboolean ret = TRUE; + RclTimedateDaemon *object; + GDBusMethodInvocation *invocation; + gboolean local_rtc; + gboolean fix_system; + gboolean interactive; + RclDaemon *daemon; +}; - if( daemon->priv->local_rtc == local_rtc && !fix_system ) - goto out; +static void +set_local_rtc_data_free( struct set_local_rtc_data * data ) +{ + if( data == NULL ) + return; + + g_free( data ); +} - if( !_check_polkit_for_action( daemon, invocation, "set-local-rtc" ) ) +static void +set_local_rtc_authorized_callback( GObject *source_object, + GAsyncResult *result, + gpointer user_data ) +{ + GError *error = NULL; + struct set_local_rtc_data *data = (struct set_local_rtc_data *)user_data; + gboolean ret = TRUE; + struct timespec ts; + + if( !check_polkit_finish( result, &error ) ) { g_debug( "set-local-rtc: error: '%s'", "User is not privileged" ); - return TRUE; + set_local_rtc_data_free( data ); + return; } - if( daemon->priv->local_rtc != local_rtc ) + if( data->daemon->priv->local_rtc != data->local_rtc ) { - daemon->priv->local_rtc = local_rtc; + data->daemon->priv->local_rtc = data->local_rtc; /* Write new configuration files */ - ret = write_data_local_rtc( daemon->priv->local_rtc ); + ret = write_data_local_rtc( data->daemon->priv->local_rtc ); if( !ret ) { - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "Cannot set LocalRTC" ); - return TRUE; + set_local_rtc_data_free( data ); + return; } } @@ -291,22 +459,24 @@ gboolean handle_set_local_rtc( RclTimedateDaemon *object, if( !ret ) { g_debug( "set-local-rtc: error: Cannot set timezone clock after SetLocal_RTC" ); - return TRUE; + set_local_rtc_data_free( data ); + return; } /* 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; + set_local_rtc_data_free( data ); + return; } - if( fix_system ) + if( data->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); + localtime_or_gmtime_r( &ts.tv_sec, &tm, !data->daemon->priv->local_rtc); /* Override the main fields of struct tm, but not the timezone fields */ ret = clock_get_hwclock( &tm ); @@ -317,7 +487,7 @@ gboolean handle_set_local_rtc( RclTimedateDaemon *object, else { /* And set the system clock with this */ - ts.tv_sec = mktime_or_timegm( &tm, !daemon->priv->local_rtc ); + ts.tv_sec = mktime_or_timegm( &tm, !data->daemon->priv->local_rtc ); if( clock_settime( CLOCK_REALTIME, &ts ) < 0 ) { @@ -330,7 +500,7 @@ gboolean handle_set_local_rtc( RclTimedateDaemon *object, struct tm tm; /* Sync RTC from system clock */ - localtime_or_gmtime_r( &ts.tv_sec, &tm, !daemon->priv->local_rtc ); + localtime_or_gmtime_r( &ts.tv_sec, &tm, !data->daemon->priv->local_rtc ); ret = clock_set_hwclock( &tm ); if( !ret ) @@ -339,60 +509,99 @@ gboolean handle_set_local_rtc( RclTimedateDaemon *object, } } - g_debug( "set-local-rtc: RTC configured to %s time", (daemon->priv->local_rtc) ? "localtime" : "UTC" ); + g_debug( "set-local-rtc: RTC configured to %s time", (data->daemon->priv->local_rtc) ? "localtime" : "UTC" ); - rcl_timedate_daemon_set_local_rtc( object, daemon->priv->local_rtc ); + rcl_timedate_daemon_set_local_rtc( data->object, data->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" ); + (data->daemon->priv->local_rtc) ? "localtime" : "UTC", + (data->fix_system) ? "true" : "false", + (data->interactive) ? "true" : "false" ); + + rcl_timedate_daemon_complete_set_local_rtc( data->object, data->invocation ); + + set_local_rtc_data_free( data ); +} + +gboolean handle_set_local_rtc( RclTimedateDaemon *object, + GDBusMethodInvocation *invocation, + gboolean local_rtc, + gboolean fix_system, + gboolean interactive, + RclDaemon *daemon ) +{ + struct set_local_rtc_data *data; - rcl_timedate_daemon_complete_set_local_rtc( object, invocation ); + if( daemon->priv->local_rtc == local_rtc && !fix_system ) + goto out; + data = g_new0( struct set_local_rtc_data, 1 ); + data->object = object; + data->invocation = invocation; + data->local_rtc = local_rtc; + data->fix_system = fix_system; + data->interactive = interactive; + data->daemon = daemon; + + _check_polkit_for_action_async( invocation, + "set-local-rtc", + interactive, + set_local_rtc_authorized_callback, + data ); +out: return TRUE; } -gboolean handle_set_ntp( RclTimedateDaemon *object, - GDBusMethodInvocation *invocation, - gboolean use_ntp, - gboolean interactive, - RclDaemon *daemon ) +/******************************* + SetNTP: + ------ + */ +struct set_ntp_data { - /* 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 ); - } + RclTimedateDaemon *object; + GDBusMethodInvocation *invocation; + gboolean use_ntp; + gboolean interactive; + RclDaemon *daemon; +}; - if( !daemon->priv->can_ntp ) - { - daemon->priv->use_ntp = FALSE; - rcl_timedate_daemon_set_ntp( object, daemon->priv->use_ntp ); - return TRUE; - } +static void +set_ntp_data_free( struct set_ntp_data * data ) +{ + if( data == NULL ) + return; - if( !_check_polkit_for_action( daemon, invocation, "set-ntp" ) ) + g_free( data ); +} + +static void +set_ntp_authorized_callback( GObject *source_object, + GAsyncResult *result, + gpointer user_data ) +{ + GError *error = NULL; + struct set_ntp_data *data = (struct set_ntp_data *)user_data; + + if( !check_polkit_finish( result, &error ) ) { g_debug( "set-ntp: error: '%s'", "User is not privileged" ); - return TRUE; + set_ntp_data_free( data ); + return; } - if( daemon->priv->use_ntp == use_ntp ) + if( data->daemon->priv->use_ntp == data->use_ntp ) goto out; - if( use_ntp ) /* enable and start NTP daemon: */ + if( data->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; + data->daemon->priv->use_ntp = TRUE; /* SUCCESS */ } else @@ -400,18 +609,19 @@ gboolean handle_set_ntp( RclTimedateDaemon *object, if( !start_ntp_daemon() ) { g_debug( "set-ntp: error: Cannot start NTPD daemon" ); - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "Cannot start NTP Daemon" ); - daemon->priv->use_ntp = FALSE; + data->daemon->priv->use_ntp = FALSE; /* FAILURE */ - return TRUE; + set_ntp_data_free( data ); + return; } else { g_debug( "set-ntp: The NTPD daemon started successful" ); - daemon->priv->use_ntp = TRUE; + data->daemon->priv->use_ntp = TRUE; /* SUCCESS */ } } @@ -421,31 +631,33 @@ gboolean handle_set_ntp( RclTimedateDaemon *object, if( !enable_ntp_daemon() ) { g_debug( "set-ntp: error: Cannot enable NTPD daemon" ); - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "Cannot enable NTP Daemon" ); - daemon->priv->use_ntp = FALSE; + data->daemon->priv->use_ntp = FALSE; /* FAILURE */ - return TRUE; + set_ntp_data_free( data ); + return; } else { if( !start_ntp_daemon() ) { g_debug( "set-ntp: error: Cannot start NTPD daemon" ); - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "Cannot start NTP Daemon" ); - daemon->priv->use_ntp = FALSE; + data->daemon->priv->use_ntp = FALSE; /* FAILURE */ - return TRUE; + set_ntp_data_free( data ); + return; } else { g_debug( "set-ntp: The NTPD daemon started successful" ); - daemon->priv->use_ntp = TRUE; + data->daemon->priv->use_ntp = TRUE; /* SUCCESS */ } } @@ -461,13 +673,14 @@ gboolean handle_set_ntp( RclTimedateDaemon *object, if( !stop_ntp_daemon() ) { g_debug( "set-ntp: error: Cannot stop NTPD daemon" ); - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "Cannot stop NTP Daemon" ); - daemon->priv->use_ntp = TRUE; + data->daemon->priv->use_ntp = TRUE; /* FAILURE */ - return TRUE; + set_ntp_data_free( data ); + return; } else { @@ -475,18 +688,18 @@ gboolean handle_set_ntp( RclTimedateDaemon *object, if( !disable_ntp_daemon() ) { g_debug( "set-ntp: Cannot disable NTPD daemon" ); - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "Cannot disable NTP Daemon" ); - daemon->priv->use_ntp = FALSE; + data->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; + data->daemon->priv->use_ntp = FALSE; /* SUCCESS */ } } @@ -497,18 +710,18 @@ gboolean handle_set_ntp( RclTimedateDaemon *object, if( !disable_ntp_daemon() ) { g_debug( "set-ntp: error: Cannot disable NTPD daemon" ); - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "Cannot disable NTP Daemon" ); - daemon->priv->use_ntp = FALSE; + data->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; + data->daemon->priv->use_ntp = FALSE; /* SUCCESS */ } } @@ -516,112 +729,146 @@ gboolean handle_set_ntp( RclTimedateDaemon *object, else { g_debug( "set-ntp: The NTPD daemon already disabled" ); - daemon->priv->use_ntp = FALSE; + data->daemon->priv->use_ntp = FALSE; /* SUCCESS */ } } - g_debug( "set-ntp: NTP configured to %s", (daemon->priv->use_ntp) ? "enabled" : "disabled" ); + g_debug( "set-ntp: NTP configured to %s", (data->daemon->priv->use_ntp) ? "enabled" : "disabled" ); - rcl_timedate_daemon_set_ntp( object, daemon->priv->use_ntp ); + rcl_timedate_daemon_set_ntp( data->object, data->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" ); + (data->daemon->priv->use_ntp) ? "true" : "false", + (data->interactive) ? "true" : "false" ); - rcl_timedate_daemon_complete_set_ntp( object, invocation ); + rcl_timedate_daemon_complete_set_ntp( data->object, data->invocation ); - return TRUE; + set_ntp_data_free( data ); } - -gboolean handle_set_time( RclTimedateDaemon *object, - GDBusMethodInvocation *invocation, - gint64 usec_utc, - gboolean relative, - gboolean interactive, - RclDaemon *daemon ) +gboolean handle_set_ntp( RclTimedateDaemon *object, + GDBusMethodInvocation *invocation, + gboolean use_ntp, + gboolean interactive, + RclDaemon *daemon ) { - struct timespec ts; - struct tm tm; - guint64 start; + struct set_ntp_data *data; - if( ntp_daemon_installed() && ntp_daemon_enabled() && ntp_daemon_status() ) + /* check CanNTP (in case NTPD was uninstalled while timedated running) */ + if( !ntp_daemon_installed() ) { - /* 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; + daemon->priv->can_ntp = FALSE; + rcl_timedate_daemon_set_can_ntp( object, daemon->priv->can_ntp ); } - start = now( CLOCK_MONOTONIC ); - - if( !relative && usec_utc <= 0 ) + if( !daemon->priv->can_ntp ) { - 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" ); + daemon->priv->use_ntp = FALSE; + rcl_timedate_daemon_set_ntp( object, daemon->priv->use_ntp ); return TRUE; } - if( relative && usec_utc == 0 ) + data = g_new0( struct set_ntp_data, 1 ); + data->object = object; + data->invocation = invocation; + data->use_ntp = use_ntp; + data->interactive = interactive; + data->daemon = daemon; + + _check_polkit_for_action_async( invocation, + "set-ntp", + interactive, + set_ntp_authorized_callback, + data ); + return TRUE; +} + + +/******************************* + SetTime: + ------- + */ +struct set_time_data +{ + RclTimedateDaemon *object; + GDBusMethodInvocation *invocation; + guint64 start; + guint64 usec_utc; + gboolean relative; + gboolean interactive; + RclDaemon *daemon; +}; + +static void +set_time_data_free( struct set_time_data * data ) +{ + if( data == NULL ) + return; + + g_free( data ); +} + +static void +set_time_authorized_callback( GObject *source_object, + GAsyncResult *result, + gpointer user_data ) +{ + GError *error = NULL; + struct set_time_data *data = (struct set_time_data *)user_data; + struct timespec ts; + struct tm tm; + + if( !check_polkit_finish( result, &error ) ) { - /* Nothing to do */ - goto out; + g_debug( "set-local-rtc: error: '%s'", "User is not privileged" ); + set_time_data_free( data ); + return; } - if( relative ) + if( data->relative ) { guint64 n, x; n = now( CLOCK_REALTIME ); - x = n + usec_utc; + x = n + data->usec_utc; - if( (usec_utc > 0 && x < n) || - (usec_utc < 0 && x > n) ) + if( (data->usec_utc > 0 && x < n) || + (data->usec_utc < 0 && x > n) ) { g_debug( "set-time: error: Time value overflow" ); - g_dbus_method_invocation_return_error( invocation, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_INVALID_ARGS, "set-time: Time value overflow" ); - return TRUE; + set_time_data_free( data ); + return; } 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, (guint64)data->usec_utc); } - timespec_store( &ts, timespec_load( &ts ) + (now( CLOCK_MONOTONIC ) - start) ); + timespec_store( &ts, timespec_load( &ts ) + (now( CLOCK_MONOTONIC ) - data->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, + g_dbus_method_invocation_return_error( data->invocation, RCL_DAEMON_ERROR, RCL_DAEMON_ERROR_GENERAL, "set-time: Failed to set local time" ); - return TRUE; + set_time_data_free( data ); + return; } /* Sync down to RTC */ - localtime_or_gmtime_r( &ts.tv_sec, &tm, !daemon->priv->local_rtc ); + localtime_or_gmtime_r( &ts.tv_sec, &tm, !data->daemon->priv->local_rtc ); if( !clock_set_hwclock( &tm ) ) { @@ -631,22 +878,83 @@ gboolean handle_set_time( RclTimedateDaemon *object, g_debug( "set-time: SetTime method returns successful status" ); /* Update RTCTimeUSec (by the way) */ - (void)get_rtctime_usec( object ); + (void)get_rtctime_usec( data->object ); - rcl_timedate_daemon_set_time_usec( object, timespec_load( &ts ) ); + rcl_timedate_daemon_set_time_usec( data->object, timespec_load( &ts ) ); -out: g_debug( "set-time: SetTime to %" PRIu64 " returns successful status(relative=%s; interactive=%s)", - usec_utc, - (relative) ? "true" : "false", - (interactive) ? "true" : "false" ); + data->usec_utc, + (data->relative) ? "true" : "false", + (data->interactive) ? "true" : "false" ); - rcl_timedate_daemon_complete_set_time( object, invocation ); + rcl_timedate_daemon_complete_set_time( data->object, data->invocation ); + + set_time_data_free( data ); +} + +gboolean handle_set_time( RclTimedateDaemon *object, + GDBusMethodInvocation *invocation, + gint64 usec_utc, + gboolean relative, + gboolean interactive, + RclDaemon *daemon ) +{ + struct set_time_data *data; + 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; + } + + data = g_new0( struct set_time_data, 1 ); + data->object = object; + data->invocation = invocation; + data->start = start; + data->usec_utc = usec_utc; + data->relative = relative; + data->interactive = interactive; + data->daemon = daemon; + + _check_polkit_for_action_async( invocation, + "set-time", + interactive, + set_time_authorized_callback, + data ); + +out: return TRUE; } +/******************************* + ListTimezones: + ------------- + */ gboolean handle_list_timezones( RclTimedateDaemon *object, GDBusMethodInvocation *invocation, RclDaemon *daemon ) @@ -675,7 +983,6 @@ gboolean handle_list_timezones( RclTimedateDaemon *object, } - /*************************************************************** Daemon functions: ================ -- cgit v1.2.3