276 lines
10 KiB
C
276 lines
10 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include "bus-error.h"
|
|
#include "bus-locator.h"
|
|
#include "sort-util.h"
|
|
#include "systemctl-list-unit-files.h"
|
|
#include "systemctl-util.h"
|
|
#include "systemctl.h"
|
|
#include "terminal-util.h"
|
|
|
|
static int compare_unit_file_list(const UnitFileList *a, const UnitFileList *b) {
|
|
const char *d1, *d2;
|
|
|
|
d1 = strrchr(a->path, '.');
|
|
d2 = strrchr(b->path, '.');
|
|
|
|
if (d1 && d2) {
|
|
int r;
|
|
|
|
r = strcasecmp(d1, d2);
|
|
if (r != 0)
|
|
return r;
|
|
}
|
|
|
|
return strcasecmp(basename(a->path), basename(b->path));
|
|
}
|
|
|
|
static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) {
|
|
assert(u);
|
|
|
|
if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE))
|
|
return false;
|
|
|
|
if (!strv_isempty(arg_types)) {
|
|
const char *dot;
|
|
|
|
dot = strrchr(u->path, '.');
|
|
if (!dot)
|
|
return false;
|
|
|
|
if (!strv_find(arg_types, dot+1))
|
|
return false;
|
|
}
|
|
|
|
if (!strv_isempty(states) &&
|
|
!strv_find(states, unit_file_state_to_string(u->state)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int output_unit_file_list(const UnitFileList *units, unsigned c) {
|
|
_cleanup_(table_unrefp) Table *table = NULL;
|
|
_cleanup_(unit_file_presets_freep) UnitFilePresets presets = {};
|
|
int r;
|
|
|
|
table = table_new("unit file", "state", "vendor preset");
|
|
if (!table)
|
|
return log_oom();
|
|
|
|
table_set_header(table, !arg_no_legend);
|
|
if (arg_full)
|
|
table_set_width(table, 0);
|
|
|
|
(void) table_set_empty_string(table, "-");
|
|
|
|
for (const UnitFileList *u = units; u < units + c; u++) {
|
|
const char *on_underline = NULL, *on_unit_color = NULL, *id;
|
|
bool underline;
|
|
|
|
underline = u + 1 < units + c &&
|
|
!streq(unit_type_suffix(u->path), unit_type_suffix((u + 1)->path));
|
|
|
|
if (underline)
|
|
on_underline = ansi_underline();
|
|
|
|
if (IN_SET(u->state,
|
|
UNIT_FILE_MASKED,
|
|
UNIT_FILE_MASKED_RUNTIME,
|
|
UNIT_FILE_DISABLED,
|
|
UNIT_FILE_BAD))
|
|
on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
|
|
else if (IN_SET(u->state,
|
|
UNIT_FILE_ENABLED,
|
|
UNIT_FILE_ALIAS))
|
|
on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
|
|
else
|
|
on_unit_color = on_underline;
|
|
|
|
id = basename(u->path);
|
|
|
|
r = table_add_many(table,
|
|
TABLE_STRING, id,
|
|
TABLE_SET_BOTH_COLORS, strempty(on_underline),
|
|
TABLE_STRING, unit_file_state_to_string(u->state),
|
|
TABLE_SET_BOTH_COLORS, strempty(on_unit_color));
|
|
if (r < 0)
|
|
return table_log_add_error(r);
|
|
|
|
if (show_preset_for_state(u->state)) {
|
|
const char *unit_preset_str, *on_preset_color;
|
|
|
|
r = unit_file_query_preset(arg_scope, arg_root, id, &presets);
|
|
if (r < 0) {
|
|
unit_preset_str = "n/a";
|
|
on_preset_color = underline ? on_underline : ansi_normal();
|
|
} else if (r == 0) {
|
|
unit_preset_str = "disabled";
|
|
on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
|
|
} else {
|
|
unit_preset_str = "enabled";
|
|
on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
|
|
}
|
|
|
|
r = table_add_many(table,
|
|
TABLE_STRING, unit_preset_str,
|
|
TABLE_SET_BOTH_COLORS, strempty(on_preset_color));
|
|
} else
|
|
r = table_add_many(table,
|
|
TABLE_EMPTY,
|
|
TABLE_SET_BOTH_COLORS, underline ? ansi_grey_underline() : ansi_grey());
|
|
if (r < 0)
|
|
return table_log_add_error(r);
|
|
}
|
|
|
|
r = output_table(table);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!arg_no_legend)
|
|
printf("\n%u unit files listed.\n", c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int list_unit_files(int argc, char *argv[], void *userdata) {
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
_cleanup_free_ UnitFileList *units = NULL;
|
|
UnitFileList *unit;
|
|
size_t size = 0;
|
|
unsigned c = 0;
|
|
const char *state;
|
|
char *path;
|
|
int r;
|
|
bool fallback = false;
|
|
|
|
if (install_client_side()) {
|
|
Hashmap *h;
|
|
UnitFileList *u;
|
|
unsigned n_units;
|
|
|
|
h = hashmap_new(&string_hash_ops);
|
|
if (!h)
|
|
return log_oom();
|
|
|
|
r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1));
|
|
if (r < 0) {
|
|
unit_file_list_free(h);
|
|
return log_error_errno(r, "Failed to get unit file list: %m");
|
|
}
|
|
|
|
n_units = hashmap_size(h);
|
|
|
|
units = new(UnitFileList, n_units ?: 1); /* avoid malloc(0) */
|
|
if (!units) {
|
|
unit_file_list_free(h);
|
|
return log_oom();
|
|
}
|
|
|
|
HASHMAP_FOREACH(u, h) {
|
|
if (!output_show_unit_file(u, NULL, NULL))
|
|
continue;
|
|
|
|
units[c++] = *u;
|
|
free(u);
|
|
}
|
|
|
|
assert(c <= n_units);
|
|
hashmap_free(h);
|
|
|
|
r = 0;
|
|
} else {
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
sd_bus *bus;
|
|
|
|
r = acquire_bus(BUS_MANAGER, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFilesByPatterns");
|
|
if (r < 0)
|
|
return bus_log_create_error(r);
|
|
|
|
r = sd_bus_message_append_strv(m, arg_states);
|
|
if (r < 0)
|
|
return bus_log_create_error(r);
|
|
|
|
if (arg_with_dependencies) {
|
|
_cleanup_strv_free_ char **names_with_deps = NULL;
|
|
|
|
r = append_unit_dependencies(bus, strv_skip(argv, 1), &names_with_deps);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to append unit dependencies: %m");
|
|
|
|
r = sd_bus_message_append_strv(m, names_with_deps);
|
|
if (r < 0)
|
|
return bus_log_create_error(r);
|
|
} else {
|
|
r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
|
|
if (r < 0)
|
|
return bus_log_create_error(r);
|
|
}
|
|
|
|
r = sd_bus_call(bus, m, 0, &error, &reply);
|
|
if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
|
|
/* Fallback to legacy ListUnitFiles method */
|
|
fallback = true;
|
|
log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r));
|
|
m = sd_bus_message_unref(m);
|
|
sd_bus_error_free(&error);
|
|
|
|
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFiles");
|
|
if (r < 0)
|
|
return bus_log_create_error(r);
|
|
|
|
r = sd_bus_call(bus, m, 0, &error, &reply);
|
|
}
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r));
|
|
|
|
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)");
|
|
if (r < 0)
|
|
return bus_log_parse_error(r);
|
|
|
|
while ((r = sd_bus_message_read(reply, "(ss)", &path, &state)) > 0) {
|
|
|
|
if (!GREEDY_REALLOC(units, size, c + 1))
|
|
return log_oom();
|
|
|
|
units[c] = (struct UnitFileList) {
|
|
path,
|
|
unit_file_state_from_string(state)
|
|
};
|
|
|
|
if (output_show_unit_file(&units[c],
|
|
fallback ? arg_states : NULL,
|
|
fallback ? strv_skip(argv, 1) : NULL))
|
|
c++;
|
|
|
|
}
|
|
if (r < 0)
|
|
return bus_log_parse_error(r);
|
|
|
|
r = sd_bus_message_exit_container(reply);
|
|
if (r < 0)
|
|
return bus_log_parse_error(r);
|
|
}
|
|
|
|
(void) pager_open(arg_pager_flags);
|
|
|
|
typesafe_qsort(units, c, compare_unit_file_list);
|
|
r = output_unit_file_list(units, c);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (install_client_side())
|
|
for (unit = units; unit < units + c; unit++)
|
|
free(unit->path);
|
|
|
|
if (c == 0)
|
|
return -ENOENT;
|
|
|
|
return 0;
|
|
}
|