2 Copyright 2020 Robert Nagy
4 Copyright 2012 Alexandre Rostovtsev
6 Some parts are based on the code from the systemd project; these are
7 copyright 2011 Lennart Poettering and others.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <dbus/dbus-protocol.h>
31 #include <glib/gstdio.h>
34 #include "timedated.h"
35 #include "timedate1-generated.h"
40 #define ZONEINFO "/usr/share/zoneinfo"
41 #define SERVICE_NAME "poetteringd timedated"
43 static guint bus_id
= 0;
44 static gboolean read_only
= FALSE
;
46 static PoetteringdTimedatedTimedate1
*timedate1
= NULL
;
48 static GFile
*localtime_file
= NULL
;
50 /* this is loonix only */
51 gboolean local_rtc
= FALSE
;
53 gchar
*timezone_name
= NULL
;
54 G_LOCK_DEFINE_STATIC (clock
);
56 gboolean use_ntp
= FALSE
;
57 G_LOCK_DEFINE_STATIC (ntp
);
60 get_timezone_name (GError
**error
)
63 gchar
*localtime_filename
= NULL
;
66 localtime_filename
= g_file_get_path (localtime_file
);
67 if (realpath(localtime_filename
, res
) == NULL
) {
68 g_set_error (error
, G_FILE_ERROR
, G_FILE_ERROR_FAILED
,
69 "realpath failed on %s", localtime_filename
);
74 ret
= g_strdup(res
+ (strlen(ZONEINFO
) + 1));
76 g_free (localtime_filename
);
81 set_timezone (const gchar
*_timezone_name
,
87 tzpath
= g_build_filename(ZONEINFO
, _timezone_name
, NULL
);
89 g_debug("Setting timezone to %s", tzpath
);
91 if (g_unlink(SYSCONFDIR
"/localtime") == -1)
92 g_warning("Unable to remove localtime");
94 if (g_file_make_symbolic_link(localtime_file
, tzpath
, NULL
, error
))
103 const gchar
*service
= "ntpd";
108 service_started (const gchar
*service
,
111 return rc_cmd(service
, RC_CHECK
);
115 service_disable (const gchar
*service
,
118 if (!rc_cmd(service
, RC_DISABLE
)) {
119 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
,
120 "unable to disable service: %s", service
);
124 if (!rc_cmd(service
, RC_STOP
)) {
125 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
,
126 "unable to stop service: %s", service
);
134 service_enable (const gchar
*service
,
137 if (!rc_cmd(service
, RC_ENABLE
)) {
138 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
,
139 "unable to enable service: %s", service
);
143 if (!rc_cmd(service
, RC_START
)) {
144 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
,
145 "unable to start service: %s", service
);
152 struct invoked_set_time
{
153 GDBusMethodInvocation
*invocation
;
159 on_handle_set_time_authorized_cb (GObject
*source_object
,
164 struct invoked_set_time
*data
= (struct invoked_set_time
*) user_data
;
165 struct timespec ts
= { 0, 0 };
167 data
= (struct invoked_set_time
*) user_data
;
168 if (!check_polkit_finish (res
, &err
)) {
169 g_dbus_method_invocation_return_gerror (data
->invocation
, err
);
174 if (!data
->relative
&& data
->usec_utc
< 0) {
175 g_dbus_method_invocation_return_dbus_error (data
->invocation
, DBUS_ERROR_INVALID_ARGS
, "Attempt to set time before epoch");
179 if (data
->relative
&& clock_gettime (CLOCK_REALTIME
, &ts
)) {
181 g_dbus_method_invocation_return_dbus_error (data
->invocation
, DBUS_ERROR_FAILED
, strerror (errsv
));
185 ts
.tv_sec
+= data
->usec_utc
/ 1000000;
186 ts
.tv_nsec
+= (data
->usec_utc
% 1000000) * 1000;
187 if (clock_settime (CLOCK_REALTIME
, &ts
)) {
189 g_dbus_method_invocation_return_dbus_error (data
->invocation
, DBUS_ERROR_FAILED
, strerror (errsv
));
193 poetteringd_timedated_timedate1_complete_set_time (timedate1
, data
->invocation
);
205 on_handle_set_time (PoetteringdTimedatedTimedate1
*timedate1
,
206 GDBusMethodInvocation
*invocation
,
207 const gint64 usec_utc
,
208 const gboolean relative
,
209 const gboolean user_interaction
,
213 g_dbus_method_invocation_return_dbus_error (invocation
,
214 DBUS_ERROR_NOT_SUPPORTED
,
215 SERVICE_NAME
" is in read-only mode");
217 struct invoked_set_time
*data
;
218 data
= g_new0 (struct invoked_set_time
, 1);
219 data
->invocation
= invocation
;
220 data
->usec_utc
= usec_utc
;
221 data
->relative
= relative
;
222 check_polkit_async (g_dbus_method_invocation_get_sender (invocation
), "org.freedesktop.timedate1.set-time", user_interaction
, on_handle_set_time_authorized_cb
, data
);
228 struct invoked_set_timezone
{
229 GDBusMethodInvocation
*invocation
;
230 gchar
*timezone
; /* newly allocated */
234 on_handle_set_timezone_authorized_cb (GObject
*source_object
,
239 struct invoked_set_timezone
*data
= (struct invoked_set_timezone
*) user_data
;
241 if (!check_polkit_finish (res
, &err
)) {
242 g_dbus_method_invocation_return_gerror (data
->invocation
, err
);
247 if (!set_timezone(data
->timezone
, &err
)) {
248 g_dbus_method_invocation_return_gerror (data
->invocation
, err
);
252 poetteringd_timedated_timedate1_complete_set_timezone (timedate1
, data
->invocation
);
253 g_free (timezone_name
);
254 timezone_name
= data
->timezone
;
255 poetteringd_timedated_timedate1_set_timezone (timedate1
, timezone_name
);
267 on_handle_set_timezone (PoetteringdTimedatedTimedate1
*timedate1
,
268 GDBusMethodInvocation
*invocation
,
269 const gchar
*timezone
,
270 const gboolean user_interaction
,
274 g_dbus_method_invocation_return_dbus_error (invocation
,
275 DBUS_ERROR_NOT_SUPPORTED
,
276 SERVICE_NAME
" is in read-only mode");
278 struct invoked_set_timezone
*data
;
279 data
= g_new0 (struct invoked_set_timezone
, 1);
280 data
->invocation
= invocation
;
281 data
->timezone
= g_strdup (timezone
);
282 check_polkit_async (g_dbus_method_invocation_get_sender (invocation
), "org.freedesktop.timedate1.set-timezone", user_interaction
, on_handle_set_timezone_authorized_cb
, data
);
288 struct invoked_set_local_rtc
{
289 GDBusMethodInvocation
*invocation
;
295 on_handle_set_local_rtc (PoetteringdTimedatedTimedate1
*timedate1
,
296 GDBusMethodInvocation
*invocation
,
297 const gboolean _local_rtc
,
298 const gboolean fix_system
,
299 const gboolean user_interaction
,
302 g_dbus_method_invocation_return_dbus_error (invocation
,
303 DBUS_ERROR_NOT_SUPPORTED
,
304 SERVICE_NAME
" does not support local RTC");
309 struct invoked_set_ntp
{
310 GDBusMethodInvocation
*invocation
;
315 on_handle_set_ntp_authorized_cb (GObject
*source_object
,
320 struct invoked_set_ntp
* data
= (struct invoked_set_ntp
*) user_data
;
322 if (!check_polkit_finish (res
, &err
)) {
323 g_dbus_method_invocation_return_gerror (data
->invocation
, err
);
328 if ((data
->use_ntp
&& !service_enable (ntp_service (), &err
)) ||
329 (!data
->use_ntp
&& !service_disable (ntp_service (), &err
))) {
330 g_dbus_method_invocation_return_gerror (data
->invocation
, err
);
334 poetteringd_timedated_timedate1_complete_set_ntp (timedate1
, data
->invocation
);
335 use_ntp
= data
->use_ntp
;
336 poetteringd_timedated_timedate1_set_ntp (timedate1
, use_ntp
);
348 on_handle_set_ntp (PoetteringdTimedatedTimedate1
*timedate1
,
349 GDBusMethodInvocation
*invocation
,
350 const gboolean _use_ntp
,
351 const gboolean user_interaction
,
355 g_dbus_method_invocation_return_dbus_error (invocation
,
356 DBUS_ERROR_NOT_SUPPORTED
,
357 SERVICE_NAME
" is in read-only mode");
359 struct invoked_set_ntp
*data
;
360 data
= g_new0 (struct invoked_set_ntp
, 1);
361 data
->invocation
= invocation
;
362 data
->use_ntp
= _use_ntp
;
363 check_polkit_async (g_dbus_method_invocation_get_sender (invocation
), "org.freedesktop.timedate1.set-ntp", user_interaction
, on_handle_set_ntp_authorized_cb
, data
);
370 on_bus_acquired (GDBusConnection
*connection
,
371 const gchar
*bus_name
,
376 g_debug ("Acquired a message bus connection");
378 timedate1
= poetteringd_timedated_timedate1_skeleton_new ();
380 poetteringd_timedated_timedate1_set_timezone (timedate1
, timezone_name
);
381 poetteringd_timedated_timedate1_set_local_rtc (timedate1
, local_rtc
);
382 poetteringd_timedated_timedate1_set_ntp (timedate1
, use_ntp
);
384 g_signal_connect (timedate1
, "handle-set-time", G_CALLBACK (on_handle_set_time
), NULL
);
385 g_signal_connect (timedate1
, "handle-set-timezone", G_CALLBACK (on_handle_set_timezone
), NULL
);
386 g_signal_connect (timedate1
, "handle-set-local-rtc", G_CALLBACK (on_handle_set_local_rtc
), NULL
);
387 g_signal_connect (timedate1
, "handle-set-ntp", G_CALLBACK (on_handle_set_ntp
), NULL
);
389 if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (timedate1
),
391 "/org/freedesktop/timedate1",
394 g_critical ("Failed to export interface on /org/freedesktop/timedate1: %s", err
->message
);
395 poetteringd_exit (1);
401 on_name_acquired (GDBusConnection
*connection
,
402 const gchar
*bus_name
,
405 g_debug ("Acquired the name %s", bus_name
);
406 poetteringd_component_started ();
410 on_name_lost (GDBusConnection
*connection
,
411 const gchar
*bus_name
,
414 if (connection
== NULL
)
415 g_critical ("Failed to acquire a dbus connection");
417 g_critical ("Failed to acquire dbus name %s", bus_name
);
418 poetteringd_exit (1);
422 timedated_init (gboolean _read_only
)
426 read_only
= _read_only
;
428 localtime_file
= g_file_new_for_path (SYSCONFDIR
"/localtime");
430 timezone_name
= get_timezone_name (&err
);
432 g_warning ("%s", err
->message
);
433 g_clear_error (&err
);
435 if (ntp_service () == NULL
) {
436 g_warning ("No ntp implementation found.");
439 use_ntp
= service_started (ntp_service (), &err
);
441 g_warning ("%s", err
->message
);
442 g_clear_error (&err
);
446 bus_id
= g_bus_own_name (G_BUS_TYPE_SYSTEM
,
447 "org.freedesktop.timedate1",
448 G_BUS_NAME_OWNER_FLAGS_NONE
,
457 timedated_destroy (void)
459 g_bus_unown_name (bus_id
);
463 g_object_unref (localtime_file
);