Merge pull request #8847 from poettering/transient-once

enforce that scope units are started at most once
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-04-30 09:50:03 +02:00 committed by GitHub
commit 2ff04e5b7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 34 additions and 20 deletions

9
TODO
View file

@ -52,8 +52,6 @@ Features:
* add --vacuum-xyz options to coredumpctl, matching those journalctl already has.
* enforce that transient units are started exactly once only
* list the exit codes from the BSD/glibc <sysexits.h> in our own
exit-codes.[ch] tables.
@ -517,7 +515,6 @@ Features:
* transient units:
- add field to transient units that indicate whether systemd or somebody else saves/restores its settings, for integration with libvirt
- ensure scope units may be started only a single time
* Automatically configure swap partition to use for hibernation by looking for largest swap partition on the root disk?
@ -640,10 +637,6 @@ Features:
logout dialog. If it is pressed for 1s, do the usual
shutdown. Inspiration are Macs here.
- expose "Locked" property on logind sesison objects
- given that logind now lets PID 1 do all nasty work, we can
probably reduce the capability set it retains substantially.
(we need CAP_SYS_ADMIN for drmSetMaster(), so maybe not worth it)
- expose orientation sensors and tablet mode through logind
- maybe allow configuration of the StopTimeout for session scopes
- rename session scope so that it includes the UID. THat way
the session scope can be arranged freely in slices and we don't have
@ -700,8 +693,6 @@ Features:
lazily. Encode just enough information in the file name, so that we
do not have to open it to know that it is not interesting for us, for
the most common operations.
- journal-or-kmsg is currently broken? See reverted
commit 4a01181e460686d8b4a543b1dfa7f77c9e3c5ab8.
- man: document that corrupted journal files is nothing to act on
- rework journald sigbus stuff to use mutex
- Set RLIMIT_NPROC for systemd-journal-xyz, and all other of our

View 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);

View 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
};

View file

@ -587,6 +587,7 @@ const UnitVTable scope_vtable = {
.can_transient = true,
.can_delegate = true,
.once_only = true,
.init = scope_init,
.load = scope_load,

View 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;
}

View 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;
};

View 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) {