diff --git a/Action.c b/Action.c index c894c92f..5cffe760 100644 --- a/Action.c +++ b/Action.c @@ -254,11 +254,17 @@ static Htop_Reaction actionIncSearch(State* st) { } static Htop_Reaction actionHigherPriority(State* st) { + if (Settings_isReadonly()) + return HTOP_OK; + bool changed = changePriority(st->mainPanel, -1); return changed ? HTOP_REFRESH : HTOP_OK; } static Htop_Reaction actionLowerPriority(State* st) { + if (Settings_isReadonly()) + return HTOP_OK; + bool changed = changePriority(st->mainPanel, 1); return changed ? HTOP_REFRESH : HTOP_OK; } @@ -292,6 +298,9 @@ static Htop_Reaction actionQuit(ATTR_UNUSED State* st) { } static Htop_Reaction actionSetAffinity(State* st) { + if (Settings_isReadonly()) + return HTOP_OK; + if (st->pl->cpuCount == 1) return HTOP_OK; @@ -323,6 +332,9 @@ static Htop_Reaction actionSetAffinity(State* st) { } static Htop_Reaction actionKill(State* st) { + if (Settings_isReadonly()) + return HTOP_OK; + Panel* signalsPanel = SignalsPanel_new(); const ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true); if (sgn && sgn->key != 0) { @@ -370,6 +382,9 @@ static Htop_Reaction actionSetup(State* st) { } static Htop_Reaction actionLsof(State* st) { + if (Settings_isReadonly()) + return HTOP_OK; + const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel); if (!p) return HTOP_OK; @@ -394,6 +409,9 @@ static Htop_Reaction actionShowLocks(State* st) { } static Htop_Reaction actionStrace(State* st) { + if (Settings_isReadonly()) + return HTOP_OK; + const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel); if (!p) return HTOP_OK; @@ -431,49 +449,51 @@ static Htop_Reaction actionTogglePauseProcessUpdate(State* st) { static const struct { const char* key; + bool roInactive; const char* info; } helpLeft[] = { - { .key = " Arrows: ", .info = "scroll process list" }, - { .key = " Digits: ", .info = "incremental PID search" }, - { .key = " F3 /: ", .info = "incremental name search" }, - { .key = " F4 \\: ",.info = "incremental name filtering" }, - { .key = " F5 t: ", .info = "tree view" }, - { .key = " p: ", .info = "toggle program path" }, - { .key = " m: ", .info = "toggle merged command" }, - { .key = " Z: ", .info = "pause/resume process updates" }, - { .key = " u: ", .info = "show processes of a single user" }, - { .key = " H: ", .info = "hide/show user process threads" }, - { .key = " K: ", .info = "hide/show kernel threads" }, - { .key = " F: ", .info = "cursor follows process" }, - { .key = " + - *: ", .info = "expand/collapse tree/toggle all" }, - { .key = "N P M T: ", .info = "sort by PID, CPU%, MEM% or TIME" }, - { .key = " I: ", .info = "invert sort order" }, - { .key = " F6 > .: ", .info = "select sort column" }, + { .key = " Arrows: ", .roInactive = false, .info = "scroll process list" }, + { .key = " Digits: ", .roInactive = false, .info = "incremental PID search" }, + { .key = " F3 /: ", .roInactive = false, .info = "incremental name search" }, + { .key = " F4 \\: ", .roInactive = false, .info = "incremental name filtering" }, + { .key = " F5 t: ", .roInactive = false, .info = "tree view" }, + { .key = " p: ", .roInactive = false, .info = "toggle program path" }, + { .key = " m: ", .roInactive = false, .info = "toggle merged command" }, + { .key = " Z: ", .roInactive = false, .info = "pause/resume process updates" }, + { .key = " u: ", .roInactive = false, .info = "show processes of a single user" }, + { .key = " H: ", .roInactive = false, .info = "hide/show user process threads" }, + { .key = " K: ", .roInactive = false, .info = "hide/show kernel threads" }, + { .key = " F: ", .roInactive = false, .info = "cursor follows process" }, + { .key = " + - *: ", .roInactive = false, .info = "expand/collapse tree/toggle all" }, + { .key = "N P M T: ", .roInactive = false, .info = "sort by PID, CPU%, MEM% or TIME" }, + { .key = " I: ", .roInactive = false, .info = "invert sort order" }, + { .key = " F6 > .: ", .roInactive = false, .info = "select sort column" }, { .key = NULL, .info = NULL } }; static const struct { const char* key; + bool roInactive; const char* info; } helpRight[] = { - { .key = " Space: ", .info = "tag process" }, - { .key = " c: ", .info = "tag process and its children" }, - { .key = " U: ", .info = "untag all processes" }, - { .key = " F9 k: ", .info = "kill process/tagged processes" }, - { .key = " F7 ]: ", .info = "higher priority (root only)" }, - { .key = " F8 [: ", .info = "lower priority (+ nice)" }, + { .key = " Space: ", .roInactive = false, .info = "tag process" }, + { .key = " c: ", .roInactive = false, .info = "tag process and its children" }, + { .key = " U: ", .roInactive = false, .info = "untag all processes" }, + { .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" }, + { .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" }, + { .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" }, #if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) - { .key = " a: ", .info = "set CPU affinity" }, + { .key = " a: ", .roInactive = true, .info = "set CPU affinity" }, #endif - { .key = " e: ", .info = "show process environment" }, - { .key = " i: ", .info = "set IO priority" }, - { .key = " l: ", .info = "list open files with lsof" }, - { .key = " x: ", .info = "list file locks of process" }, - { .key = " s: ", .info = "trace syscalls with strace" }, - { .key = " w: ", .info = "wrap process command in multiple lines" }, - { .key = " F2 C S: ", .info = "setup" }, - { .key = " F1 h: ", .info = "show this help screen" }, - { .key = " F10 q: ", .info = "quit" }, + { .key = " e: ", .roInactive = false, .info = "show process environment" }, + { .key = " i: ", .roInactive = true, .info = "set IO priority" }, + { .key = " l: ", .roInactive = true, .info = "list open files with lsof" }, + { .key = " x: ", .roInactive = false, .info = "list file locks of process" }, + { .key = " s: ", .roInactive = true, .info = "trace syscalls with strace" }, + { .key = " w: ", .roInactive = false, .info = "wrap process command in multiple lines" }, + { .key = " F2 C S: ", .roInactive = false, .info = "setup" }, + { .key = " F1 h: ", .roInactive = false, .info = "show this help screen" }, + { .key = " F10 q: ", .roInactive = false, .info = "quit" }, { .key = NULL, .info = NULL } }; @@ -549,26 +569,28 @@ static Htop_Reaction actionHelp(State* st) { line++; + const bool readonly = Settings_isReadonly(); + int item; for (item = 0; helpLeft[item].key; item++) { - attrset(CRT_colors[DEFAULT_COLOR]); + attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]); mvaddstr(line + item, 10, helpLeft[item].info); - attrset(CRT_colors[HELP_BOLD]); + attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]); mvaddstr(line + item, 1, helpLeft[item].key); if (String_eq(helpLeft[item].key, " H: ")) { - attrset(CRT_colors[PROCESS_THREAD]); + attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[PROCESS_THREAD]); mvaddstr(line + item, 33, "threads"); } else if (String_eq(helpLeft[item].key, " K: ")) { - attrset(CRT_colors[PROCESS_THREAD]); + attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[PROCESS_THREAD]); mvaddstr(line + item, 27, "threads"); } } int leftHelpItems = item; for (item = 0; helpRight[item].key; item++) { - attrset(CRT_colors[HELP_BOLD]); + attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]); mvaddstr(line + item, 41, helpRight[item].key); - attrset(CRT_colors[DEFAULT_COLOR]); + attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]); mvaddstr(line + item, 50, helpRight[item].info); } line += MAXIMUM(leftHelpItems, item); diff --git a/CRT.c b/CRT.c index acf21bb6..481e16c1 100644 --- a/CRT.c +++ b/CRT.c @@ -166,6 +166,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black), [LOAD] = A_BOLD, [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black), + [HELP_SHADOW] = A_BOLD | ColorPairGrayBlack, [CLOCK] = A_BOLD, [DATE] = A_BOLD, [DATETIME] = A_BOLD, @@ -258,6 +259,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [LOAD_AVERAGE_ONE] = A_BOLD, [LOAD] = A_BOLD, [HELP_BOLD] = A_BOLD, + [HELP_SHADOW] = A_DIM, [CLOCK] = A_BOLD, [DATE] = A_BOLD, [DATETIME] = A_BOLD, @@ -350,6 +352,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [LOAD_AVERAGE_ONE] = ColorPair(Black, White), [LOAD] = ColorPair(Black, White), [HELP_BOLD] = ColorPair(Blue, White), + [HELP_SHADOW] = A_BOLD | ColorPair(Black, White), [CLOCK] = ColorPair(Black, White), [DATE] = ColorPair(Black, White), [DATETIME] = ColorPair(Black, White), @@ -442,6 +445,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [LOAD_AVERAGE_ONE] = ColorPair(Black, Black), [LOAD] = ColorPairWhiteDefault, [HELP_BOLD] = ColorPair(Blue, Black), + [HELP_SHADOW] = A_BOLD | ColorPairGrayBlack, [CLOCK] = ColorPairWhiteDefault, [DATE] = ColorPairWhiteDefault, [DATETIME] = ColorPairWhiteDefault, @@ -534,6 +538,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Blue), [LOAD] = A_BOLD | ColorPair(White, Blue), [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Blue), + [HELP_SHADOW] = A_BOLD | ColorPair(Black, Blue), [CLOCK] = ColorPair(White, Blue), [DATE] = ColorPair(White, Blue), [DATETIME] = ColorPair(White, Blue), @@ -626,6 +631,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Green, Black), [LOAD] = A_BOLD, [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black), + [HELP_SHADOW] = A_BOLD | ColorPairGrayBlack, [CLOCK] = ColorPair(Green, Black), [CHECK_BOX] = ColorPair(Green, Black), [CHECK_MARK] = A_BOLD | ColorPair(Green, Black), diff --git a/CRT.h b/CRT.h index 92357528..e85247bd 100644 --- a/CRT.h +++ b/CRT.h @@ -109,6 +109,7 @@ typedef enum ColorElements_ { DATE, DATETIME, HELP_BOLD, + HELP_SHADOW, HOSTNAME, CPU_NICE, CPU_NICE_TEXT, diff --git a/CommandLine.c b/CommandLine.c index 796018af..16ded1fa 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -53,6 +53,7 @@ static void printHelpFlag(const char* name) { "-H --highlight-changes[=DELAY] Highlight new and old processes\n" "-M --no-mouse Disable the mouse\n" "-p --pid=PID[,PID,PID...] Show only the given PIDs\n" + " --readonly Disable all system and process changing features\n" "-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\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" @@ -79,6 +80,7 @@ typedef struct CommandLineSettings_ { bool allowUnicode; bool highlightChanges; int highlightDelaySecs; + bool readonly; } CommandLineSettings; static CommandLineSettings parseArguments(const char* program, int argc, char** argv) { @@ -95,6 +97,7 @@ static CommandLineSettings parseArguments(const char* program, int argc, char** .allowUnicode = true, .highlightChanges = false, .highlightDelaySecs = -1, + .readonly = false, }; const struct option long_opts[] = @@ -112,6 +115,7 @@ static CommandLineSettings parseArguments(const char* program, int argc, char** {"pid", required_argument, 0, 'p'}, {"filter", required_argument, 0, 'F'}, {"highlight-changes", optional_argument, 0, 'H'}, + {"readonly", no_argument, 0, 128}, PLATFORM_LONG_OPTIONS {0,0,0,0} }; @@ -231,6 +235,9 @@ static CommandLineSettings parseArguments(const char* program, int argc, char** flags.highlightChanges = true; break; } + case 128: + flags.readonly = true; + break; default: if (Platform_getLongOption(opt, argc, argv) == false) @@ -273,6 +280,9 @@ int CommandLine_run(const char* name, int argc, char** argv) { CommandLineSettings flags = parseArguments(name, argc, argv); + if (flags.readonly) + Settings_enableReadonly(); + Platform_init(); Process_setupColumnWidths(); diff --git a/MainPanel.c b/MainPanel.c index 842e8e15..cf7392e4 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -21,7 +21,8 @@ in the source distribution for its full text. #include "XUtils.h" -static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL}; +static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL}; +static const char* const MainFunctions_ro[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", " ", " ", " ", "Quit ", NULL}; void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { FunctionBar* bar = MainPanel_getFunctionBar(this); @@ -195,7 +196,7 @@ const PanelClass MainPanel_class = { MainPanel* MainPanel_new() { MainPanel* this = AllocThis(MainPanel); - Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(MainFunctions, NULL, NULL)); + Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(Settings_isReadonly() ? MainFunctions_ro : MainFunctions, NULL, NULL)); this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action)); this->inc = IncSet_new(MainPanel_getFunctionBar(this)); diff --git a/Process.c b/Process.c index 1ca942a3..e7431cec 100644 --- a/Process.c +++ b/Process.c @@ -482,6 +482,9 @@ bool Process_isTomb(const Process* this) { } bool Process_setPriority(Process* this, int priority) { + if (Settings_isReadonly()) + return false; + int old_prio = getpriority(PRIO_PROCESS, this->pid); int err = setpriority(PRIO_PROCESS, this->pid, priority); diff --git a/Settings.c b/Settings.c index 8baad6a7..0606df30 100644 --- a/Settings.c +++ b/Settings.c @@ -472,3 +472,13 @@ void Settings_setSortKey(Settings* this, ProcessField sortKey) { this->treeDirection = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1; } } + +static bool readonly = false; + +void Settings_enableReadonly(void) { + readonly = true; +} + +bool Settings_isReadonly(void) { + return readonly; +} diff --git a/Settings.h b/Settings.h index f10b3055..7313ce38 100644 --- a/Settings.h +++ b/Settings.h @@ -95,4 +95,8 @@ void Settings_invertSortOrder(Settings* this); void Settings_setSortKey(Settings* this, ProcessField sortKey); +void Settings_enableReadonly(void); + +bool Settings_isReadonly(void); + #endif diff --git a/htop.1.in b/htop.1.in index 5408dbb4..e8cfc3f6 100644 --- a/htop.1.in +++ b/htop.1.in @@ -54,6 +54,9 @@ Do not use unicode but ASCII characters for graph meters \fB\-M \-\-no-mouse\fR Disable support of mouse control .TP +\fB\-\-readonly\fR +Disable all system and process changing features +.TP \fB\-V \-\-version Output version information and exit .TP diff --git a/linux/Platform.c b/linux/Platform.c index 831ea273..af3fa277 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -130,6 +130,9 @@ static enum CapMode Platform_capabilitiesMode = CAP_MODE_BASIC; #endif static Htop_Reaction Platform_actionSetIOPriority(State* st) { + if (Settings_isReadonly()) + return HTOP_OK; + const LinuxProcess* p = (const LinuxProcess*) Panel_getSelected((Panel*)st->mainPanel); if (!p) return HTOP_OK; @@ -873,7 +876,7 @@ bool Platform_getLongOption(int opt, int argc, char** argv) { switch (opt) { #ifdef HAVE_LIBCAP - case 128: { + case 160: { const char* mode = optarg; if (!mode && optind < argc && argv[optind] != NULL && (argv[optind][0] != '\0' && argv[optind][0] != '-')) { diff --git a/linux/Platform.h b/linux/Platform.h index 78a44275..2506fae5 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -85,7 +85,7 @@ static inline void Platform_getRelease(char** string) { #ifdef HAVE_LIBCAP #define PLATFORM_LONG_OPTIONS \ - {"drop-capabilities", optional_argument, 0, 128}, + {"drop-capabilities", optional_argument, 0, 160}, #else #define PLATFORM_LONG_OPTIONS #endif diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c index 24a47f74..a891eb11 100644 --- a/linux/SystemdMeter.c +++ b/linux/SystemdMeter.c @@ -195,6 +195,9 @@ dlfailure: #endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */ static void updateViaExec(void) { + if (Settings_isReadonly()) + return; + int fdpair[2]; if (pipe(fdpair) < 0) return;