]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: enforce that scope units can be started only once
authorLennart Poettering <lennart@poettering.net>
Fri, 27 Apr 2018 18:35:10 +0000 (20:35 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 27 Apr 2018 19:52:45 +0000 (21:52 +0200)
Scope units are populated from PIDs specified by the bus client. We do
that when a scope is started. We really shouldn't allow scopes to be
started multiple times, as the PIDs then might be heavily out of date.
Moreover, clients should have the guarantee that any scope they allocate
has a clear runtime cycle which is not repetitive.

src/core/job.c
src/core/job.h
src/core/scope.c
src/core/unit.c
src/core/unit.h
src/shared/bus-unit-util.c

index 4581dd520eed73fb9187c70b00c9f0df064ef3d2..32f6fc1a50f243583c218800631d31ebf3c38e3a 100644 (file)
@@ -612,6 +612,8 @@ int job_run_and_invalidate(Job *j) {
                         r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false);
                 else if (r == -ENOLINK)
                         r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
+                else if (r == -ESTALE)
+                        r = job_finish_and_invalidate(j, JOB_ONCE, true, false);
                 else if (r == -EAGAIN)
                         job_set_state(j, JOB_WAITING);
                 else if (r < 0)
@@ -631,6 +633,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
                 [JOB_ASSERT]      = "Assertion failed for %s.",
                 [JOB_UNSUPPORTED] = "Starting of %s not supported.",
                 [JOB_COLLECTED]   = "Unnecessary job for %s was removed.",
+                [JOB_ONCE]        = "Unit %s has been started before and cannot be started again."
         };
         static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
                 [JOB_DONE]        = "Stopped %s.",
@@ -690,6 +693,7 @@ static const struct {
         [JOB_ASSERT]      = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" },
         [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" },
         /* JOB_COLLECTED */
+        [JOB_ONCE]        = { ANSI_HIGHLIGHT_RED,    " ONCE " },
 };
 
 static void job_print_status_message(Unit *u, JobType t, JobResult result) {
@@ -747,6 +751,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
                 [JOB_ASSERT]      = LOG_WARNING,
                 [JOB_UNSUPPORTED] = LOG_WARNING,
                 [JOB_COLLECTED]   = LOG_INFO,
+                [JOB_ONCE]        = LOG_ERR,
         };
 
         assert(u);
@@ -1523,6 +1528,7 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = {
         [JOB_ASSERT] = "assert",
         [JOB_UNSUPPORTED] = "unsupported",
         [JOB_COLLECTED] = "collected",
+        [JOB_ONCE] = "once",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
index 87f8d63c82e48f8bf7098648bd58b4b04b2182e2..ccb8e1b67415c7daeb944cd27c466a67f1f633a9 100644 (file)
@@ -96,6 +96,7 @@ enum JobResult {
         JOB_ASSERT,              /* Couldn't start a unit, because an assert didn't hold */
         JOB_UNSUPPORTED,         /* Couldn't start a unit, because the unit type is not supported on the system */
         JOB_COLLECTED,           /* Job was garbage collected, since nothing needed it anymore */
+        JOB_ONCE,                /* Unit was started before, and hence can't be started again */
         _JOB_RESULT_MAX,
         _JOB_RESULT_INVALID = -1
 };
index 6c8c5a57ba06695cbe5078bbeae82d5c0cd5cbf3..1469cebff92b54fd631afd533c46f6031a5fd0b0 100644 (file)
@@ -587,6 +587,7 @@ const UnitVTable scope_vtable = {
 
         .can_transient = true,
         .can_delegate = true,
+        .once_only = true,
 
         .init = scope_init,
         .load = scope_load,
index 4c7e2cb8ae56b94ff37108e9ed5688958de05329..09ed43a104c5104ba4dfb46a7b449c29caa39481 100644 (file)
@@ -1753,6 +1753,7 @@ static bool unit_verify_deps(Unit *u) {
  *         -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
  */
 int unit_start(Unit *u) {
         UnitActiveState state;
@@ -1772,6 +1773,10 @@ int unit_start(Unit *u) {
         if (u->load_state != UNIT_LOADED)
                 return -EINVAL;
 
+        /* Refuse starting scope units more than once */
+        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,
@@ -1835,6 +1840,10 @@ bool unit_can_start(Unit *u) {
         if (!unit_supported(u))
                 return false;
 
+        /* Scope units may be started only once */
+        if (UNIT_VTABLE(u)->once_only && dual_timestamp_is_set(&u->inactive_exit_timestamp))
+                return false;
+
         return !!UNIT_VTABLE(u)->start;
 }
 
index 89e9faa021d156941ccb74a3bd8e52b56b17d904..26194ef35a0a90eab44a7a999da8ed16aa3974ce 100644 (file)
@@ -561,6 +561,9 @@ struct UnitVTable {
         /* True if cgroup delegation is permissible */
         bool can_delegate:1;
 
+        /* True if units of this type shall be startable only once and then never again */
+        bool once_only:1;
+
         /* True if queued jobs of this type should be GC'ed if no other job needs them anymore */
         bool gc_jobs:1;
 };
index 1d3f991bef1fdbd9b7b0b4399aaaec1642689067..a3b9bee6ce789cd2a45bc7c6065ec85c86826c3a 100644 (file)
@@ -1900,8 +1900,6 @@ finish:
 }
 
 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
-        int r = 0;
-
         assert(d->result);
 
         if (!quiet) {
@@ -1919,6 +1917,8 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const*
                         log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
                 else if (streq(d->result, "collected"))
                         log_error("Queued job for %s was garbage collected.", strna(d->name));
+                else if (streq(d->result, "once"))
+                        log_error("Unit %s was started already once and can't be started again.", strna(d->name));
                 else if (!STR_IN_SET(d->result, "done", "skipped")) {
                         if (d->name) {
                                 _cleanup_free_ char *result = NULL;
@@ -1935,21 +1935,24 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const*
         }
 
         if (STR_IN_SET(d->result, "canceled", "collected"))
-                r = -ECANCELED;
+                return -ECANCELED;
         else if (streq(d->result, "timeout"))
-                r = -ETIME;
+                return -ETIME;
         else if (streq(d->result, "dependency"))
-                r = -EIO;
+                return -EIO;
         else if (streq(d->result, "invalid"))
-                r = -ENOEXEC;
+                return -ENOEXEC;
         else if (streq(d->result, "assert"))
-                r = -EPROTO;
+                return -EPROTO;
         else if (streq(d->result, "unsupported"))
-                r = -EOPNOTSUPP;
-        else if (!STR_IN_SET(d->result, "done", "skipped"))
-                r = -EIO;
+                return -EOPNOTSUPP;
+        else if (streq(d->result, "once"))
+                return -ESTALE;
+        else if (STR_IN_SET(d->result, "done", "skipped"))
+                return 0;
 
-        return r;
+        log_debug("Unexpected job result, assuming server side newer than us: %s", d->result);
+        return -EIO;
 }
 
 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {