summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dbus-manager.c8
-rw-r--r--src/dbus-unit.c17
-rw-r--r--src/dbus-unit.h3
-rw-r--r--src/device.c242
-rw-r--r--src/device.h6
-rw-r--r--src/hashmap.c27
-rw-r--r--src/hashmap.h1
-rw-r--r--src/manager.h1
-rw-r--r--src/path.h13
-rw-r--r--src/service.h19
-rw-r--r--src/systemctl.c27
-rw-r--r--src/systemd-interfaces.vala1
-rw-r--r--src/unit.c3
-rw-r--r--src/unit.h3
-rw-r--r--src/util.c32
-rw-r--r--src/util.h2
16 files changed, 279 insertions, 126 deletions
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 @@
" <method name=\"ClearJobs\"/>\n" \
" <method name=\"ResetMaintenance\"/>\n" \
" <method name=\"ListUnits\">\n" \
- " <arg name=\"units\" type=\"a(sssssouso)\" direction=\"out\"/>\n" \
+ " <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListJobs\">\n" \
" <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\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 @@
" <signal name=\"Changed\"/>\n" \
" <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Names\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Following\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Requires\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequiresOverridable\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Requisite\" type=\"as\" access=\"read\"/>\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,134 +175,108 @@ 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;
- struct udev_list_entry *item = NULL, *first = NULL;
assert(m);
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");
-
- /* 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)
+ if ((r = device_find_escape_name(m, path, &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 && 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, sysfs, true)) < 0)
+ if ((r = device_add_escaped_name(u, path)) < 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;
+ unit_add_to_load_queue(u);
- if (dn)
- if ((r = device_add_escaped_name(u, dn, true)) < 0)
- goto fail;
+ if (!m->devices_by_sysfs)
+ if (!(m->devices_by_sysfs = hashmap_new(string_hash_func, string_compare_func))) {
+ r = -ENOMEM;
+ goto fail;
+ }
- 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)
+ 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 (dn) {
- if ((r = unit_set_description(u, dn)) < 0)
- goto fail;
} else
- if ((r = unit_set_description(u, sysfs)) < 0)
+ if ((r = unit_set_description(u, path)) < 0)
goto fail;
- if (wants) {
- FOREACH_WORD_QUOTED(w, l, wants, state) {
- char *e;
-
- if (!(e = strndup(w, l))) {
- r = -ENOMEM;
- 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;
}
+ }
- r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
- free(e);
+ if ((wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"))) {
+ char *state, *w;
+ size_t l;
- if (r < 0)
- goto fail;
+ 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;
+ }
}
- }
- if (update_state) {
- manager_dispatch_load_queue(u->meta.manager);
- device_set_state(DEVICE(u), DEVICE_PLUGGED);
- }
+ 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)
@@ -288,6 +285,50 @@ fail:
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);
+
+ if (!(sysfs = udev_device_get_syspath(dev)))
+ return -ENOMEM;
+
+ /* Add the main unit named after the sysfs path */
+ device_update_unit(m, dev, sysfs, true);
+
+ /* 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) {
+ const char *p;
+
+ /* Don't bother with the /dev/block links */
+ p = udev_list_entry_get_name(item);
+
+ if (path_startswith(p, "/dev/block/") ||
+ path_startswith(p, "/dev/char/"))
+ continue;
+
+ device_update_unit(m, dev, p, false);
+ }
+
+ if (update_state) {
+ 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);
+ }
+
+ return 0;
+}
+
static int device_process_path(Manager *m, const char *path, bool update_state) {
int r;
struct udev_device *dev;
@@ -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;
-
- u = manager_get_unit(m, e);
- free(e);
-
- if (!u)
- return 0;
-
- d = DEVICE(u);
- free(d->sysfs);
- d->sysfs = NULL;
+ /* 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);
+ }
- 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;
+ Watch watch;
+
+ LIST_FIELDS(struct PathSpec, spec);
+
+ PathType type;
int inotify_fd;
int primary_wd;
- bool previous_exists;
- Watch watch;
+ bool previous_exists;
- LIST_FIELDS(struct PathSpec, spec);
} 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);