Merge pull request #564 from natoscott/platform-options

Move libcap use to (Linux) platform-specific code
This commit is contained in:
Nathan Scott 2021-03-22 17:25:01 +11:00 committed by GitHub
commit 6fd5b05151
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 250 additions and 169 deletions

View File

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

View File

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

View File

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

173
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,19 @@ 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>
#endif
static void printVersionFlag(const char* name) {
#ifdef HAVE_LIBCAP printf("%s " VERSION "\n", name);
enum CapMode {
CAP_MODE_NONE,
CAP_MODE_BASIC,
CAP_MODE_STRICT
};
#endif
static void printVersionFlag(void) {
fputs(PACKAGE " " VERSION "\n", stdout);
} }
static void printHelpFlag(void) { static void printHelpFlag(const char* name) {
fputs(PACKAGE " " VERSION "\n" printf("%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
" --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"
@ -73,12 +55,12 @@ static void printHelpFlag(void) {
"-t --tree Show the tree view (can be combined with -s)\n" "-t --tree Show the tree view (can be combined with -s)\n"
"-u --user[=USERNAME] Show only processes for a given user (or $USER)\n" "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
"-U --no-unicode Do not use unicode but plain ASCII\n" "-U --no-unicode Do not use unicode but plain ASCII\n"
"-V --version Print version info\n" "-V --version Print version info\n", name);
"\n" Platform_longOptionsUsage(name);
printf("\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", name, name);
stdout);
} }
// ---------------------------------------- // ----------------------------------------
@ -95,12 +77,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 +93,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 +110,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 +120,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 */
@ -218,7 +192,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
char* saveptr; char* saveptr;
const char* pid = strtok_r(argCopy, ",", &saveptr); const char* pid = strtok_r(argCopy, ",", &saveptr);
if(!flags.pidMatchList) { if (!flags.pidMatchList) {
flags.pidMatchList = Hashtable_new(8, false); flags.pidMatchList = Hashtable_new(8, false);
} }
@ -255,29 +229,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:
exit(1); if (Platform_getLongOption(opt, argc, argv) == false)
exit(1);
break;
} }
} }
return flags; return flags;
@ -304,92 +260,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 +269,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,160 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) {
Platform_Battery_cacheIsOnAC = *isOnAC; Platform_Battery_cacheIsOnAC = *isOnAC;
Platform_Battery_cacheTime = now; Platform_Battery_cacheTime = now;
} }
void Platform_longOptionsUsage(const char* name)
{
#ifdef HAVE_LIBCAP
printf(
" --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 %s\n"
" strict - drop all capabilities except those needed for\n"
" core functionality\n", name);
#else
(void) name;
#endif
}
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

@ -82,4 +82,15 @@ static inline void Platform_getRelease(char** string) {
*string = Generic_uname(); *string = Generic_uname();
} }
#ifdef HAVE_LIBCAP
#define PLATFORM_LONG_OPTIONS \
{"drop-capabilities", optional_argument, 0, 128},
#else
#define PLATFORM_LONG_OPTIONS
#endif
void Platform_longOptionsUsage(const char* name);
bool Platform_getLongOption(int opt, int argc, char** argv);
#endif #endif

View File

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

View File

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

View File

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