Files
David Galiffi 6a960a1edb Added copyright information to requested files (#167)
For SWDEV-526556

[ROCm/rocprofiler-systems commit: b25b6cec92]
2025-04-15 18:39:53 -04:00

497 行
12 KiB
C++

// MIT License
//
// Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <initializer_list>
#include <iomanip>
#include <iostream>
#include <linux/capability.h>
#include <sstream>
#include <string>
#include <string_view>
#include <unistd.h>
#include <vector>
using cap_value_t = int;
struct cap_info
{
const char* name = nullptr;
cap_value_t value = -1;
};
struct cap_status
{
unsigned long long inherited = 0;
unsigned long long permitted = 0;
unsigned long long effective = 0;
unsigned long long bounding = 0;
unsigned long long ambient = 0;
};
#define CAP_INFO_ENTRY(VAL) \
cap_info { #VAL, VAL }
namespace
{
std::initializer_list<cap_info> known_capabilities = {
#if defined(CAP_CHOWN)
CAP_INFO_ENTRY(CAP_CHOWN),
#endif
#if defined(CAP_DAC_OVERRIDE)
CAP_INFO_ENTRY(CAP_DAC_OVERRIDE),
#endif
#if defined(CAP_DAC_READ_SEARCH)
CAP_INFO_ENTRY(CAP_DAC_READ_SEARCH),
#endif
#if defined(CAP_FOWNER)
CAP_INFO_ENTRY(CAP_FOWNER),
#endif
#if defined(CAP_FSETID)
CAP_INFO_ENTRY(CAP_FSETID),
#endif
#if defined(CAP_KILL)
CAP_INFO_ENTRY(CAP_KILL),
#endif
#if defined(CAP_SETGID)
CAP_INFO_ENTRY(CAP_SETGID),
#endif
#if defined(CAP_SETUID)
CAP_INFO_ENTRY(CAP_SETUID),
#endif
#if defined(CAP_SETPCAP)
CAP_INFO_ENTRY(CAP_SETPCAP),
#endif
#if defined(CAP_LINUX_IMMUTABLE)
CAP_INFO_ENTRY(CAP_LINUX_IMMUTABLE),
#endif
#if defined(CAP_NET_BIND_SERVICE)
CAP_INFO_ENTRY(CAP_NET_BIND_SERVICE),
#endif
#if defined(CAP_NET_BROADCAST)
CAP_INFO_ENTRY(CAP_NET_BROADCAST),
#endif
#if defined(CAP_NET_ADMIN)
CAP_INFO_ENTRY(CAP_NET_ADMIN),
#endif
#if defined(CAP_NET_RAW)
CAP_INFO_ENTRY(CAP_NET_RAW),
#endif
#if defined(CAP_IPC_LOCK)
CAP_INFO_ENTRY(CAP_IPC_LOCK),
#endif
#if defined(CAP_IPC_OWNER)
CAP_INFO_ENTRY(CAP_IPC_OWNER),
#endif
#if defined(CAP_SYS_MODULE)
CAP_INFO_ENTRY(CAP_SYS_MODULE),
#endif
#if defined(CAP_SYS_RAWIO)
CAP_INFO_ENTRY(CAP_SYS_RAWIO),
#endif
#if defined(CAP_SYS_CHROOT)
CAP_INFO_ENTRY(CAP_SYS_CHROOT),
#endif
#if defined(CAP_SYS_PTRACE)
CAP_INFO_ENTRY(CAP_SYS_PTRACE),
#endif
#if defined(CAP_SYS_PACCT)
CAP_INFO_ENTRY(CAP_SYS_PACCT),
#endif
#if defined(CAP_SYS_ADMIN)
CAP_INFO_ENTRY(CAP_SYS_ADMIN),
#endif
#if defined(CAP_SYS_BOOT)
CAP_INFO_ENTRY(CAP_SYS_BOOT),
#endif
#if defined(CAP_SYS_NICE)
CAP_INFO_ENTRY(CAP_SYS_NICE),
#endif
#if defined(CAP_SYS_RESOURCE)
CAP_INFO_ENTRY(CAP_SYS_RESOURCE),
#endif
#if defined(CAP_SYS_TIME)
CAP_INFO_ENTRY(CAP_SYS_TIME),
#endif
#if defined(CAP_SYS_TTY_CONFIG)
CAP_INFO_ENTRY(CAP_SYS_TTY_CONFIG),
#endif
#if defined(CAP_MKNOD)
CAP_INFO_ENTRY(CAP_MKNOD),
#endif
#if defined(CAP_LEASE)
CAP_INFO_ENTRY(CAP_LEASE),
#endif
#if defined(CAP_AUDIT_WRITE)
CAP_INFO_ENTRY(CAP_AUDIT_WRITE),
#endif
#if defined(CAP_AUDIT_CONTROL)
CAP_INFO_ENTRY(CAP_AUDIT_CONTROL),
#endif
#if defined(CAP_SETFCAP)
CAP_INFO_ENTRY(CAP_SETFCAP),
#endif
#if defined(CAP_MAC_OVERRIDE)
CAP_INFO_ENTRY(CAP_MAC_OVERRIDE),
#endif
#if defined(CAP_MAC_ADMIN)
CAP_INFO_ENTRY(CAP_MAC_ADMIN),
#endif
#if defined(CAP_SYSLOG)
CAP_INFO_ENTRY(CAP_SYSLOG),
#endif
#if defined(CAP_WAKE_ALARM)
CAP_INFO_ENTRY(CAP_WAKE_ALARM),
#endif
#if defined(CAP_BLOCK_SUSPEND)
CAP_INFO_ENTRY(CAP_BLOCK_SUSPEND),
#endif
#if defined(CAP_AUDIT_READ)
CAP_INFO_ENTRY(CAP_AUDIT_READ),
#endif
#if defined(CAP_PERFMON)
CAP_INFO_ENTRY(CAP_PERFMON),
#endif
#if defined(CAP_BPF)
CAP_INFO_ENTRY(CAP_BPF),
#endif
#if defined(CAP_CHECKPOINT_RESTORE)
CAP_INFO_ENTRY(CAP_CHECKPOINT_RESTORE),
#endif
#if defined(CAP_LAST_CAP)
CAP_INFO_ENTRY(CAP_LAST_CAP),
#endif
};
auto cap_max_bits_v = []() {
unsigned _value = 0;
for(const auto& itr : known_capabilities)
_value = std::max<unsigned>(_value, itr.value + 1);
return _value;
}();
std::string
to_lower(std::string&& _s)
{
for(auto& citr : _s)
citr = tolower(citr);
return _s;
}
std::string
to_upper(std::string _s)
{
for(auto& citr : _s)
citr = toupper(citr);
return _s;
}
cap_status
cap_read(pid_t _pid)
{
auto fname = std::string{ "/proc/" } + std::to_string(_pid) + "/status";
auto ifs = std::ifstream{ fname };
if(!ifs) return cap_status{};
auto _lines = std::vector<std::string>{};
while(ifs && ifs.good())
{
auto _line = std::string{};
std::getline(ifs, _line);
if(ifs && ifs.good() && !_line.empty()) _lines.emplace_back(std::move(_line));
}
auto _data = cap_status{};
for(const auto& itr : _lines)
{
auto iss = std::istringstream{ itr };
auto _key = std::string{};
auto _value = std::string{};
iss >> _key;
if(_key.find("Cap") == 0) iss >> _value;
if(!_value.empty())
{
auto _key_matches = [&_key](std::string_view _cap_id_str) {
return (_key.find(_cap_id_str) == 0);
};
if(_key_matches("CapInh"))
_data.inherited = std::stoull(_value, nullptr, 16);
else if(_key_matches("CapPrm"))
_data.permitted = std::stoull(_value, nullptr, 16);
else if(_key_matches("CapEff"))
_data.effective = std::stoull(_value, nullptr, 16);
else if(_key_matches("CapBnd"))
_data.bounding = std::stoull(_value, nullptr, 16);
else if(_key_matches("CapAmb"))
_data.ambient = std::stoull(_value, nullptr, 16);
}
}
return _data;
}
std::string
cap_name(cap_value_t _v)
{
for(const auto& itr : known_capabilities)
if(itr.value == _v) return to_lower(std::string{ itr.name });
return std::string{};
}
std::vector<cap_value_t>
decode(unsigned long long value)
{
auto _data = std::vector<cap_value_t>{};
for(unsigned cap = 0; (cap < 64) && ((value >> cap) != 0U); ++cap)
{
auto _mask = value & (1ULL << cap);
if(_mask != 0U)
{
if(cap >= 0 && cap < cap_max_bits_v) _data.emplace_back(cap);
}
}
return _data;
}
std::vector<cap_value_t>
decode(const char* arg)
{
return decode(std::strtoull(arg, nullptr, 16));
}
/*
template <typename Tp>
std::string
as_hex(Tp _v, size_t _width = 16)
{
std::stringstream _ss;
_ss.fill('0');
_ss << "0x" << std::hex << std::setw(_width) << _v;
return _ss.str();
}
void run(std::string&& arg)
{
if(arg.find("0x") == 0)
arg = arg.substr(2);
arg.insert(0, "0x");
// arg = std::string{ "0x" } + arg;
std::cout << arg << "=";
auto _decoded = decode(arg.c_str());
auto _msg = std::stringstream{};
for(auto&& itr : _decoded)
{
_msg << "," << cap_name(itr);
}
auto _msg_v = _msg.str();
if(!_msg_v.empty())
{
std::cout << _msg_v.substr(1);
}
std::cout << "\n";
}
void
run(std::string_view _label, unsigned long long arg)
{
std::cout << " " << std::setw(12) << _label << " ";
run(as_hex(arg));
}
*/
} // namespace
int
main(int argc, char** argv)
{
const auto* _usage = R"usage(
usage: rocprof-sys-capchk <capability-name> <capability-set> <pid>
Description:
Simple tool for checking the effective capabilities of a running process
Arguments:
capability-name (string):
case-insensitive string matching CAP_* fields defined in `man 7 capabilities`
capability-set (string; optional):
Choices:
- effective (default)
- permitted
- inherited
- bounding
- ambient
See `man 7 capabilities` for more info
pid (numeric process-identifier; optional):
target process identifier for capability query. If not specified, queries the
capabilities of the current process (this exe).
Exit value:
0 if the process has the specified capability in the specified set
1 if the process does not have the capability
2 if the capability name is not supported
3 if the capability set name is not supported
Examples:
$ rocprof-sys-capchk CAP_SYS_ADMIN
Check if this exe (self) has CAP_SYS_ADMIN capability in the (default) effective capability set
$ rocprof-sys-capchk sys_admin bounding 423032
Check if process 423032 has CAP_SYS_ADMIN capability in the bounding capability set
)usage";
std::string capability_name = {};
std::string capability_mode = "effective";
pid_t target_pid = getpid();
for(int i = 1; i < argc; ++i)
{
auto arg = std::string_view{ argv[i] };
if(arg == "-h" || arg == "--help" || arg == "-?")
{
std::cout << _usage << "\n";
return EXIT_SUCCESS;
}
}
if(argc > 1) capability_name = to_lower(std::string{ argv[1] });
if(argc > 2) capability_mode = to_lower(std::string{ argv[2] });
if(argc > 3)
{
auto _pid_s = to_lower(std::string{ argv[3] });
if(_pid_s != "self") target_pid = std::stoul(argv[3]);
}
if(capability_name.find("cap_") != 0) capability_name.insert(0, "cap_");
capability_name = to_upper(capability_name);
const cap_info* _info = nullptr;
for(const auto& itr : known_capabilities)
{
if(capability_name == std::string_view{ itr.name })
{
_info = &itr;
break;
}
}
if(!_info)
{
fprintf(stderr, "Error! invalid capability: %s\n", capability_name.c_str());
return EXIT_FAILURE + 1;
}
auto _status = cap_read(target_pid);
auto* _dataset = &_status.effective;
/*
std::cout << "pid=" << target_pid << ":\n";
run("inherited", _status.inherited);
run("permitted", _status.permitted);
run("effective", _status.effective);
run("bounding", _status.bounding);
run("ambient", _status.ambient);
*/
if(capability_mode == "effective")
_dataset = &_status.effective;
else if(capability_mode == "permitted")
_dataset = &_status.permitted;
else if(capability_mode == "inherited")
_dataset = &_status.inherited;
else if(capability_mode == "bounding")
_dataset = &_status.bounding;
else if(capability_mode == "ambient")
_dataset = &_status.ambient;
else
{
fprintf(stderr, "Error! invalid capability set: %s\n", capability_mode.c_str());
return EXIT_FAILURE + 2;
}
auto _ec = EXIT_FAILURE;
for(auto&& itr : decode(*_dataset))
{
if(itr == _info->value)
{
_ec = EXIT_SUCCESS;
break;
}
}
std::cout << ((_ec == EXIT_SUCCESS) ? "Found" : "Missing") << " capability "
<< capability_name << " in " << capability_mode
<< " capability set. Exit code: " << _ec << ".\n";
return _ec;
}