~ubuntu-branches/debian/jessie/systemd/jessie

« back to all changes in this revision

Viewing changes to src/timedate/timedated.c

  • Committer: Package Import Robot
  • Author(s): Tollef Fog Heen, Tollef Fog Heen, Michael Biebl
  • Date: 2012-04-03 19:59:17 UTC
  • mfrom: (1.1.10) (6.1.3 experimental)
  • Revision ID: package-import@ubuntu.com-20120403195917-l532urrbg4pkreas
Tags: 44-1
[ Tollef Fog Heen ]
* New upstream version.
  - Backport 3492207: journal: PAGE_SIZE is not known on ppc and other
    archs
  - Backport 5a2a2a1: journal: react with immediate rotation to a couple
    of more errors
  - Backport 693ce21: util: never follow symlinks in rm_rf_children()
    Fixes CVE-2012-1174, closes: #664364
* Drop output message from init-functions hook, it's pointless.
* Only rmdir /lib/init/rw if it exists.
* Explicitly order debian-fixup before sysinit.target to prevent a
  possible race condition with the creation of sockets.  Thanks to
  Michael Biebl for debugging this.
* Always restart the initctl socket on upgrades, to mask sysvinit
  removing it.

[ Michael Biebl ]
* Remove workaround for non-interactive sessions from pam config again.
* Create compat /dev/initctl symlink in case we are upgrading from a system
  running a newer version of sysvinit (using /run/initctl) and sysvinit is
  replaced with systemd-sysv during the upgrade. Closes: #663219
* Install new man pages.
* Build-Depend on valac (>= 0.12) instead of valac-0.12. Closes: #663323

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 
2
 
 
3
/***
 
4
  This file is part of systemd.
 
5
 
 
6
  Copyright 2011 Lennart Poettering
 
7
 
 
8
  systemd is free software; you can redistribute it and/or modify it
 
9
  under the terms of the GNU General Public License as published by
 
10
  the Free Software Foundation; either version 2 of the License, or
 
11
  (at your option) any later version.
 
12
 
 
13
  systemd is distributed in the hope that it will be useful, but
 
14
  WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
16
  General Public License for more details.
 
17
 
 
18
  You should have received a copy of the GNU General Public License
 
19
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 
20
***/
 
21
 
 
22
#include <dbus/dbus.h>
 
23
 
 
24
#include <errno.h>
 
25
#include <string.h>
 
26
#include <unistd.h>
 
27
 
 
28
#include "util.h"
 
29
#include "strv.h"
 
30
#include "dbus-common.h"
 
31
#include "polkit.h"
 
32
#include "def.h"
 
33
 
 
34
#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
 
35
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
 
36
 
 
37
#define INTERFACE                                                       \
 
38
        " <interface name=\"org.freedesktop.timedate1\">\n"             \
 
39
        "  <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n"  \
 
40
        "  <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n"  \
 
41
        "  <property name=\"NTP\" type=\"b\" access=\"read\"/>\n"       \
 
42
        "  <method name=\"SetTime\">\n"                                 \
 
43
        "   <arg name=\"usec_utc\" type=\"x\" direction=\"in\"/>\n"     \
 
44
        "   <arg name=\"relative\" type=\"b\" direction=\"in\"/>\n"     \
 
45
        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
 
46
        "  </method>\n"                                                 \
 
47
        "  <method name=\"SetTimezone\">\n"                             \
 
48
        "   <arg name=\"timezone\" type=\"s\" direction=\"in\"/>\n"     \
 
49
        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
 
50
        "  </method>\n"                                                 \
 
51
        "  <method name=\"SetLocalRTC\">\n"                             \
 
52
        "   <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n"    \
 
53
        "   <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n"   \
 
54
        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
 
55
        "  </method>\n"                                                 \
 
56
        "  <method name=\"SetNTP\">\n"                                  \
 
57
        "   <arg name=\"use_ntp\" type=\"b\" direction=\"in\"/>\n"      \
 
58
        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
 
59
        "  </method>\n"                                                 \
 
60
        " </interface>\n"
 
61
 
 
62
#define INTROSPECTION                                                   \
 
63
        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
 
64
        "<node>\n"                                                      \
 
65
        INTERFACE                                                       \
 
66
        BUS_PROPERTIES_INTERFACE                                        \
 
67
        BUS_INTROSPECTABLE_INTERFACE                                    \
 
68
        BUS_PEER_INTERFACE                                              \
 
69
        "</node>\n"
 
70
 
 
71
#define INTERFACES_LIST                         \
 
72
        BUS_GENERIC_INTERFACES_LIST             \
 
73
        "org.freedesktop.timedate1\0"
 
74
 
 
75
const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
 
76
 
 
77
typedef struct TZ {
 
78
        char *zone;
 
79
        bool local_rtc;
 
80
        int use_ntp;
 
81
} TZ;
 
82
 
 
83
static TZ tz = {
 
84
        .use_ntp = -1,
 
85
};
 
86
 
 
87
static usec_t remain_until;
 
88
 
 
89
static void free_data(void) {
 
90
        free(tz.zone);
 
91
        tz.zone = NULL;
 
92
 
 
93
        tz.local_rtc = false;
 
94
}
 
95
 
 
96
static bool valid_timezone(const char *name) {
 
97
        const char *p;
 
98
        char *t;
 
99
        bool slash = false;
 
100
        int r;
 
101
        struct stat st;
 
102
 
 
103
        assert(name);
 
104
 
 
105
        if (*name == '/' || *name == 0)
 
106
                return false;
 
107
 
 
108
        for (p = name; *p; p++) {
 
109
                if (!(*p >= '0' && *p <= '9') &&
 
110
                    !(*p >= 'a' && *p <= 'z') &&
 
111
                    !(*p >= 'A' && *p <= 'Z') &&
 
112
                    !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
 
113
                        return false;
 
114
 
 
115
                if (*p == '/') {
 
116
 
 
117
                        if (slash)
 
118
                                return false;
 
119
 
 
120
                        slash = true;
 
121
                } else
 
122
                        slash = false;
 
123
        }
 
124
 
 
125
        if (slash)
 
126
                return false;
 
127
 
 
128
        t = strappend("/usr/share/zoneinfo/", name);
 
129
        if (!t)
 
130
                return false;
 
131
 
 
132
        r = stat(t, &st);
 
133
        free(t);
 
134
 
 
135
        if (r < 0)
 
136
                return false;
 
137
 
 
138
        if (!S_ISREG(st.st_mode))
 
139
                return false;
 
140
 
 
141
        return true;
 
142
}
 
143
 
 
144
static void verify_timezone(void) {
 
145
        char *p, *a = NULL, *b = NULL;
 
146
        size_t l, q;
 
147
        int j, k;
 
148
 
 
149
        if (!tz.zone)
 
150
                return;
 
151
 
 
152
        p = strappend("/usr/share/zoneinfo/", tz.zone);
 
153
        if (!p) {
 
154
                log_error("Out of memory");
 
155
                return;
 
156
        }
 
157
 
 
158
        j = read_full_file("/etc/localtime", &a, &l);
 
159
        k = read_full_file(p, &b, &q);
 
160
 
 
161
        free(p);
 
162
 
 
163
        if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) {
 
164
                log_warning("/etc/localtime and /etc/timezone out of sync.");
 
165
                free(tz.zone);
 
166
                tz.zone = NULL;
 
167
        }
 
168
 
 
169
        free(a);
 
170
        free(b);
 
171
}
 
172
 
 
173
static int read_data(void) {
 
174
        int r;
 
175
 
 
176
        free_data();
 
177
 
 
178
        r = read_one_line_file("/etc/timezone", &tz.zone);
 
179
        if (r < 0) {
 
180
                if (r != -ENOENT)
 
181
                        log_warning("Failed to read /etc/timezone: %s", strerror(-r));
 
182
 
 
183
#ifdef TARGET_FEDORA
 
184
                r = parse_env_file("/etc/sysconfig/clock", NEWLINE,
 
185
                                   "ZONE", &tz.zone,
 
186
                                   NULL);
 
187
 
 
188
                if (r < 0 && r != -ENOENT)
 
189
                        log_warning("Failed to read /etc/sysconfig/clock: %s", strerror(-r));
 
190
#endif
 
191
        }
 
192
 
 
193
        if (isempty(tz.zone)) {
 
194
                free(tz.zone);
 
195
                tz.zone = NULL;
 
196
        }
 
197
 
 
198
        verify_timezone();
 
199
 
 
200
        tz.local_rtc = hwclock_is_localtime() > 0;
 
201
 
 
202
        return 0;
 
203
}
 
204
 
 
205
static int write_data_timezone(void) {
 
206
        int r = 0;
 
207
        char *p;
 
208
 
 
209
        if (!tz.zone) {
 
210
                if (unlink("/etc/timezone") < 0 && errno != ENOENT)
 
211
                        r = -errno;
 
212
 
 
213
                if (unlink("/etc/localtime") < 0 && errno != ENOENT)
 
214
                        r = -errno;
 
215
 
 
216
                return r;
 
217
        }
 
218
 
 
219
        p = strappend("/usr/share/zoneinfo/", tz.zone);
 
220
        if (!p) {
 
221
                log_error("Out of memory");
 
222
                return -ENOMEM;
 
223
        }
 
224
 
 
225
        r = symlink_or_copy_atomic(p, "/etc/localtime");
 
226
        free(p);
 
227
 
 
228
        if (r < 0)
 
229
                return r;
 
230
 
 
231
        r = write_one_line_file_atomic("/etc/timezone", tz.zone);
 
232
        if (r < 0)
 
233
                return r;
 
234
 
 
235
        return 0;
 
236
}
 
237
 
 
238
static int write_data_local_rtc(void) {
 
239
        int r;
 
240
        char *s, *w;
 
241
 
 
242
        r = read_full_file("/etc/adjtime", &s, NULL);
 
243
        if (r < 0) {
 
244
                if (r != -ENOENT)
 
245
                        return r;
 
246
 
 
247
                if (!tz.local_rtc)
 
248
                        return 0;
 
249
 
 
250
                w = strdup(NULL_ADJTIME_LOCAL);
 
251
                if (!w)
 
252
                        return -ENOMEM;
 
253
        } else {
 
254
                char *p, *e;
 
255
                size_t a, b;
 
256
 
 
257
                p = strchr(s, '\n');
 
258
                if (!p) {
 
259
                        free(s);
 
260
                        return -EIO;
 
261
                }
 
262
 
 
263
                p = strchr(p+1, '\n');
 
264
                if (!p) {
 
265
                        free(s);
 
266
                        return -EIO;
 
267
                }
 
268
 
 
269
                p++;
 
270
                e = strchr(p, '\n');
 
271
                if (!e) {
 
272
                        free(s);
 
273
                        return -EIO;
 
274
                }
 
275
 
 
276
                a = p - s;
 
277
                b = strlen(e);
 
278
 
 
279
                w = new(char, a + (tz.local_rtc ? 5 : 3) + b + 1);
 
280
                if (!w) {
 
281
                        free(s);
 
282
                        return -ENOMEM;
 
283
                }
 
284
 
 
285
                *(char*) mempcpy(stpcpy(mempcpy(w, s, a), tz.local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
 
286
 
 
287
                if (streq(w, NULL_ADJTIME_UTC)) {
 
288
                        free(w);
 
289
 
 
290
                        if (unlink("/etc/adjtime") < 0) {
 
291
                                if (errno != ENOENT)
 
292
                                        return -errno;
 
293
                        }
 
294
 
 
295
                        return 0;
 
296
                }
 
297
        }
 
298
 
 
299
        r = write_one_line_file_atomic("/etc/adjtime", w);
 
300
        free(w);
 
301
 
 
302
        return r;
 
303
}
 
304
 
 
305
static int read_ntp(DBusConnection *bus) {
 
306
        DBusMessage *m = NULL, *reply = NULL;
 
307
        const char *name = "ntpd.service", *s;
 
308
        DBusError error;
 
309
        int r;
 
310
 
 
311
        assert(bus);
 
312
 
 
313
        dbus_error_init(&error);
 
314
 
 
315
        m = dbus_message_new_method_call(
 
316
                        "org.freedesktop.systemd1",
 
317
                        "/org/freedesktop/systemd1",
 
318
                        "org.freedesktop.systemd1.Manager",
 
319
                        "GetUnitFileState");
 
320
 
 
321
        if (!m) {
 
322
                log_error("Out of memory");
 
323
                r = -ENOMEM;
 
324
                goto finish;
 
325
        }
 
326
 
 
327
        if (!dbus_message_append_args(m,
 
328
                                      DBUS_TYPE_STRING, &name,
 
329
                                      DBUS_TYPE_INVALID)) {
 
330
                log_error("Could not append arguments to message.");
 
331
                r = -ENOMEM;
 
332
                goto finish;
 
333
        }
 
334
 
 
335
        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
 
336
        if (!reply) {
 
337
 
 
338
                if (streq(error.name, "org.freedesktop.DBus.Error.FileNotFound")) {
 
339
                        /* NTP is not installed. */
 
340
                        tz.use_ntp = false;
 
341
                        r = 0;
 
342
                        goto finish;
 
343
                }
 
344
 
 
345
                log_error("Failed to issue method call: %s", bus_error_message(&error));
 
346
                r = -EIO;
 
347
                goto finish;
 
348
        }
 
349
 
 
350
        if (!dbus_message_get_args(reply, &error,
 
351
                                   DBUS_TYPE_STRING, &s,
 
352
                                   DBUS_TYPE_INVALID)) {
 
353
                log_error("Failed to parse reply: %s", bus_error_message(&error));
 
354
                r = -EIO;
 
355
                goto finish;
 
356
        }
 
357
 
 
358
        tz.use_ntp =
 
359
                streq(s, "enabled") ||
 
360
                streq(s, "enabled-runtime");
 
361
        r = 0;
 
362
 
 
363
finish:
 
364
        if (m)
 
365
                dbus_message_unref(m);
 
366
 
 
367
        if (reply)
 
368
                dbus_message_unref(reply);
 
369
 
 
370
        dbus_error_free(&error);
 
371
 
 
372
        return r;
 
373
}
 
374
 
 
375
static int start_ntp(DBusConnection *bus, DBusError *error) {
 
376
        DBusMessage *m = NULL, *reply = NULL;
 
377
        const char *name = "ntpd.service", *mode = "replace";
 
378
        int r;
 
379
 
 
380
        assert(bus);
 
381
        assert(error);
 
382
 
 
383
        m = dbus_message_new_method_call(
 
384
                        "org.freedesktop.systemd1",
 
385
                        "/org/freedesktop/systemd1",
 
386
                        "org.freedesktop.systemd1.Manager",
 
387
                        tz.use_ntp ? "StartUnit" : "StopUnit");
 
388
        if (!m) {
 
389
                log_error("Could not allocate message.");
 
390
                r = -ENOMEM;
 
391
                goto finish;
 
392
        }
 
393
 
 
394
        if (!dbus_message_append_args(m,
 
395
                                      DBUS_TYPE_STRING, &name,
 
396
                                      DBUS_TYPE_STRING, &mode,
 
397
                                      DBUS_TYPE_INVALID)) {
 
398
                log_error("Could not append arguments to message.");
 
399
                r = -ENOMEM;
 
400
                goto finish;
 
401
        }
 
402
 
 
403
        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
 
404
        if (!reply) {
 
405
                log_error("Failed to issue method call: %s", bus_error_message(error));
 
406
                r = -EIO;
 
407
                goto finish;
 
408
        }
 
409
 
 
410
        r = 0;
 
411
 
 
412
finish:
 
413
        if (m)
 
414
                dbus_message_unref(m);
 
415
 
 
416
        if (reply)
 
417
                dbus_message_unref(reply);
 
418
 
 
419
        return r;
 
420
}
 
421
 
 
422
static int enable_ntp(DBusConnection *bus, DBusError *error) {
 
423
        DBusMessage *m = NULL, *reply = NULL;
 
424
        const char * const names[] = { "ntpd.service", NULL };
 
425
        int r;
 
426
        DBusMessageIter iter;
 
427
        dbus_bool_t f = FALSE, t = TRUE;
 
428
 
 
429
        assert(bus);
 
430
        assert(error);
 
431
 
 
432
        m = dbus_message_new_method_call(
 
433
                        "org.freedesktop.systemd1",
 
434
                        "/org/freedesktop/systemd1",
 
435
                        "org.freedesktop.systemd1.Manager",
 
436
                        tz.use_ntp ? "EnableUnitFiles" : "DisableUnitFiles");
 
437
 
 
438
        if (!m) {
 
439
                log_error("Could not allocate message.");
 
440
                r = -ENOMEM;
 
441
                goto finish;
 
442
        }
 
443
 
 
444
        dbus_message_iter_init_append(m, &iter);
 
445
 
 
446
        r = bus_append_strv_iter(&iter, (char**) names);
 
447
        if (r < 0) {
 
448
                log_error("Failed to append unit files.");
 
449
                goto finish;
 
450
        }
 
451
        /* send runtime bool */
 
452
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) {
 
453
                log_error("Failed to append runtime boolean.");
 
454
                r = -ENOMEM;
 
455
                goto finish;
 
456
        }
 
457
 
 
458
        if (tz.use_ntp) {
 
459
                /* send force bool */
 
460
                if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) {
 
461
                        log_error("Failed to append force boolean.");
 
462
                        r = -ENOMEM;
 
463
                        goto finish;
 
464
                }
 
465
        }
 
466
 
 
467
        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
 
468
        if (!reply) {
 
469
                log_error("Failed to issue method call: %s", bus_error_message(error));
 
470
                r = -EIO;
 
471
                goto finish;
 
472
        }
 
473
 
 
474
        dbus_message_unref(m);
 
475
        m = dbus_message_new_method_call(
 
476
                        "org.freedesktop.systemd1",
 
477
                        "/org/freedesktop/systemd1",
 
478
                        "org.freedesktop.systemd1.Manager",
 
479
                        "Reload");
 
480
        if (!m) {
 
481
                log_error("Could not allocate message.");
 
482
                r = -ENOMEM;
 
483
                goto finish;
 
484
        }
 
485
 
 
486
        dbus_message_unref(reply);
 
487
        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
 
488
        if (!reply) {
 
489
                log_error("Failed to issue method call: %s", bus_error_message(error));
 
490
                r = -EIO;
 
491
                goto finish;
 
492
        }
 
493
 
 
494
        r = 0;
 
495
 
 
496
finish:
 
497
        if (m)
 
498
                dbus_message_unref(m);
 
499
 
 
500
        if (reply)
 
501
                dbus_message_unref(reply);
 
502
 
 
503
        return r;
 
504
}
 
505
 
 
506
static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) {
 
507
        dbus_bool_t db;
 
508
 
 
509
        assert(i);
 
510
        assert(property);
 
511
 
 
512
        db = tz.use_ntp > 0;
 
513
 
 
514
        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
 
515
                return -ENOMEM;
 
516
 
 
517
        return 0;
 
518
}
 
519
 
 
520
static const BusProperty bus_timedate_properties[] = {
 
521
        { "Timezone", bus_property_append_string, "s", offsetof(TZ, zone),     true },
 
522
        { "LocalRTC", bus_property_append_bool,   "b", offsetof(TZ, local_rtc) },
 
523
        { "NTP",      property_append_ntp,        "b", offsetof(TZ, use_ntp)   },
 
524
        { NULL, }
 
525
};
 
526
 
 
527
static const BusBoundProperties bps[] = {
 
528
        { "org.freedesktop.timedate1", bus_timedate_properties, &tz },
 
529
        { NULL, }
 
530
};
 
531
 
 
532
static DBusHandlerResult timedate_message_handler(
 
533
                DBusConnection *connection,
 
534
                DBusMessage *message,
 
535
                void *userdata) {
 
536
 
 
537
        DBusMessage *reply = NULL, *changed = NULL;
 
538
        DBusError error;
 
539
        int r;
 
540
 
 
541
        assert(connection);
 
542
        assert(message);
 
543
 
 
544
        dbus_error_init(&error);
 
545
 
 
546
        if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) {
 
547
                const char *z;
 
548
                dbus_bool_t interactive;
 
549
 
 
550
                if (!dbus_message_get_args(
 
551
                                    message,
 
552
                                    &error,
 
553
                                    DBUS_TYPE_STRING, &z,
 
554
                                    DBUS_TYPE_BOOLEAN, &interactive,
 
555
                                    DBUS_TYPE_INVALID))
 
556
                        return bus_send_error_reply(connection, message, &error, -EINVAL);
 
557
 
 
558
                if (!valid_timezone(z))
 
559
                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
 
560
 
 
561
                if (!streq_ptr(z, tz.zone)) {
 
562
                        char *t;
 
563
 
 
564
                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, NULL, &error);
 
565
                        if (r < 0)
 
566
                                return bus_send_error_reply(connection, message, &error, r);
 
567
 
 
568
                        t = strdup(z);
 
569
                        if (!t)
 
570
                                goto oom;
 
571
 
 
572
                        free(tz.zone);
 
573
                        tz.zone = t;
 
574
 
 
575
                        /* 1. Write new configuration file */
 
576
                        r = write_data_timezone();
 
577
                        if (r < 0) {
 
578
                                log_error("Failed to set timezone: %s", strerror(-r));
 
579
                                return bus_send_error_reply(connection, message, NULL, r);
 
580
                        }
 
581
 
 
582
                        if (tz.local_rtc) {
 
583
                                struct timespec ts;
 
584
                                struct tm *tm;
 
585
 
 
586
                                /* 2. Teach kernel new timezone */
 
587
                                hwclock_apply_localtime_delta(NULL);
 
588
 
 
589
                                /* 3. Sync RTC from system clock, with the new delta */
 
590
                                assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
 
591
                                assert_se(tm = localtime(&ts.tv_sec));
 
592
                                hwclock_set_time(tm);
 
593
                        }
 
594
 
 
595
                        log_info("Changed timezone to '%s'.", tz.zone);
 
596
 
 
597
                        changed = bus_properties_changed_new(
 
598
                                        "/org/freedesktop/timedate1",
 
599
                                        "org.freedesktop.timedate1",
 
600
                                        "Timezone\0");
 
601
                        if (!changed)
 
602
                                goto oom;
 
603
                }
 
604
 
 
605
        } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
 
606
                dbus_bool_t lrtc;
 
607
                dbus_bool_t fix_system;
 
608
                dbus_bool_t interactive;
 
609
 
 
610
                if (!dbus_message_get_args(
 
611
                                    message,
 
612
                                    &error,
 
613
                                    DBUS_TYPE_BOOLEAN, &lrtc,
 
614
                                    DBUS_TYPE_BOOLEAN, &fix_system,
 
615
                                    DBUS_TYPE_BOOLEAN, &interactive,
 
616
                                    DBUS_TYPE_INVALID))
 
617
                        return bus_send_error_reply(connection, message, &error, -EINVAL);
 
618
 
 
619
                if (lrtc != tz.local_rtc) {
 
620
                        struct timespec ts;
 
621
 
 
622
                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, NULL, &error);
 
623
                        if (r < 0)
 
624
                                return bus_send_error_reply(connection, message, &error, r);
 
625
 
 
626
                        tz.local_rtc = lrtc;
 
627
 
 
628
                        /* 1. Write new configuration file */
 
629
                        r = write_data_local_rtc();
 
630
                        if (r < 0) {
 
631
                                log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
 
632
                                return bus_send_error_reply(connection, message, NULL, r);
 
633
                        }
 
634
 
 
635
                        /* 2. Teach kernel new timezone */
 
636
                        if (tz.local_rtc)
 
637
                                hwclock_apply_localtime_delta(NULL);
 
638
                        else
 
639
                                hwclock_reset_localtime_delta();
 
640
 
 
641
                        /* 3. Synchronize clocks */
 
642
                        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
 
643
 
 
644
                        if (fix_system) {
 
645
                                struct tm tm;
 
646
 
 
647
                                /* Sync system clock from RTC; first,
 
648
                                 * initialize the timezone fields of
 
649
                                 * struct tm. */
 
650
                                if (tz.local_rtc)
 
651
                                        tm = *localtime(&ts.tv_sec);
 
652
                                else
 
653
                                        tm = *gmtime(&ts.tv_sec);
 
654
 
 
655
                                /* Override the main fields of
 
656
                                 * struct tm, but not the timezone
 
657
                                 * fields */
 
658
                                if (hwclock_get_time(&tm) >= 0) {
 
659
 
 
660
                                        /* And set the system clock
 
661
                                         * with this */
 
662
                                        if (tz.local_rtc)
 
663
                                                ts.tv_sec = mktime(&tm);
 
664
                                        else
 
665
                                                ts.tv_sec = timegm(&tm);
 
666
 
 
667
                                        clock_settime(CLOCK_REALTIME, &ts);
 
668
                                }
 
669
 
 
670
                        } else {
 
671
                                struct tm *tm;
 
672
 
 
673
                                /* Sync RTC from system clock */
 
674
                                if (tz.local_rtc)
 
675
                                        tm = localtime(&ts.tv_sec);
 
676
                                else
 
677
                                        tm = gmtime(&ts.tv_sec);
 
678
 
 
679
                                hwclock_set_time(tm);
 
680
                        }
 
681
 
 
682
                        log_info("RTC configured to %s time.", tz.local_rtc ? "local" : "UTC");
 
683
 
 
684
                        changed = bus_properties_changed_new(
 
685
                                        "/org/freedesktop/timedate1",
 
686
                                        "org.freedesktop.timedate1",
 
687
                                        "LocalRTC\0");
 
688
                        if (!changed)
 
689
                                goto oom;
 
690
                }
 
691
 
 
692
        } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) {
 
693
                int64_t utc;
 
694
                dbus_bool_t relative;
 
695
                dbus_bool_t interactive;
 
696
 
 
697
                if (!dbus_message_get_args(
 
698
                                    message,
 
699
                                    &error,
 
700
                                    DBUS_TYPE_INT64, &utc,
 
701
                                    DBUS_TYPE_BOOLEAN, &relative,
 
702
                                    DBUS_TYPE_BOOLEAN, &interactive,
 
703
                                    DBUS_TYPE_INVALID))
 
704
                        return bus_send_error_reply(connection, message, &error, -EINVAL);
 
705
 
 
706
                if (!relative && utc <= 0)
 
707
                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
 
708
 
 
709
                if (!relative || utc != 0) {
 
710
                        struct timespec ts;
 
711
                        struct tm* tm;
 
712
 
 
713
                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, NULL, &error);
 
714
                        if (r < 0)
 
715
                                return bus_send_error_reply(connection, message, &error, r);
 
716
 
 
717
                        if (relative)
 
718
                                timespec_store(&ts, now(CLOCK_REALTIME) + utc);
 
719
                        else
 
720
                                timespec_store(&ts, utc);
 
721
 
 
722
                        /* Set system clock */
 
723
                        if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
 
724
                                log_error("Failed to set local time: %m");
 
725
                                return bus_send_error_reply(connection, message, NULL, -errno);
 
726
                        }
 
727
 
 
728
                        /* Sync down to RTC */
 
729
                        if (tz.local_rtc)
 
730
                                tm = localtime(&ts.tv_sec);
 
731
                        else
 
732
                                tm = gmtime(&ts.tv_sec);
 
733
 
 
734
                        hwclock_set_time(tm);
 
735
 
 
736
                        log_info("Changed local time to %s", ctime(&ts.tv_sec));
 
737
                }
 
738
        } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) {
 
739
                dbus_bool_t ntp;
 
740
                dbus_bool_t interactive;
 
741
 
 
742
                if (!dbus_message_get_args(
 
743
                                    message,
 
744
                                    &error,
 
745
                                    DBUS_TYPE_BOOLEAN, &ntp,
 
746
                                    DBUS_TYPE_BOOLEAN, &interactive,
 
747
                                    DBUS_TYPE_INVALID))
 
748
                        return bus_send_error_reply(connection, message, &error, -EINVAL);
 
749
 
 
750
                if (ntp != !!tz.use_ntp) {
 
751
 
 
752
                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, NULL, &error);
 
753
                        if (r < 0)
 
754
                                return bus_send_error_reply(connection, message, &error, r);
 
755
 
 
756
                        tz.use_ntp = !!ntp;
 
757
 
 
758
                        r = enable_ntp(connection, &error);
 
759
                        if (r < 0)
 
760
                                return bus_send_error_reply(connection, message, &error, r);
 
761
 
 
762
                        r = start_ntp(connection, &error);
 
763
                        if (r < 0)
 
764
                                return bus_send_error_reply(connection, message, &error, r);
 
765
 
 
766
                        log_info("Set NTP to %s", tz.use_ntp ? "enabled" : "disabled");
 
767
 
 
768
                        changed = bus_properties_changed_new(
 
769
                                        "/org/freedesktop/timedate1",
 
770
                                        "org.freedesktop.timedate1",
 
771
                                        "NTP\0");
 
772
                        if (!changed)
 
773
                                goto oom;
 
774
                }
 
775
 
 
776
        } else
 
777
                return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
 
778
 
 
779
        if (!(reply = dbus_message_new_method_return(message)))
 
780
                goto oom;
 
781
 
 
782
        if (!dbus_connection_send(connection, reply, NULL))
 
783
                goto oom;
 
784
 
 
785
        dbus_message_unref(reply);
 
786
        reply = NULL;
 
787
 
 
788
        if (changed) {
 
789
 
 
790
                if (!dbus_connection_send(connection, changed, NULL))
 
791
                        goto oom;
 
792
 
 
793
                dbus_message_unref(changed);
 
794
        }
 
795
 
 
796
        return DBUS_HANDLER_RESULT_HANDLED;
 
797
 
 
798
oom:
 
799
        if (reply)
 
800
                dbus_message_unref(reply);
 
801
 
 
802
        if (changed)
 
803
                dbus_message_unref(changed);
 
804
 
 
805
        dbus_error_free(&error);
 
806
 
 
807
        return DBUS_HANDLER_RESULT_NEED_MEMORY;
 
808
}
 
809
 
 
810
static int connect_bus(DBusConnection **_bus) {
 
811
        static const DBusObjectPathVTable timedate_vtable = {
 
812
                .message_function = timedate_message_handler
 
813
        };
 
814
        DBusError error;
 
815
        DBusConnection *bus = NULL;
 
816
        int r;
 
817
 
 
818
        assert(_bus);
 
819
 
 
820
        dbus_error_init(&error);
 
821
 
 
822
        bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
 
823
        if (!bus) {
 
824
                log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
 
825
                r = -ECONNREFUSED;
 
826
                goto fail;
 
827
        }
 
828
 
 
829
        dbus_connection_set_exit_on_disconnect(bus, FALSE);
 
830
 
 
831
        if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) ||
 
832
            !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
 
833
                log_error("Not enough memory");
 
834
                r = -ENOMEM;
 
835
                goto fail;
 
836
        }
 
837
 
 
838
        r = dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
 
839
        if (dbus_error_is_set(&error)) {
 
840
                log_error("Failed to register name on bus: %s", bus_error_message(&error));
 
841
                r = -EEXIST;
 
842
                goto fail;
 
843
        }
 
844
 
 
845
        if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)  {
 
846
                log_error("Failed to acquire name.");
 
847
                r = -EEXIST;
 
848
                goto fail;
 
849
        }
 
850
 
 
851
        if (_bus)
 
852
                *_bus = bus;
 
853
 
 
854
        return 0;
 
855
 
 
856
fail:
 
857
        dbus_connection_close(bus);
 
858
        dbus_connection_unref(bus);
 
859
 
 
860
        dbus_error_free(&error);
 
861
 
 
862
        return r;
 
863
}
 
864
 
 
865
int main(int argc, char *argv[]) {
 
866
        int r;
 
867
        DBusConnection *bus = NULL;
 
868
        bool exiting = false;
 
869
 
 
870
        log_set_target(LOG_TARGET_AUTO);
 
871
        log_parse_environment();
 
872
        log_open();
 
873
 
 
874
        umask(0022);
 
875
 
 
876
        if (argc == 2 && streq(argv[1], "--introspect")) {
 
877
                fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
 
878
                      "<node>\n", stdout);
 
879
                fputs(timedate_interface, stdout);
 
880
                fputs("</node>\n", stdout);
 
881
                return 0;
 
882
        }
 
883
 
 
884
        if (argc != 1) {
 
885
                log_error("This program takes no arguments.");
 
886
                r = -EINVAL;
 
887
                goto finish;
 
888
        }
 
889
 
 
890
        r = read_data();
 
891
        if (r < 0) {
 
892
                log_error("Failed to read timezone data: %s", strerror(-r));
 
893
                goto finish;
 
894
        }
 
895
 
 
896
        r = connect_bus(&bus);
 
897
        if (r < 0)
 
898
                goto finish;
 
899
 
 
900
        r = read_ntp(bus);
 
901
        if (r < 0) {
 
902
                log_error("Failed to determine whether NTP is enabled: %s", strerror(-r));
 
903
                goto finish;
 
904
        }
 
905
 
 
906
        remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
 
907
        for (;;) {
 
908
 
 
909
                if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
 
910
                        break;
 
911
 
 
912
                if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
 
913
                        exiting = true;
 
914
                        bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
 
915
                }
 
916
        }
 
917
 
 
918
        r = 0;
 
919
 
 
920
finish:
 
921
        free_data();
 
922
 
 
923
        if (bus) {
 
924
                dbus_connection_flush(bus);
 
925
                dbus_connection_close(bus);
 
926
                dbus_connection_unref(bus);
 
927
        }
 
928
 
 
929
        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 
930
}