Merge pull request #24033 from dtardon/list-automounts
Add `systemctl list-automounts`
This commit is contained in:
@@ -108,6 +108,25 @@ To show all installed unit files use 'systemctl list-unit-files'.</programlistin
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>list-automounts</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
|
||||
|
||||
<listitem>
|
||||
<para>List automount units currently in memory, ordered by mount path. If one or more
|
||||
<replaceable>PATTERN</replaceable>s are specified, only automount units matching one of them are shown.
|
||||
Produces output similar to
|
||||
<programlisting>
|
||||
WHAT WHERE MOUNTED IDLE TIMEOUT UNIT
|
||||
/dev/sdb1 /mnt/test no 120s mnt-test.automount
|
||||
binfmt_misc /proc/sys/fs/binfmt_misc yes 0 proc-sys-fs-binfmt_misc.automount
|
||||
|
||||
2 automounts listed.</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Also see <option>--show-types</option>, <option>--all</option>, and <option>--state=</option>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>list-sockets</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ _systemctl () {
|
||||
suspend-then-hibernate kexec list-jobs list-sockets
|
||||
list-timers list-units list-unit-files poweroff
|
||||
reboot rescue show-environment suspend get-default
|
||||
is-system-running preset-all'
|
||||
is-system-running preset-all list-automounts'
|
||||
[FILE]='link switch-root bind mount-image'
|
||||
[TARGETS]='set-default'
|
||||
[MACHINES]='list-machines'
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
local -a unit_commands=(
|
||||
# Unit Commands
|
||||
"list-automounts:List automounts"
|
||||
"list-sockets:List sockets"
|
||||
"list-timers:List timers"
|
||||
"list-units:List units"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "sd-login.h"
|
||||
|
||||
#include "bus-error.h"
|
||||
#include "bus-locator.h"
|
||||
#include "format-table.h"
|
||||
#include "locale-util.h"
|
||||
#include "set.h"
|
||||
@@ -87,6 +88,19 @@ static int get_unit_list_recursive(
|
||||
return c;
|
||||
}
|
||||
|
||||
static void output_legend(const char *type, size_t n_items) {
|
||||
const char *on, *off;
|
||||
|
||||
assert(type);
|
||||
|
||||
on = n_items > 0 ? ansi_highlight() : ansi_highlight_red();
|
||||
off = ansi_normal();
|
||||
|
||||
printf("\n%s%zu %ss listed.%s\n", on, n_items, type, off);
|
||||
if (!arg_all)
|
||||
printf("Pass --all to see loaded but inactive %ss, too.\n", type);
|
||||
}
|
||||
|
||||
static int output_units_list(const UnitInfo *unit_infos, size_t c) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
size_t job_count = 0;
|
||||
@@ -187,13 +201,11 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) {
|
||||
"SUB = The low-level unit activation state, values depend on unit type.");
|
||||
if (job_count > 0)
|
||||
puts("JOB = Pending job for the unit.\n");
|
||||
on = ansi_highlight();
|
||||
off = ansi_normal();
|
||||
} else {
|
||||
on = ansi_highlight_red();
|
||||
off = ansi_normal();
|
||||
}
|
||||
|
||||
on = records > 0 ? ansi_highlight() : ansi_highlight_red();
|
||||
off = ansi_normal();
|
||||
|
||||
if (arg_all || strv_contains(arg_states, "inactive"))
|
||||
printf("%s%zu loaded units listed.%s\n"
|
||||
"To show all installed unit files use 'systemctl list-unit-files'.\n",
|
||||
@@ -350,9 +362,10 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
|
||||
|
||||
static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
const char *on, *off;
|
||||
int r;
|
||||
|
||||
assert(socket_infos || cs == 0);
|
||||
|
||||
table = table_new("listen", "type", "unit", "activates");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
@@ -370,56 +383,44 @@ static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
|
||||
|
||||
(void) table_set_empty_string(table, "-");
|
||||
|
||||
if (cs > 0) {
|
||||
for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
const char *path;
|
||||
for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
const char *path;
|
||||
|
||||
if (s->machine) {
|
||||
j = strjoin(s->machine, ":", s->path);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
path = j;
|
||||
} else
|
||||
path = s->path;
|
||||
if (s->machine) {
|
||||
j = strjoin(s->machine, ":", s->path);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
path = j;
|
||||
} else
|
||||
path = s->path;
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, path,
|
||||
TABLE_STRING, s->type,
|
||||
TABLE_STRING, s->id);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, path,
|
||||
TABLE_STRING, s->type,
|
||||
TABLE_STRING, s->id);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
if (strv_isempty(s->triggered))
|
||||
r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
|
||||
else if (strv_length(s->triggered) == 1)
|
||||
r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]);
|
||||
else
|
||||
/* This should never happen, currently our socket units can only trigger a
|
||||
* single unit. But let's handle this anyway, who knows what the future
|
||||
* brings? */
|
||||
r = table_add_cell(table, NULL, TABLE_STRV, s->triggered);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
}
|
||||
|
||||
on = ansi_highlight();
|
||||
off = ansi_normal();
|
||||
} else {
|
||||
on = ansi_highlight_red();
|
||||
off = ansi_normal();
|
||||
if (strv_isempty(s->triggered))
|
||||
r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
|
||||
else if (strv_length(s->triggered) == 1)
|
||||
r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]);
|
||||
else
|
||||
/* This should never happen, currently our socket units can only trigger a
|
||||
* single unit. But let's handle this anyway, who knows what the future
|
||||
* brings? */
|
||||
r = table_add_cell(table, NULL, TABLE_STRV, s->triggered);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
r = output_table(table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_legend != 0) {
|
||||
printf("\n%s%zu sockets listed.%s\n", on, cs, off);
|
||||
if (!arg_all)
|
||||
printf("Pass --all to see loaded but inactive sockets, too.\n");
|
||||
}
|
||||
if (arg_legend != 0)
|
||||
output_legend("socket", cs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -599,7 +600,6 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
|
||||
|
||||
static int output_timers_list(struct timer_info *timer_infos, size_t n) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
const char *on, *off;
|
||||
int r;
|
||||
|
||||
assert(timer_infos || n == 0);
|
||||
@@ -641,23 +641,12 @@ static int output_timers_list(struct timer_info *timer_infos, size_t n) {
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
on = ansi_highlight();
|
||||
off = ansi_normal();
|
||||
} else {
|
||||
on = ansi_highlight_red();
|
||||
off = ansi_normal();
|
||||
}
|
||||
|
||||
r = output_table(table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_legend != 0) {
|
||||
printf("\n%s%zu timers listed.%s\n", on, n, off);
|
||||
if (!arg_all)
|
||||
printf("Pass --all to see loaded but inactive timers, too.\n");
|
||||
}
|
||||
if (arg_legend != 0)
|
||||
output_legend("timer", n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -760,3 +749,186 @@ int verb_list_timers(int argc, char *argv[], void *userdata) {
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
struct automount_info {
|
||||
const char *machine;
|
||||
const char *id;
|
||||
char *what;
|
||||
char *where;
|
||||
usec_t timeout_idle_usec;
|
||||
bool mounted;
|
||||
};
|
||||
|
||||
static int automount_info_compare(const struct automount_info *a, const struct automount_info *b) {
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
r = strcasecmp_ptr(a->machine, b->machine);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return strcmp(a->where, b->where);
|
||||
}
|
||||
|
||||
static int collect_automount_info(sd_bus* bus, const UnitInfo* info, struct automount_info *ret_info) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_free_ char *mount = NULL, *mount_path = NULL, *where = NULL, *what = NULL, *state = NULL;
|
||||
usec_t timeout_idle_usec;
|
||||
BusLocator locator;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(info);
|
||||
assert(ret_info);
|
||||
|
||||
locator = (BusLocator) {
|
||||
.destination = "org.freedesktop.systemd1",
|
||||
.path = info->unit_path,
|
||||
.interface = "org.freedesktop.systemd1.Automount",
|
||||
};
|
||||
|
||||
r = bus_get_property_string(bus, &locator, "Where", &error, &where);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get automount target: %s", bus_error_message(&error, r));
|
||||
|
||||
r = bus_get_property_trivial(bus, &locator, "TimeoutIdleUSec", &error, 't', &timeout_idle_usec);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get idle timeout: %s", bus_error_message(&error, r));
|
||||
|
||||
r = unit_name_from_path(where, ".mount", &mount);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate unit name from path: %m");
|
||||
|
||||
mount_path = unit_dbus_path_from_name(mount);
|
||||
if (!mount_path)
|
||||
return log_oom();
|
||||
|
||||
locator.path = mount_path;
|
||||
locator.interface = "org.freedesktop.systemd1.Mount";
|
||||
|
||||
r = bus_get_property_string(bus, &locator, "What", &error, &what);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get mount source: %s", bus_error_message(&error, r));
|
||||
|
||||
locator.interface = "org.freedesktop.systemd1.Unit";
|
||||
|
||||
r = bus_get_property_string(bus, &locator, "ActiveState", &error, &state);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get mount state: %s", bus_error_message(&error, r));
|
||||
|
||||
*ret_info = (struct automount_info) {
|
||||
.machine = info->machine,
|
||||
.id = info->id,
|
||||
.what = TAKE_PTR(what),
|
||||
.where = TAKE_PTR(where),
|
||||
.timeout_idle_usec = timeout_idle_usec,
|
||||
.mounted = streq_ptr(state, "active"),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int output_automounts_list(struct automount_info *infos, size_t n_infos) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
int r;
|
||||
|
||||
assert(infos || n_infos == 0);
|
||||
|
||||
table = table_new("what", "where", "mounted", "idle timeout", "unit");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
table_set_header(table, arg_legend != 0);
|
||||
if (arg_full)
|
||||
table_set_width(table, 0);
|
||||
|
||||
(void) table_set_empty_string(table, "-");
|
||||
|
||||
for (struct automount_info *info = infos; info < infos + n_infos; info++) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
const char *unit;
|
||||
|
||||
if (info->machine) {
|
||||
j = strjoin(info->machine, ":", info->id);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
unit = j;
|
||||
} else
|
||||
unit = info->id;
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, info->what,
|
||||
TABLE_STRING, info->where,
|
||||
TABLE_BOOLEAN, info->mounted,
|
||||
TABLE_TIMESPAN_MSEC, info->timeout_idle_usec,
|
||||
TABLE_STRING, unit);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
r = output_table(table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_legend != 0)
|
||||
output_legend("automount", n_infos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verb_list_automounts(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(message_set_freep) Set *replies = NULL;
|
||||
_cleanup_strv_free_ char **machines = NULL, **automounts = NULL;
|
||||
_cleanup_free_ UnitInfo *unit_infos = NULL;
|
||||
_cleanup_free_ struct automount_info *automount_infos = NULL;
|
||||
size_t c = 0;
|
||||
int r, n;
|
||||
sd_bus *bus;
|
||||
|
||||
r = acquire_bus(BUS_MANAGER, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
r = expand_unit_names(bus, strv_skip(argv, 1), ".automount", &automounts, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (argc == 1 || automounts) {
|
||||
n = get_unit_list_recursive(bus, automounts, &unit_infos, &replies, &machines);
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
|
||||
if (!endswith(u->id, ".automount"))
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(automount_infos, c + 1)) {
|
||||
r = log_oom();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
r = collect_automount_info(bus, u, &automount_infos[c]);
|
||||
if (r < 0)
|
||||
goto cleanup;
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
typesafe_qsort(automount_infos, c, automount_info_compare);
|
||||
}
|
||||
|
||||
output_automounts_list(automount_infos, c);
|
||||
|
||||
cleanup:
|
||||
assert(c == 0 || automount_infos);
|
||||
for (struct automount_info *info = automount_infos; info < automount_infos + c; info++) {
|
||||
free(info->what);
|
||||
free(info->where);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
int verb_list_units(int argc, char *argv[], void *userdata);
|
||||
int verb_list_sockets(int argc, char *argv[], void *userdata);
|
||||
int verb_list_timers(int argc, char *argv[], void *userdata);
|
||||
int verb_list_automounts(int argc, char *argv[], void *userdata);
|
||||
|
||||
usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next);
|
||||
|
||||
@@ -138,6 +138,8 @@ static int systemctl_help(void) {
|
||||
"%5$sQuery or send control commands to the system manager.%6$s\n"
|
||||
"\n%3$sUnit Commands:%4$s\n"
|
||||
" list-units [PATTERN...] List units currently in memory\n"
|
||||
" list-automounts [PATTERN...] List automount units currently in memory,\n"
|
||||
" ordered by path\n"
|
||||
" list-sockets [PATTERN...] List socket units currently in memory,\n"
|
||||
" ordered by address\n"
|
||||
" list-timers [PATTERN...] List timer units currently in memory,\n"
|
||||
@@ -1022,6 +1024,7 @@ static int systemctl_main(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, verb_list_units },
|
||||
{ "list-unit-files", VERB_ANY, VERB_ANY, 0, verb_list_unit_files },
|
||||
{ "list-automounts", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_automounts },
|
||||
{ "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_sockets },
|
||||
{ "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_timers },
|
||||
{ "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_jobs },
|
||||
|
||||
Reference in New Issue
Block a user