aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Rostovtsev <tetromino@gentoo.org>2012-09-05 12:48:04 -0400
committerAlexandre Rostovtsev <tetromino@gentoo.org>2012-09-05 12:51:29 -0400
commit37d9aeb4da8169c85ef5e96eefd4b0da30dcb83a (patch)
tree599a53efb3e135b802ca14b88b235cdb89da321d
parentCleanup to make valgrind happy (diff)
downloadopenrc-settingsd-37d9aeb4da8169c85ef5e96eefd4b0da30dcb83a.tar.gz
openrc-settingsd-37d9aeb4da8169c85ef5e96eefd4b0da30dcb83a.tar.bz2
openrc-settingsd-37d9aeb4da8169c85ef5e96eefd4b0da30dcb83a.zip
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.
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am31
-rw-r--r--TODO2
-rw-r--r--configure.ac2
-rw-r--r--data/timedate1.xml26
-rw-r--r--src/copypaste/hwclock.c193
-rw-r--r--src/copypaste/hwclock.h32
-rw-r--r--src/copypaste/macro.h186
-rw-r--r--src/copypaste/util.c279
-rw-r--r--src/copypaste/util.h110
-rw-r--r--src/main.c6
-rw-r--r--src/timedated.c729
-rw-r--r--src/timedated.h31
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