diff --git a/fixme b/fixme index 29e0fde741..ff00436e4a 100644 --- a/fixme +++ b/fixme @@ -38,8 +38,11 @@ * place /etc/inittab with explaining blurb. * In command lines, support both "$FOO" and $FOO - * /etc must always take precedence even if we follow symlinks! +* fix merging of device units +* color aus bei stdout auf !tty + +getty before prefdm * /lib/init/rw @@ -49,13 +52,17 @@ * fingerprint.target, wireless.target, gps.target -* fix merging of device units - * set_put(), hashmap_put() return values checken. i.e. == 0 macht kein free()! +* crash on missing hostname + +* fix merging in .swap units + * pahole -* color aus bei stdout auf !tty +* io priority + +* network.target darf nm nicht unbedingt starten External: diff --git a/man/systemctl.xml b/man/systemctl.xml index 737bcbe021..b8e00b6d5c 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -115,6 +115,15 @@ they are set or not. + + + + Do not ellipsize unit + names in the output of + list-units and + list-jobs. + + diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 969c430520..63e8083805 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -82,7 +82,7 @@ " \n" \ " \n" \ " \n" \ - " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -405,12 +405,12 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sssssouso)", &sub)) + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub)) goto oom; HASHMAP_FOREACH_KEY(u, k, m->units, i) { char *u_path, *j_path; - const char *description, *load_state, *active_state, *sub_state, *sjob_type; + const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following; DBusMessageIter sub2; uint32_t job_id; @@ -424,6 +424,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, load_state = unit_load_state_to_string(u->meta.load_state); active_state = unit_active_state_to_string(unit_active_state(u)); sub_state = unit_sub_state_to_string(u); + following = u->meta.following ? u->meta.following->meta.id : ""; if (!(u_path = unit_dbus_path(u))) goto oom; @@ -448,6 +449,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) || diff --git a/src/dbus-unit.c b/src/dbus-unit.c index 2dcd032a45..bb2541800a 100644 --- a/src/dbus-unit.c +++ b/src/dbus-unit.c @@ -47,6 +47,23 @@ int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, return 0; } +int bus_unit_append_following(Manager *m, DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + const char *d; + + assert(m); + assert(i); + assert(property); + assert(u); + + d = u->meta.following ? u->meta.following->meta.id : ""; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) + return -ENOMEM; + + return 0; +} + int bus_unit_append_dependencies(Manager *m, DBusMessageIter *i, const char *property, void *data) { Unit *u; Iterator j; diff --git a/src/dbus-unit.h b/src/dbus-unit.h index e49f82d8cb..0d17322709 100644 --- a/src/dbus-unit.h +++ b/src/dbus-unit.h @@ -60,6 +60,7 @@ " \n" \ " \n" \ " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -97,6 +98,7 @@ #define BUS_UNIT_PROPERTIES \ { "org.freedesktop.systemd1.Unit", "Id", bus_property_append_string, "s", u->meta.id }, \ { "org.freedesktop.systemd1.Unit", "Names", bus_unit_append_names, "as", u }, \ + { "org.freedesktop.systemd1.Unit", "Following", bus_unit_append_following, "s", u }, \ { "org.freedesktop.systemd1.Unit", "Requires", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRES] }, \ { "org.freedesktop.systemd1.Unit", "RequiresOverridable", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE] }, \ { "org.freedesktop.systemd1.Unit", "Requisite", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUISITE] }, \ @@ -131,6 +133,7 @@ { "org.freedesktop.systemd1.Unit", "JobTimeoutUSec", bus_property_append_usec, "t", &u->meta.job_timeout } int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, void *data); +int bus_unit_append_following(Manager *m, DBusMessageIter *i, const char *property, void *data); int bus_unit_append_dependencies(Manager *m, DBusMessageIter *i, const char *property, void *data); int bus_unit_append_description(Manager *m, DBusMessageIter *i, const char *property, void *data); int bus_unit_append_load_state(Manager *m, DBusMessageIter *i, const char *property, void *data); diff --git a/src/device.c b/src/device.c index 39ab291103..526e714378 100644 --- a/src/device.c +++ b/src/device.c @@ -35,12 +35,40 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = { [DEVICE_PLUGGED] = UNIT_ACTIVE }; +static void device_unset_sysfs(Device *d) { + Device *first; + + assert(d); + + if (d->sysfs) { + /* Remove this unit from the chain of devices which share the + * same sysfs path. */ + first = hashmap_get(d->meta.manager->devices_by_sysfs, d->sysfs); + LIST_REMOVE(Device, same_sysfs, first, d); + + if (first) + hashmap_remove_and_replace(d->meta.manager->devices_by_sysfs, d->sysfs, first->sysfs, first); + else + hashmap_remove(d->meta.manager->devices_by_sysfs, d->sysfs); + + free(d->sysfs); + d->sysfs = NULL; + } + + d->meta.following = NULL; +} + static void device_init(Unit *u) { Device *d = DEVICE(u); assert(d); assert(d->meta.load_state == UNIT_STUB); + /* In contrast to all other unit types we timeout jobs waiting + * for devices by default. This is because they otherwise wait + * indefinetely for plugged in devices, something which cannot + * happen for the other units since their operations time out + * anyway. */ d->meta.job_timeout = DEFAULT_TIMEOUT_USEC; } @@ -49,8 +77,7 @@ static void device_done(Unit *u) { assert(d); - free(d->sysfs); - d->sysfs = NULL; + device_unset_sysfs(d); } static void device_set_state(Device *d, DeviceState state) { @@ -105,7 +132,7 @@ static const char *device_sub_state_to_string(Unit *u) { return device_state_to_string(DEVICE(u)->state); } -static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) { +static int device_add_escaped_name(Unit *u, const char *dn) { char *e; int r; @@ -117,10 +144,6 @@ static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) { return -ENOMEM; r = unit_add_name(u, e); - - if (r >= 0 && make_id) - unit_choose_id(u, e); - free(e); if (r < 0 && r != -EEXIST) @@ -152,13 +175,118 @@ static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) { return 0; } -static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) { - const char *dn, *wants, *sysfs, *model, *alias; +static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) { + const char *sysfs, *model; Unit *u = NULL; int r; - char *w, *state; - size_t l; bool delete; + + assert(m); + + if (!(sysfs = udev_device_get_syspath(dev))) + return -ENOMEM; + + if ((r = device_find_escape_name(m, path, &u)) < 0) + return r; + + /* If this is a different unit, then let's not merge things */ + if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs)) { + log_error("Hmm, something's broken. Asked to create two devices with same name but different sysfs paths."); + return -EEXIST; + } + + if (!u) { + Device *first; + delete = true; + + if (!(u = unit_new(m))) + return -ENOMEM; + + if ((r = device_add_escaped_name(u, path)) < 0) + goto fail; + + if (!(DEVICE(u)->sysfs = strdup(sysfs))) { + r = -ENOMEM; + goto fail; + } + + unit_add_to_load_queue(u); + + if (!m->devices_by_sysfs) + if (!(m->devices_by_sysfs = hashmap_new(string_hash_func, string_compare_func))) { + r = -ENOMEM; + goto fail; + } + + first = hashmap_get(m->devices_by_sysfs, sysfs); + LIST_PREPEND(Device, same_sysfs, first, DEVICE(u)); + + if ((r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first)) < 0) + goto fail; + + } else + delete = false; + + if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) || + (model = udev_device_get_property_value(dev, "ID_MODEL"))) { + if ((r = unit_set_description(u, model)) < 0) + goto fail; + } else + if ((r = unit_set_description(u, path)) < 0) + goto fail; + + if (main) { + /* The additional systemd udev properties we only + * interpret for the main object */ + const char *wants, *alias; + + if ((alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"))) { + if (!is_path(alias)) + log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias); + else { + if ((r = device_add_escaped_name(u, alias)) < 0) + goto fail; + } + } + + if ((wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"))) { + char *state, *w; + size_t l; + + FOREACH_WORD_QUOTED(w, l, wants, state) { + char *e; + + if (!(e = strndup(w, l))) { + r = -ENOMEM; + goto fail; + } + + r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true); + free(e); + + if (r < 0) + goto fail; + } + } + + u->meta.following = NULL; + } else + device_find_escape_name(m, sysfs, &u->meta.following); + + unit_add_to_dbus_queue(u); + return 0; + +fail: + log_warning("Failed to load device unit: %s", strerror(-r)); + + if (delete && u) + unit_free(u); + + return r; +} + +static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) { + const char *sysfs, *dn; struct udev_list_entry *item = NULL, *first = NULL; assert(m); @@ -166,126 +294,39 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u if (!(sysfs = udev_device_get_syspath(dev))) return -ENOMEM; - /* Check whether this entry is even relevant for us. */ - dn = udev_device_get_devnode(dev); - wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"); - alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"); + /* Add the main unit named after the sysfs path */ + device_update_unit(m, dev, sysfs, true); - /* We allow exactly one alias to be configured a this time and - * it must be a path */ - - if (alias && !is_path(alias)) { - log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias); - alias = NULL; - } - - if ((r = device_find_escape_name(m, sysfs, &u)) < 0) - return r; - - if (r == 0 && dn) - if ((r = device_find_escape_name(m, dn, &u)) < 0) - return r; - - if (r == 0) { - first = udev_device_get_devlinks_list_entry(dev); - udev_list_entry_foreach(item, first) { - if ((r = device_find_escape_name(m, udev_list_entry_get_name(item), &u)) < 0) - return r; - - if (r > 0) - break; - } - } - - if (r == 0 && alias) - if ((r = device_find_escape_name(m, alias, &u)) < 0) - return r; - - /* FIXME: this needs proper merging */ - - assert((r > 0) == !!u); - - /* If this is a different unit, then let's not merge things */ - if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs)) - u = NULL; - - if (!u) { - delete = true; - - if (!(u = unit_new(m))) - return -ENOMEM; - - if ((r = device_add_escaped_name(u, sysfs, true)) < 0) - goto fail; - - unit_add_to_load_queue(u); - } else - delete = false; - - if (!(DEVICE(u)->sysfs)) - if (!(DEVICE(u)->sysfs = strdup(sysfs))) { - r = -ENOMEM; - goto fail; - } - - if (alias) - if ((r = device_add_escaped_name(u, alias, true)) < 0) - goto fail; - - if (dn) - if ((r = device_add_escaped_name(u, dn, true)) < 0) - goto fail; + /* Add an additional unit for the device node */ + if ((dn = udev_device_get_devnode(dev))) + device_update_unit(m, dev, dn, false); + /* Add additional units for all symlinks */ first = udev_device_get_devlinks_list_entry(dev); - udev_list_entry_foreach(item, first) - if ((r = device_add_escaped_name(u, udev_list_entry_get_name(item), false)) < 0) - goto fail; + udev_list_entry_foreach(item, first) { + const char *p; - if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) || - (model = udev_device_get_property_value(dev, "ID_MODEL"))) { - if ((r = unit_set_description(u, model)) < 0) - goto fail; - } else if (dn) { - if ((r = unit_set_description(u, dn)) < 0) - goto fail; - } else - if ((r = unit_set_description(u, sysfs)) < 0) - goto fail; + /* Don't bother with the /dev/block links */ + p = udev_list_entry_get_name(item); - if (wants) { - FOREACH_WORD_QUOTED(w, l, wants, state) { - char *e; + if (path_startswith(p, "/dev/block/") || + path_startswith(p, "/dev/char/")) + continue; - if (!(e = strndup(w, l))) { - r = -ENOMEM; - goto fail; - } - - r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true); - free(e); - - if (r < 0) - goto fail; - } + device_update_unit(m, dev, p, false); } if (update_state) { - manager_dispatch_load_queue(u->meta.manager); - device_set_state(DEVICE(u), DEVICE_PLUGGED); + Device *d, *l; + + manager_dispatch_load_queue(m); + + l = hashmap_get(m->devices_by_sysfs, sysfs); + LIST_FOREACH(same_sysfs, d, l) + device_set_state(d, DEVICE_PLUGGED); } - unit_add_to_dbus_queue(u); - return 0; - -fail: - - log_warning("Failed to load device unit: %s", strerror(-r)); - - if (delete && u) - unit_free(u); - - return r; } static int device_process_path(Manager *m, const char *path, bool update_state) { @@ -307,8 +348,6 @@ static int device_process_path(Manager *m, const char *path, bool update_state) static int device_process_removed_device(Manager *m, struct udev_device *dev) { const char *sysfs; - char *e; - Unit *u; Device *d; assert(m); @@ -317,21 +356,12 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) { if (!(sysfs = udev_device_get_syspath(dev))) return -ENOMEM; - assert(sysfs[0] == '/'); - if (!(e = unit_name_from_path(sysfs, ".device"))) - return -ENOMEM; + /* Remove all units of this sysfs path */ + while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) { + device_unset_sysfs(d); + device_set_state(d, DEVICE_DEAD); + } - u = manager_get_unit(m, e); - free(e); - - if (!u) - return 0; - - d = DEVICE(u); - free(d->sysfs); - d->sysfs = NULL; - - device_set_state(d, DEVICE_DEAD); return 0; } @@ -347,6 +377,9 @@ static void device_shutdown(Manager *m) { udev_unref(m->udev); m->udev = NULL; } + + hashmap_free(m->devices_by_sysfs); + m->devices_by_sysfs = NULL; } static int device_enumerate(Manager *m) { @@ -464,6 +497,7 @@ const UnitVTable device_vtable = { .no_instances = true, .no_snapshots = true, .no_isolate = true, + .no_alias = true, .init = device_init, diff --git a/src/device.h b/src/device.h index 654499cfd7..5757c9139d 100644 --- a/src/device.h +++ b/src/device.h @@ -41,6 +41,12 @@ struct Device { DeviceState state; char *sysfs; + + /* In order to be able to distuingish dependencies on + different device nodes we might end up creating multiple + devices for the same sysfs path. We chain them up here. */ + + LIST_FIELDS(struct Device, same_sysfs); }; extern const UnitVTable device_vtable; diff --git a/src/hashmap.c b/src/hashmap.c index 5a993b6e47..a59b880dff 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -296,6 +296,33 @@ int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, return 0; } +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { + struct hashmap_entry *e, *k; + unsigned old_hash, new_hash; + + if (!h) + return -ENOENT; + + old_hash = h->hash_func(old_key) % NBUCKETS; + if (!(e = hash_scan(h, old_hash, old_key))) + return -ENOENT; + + new_hash = h->hash_func(new_key) % NBUCKETS; + + if ((k = hash_scan(h, new_hash, new_key))) + if (e != k) + remove_entry(h, k); + + unlink_entry(h, e, old_hash); + + e->key = new_key; + e->value = value; + + link_entry(h, e, new_hash); + + return 0; +} + void* hashmap_remove_value(Hashmap *h, const void *key, void *value) { struct hashmap_entry *e; unsigned hash; diff --git a/src/hashmap.h b/src/hashmap.h index 3ff3efe8d1..8fb7f82357 100644 --- a/src/hashmap.h +++ b/src/hashmap.h @@ -56,6 +56,7 @@ void* hashmap_get(Hashmap *h, const void *key); void* hashmap_remove(Hashmap *h, const void *key); void* hashmap_remove_value(Hashmap *h, const void *key, void *value); int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); int hashmap_merge(Hashmap *h, Hashmap *other); void hashmap_move(Hashmap *h, Hashmap *other); diff --git a/src/manager.h b/src/manager.h index 96de120d31..7328724b09 100644 --- a/src/manager.h +++ b/src/manager.h @@ -146,6 +146,7 @@ struct Manager { struct udev* udev; struct udev_monitor* udev_monitor; Watch udev_watch; + Hashmap *devices_by_sysfs; /* Data specific to the mount subsystem */ FILE *proc_self_mountinfo; diff --git a/src/path.h b/src/path.h index e0feeea711..5c06de4dfe 100644 --- a/src/path.h +++ b/src/path.h @@ -45,16 +45,18 @@ typedef enum PathType { } PathType; typedef struct PathSpec { - PathType type; char *path; - int inotify_fd; - int primary_wd; - bool previous_exists; - Watch watch; LIST_FIELDS(struct PathSpec, spec); + + PathType type; + int inotify_fd; + int primary_wd; + + bool previous_exists; + } PathSpec; struct Path { @@ -62,9 +64,10 @@ struct Path { LIST_HEAD(PathSpec, specs); - PathState state, deserialized_state; Unit *unit; + PathState state, deserialized_state; + bool failure; }; diff --git a/src/service.h b/src/service.h index d254044316..0ddaaa4c2d 100644 --- a/src/service.h +++ b/src/service.h @@ -90,8 +90,6 @@ struct Service { ServiceType type; ServiceRestart restart; - NotifyAccess notify_access; - /* If set we'll read the main daemon PID from this file */ char *pid_file; @@ -101,10 +99,6 @@ struct Service { ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX]; ExecContext exec_context; - bool permissions_start_only; - bool root_directory_start_only; - bool valid_no_process; - ServiceState state, deserialized_state; ExecStatus main_exec_status; @@ -112,6 +106,11 @@ struct Service { ExecCommand *control_command; ServiceExecCommand control_command_id; pid_t main_pid, control_pid; + + bool permissions_start_only; + bool root_directory_start_only; + bool valid_no_process; + bool main_pid_known:1; /* If we shut down, remember why */ @@ -124,8 +123,11 @@ struct Service { bool got_socket_fd:1; bool sysv_has_lsb:1; - char *sysv_path; + + int socket_fd; int sysv_start_priority; + + char *sysv_path; char *sysv_runlevels; char *bus_name; @@ -134,10 +136,11 @@ struct Service { RateLimit ratelimit; - int socket_fd; struct Socket *socket; Watch timer_watch; + + NotifyAccess notify_access; }; extern const UnitVTable service_vtable; diff --git a/src/systemctl.c b/src/systemctl.c index acb89a5a87..bdfd0cdf87 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -58,6 +58,7 @@ static bool arg_no_sync = false; static bool arg_no_wall = false; static bool arg_dry = false; static bool arg_quiet = false; +static char arg_full = false; static char **arg_wall = NULL; static enum action { ACTION_INVALID, @@ -192,7 +193,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION"); while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *id, *description, *load_state, *active_state, *sub_state, *unit_path, *job_type, *job_path, *dot; + const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path, *job_type, *job_path, *dot; uint32_t job_id; if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { @@ -208,6 +209,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 || bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 || bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 || bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0 || bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &job_id, true) < 0 || bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &job_type, true) < 0 || @@ -219,14 +221,16 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { if ((!arg_type || ((dot = strrchr(id, '.')) && streq(dot+1, arg_type))) && - (arg_all || !streq(active_state, "inactive") || job_id > 0)) { - + (arg_all || !(streq(active_state, "inactive") || following[0]) || job_id > 0)) { + char *e; int a = 0, b = 0; if (streq(active_state, "maintenance")) fputs(ANSI_HIGHLIGHT_ON, stdout); - printf("%-45s %-6s %-12s %-12s%n", id, load_state, active_state, sub_state, &a); + e = arg_full ? NULL : ellipsize(id, 45, 33); + printf("%-45s %-6s %-12s %-12s%n", e ? e : id, load_state, active_state, sub_state, &a); + free(e); if (job_id != 0) printf(" => %-12s%n", job_type, &b); @@ -558,6 +562,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { const char *name, *type, *state, *job_path, *unit_path; uint32_t id; + char *e; if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { log_error("Failed to parse reply."); @@ -578,7 +583,10 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { goto finish; } - printf("%4u %-45s %-17s %-7s\n", id, name, type, state); + e = arg_full ? NULL : ellipsize(name, 45, 33); + printf("%4u %-45s %-17s %-7s\n", id, e ? e : name, type, state); + free(e); + k++; dbus_message_iter_next(&sub); @@ -2895,6 +2903,7 @@ static int systemctl_help(void) { " -t --type=TYPE List only units of a particular type\n" " -p --property=NAME Show only properties by this name\n" " -a --all Show all units/properties, including dead/empty ones\n" + " --full Don't ellipsize unit names.\n" " --fail When installing a new job, fail if conflicting jobs are\n" " pending\n" " --system Connect to system bus\n" @@ -3022,7 +3031,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_NO_BLOCK, ARG_NO_WALL, ARG_ORDER, - ARG_REQUIRE + ARG_REQUIRE, + ARG_FULL }; static const struct option options[] = { @@ -3030,6 +3040,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "type", required_argument, NULL, 't' }, { "property", required_argument, NULL, 'p' }, { "all", no_argument, NULL, 'a' }, + { "full", no_argument, NULL, ARG_FULL }, { "fail", no_argument, NULL, ARG_FAIL }, { "session", no_argument, NULL, ARG_SESSION }, { "system", no_argument, NULL, ARG_SYSTEM }, @@ -3099,6 +3110,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_dot = DOT_REQUIRE; break; + case ARG_FULL: + arg_full = true; + break; + case 'q': arg_quiet = true; break; diff --git a/src/systemd-interfaces.vala b/src/systemd-interfaces.vala index 7445479911..612cb13228 100644 --- a/src/systemd-interfaces.vala +++ b/src/systemd-interfaces.vala @@ -28,6 +28,7 @@ public interface Manager : DBus.Object { string load_state; string active_state; string sub_state; + string following; ObjectPath unit_path; uint32 job_id; string job_type; diff --git a/src/unit.c b/src/unit.c index 5807e4f4d1..50f3b8fabd 100644 --- a/src/unit.c +++ b/src/unit.c @@ -625,6 +625,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { SET_FOREACH(t, u->meta.names, i) fprintf(f, "%s\tName: %s\n", prefix, t); + if (u->meta.following) + fprintf(f, "%s\tFollowing: %s\n", prefix, u->meta.following->meta.id); + if (u->meta.fragment_path) fprintf(f, "%s\tFragment Path: %s\n", prefix, u->meta.fragment_path); diff --git a/src/unit.h b/src/unit.h index 1295d9ff52..f1171270f8 100644 --- a/src/unit.h +++ b/src/unit.h @@ -176,6 +176,9 @@ struct Meta { /* GC queue */ LIST_FIELDS(Meta, gc_queue); + /* This follows another unit in state */ + Unit *following; + /* Used during GC sweeps */ unsigned gc_marker; diff --git a/src/util.c b/src/util.c index 519d22902a..45de609c29 100644 --- a/src/util.c +++ b/src/util.c @@ -2912,6 +2912,38 @@ int running_in_chroot(void) { a.st_ino != b.st_ino; } +char *ellipsize(const char *s, unsigned length, unsigned percent) { + size_t l, x; + char *r; + + assert(s); + assert(percent <= 100); + assert(length >= 3); + + l = strlen(s); + + if (l <= 3 || l <= length) + return strdup(s); + + if (!(r = new0(char, length+1))) + return r; + + x = (length * percent) / 100; + + if (x > length - 3) + x = length - 3; + + memcpy(r, s, x); + r[x] = '.'; + r[x+1] = '.'; + r[x+2] = '.'; + memcpy(r + x + 3, + s + l - (length - x - 3), + length - x - 3); + + return r; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index e4b1f81c06..782adb8348 100644 --- a/src/util.h +++ b/src/util.h @@ -334,6 +334,8 @@ int columns(void); int running_in_chroot(void); +char *ellipsize(const char *s, unsigned length, unsigned percent); + const char *ioprio_class_to_string(int i); int ioprio_class_from_string(const char *s);