Merge pull request #4481 from poettering/perpetual

Add "perpetual" unit concept, sysctl fixes, networkd fixes, systemctl color fixes, nspawn discard.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-11-02 21:03:26 -04:00 committed by GitHub
commit 7fa6328cc4
15 changed files with 212 additions and 109 deletions

View file

@ -117,3 +117,6 @@
/* The scope unit systemd itself lives in. */
#define SPECIAL_INIT_SCOPE "init.scope"
/* The root directory. */
#define SPECIAL_ROOT_MOUNT "-.mount"

View file

@ -263,10 +263,7 @@ static int property_get_can_stop(
assert(reply);
assert(u);
/* On the lower levels we assume that every unit we can start
* we can also stop */
return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop);
return sd_bus_message_append(reply, "b", unit_can_stop(u) && !u->refuse_manual_stop);
}
static int property_get_can_reload(
@ -760,6 +757,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Perpetual", "b", bus_property_get_bool, offsetof(Unit, perpetual), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),

View file

@ -331,11 +331,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
if (!u) {
delete = true;
u = unit_new(m, sizeof(Device));
if (!u)
return log_oom();
r = unit_add_name(u, e);
r = unit_new_for_name(m, sizeof(Device), e, &u);
if (r < 0)
goto fail;

View file

@ -159,17 +159,6 @@ static void mount_init(Unit *u) {
m->timeout_usec = u->manager->default_timeout_start_usec;
m->directory_mode = 0755;
if (unit_has_name(u, "-.mount")) {
/* Don't allow start/stop for root directory */
u->refuse_manual_start = true;
u->refuse_manual_stop = true;
} else {
/* The stdio/kmsg bridge socket is on /, in order to avoid a
* dep loop, don't use kmsg logging for -.mount */
m->exec_context.std_output = u->manager->default_std_output;
m->exec_context.std_error = u->manager->default_std_error;
}
/* We need to make sure that /usr/bin/mount is always called
* in the same process group as us, so that the autofs kernel
* side doesn't send us another mount request while we are
@ -577,6 +566,25 @@ static int mount_add_extras(Mount *m) {
return 0;
}
static int mount_load_root_mount(Unit *u) {
assert(u);
if (!unit_has_name(u, SPECIAL_ROOT_MOUNT))
return 0;
u->perpetual = true;
u->default_dependencies = false;
/* The stdio/kmsg bridge socket is on /, in order to avoid a dep loop, don't use kmsg logging for -.mount */
MOUNT(u)->exec_context.std_output = EXEC_OUTPUT_NULL;
MOUNT(u)->exec_context.std_input = EXEC_INPUT_NULL;
if (!u->description)
u->description = strdup("Root Mount");
return 1;
}
static int mount_load(Unit *u) {
Mount *m = MOUNT(u);
int r;
@ -584,11 +592,14 @@ static int mount_load(Unit *u) {
assert(u);
assert(u->load_state == UNIT_STUB);
if (m->from_proc_self_mountinfo)
r = mount_load_root_mount(u);
if (r < 0)
return r;
if (m->from_proc_self_mountinfo || u->perpetual)
r = unit_load_fragment_and_dropin_optional(u);
else
r = unit_load_fragment_and_dropin(u);
if (r < 0)
return r;
@ -1393,11 +1404,7 @@ static int mount_setup_unit(
if (!u) {
delete = true;
u = unit_new(m, sizeof(Mount));
if (!u)
return log_oom();
r = unit_add_name(u, e);
r = unit_new_for_name(m, sizeof(Mount), e, &u);
if (r < 0)
goto fail;
@ -1592,11 +1599,46 @@ static int mount_get_timeout(Unit *u, usec_t *timeout) {
return 1;
}
static int synthesize_root_mount(Manager *m) {
Unit *u;
int r;
assert(m);
/* Whatever happens, we know for sure that the root directory is around, and cannot go away. Let's
* unconditionally synthesize it here and mark it as perpetual. */
u = manager_get_unit(m, SPECIAL_ROOT_MOUNT);
if (!u) {
r = unit_new_for_name(m, sizeof(Mount), SPECIAL_ROOT_MOUNT, &u);
if (r < 0)
return log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_MOUNT " unit: %m");
}
u->perpetual = true;
MOUNT(u)->deserialized_state = MOUNT_MOUNTED;
unit_add_to_load_queue(u);
unit_add_to_dbus_queue(u);
return 0;
}
static bool mount_is_mounted(Mount *m) {
assert(m);
return UNIT(m)->perpetual || m->is_mounted;
}
static void mount_enumerate(Manager *m) {
int r;
assert(m);
r = synthesize_root_mount(m);
if (r < 0)
goto fail;
mnt_init_debug(0);
if (!m->mount_monitor) {
@ -1703,7 +1745,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
Mount *mount = MOUNT(u);
if (!mount->is_mounted) {
if (!mount_is_mounted(mount)) {
/* A mount point is not around right now. It
* might be gone, or might never have
@ -1764,7 +1806,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
}
}
if (mount->is_mounted &&
if (mount_is_mounted(mount) &&
mount->from_proc_self_mountinfo &&
mount->parameters_proc_self_mountinfo.what) {

View file

@ -154,15 +154,13 @@ static int scope_load_init_scope(Unit *u) {
return 0;
u->transient = true;
u->no_gc = true;
u->perpetual = true;
/* init.scope is a bit special, as it has to stick around forever. Because of its special semantics we
* synthesize it here, instead of relying on the unit file on disk. */
u->default_dependencies = false;
u->ignore_on_isolate = true;
u->refuse_manual_start = true;
u->refuse_manual_stop = true;
SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14;
@ -565,22 +563,15 @@ static void scope_enumerate(Manager *m) {
u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
if (!u) {
u = unit_new(m, sizeof(Scope));
if (!u) {
log_oom();
return;
}
r = unit_add_name(u, SPECIAL_INIT_SCOPE);
r = unit_new_for_name(m, sizeof(Scope), SPECIAL_INIT_SCOPE, &u);
if (r < 0) {
unit_free(u);
log_error_errno(r, "Failed to add the " SPECIAL_INIT_SCOPE " name: %m");
log_error_errno(r, "Failed to allocate the special " SPECIAL_INIT_SCOPE " unit: %m");
return;
}
}
u->transient = true;
u->no_gc = true;
u->perpetual = true;
SCOPE(u)->deserialized_state = SCOPE_RUNNING;
unit_add_to_load_queue(u);

View file

@ -136,15 +136,13 @@ static int slice_load_root_slice(Unit *u) {
if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
return 0;
u->no_gc = true;
u->perpetual = true;
/* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its
* special semantics we synthesize it here, instead of relying on the unit file on disk. */
u->default_dependencies = false;
u->ignore_on_isolate = true;
u->refuse_manual_start = true;
u->refuse_manual_stop = true;
if (!u->description)
u->description = strdup("Root Slice");
@ -301,21 +299,14 @@ static void slice_enumerate(Manager *m) {
u = manager_get_unit(m, SPECIAL_ROOT_SLICE);
if (!u) {
u = unit_new(m, sizeof(Slice));
if (!u) {
log_oom();
return;
}
r = unit_add_name(u, SPECIAL_ROOT_SLICE);
r = unit_new_for_name(m, sizeof(Slice), SPECIAL_ROOT_SLICE, &u);
if (r < 0) {
unit_free(u);
log_error_errno(r, "Failed to add the "SPECIAL_ROOT_SLICE " name: %m");
log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_SLICE " unit: %m");
return;
}
}
u->no_gc = true;
u->perpetual = true;
SLICE(u)->deserialized_state = SLICE_ACTIVE;
unit_add_to_load_queue(u);

View file

@ -381,11 +381,7 @@ static int swap_setup_unit(
if (!u) {
delete = true;
u = unit_new(m, sizeof(Swap));
if (!u)
return log_oom();
r = unit_add_name(u, e);
r = unit_new_for_name(m, sizeof(Swap), e, &u);
if (r < 0)
goto fail;

View file

@ -109,6 +109,24 @@ Unit *unit_new(Manager *m, size_t size) {
return u;
}
int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) {
Unit *u;
int r;
u = unit_new(m, size);
if (!u)
return -ENOMEM;
r = unit_add_name(u, name);
if (r < 0) {
unit_free(u);
return r;
}
*ret = u;
return r;
}
bool unit_has_name(Unit *u, const char *name) {
assert(u);
assert(name);
@ -325,7 +343,7 @@ bool unit_check_gc(Unit *u) {
if (!inactive)
return true;
if (u->no_gc)
if (u->perpetual)
return true;
if (u->refs)
@ -926,6 +944,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tGC Check Good: %s\n"
"%s\tNeed Daemon Reload: %s\n"
"%s\tTransient: %s\n"
"%s\tPerpetual: %s\n"
"%s\tSlice: %s\n"
"%s\tCGroup: %s\n"
"%s\tCGroup realized: %s\n"
@ -944,6 +963,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(unit_check_gc(u)),
prefix, yes_no(unit_need_daemon_reload(u)),
prefix, yes_no(u->transient),
prefix, yes_no(u->perpetual),
prefix, strna(unit_slice_name(u)),
prefix, strna(u->cgroup_path),
prefix, yes_no(u->cgroup_realized),
@ -1618,6 +1638,18 @@ int unit_stop(Unit *u) {
return UNIT_VTABLE(u)->stop(u);
}
bool unit_can_stop(Unit *u) {
assert(u);
if (!unit_supported(u))
return false;
if (u->perpetual)
return false;
return !!UNIT_VTABLE(u)->stop;
}
/* Errors:
* -EBADR: This unit type does not support reloading.
* -ENOEXEC: Unit is not started.
@ -2152,13 +2184,20 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
case JOB_VERIFY_ACTIVE:
case JOB_START:
case JOB_STOP:
case JOB_NOP:
/* Note that we don't check unit_can_start() here. That's because .device units and suchlike are not
* startable by us but may appear due to external events, and it thus makes sense to permit enqueing
* jobs for it. */
return true;
case JOB_STOP:
/* Similar as above. However, perpetual units can never be stopped (neither explicitly nor due to
* external events), hence it makes no sense to permit enqueing such a request either. */
return !u->perpetual;
case JOB_RESTART:
case JOB_TRY_RESTART:
return unit_can_start(u);
return unit_can_stop(u) && unit_can_start(u);
case JOB_RELOAD:
case JOB_TRY_RELOAD:

View file

@ -236,6 +236,9 @@ struct Unit {
/* Is this a transient unit? */
bool transient;
/* Is this a unit that is always running and cannot be stopped? */
bool perpetual;
bool in_load_queue:1;
bool in_dbus_queue:1;
bool in_cleanup_queue:1;
@ -244,8 +247,6 @@ struct Unit {
bool sent_dbus_new_signal:1;
bool no_gc:1;
bool in_audit:1;
bool cgroup_realized:1;
@ -480,6 +481,7 @@ DEFINE_CAST(SCOPE, Scope);
Unit *unit_new(Manager *m, size_t size);
void unit_free(Unit *u);
int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret);
int unit_add_name(Unit *u, const char *name);
int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference);
@ -524,6 +526,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix);
bool unit_can_reload(Unit *u) _pure_;
bool unit_can_start(Unit *u) _pure_;
bool unit_can_stop(Unit *u) _pure_;
bool unit_can_isolate(Unit *u) _pure_;
int unit_start(Unit *u);

View file

@ -514,13 +514,12 @@ static void link_free(Link *link) {
sd_lldp_unref(link->lldp);
free(link->lldp_file);
ndisc_flush(link);
sd_ipv4ll_unref(link->ipv4ll);
sd_dhcp6_client_unref(link->dhcp6_client);
sd_ndisc_unref(link->ndisc);
set_free_free(link->ndisc_rdnss);
set_free_free(link->ndisc_dnssl);
if (link->manager)
hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
@ -2427,6 +2426,8 @@ static int link_drop_config(Link *link) {
return r;
}
ndisc_flush(link);
return 0;
}

View file

@ -680,13 +680,22 @@ void ndisc_vacuum(Link *link) {
SET_FOREACH(r, link->ndisc_rdnss, i)
if (r->valid_until < time_now) {
(void) set_remove(link->ndisc_rdnss, r);
free(set_remove(link->ndisc_rdnss, r));
link_dirty(link);
}
SET_FOREACH(d, link->ndisc_dnssl, i)
if (d->valid_until < time_now) {
(void) set_remove(link->ndisc_dnssl, d);
free(set_remove(link->ndisc_dnssl, d));
link_dirty(link);
}
}
void ndisc_flush(Link *link) {
assert(link);
/* Removes all RDNSS and DNSSL entries, without exception */
link->ndisc_rdnss = set_free_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
}

View file

@ -37,3 +37,4 @@ static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
int ndisc_configure(Link *link);
void ndisc_vacuum(Link *link);
void ndisc_flush(Link *link);

View file

@ -2260,7 +2260,7 @@ static int dissect_image(
static int mount_device(const char *what, const char *where, const char *directory, bool rw) {
#ifdef HAVE_BLKID
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype, *p;
const char *fstype, *p, *options;
int r;
assert(what);
@ -2309,7 +2309,17 @@ static int mount_device(const char *what, const char *where, const char *directo
return -EOPNOTSUPP;
}
return mount_verbose(LOG_ERR, what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), NULL);
/* If this is a loopback device then let's mount the image with discard, so that the underlying file remains
* sparse when possible. */
if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs")) {
const char *l;
l = path_startswith(what, "/dev");
if (l && startswith(l, "loop"))
options = "discard";
}
return mount_verbose(LOG_ERR, what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
#else
log_error("--image= is not supported, compiled without blkid support.");
return -EOPNOTSUPP;

View file

@ -51,19 +51,46 @@ static int apply_all(OrderedHashmap *sysctl_options) {
k = sysctl_write(property, value);
if (k < 0) {
log_full_errno(k == -ENOENT ? LOG_INFO : LOG_WARNING, k,
"Couldn't write '%s' to '%s', ignoring: %m", value, property);
/* If the sysctl is not available in the kernel or we are running with reduced privileges and
* cannot write it, then log about the issue at LOG_NOTICE level, and proceed without
* failing. (EROFS is treated as a permission problem here, since that's how container managers
* usually protected their sysctls.) In all other cases log an error and make the tool fail. */
if (r == 0 && k != -ENOENT)
r = k;
if (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT))
log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", value, property);
else {
log_error_errno(k, "Couldn't write '%s' to '%s': %m", value, property);
if (r == 0)
r = k;
}
}
}
return r;
}
static bool test_prefix(const char *p) {
char **i;
if (strv_isempty(arg_prefixes))
return true;
STRV_FOREACH(i, arg_prefixes) {
const char *t;
t = path_startswith(*i, "/proc/sys/");
if (!t)
t = *i;
if (path_startswith(p, t))
return true;
}
return false;
}
static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
unsigned c = 0;
int r;
assert(path);
@ -77,7 +104,7 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
}
log_debug("Parsing %s", path);
while (!feof(f)) {
for (;;) {
char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
void *v;
int k;
@ -89,6 +116,8 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
}
c++;
p = strstrip(l);
if (!*p)
continue;
@ -98,7 +127,7 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
value = strchr(p, '=');
if (!value) {
log_error("Line is not an assignment in file '%s': %s", path, value);
log_error("Line is not an assignment at '%s:%u': %s", path, c, value);
if (r == 0)
r = -EINVAL;
@ -111,26 +140,15 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
p = sysctl_normalize(strstrip(p));
value = strstrip(value);
if (!strv_isempty(arg_prefixes)) {
char **i, *t;
STRV_FOREACH(i, arg_prefixes) {
t = path_startswith(*i, "/proc/sys/");
if (t == NULL)
t = *i;
if (path_startswith(p, t))
goto found;
}
/* not found */
if (!test_prefix(p))
continue;
}
found:
existing = ordered_hashmap_get2(sysctl_options, p, &v);
if (existing) {
if (streq(value, existing))
continue;
log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path);
log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c);
free(ordered_hashmap_remove(sysctl_options, p));
free(v);
}
@ -229,12 +247,12 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
OrderedHashmap *sysctl_options = NULL;
int r = 0, k;
OrderedHashmap *sysctl_options;
r = parse_argv(argc, argv);
if (r <= 0)
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
goto finish;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();

View file

@ -410,23 +410,24 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
}
static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len;
unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len, max_desc_len;
const UnitInfo *u;
unsigned n_shown = 0;
int job_count = 0, desc_len;
int job_count = 0;
max_id_len = strlen("UNIT");
load_len = strlen("LOAD");
active_len = strlen("ACTIVE");
sub_len = strlen("SUB");
job_len = strlen("JOB");
desc_len = 0;
max_desc_len = strlen("DESCRIPTION");
for (u = unit_infos; u < unit_infos + c; u++) {
max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0));
load_len = MAX(load_len, strlen(u->load_state));
active_len = MAX(active_len, strlen(u->active_state));
sub_len = MAX(sub_len, strlen(u->sub_state));
max_desc_len = MAX(max_desc_len, strlen(u->description));
if (u->job_id != 0) {
job_len = MAX(job_len, strlen(u->job_type));
@ -442,7 +443,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
if (!arg_full && original_stdout_is_tty) {
unsigned basic_len;
id_len = MIN(max_id_len, 25u);
id_len = MIN(max_id_len, 25u); /* as much as it needs, but at most 25 for now */
basic_len = circle_len + 5 + id_len + 5 + active_len + sub_len;
if (job_count)
@ -455,19 +456,21 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
/* Either UNIT already got 25, or is fully satisfied.
* Grant up to 25 to DESC now. */
incr = MIN(extra_len, 25u);
desc_len += incr;
desc_len = incr;
extra_len -= incr;
/* split the remaining space between UNIT and DESC,
* but do not give UNIT more than it needs. */
/* Of the remainder give as much as the ID needs to the ID, and give the rest to the
* description but not more than it needs. */
if (extra_len > 0) {
incr = MIN(extra_len / 2, max_id_len - id_len);
incr = MIN(max_id_len - id_len, extra_len);
id_len += incr;
desc_len += extra_len - incr;
desc_len += MIN(extra_len - incr, max_desc_len - desc_len);
}
}
} else
} else {
id_len = max_id_len;
desc_len = max_desc_len;
}
for (u = unit_infos; u < unit_infos + c; u++) {
_cleanup_free_ char *e = NULL, *j = NULL;
@ -493,8 +496,9 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
if (job_count)
printf("%-*s ", job_len, "JOB");
printf("%.*s%s\n",
!arg_full && arg_no_pager ? desc_len : -1,
printf("%-*.*s%s\n",
desc_len,
!arg_full && arg_no_pager ? (int) desc_len : -1,
"DESCRIPTION",
ansi_normal());
}
@ -513,13 +517,13 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
off_circle = ansi_normal();
circle = true;
on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
off_loaded = on_underline;
off_loaded = underline ? on_underline : ansi_normal();
} else if (streq(u->active_state, "failed") && !arg_plain) {
on_circle = ansi_highlight_red();
off_circle = ansi_normal();
circle = true;
on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
off_active = on_underline;
off_active = underline ? on_underline : ansi_normal();
}
if (u->machine) {
@ -550,8 +554,9 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
sub_len, u->sub_state, off_active,
job_count ? job_len + 1 : 0, u->job_id ? u->job_type : "");
printf("%.*s%s\n",
desc_len > 0 ? desc_len : -1,
printf("%-*.*s%s\n",
desc_len,
!arg_full && arg_no_pager ? (int) desc_len : -1,
u->description,
off_underline);
}