chiark / gitweb /
core: rework device state logic
[elogind.git] / src / core / unit.c
index 88cddbd02355b2c0e3bc4d7a9ba328060b3d4cc1..7cd704351cb0e30300cf37436fed9c0c49156cd7 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <assert.h>
 #include <errno.h>
 #include <string.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <sys/poll.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/stat.h>
 #include "cgroup-util.h"
 #include "missing.h"
 #include "mkdir.h"
-#include "label.h"
 #include "fileio-label.h"
-#include "bus-errors.h"
+#include "bus-common-errors.h"
 #include "dbus.h"
 #include "execute.h"
-#include "virt.h"
 #include "dropin.h"
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
@@ -92,6 +86,7 @@ Unit *unit_new(Manager *m, size_t size) {
         u->deserialized_job = _JOB_TYPE_INVALID;
         u->default_dependencies = true;
         u->unit_file_state = _UNIT_FILE_STATE_INVALID;
+        u->unit_file_preset = -1;
         u->on_failure_job_mode = JOB_REPLACE;
 
         return u;
@@ -277,21 +272,32 @@ int unit_set_description(Unit *u, const char *description) {
 }
 
 bool unit_check_gc(Unit *u) {
+        UnitActiveState state;
         assert(u);
 
-        if (UNIT_VTABLE(u)->no_gc)
+        if (u->job)
                 return true;
 
-        if (u->no_gc)
+        if (u->nop_job)
                 return true;
 
-        if (u->job)
+        state = unit_active_state(u);
+
+        /* If the unit is inactive and failed and no job is queued for
+         * it, then release its runtime resources */
+        if (UNIT_IS_INACTIVE_OR_FAILED(state) &&
+            UNIT_VTABLE(u)->release_resources)
+                UNIT_VTABLE(u)->release_resources(u);
+
+        /* But we keep the unit object around for longer when it is
+         * referenced or configured to not be gc'ed */
+        if (state != UNIT_INACTIVE)
                 return true;
 
-        if (u->nop_job)
+        if (UNIT_VTABLE(u)->no_gc)
                 return true;
 
-        if (unit_active_state(u) != UNIT_INACTIVE)
+        if (u->no_gc)
                 return true;
 
         if (u->refs)
@@ -510,7 +516,7 @@ void unit_free(Unit *u) {
                 free(u->cgroup_path);
         }
 
-        set_remove(u->manager->failed_units, u);
+        manager_update_failed_units(u->manager, u, false);
         set_remove(u->manager->startup_units, u);
 
         free(u->description);
@@ -608,7 +614,7 @@ static int reserve_dependencies(Unit *u, Unit *other, UnitDependency d) {
 
         /*
          * If u does not have this dependency set allocated, there is no need
-         * to reserve anything. In that case other's set will be transfered
+         * to reserve anything. In that case other's set will be transferred
          * as a whole to u by complete_move().
          */
         if (!u->dependencies[d])
@@ -856,7 +862,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         assert(u->type >= 0);
 
         prefix = strempty(prefix);
-        prefix2 = strappenda(prefix, "\t");
+        prefix2 = strjoina(prefix, "\t");
 
         fprintf(f,
                 "%s-> Unit %s:\n"
@@ -1387,7 +1393,6 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
         snprintf(buf, sizeof(buf), format, unit_description(u));
-        char_array_0(buf);
         REENABLE_WARNING;
 
         mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING :
@@ -1411,6 +1416,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 int unit_start(Unit *u) {
         UnitActiveState state;
         Unit *following;
+        int r;
 
         assert(u);
 
@@ -1449,8 +1455,8 @@ int unit_start(Unit *u) {
                 return unit_start(following);
         }
 
-        unit_status_log_starting_stopping_reloading(u, JOB_START);
-        unit_status_print_starting_stopping(u, JOB_START);
+        if (UNIT_VTABLE(u)->supported && !UNIT_VTABLE(u)->supported(u->manager))
+                return -ENOTSUP;
 
         /* If it is stopped, but we cannot start it, then fail */
         if (!UNIT_VTABLE(u)->start)
@@ -1464,7 +1470,14 @@ int unit_start(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        return UNIT_VTABLE(u)->start(u);
+        r = UNIT_VTABLE(u)->start(u);
+        if (r <= 0)
+                return r;
+
+        /* Log if the start function actually did something */
+        unit_status_log_starting_stopping_reloading(u, JOB_START);
+        unit_status_print_starting_stopping(u, JOB_START);
+        return r;
 }
 
 bool unit_can_start(Unit *u) {
@@ -1488,6 +1501,7 @@ bool unit_can_isolate(Unit *u) {
 int unit_stop(Unit *u) {
         UnitActiveState state;
         Unit *following;
+        int r;
 
         assert(u);
 
@@ -1495,21 +1509,24 @@ int unit_stop(Unit *u) {
         if (UNIT_IS_INACTIVE_OR_FAILED(state))
                 return -EALREADY;
 
-        if ((following = unit_following(u))) {
-                log_unit_debug(u->id, "Redirecting stop request from %s to %s.",
-                               u->id, following->id);
+        following = unit_following(u);
+        if (following) {
+                log_unit_debug(u->id, "Redirecting stop request from %s to %s.", u->id, following->id);
                 return unit_stop(following);
         }
 
-        unit_status_log_starting_stopping_reloading(u, JOB_STOP);
-        unit_status_print_starting_stopping(u, JOB_STOP);
-
         if (!UNIT_VTABLE(u)->stop)
                 return -EBADR;
 
         unit_add_to_dbus_queue(u);
 
-        return UNIT_VTABLE(u)->stop(u);
+        r = UNIT_VTABLE(u)->stop(u);
+        if (r <= 0)
+                return r;
+
+        unit_status_log_starting_stopping_reloading(u, JOB_STOP);
+        unit_status_print_starting_stopping(u, JOB_STOP);
+        return r;
 }
 
 /* Errors:
@@ -1520,6 +1537,7 @@ int unit_stop(Unit *u) {
 int unit_reload(Unit *u) {
         UnitActiveState state;
         Unit *following;
+        int r;
 
         assert(u);
 
@@ -1534,22 +1552,24 @@ int unit_reload(Unit *u) {
                 return -EALREADY;
 
         if (state != UNIT_ACTIVE) {
-                log_unit_warning(u->id, "Unit %s cannot be reloaded because it is inactive.",
-                                 u->id);
+                log_unit_warning(u->id, "Unit %s cannot be reloaded because it is inactive.", u->id);
                 return -ENOEXEC;
         }
 
         following = unit_following(u);
         if (following) {
-                log_unit_debug(u->id, "Redirecting reload request from %s to %s.",
-                               u->id, following->id);
+                log_unit_debug(u->id, "Redirecting reload request from %s to %s.", u->id, following->id);
                 return unit_reload(following);
         }
 
-        unit_status_log_starting_stopping_reloading(u, JOB_RELOAD);
-
         unit_add_to_dbus_queue(u);
-        return UNIT_VTABLE(u)->reload(u);
+
+        r = UNIT_VTABLE(u)->reload(u);
+        if (r <= 0)
+                return r;
+
+        unit_status_log_starting_stopping_reloading(u, JOB_RELOAD);
+        return r;
 }
 
 bool unit_can_reload(Unit *u) {
@@ -1622,12 +1642,14 @@ static void unit_check_binds_to(Unit *u) {
                         continue;
 
                 stop = true;
+                break;
         }
 
         if (!stop)
                 return;
 
-        log_unit_info(u->id, "Unit %s is bound to inactive service. Stopping, too.", u->id);
+        assert(other);
+        log_unit_info(u->id, "Unit %s is bound to inactive unit %s. Stopping, too.", u->id, other->id);
 
         /* A unit we need to run is gone. Sniff. Let's stop this. */
         manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
@@ -1726,7 +1748,7 @@ void unit_start_on_failure(Unit *u) {
 
                 r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL);
                 if (r < 0)
-                        log_unit_error(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r));
+                        log_unit_error_errno(u->id, r, "Failed to enqueue OnFailure= job: %m");
         }
 }
 
@@ -1775,14 +1797,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
         }
 
         /* Keep track of failed units */
-        if (ns == UNIT_FAILED)
-                set_put(u->manager->failed_units, u);
-        else
-                set_remove(u->manager->failed_units, u);
+        manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
 
         /* Make sure the cgroup is always removed when we become inactive */
         if (UNIT_IS_INACTIVE_OR_FAILED(ns))
-                unit_destroy_cgroup(u);
+                unit_destroy_cgroup_if_empty(u);
 
         /* Note that this doesn't apply to RemainAfterExit services exiting
          * successfully, since there's no change of state in that case. Which is
@@ -2672,7 +2691,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                 if (streq(l, "job")) {
                         if (v[0] == '\0') {
                                 /* new-style serialized job */
-                                Job *j = job_new_raw(u);
+                                Job *j;
+
+                                j = job_new_raw(u);
                                 if (!j)
                                         return -ENOMEM;
 
@@ -2694,12 +2715,11 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                                         job_free(j);
                                         return r;
                                 }
-
-                                if (j->state == JOB_RUNNING)
-                                        u->manager->n_running_jobs++;
                         } else {
                                 /* legacy */
-                                JobType type = job_type_from_string(v);
+                                JobType type;
+
+                                type = job_type_from_string(v);
                                 if (type < 0)
                                         log_debug("Failed to parse job type value %s", v);
                                 else
@@ -2814,11 +2834,10 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
                 return -ENOMEM;
 
         r = manager_load_unit(u->manager, e, NULL, NULL, &device);
-
         if (r < 0)
                 return r;
 
-        r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true);
+        r = unit_add_two_dependencies(u, UNIT_AFTER, u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_BINDS_TO : UNIT_WANTS, device, true);
         if (r < 0)
                 return r;
 
@@ -2892,7 +2911,7 @@ bool unit_need_daemon_reload(Unit *u) {
                         return true;
         }
 
-        t = unit_find_dropin_paths(u);
+        (void) unit_find_dropin_paths(u, &t);
         loaded_cnt = strv_length(t);
         current_cnt = strv_length(u->dropin_paths);
 
@@ -3090,6 +3109,17 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
         return u->unit_file_state;
 }
 
+int unit_get_unit_file_preset(Unit *u) {
+        assert(u);
+
+        if (u->unit_file_preset < 0 && u->fragment_path)
+                u->unit_file_preset = unit_file_query_preset(
+                                u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+                                NULL, basename(u->fragment_path));
+
+        return u->unit_file_preset;
+}
+
 Unit* unit_ref_set(UnitRef *ref, Unit *u) {
         assert(ref);
         assert(u);
@@ -3140,6 +3170,10 @@ int unit_patch_contexts(Unit *u) {
                         r = get_home_dir(&ec->working_directory);
                         if (r < 0)
                                 return r;
+
+                        /* Allow user services to run, even if the
+                         * home directory is missing */
+                        ec->working_directory_missing_ok = true;
                 }
 
                 if (u->manager->running_as == SYSTEMD_USER &&
@@ -3260,7 +3294,7 @@ static int unit_drop_in_file(Unit *u,
 
 int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
 
-        _cleanup_free_ char *dir = NULL;
+        _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
         int r;
 
         assert(u);
@@ -3272,7 +3306,24 @@ int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, co
         if (r < 0)
                 return r;
 
-        return write_drop_in(dir, u->id, 50, name, data);
+        r = write_drop_in(dir, u->id, 50, name, data);
+        if (r < 0)
+                return r;
+
+        r = drop_in_file(dir, u->id, 50, name, &p, &q);
+        if (r < 0)
+                return r;
+
+        r = strv_extend(&u->dropin_paths, q);
+        if (r < 0)
+                return r;
+
+        strv_sort(u->dropin_paths);
+        strv_uniq(u->dropin_paths);
+
+        u->dropin_mtime = now(CLOCK_REALTIME);
+
+        return 0;
 }
 
 int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {
@@ -3435,7 +3486,7 @@ int unit_kill_context(
                         _cleanup_free_ char *comm = NULL;
                         get_process_comm(main_pid, &comm);
 
-                        log_unit_warning(u->id, "Failed to kill main process " PID_FMT " (%s): %s", main_pid, strna(comm), strerror(-r));
+                        log_unit_warning_errno(u->id, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
                 } else {
                         if (!main_pid_alien)
                                 wait_for_exit = true;
@@ -3452,7 +3503,7 @@ int unit_kill_context(
                         _cleanup_free_ char *comm = NULL;
                         get_process_comm(control_pid, &comm);
 
-                        log_unit_warning(u->id, "Failed to kill control process " PID_FMT " (%s): %s", control_pid, strna(comm), strerror(-r));
+                        log_unit_warning_errno(u->id, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
                 } else {
                         wait_for_exit = true;
 
@@ -3472,7 +3523,7 @@ int unit_kill_context(
                 r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
                 if (r < 0) {
                         if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
-                                log_unit_warning(u->id, "Failed to kill control group: %s", strerror(-r));
+                                log_unit_warning_errno(u->id, r, "Failed to kill control group: %m");
                 } else if (r > 0) {
 
                         /* FIXME: For now, we will not wait for the