diff options
Diffstat (limited to 'tools/perf/util/cap.c')
| -rw-r--r-- | tools/perf/util/cap.c | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/tools/perf/util/cap.c b/tools/perf/util/cap.c new file mode 100644 index 000000000000..24a0ea7e6d97 --- /dev/null +++ b/tools/perf/util/cap.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Capability utilities + */ + +#include "cap.h" +#include "debug.h" +#include <errno.h> +#include <string.h> +#include <sys/syscall.h> +#include <unistd.h> + +#define MAX_LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3 + +bool perf_cap__capable(int cap, bool *used_root) +{ + struct __user_cap_header_struct header = { + .version = _LINUX_CAPABILITY_VERSION_3, + .pid = 0, + }; + struct __user_cap_data_struct data[MAX_LINUX_CAPABILITY_U32S] = {}; + __u32 cap_val; + + *used_root = false; + while (syscall(SYS_capget, &header, &data[0]) == -1) { + /* Retry, first attempt has set the header.version correctly. */ + if (errno == EINVAL && header.version != _LINUX_CAPABILITY_VERSION_3 && + header.version == _LINUX_CAPABILITY_VERSION_1) + continue; + + pr_debug2("capget syscall failed (%s - %d) fall back on root check\n", + strerror(errno), errno); + *used_root = true; + return geteuid() == 0; + } + + /* Extract the relevant capability bit. */ + if (cap >= 32) { + if (header.version == _LINUX_CAPABILITY_VERSION_3) { + cap_val = data[1].effective; + } else { + /* Capability beyond 32 is requested but only 32 are supported. */ + return false; + } + } else { + cap_val = data[0].effective; + } + return (cap_val & (1 << (cap & 0x1f))) != 0; +} |
