Merge pull request #9121 from poettering/sd-event-inotify

add "sd_event_add_inotify()" and use it for making PID 1 rescheduler .timer units properly on timezone change
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-06-06 12:38:55 +02:00 committed by GitHub
commit 6585f72c08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1438 additions and 95 deletions

9
TODO
View File

@ -24,16 +24,13 @@ Janitorial Clean-ups:
Features:
* Add OnTimezoneChange= and OnTimeChange= stanzas to .timer units in order to
schedule events based on time and timezone changes.
* add O_TMPFILE support to copy_file_atomic()
* nspawn: greater control over selinux label?
* sd-event: implement inotify events, as we can safely and robustly do that now
for any inode without fearing confusion by inodes appearing at multiple
places: we can open it with O_PATH first, then store its inode in a hash
table, to recognize duplicate watches before creating (and thus corrupting
pre-existing ones) them, and using /proc/self/fd/ to add it right after.
* the error paths in usbffs_dispatch_ep() leak memory
* cgroups: figure out if we can somehow communicate in a cleaner way whether a

View File

@ -263,6 +263,10 @@ manpages = [
'3',
['sd_event_add_exit', 'sd_event_add_post', 'sd_event_handler_t'],
''],
['sd_event_add_inotify',
'3',
['sd_event_inotify_handler_t', 'sd_event_source_get_inotify_mask'],
''],
['sd_event_add_io',
'3',
['sd_event_io_handler_t',

View File

@ -62,6 +62,7 @@
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
@ -152,6 +153,7 @@
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,

View File

@ -223,6 +223,7 @@
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,

View File

@ -194,6 +194,7 @@
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,

View File

@ -0,0 +1,181 @@
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<refentry id="sd_event_add_inotify" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_event_add_inotify</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sd_event_add_inotify</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_event_add_inotify</refname>
<refname>sd_event_source_get_inotify_mask</refname>
<refname>sd_event_inotify_handler_t</refname>
<refpurpose>Add an "inotify" file system inode event source to an event loop</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
<funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo>
<funcprototype>
<funcdef>typedef int (*<function>sd_event_inotify_handler_t</function>)</funcdef>
<paramdef>sd_event_source *<parameter>s</parameter></paramdef>
<paramdef>const struct inotify_event *<parameter>event</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_add_inotify</function></funcdef>
<paramdef>sd_event *<parameter>event</parameter></paramdef>
<paramdef>sd_event_source **<parameter>source</parameter></paramdef>
<paramdef>const char *<parameter>path</parameter></paramdef>
<paramdef>uint32_t <parameter>mask</parameter></paramdef>
<paramdef>sd_event_inotify_handler_t <parameter>handler</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_get_inotify_mask</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>uint32_t *<parameter>mask</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>sd_event_add_inotify()</function> adds a new <citerefentry
project='man-pages'><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry> file system inode
event source to an event loop. The event loop object is specified in the <parameter>event</parameter> parameter,
the event source object is returned in the <parameter>source</parameter> parameter. The <parameter>path</parameter>
parameter specifies the path of the file system inode to watch. The <parameter>handler</parameter> must reference a
function to call when the inode changes. The handler function will be passed the <parameter>userdata</parameter>
pointer, which may be chosen freely by the caller. The handler also receives a pointer to a <structname>struct
inotify_event</structname> structure containing information about the inode event. The <parameter>mask</parameter>
parameter specifie which types of inode events to watch specifically. It must contain an OR-ed combination of
<constant>IN_ACCESS</constant>, <constant>IN_ATTRIB</constant>, <constant>IN_CLOSE_WRITE</constant>, … flags. See
<citerefentry project='man-pages'><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
further information.</para>
<para>If multiple event sources are installed for the same inode the backing inotify watch descriptor is
automatically shared. The mask parameter may contain any flag defined by the inotify API, with the exception of
<constant>IN_MASK_ADD</constant>.</para>
<para>The handler is enabled continuously (<constant>SD_EVENT_ON</constant>), but this may be changed with
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Alternatively,
the <constant>IN_ONESHOT</constant> mask flag may be used to request <constant>SD_EVENT_ONESHOT</constant> mode.
If the handler function returns a negative error code, it will be disabled after the invocation, even if the
<constant>SD_EVENT_ON</constant> mode was requested before.
</para>
<para>As a special limitation the priority of inotify event sources may only be altered (see
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
in the time between creation of the event source object with <function>sd_event_add_inotify()</function> and the
beginning of the next event loop iteration. Attempts of changing the priority any later will be refused. Consider
freeing and allocating a new inotify event source to change the priority at that point.</para>
<para>To destroy an event source object use
<citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>, but note
that the event source is only removed from the event loop when all references to the event source are dropped. To
make sure an event source does not fire anymore, even when there's still a reference to it kept, consider disabling
it with
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
<para>If the second parameter of <function>sd_event_add_inotify()</function> is passed as NULL no reference to the
event source object is returned. In this case the event source is considered "floating", and will be destroyed
implicitly when the event loop itself is destroyed.</para>
<para><function>sd_event_source_get_inotify_mask()</function> retrieves the configured inotify watch mask of an
event source created previously with <function>sd_event_add_inotify()</function>. It takes the event source object
as the <parameter>source</parameter> parameter and a pointer to a <type>uint32_t</type> variable to return the mask
in.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style
error code.</para>
</refsect1>
<refsect1>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-ENOMEM</constant></term>
<listitem><para>Not enough memory to allocate an object.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para>An invalid argument has been passed. This includes specifying a mask with
<constant>IN_MASK_ADD</constant> set.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ESTALE</constant></term>
<listitem><para>The event loop is already terminated.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ECHILD</constant></term>
<listitem><para>The event loop has been created in a different process.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EDOM</constant></term>
<listitem><para>The passed event source is not an inotify process event source.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -275,6 +275,7 @@
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,

View File

@ -198,6 +198,7 @@
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,

View File

@ -288,6 +288,7 @@
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,

View File

@ -108,6 +108,12 @@
particular event sources do not starve or dominate the event
loop.</para>
<para>The priority of event sources may be changed at any time of their lifetime, with the exception of inotify
event sources (i.e. those created with
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>) whose
priority may only be changed in the time between their initial creation and the first subsequent event loop
iteration.</para>
<para><function>sd_event_source_get_priority()</function> may be
used to query the current priority assigned to the event source
object <parameter>source</parameter>.</para>

View File

@ -1463,3 +1463,27 @@ bool in_utc_timezone(void) {
return timezone == 0 && daylight == 0;
}
int time_change_fd(void) {
/* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
static const struct itimerspec its = {
.it_value.tv_sec = TIME_T_MAX,
};
_cleanup_close_ int fd;
assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
/* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
* CLOCK_MONOTONIC. */
fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
if (fd < 0)
return -errno;
if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
return -errno;
return TAKE_FD(fd);
}

View File

@ -185,3 +185,5 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
#else
#error "Yuck, time_t is neither 4 nor 8 bytes wide?"
#endif
int time_change_fd(void);

View File

@ -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);
@ -352,35 +353,27 @@ static void manager_close_idle_pipe(Manager *m) {
static int manager_setup_time_change(Manager *m) {
int r;
/* We only care for the cancellation event, hence we set the
* timeout to the latest possible value. */
struct itimerspec its = {
.it_value.tv_sec = TIME_T_MAX,
};
assert(m);
assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
if (m->test_run_flags)
return 0;
/* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
* CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */
m->time_change_event_source = sd_event_source_unref(m->time_change_event_source);
m->time_change_fd = safe_close(m->time_change_fd);
m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
m->time_change_fd = time_change_fd();
if (m->time_change_fd < 0)
return log_error_errno(errno, "Failed to create timerfd: %m");
if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) {
log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m");
m->time_change_fd = safe_close(m->time_change_fd);
return 0;
}
return log_error_errno(m->time_change_fd, "Failed to create timer change timer fd: %m");
r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m);
if (r < 0)
return log_error_errno(r, "Failed to create time change event source: %m");
/* Schedule this slightly earlier than the .timer event sources */
r = sd_event_source_set_priority(m->time_change_event_source, SD_EVENT_PRIORITY_NORMAL-1);
if (r < 0)
return log_error_errno(r, "Failed to set priority of time change event sources: %m");
(void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change");
log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd.");
@ -388,6 +381,70 @@ 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) {
_cleanup_(sd_event_source_unrefp) 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");
/* Schedule this slightly earlier than the .timer event sources */
r = sd_event_source_set_priority(new_event, SD_EVENT_PRIORITY_NORMAL-1);
if (r < 0)
return log_error_errno(r, "Failed to set priority of timezone change event sources: %m");
sd_event_source_unref(m->timezone_change_event_source);
m->timezone_change_event_source = TAKE_PTR(new_event);
return 0;
}
static int enable_special_signals(Manager *m) {
_cleanup_close_ int fd = -1;
@ -772,6 +829,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;
@ -1213,6 +1278,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);
@ -2558,10 +2624,7 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint
LOG_MESSAGE("Time has been changed"));
/* Restart the watch */
m->time_change_event_source = sd_event_source_unref(m->time_change_event_source);
m->time_change_fd = safe_close(m->time_change_fd);
manager_setup_time_change(m);
(void) manager_setup_time_change(m);
HASHMAP_FOREACH(u, m->units, i)
if (UNIT_VTABLE(u)->time_change)
@ -2570,6 +2633,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;

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -563,4 +563,6 @@ global:
sd_bus_open_system_with_description;
sd_bus_slot_get_floating;
sd_bus_slot_set_floating;
sd_event_add_inotify;
sd_event_source_get_inotify_mask;
} LIBSYSTEMD_238;

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,19 @@
#include "sd-event.h"
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "log.h"
#include "macro.h"
#include "signal-util.h"
#include "util.h"
#include "parse-util.h"
#include "process-util.h"
#include "rm-rf.h"
#include "signal-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "util.h"
static int prepare_handler(sd_event_source *s, void *userdata) {
log_info("preparing %c", PTR_TO_INT(userdata));
@ -342,6 +349,142 @@ static void test_rtqueue(void) {
sd_event_unref(e);
}
#define CREATE_EVENTS_MAX (70000U)
struct inotify_context {
bool delete_self_handler_called;
unsigned create_called[CREATE_EVENTS_MAX];
unsigned create_overflow;
unsigned n_create_events;
};
static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
unsigned n;
assert(s);
assert(c);
if (!c->delete_self_handler_called)
return;
for (n = 0; n < 3; n++) {
unsigned i;
if (c->create_overflow & (1U << n))
continue;
for (i = 0; i < c->n_create_events; i++)
if (!(c->create_called[i] & (1U << n)))
return;
}
sd_event_exit(sd_event_source_get_event(s), 0);
}
static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
struct inotify_context *c = userdata;
const char *description;
unsigned bit, n;
assert_se(sd_event_source_get_description(s, &description) >= 0);
assert_se(safe_atou(description, &n) >= 0);
assert_se(n <= 3);
bit = 1U << n;
if (ev->mask & IN_Q_OVERFLOW) {
log_info("inotify-handler <%s>: overflow", description);
c->create_overflow |= bit;
} else if (ev->mask & IN_CREATE) {
unsigned i;
log_info("inotify-handler <%s>: create on %s", description, ev->name);
if (!streq(ev->name, "sub")) {
assert_se(safe_atou(ev->name, &i) >= 0);
assert_se(i < c->n_create_events);
c->create_called[i] |= bit;
}
} else if (ev->mask & IN_DELETE) {
log_info("inotify-handler <%s>: delete of %s", description, ev->name);
assert_se(streq(ev->name, "sub"));
} else
assert_not_reached("unexpected inotify event");
maybe_exit(s, c);
return 1;
}
static int delete_self_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
struct inotify_context *c = userdata;
if (ev->mask & IN_Q_OVERFLOW) {
log_info("delete-self-handler: overflow");
c->delete_self_handler_called = true;
} else if (ev->mask & IN_DELETE_SELF) {
log_info("delete-self-handler: delete-self");
c->delete_self_handler_called = true;
} else if (ev->mask & IN_IGNORED) {
log_info("delete-self-handler: ignore");
} else
assert_not_reached("unexpected inotify event (delete-self)");
maybe_exit(s, c);
return 1;
}
static void test_inotify(unsigned n_create_events) {
_cleanup_(rm_rf_physical_and_freep) char *p = NULL;
sd_event_source *a = NULL, *b = NULL, *c = NULL, *d = NULL;
struct inotify_context context = {
.n_create_events = n_create_events,
};
sd_event *e = NULL;
const char *q;
unsigned i;
assert_se(sd_event_default(&e) >= 0);
assert_se(mkdtemp_malloc("/tmp/test-inotify-XXXXXX", &p) >= 0);
assert_se(sd_event_add_inotify(e, &a, p, IN_CREATE|IN_ONLYDIR, inotify_handler, &context) >= 0);
assert_se(sd_event_add_inotify(e, &b, p, IN_CREATE|IN_DELETE|IN_DONT_FOLLOW, inotify_handler, &context) >= 0);
assert_se(sd_event_source_set_priority(b, SD_EVENT_PRIORITY_IDLE) >= 0);
assert_se(sd_event_source_set_priority(b, SD_EVENT_PRIORITY_NORMAL) >= 0);
assert_se(sd_event_add_inotify(e, &c, p, IN_CREATE|IN_DELETE|IN_EXCL_UNLINK, inotify_handler, &context) >= 0);
assert_se(sd_event_source_set_priority(c, SD_EVENT_PRIORITY_IDLE) >= 0);
assert_se(sd_event_source_set_description(a, "0") >= 0);
assert_se(sd_event_source_set_description(b, "1") >= 0);
assert_se(sd_event_source_set_description(c, "2") >= 0);
q = strjoina(p, "/sub");
assert_se(touch(q) >= 0);
assert_se(sd_event_add_inotify(e, &d, q, IN_DELETE_SELF, delete_self_handler, &context) >= 0);
for (i = 0; i < n_create_events; i++) {
char buf[DECIMAL_STR_MAX(unsigned)+1];
_cleanup_free_ char *z;
xsprintf(buf, "%u", i);
assert_se(z = strjoin(p, "/", buf));
assert_se(touch(z) >= 0);
}
assert_se(unlink(q) >= 0);
assert_se(sd_event_loop(e) >= 0);
sd_event_source_unref(a);
sd_event_source_unref(b);
sd_event_source_unref(c);
sd_event_source_unref(d);
sd_event_unref(e);
}
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
@ -351,5 +494,8 @@ int main(int argc, char *argv[]) {
test_sd_event_now();
test_rtqueue();
test_inotify(100); /* should work without overflow */
test_inotify(33000); /* should trigger a q overflow */
return 0;
}

View File

@ -24,6 +24,7 @@
#include <inttypes.h>
#include <signal.h>
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <time.h>
@ -78,6 +79,7 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si,
#else
typedef void* sd_event_child_handler_t;
#endif
typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata);
int sd_event_default(sd_event **e);
@ -89,6 +91,7 @@ int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, s
int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata);
int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata);
int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
@ -139,6 +142,7 @@ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec);
int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock);
int sd_event_source_get_signal(sd_event_source *s);
int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);

View File

@ -115,16 +115,15 @@ static int inotify_handler(sd_event_source *s,
return 0;
}
static int clock_state_update(ClockState *sp,
sd_event *event) {
static const struct itimerspec its = {
.it_value.tv_sec = TIME_T_MAX,
};
int r;
struct timex tx = {};
static int clock_state_update(
ClockState *sp,
sd_event *event) {
char buf[MAX((size_t)FORMAT_TIMESTAMP_MAX, STRLEN("unrepresentable"))];
usec_t t;
struct timex tx = {};
const char * ts;
usec_t t;
int r;
clock_state_release_timerfd(sp);
@ -151,19 +150,14 @@ static int clock_state_update(ClockState *sp,
* it synchronized. When an NTP source is selected it sets the clock again with clock_adjtime(2) which marks it
* synchronized and also touches /run/systemd/timesync/synchronized which covers the case when the clock wasn't
* "set". */
r = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
r = time_change_fd();
if (r < 0) {
log_error_errno(errno, "Failed to create timerfd: %m");
log_error_errno(r, "Failed to create timerfd: %m");
goto finish;
}
sp->timerfd_fd = r;
r = timerfd_settime(sp->timerfd_fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &its, NULL);
if (r < 0) {
log_error_errno(errno, "Failed to set timerfd conditions: %m");
goto finish;
}
r = adjtimex(&tx);
if (r < 0) {
log_error_errno(errno, "Failed to read adjtimex state: %m");

View File

@ -224,11 +224,6 @@ static int manager_clock_watch(sd_event_source *source, int fd, uint32_t revents
/* wake up when the system time changes underneath us */
static int manager_clock_watch_setup(Manager *m) {
struct itimerspec its = {
.it_value.tv_sec = TIME_T_MAX
};
int r;
assert(m);
@ -236,12 +231,9 @@ static int manager_clock_watch_setup(Manager *m) {
m->event_clock_watch = sd_event_source_unref(m->event_clock_watch);
safe_close(m->clock_watch_fd);
m->clock_watch_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
m->clock_watch_fd = time_change_fd();
if (m->clock_watch_fd < 0)
return log_error_errno(errno, "Failed to create timerfd: %m");
if (timerfd_settime(m->clock_watch_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
return log_error_errno(errno, "Failed to set up timerfd: %m");
return log_error_errno(m->clock_watch_fd, "Failed to create timerfd: %m");
r = sd_event_add_io(m->event, &m->event_clock_watch, m->clock_watch_fd, EPOLLIN, manager_clock_watch, m);
if (r < 0)