[systemd-commits] 4 commits - Makefile.am man/file-hierarchy.xml man/systemd.automount.xml man/systemd.mount.xml src/core src/fstab-generator src/shared

Lennart Poettering lennart at kemper.freedesktop.org
Tue Apr 21 11:42:47 PDT 2015


 Makefile.am                           |    5 
 man/file-hierarchy.xml                |    1 
 man/systemd.automount.xml             |    8 +
 man/systemd.mount.xml                 |    9 +
 src/core/automount.c                  |  239 +++++++++++++++++++++++++++++++---
 src/core/automount.h                  |    6 
 src/core/dbus-automount.c             |    1 
 src/core/load-fragment-gperf.gperf.m4 |    1 
 src/core/mount.c                      |   20 --
 src/core/systemd.pc.in                |    1 
 src/core/unit.c                       |   97 ++++++++-----
 src/fstab-generator/fstab-generator.c |   26 +++
 src/shared/time-util.c                |   15 +-
 src/shared/time-util.h                |    2 
 14 files changed, 350 insertions(+), 81 deletions(-)

New commits:
commit 3bd3150367f5751e4d1dff8e070d2deb3be3489a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Apr 21 20:35:17 2015 +0200

    build-sys: move systemd.pc from pkgconfiglibdir back into pkgconfigdatadir
    
    The original idea of systemd.pc was to contain arch-independent system
    and systemd information. By exposing libdir as part of the fields (added
    in eb39a6239c631873db62f6a942e6cb3dab0a2db4), it started to carry
    arch-dependent data, thus breaking multilib systems. It was then moved
    to pkgconfiglibdir to deal with this (in
    aec432c6134146e138124c4130be2ee89dca07fa), but actually the right
    approach is to simply not include libdir in the .pc file at all.
    
    THis patch hence more or less reverts both commits again, and moves the
    .pc file back into pkgconfigdatadir.
    
    As alternative for querying the systems primary libdir there's now
    "systemd-path system-library-arch", hence a more correct alternative
    exists for querying this variable from the .pc file.

diff --git a/Makefile.am b/Makefile.am
index 6a03bc4..77a92cf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -74,6 +74,7 @@ dbussessionservicedir=@dbussessionservicedir@
 dbussystemservicedir=@dbussystemservicedir@
 pamlibdir=@pamlibdir@
 pamconfdir=@pamconfdir@
+pkgconfigdatadir=$(datadir)/pkgconfig
 pkgconfiglibdir=$(libdir)/pkgconfig
 polkitpolicydir=$(datadir)/polkit-1/actions
 bashcompletiondir=@bashcompletiondir@
@@ -131,6 +132,7 @@ noinst_LTLIBRARIES =
 lib_LTLIBRARIES =
 include_HEADERS =
 noinst_DATA =
+pkgconfigdata_DATA =
 pkgconfiglib_DATA =
 polkitpolicy_in_in_files =
 polkitpolicy_in_files =
@@ -164,6 +166,7 @@ in_in_files = $(filter %.in.in, $(in_files))
 m4_files = $(filter %.m4,$(EXTRA_DIST) $(in_files:.m4.in=.m4))
 
 CLEANFILES = $(BUILT_SOURCES) \
+	$(pkgconfigdata_DATA) \
 	$(pkgconfiglib_DATA) \
 	$(in_files:.in=) $(in_in_files:.in.in=) \
 	$(m4_files:.m4=)
@@ -1346,7 +1349,7 @@ dist_dbussystemservice_DATA += \
 polkitpolicy_in_in_files += \
 	src/core/org.freedesktop.systemd1.policy.in.in
 
-pkgconfiglib_DATA += \
+pkgconfigdata_DATA += \
 	src/core/systemd.pc
 
 nodist_rpmmacros_DATA = \
diff --git a/man/file-hierarchy.xml b/man/file-hierarchy.xml
index df97884..3a5627d 100644
--- a/man/file-hierarchy.xml
+++ b/man/file-hierarchy.xml
@@ -257,7 +257,6 @@
         architecture-dependent, too. To query
         <varname>$libdir</varname> for the primary architecture of the
         system, invoke:
-        <programlisting># pkg-config --variable=libdir systemd</programlisting> or
         <programlisting># systemd-path system-library-arch</programlisting></para></listitem>
 
       </varlistentry>
diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
index d5b86bf..ac52b30 100644
--- a/src/core/systemd.pc.in
+++ b/src/core/systemd.pc.in
@@ -6,7 +6,6 @@
 #  (at your option) any later version.
 
 prefix=@prefix@
-libdir=@libdir@
 systemdutildir=@rootlibexecdir@
 systemdsystemunitdir=@systemunitdir@
 systemdsystempresetdir=@systempresetdir@

commit e911de996a72af7659e4019f03b80f11c476f3f3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Apr 21 20:22:51 2015 +0200

    core: make unit deserialization more defensive

diff --git a/src/core/unit.c b/src/core/unit.c
index 494dee4..e921b48 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2644,6 +2644,40 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
         fprintf(f, "%s=%s\n", key, value);
 }
 
+static int unit_set_cgroup_path(Unit *u, const char *path) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(u);
+
+        if (path) {
+                p = strdup(path);
+                if (!p)
+                        return -ENOMEM;
+        } else
+                p = NULL;
+
+        if (streq_ptr(u->cgroup_path, p))
+                return 0;
+
+        if (p) {
+                r = hashmap_put(u->manager->cgroup_unit, p, u);
+                if (r < 0)
+                        return r;
+        }
+
+        if (u->cgroup_path) {
+                log_unit_debug(u->id, "%s: Changing cgroup path from %s to %s.", u->id, u->cgroup_path, strna(p));
+                hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
+                free(u->cgroup_path);
+        }
+
+        u->cgroup_path = p;
+        p = NULL;
+
+        return 0;
+}
+
 int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
         ExecRuntime **rt = NULL;
         size_t offset;
@@ -2671,7 +2705,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                 l = strstrip(line);
 
                 /* End marker */
-                if (l[0] == 0)
+                if (isempty(l))
                         return 0;
 
                 k = strcspn(l, "=");
@@ -2689,7 +2723,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
 
                                 j = job_new_raw(u);
                                 if (!j)
-                                        return -ENOMEM;
+                                        return log_oom();
 
                                 r = job_deserialize(j, f, fds);
                                 if (r < 0) {
@@ -2739,61 +2773,48 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                         dual_timestamp_deserialize(v, &u->assert_timestamp);
                         continue;
                 } else if (streq(l, "condition-result")) {
-                        int b;
 
-                        b = parse_boolean(v);
-                        if (b < 0)
-                                log_debug("Failed to parse condition result value %s", v);
+                        r = parse_boolean(v);
+                        if (r < 0)
+                                log_debug("Failed to parse condition result value %s, ignoring.", v);
                         else
-                                u->condition_result = b;
+                                u->condition_result = r;
 
                         continue;
 
                 } else if (streq(l, "assert-result")) {
-                        int b;
 
-                        b = parse_boolean(v);
-                        if (b < 0)
-                                log_debug("Failed to parse assert result value %s", v);
+                        r = parse_boolean(v);
+                        if (r < 0)
+                                log_debug("Failed to parse assert result value %s, ignoring.", v);
                         else
-                                u->assert_result = b;
+                                u->assert_result = r;
 
                         continue;
 
                 } else if (streq(l, "transient")) {
-                        int b;
 
-                        b = parse_boolean(v);
-                        if (b < 0)
-                                log_debug("Failed to parse transient bool %s", v);
+                        r = parse_boolean(v);
+                        if (r < 0)
+                                log_debug("Failed to parse transient bool %s, ignoring.", v);
                         else
-                                u->transient = b;
+                                u->transient = r;
 
                         continue;
+
                 } else if (streq(l, "cpuacct-usage-base")) {
 
                         r = safe_atou64(v, &u->cpuacct_usage_base);
                         if (r < 0)
-                                log_debug("Failed to parse CPU usage %s", v);
+                                log_debug("Failed to parse CPU usage %s, ignoring.", v);
 
                         continue;
-                } else if (streq(l, "cgroup")) {
-                        char *s;
-
-                        s = strdup(v);
-                        if (!s)
-                                return -ENOMEM;
-
-                        if (u->cgroup_path) {
-                                void *p;
 
-                                p = hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
-                                log_info("Removing cgroup_path %s from hashmap (%p)", u->cgroup_path, p);
-                                free(u->cgroup_path);
-                        }
+                } else if (streq(l, "cgroup")) {
 
-                        u->cgroup_path = s;
-                        assert(hashmap_put(u->manager->cgroup_unit, s, u) == 1);
+                        r = unit_set_cgroup_path(u, v);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to set cgroup path %s, ignoring: %m", v);
 
                         continue;
                 }
@@ -2801,15 +2822,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                 if (unit_can_serialize(u)) {
                         if (rt) {
                                 r = exec_runtime_deserialize_item(rt, u, l, v, fds);
-                                if (r < 0)
-                                        return r;
+                                if (r < 0) {
+                                        log_unit_warning(u->id, "Failed to deserialize runtime parameter '%s', ignoring.", l);
+                                        continue;
+                                }
+
+                                /* Returns positive if key was handled by the call */
                                 if (r > 0)
                                         continue;
                         }
 
                         r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
                         if (r < 0)
-                                return r;
+                                log_unit_warning(u->id, "Failed to deserialize unit parameter '%s', ignoring.", l);
                 }
         }
 }
diff --git a/src/shared/time-util.c b/src/shared/time-util.c
index 1c36c57..12f1b19 100644
--- a/src/shared/time-util.c
+++ b/src/shared/time-util.c
@@ -398,18 +398,21 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
                 t->monotonic);
 }
 
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
         unsigned long long a, b;
 
         assert(value);
         assert(t);
 
-        if (sscanf(value, "%llu %llu", &a, &b) != 2)
-                log_debug("Failed to parse finish timestamp value %s", value);
-        else {
-                t->realtime = a;
-                t->monotonic = b;
+        if (sscanf(value, "%llu %llu", &a, &b) != 2) {
+                log_debug("Failed to parse finish timestamp value %s.", value);
+                return -EINVAL;
         }
+
+        t->realtime = a;
+        t->monotonic = b;
+
+        return 0;
 }
 
 int parse_timestamp(const char *t, usec_t *usec) {
diff --git a/src/shared/time-util.h b/src/shared/time-util.h
index fca8a4d..7a64d45 100644
--- a/src/shared/time-util.h
+++ b/src/shared/time-util.h
@@ -94,7 +94,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t);
 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
 
 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t);
+int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
 
 int parse_timestamp(const char *t, usec_t *usec);
 

commit f34beace377a6cce4e148182b434c6d975b0d012
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Apr 21 18:43:57 2015 +0200

    automount: various smaller fixes

diff --git a/src/core/automount.c b/src/core/automount.c
index 82c3e3d..866fedd 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -363,7 +363,7 @@ static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
         if (param->ioctlfd < 0)
                 return -EIO;
 
-        fd_cloexec(param->ioctlfd, true);
+        (void) fd_cloexec(param->ioctlfd, true);
         return param->ioctlfd;
 }
 
@@ -713,8 +713,7 @@ static void automount_enter_runnning(Automount *a) {
 
         /* Before we do anything, let's see if somebody is playing games with us? */
         if (lstat(a->where, &st) < 0) {
-                log_unit_warning(UNIT(a)->id,
-                                 "%s failed to stat automount point: %m", UNIT(a)->id);
+                log_unit_warning_errno(UNIT(a)->id, errno, "%s failed to stat automount point: %m", UNIT(a)->id);
                 goto fail;
         }
 
@@ -840,13 +839,15 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
                 if (safe_atou(value, &token) < 0)
                         log_unit_debug(u->id, "Failed to parse token value %s", value);
                 else {
-                        if (!a->tokens)
-                                if (!(a->tokens = set_new(NULL)))
-                                        return -ENOMEM;
+                        r = set_ensure_allocated(&a->tokens, NULL);
+                        if (r < 0) {
+                                log_oom();
+                                return 0;
+                        }
 
                         r = set_put(a->tokens, UINT_TO_PTR(token));
                         if (r < 0)
-                                return r;
+                                log_unit_error_errno(u->id, r, "Failed to add token to set: %m");
                 }
         } else if (streq(key, "expire-token")) {
                 unsigned token;

commit deb0a77cf0b409141c4b116ae30becb3d878e1ad
Author: Michael Olbrich <m.olbrich at pengutronix.de>
Date:   Tue Apr 14 22:01:48 2015 +0200

    automount: add expire support

diff --git a/man/systemd.automount.xml b/man/systemd.automount.xml
index b5b5885..9561590 100644
--- a/man/systemd.automount.xml
+++ b/man/systemd.automount.xml
@@ -135,6 +135,14 @@
         creating these directories. Takes an access mode in octal
         notation. Defaults to 0755.</para></listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>TimeoutIdleSec=</varname></term>
+        <listitem><para>Configures an idleness timeout. Once the mount has been
+        idle for the specified time, systemd will attempt to unmount. Takes a
+        unit-less value in seconds, or a time span value such as "5min 20s".
+        Pass 0 to disable the timeout logic. The timeout is disabled by
+        default.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index fcb9a44..e102d27 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -148,6 +148,15 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>x-systemd.idle-timeout=</option></term>
+
+        <listitem><para>Configures the idleness timeout of the
+        automount unit. See <varname>TimeoutIdleSec=</varname> in
+        <citerefentry><refentrytitle>systemd.automount</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>x-systemd.device-timeout=</option></term>
 
         <listitem><para>Configure how long systemd should wait for a
diff --git a/src/core/automount.c b/src/core/automount.c
index ce484ff..82c3e3d 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -42,6 +42,7 @@
 #include "bus-error.h"
 #include "formats-util.h"
 #include "process-util.h"
+#include "async.h"
 
 static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
         [AUTOMOUNT_DEAD] = UNIT_INACTIVE,
@@ -50,6 +51,22 @@ static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
         [AUTOMOUNT_FAILED] = UNIT_FAILED
 };
 
+struct expire_data {
+        int dev_autofs_fd;
+        int ioctl_fd;
+};
+
+static inline void expire_data_free(struct expire_data *data) {
+        if (!data)
+                return;
+
+        safe_close(data->dev_autofs_fd);
+        safe_close(data->ioctl_fd);
+        free(data);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free);
+
 static int open_dev_autofs(Manager *m);
 static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata);
 
@@ -81,13 +98,16 @@ static void repeat_unmount(const char *path) {
         }
 }
 
+static int automount_send_ready(Automount *a, Set *tokens, int status);
+
 static void unmount_autofs(Automount *a) {
         assert(a);
 
         if (a->pipe_fd < 0)
                 return;
 
-        automount_send_ready(a, -EHOSTDOWN);
+        automount_send_ready(a, a->tokens, -EHOSTDOWN);
+        automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
 
         a->pipe_event_source = sd_event_source_unref(a->pipe_event_source);
         a->pipe_fd = safe_close(a->pipe_fd);
@@ -112,6 +132,10 @@ static void automount_done(Unit *u) {
 
         set_free(a->tokens);
         a->tokens = NULL;
+        set_free(a->expire_tokens);
+        a->expire_tokens = NULL;
+
+        a->expire_event_source = sd_event_source_unref(a->expire_event_source);
 }
 
 static int automount_add_mount_links(Automount *a) {
@@ -265,6 +289,7 @@ static int automount_coldplug(Unit *u, Hashmap *deferred_work) {
 }
 
 static void automount_dump(Unit *u, FILE *f, const char *prefix) {
+        char time_string[FORMAT_TIMESPAN_MAX];
         Automount *a = AUTOMOUNT(u);
 
         assert(a);
@@ -273,11 +298,13 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sAutomount State: %s\n"
                 "%sResult: %s\n"
                 "%sWhere: %s\n"
-                "%sDirectoryMode: %04o\n",
+                "%sDirectoryMode: %04o\n"
+                "%sTimeoutIdleUSec: %s\n",
                 prefix, automount_state_to_string(a->state),
                 prefix, automount_result_to_string(a->result),
                 prefix, a->where,
-                prefix, a->directory_mode);
+                prefix, a->directory_mode,
+                prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, a->timeout_idle_usec, USEC_PER_SEC));
 }
 
 static void automount_enter_dead(Automount *a, AutomountResult f) {
@@ -367,7 +394,7 @@ static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) {
         return 0;
 }
 
-static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) {
+static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, usec_t usec) {
         struct autofs_dev_ioctl param;
 
         assert(dev_autofs_fd >= 0);
@@ -375,7 +402,9 @@ static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) {
 
         init_autofs_dev_ioctl(&param);
         param.ioctlfd = ioctl_fd;
-        param.timeout.timeout = sec;
+
+        /* Convert to seconds, rounding up. */
+        param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC;
 
         if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, &param) < 0)
                 return -errno;
@@ -404,7 +433,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in
         return 0;
 }
 
-int automount_send_ready(Automount *a, int status) {
+static int automount_send_ready(Automount *a, Set *tokens, int status) {
         _cleanup_close_ int ioctl_fd = -1;
         unsigned token;
         int r;
@@ -412,7 +441,7 @@ int automount_send_ready(Automount *a, int status) {
         assert(a);
         assert(status <= 0);
 
-        if (set_isempty(a->tokens))
+        if (set_isempty(tokens))
                 return 0;
 
         ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id);
@@ -427,7 +456,7 @@ int automount_send_ready(Automount *a, int status) {
         r = 0;
 
         /* Autofs thankfully does not hand out 0 as a token */
-        while ((token = PTR_TO_UINT(set_steal_first(a->tokens)))) {
+        while ((token = PTR_TO_UINT(set_steal_first(tokens)))) {
                 int k;
 
                 /* Autofs fun fact II:
@@ -446,6 +475,55 @@ int automount_send_ready(Automount *a, int status) {
         return r;
 }
 
+int automount_update_mount(Automount *a, MountState old_state, MountState state) {
+        _cleanup_close_ int ioctl_fd = -1;
+
+        assert(a);
+
+        switch (state) {
+        case MOUNT_MOUNTED:
+        case MOUNT_REMOUNTING:
+                automount_send_ready(a, a->tokens, 0);
+                break;
+         case MOUNT_DEAD:
+         case MOUNT_UNMOUNTING:
+         case MOUNT_MOUNTING_SIGTERM:
+         case MOUNT_MOUNTING_SIGKILL:
+         case MOUNT_REMOUNTING_SIGTERM:
+         case MOUNT_REMOUNTING_SIGKILL:
+         case MOUNT_UNMOUNTING_SIGTERM:
+         case MOUNT_UNMOUNTING_SIGKILL:
+         case MOUNT_FAILED:
+                if (old_state != state)
+                        automount_send_ready(a, a->tokens, -ENODEV);
+                break;
+        default:
+                break;
+        }
+
+        switch (state) {
+        case MOUNT_DEAD:
+                automount_send_ready(a, a->expire_tokens, 0);
+                break;
+         case MOUNT_MOUNTING:
+         case MOUNT_MOUNTING_DONE:
+         case MOUNT_MOUNTING_SIGTERM:
+         case MOUNT_MOUNTING_SIGKILL:
+         case MOUNT_REMOUNTING_SIGTERM:
+         case MOUNT_REMOUNTING_SIGKILL:
+         case MOUNT_UNMOUNTING_SIGTERM:
+         case MOUNT_UNMOUNTING_SIGKILL:
+         case MOUNT_FAILED:
+                if (old_state != state)
+                        automount_send_ready(a, a->expire_tokens, -ENODEV);
+                break;
+        default:
+                break;
+        }
+
+        return 0;
+}
+
 static void automount_enter_waiting(Automount *a) {
         _cleanup_close_ int ioctl_fd = -1;
         int p[2] = { -1, -1 };
@@ -505,7 +583,7 @@ static void automount_enter_waiting(Automount *a) {
         if (r < 0)
                 goto fail;
 
-        r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300);
+        r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec);
         if (r < 0)
                 goto fail;
 
@@ -537,6 +615,83 @@ fail:
         automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
 }
 
+static void *expire_thread(void *p) {
+        struct autofs_dev_ioctl param;
+        _cleanup_(expire_data_freep) struct expire_data *data = (struct expire_data*)p;
+        int r;
+
+        assert(data->dev_autofs_fd >= 0);
+        assert(data->ioctl_fd >= 0);
+
+        init_autofs_dev_ioctl(&param);
+        param.ioctlfd = data->ioctl_fd;
+
+        do {
+                r = ioctl(data->dev_autofs_fd, AUTOFS_DEV_IOCTL_EXPIRE, &param);
+        } while (r >= 0);
+
+        if (errno != EAGAIN)
+                log_warning_errno(errno, "Failed to expire automount, ignoring: %m");
+
+        return NULL;
+}
+
+static int automount_start_expire(Automount *a);
+
+static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) {
+        Automount *a = AUTOMOUNT(userdata);
+        _cleanup_(expire_data_freep) struct expire_data *data = NULL;
+        int r;
+
+        assert(a);
+        assert(source == a->expire_event_source);
+
+        data = new0(struct expire_data, 1);
+        if (!data)
+                return log_oom();
+
+        data->ioctl_fd = -1;
+
+        data->dev_autofs_fd = fcntl(UNIT(a)->manager->dev_autofs_fd, F_DUPFD_CLOEXEC, 3);
+        if (data->dev_autofs_fd < 0)
+                return log_unit_error_errno(UNIT(a)->id, errno, "Failed to duplicate autofs fd: %m");
+
+        data->ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id);
+        if (data->ioctl_fd < 0)
+                return log_unit_error_errno(UNIT(a)->id, data->ioctl_fd, "Couldn't open autofs ioctl fd: %m");
+
+        r = asynchronous_job(expire_thread, data);
+        if (r < 0)
+                return log_unit_error_errno(UNIT(a)->id, r, "Failed to start expire job: %m");
+
+        data = NULL;
+
+        return automount_start_expire(a);
+}
+
+static int automount_start_expire(Automount *a) {
+        int r;
+        usec_t timeout;
+
+        assert(a);
+
+        timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/10, USEC_PER_SEC);
+
+        if (a->expire_event_source) {
+                r = sd_event_source_set_time(a->expire_event_source, timeout);
+                if (r < 0)
+                        return r;
+
+                return sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_ONESHOT);
+        }
+
+        return sd_event_add_time(
+                        UNIT(a)->manager->event,
+                        &a->expire_event_source,
+                        CLOCK_MONOTONIC, timeout, 0,
+                        automount_dispatch_expire, a);
+}
+
 static void automount_enter_runnning(Automount *a) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         struct stat st;
@@ -549,7 +704,8 @@ static void automount_enter_runnning(Automount *a) {
         if (unit_stop_pending(UNIT(a))) {
                 log_unit_debug(UNIT(a)->id,
                                "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
-                automount_send_ready(a, -EHOSTDOWN);
+                automount_send_ready(a, a->tokens, -EHOSTDOWN);
+                automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
                 return;
         }
 
@@ -576,6 +732,10 @@ static void automount_enter_runnning(Automount *a) {
                 }
         }
 
+        r = automount_start_expire(a);
+        if (r < 0)
+                log_unit_warning_errno(UNIT(a)->id, r, "Failed to start expiration timer, ignoring: %m");
+
         automount_set_state(a, AUTOMOUNT_RUNNING);
         return;
 
@@ -627,6 +787,8 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
 
         SET_FOREACH(p, a->tokens, i)
                 unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
+        SET_FOREACH(p, a->expire_tokens, i)
+                unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p));
 
         if (a->pipe_fd >= 0) {
                 int copy;
@@ -686,6 +848,22 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
                         if (r < 0)
                                 return r;
                 }
+        } else if (streq(key, "expire-token")) {
+                unsigned token;
+
+                if (safe_atou(value, &token) < 0)
+                        log_unit_debug(u->id, "Failed to parse token value %s", value);
+                else {
+                        r = set_ensure_allocated(&a->expire_tokens, NULL);
+                        if (r < 0) {
+                                log_oom();
+                                return 0;
+                        }
+
+                        r = set_put(a->expire_tokens, UINT_TO_PTR(token));
+                        if (r < 0)
+                                log_unit_error_errno(u->id, r, "Failed to add expire token to set: %m");
+                }
         } else if (streq(key, "pipe-fd")) {
                 int fd;
 
@@ -723,6 +901,7 @@ static bool automount_check_gc(Unit *u) {
 }
 
 static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         union autofs_v5_packet_union packet;
         Automount *a = AUTOMOUNT(userdata);
         int r;
@@ -771,6 +950,31 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
                 automount_enter_runnning(a);
                 break;
 
+        case autofs_ptype_expire_direct:
+                log_unit_debug(UNIT(a)->id, "Got direct umount request on %s", a->where);
+
+                (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+
+                r = set_ensure_allocated(&a->expire_tokens, NULL);
+                if (r < 0) {
+                        log_unit_error(UNIT(a)->id, "Failed to allocate token set.");
+                        goto fail;
+                }
+
+                r = set_put(a->expire_tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
+                if (r < 0) {
+                        log_unit_error_errno(UNIT(a)->id, r, "Failed to remember token: %m");
+                        goto fail;
+                }
+                r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL);
+                if (r < 0) {
+                        log_unit_warning(UNIT(a)->id,
+                                         "%s failed to queue umount startup job: %s",
+                                         UNIT(a)->id, bus_error_message(&error, r));
+                        goto fail;
+                }
+                break;
+
         default:
                 log_unit_error(UNIT(a)->id, "Received unknown automount request %i", packet.hdr.type);
                 break;
diff --git a/src/core/automount.h b/src/core/automount.h
index 60f5522..2a50fef 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -47,6 +47,7 @@ struct Automount {
         AutomountState state, deserialized_state;
 
         char *where;
+        usec_t timeout_idle_usec;
 
         int pipe_fd;
         sd_event_source *pipe_event_source;
@@ -54,13 +55,16 @@ struct Automount {
         dev_t dev_id;
 
         Set *tokens;
+        Set *expire_tokens;
+
+        sd_event_source *expire_event_source;
 
         AutomountResult result;
 };
 
 extern const UnitVTable automount_vtable;
 
-int automount_send_ready(Automount *a, int status);
+int automount_update_mount(Automount *a, MountState old_state, MountState state);
 
 const char* automount_state_to_string(AutomountState i) _const_;
 AutomountState automount_state_from_string(const char *s) _pure_;
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index 38acbd0..5162ce3 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -30,5 +30,6 @@ const sd_bus_vtable bus_automount_vtable[] = {
         SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Automount, where), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Automount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Automount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_VTABLE_END
 };
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 5305984..66c9145 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -318,6 +318,7 @@ KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
 m4_dnl
 Automount.Where,                 config_parse_path,                  0,                             offsetof(Automount, where)
 Automount.DirectoryMode,         config_parse_mode,                  0,                             offsetof(Automount, directory_mode)
+Automount.TimeoutIdleSec,        config_parse_sec,                   0,                             offsetof(Automount, timeout_idle_usec)
 m4_dnl
 Swap.What,                       config_parse_path,                  0,                             offsetof(Swap, parameters_fragment.what)
 Swap.Priority,                   config_parse_int,                   0,                             offsetof(Swap, parameters_fragment.priority)
diff --git a/src/core/mount.c b/src/core/mount.c
index 8bfbbed..d3a4098 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -548,7 +548,7 @@ static int mount_load(Unit *u) {
         return mount_verify(m);
 }
 
-static int mount_notify_automount(Mount *m, int status) {
+static int mount_notify_automount(Mount *m, MountState old_state, MountState state) {
         Unit *p;
         int r;
         Iterator i;
@@ -557,7 +557,7 @@ static int mount_notify_automount(Mount *m, int status) {
 
         SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i)
                 if (p->type == UNIT_AUTOMOUNT) {
-                         r = automount_send_ready(AUTOMOUNT(p), status);
+                         r = automount_update_mount(AUTOMOUNT(p), old_state, state);
                          if (r < 0)
                                  return r;
                 }
@@ -588,21 +588,7 @@ static void mount_set_state(Mount *m, MountState state) {
                 m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
         }
 
-        if (state == MOUNT_MOUNTED ||
-            state == MOUNT_REMOUNTING)
-                mount_notify_automount(m, 0);
-        else if (state == MOUNT_DEAD ||
-                 state == MOUNT_UNMOUNTING ||
-                 state == MOUNT_MOUNTING_SIGTERM ||
-                 state == MOUNT_MOUNTING_SIGKILL ||
-                 state == MOUNT_REMOUNTING_SIGTERM ||
-                 state == MOUNT_REMOUNTING_SIGKILL ||
-                 state == MOUNT_UNMOUNTING_SIGTERM ||
-                 state == MOUNT_UNMOUNTING_SIGKILL ||
-                 state == MOUNT_FAILED) {
-                if (state != old_state)
-                        mount_notify_automount(m, -ENODEV);
-        }
+        mount_notify_automount(m, old_state, state);
 
         if (state != old_state)
                 log_unit_debug(UNIT(m)->id,
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 57fb7c3..cb5112e 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -154,6 +154,28 @@ static bool mount_in_initrd(struct mntent *me) {
                streq(me->mnt_dir, "/usr");
 }
 
+static int write_idle_timeout(FILE *f, const char *where, const char *opts, char **filtered) {
+        _cleanup_free_ char *timeout = NULL;
+        char timespan[FORMAT_TIMESPAN_MAX];
+        usec_t u;
+        int r;
+
+        r = fstab_filter_options(opts, "x-systemd.idle-timeout\0", NULL, &timeout, filtered);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to parse options: %m");
+        if (r == 0)
+                return 0;
+
+        r = parse_sec(timeout, &u);
+        if (r < 0) {
+                log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
+                return 0;
+        }
+
+        fprintf(f, "TimeoutIdleSec=%s\n", format_timespan(timespan, sizeof(timespan), u, 0));
+
+        return 0;
+}
 static int add_mount(
                 const char *what,
                 const char *where,
@@ -293,6 +315,10 @@ static int add_mount(
                         "Where=%s\n",
                         where);
 
+                r = write_idle_timeout(f, where, opts, &filtered);
+                if (r < 0)
+                        return r;
+
                 fflush(f);
                 if (ferror(f))
                         return log_error_errno(errno, "Failed to write unit file %s: %m", automount_unit);



More information about the systemd-commits mailing list