1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2007 David Zeuthen <david@fubar.dk>
4 * Copyright (C) 2012-2021 MATE Developers
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <sys/wait.h>
32 #include <errno.h>
33 #include <sys/time.h>
34
35 #include <glib.h>
36 #include <glib-object.h>
37
38 #include <dbus/dbus-glib.h>
39 #include <dbus/dbus-glib-lowlevel.h>
40
41 #include <polkit/polkit.h>
42
43 #include "system-timezone.h"
44
45 #include "msd-datetime-mechanism.h"
46 #include "msd-datetime-mechanism-glue.h"
47
48 static gboolean
do_exit(gpointer user_data)49 do_exit (gpointer user_data)
50 {
51 g_debug ("Exiting due to inactivity");
52 exit (1);
53 return FALSE;
54 }
55
56 static void
reset_killtimer(void)57 reset_killtimer (void)
58 {
59 static guint timer_id = 0;
60
61 if (timer_id > 0) {
62 g_source_remove (timer_id);
63 }
64 g_debug ("Setting killtimer to 30 seconds...");
65 timer_id = g_timeout_add_seconds (30, do_exit, NULL);
66 }
67
68 struct MsdDatetimeMechanismPrivate
69 {
70 DBusGConnection *system_bus_connection;
71 DBusGProxy *system_bus_proxy;
72 PolkitAuthority *auth;
73 };
74
75 static void msd_datetime_mechanism_finalize (GObject *object);
76
G_DEFINE_TYPE_WITH_PRIVATE(MsdDatetimeMechanism,msd_datetime_mechanism,G_TYPE_OBJECT)77 G_DEFINE_TYPE_WITH_PRIVATE (MsdDatetimeMechanism, msd_datetime_mechanism, G_TYPE_OBJECT)
78
79 GQuark
80 msd_datetime_mechanism_error_quark (void)
81 {
82 static GQuark ret = 0;
83
84 if (ret == 0) {
85 ret = g_quark_from_static_string ("msd_datetime_mechanism_error");
86 }
87
88 return ret;
89 }
90
91
92 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
93
94 GType
msd_datetime_mechanism_error_get_type(void)95 msd_datetime_mechanism_error_get_type (void)
96 {
97 static GType etype = 0;
98
99 if (etype == 0)
100 {
101 static const GEnumValue values[] =
102 {
103 ENUM_ENTRY (MSD_DATETIME_MECHANISM_ERROR_GENERAL, "GeneralError"),
104 ENUM_ENTRY (MSD_DATETIME_MECHANISM_ERROR_NOT_PRIVILEGED, "NotPrivileged"),
105 ENUM_ENTRY (MSD_DATETIME_MECHANISM_ERROR_INVALID_TIMEZONE_FILE, "InvalidTimezoneFile"),
106 { 0, 0, 0 }
107 };
108
109 g_assert (MSD_DATETIME_MECHANISM_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
110
111 etype = g_enum_register_static ("MsdDatetimeMechanismError", values);
112 }
113
114 return etype;
115 }
116
117
118 static GObject *
msd_datetime_mechanism_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)119 msd_datetime_mechanism_constructor (GType type,
120 guint n_construct_properties,
121 GObjectConstructParam *construct_properties)
122 {
123 MsdDatetimeMechanism *mechanism;
124
125 mechanism = MSD_DATETIME_MECHANISM (G_OBJECT_CLASS (msd_datetime_mechanism_parent_class)->constructor (
126 type,
127 n_construct_properties,
128 construct_properties));
129
130 return G_OBJECT (mechanism);
131 }
132
133 static void
msd_datetime_mechanism_class_init(MsdDatetimeMechanismClass * klass)134 msd_datetime_mechanism_class_init (MsdDatetimeMechanismClass *klass)
135 {
136 GObjectClass *object_class = G_OBJECT_CLASS (klass);
137
138 object_class->constructor = msd_datetime_mechanism_constructor;
139 object_class->finalize = msd_datetime_mechanism_finalize;
140
141 dbus_g_object_type_install_info (MSD_DATETIME_TYPE_MECHANISM, &dbus_glib_msd_datetime_mechanism_object_info);
142
143 dbus_g_error_domain_register (MSD_DATETIME_MECHANISM_ERROR, NULL, MSD_DATETIME_MECHANISM_TYPE_ERROR);
144
145 }
146
147 static void
msd_datetime_mechanism_init(MsdDatetimeMechanism * mechanism)148 msd_datetime_mechanism_init (MsdDatetimeMechanism *mechanism)
149 {
150 mechanism->priv = msd_datetime_mechanism_get_instance_private (mechanism);
151
152 }
153
154 static void
msd_datetime_mechanism_finalize(GObject * object)155 msd_datetime_mechanism_finalize (GObject *object)
156 {
157 MsdDatetimeMechanism *mechanism;
158
159 g_return_if_fail (object != NULL);
160 g_return_if_fail (MSD_DATETIME_IS_MECHANISM (object));
161
162 mechanism = MSD_DATETIME_MECHANISM (object);
163
164 g_return_if_fail (mechanism->priv != NULL);
165
166 g_object_unref (mechanism->priv->system_bus_proxy);
167
168 G_OBJECT_CLASS (msd_datetime_mechanism_parent_class)->finalize (object);
169 }
170
171 static gboolean
register_mechanism(MsdDatetimeMechanism * mechanism)172 register_mechanism (MsdDatetimeMechanism *mechanism)
173 {
174 GError *error = NULL;
175
176 mechanism->priv->auth = polkit_authority_get_sync (NULL, &error);
177 if (mechanism->priv->auth == NULL) {
178 if (error != NULL) {
179 g_critical ("error getting system bus: %s", error->message);
180 g_error_free (error);
181 }
182 goto error;
183 }
184
185 mechanism->priv->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
186 if (mechanism->priv->system_bus_connection == NULL) {
187 if (error != NULL) {
188 g_critical ("error getting system bus: %s", error->message);
189 g_error_free (error);
190 }
191 goto error;
192 }
193
194 dbus_g_connection_register_g_object (mechanism->priv->system_bus_connection, "/",
195 G_OBJECT (mechanism));
196
197 mechanism->priv->system_bus_proxy = dbus_g_proxy_new_for_name (mechanism->priv->system_bus_connection,
198 DBUS_SERVICE_DBUS,
199 DBUS_PATH_DBUS,
200 DBUS_INTERFACE_DBUS);
201
202 reset_killtimer ();
203
204 return TRUE;
205
206 error:
207 return FALSE;
208 }
209
210
211 MsdDatetimeMechanism *
msd_datetime_mechanism_new(void)212 msd_datetime_mechanism_new (void)
213 {
214 GObject *object;
215 gboolean res;
216
217 object = g_object_new (MSD_DATETIME_TYPE_MECHANISM, NULL);
218
219 res = register_mechanism (MSD_DATETIME_MECHANISM (object));
220 if (! res) {
221 g_object_unref (object);
222 return NULL;
223 }
224
225 return MSD_DATETIME_MECHANISM (object);
226 }
227
228 static gboolean
_check_polkit_for_action(MsdDatetimeMechanism * mechanism,DBusGMethodInvocation * context,const char * action)229 _check_polkit_for_action (MsdDatetimeMechanism *mechanism, DBusGMethodInvocation *context, const char *action)
230 {
231 const char *sender;
232 GError *error;
233 PolkitSubject *subject;
234 PolkitAuthorizationResult *result;
235
236 error = NULL;
237
238 /* Check that caller is privileged */
239 sender = dbus_g_method_get_sender (context);
240 subject = polkit_system_bus_name_new (sender);
241
242 result = polkit_authority_check_authorization_sync (mechanism->priv->auth,
243 subject,
244 action,
245 NULL,
246 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
247 NULL, &error);
248 g_object_unref (subject);
249
250 if (error) {
251 dbus_g_method_return_error (context, error);
252 g_error_free (error);
253
254 return FALSE;
255 }
256
257 if (!polkit_authorization_result_get_is_authorized (result)) {
258 error = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
259 MSD_DATETIME_MECHANISM_ERROR_NOT_PRIVILEGED,
260 "Not Authorized for action %s", action);
261 dbus_g_method_return_error (context, error);
262 g_error_free (error);
263 g_object_unref (result);
264
265 return FALSE;
266 }
267
268 g_object_unref (result);
269
270 return TRUE;
271 }
272
273
274 static gboolean
_set_time(MsdDatetimeMechanism * mechanism,const struct timeval * tv,DBusGMethodInvocation * context)275 _set_time (MsdDatetimeMechanism *mechanism,
276 const struct timeval *tv,
277 DBusGMethodInvocation *context)
278 {
279 GError *error;
280
281 if (!_check_polkit_for_action (mechanism, context, "org.mate.settingsdaemon.datetimemechanism.settime"))
282 return FALSE;
283
284 if (settimeofday (tv, NULL) != 0) {
285 error = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
286 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
287 "Error calling settimeofday({%ld,%ld}): %s",
288 (gint64) tv->tv_sec, (gint64) tv->tv_usec,
289 strerror (errno));
290 dbus_g_method_return_error (context, error);
291 g_error_free (error);
292 return FALSE;
293 }
294
295 if (g_file_test ("/sbin/hwclock",
296 G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_EXECUTABLE)) {
297 int exit_status;
298 if (!g_spawn_command_line_sync ("/sbin/hwclock --systohc", NULL, NULL, &exit_status, &error)) {
299 GError *error2;
300 error2 = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
301 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
302 "Error spawning /sbin/hwclock: %s", error->message);
303 g_error_free (error);
304 dbus_g_method_return_error (context, error2);
305 g_error_free (error2);
306 return FALSE;
307 }
308 if (WEXITSTATUS (exit_status) != 0) {
309 error = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
310 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
311 "/sbin/hwclock returned %d", exit_status);
312 dbus_g_method_return_error (context, error);
313 g_error_free (error);
314 return FALSE;
315 }
316 }
317
318 dbus_g_method_return (context);
319 return TRUE;
320 }
321
322 static gboolean
_rh_update_etc_sysconfig_clock(DBusGMethodInvocation * context,const char * key,const char * value)323 _rh_update_etc_sysconfig_clock (DBusGMethodInvocation *context, const char *key, const char *value)
324 {
325 /* On Red Hat / Fedora, the /etc/sysconfig/clock file needs to be kept in sync */
326 if (g_file_test ("/etc/sysconfig/clock", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
327 char **lines;
328 int n;
329 gboolean replaced;
330 char *data;
331 gsize len;
332 GError *error;
333
334 error = NULL;
335
336 if (!g_file_get_contents ("/etc/sysconfig/clock", &data, &len, &error)) {
337 GError *error2;
338 error2 = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
339 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
340 "Error reading /etc/sysconfig/clock file: %s", error->message);
341 g_error_free (error);
342 dbus_g_method_return_error (context, error2);
343 g_error_free (error2);
344 return FALSE;
345 }
346 replaced = FALSE;
347 lines = g_strsplit (data, "\n", 0);
348 g_free (data);
349
350 for (n = 0; lines[n] != NULL; n++) {
351 if (g_str_has_prefix (lines[n], key)) {
352 g_free (lines[n]);
353 lines[n] = g_strdup_printf ("%s%s", key, value);
354 replaced = TRUE;
355 }
356 }
357 if (replaced) {
358 GString *str;
359
360 str = g_string_new (NULL);
361 for (n = 0; lines[n] != NULL; n++) {
362 g_string_append (str, lines[n]);
363 if (lines[n + 1] != NULL)
364 g_string_append_c (str, '\n');
365 }
366 data = g_string_free (str, FALSE);
367 len = strlen (data);
368 if (!g_file_set_contents ("/etc/sysconfig/clock", data, len, &error)) {
369 GError *error2;
370 error2 = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
371 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
372 "Error updating /etc/sysconfig/clock: %s", error->message);
373 g_error_free (error);
374 dbus_g_method_return_error (context, error2);
375 g_error_free (error2);
376 g_free (data);
377 return FALSE;
378 }
379 g_free (data);
380 }
381 g_strfreev (lines);
382 }
383
384 return TRUE;
385 }
386
387 /* exported methods */
388
389 gboolean
msd_datetime_mechanism_set_time(MsdDatetimeMechanism * mechanism,gint64 seconds_since_epoch,DBusGMethodInvocation * context)390 msd_datetime_mechanism_set_time (MsdDatetimeMechanism *mechanism,
391 gint64 seconds_since_epoch,
392 DBusGMethodInvocation *context)
393 {
394 struct timeval tv;
395
396 reset_killtimer ();
397 g_debug ("SetTime(%ld) called", seconds_since_epoch);
398
399 tv.tv_sec = (time_t) seconds_since_epoch;
400 tv.tv_usec = 0;
401 return _set_time (mechanism, &tv, context);
402 }
403
404 gboolean
msd_datetime_mechanism_adjust_time(MsdDatetimeMechanism * mechanism,gint64 seconds_to_add,DBusGMethodInvocation * context)405 msd_datetime_mechanism_adjust_time (MsdDatetimeMechanism *mechanism,
406 gint64 seconds_to_add,
407 DBusGMethodInvocation *context)
408 {
409 struct timeval tv;
410
411 reset_killtimer ();
412 g_debug ("AdjustTime(%ld) called", seconds_to_add);
413
414 if (gettimeofday (&tv, NULL) != 0) {
415 GError *error;
416 error = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
417 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
418 "Error calling gettimeofday(): %s", strerror (errno));
419 dbus_g_method_return_error (context, error);
420 g_error_free (error);
421 return FALSE;
422 }
423
424 tv.tv_sec += (time_t) seconds_to_add;
425 return _set_time (mechanism, &tv, context);
426 }
427
428
429 gboolean
msd_datetime_mechanism_set_timezone(MsdDatetimeMechanism * mechanism,const char * zone_file,DBusGMethodInvocation * context)430 msd_datetime_mechanism_set_timezone (MsdDatetimeMechanism *mechanism,
431 const char *zone_file,
432 DBusGMethodInvocation *context)
433 {
434 GError *error;
435
436 reset_killtimer ();
437 g_debug ("SetTimezone('%s') called", zone_file);
438
439 if (!_check_polkit_for_action (mechanism, context, "org.mate.settingsdaemon.datetimemechanism.settimezone"))
440 return FALSE;
441
442 error = NULL;
443
444 if (!system_timezone_set_from_file (zone_file, &error)) {
445 GError *error2;
446 int code;
447
448 if (error->code == SYSTEM_TIMEZONE_ERROR_INVALID_TIMEZONE_FILE)
449 code = MSD_DATETIME_MECHANISM_ERROR_INVALID_TIMEZONE_FILE;
450 else
451 code = MSD_DATETIME_MECHANISM_ERROR_GENERAL;
452
453 error2 = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
454 code, "%s", error->message);
455
456 g_error_free (error);
457
458 dbus_g_method_return_error (context, error2);
459 g_error_free (error2);
460
461 return FALSE;
462 }
463
464 dbus_g_method_return (context);
465 return TRUE;
466 }
467
468
469 gboolean
msd_datetime_mechanism_get_timezone(MsdDatetimeMechanism * mechism,DBusGMethodInvocation * context)470 msd_datetime_mechanism_get_timezone (MsdDatetimeMechanism *mechism,
471 DBusGMethodInvocation *context)
472 {
473 gchar *timezone;
474
475 reset_killtimer ();
476
477 timezone = system_timezone_find ();
478
479 dbus_g_method_return (context, timezone);
480
481 return TRUE;
482 }
483
484 gboolean
msd_datetime_mechanism_get_hardware_clock_using_utc(MsdDatetimeMechanism * mechanism,DBusGMethodInvocation * context)485 msd_datetime_mechanism_get_hardware_clock_using_utc (MsdDatetimeMechanism *mechanism,
486 DBusGMethodInvocation *context)
487 {
488 char **lines;
489 char *data;
490 gsize len;
491 GError *error;
492 gboolean is_utc;
493
494 error = NULL;
495
496 if (!g_file_get_contents ("/etc/adjtime", &data, &len, &error)) {
497 GError *error2;
498 error2 = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
499 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
500 "Error reading /etc/adjtime file: %s", error->message);
501 g_error_free (error);
502 dbus_g_method_return_error (context, error2);
503 g_error_free (error2);
504 return FALSE;
505 }
506
507 lines = g_strsplit (data, "\n", 0);
508 g_free (data);
509
510 if (g_strv_length (lines) < 3) {
511 error = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
512 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
513 "Cannot parse /etc/adjtime");
514 dbus_g_method_return_error (context, error);
515 g_error_free (error);
516 g_strfreev (lines);
517 return FALSE;
518 }
519
520 if (strcmp (lines[2], "UTC") == 0) {
521 is_utc = TRUE;
522 } else if (strcmp (lines[2], "LOCAL") == 0) {
523 is_utc = FALSE;
524 } else {
525 error = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
526 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
527 "Expected UTC or LOCAL at line 3 of /etc/adjtime; found '%s'", lines[2]);
528 dbus_g_method_return_error (context, error);
529 g_error_free (error);
530 g_strfreev (lines);
531 return FALSE;
532 }
533 g_strfreev (lines);
534 dbus_g_method_return (context, is_utc);
535 return TRUE;
536 }
537
538 gboolean
msd_datetime_mechanism_set_hardware_clock_using_utc(MsdDatetimeMechanism * mechanism,gboolean using_utc,DBusGMethodInvocation * context)539 msd_datetime_mechanism_set_hardware_clock_using_utc (MsdDatetimeMechanism *mechanism,
540 gboolean using_utc,
541 DBusGMethodInvocation *context)
542 {
543 GError *error;
544
545 error = NULL;
546
547 if (!_check_polkit_for_action (mechanism, context,
548 "org.mate.settingsdaemon.datetimemechanism.configurehwclock"))
549 return FALSE;
550
551 if (g_file_test ("/sbin/hwclock",
552 G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_EXECUTABLE)) {
553 int exit_status;
554 char *cmd;
555 cmd = g_strdup_printf ("/sbin/hwclock %s --systohc", using_utc ? "--utc" : "--localtime");
556 if (!g_spawn_command_line_sync (cmd, NULL, NULL, &exit_status, &error)) {
557 GError *error2;
558 error2 = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
559 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
560 "Error spawning /sbin/hwclock: %s", error->message);
561 g_error_free (error);
562 dbus_g_method_return_error (context, error2);
563 g_error_free (error2);
564 g_free (cmd);
565 return FALSE;
566 }
567 g_free (cmd);
568 if (WEXITSTATUS (exit_status) != 0) {
569 error = g_error_new (MSD_DATETIME_MECHANISM_ERROR,
570 MSD_DATETIME_MECHANISM_ERROR_GENERAL,
571 "/sbin/hwclock returned %d", exit_status);
572 dbus_g_method_return_error (context, error);
573 g_error_free (error);
574 return FALSE;
575 }
576
577 if (!_rh_update_etc_sysconfig_clock (context, "UTC=", using_utc ? "true" : "false"))
578 return FALSE;
579
580 }
581 dbus_g_method_return (context);
582 return TRUE;
583 }
584
585 static void
check_can_do(MsdDatetimeMechanism * mechanism,const char * action,DBusGMethodInvocation * context)586 check_can_do (MsdDatetimeMechanism *mechanism,
587 const char *action,
588 DBusGMethodInvocation *context)
589 {
590 const char *sender;
591 PolkitSubject *subject;
592 PolkitAuthorizationResult *result;
593 GError *error;
594
595 /* Check that caller is privileged */
596 sender = dbus_g_method_get_sender (context);
597 subject = polkit_system_bus_name_new (sender);
598
599 error = NULL;
600 result = polkit_authority_check_authorization_sync (mechanism->priv->auth,
601 subject,
602 action,
603 NULL,
604 0,
605 NULL,
606 &error);
607 g_object_unref (subject);
608
609 if (error) {
610 dbus_g_method_return_error (context, error);
611 g_error_free (error);
612 return;
613 }
614
615 if (polkit_authorization_result_get_is_authorized (result)) {
616 dbus_g_method_return (context, 2);
617 }
618 else if (polkit_authorization_result_get_is_challenge (result)) {
619 dbus_g_method_return (context, 1);
620 }
621 else {
622 dbus_g_method_return (context, 0);
623 }
624
625 g_object_unref (result);
626 }
627
628
629 gboolean
msd_datetime_mechanism_can_set_time(MsdDatetimeMechanism * mechanism,DBusGMethodInvocation * context)630 msd_datetime_mechanism_can_set_time (MsdDatetimeMechanism *mechanism,
631 DBusGMethodInvocation *context)
632 {
633 check_can_do (mechanism,
634 "org.mate.settingsdaemon.datetimemechanism.settime",
635 context);
636
637 return TRUE;
638 }
639
640 gboolean
msd_datetime_mechanism_can_set_timezone(MsdDatetimeMechanism * mechanism,DBusGMethodInvocation * context)641 msd_datetime_mechanism_can_set_timezone (MsdDatetimeMechanism *mechanism,
642 DBusGMethodInvocation *context)
643 {
644 check_can_do (mechanism,
645 "org.mate.settingsdaemon.datetimemechanism.settimezone",
646 context);
647
648 return TRUE;
649 }
650