]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #2921 from keszybz/do-not-report-masked-units-as-changed
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 3 May 2016 18:08:39 +0000 (14:08 -0400)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 3 May 2016 18:08:39 +0000 (14:08 -0400)
1  2 
src/core/load-fragment.c
src/core/unit.c

diff --combined src/core/load-fragment.c
index 67867dfbe99702417b933eb33a4cf26f97172dc1,f1a874cfdfbfe9e39180f54e2e46bb5063aa16b8..1a8c03904cd1d799bca119b3ebf4d36ecd3d0c90
@@@ -2495,7 -2495,7 +2495,7 @@@ int config_parse_syscall_filter
  
          /* Turn on NNP, but only if it wasn't configured explicitly
           * before, and only if we are in user mode. */
 -        if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER)
 +        if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager))
                  c->no_new_privileges = true;
  
          return 0;
@@@ -2847,12 -2847,11 +2847,12 @@@ int config_parse_device_allow
                  void *data,
                  void *userdata) {
  
 -        _cleanup_free_ char *path = NULL;
 +        _cleanup_free_ char *path = NULL, *t = NULL;
          CGroupContext *c = data;
          CGroupDeviceAllow *a;
 -        const char *m;
 +        const char *m = NULL;
          size_t n;
 +        int r;
  
          if (isempty(rvalue)) {
                  while (c->device_allow)
                  return 0;
          }
  
 -        n = strcspn(rvalue, WHITESPACE);
 -        path = strndup(rvalue, n);
 +        r = unit_full_printf(userdata, rvalue, &t);
 +        if(r < 0) {
 +                log_syntax(unit, LOG_WARNING, filename, line, r,
 +                           "Failed to resolve specifiers in %s, ignoring: %m",
 +                           rvalue);
 +        }
 +
 +        n = strcspn(t, WHITESPACE);
 +
 +        path = strndup(t, n);
          if (!path)
                  return log_oom();
  
                  return 0;
          }
  
 -        m = rvalue + n + strspn(rvalue + n, WHITESPACE);
 +        m = t + n + strspn(t + n, WHITESPACE);
          if (isempty(m))
                  m = "rwm";
  
@@@ -3427,10 -3418,10 +3427,10 @@@ int config_parse_protect_system
  #define FOLLOW_MAX 8
  
  static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
 +        char *id = NULL;
          unsigned c = 0;
          int fd, r;
          FILE *f;
 -        char *id = NULL;
  
          assert(filename);
          assert(*filename);
                   * the names of this unit, but only if it is a valid
                   * unit name. */
                  name = basename(*filename);
 -
                  if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
  
                          id = set_get(names, name);
  
          *_f = f;
          *_final = id;
 +
          return 0;
  }
  
@@@ -3552,13 -3543,13 +3552,13 @@@ static int merge_by_names(Unit **u, Se
  }
  
  static int load_from_path(Unit *u, const char *path) {
 -        int r;
          _cleanup_set_free_free_ Set *symlink_names = NULL;
          _cleanup_fclose_ FILE *f = NULL;
          _cleanup_free_ char *filename = NULL;
          char *id = NULL;
          Unit *merged;
          struct stat st;
 +        int r;
  
          assert(u);
          assert(path);
          } else  {
                  char **p;
  
 -                STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
 +                STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
  
                          /* Instead of opening the path right away, we manually
                           * follow all symlinks and add their name to our unit
                                  r = -ENOENT;
                          else
                                  r = open_follow(&filename, &f, symlink_names, &id);
 +                        if (r >= 0)
 +                                break;
 +                        filename = mfree(filename);
 +                        if (r != -ENOENT)
 +                                return r;
  
 -                        if (r < 0) {
 -                                filename = mfree(filename);
 -                                if (r != -ENOENT)
 -                                        return r;
 -
 -                                /* Empty the symlink names for the next run */
 -                                set_clear_free(symlink_names);
 -                                continue;
 -                        }
 -
 -                        break;
 +                        /* Empty the symlink names for the next run */
 +                        set_clear_free(symlink_names);
                  }
          }
  
                  /* Hmm, no suitable file found? */
                  return 0;
  
 +        if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
 +                log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
 +                return -ELOOP;
 +        }
 +
          merged = u;
          r = merge_by_names(&merged, symlink_names, id);
          if (r < 0)
          if (fstat(fileno(f), &st) < 0)
                  return -errno;
  
-         if (null_or_empty(&st))
+         if (null_or_empty(&st)) {
                  u->load_state = UNIT_MASKED;
-         else {
+                 u->fragment_mtime = 0;
+         } else {
                  u->load_state = UNIT_LOADED;
+                 u->fragment_mtime = timespec_load(&st.st_mtim);
  
                  /* Now, parse the file contents */
                  r = config_parse(u->id, filename, f,
          u->fragment_path = filename;
          filename = NULL;
  
-         u->fragment_mtime = timespec_load(&st.st_mtim);
          if (u->source_path) {
                  if (stat(u->source_path, &st) >= 0)
                          u->source_mtime = timespec_load(&st.st_mtim);
diff --combined src/core/unit.c
index 5b8b0130bddf583b9b231a42e9f8c8c2fb30c673,70175557f74db5aa1781a3d606aa1c38909b5211..4ace6b075ba48afc871266987e09f9d83b07cfcd
  #include "path-util.h"
  #include "process-util.h"
  #include "set.h"
 +#include "signal-util.h"
  #include "special.h"
  #include "stat-util.h"
  #include "stdio-util.h"
  #include "string-util.h"
  #include "strv.h"
 +#include "umask-util.h"
  #include "unit-name.h"
  #include "unit.h"
  #include "user-util.h"
@@@ -193,7 -191,7 +193,7 @@@ int unit_add_name(Unit *u, const char *
          if (r < 0)
                  return r;
  
 -        if (i && unit_vtable[t]->no_instances)
 +        if (i && !unit_type_may_template(t))
                  return -EINVAL;
  
          /* Ensure that this unit is either instanced or not instanced,
          if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
                  return -EINVAL;
  
 -        if (unit_vtable[t]->no_alias && !set_isempty(u->names))
 +        if (!unit_type_may_alias(t) && !set_isempty(u->names))
                  return -EEXIST;
  
          if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
@@@ -420,22 -418,13 +420,22 @@@ static void unit_remove_transient(Unit 
                  (void) unlink(u->fragment_path);
  
          STRV_FOREACH(i, u->dropin_paths) {
 -                _cleanup_free_ char *p = NULL;
 +                _cleanup_free_ char *p = NULL, *pp = NULL;
  
 -                (void) unlink(*i);
 +                p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */
 +                if (!p)
 +                        continue;
 +
 +                pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */
 +                if (!pp)
 +                        continue;
 +
 +                /* Only drop transient drop-ins */
 +                if (!path_equal(u->manager->lookup_paths.transient, pp))
 +                        continue;
  
 -                p = dirname_malloc(*i);
 -                if (p)
 -                        (void) rmdir(p);
 +                (void) unlink(*i);
 +                (void) rmdir(p);
          }
  }
  
@@@ -494,10 -483,7 +494,10 @@@ void unit_free(Unit *u) 
  
          assert(u);
  
 -        if (u->manager->n_reloading <= 0)
 +        if (u->transient_file)
 +                fclose(u->transient_file);
 +
 +        if (!MANAGER_IS_RELOADING(u->manager))
                  unit_remove_transient(u);
  
          bus_unit_send_removed_signal(u);
@@@ -720,9 -706,6 +720,9 @@@ int unit_merge(Unit *u, Unit *other) 
          if (!u->instance != !other->instance)
                  return -EINVAL;
  
 +        if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */
 +                return -EEXIST;
 +
          if (other->load_state != UNIT_STUB &&
              other->load_state != UNIT_NOT_FOUND)
                  return -EEXIST;
  }
  
  int unit_merge_by_name(Unit *u, const char *name) {
 +        _cleanup_free_ char *s = NULL;
          Unit *other;
          int r;
 -        _cleanup_free_ char *s = NULL;
  
          assert(u);
          assert(name);
@@@ -831,7 -814,7 +831,7 @@@ int unit_add_exec_dependencies(Unit *u
                          return r;
          }
  
 -        if (u->manager->running_as != MANAGER_SYSTEM)
 +        if (!MANAGER_IS_SYSTEM(u->manager))
                  return 0;
  
          if (c->private_tmp) {
@@@ -1239,17 -1222,6 +1239,17 @@@ int unit_load(Unit *u) 
          if (u->load_state != UNIT_STUB)
                  return 0;
  
 +        if (u->transient_file) {
 +                r = fflush_and_check(u->transient_file);
 +                if (r < 0)
 +                        goto fail;
 +
 +                fclose(u->transient_file);
 +                u->transient_file = NULL;
 +
 +                u->dropin_mtime = now(CLOCK_REALTIME);
 +        }
 +
          if (UNIT_VTABLE(u)->load) {
                  r = UNIT_VTABLE(u)->load(u);
                  if (r < 0)
@@@ -1500,6 -1472,11 +1500,6 @@@ int unit_start(Unit *u) 
          if (UNIT_IS_ACTIVE_OR_RELOADING(state))
                  return -EALREADY;
  
 -        /* Make sure we don't enter a busy loop of some kind. */
 -        r = unit_start_limit_test(u);
 -        if (r < 0)
 -                return r;
 -
          /* Units that aren't loaded cannot be started */
          if (u->load_state != UNIT_LOADED)
                  return -EINVAL;
          if (!UNIT_VTABLE(u)->start)
                  return -EBADR;
  
 +        /* Make sure we don't enter a busy loop of some kind. */
 +        r = unit_start_limit_test(u);
 +        if (r < 0)
 +                return r;
 +
          /* We don't suppress calls to ->start() here when we are
           * already starting, to allow this request to be used as a
           * "hurry up" call, for example when the unit is in some "auto
@@@ -1862,7 -1834,7 +1862,7 @@@ void unit_notify(Unit *u, UnitActiveSta
          m = u->manager;
  
          /* Update timestamps for state changes */
 -        if (m->n_reloading <= 0) {
 +        if (!MANAGER_IS_RELOADING(m)) {
                  dual_timestamp_get(&u->state_change_timestamp);
  
                  if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
          } else
                  unexpected = true;
  
 -        if (m->n_reloading <= 0) {
 +        if (!MANAGER_IS_RELOADING(m)) {
  
                  /* If this state change happened without being
                   * requested by a job, then let's retroactively start
  
                  if (u->type == UNIT_SERVICE &&
                      !UNIT_IS_ACTIVE_OR_RELOADING(os) &&
 -                    m->n_reloading <= 0) {
 +                    !MANAGER_IS_RELOADING(m)) {
                          /* Write audit record if we have just finished starting up */
                          manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
                          u->in_audit = true;
                  if (u->type == UNIT_SERVICE &&
                      UNIT_IS_INACTIVE_OR_FAILED(ns) &&
                      !UNIT_IS_INACTIVE_OR_FAILED(os) &&
 -                    m->n_reloading <= 0) {
 +                    !MANAGER_IS_RELOADING(m)) {
  
                          /* Hmm, if there was no start record written
                           * write it now, so that we always have a nice
          manager_recheck_journal(m);
          unit_trigger_notify(u);
  
 -        if (u->manager->n_reloading <= 0) {
 +        if (!MANAGER_IS_RELOADING(u->manager)) {
                  /* Maybe we finished startup and are now ready for
                   * being stopped because unneeded? */
                  unit_check_unneeded(u);
@@@ -2441,7 -2413,7 +2441,7 @@@ int unit_set_default_slice(Unit *u) 
                  if (!escaped)
                          return -ENOMEM;
  
 -                if (u->manager->running_as == MANAGER_SYSTEM)
 +                if (MANAGER_IS_SYSTEM(u->manager))
                          b = strjoin("system-", escaped, ".slice", NULL);
                  else
                          b = strappend(escaped, ".slice");
                  slice_name = b;
          } else
                  slice_name =
 -                        u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE)
 +                        MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE)
                          ? SPECIAL_SYSTEM_SLICE
                          : SPECIAL_ROOT_SLICE;
  
@@@ -2521,11 -2493,12 +2521,11 @@@ int unit_install_bus_match(Unit *u, sd_
                  return -EBUSY;
  
          match = strjoina("type='signal',"
 -                        "sender='org.freedesktop.DBus',"
 -                        "path='/org/freedesktop/DBus',"
 -                        "interface='org.freedesktop.DBus',"
 -                        "member='NameOwnerChanged',"
 -                        "arg0='", name, "'",
 -                        NULL);
 +                         "sender='org.freedesktop.DBus',"
 +                         "path='/org/freedesktop/DBus',"
 +                         "interface='org.freedesktop.DBus',"
 +                         "member='NameOwnerChanged',"
 +                         "arg0='", name, "'");
  
          return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
  }
@@@ -2911,7 -2884,7 +2911,7 @@@ int unit_add_node_link(Unit *u, const c
                  return r;
  
          r = unit_add_two_dependencies(u, UNIT_AFTER,
 -                                      u->manager->running_as == MANAGER_SYSTEM ? dep : UNIT_WANTS,
 +                                      MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS,
                                        device, true);
          if (r < 0)
                  return r;
@@@ -2951,34 -2924,36 +2951,36 @@@ int unit_coldplug(Unit *u) 
          return 0;
  }
  
+ static bool fragment_mtime_changed(const char *path, usec_t mtime) {
+         struct stat st;
+         if (!path)
+                 return false;
+         if (stat(path, &st) < 0)
+                 /* What, cannot access this anymore? */
+                 return true;
+         if (mtime > 0)
+                 /* For non-empty files check the mtime */
+                 return timespec_load(&st.st_mtim) != mtime;
+         else if (!null_or_empty(&st))
+                 /* For masked files check if they are still so */
+                 return true;
+         return false;
+ }
  bool unit_need_daemon_reload(Unit *u) {
          _cleanup_strv_free_ char **t = NULL;
          char **path;
-         struct stat st;
          unsigned loaded_cnt, current_cnt;
  
          assert(u);
  
-         if (u->fragment_path) {
-                 zero(st);
-                 if (stat(u->fragment_path, &st) < 0)
-                         /* What, cannot access this anymore? */
-                         return true;
-                 if (u->fragment_mtime > 0 &&
-                     timespec_load(&st.st_mtim) != u->fragment_mtime)
-                         return true;
-         }
-         if (u->source_path) {
-                 zero(st);
-                 if (stat(u->source_path, &st) < 0)
-                         return true;
-                 if (u->source_mtime > 0 &&
-                     timespec_load(&st.st_mtim) != u->source_mtime)
-                         return true;
-         }
+         if (fragment_mtime_changed(u->fragment_path, u->fragment_mtime) ||
+             fragment_mtime_changed(u->source_path, u->source_mtime))
+                 return true;
  
          (void) unit_find_dropin_paths(u, &t);
          loaded_cnt = strv_length(t);
                          return false;
  
                  if (strv_overlap(u->dropin_paths, t)) {
-                         STRV_FOREACH(path, u->dropin_paths) {
-                                 zero(st);
-                                 if (stat(*path, &st) < 0)
-                                         return true;
-                                 if (u->dropin_mtime > 0 &&
-                                     timespec_load(&st.st_mtim) > u->dropin_mtime)
+                         STRV_FOREACH(path, u->dropin_paths)
+                                 if (fragment_mtime_changed(*path, u->dropin_mtime))
                                          return true;
-                         }
  
                          return false;
-                 } else
-                         return true;
-         } else
-                 return true;
+                 }
+         }
+         return true;
  }
  
  void unit_reset_failed(Unit *u) {
@@@ -3071,7 -3040,8 +3067,7 @@@ bool unit_active_or_pending(Unit *u) 
  int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
          assert(u);
          assert(w >= 0 && w < _KILL_WHO_MAX);
 -        assert(signo > 0);
 -        assert(signo < _NSIG);
 +        assert(SIGNAL_VALID(signo));
  
          if (!UNIT_VTABLE(u)->kill)
                  return -EOPNOTSUPP;
@@@ -3188,7 -3158,7 +3184,7 @@@ UnitFileState unit_get_unit_file_state(
  
          if (u->unit_file_state < 0 && u->fragment_path) {
                  r = unit_file_get_state(
 -                                u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
 +                                u->manager->unit_file_scope,
                                  NULL,
                                  basename(u->fragment_path),
                                  &u->unit_file_state);
@@@ -3204,7 -3174,7 +3200,7 @@@ int unit_get_unit_file_preset(Unit *u) 
  
          if (u->unit_file_preset < 0 && u->fragment_path)
                  u->unit_file_preset = unit_file_query_preset(
 -                                u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
 +                                u->manager->unit_file_scope,
                                  NULL,
                                  basename(u->fragment_path));
  
@@@ -3229,10 -3199,6 +3225,10 @@@ void unit_ref_unset(UnitRef *ref) 
          if (!ref->unit)
                  return;
  
 +        /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might
 +         * be unreferenced now. */
 +        unit_add_to_gc_queue(ref->unit);
 +
          LIST_REMOVE(refs, ref->unit->refs, ref);
          ref->unit = NULL;
  }
@@@ -3259,7 -3225,7 +3255,7 @@@ int unit_patch_contexts(Unit *u) 
                                          return -ENOMEM;
                          }
  
 -                if (u->manager->running_as == MANAGER_USER &&
 +                if (MANAGER_IS_USER(u->manager) &&
                      !ec->working_directory) {
  
                          r = get_home_dir(&ec->working_directory);
                          ec->working_directory_missing_ok = true;
                  }
  
 -                if (u->manager->running_as == MANAGER_USER &&
 +                if (MANAGER_IS_USER(u->manager) &&
                      (ec->syscall_whitelist ||
                       !set_isempty(ec->syscall_filter) ||
                       !set_isempty(ec->syscall_archs) ||
@@@ -3349,62 -3315,59 +3345,62 @@@ ExecRuntime *unit_get_exec_runtime(Uni
          return *(ExecRuntime**) ((uint8_t*) u + offset);
  }
  
 -static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
 +static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) {
          assert(u);
  
 -        if (u->manager->running_as == MANAGER_USER) {
 -                int r;
 +        if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT))
 +                return NULL;
  
 -                if (mode == UNIT_PERSISTENT && !transient)
 -                        r = user_config_home(dir);
 -                else
 -                        r = user_runtime_dir(dir);
 -                if (r == 0)
 -                        return -ENOENT;
 +        if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */
 +                return u->manager->lookup_paths.transient;
  
 -                return r;
 -        }
 +        if (mode == UNIT_RUNTIME)
 +                return u->manager->lookup_paths.runtime_control;
  
 -        if (mode == UNIT_PERSISTENT && !transient)
 -                *dir = strdup("/etc/systemd/system");
 -        else
 -                *dir = strdup("/run/systemd/system");
 -        if (!*dir)
 -                return -ENOMEM;
 +        if (mode == UNIT_PERSISTENT)
 +                return u->manager->lookup_paths.persistent_control;
  
 -        return 0;
 +        return NULL;
  }
  
  int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
 -
 -        _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
 +        _cleanup_free_ char *p = NULL, *q = NULL;
 +        const char *dir, *prefixed;
          int r;
  
          assert(u);
  
 +        if (u->transient_file) {
 +                /* When this is a transient unit file in creation, then let's not create a new drop-in but instead
 +                 * write to the transient unit file. */
 +                fputs(data, u->transient_file);
 +                return 0;
 +        }
 +
          if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
                  return 0;
  
 -        r = unit_drop_in_dir(u, mode, u->transient, &dir);
 -        if (r < 0)
 -                return r;
 +        dir = unit_drop_in_dir(u, mode);
 +        if (!dir)
 +                return -EINVAL;
  
 -        r = write_drop_in(dir, u->id, 50, name, data);
 +        prefixed = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\" or an equivalent operation. Do not edit.\n",
 +                            data);
 +
 +        r = drop_in_file(dir, u->id, 50, name, &p, &q);
          if (r < 0)
                  return r;
  
 -        r = drop_in_file(dir, u->id, 50, name, &p, &q);
 +        (void) mkdir_p(p, 0755);
 +        r = write_string_file_atomic_label(q, prefixed);
          if (r < 0)
                  return r;
  
 -        r = strv_extend(&u->dropin_paths, q);
 +        r = strv_push(&u->dropin_paths, q);
          if (r < 0)
                  return r;
 +        q = NULL;
  
 -        strv_sort(u->dropin_paths);
          strv_uniq(u->dropin_paths);
  
          u->dropin_mtime = now(CLOCK_REALTIME);
@@@ -3435,7 -3398,7 +3431,7 @@@ int unit_write_drop_in_format(Unit *u, 
  }
  
  int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
 -        _cleanup_free_ char *ndata = NULL;
 +        const char *ndata;
  
          assert(u);
          assert(name);
          if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
                  return 0;
  
 -        ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL);
 -        if (!ndata)
 -                return -ENOMEM;
 +        ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data);
  
          return unit_write_drop_in(u, mode, name, ndata);
  }
@@@ -3475,51 -3440,24 +3471,51 @@@ int unit_write_drop_in_private_format(U
  }
  
  int unit_make_transient(Unit *u) {
 +        FILE *f;
 +        char *path;
 +
          assert(u);
  
          if (!UNIT_VTABLE(u)->can_transient)
                  return -EOPNOTSUPP;
  
 -        u->load_state = UNIT_STUB;
 -        u->load_error = 0;
 -        u->transient = true;
 +        path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL);
 +        if (!path)
 +                return -ENOMEM;
 +
 +        /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are
 +         * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */
 +
 +        RUN_WITH_UMASK(0022) {
 +                f = fopen(path, "we");
 +                if (!f) {
 +                        free(path);
 +                        return -errno;
 +                }
 +        }
 +
 +        if (u->transient_file)
 +                fclose(u->transient_file);
 +        u->transient_file = f;
 +
 +        free(u->fragment_path);
 +        u->fragment_path = path;
  
 -        u->fragment_path = mfree(u->fragment_path);
          u->source_path = mfree(u->source_path);
          u->dropin_paths = strv_free(u->dropin_paths);
          u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0;
  
 +        u->load_state = UNIT_STUB;
 +        u->load_error = 0;
 +        u->transient = true;
 +
          unit_add_to_dbus_queue(u);
          unit_add_to_gc_queue(u);
          unit_add_to_load_queue(u);
  
 +        fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n",
 +              u->transient_file);
 +
          return 0;
  }
  
@@@ -3611,7 -3549,7 +3607,7 @@@ int unit_kill_context
                           * cases. It doesn't work at all in
                           * containers, and outside of containers it
                           * can be confused easily by left-over
 -                         * directories in the cgroup -- which however
 +                         * directories in the cgroup  which however
                           * should not exist in non-delegated units. On
                           * the unified hierarchy that's different,
                           * there we get proper events. Hence rely on
@@@ -3812,21 -3750,3 +3808,21 @@@ bool unit_is_pristine(Unit *u) 
                   u->job ||
                   u->merged_into);
  }
 +
 +pid_t unit_control_pid(Unit *u) {
 +        assert(u);
 +
 +        if (UNIT_VTABLE(u)->control_pid)
 +                return UNIT_VTABLE(u)->control_pid(u);
 +
 +        return 0;
 +}
 +
 +pid_t unit_main_pid(Unit *u) {
 +        assert(u);
 +
 +        if (UNIT_VTABLE(u)->main_pid)
 +                return UNIT_VTABLE(u)->main_pid(u);
 +
 +        return 0;
 +}