timedate: assorted improvements

- Make writing/reading of /etc/timezone dependendent of HAVE_SYSV_COMPAT

- Introduce symlink_atomic() after all, and use it

- Use relative symlink for /etc/localtime
This commit is contained in:
Lennart Poettering 2012-09-14 20:02:52 +02:00
parent 608da9e9b5
commit 424a19f8a2
6 changed files with 45 additions and 116 deletions

4
TODO
View File

@ -53,6 +53,10 @@ Bugfixes:
Features: Features:
* hw watchdog: optionally try to use the preset watchdog timeout instead of always overriding it
* after deserializing sockets in socket.c we should reapply sockopts and things
* journald: warn if we drop messages we forward to the syslog socket * journald: warn if we drop messages we forward to the syslog socket
* does vasprintf advance the struct vaargs? http://pastie.org/pastes/4712773/text * does vasprintf advance the struct vaargs? http://pastie.org/pastes/4712773/text

View File

@ -264,12 +264,12 @@ char *path_kill_slashes(char *path) {
return path; return path;
} }
bool path_startswith(const char *path, const char *prefix) { char* path_startswith(const char *path, const char *prefix) {
assert(path); assert(path);
assert(prefix); assert(prefix);
if ((path[0] == '/') != (prefix[0] == '/')) if ((path[0] == '/') != (prefix[0] == '/'))
return false; return NULL;
for (;;) { for (;;) {
size_t a, b; size_t a, b;
@ -278,19 +278,19 @@ bool path_startswith(const char *path, const char *prefix) {
prefix += strspn(prefix, "/"); prefix += strspn(prefix, "/");
if (*prefix == 0) if (*prefix == 0)
return true; return (char*) path;
if (*path == 0) if (*path == 0)
return false; return NULL;
a = strcspn(path, "/"); a = strcspn(path, "/");
b = strcspn(prefix, "/"); b = strcspn(prefix, "/");
if (a != b) if (a != b)
return false; return NULL;
if (memcmp(path, prefix, a) != 0) if (memcmp(path, prefix, a) != 0)
return false; return NULL;
path += a; path += a;
prefix += b; prefix += b;

View File

@ -32,7 +32,7 @@ bool path_is_absolute(const char *p);
char *path_make_absolute(const char *p, const char *prefix); char *path_make_absolute(const char *p, const char *prefix);
char *path_make_absolute_cwd(const char *p); char *path_make_absolute_cwd(const char *p);
char *path_kill_slashes(char *path); char *path_kill_slashes(char *path);
bool path_startswith(const char *path, const char *prefix); char *path_startswith(const char *path, const char *prefix);
bool path_equal(const char *a, const char *b); bool path_equal(const char *a, const char *b);
char **path_strv_make_absolute_cwd(char **l); char **path_strv_make_absolute_cwd(char **l);

View File

@ -4644,49 +4644,9 @@ int copy_file(const char *from, const char *to) {
return 0; return 0;
} }
int symlink_or_copy(const char *from, const char *to) { int symlink_atomic(const char *from, const char *to) {
char *pf = NULL, *pt = NULL; char *x;
struct stat a, b; _cleanup_free_ char *t;
int r;
assert(from);
assert(to);
if (path_get_parent(from, &pf) < 0 ||
path_get_parent(to, &pt) < 0) {
r = -ENOMEM;
goto finish;
}
if (stat(pf, &a) < 0 ||
stat(pt, &b) < 0) {
r = -errno;
goto finish;
}
if (a.st_dev != b.st_dev) {
free(pf);
free(pt);
return copy_file(from, to);
}
if (symlink(from, to) < 0) {
r = -errno;
goto finish;
}
r = 0;
finish:
free(pf);
free(pt);
return r;
}
int symlink_or_copy_atomic(const char *from, const char *to) {
char *t, *x;
const char *fn; const char *fn;
size_t k; size_t k;
unsigned long long ull; unsigned long long ull;
@ -4714,22 +4674,16 @@ int symlink_or_copy_atomic(const char *from, const char *to) {
*x = 0; *x = 0;
r = symlink_or_copy(from, t); if (symlink(from, t) < 0)
if (r < 0) { return -errno;
unlink(t);
free(t);
return r;
}
if (rename(t, to) < 0) { if (rename(t, to) < 0) {
r = -errno; r = -errno;
unlink(t); unlink(t);
free(t);
return r; return r;
} }
free(t); return 0;
return r;
} }
bool display_is_local(const char *display) { bool display_is_local(const char *display) {

View File

@ -430,8 +430,8 @@ int terminal_vhangup(const char *name);
int vt_disallocate(const char *name); int vt_disallocate(const char *name);
int copy_file(const char *from, const char *to); int copy_file(const char *from, const char *to);
int symlink_or_copy(const char *from, const char *to);
int symlink_or_copy_atomic(const char *from, const char *to); int symlink_atomic(const char *from, const char *to);
int fchmod_umask(int fd, mode_t mode); int fchmod_umask(int fd, mode_t mode);

View File

@ -34,6 +34,7 @@
#include "def.h" #include "def.h"
#include "hwclock.h" #include "hwclock.h"
#include "conf-files.h" #include "conf-files.h"
#include "path-util.h"
#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
@ -76,9 +77,6 @@
BUS_GENERIC_INTERFACES_LIST \ BUS_GENERIC_INTERFACES_LIST \
"org.freedesktop.timedate1\0" "org.freedesktop.timedate1\0"
/* Must start and end with '/' */
#define ZONEINFO_PATH "/usr/share/zoneinfo/"
const char timedate_interface[] _introspect_("timedate1") = INTERFACE; const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
typedef struct TZ { typedef struct TZ {
@ -132,7 +130,7 @@ static bool valid_timezone(const char *name) {
if (slash) if (slash)
return false; return false;
t = strappend(ZONEINFO_PATH, name); t = strappend("/usr/share/zoneinfo/", name);
if (!t) if (!t)
return false; return false;
@ -148,56 +146,29 @@ static bool valid_timezone(const char *name) {
return true; return true;
} }
static void verify_timezone(void) {
char *p, *a = NULL, *b = NULL;
size_t l, q;
int j, k;
if (!tz.zone)
return;
p = strappend(ZONEINFO_PATH, tz.zone);
if (!p) {
log_oom();
return;
}
k = read_full_file(p, &b, &q);
free(p);
j = read_full_file("/etc/localtime", &a, &l);
if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) {
log_warning("/etc/localtime and /etc/timezone out of sync.");
free(tz.zone);
tz.zone = NULL;
}
free(a);
free(b);
}
static int read_data(void) { static int read_data(void) {
int r; int r;
char *t = NULL; _cleanup_free_ char *t = NULL;
free_data(); free_data();
r = readlink_malloc("/etc/localtime", &t); r = readlink_malloc("/etc/localtime", &t);
if (r < 0) { if (r < 0) {
if (r == -EINVAL) if (r == -EINVAL)
log_warning("/etc/localtime should be a symbolic link to a timezone data file in " ZONEINFO_PATH); log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/.");
else else
log_warning("Failed to get target of %s: %s", "/etc/localtime", strerror(-r)); log_warning("Failed to get target of /etc/localtime: %s", strerror(-r));
} else { } else {
/* we only support the trivial relative link of (/etc/)..$ABSOLUTE */ const char *e;
int rel_link_offset = startswith(t, "..") ? strlen("..") : 0;
if (!startswith(t + rel_link_offset, ZONEINFO_PATH)) e = path_startswith(t, "/usr/share/zoneinfo/");
log_warning("/etc/localtime should be a symbolic link to a timezone data file in " ZONEINFO_PATH); if (!e)
e = path_startswith(t, "../usr/share/zoneinfo/");
if (!e)
log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/.");
else { else {
tz.zone = strdup(t + rel_link_offset + strlen(ZONEINFO_PATH)); tz.zone = strdup(e);
free(t);
if (!tz.zone) if (!tz.zone)
return log_oom(); return log_oom();
@ -205,8 +176,7 @@ static int read_data(void) {
} }
} }
free(t); #ifdef HAVE_SYSV_COMPAT
r = read_one_line_file("/etc/timezone", &tz.zone); r = read_one_line_file("/etc/timezone", &tz.zone);
if (r < 0) { if (r < 0) {
if (r != -ENOENT) if (r != -ENOENT)
@ -221,6 +191,7 @@ static int read_data(void) {
log_warning("Failed to read /etc/sysconfig/clock: %s", strerror(-r)); log_warning("Failed to read /etc/sysconfig/clock: %s", strerror(-r));
#endif #endif
} }
#endif
have_timezone: have_timezone:
if (isempty(tz.zone)) { if (isempty(tz.zone)) {
@ -228,8 +199,6 @@ have_timezone:
tz.zone = NULL; tz.zone = NULL;
} }
verify_timezone();
tz.local_rtc = hwclock_is_localtime() > 0; tz.local_rtc = hwclock_is_localtime() > 0;
return 0; return 0;
@ -237,34 +206,36 @@ have_timezone:
static int write_data_timezone(void) { static int write_data_timezone(void) {
int r = 0; int r = 0;
char *p; _cleanup_free_ char *p = NULL;
struct stat st; struct stat st;
if (!tz.zone) { if (!tz.zone) {
if (unlink("/etc/timezone") < 0 && errno != ENOENT)
r = -errno;
if (unlink("/etc/localtime") < 0 && errno != ENOENT) if (unlink("/etc/localtime") < 0 && errno != ENOENT)
r = -errno; r = -errno;
#ifdef HAVE_SYSV_COMPAT
if (unlink("/etc/timezone") < 0 && errno != ENOENT)
r = -errno;
#endif
return r; return r;
} }
p = strappend(ZONEINFO_PATH, tz.zone); p = strappend("../usr/share/zoneinfo/", tz.zone);
if (!p) if (!p)
return log_oom(); return log_oom();
r = symlink(p, "/etc/localtime"); r = symlink_atomic(p, "/etc/localtime");
free(p);
if (r < 0) if (r < 0)
return -errno; return r;
#ifdef HAVE_SYSV_COMPAT
if (stat("/etc/timezone", &st) == 0 && S_ISREG(st.st_mode)) { if (stat("/etc/timezone", &st) == 0 && S_ISREG(st.st_mode)) {
r = write_one_line_file_atomic("/etc/timezone", tz.zone); r = write_one_line_file_atomic("/etc/timezone", tz.zone);
if (r < 0) if (r < 0)
return r; return r;
} }
#endif
return 0; return 0;
} }