public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/openrc-settingsd:master commit in: src/, /, src/copypaste/, data/
@ 2012-09-05 16:54 Alexandre Restovtsev
  0 siblings, 0 replies; only message in thread
From: Alexandre Restovtsev @ 2012-09-05 16:54 UTC (permalink / raw
  To: gentoo-commits

commit:     37d9aeb4da8169c85ef5e96eefd4b0da30dcb83a
Author:     Alexandre Rostovtsev <tetromino <AT> gentoo <DOT> org>
AuthorDate: Wed Sep  5 16:48:04 2012 +0000
Commit:     Alexandre Restovtsev <tetromino <AT> gmail <DOT> com>
CommitDate: Wed Sep  5 16:51:29 2012 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/openrc-settingsd.git;a=commit;h=37d9aeb4

Add timedated implementation

Includes a chunk of copy-pasted code from systemd for dealing with RTC.
Should probably be replaced with something more glib-ish eventually.

---
 .gitignore              |    1 +
 Makefile.am             |   31 ++-
 TODO                    |    2 +-
 configure.ac            |    2 +
 data/timedate1.xml      |   26 ++
 src/copypaste/hwclock.c |  193 +++++++++++++
 src/copypaste/hwclock.h |   32 ++
 src/copypaste/macro.h   |  186 ++++++++++++
 src/copypaste/util.c    |  279 ++++++++++++++++++
 src/copypaste/util.h    |  110 +++++++
 src/main.c              |    6 +
 src/timedated.c         |  729 +++++++++++++++++++++++++++++++++++++++++++++++
 src/timedated.h         |   31 ++
 13 files changed, 1625 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8fae933..d96a32a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@
 /stamp-h1
 /src/hostname1-generated.[ch]
 /src/locale1-generated.[ch]
+/src/timedate1-generated.[ch]
 
 # Relative rules
 *.o

diff --git a/Makefile.am b/Makefile.am
index b0694bf..32890fb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,6 +3,7 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 EXTRA_DIST = \
 	data/hostname1.xml \
 	data/locale1.xml \
+	data/timedate1.xml \
 	$(NULL)
 
 pkgdata_DATA = \
@@ -12,6 +13,7 @@ pkgdata_DATA = \
 AM_CPPFLAGS = \
 	-include $(top_builddir)/config.h \
 	-DSYSCONFDIR=\""$(sysconfdir)"\" \
+	-DDATADIR=\""$(datadir)"\" \
 	-DLIBEXECDIR=\""$(libexecdir)"\" \
 	-DPKGDATADIR=\""$(pkgdatadir)"\" \
 	$(OPENRC_SETTINGSD_CFLAGS) \
@@ -33,13 +35,30 @@ localed_built_sources = \
 	src/locale1-generated.h \
 	$(NULL)
 
+timedated_built_sources = \
+	src/timedate1-generated.c \
+	src/timedate1-generated.h \
+	$(NULL)
+
+copypaste_sources = \
+	src/copypaste/hwclock.c \
+	src/copypaste/hwclock.h \
+	src/copypaste/macro.h \
+	src/copypaste/util.c \
+	src/copypaste/util.h \
+	$(NULL)
+
 openrc_settingsd_SOURCES = \
 	$(hostnamed_built_sources) \
 	$(localed_built_sources) \
+	$(timedated_built_sources) \
+	$(copypaste_sources) \
 	src/hostnamed.c \
 	src/hostnamed.h \
 	src/localed.c \
 	src/localed.h \
+	src/timedated.c \
+	src/timedated.h \
 	src/bus-utils.c \
 	src/bus-utils.h \
 	src/shell-utils.c \
@@ -63,5 +82,13 @@ $(localed_built_sources) : data/locale1.xml
 	$(srcdir)/data/locale1.xml; \
 	mv locale1-generated.{c,h} $(top_srcdir)/src/ )
 
-BUILT_SOURCES = $(hostnamed_built_sources) $(localed_built_sources)
-CLEANFILES = $(hostnamed_built_sources) $(localed_built_sources)
+$(timedated_built_sources) : data/timedate1.xml
+	( $(GDBUS_CODEGEN) \
+	--interface-prefix org.freedesktop. \
+	--c-namespace OpenrcSettingsdTimedated \
+	--generate-c-code timedate1-generated \
+	$(srcdir)/data/timedate1.xml; \
+	mv timedate1-generated.{c,h} $(top_srcdir)/src/ )
+
+BUILT_SOURCES = $(hostnamed_built_sources) $(localed_built_sources) $(timedated_built_sources)
+CLEANFILES = $(hostnamed_built_sources) $(localed_built_sources) $(timedated_built_sources)

diff --git a/TODO b/TODO
index d75749a..ff32ee0 100644
--- a/TODO
+++ b/TODO
@@ -9,7 +9,7 @@ document that this case is not supported?
 Write an init.d file to read RC_SYS so that we can piggyback on openrc's
 built-in virtualization detection.
 
-Implement timedated.
+De-systemd-ize hwclock.c
 
 Do something about runtime dependency on systemd's org.freedesktop.hostname1.policy,
 org.freedesktop.locale1.policy, and org.freedesktop.timedate1.policy files.

diff --git a/configure.ac b/configure.ac
index 9e64fea..4e2e9ac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,6 +10,8 @@ LT_INIT([disable-static pic-only])
 AC_PREFIX_DEFAULT([/usr])
 
 AC_PROG_MKDIR_P
+AC_SEARCH_LIBS([rc_service_exists], [rc], [], [AC_MSG_ERROR([OpenRC's librc not found])])
+AC_SEARCH_LIBS([clock_gettime], [rt], [], [AC_MSG_ERROR([librt not found])])
 PKG_CHECK_MODULES(OPENRC_SETTINGSD, [gio-unix-2.0 >= 2.30 gio-2.0 >= 2.30 glib-2.0 >= 2.30 dbus-1 polkit-gobject-1])
 AC_SUBST(OPENRC_SETTINGSD_CFLAGS)
 AC_SUBST(OPENRC_SETTINGSD_LIBS)

diff --git a/data/timedate1.xml b/data/timedate1.xml
new file mode 100644
index 0000000..18aa6d6
--- /dev/null
+++ b/data/timedate1.xml
@@ -0,0 +1,26 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/freedesktop/timedate1">
+    <interface name="org.freedesktop.timedate1">
+        <method name="SetTime">
+            <arg direction="in" type="x" name="usec_utc"/>
+            <arg direction="in" type="b" name="relative"/>
+            <arg direction="in" type="b" name="user_interaction"/>
+        </method>
+        <method name="SetTimezone">
+            <arg direction="in" type="s" name="timezone"/>
+            <arg direction="in" type="b" name="user_interaction"/>
+        </method>
+        <method name="SetLocalRTC">
+            <arg direction="in" type="b" name="local_rtc"/>
+            <arg direction="in" type="b" name="fix_system"/>
+            <arg direction="in" type="b" name="user_interaction"/>
+        </method>
+        <method name="SetNTP">
+            <arg direction="in" type="b" name="use_ntp"/>
+            <arg direction="in" type="b" name="user_interaction"/>
+        </method>
+        <property name="Timezone" type="s" access="read"/>
+        <property name="LocalRTC" type="b" access="read"/>
+        <property name="NTP" type="b" access="read"/>
+    </interface>
+</node>

diff --git a/src/copypaste/hwclock.c b/src/copypaste/hwclock.c
new file mode 100644
index 0000000..ddf206d
--- /dev/null
+++ b/src/copypaste/hwclock.c
@@ -0,0 +1,193 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/time.h>
+#include <linux/rtc.h>
+
+#include "macro.h"
+#include "util.h"
+#include "hwclock.h"
+
+static int rtc_open(int flags) {
+        int fd;
+        DIR *d;
+
+        /* First, we try to make use of the /dev/rtc symlink. If that
+         * doesn't exist, we open the first RTC which has hctosys=1
+         * set. If we don't find any we just take the first RTC that
+         * exists at all. */
+
+        fd = open("/dev/rtc", flags);
+        if (fd >= 0)
+                return fd;
+
+        d = opendir("/sys/class/rtc");
+        if (!d)
+                goto fallback;
+
+        for (;;) {
+                char *p, *v;
+                struct dirent buf, *de;
+                int r;
+
+                r = readdir_r(d, &buf, &de);
+                if (r != 0)
+                        goto fallback;
+
+                if (!de)
+                        goto fallback;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                p = strjoin("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
+                if (!p) {
+                        closedir(d);
+                        return -ENOMEM;
+                }
+
+                r = read_one_line_file(p, &v);
+                free(p);
+
+                if (r < 0)
+                        continue;
+
+                r = parse_boolean(v);
+                free(v);
+
+                if (r <= 0)
+                        continue;
+
+                p = strappend("/dev/", de->d_name);
+                fd = open(p, flags);
+                free(p);
+
+                if (fd >= 0) {
+                        closedir(d);
+                        return fd;
+                }
+        }
+
+fallback:
+        if (d)
+                closedir(d);
+
+        fd = open("/dev/rtc0", flags);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
+int hwclock_get_time(struct tm *tm) {
+        int fd;
+        int err = 0;
+
+        assert(tm);
+
+        fd = rtc_open(O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        /* This leaves the timezone fields of struct tm
+         * uninitialized! */
+        if (ioctl(fd, RTC_RD_TIME, tm) < 0)
+                err = -errno;
+
+        /* We don't know daylight saving, so we reset this in order not
+         * to confused mktime(). */
+        tm->tm_isdst = -1;
+
+        close_nointr_nofail(fd);
+
+        return err;
+}
+
+int hwclock_set_time(const struct tm *tm) {
+        int fd;
+        int err = 0;
+
+        assert(tm);
+
+        fd = rtc_open(O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (ioctl(fd, RTC_SET_TIME, tm) < 0)
+                err = -errno;
+
+        close_nointr_nofail(fd);
+
+        return err;
+}
+
+int hwclock_apply_localtime_delta(int *min) {
+        const struct timeval *tv_null = NULL;
+        struct timespec ts;
+        struct tm *tm;
+        int minuteswest;
+        struct timezone tz;
+
+        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+        assert_se(tm = localtime(&ts.tv_sec));
+        minuteswest = tm->tm_gmtoff / 60;
+
+        tz.tz_minuteswest = -minuteswest;
+        tz.tz_dsttime = 0; /* DST_NONE*/
+
+        /*
+         * If the hardware clock does not run in UTC, but in local time:
+         * The very first time we set the kernel's timezone, it will warp
+         * the clock so that it runs in UTC instead of local time.
+         */
+        if (settimeofday(tv_null, &tz) < 0)
+                return -errno;
+        if (min)
+                *min = minuteswest;
+        return 0;
+}
+
+int hwclock_reset_localtime_delta(void) {
+        const struct timeval *tv_null = NULL;
+        struct timezone tz;
+
+        tz.tz_minuteswest = 0;
+        tz.tz_dsttime = 0; /* DST_NONE*/
+
+        if (settimeofday(tv_null, &tz) < 0)
+                return -errno;
+
+        return 0;
+}

diff --git a/src/copypaste/hwclock.h b/src/copypaste/hwclock.h
new file mode 100644
index 0000000..1092e53
--- /dev/null
+++ b/src/copypaste/hwclock.h
@@ -0,0 +1,32 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foohwclockhfoo
+#define foohwclockhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <time.h>
+
+int hwclock_apply_localtime_delta(int *min);
+int hwclock_reset_localtime_delta(void);
+int hwclock_get_time(struct tm *tm);
+int hwclock_set_time(const struct tm *tm);
+
+#endif

diff --git a/src/copypaste/macro.h b/src/copypaste/macro.h
new file mode 100644
index 0000000..e41b5a7
--- /dev/null
+++ b/src/copypaste/macro.h
@@ -0,0 +1,186 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <glib.h>
+
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <inttypes.h>
+
+#define _printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+#define _sentinel_ __attribute__ ((sentinel))
+#define _noreturn_ __attribute__((noreturn))
+#define _unused_ __attribute__ ((unused))
+#define _destructor_ __attribute__ ((destructor))
+#define _pure_ __attribute__ ((pure))
+#define _const_ __attribute__ ((const))
+#define _deprecated_ __attribute__ ((deprecated))
+#define _packed_ __attribute__ ((packed))
+#define _malloc_ __attribute__ ((malloc))
+#define _weak_ __attribute__ ((weak))
+#define _likely_(x) (__builtin_expect(!!(x),1))
+#define _unlikely_(x) (__builtin_expect(!!(x),0))
+#define _public_ __attribute__ ((visibility("default")))
+#define _hidden_ __attribute__ ((visibility("hidden")))
+#define _weakref_(x) __attribute__((weakref(#x)))
+#define _introspect_(x) __attribute__((section("introspect." x)))
+#define _alignas_(x) __attribute__((aligned(__alignof(x))))
+
+#define XSTRINGIFY(x) #x
+#define STRINGIFY(x) XSTRINGIFY(x)
+
+/* Rounds up */
+#define ALIGN(l) ALIGN_TO((l), sizeof(void*))
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        return ((l + ali - 1) & ~(ali - 1));
+}
+
+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+/*
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#ifndef MAX
+#define MAX(a,b)                                \
+        __extension__ ({                        \
+                        typeof(a) _a = (a);     \
+                        typeof(b) _b = (b);     \
+                        _a > _b ? _a : _b;      \
+                })
+#endif
+
+#define MAX3(a,b,c)                             \
+        MAX(MAX(a,b),c)
+
+#ifndef MIN
+#define MIN(a,b)                                \
+        __extension__ ({                        \
+                        typeof(a) _a = (a);     \
+                        typeof(b) _b = (b);     \
+                        _a < _b ? _a : _b;      \
+                })
+#endif
+
+#define MIN3(a,b,c)                             \
+        MIN(MIN(a,b),c)
+
+#define assert_se(expr)                                                 \
+        do {                                                            \
+                if (_unlikely_(!(expr)))                                \
+/*                      log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); */ \
+                        g_error ("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+        } while (false)                                                 \
+
+/* We override the glibc assert() here. */
+#undef assert
+#ifdef NDEBUG
+#define assert(expr) do {} while(false)
+#else
+#define assert(expr) assert_se(expr)
+#endif
+
+#define assert_not_reached(t)                                           \
+        do {                                                            \
+/*              log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); */ \
+                g_error ("Code should not be reached '%s' at %s:%u, function %s(). Aborting.", t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+        } while (false)
+
+#define assert_cc(expr)                            \
+        do {                                       \
+                switch (0) {                       \
+                        case 0:                    \
+                        case !!(expr):             \
+                                ;                  \
+                }                                  \
+        } while (false)
+
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p)))
+#define ULONG_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define INT32_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
+#define LONG_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define memzero(x,l) (memset((x), 0, (l)))
+#define zero(x) (memzero(&(x), sizeof(x)))
+
+#define char_array_0(x) x[sizeof(x)-1] = 0;
+
+#define IOVEC_SET_STRING(i, s)                  \
+        do {                                    \
+                struct iovec *_i = &(i);        \
+                char *_s = (char *)(s);         \
+                _i->iov_base = _s;              \
+                _i->iov_len = strlen(_s);       \
+        } while(false)
+
+static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
+        unsigned j;
+        size_t r = 0;
+
+        for (j = 0; j < n; j++)
+                r += i[j].iov_len;
+
+        return r;
+}
+
+static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
+        unsigned j;
+
+        for (j = 0; j < n; j++) {
+                size_t sub;
+
+                if (_unlikely_(k <= 0))
+                        break;
+
+                sub = MIN(i[j].iov_len, k);
+                i[j].iov_len -= sub;
+                i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
+                k -= sub;
+        }
+
+        return k;
+}
+
+/* #include "log.h" */

diff --git a/src/copypaste/util.c b/src/copypaste/util.c
new file mode 100644
index 0000000..5375921
--- /dev/null
+++ b/src/copypaste/util.c
@@ -0,0 +1,279 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <linux/sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <netinet/ip.h>
+#include <linux/kd.h>
+#include <dlfcn.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <glob.h>
+#include <grp.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+
+#include "macro.h"
+#include "util.h"
+
+int saved_argc = 0;
+char **saved_argv = NULL;
+
+usec_t now(clockid_t clock_id) {
+        struct timespec ts;
+
+        assert_se(clock_gettime(clock_id, &ts) == 0);
+
+        return timespec_load(&ts);
+}
+
+usec_t timespec_load(const struct timespec *ts) {
+        assert(ts);
+
+        return
+                (usec_t) ts->tv_sec * USEC_PER_SEC +
+                (usec_t) ts->tv_nsec / NSEC_PER_USEC;
+}
+
+bool endswith(const char *s, const char *postfix) {
+        size_t sl, pl;
+
+        assert(s);
+        assert(postfix);
+
+        sl = strlen(s);
+        pl = strlen(postfix);
+
+        if (pl == 0)
+                return true;
+
+        if (sl < pl)
+                return false;
+
+        return memcmp(s + sl - pl, postfix, pl) == 0;
+}
+
+int close_nointr(int fd) {
+        assert(fd >= 0);
+
+        for (;;) {
+                int r;
+
+                r = close(fd);
+                if (r >= 0)
+                        return r;
+
+                if (errno != EINTR)
+                        return -errno;
+        }
+}
+
+void close_nointr_nofail(int fd) {
+        int saved_errno = errno;
+
+        /* like close_nointr() but cannot fail, and guarantees errno
+         * is unchanged */
+
+        assert_se(close_nointr(fd) == 0);
+
+        errno = saved_errno;
+}
+
+int parse_boolean(const char *v) {
+        assert(v);
+
+        if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+                return 1;
+        else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+                return 0;
+
+        return -EINVAL;
+}
+
+int read_one_line_file(const char *fn, char **line) {
+        FILE *f;
+        int r;
+        char t[LINE_MAX], *c;
+
+        assert(fn);
+        assert(line);
+
+        f = fopen(fn, "re");
+        if (!f)
+                return -errno;
+
+        if (!fgets(t, sizeof(t), f)) {
+
+                if (ferror(f)) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                t[0] = 0;
+        }
+
+        c = strdup(t);
+        if (!c) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        truncate_nl(c);
+
+        *line = c;
+        r = 0;
+
+finish:
+        fclose(f);
+        return r;
+}
+
+char *truncate_nl(char *s) {
+        assert(s);
+
+        s[strcspn(s, NEWLINE)] = 0;
+        return s;
+}
+
+char *strnappend(const char *s, const char *suffix, size_t b) {
+        size_t a;
+        char *r;
+
+        if (!s && !suffix)
+                return strdup("");
+
+        if (!s)
+                return strndup(suffix, b);
+
+        if (!suffix)
+                return strdup(s);
+
+        assert(s);
+        assert(suffix);
+
+        a = strlen(s);
+
+        if (!(r = new(char, a+b+1)))
+                return NULL;
+
+        memcpy(r, s, a);
+        memcpy(r+a, suffix, b);
+        r[a+b] = 0;
+
+        return r;
+}
+
+char *strappend(const char *s, const char *suffix) {
+        return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
+bool ignore_file(const char *filename) {
+        assert(filename);
+
+        return
+                filename[0] == '.' ||
+                streq(filename, "lost+found") ||
+                streq(filename, "aquota.user") ||
+                streq(filename, "aquota.group") ||
+                endswith(filename, "~") ||
+                endswith(filename, ".rpmnew") ||
+                endswith(filename, ".rpmsave") ||
+                endswith(filename, ".rpmorig") ||
+                endswith(filename, ".dpkg-old") ||
+                endswith(filename, ".dpkg-new") ||
+                endswith(filename, ".swp");
+}
+
+char *strjoin(const char *x, ...) {
+        va_list ap;
+        size_t l;
+        char *r, *p;
+
+        va_start(ap, x);
+
+        if (x) {
+                l = strlen(x);
+
+                for (;;) {
+                        const char *t;
+
+                        t = va_arg(ap, const char *);
+                        if (!t)
+                                break;
+
+                        l += strlen(t);
+                }
+        } else
+                l = 0;
+
+        va_end(ap);
+
+        r = new(char, l+1);
+        if (!r)
+                return NULL;
+
+        if (x) {
+                p = stpcpy(r, x);
+
+                va_start(ap, x);
+
+                for (;;) {
+                        const char *t;
+
+                        t = va_arg(ap, const char *);
+                        if (!t)
+                                break;
+
+                        p = stpcpy(p, t);
+                }
+
+                va_end(ap);
+        } else
+                r[0] = 0;
+
+        return r;
+}

diff --git a/src/copypaste/util.h b/src/copypaste/util.h
new file mode 100644
index 0000000..c3b8dcf
--- /dev/null
+++ b/src/copypaste/util.h
@@ -0,0 +1,110 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sched.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/resource.h>
+
+#include "macro.h"
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+typedef struct dual_timestamp {
+        usec_t realtime;
+        usec_t monotonic;
+} dual_timestamp;
+
+#define MSEC_PER_SEC  1000ULL
+#define USEC_PER_SEC  1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define NSEC_PER_SEC  1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC)
+#define NSEC_PER_MINUTE (60ULL*NSEC_PER_SEC)
+#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
+#define NSEC_PER_HOUR (60ULL*NSEC_PER_MINUTE)
+#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
+#define NSEC_PER_DAY (24ULL*NSEC_PER_HOUR)
+#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
+#define NSEC_PER_WEEK (7ULL*NSEC_PER_DAY)
+#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
+#define NSEC_PER_MONTH (2629800ULL*NSEC_PER_SEC)
+#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC)
+#define NSEC_PER_YEAR (31557600ULL*NSEC_PER_SEC)
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n\r"
+#define NEWLINE "\n\r"
+#define QUOTES "\"\'"
+#define COMMENTS "#;\n"
+
+usec_t now(clockid_t clock);
+
+usec_t timespec_load(const struct timespec *ts);
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+
+#define new(t, n) ((t*) malloc(sizeof(t)*(n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+#define newdup(t, p, n) ((t*) memdup(p, sizeof(t)*(n)))
+
+#define malloc0(n) (calloc((n), 1))
+
+int close_nointr(int fd);
+void close_nointr_nofail(int fd);
+void close_many(const int fds[], unsigned n_fd);
+
+int parse_boolean(const char *v);
+
+int read_one_line_file(const char *fn, char **line);
+
+char *strappend(const char *s, const char *suffix);
+char *strnappend(const char *s, const char *suffix, size_t length);
+
+char *strstrip(char *s);
+char *truncate_nl(char *s);
+
+bool ignore_file(const char *filename);
+
+char *strjoin(const char *x, ...) _sentinel_;
+
+extern int saved_argc;
+extern char **saved_argv;

diff --git a/src/main.c b/src/main.c
index 887cccd..b7d6324 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,6 +24,7 @@
 
 #include "hostnamed.h"
 #include "localed.h"
+#include "timedated.h"
 #include "shell-utils.h"
 
 #include "config.h"
@@ -32,11 +33,13 @@
 
 static gboolean debug = FALSE;
 static gboolean read_only = FALSE;
+static gchar *ntp_preferred_service = NULL;
 
 static GOptionEntry option_entries[] =
 {
     { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Enable debugging messages", NULL },
     { "read-only", 0, 0, G_OPTION_ARG_NONE, &read_only, "Run in read-only mode", NULL },
+    { "ntp-service", 0, 0, G_OPTION_ARG_STRING, &ntp_preferred_service, "Preferred rc NTP service for timedated", NULL },
     { NULL }
 };
 
@@ -76,12 +79,15 @@ main (gint argc, gchar *argv[])
     shell_utils_init ();
     hostnamed_init (read_only);
     localed_init (read_only);
+    timedated_init (read_only, ntp_preferred_service);
     loop = g_main_loop_new (NULL, FALSE);
     g_main_loop_run (loop);
 
     g_main_loop_unref (loop);
+    timedated_destroy ();
     localed_destroy ();
     hostnamed_destroy ();
     shell_utils_destroy ();
+    g_free (ntp_preferred_service);
     return 0;
 }

diff --git a/src/timedated.c b/src/timedated.c
new file mode 100644
index 0000000..bb16cf0
--- /dev/null
+++ b/src/timedated.c
@@ -0,0 +1,729 @@
+/*
+  Copyright 2012 Alexandre Rostovtsev
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <dbus/dbus-protocol.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <rc.h>
+
+#include "copypaste/hwclock.h"
+#include "timedated.h"
+#include "timedate1-generated.h"
+#include "bus-utils.h"
+#include "shell-utils.h"
+
+#include "config.h"
+
+#define SERVICE_NAME "openrc-settingsd timedated"
+
+static guint bus_id = 0;
+static gboolean read_only = FALSE;
+
+static OpenrcSettingsdTimedatedTimedate1 *timedate1 = NULL;
+
+static GFile *hwclock_file = NULL;
+static GFile *timezone_file = NULL;
+static GFile *localtime_file = NULL;
+
+gboolean local_rtc = FALSE;
+gchar *timezone_name = NULL;
+G_LOCK_DEFINE_STATIC (clock);
+
+gboolean use_ntp = FALSE;
+static const gchar *ntp_preferred_service = NULL;
+static const gchar *ntp_default_services[4] = { "ntpd", "chronyd", "busybox-ntpd", NULL };
+G_LOCK_DEFINE_STATIC (ntp);
+
+static gboolean
+get_local_rtc (GError **error)
+{
+    gchar *clock = NULL;
+    gboolean ret = FALSE;
+
+    clock = shell_utils_source_var (hwclock_file, "${clock}", error);
+    if (!g_strcmp0 (clock, "local"))
+        ret = TRUE;
+    return ret;
+}
+
+static gchar *
+get_timezone_name (GError **error)
+{
+    gchar *filebuf = NULL, *filebuf2 = NULL, *ret = NULL, *newline = NULL, *filename = NULL;
+    gchar *timezone_filename = NULL, *localtime_filename = NULL, *localtime2_filename = NULL;
+    GFile *localtime2_file = NULL;
+
+    timezone_filename = g_file_get_path (timezone_file);
+    if (!g_file_load_contents (timezone_file, NULL, &filebuf, NULL, NULL, error)) {
+        g_prefix_error (error, "Unable to read '%s':", timezone_filename);
+        ret = g_strdup ("");
+        goto out;
+    }
+    if ((newline = strstr (filebuf, "\n")) != NULL)
+        *newline = 0;
+    ret = g_strdup (g_strstrip (filebuf));
+    g_free (filebuf);
+    filebuf = NULL;
+
+    /* Log if /etc/localtime is not up to date */
+    localtime_filename = g_file_get_path (localtime_file);
+    localtime2_filename = g_strdup_printf (DATADIR "/zoneinfo/%s", ret);
+    localtime2_file = g_file_new_for_path (localtime2_filename);
+
+    if (!g_file_load_contents (localtime_file, NULL, &filebuf, NULL, NULL, error)) {
+        g_prefix_error (error, "Unable to read '%s':", localtime_filename);
+        goto out;
+    }
+    if (!g_file_load_contents (localtime2_file, NULL, &filebuf2, NULL, NULL, error)) {
+        g_prefix_error (error, "Unable to read '%s':", localtime2_filename);
+        goto out;
+    }
+    if (g_strcmp0 (filebuf, filebuf2))
+        g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s and %s differ; %s may be outdated or out of sync with %s", localtime_filename, localtime2_filename, localtime_filename, timezone_filename);
+
+  out:
+    g_free (filebuf);
+    g_free (filebuf2);
+    g_free (timezone_filename);
+    g_free (localtime_filename);
+    g_free (localtime2_filename);
+    if (localtime_file != NULL)
+        g_object_unref (localtime2_file);
+    return ret;
+}
+
+static gboolean
+set_timezone (const gchar *_timezone_name,
+              GError **error)
+{
+    gchar *filebuf = NULL;
+    gchar *timezone_filename = NULL, *localtime_filename = NULL, *localtime2_filename = NULL;
+    GFile *localtime2_file = NULL;
+    gboolean ret = FALSE;
+    gsize length = 0;
+
+    timezone_filename = g_file_get_path (timezone_file);
+    if (!g_file_replace_contents (timezone_file, _timezone_name, strlen (_timezone_name), NULL, FALSE, 0, NULL, NULL, error)) {
+        g_prefix_error (error, "Unable to write '%s':", timezone_filename);
+        goto out;
+    }
+
+    localtime_filename = g_file_get_path (localtime_file);
+    localtime2_filename = g_strdup_printf (DATADIR "/zoneinfo/%s", _timezone_name);
+    localtime2_file = g_file_new_for_path (localtime2_filename);
+    if (!g_file_load_contents (localtime2_file, NULL, &filebuf, &length, NULL, error)) {
+        g_prefix_error (error, "Unable to read '%s':", localtime2_filename);
+        goto out;
+    }
+    if (!g_file_replace_contents (localtime_file, filebuf, length, NULL, FALSE, 0, NULL, NULL, error)) {
+        g_prefix_error (error, "Unable to write '%s':", localtime_filename);
+        goto out;
+    }
+    ret = TRUE;
+
+  out:
+    g_free (filebuf);
+    g_free (timezone_filename);
+    g_free (localtime_filename);
+    g_free (localtime2_filename);
+    if (localtime_file != NULL)
+        g_object_unref (localtime2_file);
+    return ret;
+}
+
+/* Return the ntp rc service we will use; return value should NOT be freed */
+static const gchar *
+ntp_service ()
+{
+    const gchar * const *s = NULL;
+    const gchar *service = NULL;
+    gchar *runlevel = NULL;
+
+    if (ntp_preferred_service != NULL)
+        return ntp_preferred_service;
+
+    runlevel = rc_runlevel_get();
+    for (s = ntp_default_services; *s != NULL; s++) {
+        if (!rc_service_exists (*s))
+            continue;
+        if (service == NULL)
+            service = *s;
+        if (rc_service_in_runlevel (*s, runlevel)) {
+            service = *s;
+            break;
+        }
+    }
+    free (runlevel);
+
+    if (service == NULL)
+        service = ntp_default_services[0];
+    return service;
+}
+
+static gboolean
+service_started (const gchar *service,
+                 GError **error)
+{
+    RC_SERVICE state;
+
+    if (!rc_service_exists (service)) {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "%s rc service not found", service);
+        return FALSE;
+    }
+
+    state = rc_service_state (service);
+    return state == RC_SERVICE_STARTED || state == RC_SERVICE_STARTING || state == RC_SERVICE_INACTIVE;
+}
+
+static gboolean
+service_disable (const gchar *service,
+                 GError **error)
+{
+    gchar *runlevel = NULL;
+    gchar *service_script = NULL;
+    const gchar *argv[3] = { NULL, "stop", NULL };
+    gboolean ret = FALSE;
+    gint exit_status = 0;
+
+    if (!rc_service_exists (service)) {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "%s rc service not found", service);
+        goto out;
+    }
+
+    runlevel = rc_runlevel_get();
+    if (rc_service_in_runlevel (service, runlevel)) {
+        g_debug ("Removing %s rc service from %s runlevel", service, runlevel);
+        if (!rc_service_delete (runlevel, service))
+            g_warning ("Failed to remove %s rc service from %s runlevel", service, runlevel);
+    }
+
+    if ((service_script = rc_service_resolve (service)) == NULL) {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "%s rc service does not resolve", service);
+        goto out;
+    }
+
+    g_debug ("Stopping %s rc service", service);
+    argv[0] = service;
+    if (!g_spawn_sync (NULL, (gchar **)argv, NULL, 0, NULL, NULL, NULL, NULL, &exit_status, error)) {
+        g_prefix_error (error, "Failed to spawn %s rc service:", service);
+        goto out;
+    }
+    if (exit_status) {
+        g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "%s rc service failed to stop with exit status %d", service, exit_status);
+        goto out;
+    }
+    ret = TRUE;
+    
+  out:
+    if (runlevel != NULL)
+        free (runlevel);
+    if (service_script != NULL)
+        free (service_script);
+    return ret;
+}
+
+static gboolean
+service_enable (const gchar *service,
+                GError **error)
+{
+    gchar *runlevel = NULL;
+    gchar *service_script = NULL;
+    const gchar *argv[3] = { NULL, "start", NULL };
+    gboolean ret = FALSE;
+    gint exit_status = 0;
+
+    if (!rc_service_exists (service)) {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "%s rc service not found", service);
+        goto out;
+    }
+
+    runlevel = rc_runlevel_get();
+    if (!rc_service_in_runlevel (service, runlevel)) {
+        g_debug ("Adding %s rc service to %s runlevel", service, runlevel);
+        if (!rc_service_add (runlevel, service))
+            g_warning ("Failed to add %s rc service to %s runlevel", service, runlevel);
+    }
+
+    if ((service_script = rc_service_resolve (service)) == NULL) {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "%s rc service does not resolve", service);
+        goto out;
+    }
+
+    g_debug ("Starting %s rc service", service);
+    argv[0] = service;
+    if (!g_spawn_sync (NULL, (gchar **)argv, NULL, 0, NULL, NULL, NULL, NULL, &exit_status, error)) {
+        g_prefix_error (error, "Failed to spawn %s rc service:", service);
+        goto out;
+    }
+    if (exit_status) {
+        g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "%s rc service failed to start with exit status %d", service, exit_status);
+        goto out;
+    }
+    ret = TRUE;
+    
+  out:
+    if (runlevel != NULL)
+        free (runlevel);
+    if (service_script != NULL)
+        free (service_script);
+    return ret;
+}
+
+struct invoked_set_time {
+    GDBusMethodInvocation *invocation;
+    gint64 usec_utc;
+    gboolean relative;
+};
+
+static void
+on_handle_set_time_authorized_cb (GObject *source_object,
+                                  GAsyncResult *res,
+                                  gpointer user_data)
+{
+    GError *err = NULL;
+    struct invoked_set_time *data;
+    struct timespec ts = { 0, 0 };
+    struct tm *tm = NULL;
+
+    data = (struct invoked_set_time *) user_data;
+    if (!check_polkit_finish (res, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto out;
+    }
+
+    G_LOCK (clock);
+    if (!data->relative && data->usec_utc < 0) {
+        g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_INVALID_ARGS, "Attempt to set time before epoch");
+        goto unlock;
+    }
+
+    if (data->relative)
+        if (clock_gettime (CLOCK_REALTIME, &ts)) {
+            int errsv = errno;
+            g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_FAILED, strerror (errsv));
+            goto unlock;
+        }
+    ts.tv_sec += data->usec_utc / 1000000;
+    ts.tv_nsec += (data->usec_utc % 1000000) * 1000;
+    if (clock_settime (CLOCK_REALTIME, &ts)) {
+        int errsv = errno;
+        g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_FAILED, strerror (errsv));
+        goto unlock;
+    }
+
+    if (local_rtc)
+        tm = localtime(&ts.tv_sec);
+    else
+        tm = gmtime(&ts.tv_sec);
+    hwclock_set_time(tm);
+
+    openrc_settingsd_timedated_timedate1_complete_set_time (timedate1, data->invocation);
+
+  unlock:
+    G_UNLOCK (clock);
+
+  out:
+    g_free (data);
+    if (err != NULL)
+        g_error_free (err);
+}
+
+static gboolean
+on_handle_set_time (OpenrcSettingsdTimedatedTimedate1 *timedate1,
+                    GDBusMethodInvocation *invocation,
+                    const gint64 usec_utc,
+                    const gboolean relative,
+                    const gboolean user_interaction,
+                    gpointer user_data)
+{
+    if (read_only)
+        g_dbus_method_invocation_return_dbus_error (invocation,
+                                                    DBUS_ERROR_NOT_SUPPORTED,
+                                                    SERVICE_NAME " is in read-only mode");
+    else {
+        struct invoked_set_time *data;
+        data = g_new0 (struct invoked_set_time, 1);
+        data->invocation = invocation;
+        data->usec_utc = usec_utc;
+        data->relative = relative;
+        check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.timedate1.set-time", user_interaction, on_handle_set_time_authorized_cb, data);
+    }
+
+    return TRUE;
+}
+
+struct invoked_set_timezone {
+    GDBusMethodInvocation *invocation;
+    gchar *timezone; /* newly allocated */
+};
+
+static void
+on_handle_set_timezone_authorized_cb (GObject *source_object,
+                                      GAsyncResult *res,
+                                      gpointer user_data)
+{
+    GError *err = NULL;
+    struct invoked_set_timezone *data;
+
+    data = (struct invoked_set_timezone *) user_data;
+    if (!check_polkit_finish (res, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto out;
+    }
+
+    G_LOCK (clock);
+    if (!set_timezone(data->timezone, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto unlock;
+    }
+
+    if (local_rtc) {
+        struct timespec ts;
+        struct tm *tm;
+ 
+        /* Update kernel's view of the rtc timezone */
+        hwclock_apply_localtime_delta (NULL);
+        clock_gettime (CLOCK_REALTIME, &ts);
+        tm = localtime (&ts.tv_sec);
+        hwclock_set_time (tm);
+    }
+
+    openrc_settingsd_timedated_timedate1_complete_set_timezone (timedate1, data->invocation);
+    g_free (timezone_name);
+    timezone_name = data->timezone;
+    openrc_settingsd_timedated_timedate1_set_timezone (timedate1, timezone_name);
+
+  unlock:
+    G_UNLOCK (clock);
+
+  out:
+    g_free (data);
+    if (err != NULL)
+        g_error_free (err);
+}
+
+static gboolean
+on_handle_set_timezone (OpenrcSettingsdTimedatedTimedate1 *timedate1,
+                        GDBusMethodInvocation *invocation,
+                        const gchar *timezone,
+                        const gboolean user_interaction,
+                        gpointer user_data)
+{
+    if (read_only)
+        g_dbus_method_invocation_return_dbus_error (invocation,
+                                                    DBUS_ERROR_NOT_SUPPORTED,
+                                                    SERVICE_NAME " is in read-only mode");
+    else {
+        struct invoked_set_timezone *data;
+        data = g_new0 (struct invoked_set_timezone, 1);
+        data->invocation = invocation;
+        data->timezone = g_strdup (timezone);
+        check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.timedate1.set-timezone", user_interaction, on_handle_set_timezone_authorized_cb, data);
+    }
+
+    return TRUE;
+}
+
+struct invoked_set_local_rtc {
+    GDBusMethodInvocation *invocation;
+    gboolean local_rtc;
+    gboolean fix_system;
+};
+
+static void
+on_handle_set_local_rtc_authorized_cb (GObject *source_object,
+                                       GAsyncResult *res,
+                                       gpointer user_data)
+{
+    GError *err = NULL;
+    struct invoked_set_local_rtc *data;
+    gchar *clock = NULL;
+    const gchar *clock_types[2] = { "UTC", "local" };
+
+    data = (struct invoked_set_local_rtc *) user_data;
+    if (!check_polkit_finish (res, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto out;
+    }
+
+    G_LOCK (clock);
+    clock = shell_utils_source_var (hwclock_file, "${clock}", NULL);
+    if (clock != NULL || data->local_rtc)
+        if (!shell_utils_trivial_set_and_save (hwclock_file, &err, "clock", NULL, clock_types[data->local_rtc], NULL)) {
+            g_dbus_method_invocation_return_gerror (data->invocation, err);
+            goto unlock;
+        }
+
+    if (data->local_rtc != local_rtc) {
+        /* The clock sync code below taken almost verbatim from systemd's timedated.c, and is
+         * copyright 2011 Lennart Poettering */
+        struct timespec ts;
+
+        /* Update kernel's view of the rtc timezone */
+        if (data->local_rtc) 
+            hwclock_apply_localtime_delta (NULL);
+        else
+            hwclock_reset_localtime_delta ();
+
+        clock_gettime (CLOCK_REALTIME, &ts);
+        if (data->fix_system) {
+            struct tm tm;
+
+            /* Sync system clock from RTC; first,
+             * initialize the timezone fields of
+             * struct tm. */
+            if (data->local_rtc)
+                tm = *localtime(&ts.tv_sec);
+            else
+                tm = *gmtime(&ts.tv_sec);
+
+            /* Override the main fields of
+             * struct tm, but not the timezone
+             * fields */
+            if (hwclock_get_time(&tm) >= 0) {
+                /* And set the system clock
+                 * with this */
+                if (data->local_rtc)
+                    ts.tv_sec = mktime(&tm);
+                else
+                    ts.tv_sec = timegm(&tm);
+
+                clock_settime(CLOCK_REALTIME, &ts);
+            }
+
+        } else {
+            struct tm *tm;
+
+            /* Sync RTC from system clock */
+            if (data->local_rtc)
+                tm = localtime(&ts.tv_sec);
+            else
+                tm = gmtime(&ts.tv_sec);
+
+            hwclock_set_time(tm);
+        }
+    }
+
+    openrc_settingsd_timedated_timedate1_complete_set_timezone (timedate1, data->invocation);
+    g_free (timezone_name);
+    local_rtc = data->local_rtc;
+    openrc_settingsd_timedated_timedate1_set_local_rtc (timedate1, local_rtc);
+
+  unlock:
+    G_UNLOCK (clock);
+
+  out:
+    g_free (clock);
+    g_free (data);
+    if (err != NULL)
+        g_error_free (err);
+}
+
+static gboolean
+on_handle_set_local_rtc (OpenrcSettingsdTimedatedTimedate1 *timedate1,
+                         GDBusMethodInvocation *invocation,
+                         const gboolean _local_rtc,
+                         const gboolean fix_system,
+                         const gboolean user_interaction,
+                         gpointer user_data)
+{
+    if (read_only)
+        g_dbus_method_invocation_return_dbus_error (invocation,
+                                                    DBUS_ERROR_NOT_SUPPORTED,
+                                                    SERVICE_NAME " is in read-only mode");
+    else {
+        struct invoked_set_local_rtc *data;
+        data = g_new0 (struct invoked_set_local_rtc, 1);
+        data->local_rtc = _local_rtc;
+        data->fix_system = fix_system;
+        check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.timedate1.set-local-rtc", user_interaction, on_handle_set_local_rtc_authorized_cb, data);
+    }
+
+    return TRUE;
+}
+
+struct invoked_set_ntp {
+    GDBusMethodInvocation *invocation;
+    gboolean use_ntp;
+};
+
+static void
+on_handle_set_ntp_authorized_cb (GObject *source_object,
+                                 GAsyncResult *res,
+                                 gpointer user_data)
+{
+    GError *err = NULL;
+    struct invoked_set_ntp *data;
+
+    data = (struct invoked_set_ntp *) user_data;
+    if (!check_polkit_finish (res, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto out;
+    }
+
+    G_LOCK (ntp);
+    if ((data->use_ntp && !service_enable (ntp_service(), &err)) ||
+        (!data->use_ntp && !service_disable (ntp_service(), &err)))
+    {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto unlock;
+    }
+
+    openrc_settingsd_timedated_timedate1_complete_set_ntp (timedate1, data->invocation);
+    use_ntp = data->use_ntp;
+    openrc_settingsd_timedated_timedate1_set_ntp (timedate1, use_ntp);
+
+  unlock:
+    G_UNLOCK (ntp);
+
+  out:
+    g_free (data);
+    if (err != NULL)
+        g_error_free (err);
+}
+
+static gboolean
+on_handle_set_ntp (OpenrcSettingsdTimedatedTimedate1 *timedate1,
+                   GDBusMethodInvocation *invocation,
+                   const gboolean _use_ntp,
+                   const gboolean user_interaction,
+                   gpointer user_data)
+{
+    if (read_only)
+        g_dbus_method_invocation_return_dbus_error (invocation,
+                                                    DBUS_ERROR_NOT_SUPPORTED,
+                                                    SERVICE_NAME " is in read-only mode");
+    else {
+        struct invoked_set_ntp *data;
+        data = g_new0 (struct invoked_set_ntp, 1);
+        data->use_ntp = _use_ntp;
+        check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.timedate1.set-ntp", user_interaction, on_handle_set_ntp_authorized_cb, data);
+    }
+
+    return TRUE;
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar     *bus_name,
+                 gpointer         user_data)
+{
+    gchar *name;
+    GError *err = NULL;
+
+    g_debug ("Acquired a message bus connection");
+
+    timedate1 = openrc_settingsd_timedated_timedate1_skeleton_new ();
+
+    openrc_settingsd_timedated_timedate1_set_timezone (timedate1, timezone_name);
+    openrc_settingsd_timedated_timedate1_set_local_rtc (timedate1, local_rtc);
+    openrc_settingsd_timedated_timedate1_set_ntp (timedate1, use_ntp);
+
+    g_signal_connect (timedate1, "handle-set-time", G_CALLBACK (on_handle_set_time), NULL);
+    g_signal_connect (timedate1, "handle-set-timezone", G_CALLBACK (on_handle_set_timezone), NULL);
+    g_signal_connect (timedate1, "handle-set-local-rtc", G_CALLBACK (on_handle_set_local_rtc), NULL);
+    g_signal_connect (timedate1, "handle-set-ntp", G_CALLBACK (on_handle_set_ntp), NULL);
+
+    if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (timedate1),
+                                           connection,
+                                           "/org/freedesktop/timedate1",
+                                           &err)) {
+        if (err != NULL) {
+            g_printerr ("Failed to export interface on /org/freedesktop/timedate1: %s\n", err->message);
+            g_error_free (err);
+        }
+    }
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar     *bus_name,
+                  gpointer         user_data)
+{
+    g_debug ("Acquired the name %s", bus_name);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar     *bus_name,
+              gpointer         user_data)
+{
+    if (connection == NULL)
+        g_printerr ("Failed to acquire a dbus connection\n");
+    else
+        g_printerr ("Failed to acquire dbus name %s\n", bus_name);
+    exit(-1);
+}
+
+void
+timedated_init (gboolean _read_only,
+                const gchar *_ntp_preferred_service)
+{
+    GError *err = NULL;
+
+    read_only = _read_only;
+    ntp_preferred_service = _ntp_preferred_service;
+
+    hwclock_file = g_file_new_for_path (SYSCONFDIR "/conf.d/hwclock");
+    timezone_file = g_file_new_for_path (SYSCONFDIR "/timezone");
+    localtime_file = g_file_new_for_path (SYSCONFDIR "/localtime");
+
+    local_rtc = get_local_rtc (&err);
+    if (err != NULL) {
+        g_debug ("%s", err->message);
+        g_clear_error (&err);
+    }
+    timezone_name = get_timezone_name (&err);
+    if (err != NULL) {
+        g_warning ("%s", err->message);
+        g_clear_error (&err);
+    }
+    use_ntp = service_started (ntp_service (), &err);
+    if (err != NULL) {
+        g_warning ("%s", err->message);
+        g_clear_error (&err);
+    }
+
+    bus_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
+                             "org.freedesktop.timedate1",
+                             G_BUS_NAME_OWNER_FLAGS_NONE,
+                             on_bus_acquired,
+                             on_name_acquired,
+                             on_name_lost,
+                             NULL,
+                             NULL);
+}
+
+void
+timedated_destroy (void)
+{
+    g_bus_unown_name (bus_id);
+    bus_id = 0;
+    read_only = FALSE;
+    ntp_preferred_service = NULL;
+
+    g_object_unref (hwclock_file);
+    g_object_unref (timezone_file);
+    g_object_unref (localtime_file);
+}

diff --git a/src/timedated.h b/src/timedated.h
new file mode 100644
index 0000000..b7282b2
--- /dev/null
+++ b/src/timedated.h
@@ -0,0 +1,31 @@
+/*
+  Copyright 2012 Alexandre Rostovtsev
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef OPENRC_TIMEDATED_H
+#define OPENRC_TIMEDATED_H
+
+#include <glib.h>
+
+void
+timedated_init (gboolean read_only,
+                const gchar *_ntp_preferred_service);
+
+void
+timedated_destroy (void);
+
+#endif


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2012-09-05 16:54 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-05 16:54 [gentoo-commits] proj/openrc-settingsd:master commit in: src/, /, src/copypaste/, data/ Alexandre Restovtsev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox