summaryrefslogtreecommitdiff
path: root/src/core/unit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/unit.c')
-rw-r--r--src/core/unit.c218
1 files changed, 147 insertions, 71 deletions
diff --git a/src/core/unit.c b/src/core/unit.c
index 70175557f7..4ace6b075b 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -47,11 +47,13 @@
#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"
@@ -191,7 +193,7 @@ int unit_add_name(Unit *u, const char *text) {
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,
@@ -200,7 +202,7 @@ int unit_add_name(Unit *u, const char *text) {
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)
@@ -418,13 +420,22 @@ static void unit_remove_transient(Unit *u) {
(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);
}
}
@@ -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);
@@ -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;
@@ -762,9 +779,9 @@ int unit_merge(Unit *u, Unit *other) {
}
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);
@@ -814,7 +831,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
- if (u->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(u->manager))
return 0;
if (c->private_tmp) {
@@ -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)
@@ -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;
@@ -1518,6 +1541,11 @@ int unit_start(Unit *u) {
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
@@ -1834,7 +1862,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
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))
@@ -1941,7 +1969,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
} 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
@@ -1978,7 +2006,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
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;
@@ -1995,7 +2023,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
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
@@ -2016,7 +2044,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
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);
@@ -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");
@@ -2423,7 +2451,7 @@ int unit_set_default_slice(Unit *u) {
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;
@@ -2493,12 +2521,11 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
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);
}
@@ -2884,7 +2911,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
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;
@@ -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;
@@ -3158,7 +3184,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
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);
@@ -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));
@@ -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;
}
@@ -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);
@@ -3237,7 +3267,7 @@ int unit_patch_contexts(Unit *u) {
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) ||
@@ -3315,59 +3345,62 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
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);
@@ -3398,7 +3431,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n
}
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);
@@ -3410,9 +3443,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *
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);
}
@@ -3440,24 +3471,51 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const
}
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;
}
@@ -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
@@ -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;
+}