1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

analyze: capability: add support for decoding capability masks

This adds support in `systemd-analyze capability` for decoding
capability masks (sets), e.g.:

```console
$ systemd-analyze capability --mask 0000000000003c00
NAME                 NUMBER
cap_net_bind_service     10
cap_net_broadcast        11
cap_net_admin            12
cap_net_raw              13
```

This is intended as a convenience tool for pretty-printing capability
values as found in e.g. `/proc/$PID/status`.
This commit is contained in:
Ivan Shapovalov 2024-07-15 11:47:25 +02:00
parent daa453acbe
commit 3e7a029c28
5 changed files with 111 additions and 15 deletions

View File

@ -80,7 +80,16 @@
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">capability</arg>
<arg choice="opt" rep="repeat"><replaceable>CAPABILITY</replaceable></arg>
<group choice="opt">
<arg choice="plain" rep="repeat"><replaceable>CAPABILITY</replaceable></arg>
<arg choice="plain">
<group choice="req">
<arg choice="plain">-m</arg>
<arg choice="plain">--mask</arg>
</group>
<replaceable>MASK</replaceable>
</arg>
</group>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
@ -441,7 +450,20 @@ DATAERR 65 BSD
</refsect2>
<refsect2>
<title><command>systemd-analyze capability <optional><replaceable>CAPABILITY</replaceable>...</optional></command></title>
<title>
<command>systemd-analyze capability
<group choice="opt">
<arg choice="plain" rep="repeat"><replaceable>CAPABILITY</replaceable></arg>
<arg choice="plain">
<group choice="req">
<arg choice="plain">-m</arg>
<arg choice="plain">--mask</arg>
</group>
<replaceable>MASK</replaceable>
</arg>
</group>
</command>
</title>
<para>This command prints a list of Linux capabilities along with their numeric IDs. See <citerefentry
project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
@ -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.</para>
<para>Alternatively, if <option>--mask</option> 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. <literal>/proc/PID/status</literal>).</para>
<example>
<title><command>Show some example capability names</command></title>
@ -462,6 +489,18 @@ cap_audit_control 30
cap_setfcap 31
cap_mac_override 32</programlisting>
</example>
<example>
<title><command>Decode a capability mask extracted from /proc</command></title>
<programlisting>$ systemd-analyze capability -m 0000000000003c00
NAME NUMBER
cap_net_bind_service 10
cap_net_broadcast 11
cap_net_admin 12
cap_net_raw 13
</programlisting>
</example>
</refsect2>
<refsect2>

View File

@ -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);

View File

@ -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 */
}

View File

@ -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;

View File

@ -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"