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)
[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.",
[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) {
[JOB_ASSERT] = LOG_WARNING,
[JOB_UNSUPPORTED] = LOG_WARNING,
[JOB_COLLECTED] = LOG_INFO,
+ [JOB_ONCE] = LOG_ERR,
};
assert(u);
[JOB_ASSERT] = "assert",
[JOB_UNSUPPORTED] = "unsupported",
[JOB_COLLECTED] = "collected",
+ [JOB_ONCE] = "once",
};
DEFINE_STRING_TABLE_LOOKUP(job_result, 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
};
.can_transient = true,
.can_delegate = true,
+ .once_only = true,
.init = scope_init,
.load = scope_load,
* -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;
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,
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;
}
/* 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;
};
}
static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
- int r = 0;
-
assert(d->result);
if (!quiet) {
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;
}
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) {