remove unused header
[poetteringd.git] / timedated.c
blob3f88f8952d6b5372412341d9984de162d54bf354
1 /*
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
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
29 #include <dbus/dbus-protocol.h>
30 #include <glib.h>
31 #include <glib/gstdio.h>
32 #include <gio/gio.h>
34 #include "timedated.h"
35 #include "timedate1-generated.h"
36 #include "main.h"
37 #include "rc.h"
38 #include "utils.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);
59 static gchar *
60 get_timezone_name (GError **error)
62 gchar *ret = NULL;
63 gchar *localtime_filename = NULL;
64 gchar res[PATH_MAX];
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);
70 ret = g_strdup("");
71 goto out;
74 ret = g_strdup(res + (strlen(ZONEINFO) + 1));
75 out:
76 g_free (localtime_filename);
77 return ret;
80 static gboolean
81 set_timezone (const gchar *_timezone_name,
82 GError **error)
84 gboolean ret = FALSE;
85 gchar *tzpath;
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))
95 ret = TRUE;
97 return ret;
100 static const gchar *
101 ntp_service ()
103 const gchar *service = "ntpd";
104 return service;
107 static gboolean
108 service_started (const gchar *service,
109 GError **error)
111 return rc_cmd(service, RC_CHECK);
114 static gboolean
115 service_disable (const gchar *service,
116 GError **error)
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);
121 return FALSE;
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);
127 return FALSE;
130 return TRUE;
133 static gboolean
134 service_enable (const gchar *service,
135 GError **error)
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);
140 return FALSE;
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);
146 return FALSE;
149 return TRUE;
152 struct invoked_set_time {
153 GDBusMethodInvocation *invocation;
154 gint64 usec_utc;
155 gboolean relative;
158 static void
159 on_handle_set_time_authorized_cb (GObject *source_object,
160 GAsyncResult *res,
161 gpointer user_data)
163 GError *err = NULL;
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);
170 goto out;
173 G_LOCK (clock);
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");
176 goto unlock;
179 if (data->relative && clock_gettime (CLOCK_REALTIME, &ts)) {
180 int errsv = errno;
181 g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_FAILED, strerror (errsv));
182 goto unlock;
185 ts.tv_sec += data->usec_utc / 1000000;
186 ts.tv_nsec += (data->usec_utc % 1000000) * 1000;
187 if (clock_settime (CLOCK_REALTIME, &ts)) {
188 int errsv = errno;
189 g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_FAILED, strerror (errsv));
190 goto unlock;
193 poetteringd_timedated_timedate1_complete_set_time (timedate1, data->invocation);
195 unlock:
196 G_UNLOCK (clock);
198 out:
199 g_free (data);
200 if (err != NULL)
201 g_error_free (err);
204 static gboolean
205 on_handle_set_time (PoetteringdTimedatedTimedate1 *timedate1,
206 GDBusMethodInvocation *invocation,
207 const gint64 usec_utc,
208 const gboolean relative,
209 const gboolean user_interaction,
210 gpointer user_data)
212 if (read_only)
213 g_dbus_method_invocation_return_dbus_error (invocation,
214 DBUS_ERROR_NOT_SUPPORTED,
215 SERVICE_NAME " is in read-only mode");
216 else {
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);
225 return TRUE;
228 struct invoked_set_timezone {
229 GDBusMethodInvocation *invocation;
230 gchar *timezone; /* newly allocated */
233 static void
234 on_handle_set_timezone_authorized_cb (GObject *source_object,
235 GAsyncResult *res,
236 gpointer user_data)
238 GError *err = NULL;
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);
243 goto out;
246 G_LOCK (clock);
247 if (!set_timezone(data->timezone, &err)) {
248 g_dbus_method_invocation_return_gerror (data->invocation, err);
249 goto unlock;
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);
257 unlock:
258 G_UNLOCK (clock);
260 out:
261 g_free (data);
262 if (err != NULL)
263 g_error_free (err);
266 static gboolean
267 on_handle_set_timezone (PoetteringdTimedatedTimedate1 *timedate1,
268 GDBusMethodInvocation *invocation,
269 const gchar *timezone,
270 const gboolean user_interaction,
271 gpointer user_data)
273 if (read_only)
274 g_dbus_method_invocation_return_dbus_error (invocation,
275 DBUS_ERROR_NOT_SUPPORTED,
276 SERVICE_NAME " is in read-only mode");
277 else {
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);
285 return TRUE;
288 struct invoked_set_local_rtc {
289 GDBusMethodInvocation *invocation;
290 gboolean local_rtc;
291 gboolean fix_system;
294 static gboolean
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,
300 gpointer user_data)
302 g_dbus_method_invocation_return_dbus_error (invocation,
303 DBUS_ERROR_NOT_SUPPORTED,
304 SERVICE_NAME " does not support local RTC");
306 return TRUE;
309 struct invoked_set_ntp {
310 GDBusMethodInvocation *invocation;
311 gboolean use_ntp;
314 static void
315 on_handle_set_ntp_authorized_cb (GObject *source_object,
316 GAsyncResult *res,
317 gpointer user_data)
319 GError *err = NULL;
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);
324 goto out;
327 G_LOCK (ntp);
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);
331 goto unlock;
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);
338 unlock:
339 G_UNLOCK (ntp);
341 out:
342 g_free (data);
343 if (err != NULL)
344 g_error_free (err);
347 static gboolean
348 on_handle_set_ntp (PoetteringdTimedatedTimedate1 *timedate1,
349 GDBusMethodInvocation *invocation,
350 const gboolean _use_ntp,
351 const gboolean user_interaction,
352 gpointer user_data)
354 if (read_only)
355 g_dbus_method_invocation_return_dbus_error (invocation,
356 DBUS_ERROR_NOT_SUPPORTED,
357 SERVICE_NAME " is in read-only mode");
358 else {
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);
366 return TRUE;
369 static void
370 on_bus_acquired (GDBusConnection *connection,
371 const gchar *bus_name,
372 gpointer user_data)
374 GError *err = NULL;
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),
390 connection,
391 "/org/freedesktop/timedate1",
392 &err)) {
393 if (err != NULL) {
394 g_critical ("Failed to export interface on /org/freedesktop/timedate1: %s", err->message);
395 poetteringd_exit (1);
400 static void
401 on_name_acquired (GDBusConnection *connection,
402 const gchar *bus_name,
403 gpointer user_data)
405 g_debug ("Acquired the name %s", bus_name);
406 poetteringd_component_started ();
409 static void
410 on_name_lost (GDBusConnection *connection,
411 const gchar *bus_name,
412 gpointer user_data)
414 if (connection == NULL)
415 g_critical ("Failed to acquire a dbus connection");
416 else
417 g_critical ("Failed to acquire dbus name %s", bus_name);
418 poetteringd_exit (1);
421 void
422 timedated_init (gboolean _read_only)
424 GError *err = NULL;
426 read_only = _read_only;
428 localtime_file = g_file_new_for_path (SYSCONFDIR "/localtime");
430 timezone_name = get_timezone_name (&err);
431 if (err != NULL) {
432 g_warning ("%s", err->message);
433 g_clear_error (&err);
435 if (ntp_service () == NULL) {
436 g_warning ("No ntp implementation found.");
437 use_ntp = FALSE;
438 } else {
439 use_ntp = service_started (ntp_service (), &err);
440 if (err != NULL) {
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,
449 on_bus_acquired,
450 on_name_acquired,
451 on_name_lost,
452 NULL,
453 NULL);
456 void
457 timedated_destroy (void)
459 g_bus_unown_name (bus_id);
460 bus_id = 0;
461 read_only = FALSE;
463 g_object_unref (localtime_file);