]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #13246 from keszybz/add-SystemdOptions-efi-variable
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 3 Oct 2019 10:19:44 +0000 (12:19 +0200)
committerGitHub <noreply@github.com>
Thu, 3 Oct 2019 10:19:44 +0000 (12:19 +0200)
Add efi variable to augment /proc/cmdline

13 files changed:
1  2 
docs/ENVIRONMENT.md
src/basic/cgroup-util.c
src/basic/cgroup-util.h
src/core/cgroup.c
src/core/execute.c
src/core/load-fragment.c
src/core/mount-setup.c
src/core/unit.c
src/login/logind-dbus.c
src/nspawn/nspawn.c
src/shared/bootspec.c
src/shared/bus-unit-util.c
src/shared/unit-file.c

diff --combined docs/ENVIRONMENT.md
index c6a7f1babd32bc6ad045b1d39fb95ef8d057df2a,f9025247d8b654b202c0fb0fed43f86de2f7977c..83c2d251340f04ed813852782cbe086fc1c200fc
@@@ -36,10 -36,13 +36,13 @@@ All tools
  * `$SD_EVENT_PROFILE_DELAYS=1` — if set, the sd-event event loop implementation
    will print latency information at runtime.
  
- * `$SYSTEMD_PROC_CMDLINE` — if set, may contain a string that is used as kernel
-   command line instead of the actual one readable from /proc/cmdline. This is
-   useful for debugging, in order to test generators and other code against
-   specific kernel command lines.
+ * `$SYSTEMD_PROC_CMDLINE` — if set, the contents are used as the kernel command
+   line instead of the actual one in /proc/cmdline. This is useful for
+   debugging, in order to test generators and other code against specific kernel
+   command lines.
+ * `$SYSTEMD_EFI_OPTIONS` — if set, used instead of the string in SystemdOptions
+   EFI variable. Analogous to `$SYSTEMD_PROC_CMDLINE`.
  
  * `$SYSTEMD_IN_INITRD` — takes a boolean. If set, overrides initrd detection.
    This is useful for debugging and testing initrd-only programs in the main
@@@ -70,8 -73,8 +73,8 @@@ systemctl
  
  systemd-nspawn:
  
 -* `$UNIFIED_CGROUP_HIERARCHY=1` — if set, force nspawn into unified cgroup
 -  hierarchy mode.
 +* `$SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1` — if set, force nspawn into unified
 +  cgroup hierarchy mode.
  
  * `$SYSTEMD_NSPAWN_API_VFS_WRITABLE=1` — if set, make /sys and /proc/sys and
    friends writable in the container. If set to "network", leave only
diff --combined src/basic/cgroup-util.c
index 2a359a606374f2399affecaeb42487a9a02eb26b,6582b92d67ab8ad79f4df6d6a908e84e516e2a56..7531cca5bcd03296c414d09992f92b7c8f0ac1c5
@@@ -31,7 -31,6 +31,6 @@@
  #include "mkdir.h"
  #include "parse-util.h"
  #include "path-util.h"
- #include "proc-cmdline.h"
  #include "process-util.h"
  #include "set.h"
  #include "special.h"
@@@ -345,8 -344,10 +344,8 @@@ int cg_kill
             a workaround for kernel bug. It was fixed in 5.2-rc5 (c03cd7738a83), backported to 4.19.66
             (4340d175b898) and 4.14.138 (feb6b123b7dd). */
          r = cg_unified_controller(controller);
 -        if (r < 0)
 +        if (r <= 0)
                  return r;
 -        if (r == 0) /* doesn't apply to legacy hierarchy */
 -                return 0;
  
          return cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.threads");
  }
@@@ -408,173 -409,6 +407,6 @@@ int cg_kill_recursive
          return ret;
  }
  
- int cg_migrate(
-                 const char *cfrom,
-                 const char *pfrom,
-                 const char *cto,
-                 const char *pto,
-                 CGroupFlags flags) {
-         bool done = false;
-         _cleanup_set_free_ Set *s = NULL;
-         int r, ret = 0;
-         pid_t my_pid;
-         assert(cfrom);
-         assert(pfrom);
-         assert(cto);
-         assert(pto);
-         s = set_new(NULL);
-         if (!s)
-                 return -ENOMEM;
-         my_pid = getpid_cached();
-         do {
-                 _cleanup_fclose_ FILE *f = NULL;
-                 pid_t pid = 0;
-                 done = true;
-                 r = cg_enumerate_processes(cfrom, pfrom, &f);
-                 if (r < 0) {
-                         if (ret >= 0 && r != -ENOENT)
-                                 return r;
-                         return ret;
-                 }
-                 while ((r = cg_read_pid(f, &pid)) > 0) {
-                         /* This might do weird stuff if we aren't a
-                          * single-threaded program. However, we
-                          * luckily know we are not */
-                         if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
-                                 continue;
-                         if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
-                                 continue;
-                         /* Ignore kernel threads. Since they can only
-                          * exist in the root cgroup, we only check for
-                          * them there. */
-                         if (cfrom &&
-                             empty_or_root(pfrom) &&
-                             is_kernel_thread(pid) > 0)
-                                 continue;
-                         r = cg_attach(cto, pto, pid);
-                         if (r < 0) {
-                                 if (ret >= 0 && r != -ESRCH)
-                                         ret = r;
-                         } else if (ret == 0)
-                                 ret = 1;
-                         done = false;
-                         r = set_put(s, PID_TO_PTR(pid));
-                         if (r < 0) {
-                                 if (ret >= 0)
-                                         return r;
-                                 return ret;
-                         }
-                 }
-                 if (r < 0) {
-                         if (ret >= 0)
-                                 return r;
-                         return ret;
-                 }
-         } while (!done);
-         return ret;
- }
- int cg_migrate_recursive(
-                 const char *cfrom,
-                 const char *pfrom,
-                 const char *cto,
-                 const char *pto,
-                 CGroupFlags flags) {
-         _cleanup_closedir_ DIR *d = NULL;
-         int r, ret = 0;
-         char *fn;
-         assert(cfrom);
-         assert(pfrom);
-         assert(cto);
-         assert(pto);
-         ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
-         r = cg_enumerate_subgroups(cfrom, pfrom, &d);
-         if (r < 0) {
-                 if (ret >= 0 && r != -ENOENT)
-                         return r;
-                 return ret;
-         }
-         while ((r = cg_read_subgroup(d, &fn)) > 0) {
-                 _cleanup_free_ char *p = NULL;
-                 p = path_join(empty_to_root(pfrom), fn);
-                 free(fn);
-                 if (!p)
-                         return -ENOMEM;
-                 r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
-                 if (r != 0 && ret >= 0)
-                         ret = r;
-         }
-         if (r < 0 && ret >= 0)
-                 ret = r;
-         if (flags & CGROUP_REMOVE) {
-                 r = cg_rmdir(cfrom, pfrom);
-                 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
-                         return r;
-         }
-         return ret;
- }
- int cg_migrate_recursive_fallback(
-                 const char *cfrom,
-                 const char *pfrom,
-                 const char *cto,
-                 const char *pto,
-                 CGroupFlags flags) {
-         int r;
-         assert(cfrom);
-         assert(pfrom);
-         assert(cto);
-         assert(pto);
-         r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
-         if (r < 0) {
-                 char prefix[strlen(pto) + 1];
-                 /* This didn't work? Then let's try all prefixes of the destination */
-                 PATH_FOREACH_PREFIX(prefix, pto) {
-                         int q;
-                         q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
-                         if (q >= 0)
-                                 return q;
-                 }
-         }
-         return r;
- }
  static const char *controller_to_dirname(const char *controller) {
          const char *e;
  
@@@ -740,253 -574,6 +572,6 @@@ int cg_get_path_and_check(const char *c
          return cg_get_path(controller, path, suffix, fs);
  }
  
- static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
-         assert(path);
-         assert(sb);
-         assert(ftwbuf);
-         if (typeflag != FTW_DP)
-                 return 0;
-         if (ftwbuf->level < 1)
-                 return 0;
-         (void) rmdir(path);
-         return 0;
- }
- int cg_trim(const char *controller, const char *path, bool delete_root) {
-         _cleanup_free_ char *fs = NULL;
-         int r = 0, q;
-         assert(path);
-         r = cg_get_path(controller, path, NULL, &fs);
-         if (r < 0)
-                 return r;
-         errno = 0;
-         if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
-                 if (errno == ENOENT)
-                         r = 0;
-                 else
-                         r = errno_or_else(EIO);
-         }
-         if (delete_root) {
-                 if (rmdir(fs) < 0 && errno != ENOENT)
-                         return -errno;
-         }
-         q = cg_hybrid_unified();
-         if (q < 0)
-                 return q;
-         if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
-                 q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
-                 if (q < 0)
-                         log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
-         }
-         return r;
- }
- /* Create a cgroup in the hierarchy of controller.
-  * Returns 0 if the group already existed, 1 on success, negative otherwise.
-  */
- int cg_create(const char *controller, const char *path) {
-         _cleanup_free_ char *fs = NULL;
-         int r;
-         r = cg_get_path_and_check(controller, path, NULL, &fs);
-         if (r < 0)
-                 return r;
-         r = mkdir_parents(fs, 0755);
-         if (r < 0)
-                 return r;
-         r = mkdir_errno_wrapper(fs, 0755);
-         if (r == -EEXIST)
-                 return 0;
-         if (r < 0)
-                 return r;
-         r = cg_hybrid_unified();
-         if (r < 0)
-                 return r;
-         if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
-                 r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
-                 if (r < 0)
-                         log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
-         }
-         return 1;
- }
- int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
-         int r, q;
-         assert(pid >= 0);
-         r = cg_create(controller, path);
-         if (r < 0)
-                 return r;
-         q = cg_attach(controller, path, pid);
-         if (q < 0)
-                 return q;
-         /* This does not remove the cgroup on failure */
-         return r;
- }
- int cg_attach(const char *controller, const char *path, pid_t pid) {
-         _cleanup_free_ char *fs = NULL;
-         char c[DECIMAL_STR_MAX(pid_t) + 2];
-         int r;
-         assert(path);
-         assert(pid >= 0);
-         r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
-         if (r < 0)
-                 return r;
-         if (pid == 0)
-                 pid = getpid_cached();
-         xsprintf(c, PID_FMT "\n", pid);
-         r = write_string_file(fs, c, WRITE_STRING_FILE_DISABLE_BUFFER);
-         if (r < 0)
-                 return r;
-         r = cg_hybrid_unified();
-         if (r < 0)
-                 return r;
-         if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
-                 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
-                 if (r < 0)
-                         log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
-         }
-         return 0;
- }
- int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
-         int r;
-         assert(controller);
-         assert(path);
-         assert(pid >= 0);
-         r = cg_attach(controller, path, pid);
-         if (r < 0) {
-                 char prefix[strlen(path) + 1];
-                 /* This didn't work? Then let's try all prefixes of
-                  * the destination */
-                 PATH_FOREACH_PREFIX(prefix, path) {
-                         int q;
-                         q = cg_attach(controller, prefix, pid);
-                         if (q >= 0)
-                                 return q;
-                 }
-         }
-         return r;
- }
- int cg_set_access(
-                 const char *controller,
-                 const char *path,
-                 uid_t uid,
-                 gid_t gid) {
-         struct Attribute {
-                 const char *name;
-                 bool fatal;
-         };
-         /* cgroup v1, aka legacy/non-unified */
-         static const struct Attribute legacy_attributes[] = {
-                 { "cgroup.procs",           true  },
-                 { "tasks",                  false },
-                 { "cgroup.clone_children",  false },
-                 {},
-         };
-         /* cgroup v2, aka unified */
-         static const struct Attribute unified_attributes[] = {
-                 { "cgroup.procs",           true  },
-                 { "cgroup.subtree_control", true  },
-                 { "cgroup.threads",         false },
-                 {},
-         };
-         static const struct Attribute* const attributes[] = {
-                 [false] = legacy_attributes,
-                 [true]  = unified_attributes,
-         };
-         _cleanup_free_ char *fs = NULL;
-         const struct Attribute *i;
-         int r, unified;
-         assert(path);
-         if (uid == UID_INVALID && gid == GID_INVALID)
-                 return 0;
-         unified = cg_unified_controller(controller);
-         if (unified < 0)
-                 return unified;
-         /* Configure access to the cgroup itself */
-         r = cg_get_path(controller, path, NULL, &fs);
-         if (r < 0)
-                 return r;
-         r = chmod_and_chown(fs, 0755, uid, gid);
-         if (r < 0)
-                 return r;
-         /* Configure access to the cgroup's attributes */
-         for (i = attributes[unified]; i->name; i++) {
-                 fs = mfree(fs);
-                 r = cg_get_path(controller, path, i->name, &fs);
-                 if (r < 0)
-                         return r;
-                 r = chmod_and_chown(fs, 0644, uid, gid);
-                 if (r < 0) {
-                         if (i->fatal)
-                                 return r;
-                         log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
-                 }
-         }
-         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
-                 r = cg_hybrid_unified();
-                 if (r < 0)
-                         return r;
-                 if (r > 0) {
-                         /* Always propagate access mode from unified to legacy controller */
-                         r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
-                         if (r < 0)
-                                 log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path);
-                 }
-         }
-         return 0;
- }
  int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
          _cleanup_free_ char *fs = NULL;
          int r;
@@@ -2141,194 -1728,6 +1726,6 @@@ fail
  done:
          memcpy(ret_values, v, sizeof(char*) * n);
          return 0;
- }
- int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
-         CGroupController c;
-         CGroupMask done;
-         bool created;
-         int r;
-         /* This one will create a cgroup in our private tree, but also
-          * duplicate it in the trees specified in mask, and remove it
-          * in all others.
-          *
-          * Returns 0 if the group already existed in the systemd hierarchy,
-          * 1 on success, negative otherwise.
-          */
-         /* First create the cgroup in our own hierarchy. */
-         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
-         if (r < 0)
-                 return r;
-         created = r;
-         /* If we are in the unified hierarchy, we are done now */
-         r = cg_all_unified();
-         if (r < 0)
-                 return r;
-         if (r > 0)
-                 return created;
-         supported &= CGROUP_MASK_V1;
-         mask = CGROUP_MASK_EXTEND_JOINED(mask);
-         done = 0;
-         /* Otherwise, do the same in the other hierarchies */
-         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
-                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
-                 const char *n;
-                 if (!FLAGS_SET(supported, bit))
-                         continue;
-                 if (FLAGS_SET(done, bit))
-                         continue;
-                 n = cgroup_controller_to_string(c);
-                 if (FLAGS_SET(mask, bit))
-                         (void) cg_create(n, path);
-                 else
-                         (void) cg_trim(n, path, true);
-                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
-         }
-         return created;
- }
- int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
-         CGroupController c;
-         CGroupMask done;
-         int r;
-         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
-         if (r < 0)
-                 return r;
-         r = cg_all_unified();
-         if (r < 0)
-                 return r;
-         if (r > 0)
-                 return 0;
-         supported &= CGROUP_MASK_V1;
-         done = 0;
-         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
-                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
-                 const char *p = NULL;
-                 if (!FLAGS_SET(supported, bit))
-                         continue;
-                 if (FLAGS_SET(done, bit))
-                         continue;
-                 if (path_callback)
-                         p = path_callback(bit, userdata);
-                 if (!p)
-                         p = path;
-                 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
-                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
-         }
-         return 0;
- }
- int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
-         Iterator i;
-         void *pidp;
-         int r = 0;
-         SET_FOREACH(pidp, pids, i) {
-                 pid_t pid = PTR_TO_PID(pidp);
-                 int q;
-                 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
-                 if (q < 0 && r >= 0)
-                         r = q;
-         }
-         return r;
- }
- int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
-         CGroupController c;
-         CGroupMask done;
-         int r = 0, q;
-         if (!path_equal(from, to))  {
-                 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
-                 if (r < 0)
-                         return r;
-         }
-         q = cg_all_unified();
-         if (q < 0)
-                 return q;
-         if (q > 0)
-                 return r;
-         supported &= CGROUP_MASK_V1;
-         done = 0;
-         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
-                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
-                 const char *p = NULL;
-                 if (!FLAGS_SET(supported, bit))
-                         continue;
-                 if (FLAGS_SET(done, bit))
-                         continue;
-                 if (to_callback)
-                         p = to_callback(bit, userdata);
-                 if (!p)
-                         p = to;
-                 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
-                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
-         }
-         return r;
- }
- int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
-         CGroupController c;
-         CGroupMask done;
-         int r, q;
-         r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
-         if (r < 0)
-                 return r;
-         q = cg_all_unified();
-         if (q < 0)
-                 return q;
-         if (q > 0)
-                 return r;
-         supported &= CGROUP_MASK_V1;
-         done = 0;
-         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
-                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
-                 if (!FLAGS_SET(supported, bit))
-                         continue;
-                 if (FLAGS_SET(done, bit))
-                         continue;
-                 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
-                 done |= CGROUP_MASK_EXTEND_JOINED(bit);
-         }
-         return r;
  }
  
  int cg_mask_to_string(CGroupMask mask, char **ret) {
@@@ -2435,7 -1834,8 +1832,7 @@@ int cg_mask_supported(CGroupMask *ret) 
                  if (r < 0)
                          return r;
  
 -                /* Currently, we support the cpu, memory, io and pids controller in the unified hierarchy, mask
 -                 * everything else off. */
 +                /* Mask controllers that are not supported in unified hierarchy. */
                  mask &= CGROUP_MASK_V2;
  
          } else {
@@@ -2523,20 -1923,20 +1920,20 @@@ int cg_kernel_controllers(Set **ret) 
          return 0;
  }
  
- static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
- /* The hybrid mode was initially implemented in v232 and simply mounted cgroup2 on /sys/fs/cgroup/systemd.  This
-  * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
-  * /sys/fs/cgroup/systemd.  From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
-  * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
+ /* The hybrid mode was initially implemented in v232 and simply mounted cgroup2 on
+  * /sys/fs/cgroup/systemd. This unfortunately broke other tools (such as docker) which expected the v1
+  * "name=systemd" hierarchy on /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mounts v2 on
+  * /sys/fs/cgroup/unified and maintains "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility
+  * with other tools.
   *
-  * To keep live upgrade working, we detect and support v232 layout.  When v232 layout is detected, to keep cgroup v2
-  * process management but disable the compat dual layout, we return %true on
-  * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
+  * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep
+  * cgroup v2 process management but disable the compat dual layout, we return true on
+  * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and false on cg_hybrid_unified().
   */
  static thread_local bool unified_systemd_v232;
  
- static int cg_unified_update(void) {
+ int cg_unified_cached(bool flush) {
+         static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
  
          struct statfs fs;
  
           * have any other trouble determining if the unified hierarchy
           * is supported. */
  
-         if (unified_cache >= CGROUP_UNIFIED_NONE)
-                 return 0;
+         if (flush)
+                 unified_cache = CGROUP_UNIFIED_UNKNOWN;
+         else if (unified_cache >= CGROUP_UNIFIED_NONE)
+                 return unified_cache;
  
          if (statfs("/sys/fs/cgroup/", &fs) < 0)
                  return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\") failed: %m");
                                         "Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
                                         (unsigned long long)fs.f_type);
  
-         return 0;
+         return unified_cache;
  }
  
  int cg_unified_controller(const char *controller) {
          int r;
  
-         r = cg_unified_update();
+         r = cg_unified_cached(false);
          if (r < 0)
                  return r;
  
-         if (unified_cache == CGROUP_UNIFIED_NONE)
+         if (r == CGROUP_UNIFIED_NONE)
                  return false;
  
-         if (unified_cache >= CGROUP_UNIFIED_ALL)
+         if (r >= CGROUP_UNIFIED_ALL)
                  return true;
  
          return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
  int cg_all_unified(void) {
          int r;
  
-         r = cg_unified_update();
+         r = cg_unified_cached(false);
          if (r < 0)
                  return r;
  
-         return unified_cache >= CGROUP_UNIFIED_ALL;
+         return r >= CGROUP_UNIFIED_ALL;
  }
  
  int cg_hybrid_unified(void) {
          int r;
  
-         r = cg_unified_update();
+         r = cg_unified_cached(false);
          if (r < 0)
                  return r;
  
-         return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
- }
- int cg_unified_flush(void) {
-         unified_cache = CGROUP_UNIFIED_UNKNOWN;
-         return cg_unified_update();
- }
- int cg_enable_everywhere(
-                 CGroupMask supported,
-                 CGroupMask mask,
-                 const char *p,
-                 CGroupMask *ret_result_mask) {
-         _cleanup_fclose_ FILE *f = NULL;
-         _cleanup_free_ char *fs = NULL;
-         CGroupController c;
-         CGroupMask ret = 0;
-         int r;
-         assert(p);
-         if (supported == 0) {
-                 if (ret_result_mask)
-                         *ret_result_mask = 0;
-                 return 0;
-         }
-         r = cg_all_unified();
-         if (r < 0)
-                 return r;
-         if (r == 0) {
-                 /* On the legacy hierarchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
-                  * complete success right away. (If you wonder why we return the full mask here, rather than zero: the
-                  * caller tends to use the returned mask later on to compare if all controllers where properly joined,
-                  * and if not requeues realization. This use is the primary purpose of the return value, hence let's
-                  * minimize surprises here and reduce triggers for re-realization by always saying we fully
-                  * succeeded.) */
-                 if (ret_result_mask)
-                         *ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
-                                                                                * CGROUP_MASK_V2: The 'supported' mask
-                                                                                * might contain pure-V1 or BPF
-                                                                                * controllers, and we never want to
-                                                                                * claim that we could enable those with
-                                                                                * cgroup.subtree_control */
-                 return 0;
-         }
-         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
-         if (r < 0)
-                 return r;
-         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
-                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
-                 const char *n;
-                 if (!FLAGS_SET(CGROUP_MASK_V2, bit))
-                         continue;
-                 if (!FLAGS_SET(supported, bit))
-                         continue;
-                 n = cgroup_controller_to_string(c);
-                 {
-                         char s[1 + strlen(n) + 1];
-                         s[0] = FLAGS_SET(mask, bit) ? '+' : '-';
-                         strcpy(s + 1, n);
-                         if (!f) {
-                                 f = fopen(fs, "we");
-                                 if (!f)
-                                         return log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
-                         }
-                         r = write_string_stream(f, s, WRITE_STRING_FILE_DISABLE_BUFFER);
-                         if (r < 0) {
-                                 log_debug_errno(r, "Failed to %s controller %s for %s (%s): %m",
-                                                 FLAGS_SET(mask, bit) ? "enable" : "disable", n, p, fs);
-                                 clearerr(f);
-                                 /* If we can't turn off a controller, leave it on in the reported resulting mask. This
-                                  * happens for example when we attempt to turn off a controller up in the tree that is
-                                  * used down in the tree. */
-                                 if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
-                                                                            * only here, and not follow the same logic
-                                                                            * for other errors such as EINVAL or
-                                                                            * EOPNOTSUPP or anything else. That's
-                                                                            * because EBUSY indicates that the
-                                                                            * controllers is currently enabled and
-                                                                            * cannot be disabled because something down
-                                                                            * the hierarchy is still using it. Any other
-                                                                            * error most likely means something like "I
-                                                                            * never heard of this controller" or
-                                                                            * similar. In the former case it's hence
-                                                                            * safe to assume the controller is still on
-                                                                            * after the failed operation, while in the
-                                                                            * latter case it's safer to assume the
-                                                                            * controller is unknown and hence certainly
-                                                                            * not enabled. */
-                                         ret |= bit;
-                         } else {
-                                 /* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
-                                 if (FLAGS_SET(mask, bit))
-                                         ret |= bit;
-                         }
-                 }
-         }
-         /* Let's return the precise set of controllers now enabled for the cgroup. */
-         if (ret_result_mask)
-                 *ret_result_mask = ret;
-         return 0;
- }
- bool cg_is_unified_wanted(void) {
-         static thread_local int wanted = -1;
-         int r;
-         bool b;
-         const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
-         _cleanup_free_ char *c = NULL;
-         /* If we have a cached value, return that. */
-         if (wanted >= 0)
-                 return wanted;
-         /* If the hierarchy is already mounted, then follow whatever
-          * was chosen for it. */
-         if (cg_unified_flush() >= 0)
-                 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
-         /* If we were explicitly passed systemd.unified_cgroup_hierarchy,
-          * respect that. */
-         r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
-         if (r > 0)
-                 return (wanted = b);
-         /* If we passed cgroup_no_v1=all with no other instructions, it seems
-          * highly unlikely that we want to use hybrid or legacy hierarchy. */
-         r = proc_cmdline_get_key("cgroup_no_v1", 0, &c);
-         if (r > 0 && streq_ptr(c, "all"))
-                 return (wanted = true);
-         return (wanted = is_default);
- }
- bool cg_is_legacy_wanted(void) {
-         static thread_local int wanted = -1;
-         /* If we have a cached value, return that. */
-         if (wanted >= 0)
-                 return wanted;
-         /* Check if we have cgroup v2 already mounted. */
-         if (cg_unified_flush() >= 0 &&
-             unified_cache == CGROUP_UNIFIED_ALL)
-                 return (wanted = false);
-         /* Otherwise, assume that at least partial legacy is wanted,
-          * since cgroup v2 should already be mounted at this point. */
-         return (wanted = true);
- }
- bool cg_is_hybrid_wanted(void) {
-         static thread_local int wanted = -1;
-         int r;
-         bool b;
-         const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
-         /* We default to true if the default is "hybrid", obviously,
-          * but also when the default is "unified", because if we get
-          * called, it means that unified hierarchy was not mounted. */
-         /* If we have a cached value, return that. */
-         if (wanted >= 0)
-                 return wanted;
-         /* If the hierarchy is already mounted, then follow whatever
-          * was chosen for it. */
-         if (cg_unified_flush() >= 0 &&
-             unified_cache == CGROUP_UNIFIED_ALL)
-                 return (wanted = false);
-         /* Otherwise, let's see what the kernel command line has to say.
-          * Since checking is expensive, cache a non-error result. */
-         r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
-         /* The meaning of the kernel option is reversed wrt. to the return value
-          * of this function, hence the negation. */
-         return (wanted = r > 0 ? !b : is_default);
- }
- int cg_weight_parse(const char *s, uint64_t *ret) {
-         uint64_t u;
-         int r;
-         if (isempty(s)) {
-                 *ret = CGROUP_WEIGHT_INVALID;
-                 return 0;
-         }
-         r = safe_atou64(s, &u);
-         if (r < 0)
-                 return r;
-         if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
-                 return -ERANGE;
-         *ret = u;
-         return 0;
+         return r == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
  }
  
  const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
@@@ -2847,46 -2039,6 +2036,6 @@@ static const char* const cgroup_io_limi
  
  DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
  
- int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
-         uint64_t u;
-         int r;
-         if (isempty(s)) {
-                 *ret = CGROUP_CPU_SHARES_INVALID;
-                 return 0;
-         }
-         r = safe_atou64(s, &u);
-         if (r < 0)
-                 return r;
-         if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
-                 return -ERANGE;
-         *ret = u;
-         return 0;
- }
- int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
-         uint64_t u;
-         int r;
-         if (isempty(s)) {
-                 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
-                 return 0;
-         }
-         r = safe_atou64(s, &u);
-         if (r < 0)
-                 return r;
-         if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
-                 return -ERANGE;
-         *ret = u;
-         return 0;
- }
  bool is_cgroup_fs(const struct statfs *s) {
          return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
                 is_fs_type(s, CGROUP2_SUPER_MAGIC);
@@@ -2904,7 -2056,6 +2053,7 @@@ bool fd_is_cgroup_fs(int fd) 
  static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
          [CGROUP_CONTROLLER_CPU] = "cpu",
          [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
 +        [CGROUP_CONTROLLER_CPUSET] = "cpuset",
          [CGROUP_CONTROLLER_IO] = "io",
          [CGROUP_CONTROLLER_BLKIO] = "blkio",
          [CGROUP_CONTROLLER_MEMORY] = "memory",
diff --combined src/basic/cgroup-util.h
index ab094fdc4fb296f0e80cf097d5244b13a27a6014,147226520414191f8448a79d0546bbf7c2fe51e0..0a64715af5e22066d745b1aa3282d6b00bdce739
@@@ -20,7 -20,6 +20,7 @@@ typedef enum CGroupController 
          /* Original cgroup controllers */
          CGROUP_CONTROLLER_CPU,
          CGROUP_CONTROLLER_CPUACCT,    /* v1 only */
 +        CGROUP_CONTROLLER_CPUSET,     /* v2 only */
          CGROUP_CONTROLLER_IO,         /* v2 only */
          CGROUP_CONTROLLER_BLKIO,      /* v1 only */
          CGROUP_CONTROLLER_MEMORY,
@@@ -41,7 -40,6 +41,7 @@@
  typedef enum CGroupMask {
          CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU),
          CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT),
 +        CGROUP_MASK_CPUSET = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUSET),
          CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO),
          CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO),
          CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY),
@@@ -54,7 -52,7 +54,7 @@@
          CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
  
          /* All real cgroup v2 controllers */
 -        CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
 +        CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
  
          /* All cgroup v2 BPF pseudo-controllers */
          CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES,
@@@ -174,10 -172,6 +174,6 @@@ typedef int (*cg_kill_log_func_t)(pid_
  int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
  int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
  
- int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
- int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
- int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
  int cg_split_spec(const char *spec, char **controller, char **path);
  int cg_mangle_path(const char *path, char **result);
  
@@@ -186,15 -180,8 +182,8 @@@ int cg_get_path_and_check(const char *c
  
  int cg_pid_get_path(const char *controller, pid_t pid, char **path);
  
- int cg_trim(const char *controller, const char *path, bool delete_root);
  int cg_rmdir(const char *controller, const char *path);
  
- int cg_create(const char *controller, const char *path);
- int cg_attach(const char *controller, const char *path, pid_t pid);
- int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
- int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
  int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
  int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
  int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values);
@@@ -242,13 -229,6 +231,6 @@@ int cg_slice_to_path(const char *unit, 
  
  typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata);
  
- int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path);
- int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
- int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
- int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
- int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
- int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p, CGroupMask *ret_result_mask);
  int cg_mask_supported(CGroupMask *ret);
  int cg_mask_from_string(const char *s, CGroupMask *ret);
  int cg_mask_to_string(CGroupMask mask, char **ret);
@@@ -260,18 -240,13 +242,13 @@@ bool cg_ns_supported(void)
  int cg_all_unified(void);
  int cg_hybrid_unified(void);
  int cg_unified_controller(const char *controller);
- int cg_unified_flush(void);
- bool cg_is_unified_wanted(void);
- bool cg_is_legacy_wanted(void);
- bool cg_is_hybrid_wanted(void);
+ int cg_unified_cached(bool flush);
+ static inline int cg_unified(void) {
+         return cg_unified_cached(true);
+ }
  
  const char* cgroup_controller_to_string(CGroupController c) _const_;
  CGroupController cgroup_controller_from_string(const char *s) _pure_;
  
- int cg_weight_parse(const char *s, uint64_t *ret);
- int cg_cpu_shares_parse(const char *s, uint64_t *ret);
- int cg_blkio_weight_parse(const char *s, uint64_t *ret);
  bool is_cgroup_fs(const struct statfs *s);
  bool fd_is_cgroup_fs(int fd);
diff --combined src/core/cgroup.c
index 74579a0a19eb059007e7ccbc4866750405e59d71,00690920cdb6cf7c7b61d17ef9c079d92339826e..edd10fc31d7d28f925792e4ff83b70230590467d
@@@ -11,6 -11,7 +11,7 @@@
  #include "bpf-firewall.h"
  #include "btrfs-util.h"
  #include "bus-error.h"
+ #include "cgroup-setup.h"
  #include "cgroup-util.h"
  #include "cgroup.h"
  #include "fd-util.h"
@@@ -202,15 -203,10 +203,15 @@@ void cgroup_context_done(CGroupContext 
  
          c->ip_filters_ingress = strv_free(c->ip_filters_ingress);
          c->ip_filters_egress = strv_free(c->ip_filters_egress);
 +
 +        cpu_set_reset(&c->cpuset_cpus);
 +        cpu_set_reset(&c->cpuset_mems);
  }
  
  void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
          _cleanup_free_ char *disable_controllers_str = NULL;
 +        _cleanup_free_ char *cpuset_cpus = NULL;
 +        _cleanup_free_ char *cpuset_mems = NULL;
          CGroupIODeviceLimit *il;
          CGroupIODeviceWeight *iw;
          CGroupIODeviceLatency *l;
  
          (void) cg_mask_to_string(c->disable_controllers, &disable_controllers_str);
  
 +        cpuset_cpus = cpu_set_to_range_string(&c->cpuset_cpus);
 +        cpuset_mems = cpu_set_to_range_string(&c->cpuset_mems);
 +
          fprintf(f,
                  "%sCPUAccounting=%s\n"
                  "%sIOAccounting=%s\n"
                  "%sStartupCPUShares=%" PRIu64 "\n"
                  "%sCPUQuotaPerSecSec=%s\n"
                  "%sCPUQuotaPeriodSec=%s\n"
 +                "%sAllowedCPUs=%s\n"
 +                "%sAllowedMemoryNodes=%s\n"
                  "%sIOWeight=%" PRIu64 "\n"
                  "%sStartupIOWeight=%" PRIu64 "\n"
                  "%sBlockIOWeight=%" PRIu64 "\n"
                  prefix, c->startup_cpu_shares,
                  prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1),
                  prefix, format_timespan(v, sizeof(v), c->cpu_quota_period_usec, 1),
 +                prefix, cpuset_cpus,
 +                prefix, cpuset_mems,
                  prefix, c->io_weight,
                  prefix, c->startup_io_weight,
                  prefix, c->blockio_weight,
@@@ -808,16 -797,6 +809,16 @@@ static uint64_t cgroup_cpu_weight_to_sh
                       CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX);
  }
  
 +static void cgroup_apply_unified_cpuset(Unit *u, CPUSet cpus, const char *name) {
 +        _cleanup_free_ char *buf = NULL;
 +
 +        buf = cpu_set_to_range_string(&cpus);
 +        if (!buf)
 +            return;
 +
 +        (void) set_attribute_and_warn(u, "cpuset", name, buf);
 +}
 +
  static bool cgroup_context_has_io_config(CGroupContext *c) {
          return c->io_accounting ||
                  c->io_weight != CGROUP_WEIGHT_INVALID ||
@@@ -955,7 -934,7 +956,7 @@@ static bool unit_has_unified_memory_con
          c = unit_get_cgroup_context(u);
          assert(c);
  
 -        return c->memory_min > 0 || unit_get_ancestor_memory_low(u) > 0 ||
 +        return unit_get_ancestor_memory_min(u) > 0 || unit_get_ancestor_memory_low(u) > 0 ||
                 c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX ||
                 c->memory_swap_max != CGROUP_LIMIT_MAX;
  }
@@@ -1058,11 -1037,6 +1059,11 @@@ static void cgroup_context_apply
                  }
          }
  
 +        if ((apply_mask & CGROUP_MASK_CPUSET) && !is_local_root) {
 +                cgroup_apply_unified_cpuset(u, c->cpuset_cpus, "cpuset.cpus");
 +                cgroup_apply_unified_cpuset(u, c->cpuset_mems, "cpuset.mems");
 +        }
 +
          /* The 'io' controller attributes are not exported on the host's root cgroup (being a pure cgroup v2
           * controller), and in case of containers we want to leave control of these attributes to the container manager
           * (and we couldn't access that stuff anyway, even if we tried if proper delegation is used). */
                                          log_cgroup_compat(u, "Applying MemoryLimit=%" PRIu64 " as MemoryMax=", max);
                          }
  
 -                        cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min);
 +                        cgroup_apply_unified_memory_limit(u, "memory.min", unit_get_ancestor_memory_min(u));
                          cgroup_apply_unified_memory_limit(u, "memory.low", unit_get_ancestor_memory_low(u));
                          cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
                          cgroup_apply_unified_memory_limit(u, "memory.max", max);
@@@ -1435,9 -1409,6 +1436,9 @@@ static CGroupMask unit_get_cgroup_mask(
              c->cpu_quota_per_sec_usec != USEC_INFINITY)
                  mask |= CGROUP_MASK_CPU;
  
 +        if (c->cpuset_cpus.set || c->cpuset_mems.set)
 +                mask |= CGROUP_MASK_CPUSET;
 +
          if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c))
                  mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO;
  
@@@ -2869,7 -2840,7 +2870,7 @@@ int manager_setup_cgroup(Manager *m) 
          if (r < 0)
                  return log_error_errno(r, "Cannot find cgroup mount point: %m");
  
-         r = cg_unified_flush();
+         r = cg_unified();
          if (r < 0)
                  return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m");
  
@@@ -3590,32 -3561,4 +3591,32 @@@ static const char* const cgroup_device_
          [CGROUP_STRICT] = "strict",
  };
  
 +int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) {
 +        _cleanup_free_ char *v = NULL;
 +        int r;
 +
 +        assert(u);
 +        assert(cpus);
 +
 +        if (!u->cgroup_path)
 +                return -ENODATA;
 +
 +        if ((u->cgroup_realized_mask & CGROUP_MASK_CPUSET) == 0)
 +                return -ENODATA;
 +
 +        r = cg_all_unified();
 +        if (r < 0)
 +                return r;
 +        if (r == 0)
 +                return -ENODATA;
 +        if (r > 0)
 +                r = cg_get_attribute("cpuset", u->cgroup_path, name, &v);
 +        if (r == -ENOENT)
 +                return -ENODATA;
 +        if (r < 0)
 +                return r;
 +
 +        return parse_cpu_set_full(v, cpus, false, NULL, NULL, 0, NULL);
 +}
 +
  DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);
diff --combined src/core/execute.c
index 54775e9dfa047193f42450d9f408a7208583c1e1,f769d02be407453464a7cff1d87315c6dec831a4..82e7428c3c94632da08102f427a84317e9de8f93
@@@ -48,6 -48,7 +48,7 @@@
  #include "cap-list.h"
  #include "capability-util.h"
  #include "chown-recursive.h"
+ #include "cgroup-setup.h"
  #include "cpu-set-util.h"
  #include "def.h"
  #include "env-file.h"
@@@ -1621,7 -1622,7 +1622,7 @@@ static void do_idle_pipe_dance(int idle
                          n = write(idle_pipe[3], "x", 1);
                          if (n > 0)
                                  /* Wait for systemd to react to the signal above. */
 -                                fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
 +                                (void) fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
                  }
  
                  idle_pipe[0] = safe_close(idle_pipe[0]);
@@@ -4000,8 -4001,8 +4001,8 @@@ void exec_context_done(ExecContext *c) 
  
          exec_context_free_log_extra_fields(c);
  
 -        c->log_rate_limit_interval_usec = 0;
 -        c->log_rate_limit_burst = 0;
 +        c->log_ratelimit_interval_usec = 0;
 +        c->log_ratelimit_burst = 0;
  
          c->stdin_data = mfree(c->stdin_data);
          c->stdin_data_size = 0;
@@@ -4515,16 -4516,16 +4516,16 @@@ void exec_context_dump(const ExecContex
                  fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
          }
  
 -        if (c->log_rate_limit_interval_usec > 0) {
 +        if (c->log_ratelimit_interval_usec > 0) {
                  char buf_timespan[FORMAT_TIMESPAN_MAX];
  
                  fprintf(f,
                          "%sLogRateLimitIntervalSec: %s\n",
 -                        prefix, format_timespan(buf_timespan, sizeof(buf_timespan), c->log_rate_limit_interval_usec, USEC_PER_SEC));
 +                        prefix, format_timespan(buf_timespan, sizeof(buf_timespan), c->log_ratelimit_interval_usec, USEC_PER_SEC));
          }
  
 -        if (c->log_rate_limit_burst > 0)
 -                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_rate_limit_burst);
 +        if (c->log_ratelimit_burst > 0)
 +                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit_burst);
  
          if (c->n_log_extra_fields > 0) {
                  size_t j;
diff --combined src/core/load-fragment.c
index 5fdead22f9d82b88546d8f01b61b211dc23594a9,e517c8dfbf36c90cefa09f106c63ed39fc704b6b..72eb13cc006cf82b7f1f9bf295a05ef253460406
@@@ -24,7 -24,7 +24,7 @@@
  #include "bus-util.h"
  #include "cap-list.h"
  #include "capability-util.h"
- #include "cgroup.h"
+ #include "cgroup-setup.h"
  #include "conf-parser.h"
  #include "cpu-set-util.h"
  #include "env-util.h"
@@@ -133,7 -133,6 +133,7 @@@ DEFINE_CONFIG_PARSE_PTR(config_parse_cg
  DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
  DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
  DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
 +DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
  
  int config_parse_unit_deps(
                  const char *unit,
@@@ -3149,44 -3148,6 +3149,44 @@@ int config_parse_cpu_quota
          return 0;
  }
  
 +int config_parse_cpuset_cpus(
 +                const char *unit,
 +                const char *filename,
 +                unsigned line,
 +                const char *section,
 +                unsigned section_line,
 +                const char *lvalue,
 +                int ltype,
 +                const char *rvalue,
 +                void *data,
 +                void *userdata) {
 +
 +        CGroupContext *c = data;
 +
 +        (void) parse_cpu_set_extend(rvalue, &c->cpuset_cpus, true, unit, filename, line, lvalue);
 +
 +        return 0;
 +}
 +
 +int config_parse_cpuset_mems(
 +                const char *unit,
 +                const char *filename,
 +                unsigned line,
 +                const char *section,
 +                unsigned section_line,
 +                const char *lvalue,
 +                int ltype,
 +                const char *rvalue,
 +                void *data,
 +                void *userdata) {
 +
 +        CGroupContext *c = data;
 +
 +        (void) parse_cpu_set_extend(rvalue, &c->cpuset_mems, true, unit, filename, line, lvalue);
 +
 +        return 0;
 +}
 +
  int config_parse_memory_limit(
                  const char *unit,
                  const char *filename,
diff --combined src/core/mount-setup.c
index 9d9263e8faf602cf38df897cf98bf654a4474305,9f2e7ff98551534c793ae20723c8f0f3f2bbbca9..f05445e4fb452a9a7291622420b1f1efd09d0dcb
  #include "bus-util.h"
  #include "cgroup-util.h"
  #include "conf-files.h"
+ #include "cgroup-setup.h"
  #include "dev-setup.h"
- #include "efivars.h"
+ #include "dirent-util.h"
+ #include "efi-loader.h"
  #include "fd-util.h"
  #include "fileio.h"
  #include "fs-util.h"
@@@ -469,8 -471,7 +471,8 @@@ static int relabel_extra(void) 
          }
  
          /* Remove when we complete things. */
 -        if (rmdir("/run/systemd/relabel-extra.d") < 0)
 +        if (rmdir("/run/systemd/relabel-extra.d") < 0 &&
 +            errno != ENOENT)
                  log_warning_errno(errno, "Failed to remove /run/systemd/relabel-extra.d/ directory: %m");
  
          return c;
diff --combined src/core/unit.c
index 0d93b94b81c23330640d3a99f0f3b22cf7db9430,004d787d8f2ba0649ce74fed37341097eda0d412..6dd075faa775c67e7c003681cbdbb27e307d4f52
@@@ -15,6 -15,7 +15,7 @@@
  #include "bpf-firewall.h"
  #include "bus-common-errors.h"
  #include "bus-util.h"
+ #include "cgroup-setup.h"
  #include "cgroup-util.h"
  #include "dbus-unit.h"
  #include "dbus.h"
@@@ -123,8 -124,8 +124,8 @@@ Unit *unit_new(Manager *m, size_t size
  
          u->last_section_private = -1;
  
 -        RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
 -        RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
 +        u->start_ratelimit = (RateLimit) { m->default_start_limit_interval, m->default_start_limit_burst };
 +        u->auto_stop_ratelimit = (RateLimit) { 10 * USEC_PER_SEC, 16 };
  
          for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++)
                  u->io_accounting_last[i] = UINT64_MAX;
@@@ -1679,7 -1680,7 +1680,7 @@@ int unit_test_start_limit(Unit *u) 
  
          assert(u);
  
 -        if (ratelimit_below(&u->start_limit)) {
 +        if (ratelimit_below(&u->start_ratelimit)) {
                  u->start_limit_hit = false;
                  return 0;
          }
@@@ -2789,7 -2790,7 +2790,7 @@@ int unit_enqueue_rewatch_pids(Unit *u) 
  
                  r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE);
                  if (r < 0)
 -                        return log_error_errno(r, "Failed to adjust priority of event source for tidying watched PIDs: m");
 +                        return log_error_errno(r, "Failed to adjust priority of event source for tidying watched PIDs: %m");
  
                  (void) sd_event_source_set_description(s, "tidy-watch-pids");
  
@@@ -3416,8 -3417,8 +3417,8 @@@ int unit_serialize(Unit *u, FILE *f, FD
          (void) serialize_bool(f, "exported-invocation-id", u->exported_invocation_id);
          (void) serialize_bool(f, "exported-log-level-max", u->exported_log_level_max);
          (void) serialize_bool(f, "exported-log-extra-fields", u->exported_log_extra_fields);
 -        (void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_rate_limit_interval);
 -        (void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_rate_limit_burst);
 +        (void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_ratelimit_interval);
 +        (void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_ratelimit_burst);
  
          (void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
          if (u->cpu_usage_last != NSEC_INFINITY)
@@@ -3636,7 -3637,7 +3637,7 @@@ int unit_deserialize(Unit *u, FILE *f, 
                          if (r < 0)
                                  log_unit_debug(u, "Failed to parse exported log rate limit interval %s, ignoring.", v);
                          else
 -                                u->exported_log_rate_limit_interval = r;
 +                                u->exported_log_ratelimit_interval = r;
  
                          continue;
  
                          if (r < 0)
                                  log_unit_debug(u, "Failed to parse exported log rate limit burst %s, ignoring.", v);
                          else
 -                                u->exported_log_rate_limit_burst = r;
 +                                u->exported_log_ratelimit_burst = r;
  
                          continue;
  
@@@ -3901,7 -3902,6 +3902,7 @@@ int unit_add_node_dependency(Unit *u, c
  int unit_coldplug(Unit *u) {
          int r = 0, q;
          char **i;
 +        Job *uj;
  
          assert(u);
  
                          r = q;
          }
  
 -        if (u->job) {
 -                q = job_coldplug(u->job);
 +        uj = u->job ?: u->nop_job;
 +        if (uj) {
 +                q = job_coldplug(uj);
                  if (q < 0 && r >= 0)
                          r = q;
          }
@@@ -4000,7 -3999,7 +4001,7 @@@ void unit_reset_failed(Unit *u) 
          if (UNIT_VTABLE(u)->reset_failed)
                  UNIT_VTABLE(u)->reset_failed(u);
  
 -        RATELIMIT_RESET(u->start_limit);
 +        ratelimit_reset(&u->start_ratelimit);
          u->start_limit_hit = false;
  }
  
@@@ -4022,7 -4021,7 +4023,7 @@@ bool unit_stop_pending(Unit *u) 
           * different from unit_inactive_or_pending() which checks both
           * the current state and for a queued job. */
  
 -        return u->job && u->job->type == JOB_STOP;
 +        return unit_has_job_type(u, JOB_STOP);
  }
  
  bool unit_inactive_or_pending(Unit *u) {
@@@ -4057,7 -4056,12 +4058,7 @@@ bool unit_active_or_pending(Unit *u) 
  bool unit_will_restart_default(Unit *u) {
          assert(u);
  
 -        if (!u->job)
 -                return false;
 -        if (u->job->type == JOB_START)
 -                return true;
 -
 -        return false;
 +        return unit_has_job_type(u, JOB_START);
  }
  
  bool unit_will_restart(Unit *u) {
@@@ -4686,26 -4690,19 +4687,26 @@@ static int log_kill(pid_t pid, int sig
          return 1;
  }
  
 -static int operation_to_signal(KillContext *c, KillOperation k) {
 +static int operation_to_signal(const KillContext *c, KillOperation k, bool *noteworthy) {
          assert(c);
  
          switch (k) {
  
          case KILL_TERMINATE:
          case KILL_TERMINATE_AND_LOG:
 +                *noteworthy = false;
                  return c->kill_signal;
  
 +        case KILL_RESTART:
 +                *noteworthy = false;
 +                return restart_kill_signal(c);
 +
          case KILL_KILL:
 +                *noteworthy = true;
                  return c->final_kill_signal;
  
          case KILL_WATCHDOG:
 +                *noteworthy = true;
                  return c->watchdog_signal;
  
          default:
@@@ -4734,16 -4731,16 +4735,16 @@@ int unit_kill_context
          if (c->kill_mode == KILL_NONE)
                  return 0;
  
 -        sig = operation_to_signal(c, k);
 +        bool noteworthy;
 +        sig = operation_to_signal(c, k, &noteworthy);
 +        if (noteworthy)
 +                log_func = log_kill;
  
          send_sighup =
                  c->send_sighup &&
                  IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) &&
                  sig != SIGHUP;
  
 -        if (k != KILL_TERMINATE || IN_SET(sig, SIGKILL, SIGABRT))
 -                log_func = log_kill;
 -
          if (main_pid > 0) {
                  if (log_func)
                          log_func(main_pid, sig, u);
@@@ -5506,7 -5503,7 +5507,7 @@@ fail
          return r;
  }
  
 -static int unit_export_log_rate_limit_interval(Unit *u, const ExecContext *c) {
 +static int unit_export_log_ratelimit_interval(Unit *u, const ExecContext *c) {
          _cleanup_free_ char *buf = NULL;
          const char *p;
          int r;
          assert(u);
          assert(c);
  
 -        if (u->exported_log_rate_limit_interval)
 +        if (u->exported_log_ratelimit_interval)
                  return 0;
  
 -        if (c->log_rate_limit_interval_usec == 0)
 +        if (c->log_ratelimit_interval_usec == 0)
                  return 0;
  
          p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
  
 -        if (asprintf(&buf, "%" PRIu64, c->log_rate_limit_interval_usec) < 0)
 +        if (asprintf(&buf, "%" PRIu64, c->log_ratelimit_interval_usec) < 0)
                  return log_oom();
  
          r = symlink_atomic(buf, p);
          if (r < 0)
                  return log_unit_debug_errno(u, r, "Failed to create log rate limit interval symlink %s: %m", p);
  
 -        u->exported_log_rate_limit_interval = true;
 +        u->exported_log_ratelimit_interval = true;
          return 0;
  }
  
 -static int unit_export_log_rate_limit_burst(Unit *u, const ExecContext *c) {
 +static int unit_export_log_ratelimit_burst(Unit *u, const ExecContext *c) {
          _cleanup_free_ char *buf = NULL;
          const char *p;
          int r;
          assert(u);
          assert(c);
  
 -        if (u->exported_log_rate_limit_burst)
 +        if (u->exported_log_ratelimit_burst)
                  return 0;
  
 -        if (c->log_rate_limit_burst == 0)
 +        if (c->log_ratelimit_burst == 0)
                  return 0;
  
          p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
  
 -        if (asprintf(&buf, "%u", c->log_rate_limit_burst) < 0)
 +        if (asprintf(&buf, "%u", c->log_ratelimit_burst) < 0)
                  return log_oom();
  
          r = symlink_atomic(buf, p);
          if (r < 0)
                  return log_unit_debug_errno(u, r, "Failed to create log rate limit burst symlink %s: %m", p);
  
 -        u->exported_log_rate_limit_burst = true;
 +        u->exported_log_ratelimit_burst = true;
          return 0;
  }
  
@@@ -5593,8 -5590,8 +5594,8 @@@ void unit_export_state_files(Unit *u) 
          if (c) {
                  (void) unit_export_log_level_max(u, c);
                  (void) unit_export_log_extra_fields(u, c);
 -                (void) unit_export_log_rate_limit_interval(u, c);
 -                (void) unit_export_log_rate_limit_burst(u, c);
 +                (void) unit_export_log_ratelimit_interval(u, c);
 +                (void) unit_export_log_ratelimit_burst(u, c);
          }
  }
  
@@@ -5632,18 -5629,18 +5633,18 @@@ void unit_unlink_state_files(Unit *u) 
                  u->exported_log_extra_fields = false;
          }
  
 -        if (u->exported_log_rate_limit_interval) {
 +        if (u->exported_log_ratelimit_interval) {
                  p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
                  (void) unlink(p);
  
 -                u->exported_log_rate_limit_interval = false;
 +                u->exported_log_ratelimit_interval = false;
          }
  
 -        if (u->exported_log_rate_limit_burst) {
 +        if (u->exported_log_ratelimit_burst) {
                  p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
                  (void) unlink(p);
  
 -                u->exported_log_rate_limit_burst = false;
 +                u->exported_log_ratelimit_burst = false;
          }
  }
  
diff --combined src/login/logind-dbus.c
index 679eb6a62ec0c2068f91aabb9681e46cfb755306,1d76ec6885a32a3296419109a23f80ec8d045116..20a8591bd8e58ec06b8688c260506c7d50849b0d
@@@ -20,6 -20,7 +20,7 @@@
  #include "device-util.h"
  #include "dirent-util.h"
  #include "efivars.h"
+ #include "efi-loader.h"
  #include "env-util.h"
  #include "escape.h"
  #include "fd-util.h"
@@@ -1378,7 -1379,6 +1379,7 @@@ static int flush_devices(Manager *m) 
                  struct dirent *de;
  
                  FOREACH_DIRENT_ALL(de, d, break) {
 +                        dirent_ensure_type(d, de);
                          if (!dirent_is_file(de))
                                  continue;
  
diff --combined src/nspawn/nspawn.c
index 0737517ddc7c905bdd5091ab5f0e6c802600f347,91ec61402b70472771a7ff9562ae2feb83d8d510..0cd960157ce34b06abd04b6114f510700ca17d0c
@@@ -412,27 -412,15 +412,27 @@@ static int custom_mount_check_all(void
  }
  
  static int detect_unified_cgroup_hierarchy_from_environment(void) {
 -        const char *e;
 +        const char *e, *var = "SYSTEMD_NSPAWN_UNIFIED_HIERARCHY";
          int r;
  
          /* Allow the user to control whether the unified hierarchy is used */
 -        e = getenv("UNIFIED_CGROUP_HIERARCHY");
 -        if (e) {
 +
 +        e = getenv(var);
 +        if (!e) {
 +                static bool warned = false;
 +
 +                var = "UNIFIED_CGROUP_HIERARCHY";
 +                e = getenv(var);
 +                if (e && !warned) {
 +                        log_info("$UNIFIED_CGROUP_HIERARCHY has been renamed to $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY.");
 +                        warned = true;
 +                }
 +        }
 +
 +        if (!isempty(e)) {
                  r = parse_boolean(e);
                  if (r < 0)
 -                        return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY.");
 +                        return log_error_errno(r, "Failed to parse $%s: %m", var);
                  if (r > 0)
                          arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
                  else
  static int detect_unified_cgroup_hierarchy_from_image(const char *directory) {
          int r;
  
 -        /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd in the
 -         * image actually supports. */
 +        /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd
 +         * in the image actually supports. */
          r = cg_all_unified();
          if (r < 0)
                  return log_error_errno(r, "Failed to determine whether we are in all unified mode.");
          return 0;
  }
  
 -static void parse_share_ns_env(const char *name, unsigned long ns_flag) {
 +static int parse_share_ns_env(const char *name, unsigned long ns_flag) {
          int r;
  
          r = getenv_bool(name);
          if (r == -ENXIO)
 -                return;
 +                return 0;
          if (r < 0)
 -                log_warning_errno(r, "Failed to parse %s from environment, defaulting to false.", name);
 +                return log_error_errno(r, "Failed to parse $%s: %m", name);
  
          arg_clone_ns_flags = (arg_clone_ns_flags & ~ns_flag) | (r > 0 ? 0 : ns_flag);
          arg_settings_mask |= SETTING_CLONE_NS_FLAGS;
 +        return 0;
  }
  
 -static void parse_mount_settings_env(void) {
 +static int parse_mount_settings_env(void) {
          const char *e;
          int r;
  
          r = getenv_bool("SYSTEMD_NSPAWN_TMPFS_TMP");
 +        if (r < 0 && r != -ENXIO)
 +                return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_TMPFS_TMP: %m");
          if (r >= 0)
                  SET_FLAG(arg_mount_settings, MOUNT_APPLY_TMPFS_TMP, r > 0);
 -        else if (r != -ENXIO)
 -                log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_TMPFS_TMP, ignoring: %m");
  
          e = getenv("SYSTEMD_NSPAWN_API_VFS_WRITABLE");
 -        if (!e)
 -                return;
 -
 -        if (streq(e, "network")) {
 +        if (streq_ptr(e, "network"))
                  arg_mount_settings |= MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS;
 -                return;
 -        }
  
 -        r = parse_boolean(e);
 -        if (r < 0) {
 -                log_warning_errno(r, "Failed to parse SYSTEMD_NSPAWN_API_VFS_WRITABLE from environment, ignoring.");
 -                return;
 +        else if (e) {
 +                r = parse_boolean(e);
 +                if (r < 0)
 +                        return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_API_VFS_WRITABLE: %m");
 +
 +                SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_RO, r == 0);
 +                SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false);
          }
  
 -        SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_RO, r == 0);
 -        SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false);
 +        return 0;
  }
  
 -static void parse_environment(void) {
 +static int parse_environment(void) {
          const char *e;
          int r;
  
 -        parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
 -        parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
 -        parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
 -        parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
 +        r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
 +        if (r < 0)
 +                return r;
 +        r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
 +        if (r < 0)
 +                return r;
 +        r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
 +        if (r < 0)
 +                return r;
 +        r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
 +        if (r < 0)
 +                return r;
  
 -        parse_mount_settings_env();
 +        r = parse_mount_settings_env();
 +        if (r < 0)
 +                return r;
  
          /* SYSTEMD_NSPAWN_USE_CGNS=0 can be used to disable CLONE_NEWCGROUP use,
           * even if it is supported. If not supported, it has no effect. */
                  r = getenv_bool("SYSTEMD_NSPAWN_USE_CGNS");
                  if (r < 0) {
                          if (r != -ENXIO)
 -                                log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_USE_CGNS, ignoring: %m");
 +                                return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_USE_CGNS: %m");
  
                          arg_use_cgns = true;
                  } else {
          if (e)
                  arg_container_service_name = e;
  
 -        detect_unified_cgroup_hierarchy_from_environment();
 +        return detect_unified_cgroup_hierarchy_from_environment();
  }
  
  static int parse_argv(int argc, char *argv[]) {
          arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? UINT64_C(1) << CAP_NET_ADMIN : 0)) & ~minus;
  
          /* Make sure to parse environment before we reset the settings mask below */
 -        parse_environment();
 +        r = parse_environment();
 +        if (r < 0)
 +                return r;
  
          /* Load all settings from .nspawn files */
          if (mask_no_settings)
  static int verify_arguments(void) {
          int r;
  
 +        if (arg_start_mode == START_PID2 && arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
 +                /* If we are running the stub init in the container, we don't need to look at what the init
 +                 * in the container supports, because we are not using it. Let's immediately pick the right
 +                 * setting based on the host system configuration.
 +                 *
 +                 * We only do this, if the user didn't use an environment variable to override the detection.
 +                 */
 +
 +                r = cg_all_unified();
 +                if (r < 0)
 +                        return log_error_errno(r, "Failed to determine whether we are in all unified mode.");
 +                if (r > 0)
 +                        arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
 +                else if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)
 +                        arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD;
 +                else
 +                        arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
 +        }
 +
          if (arg_userns_mode != USER_NAMESPACE_NO)
                  arg_mount_settings |= MOUNT_USE_USERNS;
  
@@@ -4761,7 -4720,7 +4761,7 @@@ static int run(int argc, char *argv[]) 
          if (r < 0)
                  goto finish;
  
-         r = cg_unified_flush();
+         r = cg_unified();
          if (r < 0) {
                  log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
                  goto finish;
          if (r < 0)
                  goto finish;
  
 -        r = detect_unified_cgroup_hierarchy_from_environment();
 -        if (r < 0)
 -                goto finish;
 +        /* Reapply environment settings. */
 +        (void) detect_unified_cgroup_hierarchy_from_environment();
  
          /* Ignore SIGPIPE here, because we use splice() on the ptyfwd stuff and that will generate SIGPIPE if
           * the result is closed. Note that the container payload child will reset signal mask+handler anyway,
diff --combined src/shared/bootspec.c
index 22736098421982eacfa8efebc59b027af006a479,5221862a68cf5878ab82025b83b52a611bb5163b..4f750dc1da1b50c282576429529bd0024a3b2b63
@@@ -15,6 -15,7 +15,7 @@@
  #include "device-nodes.h"
  #include "dirent-util.h"
  #include "efivars.h"
+ #include "efi-loader.h"
  #include "env-file.h"
  #include "env-util.h"
  #include "fd-util.h"
@@@ -475,7 -476,6 +476,7 @@@ static int boot_entries_find_unified
                  _cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL;
                  _cleanup_close_ int fd = -1;
  
 +                dirent_ensure_type(d, de);
                  if (!dirent_is_file(de))
                          continue;
  
@@@ -877,7 -877,7 +878,7 @@@ static int verify_esp_blkid
          errno = 0;
          r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
          if (r != 0)
 -                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": m", node);
 +                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
          r = safe_atou32(v, &part);
          if (r < 0)
                  return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
index 0b0772c9724ecbbce7043a9ffbbd73eaf8a65d44,286ef8987854d4dbf6c92f09b13668490f279c42..0992ffac3a80e3414dd44eb165663afdfa823f80
@@@ -5,6 -5,7 +5,7 @@@
  #include "bus-unit-util.h"
  #include "bus-util.h"
  #include "cap-list.h"
+ #include "cgroup-setup.h"
  #include "cgroup-util.h"
  #include "condition.h"
  #include "cpu-set-util.h"
@@@ -435,22 -436,6 +436,22 @@@ static int bus_append_cgroup_property(s
  
                  return bus_append_cg_cpu_shares_parse(m, field, eq);
  
 +        if (STR_IN_SET(field, "AllowedCPUs", "AllowedMemoryNodes")) {
 +                _cleanup_(cpu_set_reset) CPUSet cpuset = {};
 +                _cleanup_free_ uint8_t *array = NULL;
 +                size_t allocated;
 +
 +                r = parse_cpu_set(eq, &cpuset);
 +                if (r < 0)
 +                        return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
 +
 +                r = cpu_set_to_dbus(&cpuset, &array, &allocated);
 +                if (r < 0)
 +                        return log_error_errno(r, "Failed to serialize CPUSet: %m");
 +
 +                return bus_append_byte_array(m, field, array, allocated);
 +        }
 +
          if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight"))
  
                  return bus_append_cg_blkio_weight_parse(m, field, eq);
@@@ -1355,12 -1340,15 +1356,12 @@@ static int bus_append_execute_property(
  static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
  
          if (streq(field, "KillMode"))
 -
                  return bus_append_string(m, field, eq);
  
          if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
 -
                  return bus_append_parse_boolean(m, field, eq);
  
 -        if (STR_IN_SET(field, "KillSignal", "FinalKillSignal", "WatchdogSignal"))
 -
 +        if (STR_IN_SET(field, "KillSignal", "RestartKillSignal", "FinalKillSignal", "WatchdogSignal"))
                  return bus_append_signal_from_string(m, field, eq);
  
          return 0;
@@@ -1447,9 -1435,8 +1448,9 @@@ static int bus_append_service_property(
  
          if (STR_IN_SET(field,
                         "ExecCondition", "ExecStartPre", "ExecStart", "ExecStartPost",
 -                       "ExecStartPreEx", "ExecStartEx", "ExecStartPostEx",
 -                       "ExecReload", "ExecStop", "ExecStopPost"))
 +                       "ExecConditionEx", "ExecStartPreEx", "ExecStartEx", "ExecStartPostEx",
 +                       "ExecReload", "ExecStop", "ExecStopPost",
 +                       "ExecReloadEx", "ExecStopEx", "ExecStopPostEx"))
                  return bus_append_exec_command(m, field, eq);
  
          if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
diff --combined src/shared/unit-file.c
index c7a6d6717244d63994e917db09a36442bff83ccd,9bcd5dc92e560ccddb8e4382b650242d15d7e0d7..b015ff93381d1fe6f0350a0269466743e2f40024
@@@ -6,6 -6,7 +6,7 @@@
  #include "macro.h"
  #include "path-lookup.h"
  #include "set.h"
+ #include "special.h"
  #include "stat-util.h"
  #include "string-util.h"
  #include "strv.h"
@@@ -279,7 -280,6 +280,7 @@@ int unit_file_build_name_map
                          if (hashmap_contains(ids, de->d_name))
                                  continue;
  
 +                        dirent_ensure_type(d, de);
                          if (de->d_type == DT_LNK) {
                                  /* We don't explicitly check for alias loops here. unit_ids_map_get() which
                                   * limits the number of hops should be used to access the map. */
@@@ -512,3 -512,47 +513,47 @@@ int unit_file_find_fragment
          // FIXME: if instance, consider any unit names with different template name
          return 0;
  }
+ static const char * const rlmap[] = {
+         "emergency", SPECIAL_EMERGENCY_TARGET,
+         "-b",        SPECIAL_EMERGENCY_TARGET,
+         "rescue",    SPECIAL_RESCUE_TARGET,
+         "single",    SPECIAL_RESCUE_TARGET,
+         "-s",        SPECIAL_RESCUE_TARGET,
+         "s",         SPECIAL_RESCUE_TARGET,
+         "S",         SPECIAL_RESCUE_TARGET,
+         "1",         SPECIAL_RESCUE_TARGET,
+         "2",         SPECIAL_MULTI_USER_TARGET,
+         "3",         SPECIAL_MULTI_USER_TARGET,
+         "4",         SPECIAL_MULTI_USER_TARGET,
+         "5",         SPECIAL_GRAPHICAL_TARGET,
+         NULL
+ };
+ static const char * const rlmap_initrd[] = {
+         "emergency", SPECIAL_EMERGENCY_TARGET,
+         "rescue",    SPECIAL_RESCUE_TARGET,
+         NULL
+ };
+ const char* runlevel_to_target(const char *word) {
+         const char * const *rlmap_ptr;
+         size_t i;
+         if (!word)
+                 return NULL;
+         if (in_initrd()) {
+                 word = startswith(word, "rd.");
+                 if (!word)
+                         return NULL;
+         }
+         rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
+         for (i = 0; rlmap_ptr[i]; i += 2)
+                 if (streq(word, rlmap_ptr[i]))
+                         return rlmap_ptr[i+1];
+         return NULL;
+ }