summaryrefslogtreecommitdiff
path: root/src/core/unit.c
diff options
context:
space:
mode:
authorAdrian Szyndela <adrian.s@samsung.com>2020-03-27 09:11:55 +0100
committerAdrian Szyndela <adrian.s@samsung.com>2020-03-27 09:11:55 +0100
commitb0d0a1cb41408251e852bce998aff876fda0b214 (patch)
tree7c0539331c52802a9609c708150e3d7fb4283dd7 /src/core/unit.c
parenta70b405fa3b2c4e93db2b3bd858d70b5ef559a1e (diff)
parent1e5d2d656420d0e755dbcf72aeba3c3aba54e956 (diff)
downloadsystemd-b0d0a1cb41408251e852bce998aff876fda0b214.tar.gz
systemd-b0d0a1cb41408251e852bce998aff876fda0b214.tar.bz2
systemd-b0d0a1cb41408251e852bce998aff876fda0b214.zip
Merge v242 into tizen
systemd v242
Diffstat (limited to 'src/core/unit.c')
-rw-r--r--src/core/unit.c225
1 files changed, 123 insertions, 102 deletions
diff --git a/src/core/unit.c b/src/core/unit.c
index a72a05d829..5b592be55c 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1590,66 +1590,43 @@ fail:
return log_unit_debug_errno(u, r, "Failed to load configuration: %m");
}
-static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) {
- Condition *c;
- int triggered = -1;
-
- assert(u);
- assert(to_string);
-
- /* If the condition list is empty, then it is true */
- if (!first)
- return true;
-
- /* Otherwise, if all of the non-trigger conditions apply and
- * if any of the trigger conditions apply (unless there are
- * none) we return true */
- LIST_FOREACH(conditions, c, first) {
- int r;
-
- r = condition_test(c);
- if (r < 0)
- log_unit_warning(u,
- "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
- to_string(c->type),
- c->trigger ? "|" : "",
- c->negate ? "!" : "",
- c->parameter);
- else
- log_unit_debug(u,
- "%s=%s%s%s %s.",
- to_string(c->type),
- c->trigger ? "|" : "",
- c->negate ? "!" : "",
- c->parameter,
- condition_result_to_string(c->result));
-
- if (!c->trigger && r <= 0)
- return false;
+_printf_(7, 8)
+static int log_unit_internal(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) {
+ Unit *u = userdata;
+ va_list ap;
+ int r;
- if (c->trigger && triggered <= 0)
- triggered = r > 0;
- }
+ va_start(ap, format);
+ if (u)
+ r = log_object_internalv(level, error, file, line, func,
+ u->manager->unit_log_field,
+ u->id,
+ u->manager->invocation_log_field,
+ u->invocation_id_string,
+ format, ap);
+ else
+ r = log_internalv(level, error, file, line, func, format, ap);
+ va_end(ap);
- return triggered != 0;
+ return r;
}
-static bool unit_condition_test(Unit *u) {
+static bool unit_test_condition(Unit *u) {
assert(u);
dual_timestamp_get(&u->condition_timestamp);
- u->condition_result = unit_condition_test_list(u, u->conditions, condition_type_to_string);
+ u->condition_result = condition_test_list(u->conditions, condition_type_to_string, log_unit_internal, u);
unit_add_to_dbus_queue(u);
return u->condition_result;
}
-static bool unit_assert_test(Unit *u) {
+static bool unit_test_assert(Unit *u) {
assert(u);
dual_timestamp_get(&u->assert_timestamp);
- u->assert_result = unit_condition_test_list(u, u->asserts, assert_type_to_string);
+ u->assert_result = condition_test_list(u->asserts, assert_type_to_string, log_unit_internal, u);
unit_add_to_dbus_queue(u);
@@ -1668,7 +1645,7 @@ void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg
REENABLE_WARNING;
}
-int unit_start_limit_test(Unit *u) {
+int unit_test_start_limit(Unit *u) {
const char *reason;
assert(u);
@@ -1683,9 +1660,11 @@ int unit_start_limit_test(Unit *u) {
reason = strjoina("unit ", u->id, " failed");
- return emergency_action(u->manager, u->start_limit_action,
- EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
- u->reboot_arg, -1, reason);
+ emergency_action(u->manager, u->start_limit_action,
+ EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
+ u->reboot_arg, -1, reason);
+
+ return -ECANCELED;
}
bool unit_shall_confirm_spawn(Unit *u) {
@@ -1726,27 +1705,31 @@ static bool unit_verify_deps(Unit *u) {
return true;
}
-/* Errors:
- * -EBADR: This unit type does not support starting.
+/* Errors that aren't really errors:
* -EALREADY: Unit is already started.
+ * -ECOMM: Condition failed
* -EAGAIN: An operation is already in progress. Retry later.
- * -ECANCELED: Too many requests for now.
+ *
+ * Errors that are real errors:
+ * -EBADR: This unit type does not support starting.
+ * -ECANCELED: Start limit hit, too many requests for now
* -EPROTO: Assert failed
* -EINVAL: Unit not loaded
* -EOPNOTSUPP: Unit type not supported
* -ENOLINK: The necessary dependencies are not fulfilled.
* -ESTALE: This unit has been started before and can't be started a second time
+ * -ENOENT: This is a triggering unit and unit to trigger is not loaded
*/
int unit_start(Unit *u) {
UnitActiveState state;
Unit *following;
+ int r;
assert(u);
- /* If this is already started, then this will succeed. Note
- * that this will even succeed if this unit is not startable
- * by the user. This is relied on to detect when we need to
- * wait for units and when waiting is finished. */
+ /* If this is already started, then this will succeed. Note that this will even succeed if this unit
+ * is not startable by the user. This is relied on to detect when we need to wait for units and when
+ * waiting is finished. */
state = unit_active_state(u);
if (UNIT_IS_ACTIVE_OR_RELOADING(state))
return -EALREADY;
@@ -1759,35 +1742,45 @@ int unit_start(Unit *u) {
if (UNIT_VTABLE(u)->once_only && dual_timestamp_is_set(&u->inactive_enter_timestamp))
return -ESTALE;
- /* If the conditions failed, don't do anything at all. If we
- * already are activating this call might still be useful to
- * speed up activation in case there is some hold-off time,
- * but we don't want to recheck the condition in that case. */
+ /* If the conditions failed, don't do anything at all. If we already are activating this call might
+ * still be useful to speed up activation in case there is some hold-off time, but we don't want to
+ * recheck the condition in that case. */
if (state != UNIT_ACTIVATING &&
- !unit_condition_test(u)) {
- log_unit_debug(u, "Starting requested but condition failed. Not starting unit.");
- return -ECOMM;
+ !unit_test_condition(u)) {
+
+ /* Let's also check the start limit here. Normally, the start limit is only checked by the
+ * .start() method of the unit type after it did some additional checks verifying everything
+ * is in order (so that those other checks can propagate errors properly). However, if a
+ * condition check doesn't hold we don't get that far but we should still ensure we are not
+ * called in a tight loop without a rate limit check enforced, hence do the check here. Note
+ * that ECOMM is generally not a reason for a job to fail, unlike most other errors here,
+ * hence the chance is big that any triggering unit for us will trigger us again. Note this
+ * condition check is a bit different from the condition check inside the per-unit .start()
+ * function, as this one will not change the unit's state in any way (and we shouldn't here,
+ * after all the condition failed). */
+
+ r = unit_test_start_limit(u);
+ if (r < 0)
+ return r;
+
+ return log_unit_debug_errno(u, SYNTHETIC_ERRNO(ECOMM), "Starting requested but condition failed. Not starting unit.");
}
/* If the asserts failed, fail the entire job */
if (state != UNIT_ACTIVATING &&
- !unit_assert_test(u)) {
- log_unit_notice(u, "Starting requested but asserts failed.");
- return -EPROTO;
- }
+ !unit_test_assert(u))
+ return log_unit_notice_errno(u, SYNTHETIC_ERRNO(EPROTO), "Starting requested but asserts failed.");
- /* Units of types that aren't supported cannot be
- * started. Note that we do this test only after the condition
- * checks, so that we rather return condition check errors
- * (which are usually not considered a true failure) than "not
- * supported" errors (which are considered a failure).
+ /* Units of types that aren't supported cannot be started. Note that we do this test only after the
+ * condition checks, so that we rather return condition check errors (which are usually not
+ * considered a true failure) than "not supported" errors (which are considered a failure).
*/
if (!unit_supported(u))
return -EOPNOTSUPP;
- /* Let's make sure that the deps really are in order before we start this. Normally the job engine should have
- * taken care of this already, but let's check this here again. After all, our dependencies might not be in
- * effect anymore, due to a reload or due to a failed condition. */
+ /* Let's make sure that the deps really are in order before we start this. Normally the job engine
+ * should have taken care of this already, but let's check this here again. After all, our
+ * dependencies might not be in effect anymore, due to a reload or due to a failed condition. */
if (!unit_verify_deps(u))
return -ENOLINK;
@@ -1802,11 +1795,9 @@ int unit_start(Unit *u) {
if (!UNIT_VTABLE(u)->start)
return -EBADR;
- /* 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
- * restart" state where it waits for a holdoff timer to elapse
- * before it will start again. */
+ /* 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 restart" state where it
+ * waits for a holdoff timer to elapse before it will start again. */
unit_add_to_dbus_queue(u);
@@ -1896,7 +1887,7 @@ int unit_reload(Unit *u) {
state = unit_active_state(u);
if (state == UNIT_RELOADING)
- return -EALREADY;
+ return -EAGAIN;
if (state != UNIT_ACTIVE) {
log_unit_warning(u, "Unit cannot be reloaded because it is inactive.");
@@ -2045,7 +2036,7 @@ static void unit_check_binds_to(Unit *u) {
log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id);
/* A unit we need to run is gone. Sniff. Let's stop this. */
- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, NULL, &error, NULL);
if (r < 0)
log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
}
@@ -2061,25 +2052,25 @@ static void retroactively_start_dependencies(Unit *u) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i)
if (!hashmap_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL);
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i)
if (!hashmap_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL);
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i)
if (!hashmap_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL, NULL);
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL);
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTED_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL);
}
static void retroactively_stop_dependencies(Unit *u) {
@@ -2093,7 +2084,7 @@ static void retroactively_stop_dependencies(Unit *u) {
/* Pull down units which are bound to us recursively if enabled */
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BOUND_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL);
}
void unit_start_on_failure(Unit *u) {
@@ -2112,7 +2103,7 @@ void unit_start_on_failure(Unit *u) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_ON_FAILURE], i) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, &error, NULL);
+ r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, &error, NULL);
if (r < 0)
log_unit_warning_errno(u, r, "Failed to enqueue OnFailure= job, ignoring: %s", bus_error_message(&error, r));
}
@@ -2502,17 +2493,17 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
if (os != UNIT_FAILED && ns == UNIT_FAILED) {
reason = strjoina("unit ", u->id, " failed");
- (void) emergency_action(m, u->failure_action, 0, u->reboot_arg, unit_failure_action_exit_status(u), reason);
+ emergency_action(m, u->failure_action, 0, u->reboot_arg, unit_failure_action_exit_status(u), reason);
} else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE) {
reason = strjoina("unit ", u->id, " succeeded");
- (void) emergency_action(m, u->success_action, 0, u->reboot_arg, unit_success_action_exit_status(u), reason);
+ emergency_action(m, u->success_action, 0, u->reboot_arg, unit_success_action_exit_status(u), reason);
}
}
unit_add_to_gc_queue(u);
}
-int unit_watch_pid(Unit *u, pid_t pid) {
+int unit_watch_pid(Unit *u, pid_t pid, bool exclusive) {
int r;
assert(u);
@@ -2520,6 +2511,12 @@ int unit_watch_pid(Unit *u, pid_t pid) {
/* Watch a specific PID */
+ /* Caller might be sure that this PID belongs to this unit only. Let's take this
+ * opportunity to remove any stalled references to this PID as they can be created
+ * easily (when watching a process which is not our direct child). */
+ if (exclusive)
+ manager_unwatch_pid(u->manager, pid);
+
r = set_ensure_allocated(&u->pids, NULL);
if (r < 0)
return r;
@@ -3199,7 +3196,7 @@ static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
return serialize_item(f, key, s);
}
-static const char *ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
+static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
[CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
[CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes",
@@ -4092,14 +4089,20 @@ int unit_patch_contexts(Unit *u) {
return -ENOMEM;
}
- /* If the dynamic user option is on, let's make sure that the unit can't leave its UID/GID
- * around in the file system or on IPC objects. Hence enforce a strict sandbox. */
+ /* If the dynamic user option is on, let's make sure that the unit can't leave its
+ * UID/GID around in the file system or on IPC objects. Hence enforce a strict
+ * sandbox. */
ec->private_tmp = true;
ec->remove_ipc = true;
ec->protect_system = PROTECT_SYSTEM_STRICT;
if (ec->protect_home == PROTECT_HOME_NO)
ec->protect_home = PROTECT_HOME_READ_ONLY;
+
+ /* Make sure this service can neither benefit from SUID/SGID binaries nor create
+ * them. */
+ ec->no_new_privileges = true;
+ ec->restrict_suid_sgid = true;
}
}
@@ -4429,7 +4432,7 @@ int unit_make_transient(Unit *u) {
return 0;
}
-static void log_kill(pid_t pid, int sig, void *userdata) {
+static int log_kill(pid_t pid, int sig, void *userdata) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(pid, &comm);
@@ -4437,13 +4440,15 @@ static void log_kill(pid_t pid, int sig, void *userdata) {
/* Don't log about processes marked with brackets, under the assumption that these are temporary processes
only, like for example systemd's own PAM stub process. */
if (comm && comm[0] == '(')
- return;
+ return 0;
log_unit_notice(userdata,
"Killing process " PID_FMT " (%s) with signal SIG%s.",
pid,
strna(comm),
signal_to_string(sig));
+
+ return 1;
}
static int operation_to_signal(KillContext *c, KillOperation k) {
@@ -4608,7 +4613,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
if (!p)
return -ENOMEM;
- path = path_simplify(p, false);
+ path = path_simplify(p, true);
if (!path_is_normalized(path))
return -EPERM;
@@ -5395,29 +5400,31 @@ int unit_prepare_exec(Unit *u) {
return 0;
}
-static void log_leftover(pid_t pid, int sig, void *userdata) {
+static int log_leftover(pid_t pid, int sig, void *userdata) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(pid, &comm);
if (comm && comm[0] == '(') /* Most likely our own helper process (PAM?), ignore */
- return;
+ return 0;
log_unit_warning(userdata,
"Found left-over process " PID_FMT " (%s) in control group while starting unit. Ignoring.\n"
"This usually indicates unclean termination of a previous run, or service implementation deficiencies.",
pid, strna(comm));
+
+ return 1;
}
-void unit_warn_leftover_processes(Unit *u) {
+int unit_warn_leftover_processes(Unit *u) {
assert(u);
(void) unit_pick_cgroup_path(u);
if (!u->cgroup_path)
- return;
+ return 0;
- (void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u);
+ return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u);
}
bool unit_needs_console(Unit *u) {
@@ -5586,6 +5593,20 @@ int unit_success_action_exit_status(Unit *u) {
return r;
}
+int unit_test_trigger_loaded(Unit *u) {
+ Unit *trigger;
+
+ /* Tests whether the unit to trigger is loaded */
+
+ trigger = UNIT_TRIGGER(u);
+ if (!trigger)
+ return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit to trigger not loaded.");
+ if (trigger->load_state != UNIT_LOADED)
+ return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit %s to trigger not loaded.", u->id);
+
+ return 0;
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",