diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index 67c7e07778a..91e7e1eda5c 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -80,7 +80,16 @@
systemd-analyze
OPTIONS
capability
- CAPABILITY
+
+ CAPABILITY
+
+
+ -m
+ --mask
+
+ MASK
+
+
systemd-analyze
@@ -441,7 +450,20 @@ DATAERR 65 BSD
- systemd-analyze capability CAPABILITY...
+
+ systemd-analyze capability
+
+ CAPABILITY
+
+
+ -m
+ --mask
+
+ MASK
+
+
+
+
This command prints a list of Linux capabilities along with their numeric IDs. See capabilities7
@@ -451,6 +473,11 @@ DATAERR 65 BSD
cabilities by name or numeric ID, in which case only the indicated capabilities are shown in the
table.
+ Alternatively, if is passed, a single numeric argument must be specified,
+ which is interpreted as a hexadecimal capability mask. In this case, only the capabilities present in
+ the mask are shown in the table. This mode is intended to aid in decoding capability sets available
+ via various debugging interfaces (e.g. /proc/PID/status).
+
Show some example capability names
@@ -462,6 +489,18 @@ cap_audit_control 30
cap_setfcap 31
cap_mac_override 32
+
+
+ Decode a capability mask extracted from /proc
+
+ $ systemd-analyze capability -m 0000000000003c00
+NAME NUMBER
+cap_net_bind_service 10
+cap_net_broadcast 11
+cap_net_admin 12
+cap_net_raw 13
+
+
diff --git a/src/analyze/analyze-capability.c b/src/analyze/analyze-capability.c
index 07ac725a691..3e9b918ed4f 100644
--- a/src/analyze/analyze-capability.c
+++ b/src/analyze/analyze-capability.c
@@ -5,6 +5,18 @@
#include "cap-list.h"
#include "capability-util.h"
#include "format-table.h"
+#include "parse-util.h"
+
+static int table_add_capability(Table *table, int c) {
+ int r;
+
+ r = table_add_many(table,
+ TABLE_STRING, capability_to_name(c) ?: "cap_???",
+ TABLE_UINT, c);
+ if (r < 0)
+ return table_log_add_error(r);
+ return 0;
+}
int verb_capabilities(int argc, char *argv[], void *userdata) {
_cleanup_(table_unrefp) Table *table = NULL;
@@ -20,15 +32,38 @@ int verb_capabilities(int argc, char *argv[], void *userdata) {
/* Determine the maximum of the last cap known by the kernel and by us */
last_cap = MAX((unsigned) CAP_LAST_CAP, cap_last_cap());
- if (strv_isempty(strv_skip(argv, 1)))
- for (unsigned c = 0; c <= last_cap; c++) {
- r = table_add_many(table,
- TABLE_STRING, capability_to_name(c) ?: "cap_???",
- TABLE_UINT, c);
- if (r < 0)
- return table_log_add_error(r);
+ if (arg_capability == CAPABILITY_MASK) {
+ uint64_t cap_mask;
+
+ if (argc != 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Exactly 1 positional argument expected.");
+
+ r = safe_atoux64(argv[1], &cap_mask);
+ if (r < 0)
+ return log_error_errno(r, "Capability mask \"%s\" is not valid.", argv[1]);
+
+ for (unsigned c = 0; cap_mask != 0; ) {
+ if (cap_mask & 0b1) {
+ if (c > last_cap)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability %u is not known.", c);
+
+ r = table_add_capability(table, c);
+ if (r < 0)
+ return r;
+ }
+ ++c;
+ cap_mask >>= 1;
}
- else {
+
+ (void) table_set_sort(table, (size_t) 1);
+
+ } else if (argc == 1) {
+ for (unsigned c = 0; c <= last_cap; c++) {
+ r = table_add_capability(table, c);
+ if (r < 0)
+ return r;
+ }
+ } else {
for (int i = 1; i < argc; i++) {
int c;
@@ -36,11 +71,9 @@ int verb_capabilities(int argc, char *argv[], void *userdata) {
if (c < 0 || (unsigned) c > last_cap)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability \"%s\" is not known.", argv[i]);
- r = table_add_many(table,
- TABLE_STRING, capability_to_name(c) ?: "cap_???",
- TABLE_UINT, (unsigned) c);
+ r = table_add_capability(table, c);
if (r < 0)
- return table_log_add_error(r);
+ return r;
}
(void) table_set_sort(table, (size_t) 1);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 2b4babc5c9a..c9675d42586 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -91,6 +91,7 @@
#include "verbs.h"
DotMode arg_dot = DEP_ALL;
+CapabilityMode arg_capability = CAPABILITY_LITERAL;
char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL;
usec_t arg_fuzz = 0;
PagerFlags arg_pager_flags = 0;
@@ -358,6 +359,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "table", optional_argument, NULL, ARG_TABLE },
{ "no-legend", optional_argument, NULL, ARG_NO_LEGEND },
{ "tldr", no_argument, NULL, ARG_TLDR },
+ { "mask", no_argument, NULL, 'm' },
{}
};
@@ -366,7 +368,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:U:q", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hqH:M:U:m", options, NULL)) >= 0)
switch (c) {
case 'h':
@@ -551,6 +553,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_cat_flags = CAT_TLDR;
break;
+ case 'm':
+ arg_capability = CAPABILITY_MASK;
+ break;
+
case '?':
return -EINVAL;
@@ -614,6 +620,9 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_table && !FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--table and --json= are mutually exclusive.");
+ if (arg_capability != CAPABILITY_LITERAL && !streq_ptr(argv[optind], "capability"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --mask is only supported for capability.");
+
return 1; /* work to do */
}
diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h
index a6920b76532..a93603243c3 100644
--- a/src/analyze/analyze.h
+++ b/src/analyze/analyze.h
@@ -18,7 +18,13 @@ typedef enum DotMode {
DEP_REQUIRE,
} DotMode;
+typedef enum CapabilityMode {
+ CAPABILITY_LITERAL,
+ CAPABILITY_MASK,
+} CapabilityMode;
+
extern DotMode arg_dot;
+extern CapabilityMode arg_capability;
extern char **arg_dot_from_patterns, **arg_dot_to_patterns;
extern usec_t arg_fuzz;
extern PagerFlags arg_pager_flags;
diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh
index fde15192f78..236c27e33b5 100755
--- a/test/units/TEST-65-ANALYZE.sh
+++ b/test/units/TEST-65-ANALYZE.sh
@@ -105,6 +105,15 @@ systemd-analyze capability cap_chown CAP_KILL
systemd-analyze capability 0 1 {30..32}
(! systemd-analyze capability cap_chown CAP_KILL "hello*")
(! systemd-analyze capability --global)
+systemd-analyze capability -m 0000000000003c00
+(! systemd-analyze capability -m 8000000000000000)
+cap="$(systemd-analyze capability -m 0000000000003c00)"
+[[ $cap != *cap_linux_immutable* ]]
+[[ $cap == *cap_net_bind_service* ]]
+[[ $cap == *cap_net_broadcast* ]]
+[[ $cap == *cap_net_admin* ]]
+[[ $cap == *cap_net_raw* ]]
+[[ $cap != *cap_ipc_lock* ]]
# condition
mkdir -p /run/systemd/system
UNIT_NAME="analyze-condition-$RANDOM.service"