1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-09 01:18:19 +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> <command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">capability</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>
<cmdsynopsis> <cmdsynopsis>
<command>systemd-analyze</command> <command>systemd-analyze</command>
@ -441,7 +450,20 @@ DATAERR 65 BSD
</refsect2> </refsect2>
<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 <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> 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 cabilities by name or numeric ID, in which case only the indicated capabilities are shown in the
table.</para> 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> <example>
<title><command>Show some example capability names</command></title> <title><command>Show some example capability names</command></title>
@ -462,6 +489,18 @@ cap_audit_control 30
cap_setfcap 31 cap_setfcap 31
cap_mac_override 32</programlisting> cap_mac_override 32</programlisting>
</example> </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>
<refsect2> <refsect2>

View File

@ -5,6 +5,18 @@
#include "cap-list.h" #include "cap-list.h"
#include "capability-util.h" #include "capability-util.h"
#include "format-table.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) { int verb_capabilities(int argc, char *argv[], void *userdata) {
_cleanup_(table_unrefp) Table *table = NULL; _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 */ /* Determine the maximum of the last cap known by the kernel and by us */
last_cap = MAX((unsigned) CAP_LAST_CAP, cap_last_cap()); last_cap = MAX((unsigned) CAP_LAST_CAP, cap_last_cap());
if (strv_isempty(strv_skip(argv, 1))) if (arg_capability == CAPABILITY_MASK) {
for (unsigned c = 0; c <= last_cap; c++) { uint64_t cap_mask;
r = table_add_many(table,
TABLE_STRING, capability_to_name(c) ?: "cap_???", if (argc != 2)
TABLE_UINT, c); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Exactly 1 positional argument expected.");
if (r < 0)
return table_log_add_error(r); 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++) { for (int i = 1; i < argc; i++) {
int c; int c;
@ -36,11 +71,9 @@ int verb_capabilities(int argc, char *argv[], void *userdata) {
if (c < 0 || (unsigned) c > last_cap) if (c < 0 || (unsigned) c > last_cap)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability \"%s\" is not known.", argv[i]); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability \"%s\" is not known.", argv[i]);
r = table_add_many(table, r = table_add_capability(table, c);
TABLE_STRING, capability_to_name(c) ?: "cap_???",
TABLE_UINT, (unsigned) c);
if (r < 0) if (r < 0)
return table_log_add_error(r); return r;
} }
(void) table_set_sort(table, (size_t) 1); (void) table_set_sort(table, (size_t) 1);

View File

@ -91,6 +91,7 @@
#include "verbs.h" #include "verbs.h"
DotMode arg_dot = DEP_ALL; DotMode arg_dot = DEP_ALL;
CapabilityMode arg_capability = CAPABILITY_LITERAL;
char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL; char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL;
usec_t arg_fuzz = 0; usec_t arg_fuzz = 0;
PagerFlags arg_pager_flags = 0; PagerFlags arg_pager_flags = 0;
@ -358,6 +359,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "table", optional_argument, NULL, ARG_TABLE }, { "table", optional_argument, NULL, ARG_TABLE },
{ "no-legend", optional_argument, NULL, ARG_NO_LEGEND }, { "no-legend", optional_argument, NULL, ARG_NO_LEGEND },
{ "tldr", no_argument, NULL, ARG_TLDR }, { "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(argc >= 0);
assert(argv); 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) { switch (c) {
case 'h': case 'h':
@ -551,6 +553,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_cat_flags = CAT_TLDR; arg_cat_flags = CAT_TLDR;
break; break;
case 'm':
arg_capability = CAPABILITY_MASK;
break;
case '?': case '?':
return -EINVAL; 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)) 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."); 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 */ return 1; /* work to do */
} }

View File

@ -18,7 +18,13 @@ typedef enum DotMode {
DEP_REQUIRE, DEP_REQUIRE,
} DotMode; } DotMode;
typedef enum CapabilityMode {
CAPABILITY_LITERAL,
CAPABILITY_MASK,
} CapabilityMode;
extern DotMode arg_dot; extern DotMode arg_dot;
extern CapabilityMode arg_capability;
extern char **arg_dot_from_patterns, **arg_dot_to_patterns; extern char **arg_dot_from_patterns, **arg_dot_to_patterns;
extern usec_t arg_fuzz; extern usec_t arg_fuzz;
extern PagerFlags arg_pager_flags; 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 0 1 {30..32}
(! systemd-analyze capability cap_chown CAP_KILL "hello*") (! systemd-analyze capability cap_chown CAP_KILL "hello*")
(! systemd-analyze capability --global) (! 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 # condition
mkdir -p /run/systemd/system mkdir -p /run/systemd/system
UNIT_NAME="analyze-condition-$RANDOM.service" UNIT_NAME="analyze-condition-$RANDOM.service"