Move libcap use to (Linux) platform-specific code

The libcap code is Linux-specific so move it all below
the linux/ platform subdirectory.  As this feature has
custom command-line long options I provide a mechanism
whereby each platform can add custom long options that
augment the main htop options.  We'll make use this of
this with the pcp/ platform in due course to implement
the --host and --archive options there.

Related to https://github.com/htop-dev/htop/pull/536
This commit is contained in:
Nathan Scott 2021-03-17 14:29:40 +11:00
parent 57e0ce7b4f
commit 0ada9f325f
9 changed files with 220 additions and 164 deletions

View File

@ -77,4 +77,8 @@ static inline void Platform_getRelease(char** string) {
*string = Generic_uname(); *string = Generic_uname();
} }
static bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
return false;
}
#endif #endif

View File

@ -67,4 +67,8 @@ static inline void Platform_getRelease(char** string) {
*string = Generic_uname(); *string = Generic_uname();
} }
static bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
return false;
}
#endif #endif

View File

@ -72,4 +72,8 @@ static inline void Platform_getRelease(char** string) {
*string = Generic_uname(); *string = Generic_uname();
} }
static bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
return false;
}
#endif #endif

168
htop.c
View File

@ -1,6 +1,7 @@
/* /*
htop - htop.c htop - htop.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
(C) 2020-2021 htop dev team
Released under the GNU GPLv2, see the COPYING file Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
@ -8,7 +9,6 @@ in the source distribution for its full text.
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <locale.h> #include <locale.h>
#include <stdbool.h> #include <stdbool.h>
@ -35,37 +35,28 @@ in the source distribution for its full text.
#include "UsersTable.h" #include "UsersTable.h"
#include "XUtils.h" #include "XUtils.h"
#ifdef HAVE_LIBCAP
#include <sys/capability.h> #ifndef PLATFORM_LONG_OPTIONS
#define PLATFORM_LONG_OPTIONS
#endif #endif
#ifndef PLATFORM_LONG_OPTIONS_USAGE
#ifdef HAVE_LIBCAP #define PLATFORM_LONG_OPTIONS_USAGE
enum CapMode {
CAP_MODE_NONE,
CAP_MODE_BASIC,
CAP_MODE_STRICT
};
#endif #endif
static void printVersionFlag(void) { static void printVersionFlag(const char* name) {
fputs(PACKAGE " " VERSION "\n", stdout); fprintf(stdout, "%s " VERSION "\n", name);
} }
static void printHelpFlag(void) { static void printHelpFlag(const char* name) {
fputs(PACKAGE " " VERSION "\n" fprintf(stdout, "%s " VERSION "\n"
COPYRIGHT "\n" COPYRIGHT "\n"
"Released under the GNU GPLv2.\n\n" "Released under the GNU GPLv2.\n\n"
"-C --no-color Use a monochrome color scheme\n" "-C --no-color Use a monochrome color scheme\n"
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n" "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-F --filter=FILTER Show only the commands matching the given filter\n" "-F --filter=FILTER Show only the commands matching the given filter\n"
"-h --help Print this help screen\n" "-h --help Print this help screen\n"
#ifdef HAVE_LIBCAP PLATFORM_LONG_OPTIONS_USAGE
" --drop-capabilities[=none|basic|strict] Drop Linux capabilities when running as root\n"
" none - do not drop any capabilities\n"
" basic (default) - drop all capabilities not needed by htop\n"
" strict - drop all capabilities except those needed for core functionality\n"
#endif
"-H --highlight-changes[=DELAY] Highlight new and old processes\n" "-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"-M --no-mouse Disable the mouse\n" "-M --no-mouse Disable the mouse\n"
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n" "-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
@ -76,9 +67,9 @@ static void printHelpFlag(void) {
"-V --version Print version info\n" "-V --version Print version info\n"
"\n" "\n"
"Long options may be passed with a single dash.\n\n" "Long options may be passed with a single dash.\n\n"
"Press F1 inside " PACKAGE " for online help.\n" "Press F1 inside %s for online help.\n"
"See 'man " PACKAGE "' for more information.\n", "See 'man %s' for more information.\n",
stdout); name, name, name);
} }
// ---------------------------------------- // ----------------------------------------
@ -95,12 +86,9 @@ typedef struct CommandLineSettings_ {
bool allowUnicode; bool allowUnicode;
bool highlightChanges; bool highlightChanges;
int highlightDelaySecs; int highlightDelaySecs;
#ifdef HAVE_LIBCAP
enum CapMode capabilitiesMode;
#endif
} CommandLineSettings; } CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) { static CommandLineSettings parseArguments(const char* program, int argc, char** argv) {
CommandLineSettings flags = { CommandLineSettings flags = {
.pidMatchList = NULL, .pidMatchList = NULL,
@ -114,9 +102,6 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.allowUnicode = true, .allowUnicode = true,
.highlightChanges = false, .highlightChanges = false,
.highlightDelaySecs = -1, .highlightDelaySecs = -1,
#ifdef HAVE_LIBCAP
.capabilitiesMode = CAP_MODE_BASIC,
#endif
}; };
const struct option long_opts[] = const struct option long_opts[] =
@ -134,9 +119,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"pid", required_argument, 0, 'p'}, {"pid", required_argument, 0, 'p'},
{"filter", required_argument, 0, 'F'}, {"filter", required_argument, 0, 'F'},
{"highlight-changes", optional_argument, 0, 'H'}, {"highlight-changes", optional_argument, 0, 'H'},
#ifdef HAVE_LIBCAP PLATFORM_LONG_OPTIONS
{"drop-capabilities", optional_argument, 0, 128},
#endif
{0,0,0,0} {0,0,0,0}
}; };
@ -146,10 +129,10 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
if (opt == EOF) break; if (opt == EOF) break;
switch (opt) { switch (opt) {
case 'h': case 'h':
printHelpFlag(); printHelpFlag(program);
exit(0); exit(0);
case 'V': case 'V':
printVersionFlag(); printVersionFlag(program);
exit(0); exit(0);
case 's': case 's':
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */ assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
@ -255,29 +238,11 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
flags.highlightChanges = true; flags.highlightChanges = true;
break; break;
} }
#ifdef HAVE_LIBCAP
case 128: {
const char* mode = optarg;
if (!mode && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
mode = argv[optind++];
}
if (!mode || String_eq(mode, "basic")) {
flags.capabilitiesMode = CAP_MODE_BASIC;
} else if (String_eq(mode, "none")) {
flags.capabilitiesMode = CAP_MODE_NONE;
} else if (String_eq(mode, "strict")) {
flags.capabilitiesMode = CAP_MODE_STRICT;
} else {
fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
exit(1);
}
break;
}
#endif
default: default:
if (Platform_getLongOption(opt, argc, argv) == false)
exit(1); exit(1);
break;
} }
} }
return flags; return flags;
@ -304,92 +269,6 @@ static void setCommFilter(State* state, char** commFilter) {
*commFilter = NULL; *commFilter = NULL;
} }
#ifdef HAVE_LIBCAP
static int dropCapabilities(enum CapMode mode) {
if (mode == CAP_MODE_NONE)
return 0;
/* capabilities we keep to operate */
const cap_value_t keepcapsStrict[] = {
CAP_DAC_READ_SEARCH,
CAP_SYS_PTRACE,
};
const cap_value_t keepcapsBasic[] = {
CAP_DAC_READ_SEARCH, /* read non world-readable process files of other users, like /proc/[pid]/io */
CAP_KILL, /* send signals to processes of other users */
CAP_SYS_NICE, /* lower process nice value / change nice value for arbitrary processes */
CAP_SYS_PTRACE, /* read /proc/[pid]/exe */
#ifdef HAVE_DELAYACCT
CAP_NET_ADMIN, /* communicate over netlink socket for delay accounting */
#endif
};
const cap_value_t* const keepcaps = (mode == CAP_MODE_BASIC) ? keepcapsBasic : keepcapsStrict;
const size_t ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
cap_t caps = cap_init();
if (caps == NULL) {
fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
return -1;
}
if (cap_clear(caps) < 0) {
fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
cap_t currCaps = cap_get_proc();
if (currCaps == NULL) {
fprintf(stderr, "Error: can not get current process capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
for (size_t i = 0; i < ncap; i++) {
if (!CAP_IS_SUPPORTED(keepcaps[i]))
continue;
cap_flag_value_t current;
if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, &current) < 0) {
fprintf(stderr, "Error: can not get current value of capability %d: %s\n", keepcaps[i], strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
if (current != CAP_SET)
continue;
if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0) {
fprintf(stderr, "Error: can not set permitted capability %d: %s\n", keepcaps[i], strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0) {
fprintf(stderr, "Error: can not set effective capability %d: %s\n", keepcaps[i], strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
}
if (cap_set_proc(caps) < 0) {
fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
cap_free(currCaps);
cap_free(caps);
return 0;
}
#endif
int main(int argc, char** argv) { int main(int argc, char** argv) {
/* initialize locale */ /* initialize locale */
@ -399,12 +278,7 @@ int main(int argc, char** argv) {
else else
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
CommandLineSettings flags = parseArguments(argc, argv); CommandLineSettings flags = parseArguments(PACKAGE, argc, argv);
#ifdef HAVE_LIBCAP
if (dropCapabilities(flags.capabilitiesMode) < 0)
exit(1);
#endif
Platform_init(); Platform_init();

View File

@ -12,6 +12,7 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <math.h> #include <math.h>
@ -61,11 +62,23 @@ in the source distribution for its full text.
#include "zfs/ZfsArcStats.h" #include "zfs/ZfsArcStats.h"
#include "zfs/ZfsCompressedArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h"
#ifdef HAVE_LIBCAP
#include <sys/capability.h>
#endif
#ifdef HAVE_SENSORS_SENSORS_H #ifdef HAVE_SENSORS_SENSORS_H
#include "LibSensors.h" #include "LibSensors.h"
#endif #endif
#ifdef HAVE_LIBCAP
enum CapMode {
CAP_MODE_NONE,
CAP_MODE_BASIC,
CAP_MODE_STRICT
};
#endif
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
@ -112,22 +125,9 @@ static time_t Platform_Battery_cacheTime;
static double Platform_Battery_cachePercent = NAN; static double Platform_Battery_cachePercent = NAN;
static ACPresence Platform_Battery_cacheIsOnAC; static ACPresence Platform_Battery_cacheIsOnAC;
void Platform_init(void) { #ifdef HAVE_LIBCAP
if (access(PROCDIR, R_OK) != 0) { static enum CapMode Platform_capabilitiesMode = CAP_MODE_BASIC;
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
exit(1);
}
#ifdef HAVE_SENSORS_SENSORS_H
LibSensors_init(NULL);
#endif #endif
}
void Platform_done(void) {
#ifdef HAVE_SENSORS_SENSORS_H
LibSensors_cleanup();
#endif
}
static Htop_Reaction Platform_actionSetIOPriority(State* st) { static Htop_Reaction Platform_actionSetIOPriority(State* st) {
const LinuxProcess* p = (const LinuxProcess*) Panel_getSelected((Panel*)st->mainPanel); const LinuxProcess* p = (const LinuxProcess*) Panel_getSelected((Panel*)st->mainPanel);
@ -845,3 +845,146 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) {
Platform_Battery_cacheIsOnAC = *isOnAC; Platform_Battery_cacheIsOnAC = *isOnAC;
Platform_Battery_cacheTime = now; Platform_Battery_cacheTime = now;
} }
bool Platform_getLongOption(int opt, int argc, char** argv) {
#ifndef HAVE_LIBCAP
(void) argc;
(void) argv;
#endif
switch (opt) {
#ifdef HAVE_LIBCAP
case 128: {
const char* mode = optarg;
if (!mode && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
mode = argv[optind++];
}
if (!mode || String_eq(mode, "basic")) {
Platform_capabilitiesMode = CAP_MODE_BASIC;
} else if (String_eq(mode, "none")) {
Platform_capabilitiesMode = CAP_MODE_NONE;
} else if (String_eq(mode, "strict")) {
Platform_capabilitiesMode = CAP_MODE_STRICT;
} else {
fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
exit(1);
}
break;
}
#endif
default:
break;
}
return false;
}
#ifdef HAVE_LIBCAP
static int dropCapabilities(enum CapMode mode) {
if (mode == CAP_MODE_NONE)
return 0;
/* capabilities we keep to operate */
const cap_value_t keepcapsStrict[] = {
CAP_DAC_READ_SEARCH,
CAP_SYS_PTRACE,
};
const cap_value_t keepcapsBasic[] = {
CAP_DAC_READ_SEARCH, /* read non world-readable process files of other users, like /proc/[pid]/io */
CAP_KILL, /* send signals to processes of other users */
CAP_SYS_NICE, /* lower process nice value / change nice value for arbitrary processes */
CAP_SYS_PTRACE, /* read /proc/[pid]/exe */
#ifdef HAVE_DELAYACCT
CAP_NET_ADMIN, /* communicate over netlink socket for delay accounting */
#endif
};
const cap_value_t* const keepcaps = (mode == CAP_MODE_BASIC) ? keepcapsBasic : keepcapsStrict;
const size_t ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
cap_t caps = cap_init();
if (caps == NULL) {
fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
return -1;
}
if (cap_clear(caps) < 0) {
fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
cap_t currCaps = cap_get_proc();
if (currCaps == NULL) {
fprintf(stderr, "Error: can not get current process capabilities: %s\n", strerror(errno));
cap_free(caps);
return -1;
}
for (size_t i = 0; i < ncap; i++) {
if (!CAP_IS_SUPPORTED(keepcaps[i]))
continue;
cap_flag_value_t current;
if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, &current) < 0) {
fprintf(stderr, "Error: can not get current value of capability %d: %s\n", keepcaps[i], strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
if (current != CAP_SET)
continue;
if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0) {
fprintf(stderr, "Error: can not set permitted capability %d: %s\n", keepcaps[i], strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0) {
fprintf(stderr, "Error: can not set effective capability %d: %s\n", keepcaps[i], strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
}
if (cap_set_proc(caps) < 0) {
fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
cap_free(currCaps);
cap_free(caps);
return -1;
}
cap_free(currCaps);
cap_free(caps);
return 0;
}
#endif
void Platform_init(void) {
#ifdef HAVE_LIBCAP
if (dropCapabilities(Platform_capabilitiesMode) < 0)
exit(1);
#endif
if (access(PROCDIR, R_OK) != 0) {
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
exit(1);
}
#ifdef HAVE_SENSORS_SENSORS_H
LibSensors_init(NULL);
#endif
}
void Platform_done(void) {
#ifdef HAVE_SENSORS_SENSORS_H
LibSensors_cleanup();
#endif
}

View File

@ -27,6 +27,19 @@ in the source distribution for its full text.
#define PATH_MAX 4096 #define PATH_MAX 4096
#endif #endif
#ifdef HAVE_LIBCAP
#define PLATFORM_LONG_OPTIONS \
{"drop-capabilities", optional_argument, 0, 128},
#define PLATFORM_LONG_OPTIONS_USAGE \
" --drop-capabilities[=none|basic|strict] Drop Linux capabilities when running as root\n" \
" none - do not drop any capabilities\n" \
" basic (default) - drop all capabilities not needed by htop\n" \
" strict - drop all capabilities except those needed for core functionality\n"
#else
#define PLATFORM_LONG_OPTIONS
#define PLATFORM_LONG_OPTIONS_USAGE
#endif
extern const ProcessField Platform_defaultFields[]; extern const ProcessField Platform_defaultFields[];
@ -82,4 +95,6 @@ static inline void Platform_getRelease(char** string) {
*string = Generic_uname(); *string = Generic_uname();
} }
bool Platform_getLongOption(int opt, int argc, char** argv);
#endif #endif

View File

@ -70,4 +70,8 @@ static inline void Platform_getRelease(char** string) {
*string = Generic_uname(); *string = Generic_uname();
} }
static bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
return false;
}
#endif #endif

View File

@ -89,4 +89,8 @@ static inline void Platform_getRelease(char** string) {
*string = Generic_uname(); *string = Generic_uname();
} }
static bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
return false;
}
#endif #endif

View File

@ -61,4 +61,8 @@ void Platform_getHostname(char* buffer, size_t size);
void Platform_getRelease(char** string); void Platform_getRelease(char** string);
static bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
return false;
}
#endif #endif