]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #10892 from mbiebl/revert-systemctl-runtime-unmask-breakage
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 12 Dec 2018 13:23:04 +0000 (14:23 +0100)
committerGitHub <noreply@github.com>
Wed, 12 Dec 2018 13:23:04 +0000 (14:23 +0100)
Revert "systemctl: when removing enablement or mask symlinks, cover both /run and /etc

1  2 
src/shared/install.c
src/systemctl/systemctl.c

diff --combined src/shared/install.c
index d172162a3c3121e74defa18a0772df920a439f43,d98aa9cbc4d64e0cbf8c32ddf735491625c1a443..822645ef43b0bb25ab1aecdb902d2c28ca4b1860
@@@ -1048,14 -1048,11 +1048,14 @@@ static int install_info_add
          if (r < 0)
                  return r;
  
 -        i = new0(UnitFileInstallInfo, 1);
 +        i = new(UnitFileInstallInfo, 1);
          if (!i)
                  return -ENOMEM;
 -        i->type = _UNIT_FILE_TYPE_INVALID;
 -        i->auxiliary = auxiliary;
 +
 +        *i = (UnitFileInstallInfo) {
 +                .type = _UNIT_FILE_TYPE_INVALID,
 +                .auxiliary = auxiliary,
 +        };
  
          i->name = strdup(name);
          if (!i->name) {
@@@ -1464,7 -1461,7 +1464,7 @@@ static int unit_file_search
          STRV_FOREACH(p, paths->search_path) {
                  char *path;
  
 -                path = path_join(NULL, *p, dropin_dir_name);
 +                path = path_join(*p, dropin_dir_name);
                  if (!path)
                          return -ENOMEM;
  
                  STRV_FOREACH(p, paths->search_path) {
                          char *path;
  
 -                        path = path_join(NULL, *p, dropin_template_dir_name);
 +                        path = path_join(*p, dropin_template_dir_name);
                          if (!path)
                                  return -ENOMEM;
  
@@@ -1752,16 -1749,12 +1752,16 @@@ static int install_info_symlink_wants
          if (strv_isempty(list))
                  return 0;
  
 -        if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance) {
 +        if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
                  UnitFileInstallInfo instance = {
                          .type = _UNIT_FILE_TYPE_INVALID,
                  };
                  _cleanup_free_ char *path = NULL;
  
 +                /* If this is a template, and we have no instance, don't do anything */
 +                if (!i->default_instance)
 +                        return 1;
 +
                  r = unit_name_replace_instance(i->name, i->default_instance, &buf);
                  if (r < 0)
                          return r;
@@@ -1939,6 -1932,7 +1939,7 @@@ static int install_context_mark_for_rem
                  InstallContext *c,
                  const LookupPaths *paths,
                  Set **remove_symlinks_to,
+                 const char *config_path,
                  UnitFileChange **changes,
                  size_t *n_changes) {
  
  
          assert(c);
          assert(paths);
+         assert(config_path);
  
          /* Marks all items for removal */
  
@@@ -2056,7 -2051,7 +2058,7 @@@ int unit_file_unmask
          size_t n_todo = 0, n_allocated = 0;
          const char *config_path;
          char **i;
-         bool dry_run = !!(flags & UNIT_FILE_DRY_RUN);
+         bool dry_run;
          int r, q;
  
          assert(scope >= 0);
          if (r < 0)
                  return r;
  
+         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+         if (!config_path)
+                 return -ENXIO;
+         dry_run = !!(flags & UNIT_FILE_DRY_RUN);
          STRV_FOREACH(i, files) {
+                 _cleanup_free_ char *path = NULL;
                  if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
                          return -EINVAL;
  
-                 FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
-                         _cleanup_free_ char *path = NULL;
-                         path = path_make_absolute(*i, config_path);
-                         if (!path)
-                                 return -ENOMEM;
+                 path = path_make_absolute(*i, config_path);
+                 if (!path)
+                         return -ENOMEM;
  
-                         r = null_or_empty_path(path);
-                         if (r == -ENOENT)
-                                 continue;
-                         if (r < 0)
-                                 return r;
-                         if (r == 0)
-                                 continue;
+                 r = null_or_empty_path(path);
+                 if (r == -ENOENT)
+                         continue;
+                 if (r < 0)
+                         return r;
+                 if (r == 0)
+                         continue;
  
-                         if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
-                                 return -ENOMEM;
+                 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
+                         return -ENOMEM;
  
-                         todo[n_todo] = strdup(*i);
-                         if (!todo[n_todo])
-                                 return -ENOMEM;
+                 todo[n_todo] = strdup(*i);
+                 if (!todo[n_todo])
+                         return -ENOMEM;
  
-                         n_todo++;
-                 }
+                 n_todo++;
          }
  
          strv_uniq(todo);
  
          r = 0;
-         FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
-                 STRV_FOREACH(i, todo) {
-                         _cleanup_free_ char *path = NULL;
-                         const char *rp;
-                         path = path_make_absolute(*i, config_path);
-                         if (!path)
-                                 return -ENOMEM;
+         STRV_FOREACH(i, todo) {
+                 _cleanup_free_ char *path = NULL;
+                 const char *rp;
  
-                         if (!dry_run && unlink(path) < 0) {
-                                 if (errno != ENOENT) {
-                                         if (r >= 0)
-                                                 r = -errno;
-                                         unit_file_changes_add(changes, n_changes, -errno, path, NULL);
-                                 }
+                 path = path_make_absolute(*i, config_path);
+                 if (!path)
+                         return -ENOMEM;
  
-                                 continue;
+                 if (!dry_run && unlink(path) < 0) {
+                         if (errno != ENOENT) {
+                                 if (r >= 0)
+                                         r = -errno;
+                                 unit_file_changes_add(changes, n_changes, -errno, path, NULL);
                          }
  
-                         unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
-                         rp = skip_root(&paths, path);
-                         q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
-                         if (q < 0)
-                                 return q;
+                         continue;
                  }
  
-                 q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
-                 if (r >= 0)
-                         r = q;
+                 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+                 rp = skip_root(&paths, path);
+                 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
+                 if (q < 0)
+                         return q;
          }
  
+         q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
+         if (r >= 0)
+                 r = q;
          return r;
  }
  
@@@ -2513,7 -2510,6 +2517,6 @@@ int unit_file_disable
          _cleanup_(lookup_paths_free) LookupPaths paths = {};
          _cleanup_(install_context_done) InstallContext c = {};
          _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
-         bool dry_run = !!(flags & UNIT_FILE_DRY_RUN);
          const char *config_path;
          char **i;
          int r;
          if (r < 0)
                  return r;
  
+         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+         if (!config_path)
+                 return -ENXIO;
          STRV_FOREACH(i, files) {
                  if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
                          return -EINVAL;
                          return r;
          }
  
-         r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, changes, n_changes);
+         r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
          if (r < 0)
                  return r;
  
-         FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
-                 r = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
-                 if (r < 0)
-                         return r;
-         }
-         return 0;
+         return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
  }
  
  int unit_file_reenable(
@@@ -3029,45 -3023,45 +3030,45 @@@ int unit_file_query_preset(UnitFileScop
  
  static int execute_preset(
                  UnitFileScope scope,
-                 UnitFileFlags flags,
                  InstallContext *plus,
                  InstallContext *minus,
                  const LookupPaths *paths,
+                 const char *config_path,
                  char **files,
                  UnitFilePresetMode mode,
+                 bool force,
                  UnitFileChange **changes,
                  size_t *n_changes) {
  
-         const char *config_path;
-         bool force = !!(flags & UNIT_FILE_FORCE);
-         bool runtime = !!(flags & UNIT_FILE_RUNTIME);
-         int r = 0, q;
+         int r;
  
          assert(plus);
          assert(minus);
          assert(paths);
+         assert(config_path);
  
          if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
                  _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
  
-                 q = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, changes, n_changes);
-                 if (q < 0)
-                         return q;
+                 r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
+                 if (r < 0)
+                         return r;
  
-                 FOREACH_STRING(config_path, paths->runtime_config, paths->persistent_config) {
-                         q = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
-                         if (r == 0)
-                                 r = q;
-                 }
-         }
+                 r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
+         } else
+                 r = 0;
  
          if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
+                 int q;
                  /* Returns number of symlinks that where supposed to be installed. */
-                 q = install_context_apply(scope, plus, paths,
-                                           runtime ? paths->runtime_config : paths->persistent_config,
-                                           force, SEARCH_LOAD, changes, n_changes);
-                 if (r == 0)
-                         r = q;
+                 q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
+                 if (r >= 0) {
+                         if (q < 0)
+                                 r = q;
+                         else
+                                 r += q;
+                 }
          }
  
          return r;
@@@ -3139,6 -3133,7 +3140,7 @@@ int unit_file_preset
          _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
          _cleanup_(lookup_paths_free) LookupPaths paths = {};
          _cleanup_(presets_freep) Presets presets = {};
+         const char *config_path;
          char **i;
          int r;
  
          if (r < 0)
                  return r;
  
+         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+         if (!config_path)
+                 return -ENXIO;
          r = read_presets(scope, root_dir, &presets);
          if (r < 0)
                  return r;
                          return r;
          }
  
-         return execute_preset(scope, flags, &plus, &minus, &paths, files, mode, changes, n_changes);
+         return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
  }
  
  int unit_file_preset_all(
          _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
          _cleanup_(lookup_paths_free) LookupPaths paths = {};
          _cleanup_(presets_freep) Presets presets = {};
+         const char *config_path = NULL;
          char **i;
          int r;
  
          if (r < 0)
                  return r;
  
+         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+         if (!config_path)
+                 return -ENXIO;
          r = read_presets(scope, root_dir, &presets);
          if (r < 0)
                  return r;
                  }
  
                  FOREACH_DIRENT(de, d, return -errno) {
                          if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
                                  continue;
  
                  }
          }
  
-         return execute_preset(scope, flags, &plus, &minus, &paths, NULL, mode, changes, n_changes);
+         return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
  }
  
  static void unit_file_list_free_one(UnitFileList *f) {
index 40fcb2cae3ef962b3970516385972dc050af5206,41ed36f1552f3baf335be5cd664f9fe64befb827..dcea9d22a4b14bb72743d515c09870c70f4e4c1d
@@@ -1,4 -1,7 +1,4 @@@
  /* SPDX-License-Identifier: LGPL-2.1+ */
 -/***
 -  Copyright Â© 2013 Marc-Antoine Perennou
 -***/
  
  #include <errno.h>
  #include <fcntl.h>
@@@ -34,6 -37,7 +34,6 @@@
  #include "escape.h"
  #include "exit-status.h"
  #include "fd-util.h"
 -#include "fileio.h"
  #include "format-util.h"
  #include "fs-util.h"
  #include "glob-util.h"
@@@ -69,7 -73,6 +69,7 @@@
  #include "string-table.h"
  #include "strv.h"
  #include "terminal-util.h"
 +#include "tmpfile-util.h"
  #include "unit-def.h"
  #include "unit-name.h"
  #include "user-util.h"
@@@ -2412,7 -2415,7 +2412,7 @@@ static void warn_unit_file_changed(cons
                      arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
  }
  
 -static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **unit_path) {
 +static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) {
          char **p;
  
          assert(lp);
                  _cleanup_free_ char *path = NULL, *lpath = NULL;
                  int r;
  
 -                path = path_join(NULL, *p, unit_name);
 +                path = path_join(*p, unit_name);
                  if (!path)
                          return log_oom();
  
                  if (r < 0)
                          return log_error_errno(r, "Failed to access path \"%s\": %m", path);
  
 -                if (unit_path)
 -                        *unit_path = TAKE_PTR(lpath);
 +                if (ret_unit_path)
 +                        *ret_unit_path = TAKE_PTR(lpath);
  
                  return 1;
          }
  
 +        if (ret_unit_path)
 +                *ret_unit_path = NULL;
 +
          return 0;
  }
  
  static int unit_find_template_path(
                  const char *unit_name,
                  LookupPaths *lp,
 -                char **fragment_path,
 -                char **template) {
 +                char **ret_fragment_path,
 +                char **ret_template) {
  
 -        _cleanup_free_ char *_template = NULL;
 +        _cleanup_free_ char *t = NULL, *f = NULL;
          int r;
  
          /* Returns 1 if a fragment was found, 0 if not found, negative on error. */
  
 -        r = unit_file_find_path(lp, unit_name, fragment_path);
 -        if (r != 0)
 -                return r; /* error or found a real unit */
 +        r = unit_file_find_path(lp, unit_name, &f);
 +        if (r < 0)
 +                return r;
 +        if (r > 0) {
 +                if (ret_fragment_path)
 +                        *ret_fragment_path = TAKE_PTR(f);
 +                if (ret_template)
 +                        *ret_template = NULL;
 +                return r; /* found a real unit */
 +        }
 +
 +        r = unit_name_template(unit_name, &t);
 +        if (r == -EINVAL) {
 +                if (ret_fragment_path)
 +                        *ret_fragment_path = NULL;
 +                if (ret_template)
 +                        *ret_template = NULL;
  
 -        r = unit_name_template(unit_name, &_template);
 -        if (r == -EINVAL)
                  return 0; /* not a template, does not exist */
 +        }
          if (r < 0)
                  return log_error_errno(r, "Failed to determine template name: %m");
  
 -        r = unit_file_find_path(lp, _template, fragment_path);
 +        r = unit_file_find_path(lp, t, ret_fragment_path);
          if (r < 0)
                  return r;
  
 -        if (template)
 -                *template = TAKE_PTR(_template);
 +        if (ret_template)
 +                *ret_template = r > 0 ? TAKE_PTR(t) : NULL;
  
          return r;
  }
@@@ -2494,59 -2481,33 +2494,59 @@@ static int unit_find_paths
                  sd_bus *bus,
                  const char *unit_name,
                  LookupPaths *lp,
 -                char **fragment_path,
 -                char ***dropin_paths) {
 +                bool force_client_side,
 +                char **ret_fragment_path,
 +                char ***ret_dropin_paths) {
  
 -        _cleanup_free_ char *path = NULL;
          _cleanup_strv_free_ char **dropins = NULL;
 +        _cleanup_free_ char *path = NULL;
          int r;
  
          /**
 -         * Finds where the unit is defined on disk. Returns 0 if the unit
 -         * is not found. Returns 1 if it is found, and sets
 -         * - the path to the unit in *path, if it exists on disk,
 -         * - and a strv of existing drop-ins in *dropins,
 -         *   if the arg is not NULL and any dropins were found.
 +         * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is found, and
 +         * sets:
 +         * - the path to the unit in *ret_frament_path, if it exists on disk,
 +         * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins were found.
 +         *
 +         * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for some
 +         * reason (the latter only applies if we are going through the service manager)
           */
  
          assert(unit_name);
 -        assert(fragment_path);
 +        assert(ret_fragment_path);
          assert(lp);
  
 -        if (!install_client_side() && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
 +        /* Go via the bus to acquire the path, unless we are explicitly told not to, or when the unit name is a template */
 +        if (!force_client_side &&
 +            !install_client_side() &&
 +            !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
                  _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 -                _cleanup_free_ char *unit = NULL;
 +                _cleanup_free_ char *load_state = NULL, *unit = NULL;
  
                  unit = unit_dbus_path_from_name(unit_name);
                  if (!unit)
                          return log_oom();
  
 +                r = sd_bus_get_property_string(
 +                                bus,
 +                                "org.freedesktop.systemd1",
 +                                unit,
 +                                "org.freedesktop.systemd1.Unit",
 +                                "LoadState",
 +                                &error,
 +                                &load_state);
 +                if (r < 0)
 +                        return log_error_errno(r, "Failed to get LoadState: %s", bus_error_message(&error, r));
 +
 +                if (streq(load_state, "masked"))
 +                        return -ERFKILL;
 +                if (streq(load_state, "not-found")) {
 +                        r = 0;
 +                        goto not_found;
 +                }
 +                if (!streq(load_state, "loaded"))
 +                        return -EKEYREJECTED;
 +
                  r = sd_bus_get_property_string(
                                  bus,
                                  "org.freedesktop.systemd1",
                  if (r < 0)
                          return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r));
  
 -                if (dropin_paths) {
 +                if (ret_dropin_paths) {
                          r = sd_bus_get_property_strv(
                                          bus,
                                          "org.freedesktop.systemd1",
                  r = unit_find_template_path(unit_name, lp, &path, &template);
                  if (r < 0)
                          return r;
 -
                  if (r > 0) {
                          if (null_or_empty_path(path))
                                  /* The template is masked. Let's cut the process short. */
                  if (r < 0)
                          return log_error_errno(r, "Failed to add unit name: %m");
  
 -                if (dropin_paths) {
 -                        r = unit_file_find_dropin_conf_paths(arg_root, lp->search_path,
 -                                                             NULL, names, &dropins);
 +                if (ret_dropin_paths) {
 +                        r = unit_file_find_dropin_conf_paths(arg_root, lp->search_path, NULL, names, &dropins);
                          if (r < 0)
                                  return r;
                  }
          }
  
 -        r = 0;
 +                r = 0;
  
          if (!isempty(path)) {
 -                *fragment_path = TAKE_PTR(path);
 +                *ret_fragment_path = TAKE_PTR(path);
                  r = 1;
 -        }
 +        } else
 +                *ret_fragment_path = NULL;
  
 -        if (dropin_paths && !strv_isempty(dropins)) {
 -                *dropin_paths = TAKE_PTR(dropins);
 -                r = 1;
 +        if (ret_dropin_paths) {
 +                if (!strv_isempty(dropins)) {
 +                        *ret_dropin_paths = TAKE_PTR(dropins);
 +                        r = 1;
 +                } else
 +                        *ret_dropin_paths = NULL;
          }
 +
   not_found:
          if (r == 0 && !arg_force)
                  log_error("No files found for %s.", unit_name);
@@@ -2799,87 -2757,63 +2799,87 @@@ static void wait_context_free(WaitConte
  }
  
  static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
 +        const char *path, *interface, *active_state = NULL, *job_path = NULL;
          WaitContext *c = userdata;
 -        const char *path;
 +        bool is_failed;
          int r;
  
 +        /* Called whenever we get a PropertiesChanged signal. Checks if ActiveState changed to inactive/failed.
 +         *
 +         * Signal parameters: (s interface, a{sv} changed_properties, as invalidated_properties) */
 +
          path = sd_bus_message_get_path(m);
          if (!set_contains(c->unit_paths, path))
                  return 0;
  
 -        /* Check if ActiveState changed to inactive/failed */
 -        /* (s interface, a{sv} changed_properties, as invalidated_properties) */
 -        r = sd_bus_message_skip(m, "s");
 +        r = sd_bus_message_read(m, "s", &interface);
          if (r < 0)
                  return bus_log_parse_error(r);
  
 +        if (!streq(interface, "org.freedesktop.systemd1.Unit")) /* ActiveState is on the Unit interface */
 +                return 0;
 +
          r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
          if (r < 0)
                  return bus_log_parse_error(r);
  
 -        while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
 +        for (;;) {
                  const char *s;
  
 -                r = sd_bus_message_read(m, "s", &s);
 +                r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
 +                if (r < 0)
 +                        return bus_log_parse_error(r);
 +                if (r == 0) /* end of array */
 +                        break;
 +
 +                r = sd_bus_message_read(m, "s", &s); /* Property name */
                  if (r < 0)
                          return bus_log_parse_error(r);
  
                  if (streq(s, "ActiveState")) {
 -                        bool is_failed;
 -
 -                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "s");
 +                        r = sd_bus_message_read(m, "v", "s", &active_state);
                          if (r < 0)
                                  return bus_log_parse_error(r);
  
 -                        r = sd_bus_message_read(m, "s", &s);
 +                        if (job_path) /* Found everything we need */
 +                                break;
 +
 +                } else if (streq(s, "Job")) {
 +                        uint32_t job_id;
 +
 +                        r = sd_bus_message_read(m, "v", "(uo)", &job_id, &job_path);
                          if (r < 0)
                                  return bus_log_parse_error(r);
  
 -                        is_failed = streq(s, "failed");
 -                        if (streq(s, "inactive") || is_failed) {
 -                                log_debug("%s became %s, dropping from --wait tracking", path, s);
 -                                free(set_remove(c->unit_paths, path));
 -                                c->any_failed = c->any_failed || is_failed;
 -                        } else
 -                                log_debug("ActiveState on %s changed to %s", path, s);
 +                        /* There's still a job pending for this unit, let's ignore this for now, and return right-away. */
 +                        if (job_id != 0)
 +                                return 0;
 +
 +                        if (active_state) /* Found everything we need */
 +                                break;
  
 -                        break; /* no need to dissect the rest of the message */
                  } else {
 -                        /* other property */
 -                        r = sd_bus_message_skip(m, "v");
 +                        r = sd_bus_message_skip(m, "v"); /* Other property */
                          if (r < 0)
                                  return bus_log_parse_error(r);
                  }
 +
                  r = sd_bus_message_exit_container(m);
                  if (r < 0)
                          return bus_log_parse_error(r);
          }
 -        if (r < 0)
 -                return bus_log_parse_error(r);
 +
 +        /* If this didn't contain the ActiveState property we can't do anything */
 +        if (!active_state)
 +                return 0;
 +
 +        is_failed = streq(active_state, "failed");
 +        if (streq(active_state, "inactive") || is_failed) {
 +                log_debug("%s became %s, dropping from --wait tracking", path, active_state);
 +                free(set_remove(c->unit_paths, path));
 +                c->any_failed = c->any_failed || is_failed;
 +        } else
 +                log_debug("ActiveState on %s changed to %s", path, active_state);
  
          if (set_isempty(c->unit_paths))
                  sd_event_exit(c->event, EXIT_SUCCESS);
@@@ -3090,7 -3024,6 +3090,7 @@@ static int start_unit(int argc, char *a
          _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
          _cleanup_(wait_context_free) WaitContext wait_context = {};
          const char *method, *mode, *one_name, *suffix = NULL;
 +        _cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */
          _cleanup_strv_free_ char **names = NULL;
          int r, ret = EXIT_SUCCESS;
          sd_bus *bus;
                  r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
                  if (ret == EXIT_SUCCESS && r < 0)
                          ret = translate_bus_error_to_exit_status(r, &error);
 +
 +                if (r >= 0 && streq(method, "StopUnit")) {
 +                        r = strv_push(&stopped_units, *name);
 +                        if (r < 0)
 +                                return log_oom();
 +                }
          }
  
          if (!arg_no_block) {
  
                  /* When stopping units, warn if they can still be triggered by
                   * another active unit (socket, path, timer) */
 -                if (!arg_quiet && streq(method, "StopUnit"))
 -                        STRV_FOREACH(name, names)
 +                if (!arg_quiet)
 +                        STRV_FOREACH(name, stopped_units)
                                  (void) check_triggering_units(bus, *name);
          }
  
@@@ -3544,9 -3471,9 +3544,9 @@@ static int load_kexec_kernel(void) 
                  return -EINVAL;
          }
  
 -        kernel = path_join(NULL, where, e->kernel);
 +        kernel = path_join(where, e->kernel);
          if (!strv_isempty(e->initrd))
 -                initrd = path_join(NULL, where, *e->initrd);
 +                initrd = path_join(where, *e->initrd);
          options = strv_join(e->options, " ");
          if (!options)
                  return log_oom();
          if (arg_dry_run)
                  return 0;
  
 -        r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
 +        r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
          if (r < 0)
                  return r;
          if (r == 0) {
@@@ -5447,16 -5374,9 +5447,16 @@@ static int cat(int argc, char *argv[], 
                  _cleanup_free_ char *fragment_path = NULL;
                  _cleanup_strv_free_ char **dropin_paths = NULL;
  
 -                r = unit_find_paths(bus, *name, &lp, &fragment_path, &dropin_paths);
 +                r = unit_find_paths(bus, *name, &lp, false, &fragment_path, &dropin_paths);
                  if (r == -ERFKILL) {
 -                        printf("%s# unit %s is masked%s\n",
 +                        printf("%s# Unit %s is masked%s.\n",
 +                               ansi_highlight_magenta(),
 +                               *name,
 +                               ansi_normal());
 +                        continue;
 +                }
 +                if (r == -EKEYREJECTED) {
 +                        printf("%s# Unit %s could not be loaded.%s\n",
                                 ansi_highlight_magenta(),
                                 *name,
                                 ansi_normal());
                  }
                  if (r < 0)
                          return r;
 -                else if (r == 0)
 +                if (r == 0)
                          return -ENOENT;
  
                  if (first)
@@@ -6033,7 -5953,7 +6033,7 @@@ static int enable_sysv_units(const cha
                  if (!arg_quiet)
                          log_info("Executing: %s", l);
  
 -                j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
 +                j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
                  if (j < 0)
                          return j;
                  if (j == 0) {
@@@ -6377,19 -6297,18 +6377,19 @@@ static int enable_unit(int argc, char *
          }
  
          if (carries_install_info == 0 && !ignore_carries_install_info)
 -                log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n"
 -                            "settings in the [Install] section, and DefaultInstance for template units).\n"
 -                            "This means they are not meant to be enabled using systemctl.\n"
 -                            "Possible reasons for having this kind of units are:\n"
 -                            "1) A unit may be statically enabled by being symlinked from another unit's\n"
 -                            "   .wants/ or .requires/ directory.\n"
 -                            "2) A unit's purpose may be to act as a helper for some other unit which has\n"
 -                            "   a requirement dependency on it.\n"
 -                            "3) A unit may be started when needed via activation (socket, path, timer,\n"
 -                            "   D-Bus, udev, scripted systemctl call, ...).\n"
 -                            "4) In case of template units, the unit is meant to be enabled with some\n"
 -                            "   instance name specified.");
 +                log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, Also=,\n"
 +                           "Alias= settings in the [Install] section, and DefaultInstance= for template\n"
 +                           "units). This means they are not meant to be enabled using systemctl.\n \n"
 +                           "Possible reasons for having this kind of units are:\n"
 +                           "%1$s A unit may be statically enabled by being symlinked from another unit's\n"
 +                           "  .wants/ or .requires/ directory.\n"
 +                           "%1$s A unit's purpose may be to act as a helper for some other unit which has\n"
 +                           "  a requirement dependency on it.\n"
 +                           "%1$s A unit may be started when needed via activation (socket, path, timer,\n"
 +                           "  D-Bus, udev, scripted systemctl call, ...).\n"
 +                           "%1$s In case of template units, the unit is meant to be enabled with some\n"
 +                           "  instance name specified.",
 +                           special_glyph(BULLET));
  
          if (arg_now && STR_IN_SET(argv[0], "enable", "disable", "mask")) {
                  sd_bus *bus;
@@@ -6861,8 -6780,7 +6861,8 @@@ static int unit_file_create_new
                  char **ret_new_path,
                  char **ret_tmp_path) {
  
 -        char *tmp_new_path, *tmp_tmp_path, *ending;
 +        _cleanup_free_ char *new_path = NULL, *tmp_path = NULL;
 +        const char *ending;
          int r;
  
          assert(unit_name);
          assert(ret_tmp_path);
  
          ending = strjoina(unit_name, suffix);
 -        r = get_file_to_edit(paths, ending, &tmp_new_path);
 +        r = get_file_to_edit(paths, ending, &new_path);
          if (r < 0)
                  return r;
  
 -        r = create_edit_temp_file(tmp_new_path, tmp_new_path, &tmp_tmp_path);
 -        if (r < 0) {
 -                free(tmp_new_path);
 +        r = create_edit_temp_file(new_path, new_path, &tmp_path);
 +        if (r < 0)
                  return r;
 -        }
  
 -        *ret_new_path = tmp_new_path;
 -        *ret_tmp_path = tmp_tmp_path;
 +        *ret_new_path = TAKE_PTR(new_path);
 +        *ret_tmp_path = TAKE_PTR(tmp_path);
  
          return 0;
  }
@@@ -6891,7 -6811,7 +6891,7 @@@ static int unit_file_create_copy
                  char **ret_new_path,
                  char **ret_tmp_path) {
  
 -        char *tmp_new_path, *tmp_tmp_path;
 +        _cleanup_free_ char *new_path = NULL, *tmp_path = NULL;
          int r;
  
          assert(fragment_path);
          assert(ret_new_path);
          assert(ret_tmp_path);
  
 -        r = get_file_to_edit(paths, unit_name, &tmp_new_path);
 +        r = get_file_to_edit(paths, unit_name, &new_path);
          if (r < 0)
                  return r;
  
 -        if (!path_equal(fragment_path, tmp_new_path) && access(tmp_new_path, F_OK) == 0) {
 +        if (!path_equal(fragment_path, new_path) && access(new_path, F_OK) >= 0) {
                  char response;
  
 -                r = ask_char(&response, "yn", "\"%s\" already exists. Overwrite with \"%s\"? [(y)es, (n)o] ", tmp_new_path, fragment_path);
 -                if (r < 0) {
 -                        free(tmp_new_path);
 +                r = ask_char(&response, "yn", "\"%s\" already exists. Overwrite with \"%s\"? [(y)es, (n)o] ", new_path, fragment_path);
 +                if (r < 0)
                          return r;
 -                }
 -                if (response != 'y') {
 -                        log_warning("%s ignored", unit_name);
 -                        free(tmp_new_path);
 -                        return -EKEYREJECTED;
 -                }
 +                if (response != 'y')
 +                        return log_warning_errno(SYNTHETIC_ERRNO(EKEYREJECTED), "%s skipped.", unit_name);
          }
  
 -        r = create_edit_temp_file(tmp_new_path, fragment_path, &tmp_tmp_path);
 -        if (r < 0) {
 -                free(tmp_new_path);
 +        r = create_edit_temp_file(new_path, fragment_path, &tmp_path);
 +        if (r < 0)
                  return r;
 -        }
  
 -        *ret_new_path = tmp_new_path;
 -        *ret_tmp_path = tmp_tmp_path;
 +        *ret_new_path = TAKE_PTR(new_path);
 +        *ret_tmp_path = TAKE_PTR(tmp_path);
  
          return 0;
  }
@@@ -6928,7 -6855,7 +6928,7 @@@ static int run_editor(char **paths) 
  
          assert(paths);
  
 -        r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
 +        r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_WAIT, NULL);
          if (r < 0)
                  return r;
          if (r == 0) {
                          n_editor_args = strv_length(editor_args);
                          argc += n_editor_args - 1;
                  }
 +
                  args = newa(const char*, argc + 1);
  
                  if (n_editor_args > 0) {
                                  args[i] = editor_args[i];
                  }
  
 -                STRV_FOREACH_PAIR(original_path, tmp_path, paths) {
 -                        args[i] = *tmp_path;
 -                        i++;
 -                }
 +                STRV_FOREACH_PAIR(original_path, tmp_path, paths)
 +                        args[i++] = *tmp_path;
                  args[i] = NULL;
  
                  if (n_editor_args > 0)
@@@ -7009,15 -6937,7 +7009,15 @@@ static int find_paths_to_edit(sd_bus *b
                  _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL;
                  const char *unit_name;
  
 -                r = unit_find_paths(bus, *name, &lp, &path, NULL);
 +                r = unit_find_paths(bus, *name, &lp, false, &path, NULL);
 +                if (r == -EKEYREJECTED) {
 +                        /* If loading of the unit failed server side complete, then the server won't tell us the unit
 +                         * file path. In that case, find the file client side. */
 +                        log_debug_errno(r, "Unit '%s' was not loaded correctly, retrying client-side.", *name);
 +                        r = unit_find_paths(bus, *name, &lp, true, &path, NULL);
 +                }
 +                if (r == -ERFKILL)
 +                        return log_error_errno(r, "Unit '%s' masked, cannot edit.", *name);
                  if (r < 0)
                          return r;
  
                          assert(!path);
  
                          if (!arg_force) {
 -                                log_error("Run 'systemctl edit%s --force %s' to create a new unit.",
 -                                          arg_scope == UNIT_FILE_GLOBAL ? " --global" :
 -                                          arg_scope == UNIT_FILE_USER ? " --user" : "",
 -                                          *name);
 +                                log_info("Run 'systemctl edit%s --force --full %s' to create a new unit.",
 +                                         arg_scope == UNIT_FILE_GLOBAL ? " --global" :
 +                                         arg_scope == UNIT_FILE_USER ? " --user" : "",
 +                                         *name);
                                  return -ENOENT;
                          }
  
                  r = strv_push_pair(paths, new_path, tmp_path);
                  if (r < 0)
                          return log_oom();
 +
                  new_path = tmp_path = NULL;
          }
  
@@@ -7109,6 -7028,7 +7109,6 @@@ static int edit(int argc, char *argv[]
                  r = unit_is_masked(bus, &lp, *tmp);
                  if (r < 0)
                          return r;
 -
                  if (r > 0) {
                          log_error("Cannot edit %s: unit is masked.", *tmp);
                          return -EINVAL;
@@@ -7580,7 -7500,7 +7580,7 @@@ static int systemctl_parse_argv(int arg
          /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */
          arg_ask_password = true;
  
 -        while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0)
 +        while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir.::", options, NULL)) >= 0)
  
                  switch (c) {
  
                                  return log_oom();
                          break;
  
 +                case '.':
 +                        /* Output an error mimicking getopt, and print a hint afterwards */
 +                        log_error("%s: invalid option -- '.'", program_invocation_name);
 +                        log_notice("Hint: to specify units starting with a dash, use \"--\":\n"
 +                                   "      %s [OPTIONS...] {COMMAND} -- -.%s ...",
 +                                   program_invocation_name, optarg ?: "mount");
 +                        _fallthrough_;
 +
                  case '?':
                          return -EINVAL;
  
                  return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                         "--wait may not be combined with --no-block.");
  
-         if (arg_runtime && STRPTR_IN_SET(argv[optind], "disable", "unmask", "preset", "preset-all"))
-                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                        "--runtime cannot be used with %s",
-                                        argv[optind]);
          return 1;
  }
  
@@@ -8338,7 -8245,6 +8333,7 @@@ static int parse_argv(int argc, char *a
                                  /* Hmm, so some other init system is running, we need to forward this request to
                                   * it. For now we simply guess that it is Upstart. */
  
 +                                (void) rlimit_nofile_safe();
                                  execv(TELINIT, argv);
  
                                  return log_error_errno(SYNTHETIC_ERRNO(EIO),
@@@ -8723,7 -8629,7 +8718,7 @@@ static int logind_cancel_shutdown(void
  #endif
  }
  
 -static int run(int argc, char*argv[]) {
 +static int run(int argc, char *argv[]) {
          int r;
  
          argv_cmdline = argv[0];