From 65357c8c46154de4e4eca14075bfe5523bb5fc14 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Mon, 7 Dec 2020 10:26:01 +0100 Subject: New upstream version 3.0.3 --- Action.c | 266 ++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 188 insertions(+), 78 deletions(-) (limited to 'Action.c') diff --git a/Action.c b/Action.c index 90d6bfe..66934be 100644 --- a/Action.c +++ b/Action.c @@ -1,34 +1,44 @@ /* htop - Action.c (C) 2015 Hisham H. Muhammad -Released under the GNU GPL, see the COPYING file +Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ -#include "config.h" +#include "config.h" // IWYU pragma: keep #include "Action.h" -#include "Affinity.h" -#include "AffinityPanel.h" + +#include +#include +#include + #include "CategoriesPanel.h" +#include "CommandScreen.h" #include "CRT.h" #include "EnvScreen.h" +#include "FunctionBar.h" +#include "Hashtable.h" +#include "IncSet.h" +#include "InfoScreen.h" +#include "ListItem.h" +#include "Macros.h" #include "MainPanel.h" #include "OpenFilesScreen.h" #include "Process.h" +#include "ProcessLocksScreen.h" +#include "ProvideCurses.h" #include "ScreenManager.h" #include "SignalsPanel.h" -#include "StringUtils.h" #include "TraceScreen.h" -#include "Platform.h" +#include "Vector.h" +#include "XUtils.h" + +#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) +#include "Affinity.h" +#include "AffinityPanel.h" +#endif -#include -#include -#include -#include -#include -#include -#include Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) { Panel* panel = st->panel; @@ -36,7 +46,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) Settings* settings = st->settings; int y = panel->y; - ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false); + ScreenManager* scr = ScreenManager_new(header, settings, st, false); scr->allowFocusChange = false; ScreenManager_add(scr, list, x - 1); ScreenManager_add(scr, panel, -1); @@ -54,59 +64,59 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) } ScreenManager_delete(scr); Panel_move(panel, 0, y); - Panel_resize(panel, COLS, LINES-y-1); + Panel_resize(panel, COLS, LINES - y - 1); if (panelFocus == list && ch == 13) { if (followProcess) { Process* selected = (Process*)Panel_getSelected(panel); if (selected && selected->pid == pid) return Panel_getSelected(list); - else - beep(); + + beep(); } else { return Panel_getSelected(list); } - } + return NULL; } // ---------------------------------------- -static void Action_runSetup(Settings* settings, const Header* header, ProcessList* pl) { - ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true); - CategoriesPanel* panelCategories = CategoriesPanel_new(scr, settings, (Header*) header, pl); +static void Action_runSetup(State* st) { + ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true); + CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl); ScreenManager_add(scr, (Panel*) panelCategories, 16); CategoriesPanel_makeMetersPage(panelCategories); Panel* panelFocus; int ch; ScreenManager_run(scr, &panelFocus, &ch); ScreenManager_delete(scr); - if (settings->changed) { - Header_writeBackToSettings(header); + if (st->settings->changed) { + Header_writeBackToSettings(st->header); } } static bool changePriority(MainPanel* panel, int delta) { bool anyTagged; - bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged); + bool ok = MainPanel_foreachProcess(panel, Process_changePriorityBy, (Arg) { .i = delta }, &anyTagged); if (!ok) beep(); return anyTagged; } -static void addUserToVector(int key, void* userCast, void* panelCast) { - char* user = (char*) userCast; - Panel* panel = (Panel*) panelCast; +static void addUserToVector(hkey_t key, void* userCast, void* panelCast) { + const char* user = userCast; + Panel* panel = panelCast; Panel_add(panel, (Object*) ListItem_new(user, key)); } bool Action_setUserOnly(const char* userName, uid_t* userId) { - struct passwd* user = getpwnam(userName); + const struct passwd* user = getpwnam(userName); if (user) { *userId = user->pw_uid; return true; } - *userId = -1; + *userId = (uid_t)-1; return false; } @@ -123,14 +133,18 @@ static void tagAllChildren(Panel* panel, Process* parent) { static bool expandCollapse(Panel* panel) { Process* p = (Process*) Panel_getSelected(panel); - if (!p) return false; + if (!p) + return false; + p->showChildren = !p->showChildren; return true; } static bool collapseIntoParent(Panel* panel) { Process* p = (Process*) Panel_getSelected(panel); - if (!p) return false; + if (!p) + return false; + pid_t ppid = Process_getParentPid(p); for (int i = 0; i < Panel_size(panel); i++) { Process* q = (Process*) Panel_get(panel, i); @@ -146,7 +160,6 @@ static bool collapseIntoParent(Panel* panel) { Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { settings->sortKey = sortKey; settings->direction = 1; - settings->treeView = false; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; } @@ -160,6 +173,7 @@ static Htop_Reaction sortBy(State* st) { Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); if (fields[i] == st->settings->sortKey) Panel_setSelected(sortPanel, i); + free(name); } ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false); @@ -167,6 +181,10 @@ static Htop_Reaction sortBy(State* st) { reaction |= Action_setSortKey(st->settings, field->key); } Object_delete(sortPanel); + + if (st->pauseProcessUpdate) + ProcessList_sort(st->pl); + return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } @@ -174,7 +192,7 @@ static Htop_Reaction sortBy(State* st) { static Htop_Reaction actionResize(State* st) { clear(); - Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1); + Panel_resize(st->panel, COLS, LINES - (st->panel->y) - 1); return HTOP_REDRAW_BAR; } @@ -197,7 +215,6 @@ static Htop_Reaction actionToggleKernelThreads(State* st) { static Htop_Reaction actionToggleUserlandThreads(State* st) { st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; - st->settings->hideThreads = st->settings->hideUserlandThreads; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; } @@ -206,9 +223,17 @@ static Htop_Reaction actionToggleProgramPath(State* st) { return HTOP_REFRESH | HTOP_SAVE_SETTINGS; } +static Htop_Reaction actionToggleMergedCommand(State* st) { + st->settings->showMergedCommand = !st->settings->showMergedCommand; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS; +} + static Htop_Reaction actionToggleTreeView(State* st) { st->settings->treeView = !st->settings->treeView; - if (st->settings->treeView) st->settings->direction = 1; + if (st->settings->treeView) { + st->settings->direction = 1; + } + ProcessList_expandTree(st->pl); return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } @@ -272,20 +297,25 @@ static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) { return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); } -static Htop_Reaction actionQuit() { +static Htop_Reaction actionQuit(ATTR_UNUSED State* st) { return HTOP_QUIT; } static Htop_Reaction actionSetAffinity(State* st) { if (st->pl->cpuCount == 1) return HTOP_OK; -#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY) + +#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) Panel* panel = st->panel; Process* p = (Process*) Panel_getSelected(panel); - if (!p) return HTOP_OK; + if (!p) + return HTOP_OK; + Affinity* affinity1 = Affinity_get(p, st->pl); - if (!affinity1) return HTOP_OK; + if (!affinity1) + return HTOP_OK; + int width; Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width); width += 1; /* we add a gap between the panels */ @@ -294,24 +324,25 @@ static Htop_Reaction actionSetAffinity(State* st) { void* set = Action_pickFromVector(st, affinityPanel, width, true); if (set) { Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl); - bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity2 }, NULL); - if (!ok) beep(); + bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL); + if (!ok) + beep(); Affinity_delete(affinity2); } - Panel_delete((Object*)affinityPanel); + Object_delete(affinityPanel); #endif return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionKill(State* st) { - Panel* signalsPanel = (Panel*) SignalsPanel_new(); + Panel* signalsPanel = SignalsPanel_new(); ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true); if (sgn) { if (sgn->key != 0) { Panel_setHeader(st->panel, "Sending..."); - Panel_draw(st->panel, true); + Panel_draw(st->panel, true, true); refresh(); - MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL); + MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL); napms(500); } } @@ -329,7 +360,7 @@ static Htop_Reaction actionFilterByUser(State* st) { ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false); if (picked) { if (picked == allUsers) { - st->pl->userId = -1; + st->pl->userId = (uid_t)-1; } else { Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId)); } @@ -345,7 +376,7 @@ Htop_Reaction Action_follow(State* st) { } static Htop_Reaction actionSetup(State* st) { - Action_runSetup(st->settings, st->header, st->pl); + Action_runSetup(st); // TODO: shouldn't need this, colors should be dynamic int headerHeight = Header_calculateHeight(st->header); Panel_move(st->panel, 0, headerHeight); @@ -355,7 +386,9 @@ static Htop_Reaction actionSetup(State* st) { static Htop_Reaction actionLsof(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); - if (!p) return HTOP_OK; + if (!p) + return HTOP_OK; + OpenFilesScreen* ofs = OpenFilesScreen_new(p); InfoScreen_run((InfoScreen*)ofs); OpenFilesScreen_delete((Object*)ofs); @@ -364,9 +397,22 @@ static Htop_Reaction actionLsof(State* st) { return HTOP_REFRESH | HTOP_REDRAW_BAR; } -static Htop_Reaction actionStrace(State* st) { +static Htop_Reaction actionShowLocks(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); if (!p) return HTOP_OK; + ProcessLocksScreen* pls = ProcessLocksScreen_new(p); + InfoScreen_run((InfoScreen*)pls); + ProcessLocksScreen_delete((Object*)pls); + clear(); + CRT_enableDelay(); + return HTOP_REFRESH | HTOP_REDRAW_BAR; +} + +static Htop_Reaction actionStrace(State* st) { + Process* p = (Process*) Panel_getSelected(st->panel); + if (!p) + return HTOP_OK; + TraceScreen* ts = TraceScreen_new(p); bool ok = TraceScreen_forkTracer(ts); if (ok) { @@ -380,24 +426,36 @@ static Htop_Reaction actionStrace(State* st) { static Htop_Reaction actionTag(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); - if (!p) return HTOP_OK; + if (!p) + return HTOP_OK; + Process_toggleTag(p); Panel_onKey(st->panel, KEY_DOWN); return HTOP_OK; } -static Htop_Reaction actionRedraw() { +static Htop_Reaction actionRedraw(ATTR_UNUSED State* st) { clear(); return HTOP_REFRESH | HTOP_REDRAW_BAR; } -static const struct { const char* key; const char* info; } helpLeft[] = { +static Htop_Reaction actionTogglePauseProcessUpdate(State* st) { + st->pauseProcessUpdate = !st->pauseProcessUpdate; + return HTOP_REFRESH | HTOP_REDRAW_BAR; +} + +static const struct { + const char* key; + 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" }, @@ -409,42 +467,54 @@ static const struct { const char* key; const char* info; } helpLeft[] = { { .key = NULL, .info = NULL } }; -static const struct { const char* key; const char* info; } helpRight[] = { +static const struct { + const char* key; + 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)" }, -#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY) +#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) { .key = " a: ", .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 = " ", .info = "" }, + { .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 = NULL, .info = NULL } }; +static inline void addattrstr( int attr, const char* str) { + attrset(attr); + addstr(str); +} + static Htop_Reaction actionHelp(State* st) { Settings* settings = st->settings; clear(); attrset(CRT_colors[HELP_BOLD]); - for (int i = 0; i < LINES-1; i++) + for (int i = 0; i < LINES - 1; i++) mvhline(i, 0, ' ', COLS); - mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT); - mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info."); + int line = 0; + + mvaddstr(line++, 0, "htop " VERSION " - " COPYRIGHT); + mvaddstr(line++, 0, "Released under the GNU GPLv2. See 'man' page for more info."); attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(3, 0, "CPU usage bar: "); - #define addattrstr(a,s) attrset(a);addstr(s) + line++; + mvaddstr(line++, 0, "CPU usage bar: "); + addattrstr(CRT_colors[BAR_BORDER], "["); if (settings->detailedCPUTime) { addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); @@ -465,7 +535,7 @@ static Htop_Reaction actionHelp(State* st) { } addattrstr(CRT_colors[BAR_BORDER], "]"); attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(4, 0, "Memory bar: "); + mvaddstr(line++, 0, "Memory bar: "); addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/"); addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/"); @@ -473,29 +543,49 @@ static Htop_Reaction actionHelp(State* st) { addattrstr(CRT_colors[BAR_SHADOW], " used/total"); addattrstr(CRT_colors[BAR_BORDER], "]"); attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(5, 0, "Swap bar: "); + mvaddstr(line++, 0, "Swap bar: "); addattrstr(CRT_colors[BAR_BORDER], "["); addattrstr(CRT_colors[SWAP], "used"); addattrstr(CRT_colors[BAR_SHADOW], " used/total"); addattrstr(CRT_colors[BAR_BORDER], "]"); attrset(CRT_colors[DEFAULT_COLOR]); - mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen."); + mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen."); if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { - mvaddstr(7, 0, "In monochrome, meters display as different chars, in order: |#*@$%&."); + mvaddstr(line, 0, "In monochrome, meters display as different chars, in order: |#*@$%&."); } - mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep"); - for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9, helpLeft[i].info); } - for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); } - attrset(CRT_colors[HELP_BOLD]); - for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); } - for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); } - attrset(CRT_colors[PROCESS_THREAD]); - mvaddstr(16, 32, "threads"); - mvaddstr(17, 26, "threads"); - attrset(CRT_colors[DEFAULT_COLOR]); + line++; + + mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep"); + + line++; + + int item; + for (item = 0; helpLeft[item].key; item++) { + attrset(CRT_colors[DEFAULT_COLOR]); + mvaddstr(line + item, 9, helpLeft[item].info); + attrset(CRT_colors[HELP_BOLD]); + mvaddstr(line + item, 0, helpLeft[item].key); + if (String_eq(helpLeft[item].key, " H: ")) { + attrset(CRT_colors[PROCESS_THREAD]); + mvaddstr(line + item, 32, "threads"); + } else if (String_eq(helpLeft[item].key, " K: ")) { + attrset(CRT_colors[PROCESS_THREAD]); + mvaddstr(line + item, 26, "threads"); + } + } + int leftHelpItems = item; + + for (item = 0; helpRight[item].key; item++) { + attrset(CRT_colors[HELP_BOLD]); + mvaddstr(line + item, 40, helpRight[item].key); + attrset(CRT_colors[DEFAULT_COLOR]); + mvaddstr(line + item, 49, helpRight[item].info); + } + line += MAXIMUM(leftHelpItems, item); + line++; attrset(CRT_colors[HELP_BOLD]); - mvaddstr(23,0, "Press any key to return."); + mvaddstr(line++, 0, "Press any key to return."); attrset(CRT_colors[DEFAULT_COLOR]); refresh(); CRT_readKey(); @@ -514,14 +604,18 @@ static Htop_Reaction actionUntagAll(State* st) { static Htop_Reaction actionTagAllChildren(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); - if (!p) return HTOP_OK; + if (!p) + return HTOP_OK; + tagAllChildren(st->panel, p); return HTOP_OK; } static Htop_Reaction actionShowEnvScreen(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); - if (!p) return HTOP_OK; + if (!p) + return HTOP_OK; + EnvScreen* es = EnvScreen_new(p); InfoScreen_run((InfoScreen*)es); EnvScreen_delete((Object*)es); @@ -530,6 +624,18 @@ static Htop_Reaction actionShowEnvScreen(State* st) { return HTOP_REFRESH | HTOP_REDRAW_BAR; } +static Htop_Reaction actionShowCommandScreen(State* st) { + Process* p = (Process*) Panel_getSelected(st->panel); + if (!p) + return HTOP_OK; + + CommandScreen* cmdScr = CommandScreen_new(p); + InfoScreen_run((InfoScreen*)cmdScr); + CommandScreen_delete((Object*)cmdScr); + clear(); + CRT_enableDelay(); + return HTOP_REFRESH | HTOP_REDRAW_BAR; +} void Action_setBindings(Htop_Action* keys) { keys[KEY_RESIZE] = actionResize; @@ -539,6 +645,7 @@ void Action_setBindings(Htop_Action* keys) { keys['H'] = actionToggleUserlandThreads; keys['K'] = actionToggleKernelThreads; keys['p'] = actionToggleProgramPath; + keys['m'] = actionToggleMergedCommand; keys['t'] = actionToggleTreeView; keys[KEY_F(5)] = actionToggleTreeView; keys[KEY_F(4)] = actionIncFilter; @@ -553,7 +660,7 @@ void Action_setBindings(Htop_Action* keys) { keys['['] = actionLowerPriority; keys[KEY_F(8)] = actionLowerPriority; keys['I'] = actionInvertSortOrder; - keys[KEY_F(6)] = actionExpandCollapseOrSortColumn; + keys[KEY_F(6)] = actionSetSortColumn; keys[KEY_F(18)] = actionExpandCollapseOrSortColumn; keys['<'] = actionSetSortColumn; keys[','] = actionSetSortColumn; @@ -574,6 +681,7 @@ void Action_setBindings(Htop_Action* keys) { keys['S'] = actionSetup; keys['C'] = actionSetup; keys[KEY_F(2)] = actionSetup; + keys['x'] = actionShowLocks; keys['l'] = actionLsof; keys['s'] = actionStrace; keys[' '] = actionTag; @@ -584,4 +692,6 @@ void Action_setBindings(Htop_Action* keys) { keys['U'] = actionUntagAll; keys['c'] = actionTagAllChildren; keys['e'] = actionShowEnvScreen; + keys['w'] = actionShowCommandScreen; + keys['Z'] = actionTogglePauseProcessUpdate; } -- cgit v1.2.3