Containers do not really support .device, .automount or .swap units;
Systems compiled without support for swap do not support .swap units;
Systems without kdbus do not support .busname units.
With this change attempts to start a unsupported unit types will result
in an immediate "unsupported" job result, which is a lot more
descriptive then before. Also, attempts to start device units in
containers will now immediately fail instead of causing jobs to be
enqueued that never go away.
a->result = AUTOMOUNT_SUCCESS;
}
a->result = AUTOMOUNT_SUCCESS;
}
+static bool automount_supported(Manager *m) {
+ static int supported = -1;
+
+ assert(m);
+
+ if (supported < 0)
+ supported = access("/dev/autofs", F_OK) >= 0;
+
+ return supported;
+}
+
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
.bus_vtable = bus_automount_vtable,
.shutdown = automount_shutdown,
.bus_vtable = bus_automount_vtable,
.shutdown = automount_shutdown,
+ .supported = automount_supported,
.status_message_formats = {
.finished_start_job = {
.status_message_formats = {
.finished_start_job = {
+static bool busname_supported(Manager *m) {
+ int supported = -1;
+ assert(m);
+
+ if (supported < 0)
+ supported = access("/sys/fs/kdbus", F_OK) >= 0;
+
+ return supported;
+}
+
static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
[BUSNAME_DEAD] = "dead",
[BUSNAME_MAKING] = "making",
static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
[BUSNAME_DEAD] = "dead",
[BUSNAME_MAKING] = "making",
.reset_failed = busname_reset_failed,
.reset_failed = busname_reset_failed,
+ .supported = busname_supported,
+
.bus_interface = "org.freedesktop.systemd1.BusName",
.bus_vtable = bus_busname_vtable,
.bus_interface = "org.freedesktop.systemd1.BusName",
.bus_vtable = bus_busname_vtable,
+static bool device_supported(Manager *m) {
+ static int read_only = -1;
+ assert(m);
+
+ /* If /sys is read-only we don't support device units, and any
+ * attempts to start one should fail immediately. */
+
+ if (read_only < 0)
+ read_only = path_is_read_only_fs("/sys");
+
+ return read_only <= 0;
+}
+
static const char* const device_state_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = "dead",
[DEVICE_PLUGGED] = "plugged"
static const char* const device_state_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = "dead",
[DEVICE_PLUGGED] = "plugged"
.enumerate = device_enumerate,
.shutdown = device_shutdown,
.enumerate = device_enumerate,
.shutdown = device_shutdown,
+ .supported = device_supported,
.status_message_formats = {
.starting_stopping = {
.status_message_formats = {
.starting_stopping = {
r = job_finish_and_invalidate(j, JOB_INVALID, true);
else if (r == -EPROTO)
r = job_finish_and_invalidate(j, JOB_ASSERT, true);
r = job_finish_and_invalidate(j, JOB_INVALID, true);
else if (r == -EPROTO)
r = job_finish_and_invalidate(j, JOB_ASSERT, true);
+ else if (r == -ENOTSUP)
+ r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true);
else if (r == -EAGAIN) {
j->state = JOB_WAITING;
m->n_running_jobs--;
else if (r == -EAGAIN) {
j->state = JOB_WAITING;
m->n_running_jobs--;
if (t == JOB_START) {
if (result == JOB_DONE)
return "Started %s.";
if (t == JOB_START) {
if (result == JOB_DONE)
return "Started %s.";
+ else if (result == JOB_TIMEOUT)
+ return "Timed out starting %s.";
else if (result == JOB_FAILED)
return "Failed to start %s.";
else if (result == JOB_DEPENDENCY)
return "Dependency failed for %s.";
else if (result == JOB_FAILED)
return "Failed to start %s.";
else if (result == JOB_DEPENDENCY)
return "Dependency failed for %s.";
- else if (result == JOB_TIMEOUT)
- return "Timed out starting %s.";
+ else if (result == JOB_ASSERT)
+ return "Assertion failed for %s.";
+ else if (result == JOB_UNSUPPORTED)
+ return "Starting of %s not supported.";
} else if (t == JOB_STOP || t == JOB_RESTART) {
if (result == JOB_DONE)
return "Stopped %s.";
} else if (t == JOB_STOP || t == JOB_RESTART) {
if (result == JOB_DONE)
return "Stopped %s.";
unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
+ case JOB_TIMEOUT:
+ manager_flip_auto_status(u->manager, true);
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
+ break;
+
case JOB_FAILED: {
bool quotes;
case JOB_FAILED: {
bool quotes;
unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
break;
unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
break;
manager_flip_auto_status(u->manager, true);
manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
+ unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format);
manager_flip_auto_status(u->manager, true);
manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format);
+ unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF, format);
[JOB_SKIPPED] = "skipped",
[JOB_INVALID] = "invalid",
[JOB_ASSERT] = "assert",
[JOB_SKIPPED] = "skipped",
[JOB_INVALID] = "invalid",
[JOB_ASSERT] = "assert",
+ [JOB_UNSUPPORTED] = "unsupported",
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
enum JobResult {
JOB_DONE, /* Job completed successfully */
JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */
enum JobResult {
JOB_DONE, /* Job completed successfully */
JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */
- JOB_TIMEOUT, /* JobTimeout elapsed */
+ JOB_TIMEOUT, /* Job timeout elapsed */
JOB_FAILED, /* Job failed */
JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */
JOB_SKIPPED, /* Negative result of JOB_VERIFY_ACTIVE */
JOB_INVALID, /* JOB_RELOAD of inactive unit */
JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */
JOB_FAILED, /* Job failed */
JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */
JOB_SKIPPED, /* Negative result of JOB_VERIFY_ACTIVE */
JOB_INVALID, /* JOB_RELOAD of inactive unit */
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_RESULT_MAX,
_JOB_RESULT_INVALID = -1
};
_JOB_RESULT_MAX,
_JOB_RESULT_INVALID = -1
};
}
int manager_enumerate(Manager *m) {
}
int manager_enumerate(Manager *m) {
UnitType c;
assert(m);
/* Let's ask every type to load all units from disk/kernel
* that it might know */
UnitType c;
assert(m);
/* Let's ask every type to load all units from disk/kernel
* that it might know */
- for (c = 0; c < _UNIT_TYPE_MAX; c++)
- if (unit_vtable[c]->enumerate) {
- q = unit_vtable[c]->enumerate(m);
- if (q < 0)
- r = q;
+ for (c = 0; c < _UNIT_TYPE_MAX; c++) {
+ int q;
+
+ if (unit_vtable[c]->supported && !unit_vtable[c]->supported(m)) {
+ log_info("Unit type .%s is not supported on this system.", unit_type_to_string(c));
+ continue;
+ if (!unit_vtable[c]->enumerate)
+ continue;
+
+ q = unit_vtable[c]->enumerate(m);
+ if (q < 0)
+ r = q;
+ }
+
manager_dispatch_load_queue(m);
return r;
}
manager_dispatch_load_queue(m);
return r;
}
if (m->running_as == SYSTEMD_SYSTEM) {
const char* target;
if (m->running_as == SYSTEMD_SYSTEM) {
const char* target;
+static bool swap_supported(Manager *m) {
+ static int supported = -1;
+
+ /* If swap support is not available in the kernel, or we are
+ * running in a container we don't support swap units, and any
+ * attempts to starting one should fail immediately. */
+
+ if (supported < 0)
+ supported =
+ access("/proc/swaps", F_OK) >= 0 &&
+ detect_container(NULL) <= 0;
+
+ return supported;
+}
+
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = "dead",
[SWAP_ACTIVATING] = "activating",
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = "dead",
[SWAP_ACTIVATING] = "activating",
.enumerate = swap_enumerate,
.shutdown = swap_shutdown,
.enumerate = swap_enumerate,
.shutdown = swap_shutdown,
+ .supported = swap_supported,
.status_message_formats = {
.starting_stopping = {
.status_message_formats = {
.starting_stopping = {
unit_status_log_starting_stopping_reloading(u, JOB_START);
unit_status_print_starting_stopping(u, JOB_START);
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)
return -EBADR;
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
return -EBADR;
if (UNIT_IS_INACTIVE_OR_FAILED(state))
return -EALREADY;
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);
}
return unit_stop(following);
}
return -EALREADY;
if (state != UNIT_ACTIVE) {
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) {
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);
}
return unit_reload(following);
}
/* Type specific cleanups. */
void (*shutdown)(Manager *m);
/* Type specific cleanups. */
void (*shutdown)(Manager *m);
+ /* If this function is set and return false all jobs for units
+ * of this type will immediately fail. */
+ bool (*supported)(Manager *m);
+
/* The interface name */
const char *bus_interface;
/* The interface name */
const char *bus_interface;
assert(d->result);
if (!arg_quiet) {
assert(d->result);
if (!arg_quiet) {
- if (streq(d->result, "timeout"))
- log_error("Job for %s timed out.", strna(d->name));
- else if (streq(d->result, "canceled"))
+ if (streq(d->result, "canceled"))
log_error("Job for %s canceled.", strna(d->name));
log_error("Job for %s canceled.", strna(d->name));
+ else if (streq(d->result, "timeout"))
+ log_error("Job for %s timed out.", strna(d->name));
else if (streq(d->result, "dependency"))
log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
else if (streq(d->result, "dependency"))
log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
+ else if (streq(d->result, "invalid"))
+ log_error("Job for %s invalid.", strna(d->name));
+ else if (streq(d->result, "assert"))
+ log_error("Assertion failed on job for %s.", strna(d->name));
+ else if (streq(d->result, "unsupported"))
+ log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
if (d->name) {
bool quotes;
else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
if (d->name) {
bool quotes;
- if (streq(d->result, "timeout"))
- r = -ETIME;
- else if (streq(d->result, "canceled"))
+ if (streq(d->result, "canceled"))
+ else if (streq(d->result, "timeout"))
+ r = -ETIME;
else if (streq(d->result, "dependency"))
r = -EIO;
else if (streq(d->result, "dependency"))
r = -EIO;
+ else if (streq(d->result, "invalid"))
+ r = -ENOEXEC;
+ else if (streq(d->result, "assert"))
+ r = -EPROTO;
+ else if (streq(d->result, "unsupported"))
+ r = -ENOTSUP;
else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
r = -EIO;
else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
r = -EIO;