+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
[MOUNT_MOUNTED] = UNIT_ACTIVE,
[MOUNT_REMOUNTING] = UNIT_RELOADING,
[MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
- [MOUNT_MOUNTING_SIGTERM] = UNIT_DEACTIVATING,
- [MOUNT_MOUNTING_SIGKILL] = UNIT_DEACTIVATING,
[MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING,
[MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING,
[MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING,
static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static bool MOUNT_STATE_WITH_PROCESS(MountState state) {
+ return IN_SET(state,
+ MOUNT_MOUNTING,
+ MOUNT_MOUNTING_DONE,
+ MOUNT_REMOUNTING,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL);
+}
+
static bool mount_needs_network(const char *options, const char *fstype) {
if (fstab_test_option(options, "_netdev\0"))
return true;
"x-systemd.automount\0");
}
-static bool mount_state_active(MountState state) {
- return IN_SET(state,
- MOUNT_MOUNTING,
- MOUNT_MOUNTING_DONE,
- MOUNT_REMOUNTING,
- MOUNT_UNMOUNTING,
- MOUNT_MOUNTING_SIGTERM,
- MOUNT_MOUNTING_SIGKILL,
- MOUNT_UNMOUNTING_SIGTERM,
- MOUNT_UNMOUNTING_SIGKILL,
- MOUNT_REMOUNTING_SIGTERM,
- MOUNT_REMOUNTING_SIGKILL);
+static bool mount_is_bound_to_device(const Mount *m) {
+ const MountParameters *p;
+
+ if (m->from_fragment)
+ return true;
+
+ p = &m->parameters_proc_self_mountinfo;
+ return fstab_test_option(p->options, "x-systemd.device-bound\0");
}
static bool needs_quota(const MountParameters *p) {
return get_mount_parameters_fragment(m);
}
-static int mount_add_mount_links(Mount *m) {
- _cleanup_free_ char *parent = NULL;
+static int mount_add_mount_dependencies(Mount *m) {
MountParameters *pm;
Unit *other;
Iterator i;
assert(m);
if (!path_equal(m->where, "/")) {
- /* Adds in links to other mount points that might lie further
- * up in the hierarchy */
+ _cleanup_free_ char *parent = NULL;
+
+ /* Adds in links to other mount points that might lie further up in the hierarchy */
parent = dirname_malloc(m->where);
if (!parent)
return -ENOMEM;
- r = unit_require_mounts_for(UNIT(m), parent);
+ r = unit_require_mounts_for(UNIT(m), parent, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return r;
}
- /* Adds in links to other mount points that might be needed
- * for the source path (if this is a bind mount or a loop mount) to be
- * available. */
+ /* Adds in dependencies to other mount points that might be needed for the source path (if this is a bind mount
+ * or a loop mount) to be available. */
pm = get_mount_parameters_fragment(m);
if (pm && pm->what &&
path_is_absolute(pm->what) &&
(mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) {
- r = unit_require_mounts_for(UNIT(m), pm->what);
+ r = unit_require_mounts_for(UNIT(m), pm->what, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
- /* Adds in links to other units that use this path or paths
- * further down in the hierarchy */
+ /* Adds in dependencies to other units that use this path or paths further down in the hierarchy */
s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where);
SET_FOREACH(other, s, i) {
if (other == UNIT(m))
continue;
- r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true);
+ r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true, UNIT_DEPENDENCY_PATH);
if (r < 0)
return r;
if (UNIT(m)->fragment_path) {
/* If we have fragment configuration, then make this dependency required */
- r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true);
+ r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true, UNIT_DEPENDENCY_PATH);
if (r < 0)
return r;
}
return 0;
}
-static int mount_add_device_links(Mount *m) {
- MountParameters *p;
+static int mount_add_device_dependencies(Mount *m) {
bool device_wants_mount = false;
+ UnitDependencyMask mask;
+ MountParameters *p;
+ UnitDependency dep;
int r;
assert(m);
if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager))
device_wants_mount = true;
- r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES);
+ /* Mount units from /proc/self/mountinfo are not bound to devices
+ * by default since they're subject to races when devices are
+ * unplugged. But the user can still force this dep with an
+ * appropriate option (or udev property) so the mount units are
+ * automatically stopped when the device disappears suddenly. */
+ dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
+
+ mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT;
+
+ r = unit_add_node_dependency(UNIT(m), p->what, device_wants_mount, dep, mask);
if (r < 0)
return r;
return 0;
}
-static int mount_add_quota_links(Mount *m) {
- int r;
+static int mount_add_quota_dependencies(Mount *m) {
+ UnitDependencyMask mask;
MountParameters *p;
+ int r;
assert(m);
if (!needs_quota(p))
return 0;
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true);
+ mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT;
+
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true, mask);
if (r < 0)
return r;
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true);
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true, mask);
if (r < 0)
return r;
return 0;
}
-static bool should_umount(Mount *m) {
+static bool mount_is_extrinsic(Mount *m) {
MountParameters *p;
+ assert(m);
- if (PATH_IN_SET(m->where, "/", "/usr") ||
- path_startswith(m->where, "/run/initramfs"))
- return false;
+ /* Returns true for all units that are "magic" and should be excluded from the usual start-up and shutdown
+ * dependencies. We call them "extrinsic" here, as they are generally mounted outside of the systemd dependency
+ * logic. We shouldn't attempt to manage them ourselves but it's fine if the user operates on them with us. */
+
+ if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) /* We only automatically manage mounts if we are in system mode */
+ return true;
+ if (PATH_IN_SET(m->where, /* Don't bother with the OS data itself */
+ "/",
+ "/usr"))
+ return true;
+
+ if (PATH_STARTSWITH_SET(m->where,
+ "/run/initramfs", /* This should stay around from before we boot until after we shutdown */
+ "/proc", /* All of this is API VFS */
+ "/sys", /* … dito … */
+ "/dev")) /* … dito … */
+ return true;
+
+ /* If this is an initrd mount, and we are not in the initrd, then leave this around forever, too. */
p = get_mount_parameters(m);
- if (p && fstab_test_option(p->options, "x-initrd.mount\0") &&
- !in_initrd())
- return false;
+ if (p && fstab_test_option(p->options, "x-initrd.mount\0") && !in_initrd())
+ return true;
- return true;
+ return false;
}
static int mount_add_default_dependencies(Mount *m) {
+ UnitDependencyMask mask;
+ int r;
MountParameters *p;
const char *after;
- int r;
assert(m);
if (!UNIT(m)->default_dependencies)
return 0;
- if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
- return 0;
-
- /* We do not add any default dependencies to /, /usr or
- * /run/initramfs/, since they are guaranteed to stay
- * mounted the whole time, since our system is on it.
- * Also, don't bother with anything mounted below virtual
- * file systems, it's also going to be virtual, and hence
- * not worth the effort. */
- if (PATH_IN_SET(m->where, "/", "/usr") ||
- path_startswith(m->where, "/run/initramfs") ||
- path_startswith(m->where, "/proc") ||
- path_startswith(m->where, "/sys") ||
- path_startswith(m->where, "/dev"))
+ /* We do not add any default dependencies to /, /usr or /run/initramfs/, since they are guaranteed to stay
+ * mounted the whole time, since our system is on it. Also, don't bother with anything mounted below virtual
+ * file systems, it's also going to be virtual, and hence not worth the effort. */
+ if (mount_is_extrinsic(m))
return 0;
p = get_mount_parameters(m);
if (!p)
return 0;
+ mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_DEFAULT;
+
if (mount_is_network(p)) {
/* We order ourselves after network.target. This is
* primarily useful at shutdown: services that take
* network.target, so that they are shut down only
* after this mount unit is stopped. */
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true, mask);
if (r < 0)
return r;
* whose purpose it is to delay this until the network
* is "up". */
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true);
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true, mask);
if (r < 0)
return r;
} else
after = SPECIAL_LOCAL_FS_PRE_TARGET;
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true, mask);
+ if (r < 0)
+ return r;
+
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, mask);
if (r < 0)
return r;
- if (should_umount(m)) {
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
+ /* If this is a tmpfs mount then we have to unmount it before we try to deactivate swaps */
+ if (streq_ptr(p->fstype, "tmpfs")) {
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, NULL, true, mask);
if (r < 0)
return r;
}
if (UNIT(m)->load_state != UNIT_LOADED)
return 0;
- if (!m->from_fragment && !m->from_proc_self_mountinfo)
+ if (!m->from_fragment && !m->from_proc_self_mountinfo && !UNIT(m)->perpetual)
return -ENOENT;
r = unit_name_from_path(m->where, ".mount", &e);
return r;
}
- r = mount_add_device_links(m);
+ r = mount_add_device_dependencies(m);
if (r < 0)
return r;
- r = mount_add_mount_links(m);
+ r = mount_add_mount_dependencies(m);
if (r < 0)
return r;
- r = mount_add_quota_links(m);
+ r = mount_add_quota_dependencies(m);
if (r < 0)
return r;
old_state = m->state;
m->state = state;
- if (!mount_state_active(state)) {
+ if (!MOUNT_STATE_WITH_PROCESS(state)) {
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
mount_unwatch_control_pid(m);
m->control_command = NULL;
log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state));
unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS);
- m->reload_result = MOUNT_SUCCESS;
}
static int mount_coldplug(Unit *u) {
if (m->control_pid > 0 &&
pid_is_unwaited(m->control_pid) &&
- mount_state_active(new_state)) {
+ MOUNT_STATE_WITH_PROCESS(new_state)) {
r = unit_watch_pid(UNIT(m), m->control_pid);
if (r < 0)
"%sOptions: %s\n"
"%sFrom /proc/self/mountinfo: %s\n"
"%sFrom fragment: %s\n"
+ "%sExtrinsic: %s\n"
"%sDirectoryMode: %04o\n"
"%sSloppyOptions: %s\n"
"%sLazyUnmount: %s\n"
prefix, p ? strna(p->options) : "n/a",
prefix, yes_no(m->from_proc_self_mountinfo),
prefix, yes_no(m->from_fragment),
+ prefix, yes_no(mount_is_extrinsic(m)),
prefix, m->directory_mode,
prefix, yes_no(m->sloppy_options),
prefix, yes_no(m->lazy_unmount),
exec_context_dump(&m->exec_context, f, prefix);
kill_context_dump(&m->kill_context, f, prefix);
+ cgroup_context_dump(&m->cgroup_context, f, prefix);
}
static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
- .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
.stdin_fd = -1,
.stdout_fd = -1,
.stderr_fd = -1,
assert(_pid);
(void) unit_realize_cgroup(UNIT(m));
- if (m->reset_cpu_usage) {
- (void) unit_reset_cpu_usage(UNIT(m));
- m->reset_cpu_usage = false;
+ if (m->reset_accounting) {
+ (void) unit_reset_cpu_accounting(UNIT(m));
+ (void) unit_reset_ip_accounting(UNIT(m));
+ m->reset_accounting = false;
}
+ unit_export_state_files(UNIT(m));
+
r = unit_setup_exec_runtime(UNIT(m));
if (r < 0)
return r;
if (r < 0)
return r;
- exec_params.environment = UNIT(m)->manager->environment;
- exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(m)->manager);
- exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported;
- exec_params.cgroup_path = UNIT(m)->cgroup_path;
- exec_params.cgroup_delegate = m->cgroup_context.delegate;
- exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(m)->manager);
+ manager_set_exec_params(UNIT(m)->manager, &exec_params);
+ unit_set_exec_params(UNIT(m), &exec_params);
r = exec_spawn(UNIT(m),
c,
if (m->result == MOUNT_SUCCESS)
m->result = f;
+ if (m->result != MOUNT_SUCCESS)
+ log_unit_warning(UNIT(m), "Failed with result '%s'.", mount_result_to_string(m->result));
+
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
exec_runtime_destroy(m->exec_runtime);
m->exec_runtime = exec_runtime_unref(m->exec_runtime);
- exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager));
+ exec_context_destroy_runtime_directory(&m->exec_context, UNIT(m)->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
unit_unref_uid_gid(UNIT(m), true);
mount_set_state(m, MOUNT_MOUNTED);
}
+static void mount_enter_dead_or_mounted(Mount *m, MountResult f) {
+ assert(m);
+
+ /* Enter DEAD or MOUNTED state, depending on what the kernel currently says about the mount point. We use this
+ * whenever we executed an operation, so that our internal state reflects what the kernel says again, after all
+ * ultimately we just mirror the kernel's internal state on this. */
+
+ if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, f);
+ else
+ mount_enter_dead(m, f);
+}
+
+static int state_to_kill_operation(MountState state) {
+ switch (state) {
+
+ case MOUNT_REMOUNTING_SIGTERM:
+ case MOUNT_UNMOUNTING_SIGTERM:
+ return KILL_TERMINATE;
+
+ case MOUNT_REMOUNTING_SIGKILL:
+ case MOUNT_UNMOUNTING_SIGKILL:
+ return KILL_KILL;
+
+ default:
+ return _KILL_OPERATION_INVALID;
+ }
+}
+
static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
int r;
r = unit_kill_context(
UNIT(m),
&m->kill_context,
- (state != MOUNT_MOUNTING_SIGTERM && state != MOUNT_UNMOUNTING_SIGTERM && state != MOUNT_REMOUNTING_SIGTERM) ?
- KILL_KILL : KILL_TERMINATE,
+ state_to_kill_operation(state),
-1,
m->control_pid,
false);
goto fail;
mount_set_state(m, state);
- } else if (state == MOUNT_REMOUNTING_SIGTERM)
+ } else if (state == MOUNT_REMOUNTING_SIGTERM && m->kill_context.send_sigkill)
mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_SUCCESS);
- else if (state == MOUNT_REMOUNTING_SIGKILL)
+ else if (IN_SET(state, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL))
mount_enter_mounted(m, MOUNT_SUCCESS);
- else if (state == MOUNT_MOUNTING_SIGTERM)
- mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_SUCCESS);
- else if (state == MOUNT_UNMOUNTING_SIGTERM)
+ else if (state == MOUNT_UNMOUNTING_SIGTERM && m->kill_context.send_sigkill)
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_SUCCESS);
else
- mount_enter_dead(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
return;
fail:
log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m");
-
- if (IN_SET(state, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL))
- mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
- else
- mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
}
static void mount_enter_unmounting(Mount *m) {
m->control_command_id = MOUNT_EXEC_UNMOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
- r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);
+ r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, "-c", NULL);
if (r >= 0 && m->lazy_unmount)
r = exec_command_append(m->control_command, "-l", NULL);
if (r >= 0 && m->force_unmount)
fail:
log_unit_warning_errno(UNIT(m), r, "Failed to run 'umount' task: %m");
- mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
}
static void mount_enter_mounting(Mount *m) {
r = exec_command_append(m->control_command, "-o", opts, NULL);
} else
r = -ENOENT;
-
if (r < 0)
goto fail;
fail:
log_unit_warning_errno(UNIT(m), r, "Failed to run 'mount' task: %m");
- mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+}
+
+static void mount_set_reload_result(Mount *m, MountResult result) {
+ assert(m);
+
+ /* Only store the first error we encounter */
+ if (m->reload_result != MOUNT_SUCCESS)
+ return;
+
+ m->reload_result = result;
}
static void mount_enter_remounting(Mount *m) {
assert(m);
+ /* Reset reload result when we are about to start a new remount operation */
+ m->reload_result = MOUNT_SUCCESS;
+
m->control_command_id = MOUNT_EXEC_REMOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
} else
r = -ENOENT;
-
if (r < 0)
goto fail;
fail:
log_unit_warning_errno(UNIT(m), r, "Failed to run 'remount' task: %m");
- m->reload_result = MOUNT_FAILURE_RESOURCES;
- mount_enter_mounted(m, MOUNT_SUCCESS);
+ mount_set_reload_result(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
}
static int mount_start(Unit *u) {
if (IN_SET(m->state,
MOUNT_UNMOUNTING,
MOUNT_UNMOUNTING_SIGTERM,
- MOUNT_UNMOUNTING_SIGKILL,
- MOUNT_MOUNTING_SIGTERM,
- MOUNT_MOUNTING_SIGKILL))
+ MOUNT_UNMOUNTING_SIGKILL))
return -EAGAIN;
/* Already on it! */
m->result = MOUNT_SUCCESS;
m->reload_result = MOUNT_SUCCESS;
- m->reset_cpu_usage = true;
+ m->reset_accounting = true;
mount_enter_mounting(m);
return 1;
assert(m);
- /* Already on it */
- if (IN_SET(m->state,
- MOUNT_UNMOUNTING,
- MOUNT_UNMOUNTING_SIGKILL,
- MOUNT_UNMOUNTING_SIGTERM,
- MOUNT_MOUNTING_SIGTERM,
- MOUNT_MOUNTING_SIGKILL))
+ switch (m->state) {
+
+ case MOUNT_UNMOUNTING:
+ case MOUNT_UNMOUNTING_SIGKILL:
+ case MOUNT_UNMOUNTING_SIGTERM:
+ /* Already on it */
return 0;
- assert(IN_SET(m->state,
- MOUNT_MOUNTING,
- MOUNT_MOUNTING_DONE,
- MOUNT_MOUNTED,
- MOUNT_REMOUNTING,
- MOUNT_REMOUNTING_SIGTERM,
- MOUNT_REMOUNTING_SIGKILL));
+ case MOUNT_MOUNTING:
+ case MOUNT_MOUNTING_DONE:
+ case MOUNT_REMOUNTING:
+ /* If we are still waiting for /bin/mount, we go directly into kill mode. */
+ mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_SUCCESS);
+ return 0;
- mount_enter_unmounting(m);
- return 1;
+ case MOUNT_REMOUNTING_SIGTERM:
+ /* If we are already waiting for a hung remount, convert this to the matching unmounting state */
+ mount_set_state(m, MOUNT_UNMOUNTING_SIGTERM);
+ return 0;
+
+ case MOUNT_REMOUNTING_SIGKILL:
+ /* as above */
+ mount_set_state(m, MOUNT_UNMOUNTING_SIGKILL);
+ return 0;
+
+ case MOUNT_MOUNTED:
+ mount_enter_unmounting(m);
+ return 1;
+
+ default:
+ assert_not_reached("Unexpected state.");
+ }
}
static int mount_reload(Unit *u) {
assert(m);
- if (m->state == MOUNT_MOUNTING_DONE)
+ if (m->state == MOUNT_MOUNTING_DONE) /* not yet ready to reload, try again */
return -EAGAIN;
assert(m->state == MOUNT_MOUNTED);
mount_enter_remounting(m);
+
return 1;
}
else
assert_not_reached("Unknown code");
- if (m->result == MOUNT_SUCCESS)
+ if (IN_SET(m->state, MOUNT_REMOUNTING, MOUNT_REMOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM))
+ mount_set_reload_result(m, f);
+ else if (m->result == MOUNT_SUCCESS)
m->result = f;
if (m->control_command) {
log_unit_full(u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
"Mount process exited, code=%s status=%i", sigchld_code_to_string(code), status);
- /* Note that mount(8) returning and the kernel sending us a
- * mount table change event might happen out-of-order. If an
- * operation succeed we assume the kernel will follow soon too
- * and already change into the resulting state. If it fails
- * we check if the kernel still knows about the mount. and
- * change state accordingly. */
+ /* Note that mount(8) returning and the kernel sending us a mount table change event might happen
+ * out-of-order. If an operation succeed we assume the kernel will follow soon too and already change into the
+ * resulting state. If it fails we check if the kernel still knows about the mount. and change state
+ * accordingly. */
switch (m->state) {
case MOUNT_MOUNTING:
case MOUNT_MOUNTING_DONE:
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_MOUNTING_SIGTERM:
if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo)
/* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are
- * happy. If we see the first condition first, we should see the the second condition
+ * happy. If we see the first condition first, we should see the second condition
* immediately after – or /bin/mount lies to us and is broken. */
mount_enter_mounted(m, f);
else
break;
case MOUNT_REMOUNTING:
- case MOUNT_REMOUNTING_SIGKILL:
case MOUNT_REMOUNTING_SIGTERM:
-
- m->reload_result = f;
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_SUCCESS);
- else
- mount_enter_dead(m, MOUNT_SUCCESS);
-
+ case MOUNT_REMOUNTING_SIGKILL:
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
break;
case MOUNT_UNMOUNTING:
case MOUNT_UNMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGTERM:
- if (f == MOUNT_SUCCESS) {
-
- if (m->from_proc_self_mountinfo) {
-
- /* Still a mount point? If so, let's
- * try again. Most likely there were
- * multiple mount points stacked on
- * top of each other. Note that due to
- * the io event priority logic we can
- * be sure the new mountinfo is loaded
- * before we process the SIGCHLD for
- * the mount command. */
-
- if (m->n_retry_umount < RETRY_UMOUNT_MAX) {
- log_unit_debug(u, "Mount still present, trying again.");
- m->n_retry_umount++;
- mount_enter_unmounting(m);
- } else {
- log_unit_debug(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
- mount_enter_mounted(m, f);
- }
- } else
- mount_enter_dead(m, f);
-
- } else if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, f);
- else
+ if (m->from_proc_self_mountinfo) {
+
+ /* Still a mount point? If so, let's try again. Most likely there were multiple mount points
+ * stacked on top of each other. Note that due to the io event priority logic we can be sure
+ * the new mountinfo is loaded before we process the SIGCHLD for the mount command. */
+
+ if (m->n_retry_umount < RETRY_UMOUNT_MAX) {
+ log_unit_debug(u, "Mount still present, trying again.");
+ m->n_retry_umount++;
+ mount_enter_unmounting(m);
+ } else {
+ log_unit_debug(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
+ mount_enter_mounted(m, f);
+ }
+ } else
mount_enter_dead(m, f);
+
break;
default:
case MOUNT_MOUNTING:
case MOUNT_MOUNTING_DONE:
- log_unit_warning(UNIT(m), "Mounting timed out. Stopping.");
- mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
+ log_unit_warning(UNIT(m), "Mounting timed out. Terminating.");
+ mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
break;
case MOUNT_REMOUNTING:
- log_unit_warning(UNIT(m), "Remounting timed out. Stopping.");
- m->reload_result = MOUNT_FAILURE_TIMEOUT;
- mount_enter_mounted(m, MOUNT_SUCCESS);
+ log_unit_warning(UNIT(m), "Remounting timed out. Terminating remount process.");
+ mount_set_reload_result(m, MOUNT_FAILURE_TIMEOUT);
+ mount_enter_signal(m, MOUNT_REMOUNTING_SIGTERM, MOUNT_SUCCESS);
break;
- case MOUNT_UNMOUNTING:
- log_unit_warning(UNIT(m), "Unmounting timed out. Stopping.");
- mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
- break;
+ case MOUNT_REMOUNTING_SIGTERM:
+ mount_set_reload_result(m, MOUNT_FAILURE_TIMEOUT);
- case MOUNT_MOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_unit_warning(UNIT(m), "Mounting timed out. Killing.");
- mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
+ log_unit_warning(UNIT(m), "Remounting timed out. Killing.");
+ mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_SUCCESS);
} else {
- log_unit_warning(UNIT(m), "Mounting timed out. Skipping SIGKILL. Ignoring.");
-
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
+ log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring.");
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
}
break;
- case MOUNT_REMOUNTING_SIGTERM:
- if (m->kill_context.send_sigkill) {
- log_unit_warning(UNIT(m), "Remounting timed out. Killing.");
- mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
- } else {
- log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring.");
+ case MOUNT_REMOUNTING_SIGKILL:
+ mount_set_reload_result(m, MOUNT_FAILURE_TIMEOUT);
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
- }
+ log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ break;
+
+ case MOUNT_UNMOUNTING:
+ log_unit_warning(UNIT(m), "Unmounting timed out. Terminating.");
+ mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
break;
case MOUNT_UNMOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_unit_warning(UNIT(m), "Unmounting timed out. Killing.");
+ log_unit_warning(UNIT(m), "Mount process timed out. Killing.");
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
- log_unit_warning(UNIT(m), "Unmounting timed out. Skipping SIGKILL. Ignoring.");
-
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
+ log_unit_warning(UNIT(m), "Mount process timed out. Skipping SIGKILL. Ignoring.");
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
}
break;
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_REMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGKILL:
- log_unit_warning(UNIT(m),"Mount process still around after SIGKILL. Ignoring.");
-
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
+ log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
break;
default:
return 0;
}
+typedef struct {
+ bool is_mounted;
+ bool just_mounted;
+ bool just_changed;
+} MountSetupFlags;
+
+static int mount_setup_new_unit(
+ Unit *u,
+ const char *what,
+ const char *where,
+ const char *options,
+ const char *fstype,
+ MountSetupFlags *flags) {
+
+ MountParameters *p;
+
+ assert(u);
+ assert(flags);
+
+ u->source_path = strdup("/proc/self/mountinfo");
+ MOUNT(u)->where = strdup(where);
+ if (!u->source_path || !MOUNT(u)->where)
+ return -ENOMEM;
+
+ /* Make sure to initialize those fields before mount_is_extrinsic(). */
+ MOUNT(u)->from_proc_self_mountinfo = true;
+ p = &MOUNT(u)->parameters_proc_self_mountinfo;
+
+ p->what = strdup(what);
+ p->options = strdup(options);
+ p->fstype = strdup(fstype);
+ if (!p->what || !p->options || !p->fstype)
+ return -ENOMEM;
+
+ if (!mount_is_extrinsic(MOUNT(u))) {
+ const char *target;
+ int r;
+
+ target = mount_is_network(p) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
+ r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
+ if (r < 0)
+ return r;
+
+ r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
+ if (r < 0)
+ return r;
+ }
+
+ unit_add_to_load_queue(u);
+ flags->is_mounted = true;
+ flags->just_mounted = true;
+ flags->just_changed = true;
+
+ return 0;
+}
+
+static int mount_setup_existing_unit(
+ Unit *u,
+ const char *what,
+ const char *where,
+ const char *options,
+ const char *fstype,
+ MountSetupFlags *flags) {
+
+ MountParameters *p;
+ bool load_extras = false;
+ int r1, r2, r3;
+
+ assert(u);
+ assert(flags);
+
+ if (!MOUNT(u)->where) {
+ MOUNT(u)->where = strdup(where);
+ if (!MOUNT(u)->where)
+ return -ENOMEM;
+ }
+
+ /* Make sure to initialize those fields before mount_is_extrinsic(). */
+ p = &MOUNT(u)->parameters_proc_self_mountinfo;
+
+ r1 = free_and_strdup(&p->what, what);
+ r2 = free_and_strdup(&p->options, options);
+ r3 = free_and_strdup(&p->fstype, fstype);
+ if (r1 < 0 || r2 < 0 || r3 < 0)
+ return -ENOMEM;
+
+ flags->just_changed = r1 > 0 || r2 > 0 || r3 > 0;
+ flags->is_mounted = true;
+ flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo;
+
+ MOUNT(u)->from_proc_self_mountinfo = true;
+
+ if (!mount_is_extrinsic(MOUNT(u)) && mount_is_network(p)) {
+ /* _netdev option may have shown up late, or on a
+ * remount. Add remote-fs dependencies, even though
+ * local-fs ones may already be there.
+ *
+ * Note: due to a current limitation (we don't track
+ * in the dependency "Set*" objects who created a
+ * dependency), we can only add deps, never lose them,
+ * until the next full daemon-reload. */
+ unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
+ load_extras = true;
+ }
+
+ if (u->load_state == UNIT_NOT_FOUND) {
+ u->load_state = UNIT_LOADED;
+ u->load_error = 0;
+
+ /* Load in the extras later on, after we
+ * finished initialization of the unit */
+
+ /* FIXME: since we're going to load the unit later on, why setting load_extras=true ? */
+ load_extras = true;
+ flags->just_changed = true;
+ }
+
+ if (load_extras)
+ return mount_add_extras(MOUNT(u));
+
+ return 0;
+}
+
static int mount_setup_unit(
Manager *m,
const char *what,
const char *fstype,
bool set_flags) {
- _cleanup_free_ char *e = NULL, *w = NULL, *o = NULL, *f = NULL;
- bool load_extras = false;
- MountParameters *p;
- bool delete, changed = false;
+ _cleanup_free_ char *e = NULL;
+ MountSetupFlags flags;
Unit *u;
int r;
u = manager_get_unit(m, e);
if (!u) {
- delete = true;
-
+ /* First time we see this mount point meaning that it's
+ * not been initiated by a mount unit but rather by the
+ * sysadmin having called mount(8) directly. */
r = unit_new_for_name(m, sizeof(Mount), e, &u);
if (r < 0)
goto fail;
- MOUNT(u)->where = strdup(where);
- if (!MOUNT(u)->where) {
- r = -ENOMEM;
- goto fail;
- }
-
- u->source_path = strdup("/proc/self/mountinfo");
- if (!u->source_path) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (MANAGER_IS_SYSTEM(m)) {
- const char* target;
-
- target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
- r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true);
- if (r < 0)
- goto fail;
-
- if (should_umount(MOUNT(u))) {
- r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
- if (r < 0)
- goto fail;
- }
- }
-
- unit_add_to_load_queue(u);
- changed = true;
- } else {
- delete = false;
-
- if (!MOUNT(u)->where) {
- MOUNT(u)->where = strdup(where);
- if (!MOUNT(u)->where) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- if (MANAGER_IS_SYSTEM(m) &&
- mount_needs_network(options, fstype)) {
- /* _netdev option may have shown up late, or on a
- * remount. Add remote-fs dependencies, even though
- * local-fs ones may already be there. */
- unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true);
- load_extras = true;
- }
-
- if (u->load_state == UNIT_NOT_FOUND) {
- u->load_state = UNIT_LOADED;
- u->load_error = 0;
-
- /* Load in the extras later on, after we
- * finished initialization of the unit */
- load_extras = true;
- changed = true;
- }
- }
+ r = mount_setup_new_unit(u, what, where, options, fstype, &flags);
+ if (r < 0)
+ unit_free(u);
+ } else
+ r = mount_setup_existing_unit(u, what, where, options, fstype, &flags);
- w = strdup(what);
- o = strdup(options);
- f = strdup(fstype);
- if (!w || !o || !f) {
- r = -ENOMEM;
+ if (r < 0)
goto fail;
- }
-
- p = &MOUNT(u)->parameters_proc_self_mountinfo;
-
- changed = changed ||
- !streq_ptr(p->options, options) ||
- !streq_ptr(p->what, what) ||
- !streq_ptr(p->fstype, fstype);
if (set_flags) {
- MOUNT(u)->is_mounted = true;
- MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo;
- MOUNT(u)->just_changed = changed;
+ MOUNT(u)->is_mounted = flags.is_mounted;
+ MOUNT(u)->just_mounted = flags.just_mounted;
+ MOUNT(u)->just_changed = flags.just_changed;
}
- MOUNT(u)->from_proc_self_mountinfo = true;
-
- free_and_replace(p->what, w);
- free_and_replace(p->options, o);
- free_and_replace(p->fstype, f);
-
- if (load_extras) {
- r = mount_add_extras(MOUNT(u));
- if (r < 0)
- goto fail;
- }
-
- if (changed)
+ if (flags.just_changed)
unit_add_to_dbus_queue(u);
return 0;
-
fail:
log_warning_errno(r, "Failed to set up mount unit: %m");
-
- if (delete && u)
- unit_free(u);
-
return r;
}
}
static void mount_shutdown(Manager *m) {
-
assert(m);
m->mount_event_source = sd_event_source_unref(m->mount_event_source);
}
static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error);
}