mirror of
https://github.com/systemd/systemd.git
synced 2025-03-01 08:58:29 +03:00
core: subscribe to /etc/localtime timezone changes and update timer elapsation accordingly
Fixes: #8233 This is our first real-life usecase for the new sd_event_add_inotify() calls we just added.
This commit is contained in:
parent
7feedd18fa
commit
bbf5fd8e41
@ -108,6 +108,7 @@ static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint
|
||||
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
|
||||
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
|
||||
static int manager_dispatch_sigchld(sd_event_source *source, void *userdata);
|
||||
static int manager_dispatch_timezone_change(sd_event_source *source, const struct inotify_event *event, void *userdata);
|
||||
static int manager_run_environment_generators(Manager *m);
|
||||
static int manager_run_generators(Manager *m);
|
||||
|
||||
@ -391,6 +392,65 @@ static int manager_setup_time_change(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_read_timezone_stat(Manager *m) {
|
||||
struct stat st;
|
||||
bool changed;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Read the current stat() data of /etc/localtime so that we detect changes */
|
||||
if (lstat("/etc/localtime", &st) < 0) {
|
||||
log_debug_errno(errno, "Failed to stat /etc/localtime, ignoring: %m");
|
||||
changed = m->etc_localtime_accessible;
|
||||
m->etc_localtime_accessible = false;
|
||||
} else {
|
||||
usec_t k;
|
||||
|
||||
k = timespec_load(&st.st_mtim);
|
||||
changed = !m->etc_localtime_accessible || k != m->etc_localtime_mtime;
|
||||
|
||||
m->etc_localtime_mtime = k;
|
||||
m->etc_localtime_accessible = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int manager_setup_timezone_change(Manager *m) {
|
||||
sd_event_source *new_event = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (m->test_run_flags != 0)
|
||||
return 0;
|
||||
|
||||
/* We watch /etc/localtime for three events: change of the link count (which might mean removal from /etc even
|
||||
* though another link might be kept), renames, and file close operations after writing. Note we don't bother
|
||||
* with IN_DELETE_SELF, as that would just report when the inode is removed entirely, i.e. after the link count
|
||||
* went to zero and all fds to it are closed.
|
||||
*
|
||||
* Note that we never follow symlinks here. This is a simplification, but should cover almost all cases
|
||||
* correctly.
|
||||
*
|
||||
* Note that we create the new event source first here, before releasing the old one. This should optimize
|
||||
* behaviour as this way sd-event can reuse the old watch in case the inode didn't change. */
|
||||
|
||||
r = sd_event_add_inotify(m->event, &new_event, "/etc/localtime",
|
||||
IN_ATTRIB|IN_MOVE_SELF|IN_CLOSE_WRITE|IN_DONT_FOLLOW, manager_dispatch_timezone_change, m);
|
||||
if (r == -ENOENT) /* If the file doesn't exist yet, subscribe to /etc instead, and wait until it is created
|
||||
* either by O_CREATE or by rename() */
|
||||
r = sd_event_add_inotify(m->event, &new_event, "/etc",
|
||||
IN_CREATE|IN_MOVED_TO|IN_ONLYDIR, manager_dispatch_timezone_change, m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create timezone change event source: %m");
|
||||
|
||||
sd_event_source_unref(m->timezone_change_event_source);
|
||||
m->timezone_change_event_source = new_event;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_special_signals(Manager *m) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
@ -775,6 +835,14 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_read_timezone_stat(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_setup_timezone_change(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_setup_sigchld_event_source(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1216,6 +1284,7 @@ Manager* manager_free(Manager *m) {
|
||||
sd_event_source_unref(m->notify_event_source);
|
||||
sd_event_source_unref(m->cgroups_agent_event_source);
|
||||
sd_event_source_unref(m->time_change_event_source);
|
||||
sd_event_source_unref(m->timezone_change_event_source);
|
||||
sd_event_source_unref(m->jobs_in_progress_event_source);
|
||||
sd_event_source_unref(m->run_queue_event_source);
|
||||
sd_event_source_unref(m->user_lookup_event_source);
|
||||
@ -2570,6 +2639,41 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_dispatch_timezone_change(
|
||||
sd_event_source *source,
|
||||
const struct inotify_event *e,
|
||||
void *userdata) {
|
||||
|
||||
Manager *m = userdata;
|
||||
int changed;
|
||||
Iterator i;
|
||||
Unit *u;
|
||||
|
||||
assert(m);
|
||||
|
||||
log_debug("inotify event for /etc/localtime");
|
||||
|
||||
changed = manager_read_timezone_stat(m);
|
||||
if (changed < 0)
|
||||
return changed;
|
||||
if (!changed)
|
||||
return 0;
|
||||
|
||||
/* Something changed, restart the watch, to ensure we watch the new /etc/localtime if it changed */
|
||||
(void) manager_setup_timezone_change(m);
|
||||
|
||||
/* Read the new timezone */
|
||||
tzset();
|
||||
|
||||
log_debug("Timezone has been changed (now: %s).", tzname[daylight]);
|
||||
|
||||
HASHMAP_FOREACH(u, m->units, i)
|
||||
if (UNIT_VTABLE(u)->timezone_change)
|
||||
UNIT_VTABLE(u)->timezone_change(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
|
||||
Manager *m = userdata;
|
||||
|
||||
|
@ -170,6 +170,8 @@ struct Manager {
|
||||
int time_change_fd;
|
||||
sd_event_source *time_change_event_source;
|
||||
|
||||
sd_event_source *timezone_change_event_source;
|
||||
|
||||
sd_event_source *jobs_in_progress_event_source;
|
||||
|
||||
int user_lookup_fds[2];
|
||||
@ -250,6 +252,10 @@ struct Manager {
|
||||
|
||||
unsigned gc_marker;
|
||||
|
||||
/* The stat() data the last time we saw /etc/localtime */
|
||||
usec_t etc_localtime_mtime;
|
||||
bool etc_localtime_accessible:1;
|
||||
|
||||
/* Flags */
|
||||
ManagerExitCode exit_code:5;
|
||||
|
||||
|
@ -819,6 +819,18 @@ static void timer_time_change(Unit *u) {
|
||||
timer_enter_waiting(t, false);
|
||||
}
|
||||
|
||||
static void timer_timezone_change(Unit *u) {
|
||||
Timer *t = TIMER(u);
|
||||
|
||||
assert(u);
|
||||
|
||||
if (t->state != TIMER_WAITING)
|
||||
return;
|
||||
|
||||
log_unit_debug(u, "Timezone change, recalculating next elapse.");
|
||||
timer_enter_waiting(t, false);
|
||||
}
|
||||
|
||||
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
|
||||
[TIMER_ACTIVE] = "OnActiveSec",
|
||||
[TIMER_BOOT] = "OnBootSec",
|
||||
@ -868,6 +880,7 @@ const UnitVTable timer_vtable = {
|
||||
|
||||
.reset_failed = timer_reset_failed,
|
||||
.time_change = timer_time_change,
|
||||
.timezone_change = timer_timezone_change,
|
||||
|
||||
.bus_vtable = bus_timer_vtable,
|
||||
.bus_set_property = bus_timer_set_property,
|
||||
|
@ -516,6 +516,9 @@ typedef struct UnitVTable {
|
||||
/* Called whenever CLOCK_REALTIME made a jump */
|
||||
void (*time_change)(Unit *u);
|
||||
|
||||
/* Called whenever /etc/localtime was modified */
|
||||
void (*timezone_change)(Unit *u);
|
||||
|
||||
/* Returns the next timeout of a unit */
|
||||
int (*get_timeout)(Unit *u, usec_t *timeout);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user