diff options
author | Alexandre Rostovtsev <tetromino@gentoo.org> | 2012-09-05 12:48:04 -0400 |
---|---|---|
committer | Alexandre Rostovtsev <tetromino@gentoo.org> | 2012-09-05 12:51:29 -0400 |
commit | 37d9aeb4da8169c85ef5e96eefd4b0da30dcb83a (patch) | |
tree | 599a53efb3e135b802ca14b88b235cdb89da321d | |
parent | Cleanup to make valgrind happy (diff) | |
download | openrc-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-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 31 | ||||
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | data/timedate1.xml | 26 | ||||
-rw-r--r-- | src/copypaste/hwclock.c | 193 | ||||
-rw-r--r-- | src/copypaste/hwclock.h | 32 | ||||
-rw-r--r-- | src/copypaste/macro.h | 186 | ||||
-rw-r--r-- | src/copypaste/util.c | 279 | ||||
-rw-r--r-- | src/copypaste/util.h | 110 | ||||
-rw-r--r-- | src/main.c | 6 | ||||
-rw-r--r-- | src/timedated.c | 729 | ||||
-rw-r--r-- | src/timedated.h | 31 |
13 files changed, 1625 insertions, 3 deletions
@@ -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) @@ -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; @@ -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 |