pax_global_header00006660000000000000000000000064137771251370014527gustar00rootroot0000000000000052 comment=c55320e9e2a8916e911bcd39ab37b79e3a7d03b2 htop-3.0.5/000077500000000000000000000000001377712513700125065ustar00rootroot00000000000000htop-3.0.5/.editorconfig000066400000000000000000000006001377712513700151570ustar00rootroot00000000000000# EditorConfig configuration for htop # http://EditorConfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file, utf-8 charset [*] end_of_line = lf insert_final_newline = true charset = utf-8 # match C source and header files, set indent to three spaces [*.{c,h}] indent_style = space indent_size = 3 trim_trailing_whitespace = true htop-3.0.5/.github/000077500000000000000000000000001377712513700140465ustar00rootroot00000000000000htop-3.0.5/.github/workflows/000077500000000000000000000000001377712513700161035ustar00rootroot00000000000000htop-3.0.5/.github/workflows/ci.yml000066400000000000000000000125031377712513700172220ustar00rootroot00000000000000name: CI on: [ push, pull_request ] env: # Enable format attributes in ncurses headers # Enable fortified memory/string handling CPPFLAGS: -DGCC_PRINTF -DGCC_SCANF -D_FORTIFY_SOURCE=2 jobs: build-ubuntu-latest-minimal-gcc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Dependencies run: sudo apt-get install libncursesw5-dev - name: Bootstrap run: ./autogen.sh - name: Configure run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors - name: Enable compatibility modes run: | sed -i 's/#define HAVE_FSTATAT 1/#undef HAVE_FSTATAT/g' config.h sed -i 's/#define HAVE_OPENAT 1/#undef HAVE_OPENAT/g' config.h sed -i 's/#define HAVE_READLINKAT 1/#undef HAVE_READLINKAT/g' config.h - name: Build run: make -k - name: Distcheck run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors" build-ubuntu-latest-minimal-clang: runs-on: ubuntu-latest env: CC: clang-11 steps: - uses: actions/checkout@v2 - name: install clang repo run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y sudo apt-get update -q - name: Install Dependencies run: sudo apt-get install clang-11 libncursesw5-dev - name: Bootstrap run: ./autogen.sh - name: Configure run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors - name: Build run: make -k - name: Distcheck run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors" build-ubuntu-latest-full-featured-gcc: runs-on: ubuntu-latest # Enable LTO, might trigger additional warnings on advanced inlining env: CFLAGS: -O3 -g -flto LDFLAGS: -O3 -g -flto steps: - uses: actions/checkout@v2 - name: Install Dependencies run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev - name: Bootstrap run: ./autogen.sh - name: Configure run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors - name: Build run: make -k - name: Distcheck run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors' build-ubuntu-latest-full-featured-clang: runs-on: ubuntu-latest env: CC: clang-11 steps: - uses: actions/checkout@v2 - name: install clang repo run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y sudo apt-get update -q - name: Install Dependencies run: sudo apt-get install clang-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev - name: Bootstrap run: ./autogen.sh - name: Configure run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors - name: Build run: make -k - name: Distcheck run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors' build-ubuntu-latest-clang-analyzer: runs-on: ubuntu-latest env: CC: clang-11 steps: - uses: actions/checkout@v2 - name: install clang repo run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y sudo apt-get update -q - name: Install Dependencies run: sudo apt-get install clang-11 clang-tools-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev - name: Bootstrap run: ./autogen.sh - name: Configure run: scan-build-11 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors - name: Build run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)" build-macos-latest-clang: runs-on: macOS-latest env: CC: clang steps: - uses: actions/checkout@v2 - name: Install Dependencies run: brew install automake - name: Bootstrap run: ./autogen.sh - name: Configure run: ./configure --enable-werror - name: Build run: make -k - name: Distcheck run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror" whitespace_check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: check-whitespaces run: git diff-tree --check $(git hash-object -t tree /dev/null) HEAD htop-3.0.5/.gitignore000066400000000000000000000007401377712513700144770ustar00rootroot00000000000000# the binary: htop # all object files *.o # skip all backups *.bak *~ .*.sw? # skip coverage files *.gcda */*.gcda *.gcno */*.gcno *.h.gch */.dirstamp # automake/autoconf related files .deps/ Makefile Makefile.in INSTALL aclocal.m4 autom4te.cache/ compile conf*/ config.guess config.h config.h.in config.log config.status config.cache config.sub configure depcomp htop.1 install-sh libtool ltmain.sh m4/ missing stamp-h1 # files related to valgrind/callgrind callgrind.out.* htop-3.0.5/.travis.yml000066400000000000000000000004601377712513700146170ustar00rootroot00000000000000language: c compiler: - clang - gcc os: - freebsd script: - ./autogen.sh # clang might warn about C11 generic selections in isnan() - CFLAGS=-Wno-c11-extensions ./configure --enable-werror - make -k - CFLAGS=-Wno-c11-extensions make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror htop-3.0.5/AUTHORS000066400000000000000000000003421377712513700135550ustar00rootroot00000000000000Originally authored by: Hisham H. Muhammad Currently maintained by the htop dev team: Benny Baumann Christian Göttsche Daniel Lange Nathan Scott For the full list of contributors see: git log --format="%aN" | sort -u htop-3.0.5/Action.c000066400000000000000000000532701377712513700140760ustar00rootroot00000000000000/* htop - Action.c (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Action.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 "TraceScreen.h" #include "Vector.h" #include "XUtils.h" #if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) #include "Affinity.h" #include "AffinityPanel.h" #endif Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) { Panel* panel = st->panel; Header* header = st->header; Settings* settings = st->settings; int y = panel->y; ScreenManager* scr = ScreenManager_new(header, settings, st, false); scr->allowFocusChange = false; ScreenManager_add(scr, list, x - 1); ScreenManager_add(scr, panel, -1); Panel* panelFocus; int ch; bool unfollow = false; int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1; if (followProcess && header->pl->following == -1) { header->pl->following = pid; unfollow = true; } ScreenManager_run(scr, &panelFocus, &ch); if (unfollow) { header->pl->following = -1; } ScreenManager_delete(scr); Panel_move(panel, 0, y); 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); beep(); } else { return Panel_getSelected(list); } } return NULL; } // ---------------------------------------- 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 (st->settings->changed) { Header_writeBackToSettings(st->header); } } static bool changePriority(MainPanel* panel, int delta) { bool anyTagged; bool ok = MainPanel_foreachProcess(panel, Process_changePriorityBy, (Arg) { .i = delta }, &anyTagged); if (!ok) beep(); return anyTagged; } static void addUserToVector(ht_key_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) { const struct passwd* user = getpwnam(userName); if (user) { *userId = user->pw_uid; return true; } *userId = (uid_t)-1; return false; } static void tagAllChildren(Panel* panel, Process* parent) { parent->tag = true; pid_t ppid = parent->pid; for (int i = 0; i < Panel_size(panel); i++) { Process* p = (Process*) Panel_get(panel, i); if (!p->tag && Process_isChildOf(p, ppid)) { tagAllChildren(panel, p); } } } static bool expandCollapse(Panel* panel) { Process* p = (Process*) Panel_getSelected(panel); 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; pid_t ppid = Process_getParentPid(p); for (int i = 0; i < Panel_size(panel); i++) { Process* q = (Process*) Panel_get(panel, i); if (q->pid == ppid) { q->showChildren = false; Panel_setSelected(panel, i); return true; } } return false; } Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { Settings_setSortKey(settings, sortKey); return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; } // ---------------------------------------- static Htop_Reaction actionSetSortColumn(State* st) { Htop_Reaction reaction = HTOP_OK; Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel ")); Panel_setHeader(sortPanel, "Sort by"); ProcessField* fields = st->settings->fields; for (int i = 0; fields[i]; i++) { char* name = String_trim(Process_fields[fields[i]].name); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); if (fields[i] == Settings_getActiveSortKey(st->settings)) Panel_setSelected(sortPanel, i); free(name); } ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false); if (field) { 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; } static Htop_Reaction actionSortByPID(State* st) { return Action_setSortKey(st->settings, PID); } static Htop_Reaction actionSortByMemory(State* st) { return Action_setSortKey(st->settings, PERCENT_MEM); } static Htop_Reaction actionSortByCPU(State* st) { return Action_setSortKey(st->settings, PERCENT_CPU); } static Htop_Reaction actionSortByTime(State* st) { return Action_setSortKey(st->settings, TIME); } static Htop_Reaction actionToggleKernelThreads(State* st) { st->settings->hideKernelThreads = !st->settings->hideKernelThreads; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; } static Htop_Reaction actionToggleUserlandThreads(State* st) { st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS; } static Htop_Reaction actionToggleProgramPath(State* st) { st->settings->showProgramPath = !st->settings->showProgramPath; 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->treeDirection = 1; } ProcessList_expandTree(st->pl); return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionIncFilter(State* st) { IncSet* inc = ((MainPanel*)st->panel)->inc; IncSet_activate(inc, INC_FILTER, st->panel); st->pl->incFilter = IncSet_filter(inc); return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionIncSearch(State* st) { IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH); IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel); return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionHigherPriority(State* st) { bool changed = changePriority((MainPanel*)st->panel, -1); return changed ? HTOP_REFRESH : HTOP_OK; } static Htop_Reaction actionLowerPriority(State* st) { bool changed = changePriority((MainPanel*)st->panel, 1); return changed ? HTOP_REFRESH : HTOP_OK; } static Htop_Reaction actionInvertSortOrder(State* st) { Settings_invertSortOrder(st->settings); if (st->pauseProcessUpdate) ProcessList_sort(st->pl); return HTOP_REFRESH | HTOP_SAVE_SETTINGS; } static Htop_Reaction actionExpandOrCollapse(State* st) { bool changed = expandCollapse(st->panel); return changed ? HTOP_RECALCULATE : HTOP_OK; } static Htop_Reaction actionCollapseIntoParent(State* st) { if (!st->settings->treeView) { return HTOP_OK; } bool changed = collapseIntoParent(st->panel); return changed ? HTOP_RECALCULATE : HTOP_OK; } static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) { return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); } 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 (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)) Panel* panel = st->panel; Process* p = (Process*) Panel_getSelected(panel); if (!p) return HTOP_OK; Affinity* affinity1 = Affinity_get(p, st->pl); if (!affinity1) return HTOP_OK; int width; Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width); width += 1; /* we add a gap between the panels */ Affinity_delete(affinity1); void* set = Action_pickFromVector(st, affinityPanel, width, true); if (set) { Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl); bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL); if (!ok) beep(); Affinity_delete(affinity2); } Object_delete(affinityPanel); #endif return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionKill(State* st) { 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, false, true, true, State_hideFunctionBar(st)); refresh(); MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL); napms(500); } } Panel_delete((Object*)signalsPanel); return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionFilterByUser(State* st) { Panel* usersPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Show ", "Cancel ")); Panel_setHeader(usersPanel, "Show processes of:"); UsersTable_foreach(st->ut, addUserToVector, usersPanel); Vector_insertionSort(usersPanel->items); ListItem* allUsers = ListItem_new("All users", -1); Panel_insert(usersPanel, 0, (Object*) allUsers); ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false); if (picked) { if (picked == allUsers) { st->pl->userId = (uid_t)-1; } else { Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId)); } } Panel_delete((Object*)usersPanel); return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } Htop_Reaction Action_follow(State* st) { st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel); Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW); return HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionSetup(State* st) { Action_runSetup(st); // TODO: shouldn't need this, colors should be dynamic int headerHeight = Header_calculateHeight(st->header); Panel_move(st->panel, 0, headerHeight); Panel_resize(st->panel, COLS, LINES-headerHeight-1); return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionLsof(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); if (!p) return HTOP_OK; OpenFilesScreen* ofs = OpenFilesScreen_new(p); InfoScreen_run((InfoScreen*)ofs); OpenFilesScreen_delete((Object*)ofs); clear(); CRT_enableDelay(); return HTOP_REFRESH | HTOP_REDRAW_BAR; } 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) { InfoScreen_run((InfoScreen*)ts); } TraceScreen_delete((Object*)ts); clear(); CRT_enableDelay(); return HTOP_REFRESH | HTOP_REDRAW_BAR; } static Htop_Reaction actionTag(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); if (!p) return HTOP_OK; Process_toggleTag(p); Panel_onKey(st->panel, KEY_DOWN); return HTOP_OK; } static Htop_Reaction actionRedraw(ATTR_UNUSED State* st) { clear(); return HTOP_REFRESH | HTOP_REDRAW_BAR; } 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" }, { .key = " F: ", .info = "cursor follows process" }, { .key = " + -: ", .info = "expand/collapse tree" }, { .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 = NULL, .info = NULL } }; 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 (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 = " 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++) mvhline(i, 0, ' ', COLS); 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]); line++; mvaddstr(line++, 0, "CPU usage bar: "); addattrstr(CRT_colors[BAR_BORDER], "["); if (settings->detailedCPUTime) { addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/"); addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/"); addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/"); addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/"); addattrstr(CRT_colors[CPU_IOWAIT], "io-wait"); addattrstr(CRT_colors[BAR_SHADOW], " used%"); } else { addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/"); addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/"); addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/"); addattrstr(CRT_colors[CPU_GUEST], "virtualiz"); addattrstr(CRT_colors[BAR_SHADOW], " used%"); } addattrstr(CRT_colors[BAR_BORDER], "]"); attrset(CRT_colors[DEFAULT_COLOR]); 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("/"); addattrstr(CRT_colors[MEMORY_CACHE], "cache"); addattrstr(CRT_colors[BAR_SHADOW], " used/total"); addattrstr(CRT_colors[BAR_BORDER], "]"); attrset(CRT_colors[DEFAULT_COLOR]); 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(line++, 0, "Type and layout of header meters are configurable in the setup screen."); if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { mvaddstr(line, 0, "In monochrome, meters display as different chars, in order: |#*@$%&."); } 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, 10, helpLeft[item].info); attrset(CRT_colors[HELP_BOLD]); mvaddstr(line + item, 1, helpLeft[item].key); if (String_eq(helpLeft[item].key, " H: ")) { attrset(CRT_colors[PROCESS_THREAD]); mvaddstr(line + item, 33, "threads"); } else if (String_eq(helpLeft[item].key, " K: ")) { attrset(CRT_colors[PROCESS_THREAD]); mvaddstr(line + item, 27, "threads"); } } int leftHelpItems = item; for (item = 0; helpRight[item].key; item++) { attrset(CRT_colors[HELP_BOLD]); mvaddstr(line + item, 41, helpRight[item].key); attrset(CRT_colors[DEFAULT_COLOR]); mvaddstr(line + item, 50, helpRight[item].info); } line += MAXIMUM(leftHelpItems, item); line++; attrset(CRT_colors[HELP_BOLD]); mvaddstr(line++, 0, "Press any key to return."); attrset(CRT_colors[DEFAULT_COLOR]); refresh(); CRT_readKey(); clear(); return HTOP_RECALCULATE | HTOP_REDRAW_BAR; } static Htop_Reaction actionUntagAll(State* st) { for (int i = 0; i < Panel_size(st->panel); i++) { Process* p = (Process*) Panel_get(st->panel, i); p->tag = false; } return HTOP_REFRESH; } static Htop_Reaction actionTagAllChildren(State* st) { Process* p = (Process*) Panel_getSelected(st->panel); 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; EnvScreen* es = EnvScreen_new(p); InfoScreen_run((InfoScreen*)es); EnvScreen_delete((Object*)es); clear(); CRT_enableDelay(); 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[' '] = actionTag; keys['+'] = actionExpandOrCollapse; keys[','] = actionSetSortColumn; keys['-'] = actionExpandOrCollapse; keys['.'] = actionSetSortColumn; keys['/'] = actionIncSearch; keys['<'] = actionSetSortColumn; keys['='] = actionExpandOrCollapse; keys['>'] = actionSetSortColumn; keys['?'] = actionHelp; keys['C'] = actionSetup; keys['F'] = Action_follow; keys['H'] = actionToggleUserlandThreads; keys['I'] = actionInvertSortOrder; keys['K'] = actionToggleKernelThreads; keys['M'] = actionSortByMemory; keys['N'] = actionSortByPID; keys['P'] = actionSortByCPU; keys['S'] = actionSetup; keys['T'] = actionSortByTime; keys['U'] = actionUntagAll; keys['Z'] = actionTogglePauseProcessUpdate; keys['['] = actionLowerPriority; keys['\014'] = actionRedraw; // Ctrl+L keys['\177'] = actionCollapseIntoParent; keys['\\'] = actionIncFilter; keys[']'] = actionHigherPriority; keys['a'] = actionSetAffinity; keys['c'] = actionTagAllChildren; keys['e'] = actionShowEnvScreen; keys['h'] = actionHelp; keys['k'] = actionKill; keys['l'] = actionLsof; keys['m'] = actionToggleMergedCommand; keys['p'] = actionToggleProgramPath; keys['q'] = actionQuit; keys['s'] = actionStrace; keys['t'] = actionToggleTreeView; keys['u'] = actionFilterByUser; keys['w'] = actionShowCommandScreen; keys['x'] = actionShowLocks; keys[KEY_F(1)] = actionHelp; keys[KEY_F(2)] = actionSetup; keys[KEY_F(3)] = actionIncSearch; keys[KEY_F(4)] = actionIncFilter; keys[KEY_F(5)] = actionToggleTreeView; keys[KEY_F(6)] = actionSetSortColumn; keys[KEY_F(7)] = actionHigherPriority; keys[KEY_F(8)] = actionLowerPriority; keys[KEY_F(9)] = actionKill; keys[KEY_F(10)] = actionQuit; keys[KEY_F(18)] = actionExpandCollapseOrSortColumn; keys[KEY_RECLICK] = actionExpandOrCollapse; } htop-3.0.5/Action.h000066400000000000000000000026601377712513700141000ustar00rootroot00000000000000#ifndef HEADER_Action #define HEADER_Action /* htop - Action.h (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include "Header.h" #include "Object.h" #include "Panel.h" #include "Process.h" #include "ProcessList.h" #include "Settings.h" #include "UsersTable.h" typedef enum { HTOP_OK = 0x00, HTOP_REFRESH = 0x01, HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH HTOP_SAVE_SETTINGS = 0x04, HTOP_KEEP_FOLLOWING = 0x08, HTOP_QUIT = 0x10, HTOP_REDRAW_BAR = 0x20, HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH } Htop_Reaction; typedef struct State_ { Settings* settings; UsersTable* ut; ProcessList* pl; Panel* panel; Header* header; bool pauseProcessUpdate; bool hideProcessSelection; } State; static inline bool State_hideFunctionBar(const State* st) { return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideProcessSelection); } typedef Htop_Reaction (*Htop_Action)(State* st); Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess); bool Action_setUserOnly(const char* userName, uid_t* userId); Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey); Htop_Reaction Action_follow(State* st); void Action_setBindings(Htop_Action* keys); #endif htop-3.0.5/Affinity.c000066400000000000000000000054431377712513700144310ustar00rootroot00000000000000/* htop - Affinity.c (C) 2004-2011 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Affinity.h" #include #include "XUtils.h" #ifdef HAVE_LIBHWLOC #include #include #ifdef __linux__ #define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD #else #define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS #endif #elif defined(HAVE_LINUX_AFFINITY) #include #endif Affinity* Affinity_new(ProcessList* pl) { Affinity* this = xCalloc(1, sizeof(Affinity)); this->size = 8; this->cpus = xCalloc(this->size, sizeof(int)); this->pl = pl; return this; } void Affinity_delete(Affinity* this) { free(this->cpus); free(this); } void Affinity_add(Affinity* this, int id) { if (this->used == this->size) { this->size *= 2; this->cpus = xRealloc(this->cpus, sizeof(int) * this->size); } this->cpus[this->used] = id; this->used++; } #ifdef HAVE_LIBHWLOC Affinity* Affinity_get(Process* proc, ProcessList* pl) { hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); Affinity* affinity = NULL; if (ok) { affinity = Affinity_new(pl); if (hwloc_bitmap_last(cpuset) == -1) { for (int i = 0; i < pl->cpuCount; i++) { Affinity_add(affinity, i); } } else { unsigned int id; hwloc_bitmap_foreach_begin(id, cpuset); Affinity_add(affinity, id); hwloc_bitmap_foreach_end(); } } hwloc_bitmap_free(cpuset); return affinity; } bool Affinity_set(Process* proc, Arg arg) { Affinity* this = arg.v; hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); for (int i = 0; i < this->used; i++) { hwloc_bitmap_set(cpuset, this->cpus[i]); } bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); hwloc_bitmap_free(cpuset); return ok; } #elif defined(HAVE_LINUX_AFFINITY) Affinity* Affinity_get(Process* proc, ProcessList* pl) { cpu_set_t cpuset; bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0); if (!ok) return NULL; Affinity* affinity = Affinity_new(pl); for (int i = 0; i < pl->cpuCount; i++) { if (CPU_ISSET(i, &cpuset)) { Affinity_add(affinity, i); } } return affinity; } bool Affinity_set(Process* proc, Arg arg) { Affinity* this = arg.v; cpu_set_t cpuset; CPU_ZERO(&cpuset); for (int i = 0; i < this->used; i++) { CPU_SET(this->cpus[i], &cpuset); } bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0); return ok; } #endif htop-3.0.5/Affinity.h000066400000000000000000000017651377712513700144410ustar00rootroot00000000000000#ifndef HEADER_Affinity #define HEADER_Affinity /* htop - Affinity.h (C) 2004-2011 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "ProcessList.h" #if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY) #include #include "Object.h" #include "Process.h" #endif #if defined(HAVE_LIBHWLOC) && defined(HAVE_LINUX_AFFINITY) #error hwloc and linux affinity are mutual exclusive. #endif typedef struct Affinity_ { ProcessList* pl; int size; int used; int* cpus; } Affinity; Affinity* Affinity_new(ProcessList* pl); void Affinity_delete(Affinity* this); void Affinity_add(Affinity* this, int id); #if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY) Affinity* Affinity_get(Process* proc, ProcessList* pl); bool Affinity_set(Process* proc, Arg arg); #endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */ #endif htop-3.0.5/AffinityPanel.c000066400000000000000000000300711377712513700154040ustar00rootroot00000000000000/* htop - AffinityPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "AffinityPanel.h" #include #include #include #include #include "CRT.h" #include "FunctionBar.h" #include "Object.h" #include "ProvideCurses.h" #include "RichString.h" #include "Settings.h" #include "Vector.h" #include "XUtils.h" #ifdef HAVE_LIBHWLOC #include #include #endif typedef struct MaskItem_ { Object super; char* text; char* indent; /* used also as an condition whether this is a tree node */ int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */ int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */ Vector* children; #ifdef HAVE_LIBHWLOC bool ownCpuset; hwloc_bitmap_t cpuset; #else int cpu; #endif } MaskItem; static void MaskItem_delete(Object* cast) { MaskItem* this = (MaskItem*) cast; free(this->text); free(this->indent); Vector_delete(this->children); #ifdef HAVE_LIBHWLOC if (this->ownCpuset) hwloc_bitmap_free(this->cpuset); #endif free(this); } static void MaskItem_display(const Object* cast, RichString* out) { const MaskItem* this = (const MaskItem*)cast; assert (this != NULL); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "["); if (this->value == 2) { RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x"); } else if (this->value == 1) { RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o"); } else { RichString_appendAscii(out, CRT_colors[CHECK_MARK], " "); } RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]"); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " "); if (this->indent) { RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent); RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->sub_tree == 2 ? CRT_treeStr[TREE_STR_OPEN] : CRT_treeStr[TREE_STR_SHUT]); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " "); } RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text); } static const ObjectClass MaskItem_class = { .display = MaskItem_display, .delete = MaskItem_delete }; #ifdef HAVE_LIBHWLOC static MaskItem* MaskItem_newMask(const char* text, const char* indent, hwloc_bitmap_t cpuset, bool owner) { MaskItem* this = AllocThis(MaskItem); this->text = xStrdup(text); this->indent = xStrdup(indent); /* nonnull for tree node */ this->value = 0; this->ownCpuset = owner; this->cpuset = cpuset; this->sub_tree = hwloc_bitmap_weight(cpuset) > 1 ? 1 : 0; this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE); return this; } #endif static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) { MaskItem* this = AllocThis(MaskItem); this->text = xStrdup(text); this->indent = NULL; /* not a tree node */ this->sub_tree = 0; this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE); #ifdef HAVE_LIBHWLOC this->ownCpuset = true; this->cpuset = hwloc_bitmap_alloc(); hwloc_bitmap_set(this->cpuset, cpu); #else this->cpu = cpu; #endif this->value = isSet ? 2 : 0; return this; } typedef struct AffinityPanel_ { Panel super; ProcessList* pl; bool topoView; Vector* cpuids; unsigned width; #ifdef HAVE_LIBHWLOC MaskItem* topoRoot; hwloc_const_cpuset_t allCpuset; hwloc_bitmap_t workCpuset; #endif } AffinityPanel; static void AffinityPanel_delete(Object* cast) { AffinityPanel* this = (AffinityPanel*) cast; Panel* super = (Panel*) this; Panel_done(super); Vector_delete(this->cpuids); #ifdef HAVE_LIBHWLOC hwloc_bitmap_free(this->workCpuset); MaskItem_delete((Object*) this->topoRoot); #endif free(this); } #ifdef HAVE_LIBHWLOC static void AffinityPanel_updateItem(AffinityPanel* this, MaskItem* item) { Panel* super = (Panel*) this; item->value = hwloc_bitmap_isincluded(item->cpuset, this->workCpuset) ? 2 : hwloc_bitmap_intersects(item->cpuset, this->workCpuset) ? 1 : 0; Panel_add(super, (Object*) item); } static void AffinityPanel_updateTopo(AffinityPanel* this, MaskItem* item) { AffinityPanel_updateItem(this, item); if (item->sub_tree == 2) return; for (int i = 0; i < Vector_size(item->children); i++) AffinityPanel_updateTopo(this, (MaskItem*) Vector_get(item->children, i)); } #endif static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) { Panel* super = (Panel*) this; FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : ""); int oldSelected = Panel_getSelectedIndex(super); Panel_prune(super); #ifdef HAVE_LIBHWLOC if (this->topoView) { AffinityPanel_updateTopo(this, this->topoRoot); } else { for (int i = 0; i < Vector_size(this->cpuids); i++) { AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i)); } } #else Panel_splice(super, this->cpuids); #endif if (keepSelected) Panel_setSelected(super, oldSelected); super->needsRedraw = true; } static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) { AffinityPanel* this = (AffinityPanel*) super; HandlerResult result = IGNORED; MaskItem* selected = (MaskItem*) Panel_getSelected(super); bool keepSelected = true; switch(ch) { case KEY_MOUSE: case KEY_RECLICK: case ' ': #ifdef HAVE_LIBHWLOC if (selected->value == 2) { /* Item was selected, so remove this mask from the top cpuset. */ hwloc_bitmap_andnot(this->workCpuset, this->workCpuset, selected->cpuset); selected->value = 0; } else { /* Item was not or only partial selected, so set all bits from this object in the top cpuset. */ hwloc_bitmap_or(this->workCpuset, this->workCpuset, selected->cpuset); selected->value = 2; } #else selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */ #endif result = HANDLED; break; #ifdef HAVE_LIBHWLOC case KEY_F(1): hwloc_bitmap_copy(this->workCpuset, this->allCpuset); result = HANDLED; break; case KEY_F(2): this->topoView = !this->topoView; keepSelected = false; result = HANDLED; break; case KEY_F(3): case '-': case '+': if (selected->sub_tree) selected->sub_tree = 1 + !(selected->sub_tree - 1); /* toggle between 1 and 2 */ result = HANDLED; break; #endif case 0x0a: case 0x0d: case KEY_ENTER: result = BREAK_LOOP; break; } if (HANDLED == result) AffinityPanel_update(this, keepSelected); return result; } #ifdef HAVE_LIBHWLOC static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) { const char* type_name = hwloc_obj_type_string(obj->type); const char* index_prefix = "#"; unsigned depth = obj->depth; unsigned index = obj->logical_index; size_t off = 0, left = 10 * depth; char buf[64], indent_buf[left + 1]; if (obj->type == HWLOC_OBJ_PU) { index = Settings_cpuId(this->pl->settings, obj->os_index); type_name = "CPU"; index_prefix = ""; } indent_buf[0] = '\0'; if (depth > 0) { for (unsigned i = 1; i < depth; i++) { xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " "); size_t len = strlen(&indent_buf[off]); off += len; left -= len; } xSnprintf(&indent_buf[off], left, "%s", obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]); // Uncomment when further appending to indent_buf //size_t len = strlen(&indent_buf[off]); //off += len; //left -= len; } xSnprintf(buf, sizeof(buf), "%s %s%u", type_name, index_prefix, index); MaskItem* item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false); if (parent) Vector_add(parent->children, item); if (item->sub_tree && parent && parent->sub_tree == 1) { /* if obj is fully included or fully excluded, collapse the item */ hwloc_bitmap_t result = hwloc_bitmap_alloc(); hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset); int weight = hwloc_bitmap_weight(result); hwloc_bitmap_free(result); if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) { item->sub_tree = 2; } } /* "[x] " + "|- " * depth + ("- ")?(if root node) + name */ unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf); if (width > this->width) { this->width = width; } return item; } static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) { MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent); if (obj->next_sibling) { indent |= (1U << obj->depth); } else { indent &= ~(1U << obj->depth); } for (unsigned i = 0; i < obj->arity; i++) { AffinityPanel_buildTopology(this, obj->children[i], indent, item); } return parent == NULL ? item : NULL; } #endif const PanelClass AffinityPanel_class = { .super = { .extends = Class(Panel), .delete = AffinityPanel_delete }, .eventHandler = AffinityPanel_eventHandler }; static const char* const AffinityPanelFunctions[] = { "Set ", "Cancel ", #ifdef HAVE_LIBHWLOC "All", "Topology", " ", #endif NULL }; static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"}; static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)}; Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) { AffinityPanel* this = AllocThis(AffinityPanel); Panel* super = (Panel*) this; Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents)); this->pl = pl; /* defaults to 15, this also includes the gap between the panels, * but this will be added by the caller */ this->width = 14; this->cpuids = Vector_new(Class(MaskItem), true, DEFAULT_SIZE); #ifdef HAVE_LIBHWLOC this->topoView = pl->settings->topologyAffinity; #else this->topoView = false; #endif #ifdef HAVE_LIBHWLOC this->allCpuset = hwloc_topology_get_complete_cpuset(pl->topology); this->workCpuset = hwloc_bitmap_alloc(); #endif Panel_setHeader(super, "Use CPUs:"); int curCpu = 0; for (int i = 0; i < pl->cpuCount; i++) { char number[16]; xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i)); unsigned cpu_width = 4 + strlen(number); if (cpu_width > this->width) { this->width = cpu_width; } bool isSet = false; if (curCpu < affinity->used && affinity->cpus[curCpu] == i) { #ifdef HAVE_LIBHWLOC hwloc_bitmap_set(this->workCpuset, i); #endif isSet = true; curCpu++; } MaskItem* cpuItem = MaskItem_newSingleton(number, i, isSet); Vector_add(this->cpuids, (Object*) cpuItem); } #ifdef HAVE_LIBHWLOC this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL); #endif if (width) { *width = this->width; } AffinityPanel_update(this, false); return super; } Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) { AffinityPanel* this = (AffinityPanel*) super; Affinity* affinity = Affinity_new(pl); #ifdef HAVE_LIBHWLOC int i; hwloc_bitmap_foreach_begin(i, this->workCpuset) Affinity_add(affinity, i); hwloc_bitmap_foreach_end(); #else for (int i = 0; i < this->pl->cpuCount; i++) { MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i); if (item->value) { Affinity_add(affinity, item->cpu); } } #endif return affinity; } htop-3.0.5/AffinityPanel.h000066400000000000000000000007431377712513700154140ustar00rootroot00000000000000#ifndef HEADER_AffinityPanel #define HEADER_AffinityPanel /* htop - AffinityPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Panel.h" #include "Affinity.h" #include "ProcessList.h" extern const PanelClass AffinityPanel_class; Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width); Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl); #endif htop-3.0.5/AvailableColumnsPanel.c000066400000000000000000000051511377712513700170550ustar00rootroot00000000000000/* htop - AvailableColumnsPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "AvailableColumnsPanel.h" #include #include #include #include "ColumnsPanel.h" #include "FunctionBar.h" #include "ListItem.h" #include "Object.h" #include "Platform.h" #include "Process.h" #include "ProvideCurses.h" #include "XUtils.h" static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL}; static void AvailableColumnsPanel_delete(Object* object) { Panel* super = (Panel*) object; AvailableColumnsPanel* this = (AvailableColumnsPanel*) object; Panel_done(super); free(this); } static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { AvailableColumnsPanel* this = (AvailableColumnsPanel*) super; HandlerResult result = IGNORED; switch(ch) { case 13: case KEY_ENTER: case KEY_F(5): { const ListItem* selected = (ListItem*) Panel_getSelected(super); if (!selected) break; int key = selected->key; int at = Panel_getSelectedIndex(this->columns); Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key)); Panel_setSelected(this->columns, at+1); ColumnsPanel_update(this->columns); result = HANDLED; break; } default: { if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) result = Panel_selectByTyping(super, ch); break; } } return result; } const PanelClass AvailableColumnsPanel_class = { .super = { .extends = Class(Panel), .delete = AvailableColumnsPanel_delete }, .eventHandler = AvailableColumnsPanel_eventHandler }; AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) { AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); Panel_setHeader(super, "Available Columns"); for (int i = 1; i < LAST_PROCESSFIELD; i++) { if (i != COMM && Process_fields[i].description) { char description[256]; xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description); Panel_add(super, (Object*) ListItem_new(description, i)); } } this->columns = columns; return this; } htop-3.0.5/AvailableColumnsPanel.h000066400000000000000000000007531377712513700170650ustar00rootroot00000000000000#ifndef HEADER_AvailableColumnsPanel #define HEADER_AvailableColumnsPanel /* htop - AvailableColumnsPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Panel.h" typedef struct AvailableColumnsPanel_ { Panel super; Panel* columns; } AvailableColumnsPanel; extern const PanelClass AvailableColumnsPanel_class; AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns); #endif htop-3.0.5/AvailableMetersPanel.c000066400000000000000000000076631377712513700167060ustar00rootroot00000000000000/* htop - AvailableMetersPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "AvailableMetersPanel.h" #include #include #include #include "CPUMeter.h" #include "FunctionBar.h" #include "Header.h" #include "ListItem.h" #include "Meter.h" #include "MetersPanel.h" #include "Object.h" #include "Platform.h" #include "ProvideCurses.h" #include "XUtils.h" static void AvailableMetersPanel_delete(Object* object) { Panel* super = (Panel*) object; AvailableMetersPanel* this = (AvailableMetersPanel*) object; Panel_done(super); free(this); } static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, int param, int column) { Meter* meter = Header_addMeterByClass(header, type, param, column); Panel_add(panel, (Object*) Meter_toListItem(meter, false)); Panel_setSelected(panel, Panel_size(panel) - 1); MetersPanel_setMoving((MetersPanel*)panel, true); } static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { AvailableMetersPanel* this = (AvailableMetersPanel*) super; Header* header = this->header; const ListItem* selected = (ListItem*) Panel_getSelected(super); if (!selected) return IGNORED; int param = selected->key & 0xff; int type = selected->key >> 16; HandlerResult result = IGNORED; bool update = false; switch(ch) { case KEY_F(5): case 'l': case 'L': { AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0); result = HANDLED; update = true; break; } case 0x0a: case 0x0d: case KEY_ENTER: case KEY_F(6): case 'r': case 'R': { AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1); result = (KEY_LEFT << 16) | SYNTH_KEY; update = true; break; } } if (update) { this->settings->changed = true; Header_calculateHeight(header); Header_draw(header); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); } return result; } const PanelClass AvailableMetersPanel_class = { .super = { .extends = Class(Panel), .delete = AvailableMetersPanel_delete }, .eventHandler = AvailableMetersPanel_eventHandler }; AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl) { AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); this->settings = settings; this->header = header; this->leftPanel = leftMeters; this->rightPanel = rightMeters; this->scr = scr; Panel_setHeader(super, "Available meters"); // Platform_meterTypes[0] should be always (&CPUMeter_class), which we will // handle separately in the code below. for (int i = 1; Platform_meterTypes[i]; i++) { const MeterClass* type = Platform_meterTypes[i]; assert(type != &CPUMeter_class); const char* label = type->description ? type->description : type->uiName; Panel_add(super, (Object*) ListItem_new(label, i << 16)); } // Handle (&CPUMeter_class) const MeterClass* type = &CPUMeter_class; int cpus = pl->cpuCount; if (cpus > 1) { Panel_add(super, (Object*) ListItem_new("CPU average", 0)); for (int i = 1; i <= cpus; i++) { char buffer[50]; xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1)); Panel_add(super, (Object*) ListItem_new(buffer, i)); } } else { Panel_add(super, (Object*) ListItem_new("CPU", 1)); } return this; } htop-3.0.5/AvailableMetersPanel.h000066400000000000000000000013731377712513700167030ustar00rootroot00000000000000#ifndef HEADER_AvailableMetersPanel #define HEADER_AvailableMetersPanel /* htop - AvailableMetersPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Header.h" #include "Panel.h" #include "ProcessList.h" #include "ScreenManager.h" #include "Settings.h" typedef struct AvailableMetersPanel_ { Panel super; ScreenManager* scr; Settings* settings; Header* header; Panel* leftPanel; Panel* rightPanel; } AvailableMetersPanel; extern const PanelClass AvailableMetersPanel_class; AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl); #endif htop-3.0.5/BatteryMeter.c000066400000000000000000000026751377712513700152730ustar00rootroot00000000000000/* htop - BatteryMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). */ #include "BatteryMeter.h" #include #include "CRT.h" #include "Object.h" #include "Platform.h" #include "XUtils.h" static const int BatteryMeter_attributes[] = { BATTERY }; static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) { ACPresence isOnAC; double percent; Platform_getBattery(&percent, &isOnAC); if (isnan(percent)) { this->values[0] = NAN; xSnprintf(buffer, len, "N/A"); return; } this->values[0] = percent; const char* text; switch (isOnAC) { case AC_PRESENT: text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)"; break; case AC_ABSENT: text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)"; break; case AC_ERROR: default: text = ""; break; } xSnprintf(buffer, len, "%.1f%%%s", percent, text); } const MeterClass BatteryMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete }, .updateValues = BatteryMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 100.0, .attributes = BatteryMeter_attributes, .name = "Battery", .uiName = "Battery", .caption = "Battery: " }; htop-3.0.5/BatteryMeter.h000066400000000000000000000006761377712513700152770ustar00rootroot00000000000000#ifndef HEADER_BatteryMeter #define HEADER_BatteryMeter /* htop - BatteryMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). */ #include "Meter.h" typedef enum ACPresence_ { AC_ABSENT, AC_PRESENT, AC_ERROR } ACPresence; extern const MeterClass BatteryMeter_class; #endif htop-3.0.5/CONTRIBUTING.md000066400000000000000000000034601377712513700147420ustar00rootroot00000000000000Contributing Guide ================== Thank you so much for taking the time to contribute in to htop! Bug Reports ----------- Bug reports should be posted in the [Github issue tracker](https://github.com/htop-dev/htop/issues). Bug reports are extremely important since it's impossible for us to test htop in every possible system, distribution and scenario. Your feedback is what keeps the tool stable and always improving! Thank you! Pull Requests ------------- Code contributions are most welcome! Just [fork the repo](https://github.com/htop-dev/htop) and send a [pull request](https://github.com/htop-dev/htop/pulls). Help is especially appreciated for support of platforms other than Linux. If proposing new features, please be mindful that htop is a system tool that needs to keep a small footprint and perform well on systems under stress -- so unfortunately we can't accept every new feature proposed, as we need to keep the tool slim and maintainable. Great ideas backed by a PR are always carefully considered for inclusion though! Also, PRs containing bug fixes and portability tweaks are always included, please send those in! Feature Requests ---------------- Please label Github issues that are feature requests with the [`feature request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+) label. Style Guide ----------- To make working with the code easier a set of guidelines have evolved in the past that new contributions should try to follow. While they are not set in stone and always up for changes should the need arise they still provide a first orientation to go by when contributing to this repository. The details of the coding style as well as what to take care about with your contributions can be found in our [style guide](docs/styleguide.md). htop-3.0.5/COPYING000066400000000000000000000445011377712513700135450ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. Appendix 2: Special exception concerning PLPA In the following exception, "PLPA" means (i) code released by the Portable Linux Processor Affinity Project, or (ii) derivative works of such code, in both cases provided that the code is covered entirely by free software licensing terms. As a special exception to the GNU GPL, the licensors of htop give you permission to combine GNU GPL-licensed code in htop (and derivative works of such code) with PLPA. You may copy and distribute such a combined work following the terms of the GNU GPL for htop and the applicable licenses of the version of PLPA used in your combined work, provided that you include the source code of such version of PLPA when and as the GNU GPL requires distribution of source code. htop-3.0.5/CPUMeter.c000066400000000000000000000421011377712513700142740ustar00rootroot00000000000000/* htop - CPUMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "CPUMeter.h" #include #include #include #include #include "CRT.h" #include "Object.h" #include "Platform.h" #include "ProcessList.h" #include "RichString.h" #include "Settings.h" #include "XUtils.h" static const int CPUMeter_attributes[] = { CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT }; typedef struct CPUMeterData_ { int cpus; Meter** meters; } CPUMeterData; static void CPUMeter_init(Meter* this) { int cpu = this->param; if (this->pl->cpuCount > 1) { char caption[10]; xSnprintf(caption, sizeof(caption), "%3d", Settings_cpuId(this->pl->settings, cpu - 1)); Meter_setCaption(this, caption); } if (this->param == 0) Meter_setCaption(this, "Avg"); } static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) { int cpu = this->param; if (cpu > this->pl->cpuCount) { xSnprintf(buffer, size, "absent"); for (uint8_t i = 0; i < this->curItems; i++) this->values[i] = 0; return; } memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); char cpuUsageBuffer[8] = { 0 }; char cpuFrequencyBuffer[16] = { 0 }; char cpuTemperatureBuffer[16] = { 0 }; double percent = Platform_setCPUValues(this, cpu); if (this->pl->settings->showCPUUsage) { xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent); } if (this->pl->settings->showCPUFrequency) { double cpuFrequency = this->values[CPU_METER_FREQUENCY]; if (isnan(cpuFrequency)) { xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A"); } else { xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz", (unsigned)cpuFrequency); } } #ifdef HAVE_SENSORS_SENSORS_H if (this->pl->settings->showCPUTemperature) { double cpuTemperature = this->values[CPU_METER_TEMPERATURE]; if (isnan(cpuTemperature)) xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A"); else if (this->pl->settings->degreeFahrenheit) xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%3d%sF", (int)(cpuTemperature * 9 / 5 + 32), CRT_degreeSign); else xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%d%sC", (int)cpuTemperature, CRT_degreeSign); } #endif xSnprintf(buffer, size, "%s%s%s%s%s", cpuUsageBuffer, (cpuUsageBuffer[0] && (cpuFrequencyBuffer[0] || cpuTemperatureBuffer[0])) ? " " : "", cpuFrequencyBuffer, (cpuFrequencyBuffer[0] && cpuTemperatureBuffer[0]) ? " " : "", cpuTemperatureBuffer); } static void CPUMeter_display(const Object* cast, RichString* out) { char buffer[50]; const Meter* this = (const Meter*)cast; RichString_prune(out); if (this->param > this->pl->cpuCount) { RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent"); return; } xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]); RichString_appendAscii(out, CRT_colors[METER_TEXT], ":"); RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer); if (this->pl->settings->detailedCPUTime) { xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:"); RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:"); RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:"); RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:"); RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer); if (!isnan(this->values[CPU_METER_STEAL])) { xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:"); RichString_appendAscii(out, CRT_colors[CPU_STEAL], buffer); } if (!isnan(this->values[CPU_METER_GUEST])) { xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:"); RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer); } xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:"); RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer); } else { xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:"); RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:"); RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer); if (!isnan(this->values[CPU_METER_IRQ])) { xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:"); RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer); } } #ifdef HAVE_SENSORS_SENSORS_H if (this->pl->settings->showCPUTemperature) { char cpuTemperatureBuffer[10]; double cpuTemperature = this->values[CPU_METER_TEMPERATURE]; if (isnan(cpuTemperature)) { xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A"); } else if (this->pl->settings->degreeFahrenheit) { xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign); } else { xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign); } RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:"); RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer); } #endif } static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) { CPUMeterData* data = this->meterData; int cpus = data->cpus; switch(Meter_name(this)[0]) { default: case 'A': // All *start = 0; *count = cpus; break; case 'L': // First Half *start = 0; *count = (cpus+1) / 2; break; case 'R': // Second Half *start = (cpus+1) / 2; *count = cpus / 2; break; } } static void CPUMeterCommonInit(Meter* this, int ncol) { int cpus = this->pl->cpuCount; CPUMeterData* data = this->meterData; if (!data) { data = this->meterData = xMalloc(sizeof(CPUMeterData)); data->cpus = cpus; data->meters = xCalloc(cpus, sizeof(Meter*)); } Meter** meters = data->meters; int start, count; AllCPUsMeter_getRange(this, &start, &count); for (int i = 0; i < count; i++) { if (!meters[i]) meters[i] = Meter_new(this->pl, start + i + 1, (const MeterClass*) Class(CPUMeter)); Meter_init(meters[i]); } if (this->mode == 0) this->mode = BAR_METERMODE; int h = Meter_modes[this->mode]->h; this->h = h * ((count + ncol - 1) / ncol); } static void CPUMeterCommonUpdateMode(Meter* this, int mode, int ncol) { CPUMeterData* data = this->meterData; Meter** meters = data->meters; this->mode = mode; int h = Meter_modes[mode]->h; int start, count; AllCPUsMeter_getRange(this, &start, &count); for (int i = 0; i < count; i++) { Meter_setMode(meters[i], mode); } this->h = h * ((count + ncol - 1) / ncol); } static void AllCPUsMeter_done(Meter* this) { CPUMeterData* data = this->meterData; Meter** meters = data->meters; int start, count; AllCPUsMeter_getRange(this, &start, &count); for (int i = 0; i < count; i++) Meter_delete((Object*)meters[i]); free(data->meters); free(data); } static void SingleColCPUsMeter_init(Meter* this) { CPUMeterCommonInit(this, 1); } static void SingleColCPUsMeter_updateMode(Meter* this, int mode) { CPUMeterCommonUpdateMode(this, mode, 1); } static void DualColCPUsMeter_init(Meter* this) { CPUMeterCommonInit(this, 2); } static void DualColCPUsMeter_updateMode(Meter* this, int mode) { CPUMeterCommonUpdateMode(this, mode, 2); } static void QuadColCPUsMeter_init(Meter* this) { CPUMeterCommonInit(this, 4); } static void QuadColCPUsMeter_updateMode(Meter* this, int mode) { CPUMeterCommonUpdateMode(this, mode, 4); } static void OctoColCPUsMeter_init(Meter* this) { CPUMeterCommonInit(this, 8); } static void OctoColCPUsMeter_updateMode(Meter* this, int mode) { CPUMeterCommonUpdateMode(this, mode, 8); } static void CPUMeterCommonDraw(Meter* this, int x, int y, int w, int ncol) { CPUMeterData* data = this->meterData; Meter** meters = data->meters; int start, count; AllCPUsMeter_getRange(this, &start, &count); int colwidth = (w - ncol) / ncol + 1; int diff = (w - (colwidth * ncol)); int nrows = (count + ncol - 1) / ncol; for (int i = 0; i < count; i++) { int d = (i / nrows) > diff ? diff : (i / nrows); // dynamic spacer int xpos = x + ((i / nrows) * colwidth) + d; int ypos = y + ((i % nrows) * meters[0]->h); meters[i]->draw(meters[i], xpos, ypos, colwidth); } } static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) { CPUMeterCommonDraw(this, x, y, w, 2); } static void QuadColCPUsMeter_draw(Meter* this, int x, int y, int w) { CPUMeterCommonDraw(this, x, y, w, 4); } static void OctoColCPUsMeter_draw(Meter* this, int x, int y, int w) { CPUMeterCommonDraw(this, x, y, w, 8); } static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) { CPUMeterData* data = this->meterData; Meter** meters = data->meters; int start, count; AllCPUsMeter_getRange(this, &start, &count); for (int i = 0; i < count; i++) { meters[i]->draw(meters[i], x, y, w); y += meters[i]->h; } } const MeterClass CPUMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .updateValues = CPUMeter_updateValues, .defaultMode = BAR_METERMODE, .maxItems = CPU_METER_ITEMCOUNT, .total = 100.0, .attributes = CPUMeter_attributes, .name = "CPU", .uiName = "CPU", .caption = "CPU", .init = CPUMeter_init }; const MeterClass AllCPUsMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "AllCPUs", .uiName = "CPUs (1/1)", .description = "CPUs (1/1): all CPUs", .caption = "CPU", .draw = SingleColCPUsMeter_draw, .init = SingleColCPUsMeter_init, .updateMode = SingleColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass AllCPUs2Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "AllCPUs2", .uiName = "CPUs (1&2/2)", .description = "CPUs (1&2/2): all CPUs in 2 shorter columns", .caption = "CPU", .draw = DualColCPUsMeter_draw, .init = DualColCPUsMeter_init, .updateMode = DualColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass LeftCPUsMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "LeftCPUs", .uiName = "CPUs (1/2)", .description = "CPUs (1/2): first half of list", .caption = "CPU", .draw = SingleColCPUsMeter_draw, .init = SingleColCPUsMeter_init, .updateMode = SingleColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass RightCPUsMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "RightCPUs", .uiName = "CPUs (2/2)", .description = "CPUs (2/2): second half of list", .caption = "CPU", .draw = SingleColCPUsMeter_draw, .init = SingleColCPUsMeter_init, .updateMode = SingleColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass LeftCPUs2Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "LeftCPUs2", .uiName = "CPUs (1&2/4)", .description = "CPUs (1&2/4): first half in 2 shorter columns", .caption = "CPU", .draw = DualColCPUsMeter_draw, .init = DualColCPUsMeter_init, .updateMode = DualColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass RightCPUs2Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "RightCPUs2", .uiName = "CPUs (3&4/4)", .description = "CPUs (3&4/4): second half in 2 shorter columns", .caption = "CPU", .draw = DualColCPUsMeter_draw, .init = DualColCPUsMeter_init, .updateMode = DualColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass AllCPUs4Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "AllCPUs4", .uiName = "CPUs (1&2&3&4/4)", .description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns", .caption = "CPU", .draw = QuadColCPUsMeter_draw, .init = QuadColCPUsMeter_init, .updateMode = QuadColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass LeftCPUs4Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "LeftCPUs4", .uiName = "CPUs (1-4/8)", .description = "CPUs (1-4/8): first half in 4 shorter columns", .caption = "CPU", .draw = QuadColCPUsMeter_draw, .init = QuadColCPUsMeter_init, .updateMode = QuadColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass RightCPUs4Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "RightCPUs4", .uiName = "CPUs (5-8/8)", .description = "CPUs (5-8/8): second half in 4 shorter columns", .caption = "CPU", .draw = QuadColCPUsMeter_draw, .init = QuadColCPUsMeter_init, .updateMode = QuadColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass AllCPUs8Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "AllCPUs8", .uiName = "CPUs (1-8/8)", .description = "CPUs (1-8/8): all CPUs in 8 shorter columns", .caption = "CPU", .draw = OctoColCPUsMeter_draw, .init = OctoColCPUsMeter_init, .updateMode = OctoColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass LeftCPUs8Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "LeftCPUs8", .uiName = "CPUs (1-8/16)", .description = "CPUs (1-8/16): first half in 8 shorter columns", .caption = "CPU", .draw = OctoColCPUsMeter_draw, .init = OctoColCPUsMeter_init, .updateMode = OctoColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; const MeterClass RightCPUs8Meter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = CPUMeter_display }, .defaultMode = CUSTOM_METERMODE, .total = 100.0, .attributes = CPUMeter_attributes, .name = "RightCPUs8", .uiName = "CPUs (9-16/16)", .description = "CPUs (9-16/16): second half in 8 shorter columns", .caption = "CPU", .draw = OctoColCPUsMeter_draw, .init = OctoColCPUsMeter_init, .updateMode = OctoColCPUsMeter_updateMode, .done = AllCPUsMeter_done }; htop-3.0.5/CPUMeter.h000066400000000000000000000022351377712513700143050ustar00rootroot00000000000000#ifndef HEADER_CPUMeter #define HEADER_CPUMeter /* htop - CPUMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" typedef enum { CPU_METER_NICE = 0, CPU_METER_NORMAL = 1, CPU_METER_KERNEL = 2, CPU_METER_IRQ = 3, CPU_METER_SOFTIRQ = 4, CPU_METER_STEAL = 5, CPU_METER_GUEST = 6, CPU_METER_IOWAIT = 7, CPU_METER_FREQUENCY = 8, CPU_METER_TEMPERATURE = 9, CPU_METER_ITEMCOUNT = 10, // number of entries in this enum } CPUMeterValues; extern const MeterClass CPUMeter_class; extern const MeterClass AllCPUsMeter_class; extern const MeterClass AllCPUs2Meter_class; extern const MeterClass LeftCPUsMeter_class; extern const MeterClass RightCPUsMeter_class; extern const MeterClass LeftCPUs2Meter_class; extern const MeterClass RightCPUs2Meter_class; extern const MeterClass AllCPUs4Meter_class; extern const MeterClass LeftCPUs4Meter_class; extern const MeterClass RightCPUs4Meter_class; extern const MeterClass AllCPUs8Meter_class; extern const MeterClass LeftCPUs8Meter_class; extern const MeterClass RightCPUs8Meter_class; #endif htop-3.0.5/CRT.c000066400000000000000000001024701377712513700133060ustar00rootroot00000000000000/* htop - CRT.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "CRT.h" #include #include #include #include #include #include #include #include "ProvideCurses.h" #include "XUtils.h" #ifdef HAVE_EXECINFO_H #include #endif #define ColorIndex(i,j) ((7-(i))*8+(j)) #define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j)) #define Black COLOR_BLACK #define Red COLOR_RED #define Green COLOR_GREEN #define Yellow COLOR_YELLOW #define Blue COLOR_BLUE #define Magenta COLOR_MAGENTA #define Cyan COLOR_CYAN #define White COLOR_WHITE #define ColorPairGrayBlack ColorPair(Magenta,Magenta) #define ColorIndexGrayBlack ColorIndex(Magenta,Magenta) #define ColorPairWhiteDefault ColorPair(Red, Red) #define ColorIndexWhiteDefault ColorIndex(Red, Red) static const char* const CRT_treeStrAscii[LAST_TREE_STR] = { [TREE_STR_VERT] = "|", [TREE_STR_RTEE] = "`", [TREE_STR_BEND] = "`", [TREE_STR_TEND] = ",", [TREE_STR_OPEN] = "+", [TREE_STR_SHUT] = "-", [TREE_STR_ASC] = "+", [TREE_STR_DESC] = "-", }; #ifdef HAVE_LIBNCURSESW static const char* const CRT_treeStrUtf8[LAST_TREE_STR] = { [TREE_STR_VERT] = "\xe2\x94\x82", // │ [TREE_STR_RTEE] = "\xe2\x94\x9c", // ├ [TREE_STR_BEND] = "\xe2\x94\x94", // â”” [TREE_STR_TEND] = "\xe2\x94\x8c", // ┌ [TREE_STR_OPEN] = "+", // +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL // WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when // Unicode 13 is common [TREE_STR_SHUT] = "\xe2\x94\x80", // ─ [TREE_STR_ASC] = "\xe2\x96\xb3", // â–ł [TREE_STR_DESC] = "\xe2\x96\xbd", // â–˝ }; bool CRT_utf8 = false; #endif const char* const* CRT_treeStr = CRT_treeStrAscii; static const int* CRT_delay; const char* CRT_degreeSign; static const char* initDegreeSign(void) { #ifdef HAVE_LIBNCURSESW if (CRT_utf8) return "\xc2\xb0"; static char buffer[4]; // this might fail if the current locale does not support wide characters int r = snprintf(buffer, sizeof(buffer), "%lc", 176); if (r > 0) return buffer; #endif return ""; } const int* CRT_colors; static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [COLORSCHEME_DEFAULT] = { [RESET_COLOR] = ColorPair(White, Black), [DEFAULT_COLOR] = ColorPair(White, Black), [FUNCTION_BAR] = ColorPair(Black, Cyan), [FUNCTION_KEY] = ColorPair(White, Black), [PANEL_HEADER_FOCUS] = ColorPair(Black, Green), [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green), [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan), [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow), [PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White), [FAILED_SEARCH] = ColorPair(Red, Cyan), [FAILED_READ] = A_BOLD | ColorPair(Red, Black), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [UPTIME] = A_BOLD | ColorPair(Cyan, Black), [BATTERY] = A_BOLD | ColorPair(Cyan, Black), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black), [METER_TEXT] = ColorPair(Cyan, Black), [METER_VALUE] = A_BOLD | ColorPair(Cyan, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_IOREAD] = ColorPair(Green, Black), [METER_VALUE_IOWRITE] = ColorPair(Blue, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black), [METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black), [LED_COLOR] = ColorPair(Green, Black), [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black), [PROCESS] = A_NORMAL, [PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack, [PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black), [PROCESS_MEGABYTES] = ColorPair(Cyan, Black), [PROCESS_GIGABYTES] = ColorPair(Green, Black), [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Black), [PROCESS_TREE] = ColorPair(Cyan, Black), [PROCESS_R_STATE] = ColorPair(Green, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_NEW] = ColorPair(Black, Green), [PROCESS_TOMB] = ColorPair(Black, Red), [PROCESS_THREAD] = ColorPair(Green, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black), [PROCESS_COMM] = ColorPair(Magenta, Black), [PROCESS_THREAD_COMM] = ColorPair(Blue, Black), [BAR_BORDER] = A_BOLD, [BAR_SHADOW] = A_BOLD | ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), [MEMORY_USED] = ColorPair(Green, Black), [MEMORY_BUFFERS] = ColorPair(Blue, Black), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan, Black), [LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan, Black), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black), [LOAD] = A_BOLD, [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black), [CLOCK] = A_BOLD, [DATE] = A_BOLD, [DATETIME] = A_BOLD, [CHECK_BOX] = ColorPair(Cyan, Black), [CHECK_MARK] = A_BOLD, [CHECK_TEXT] = A_NORMAL, [HOSTNAME] = A_BOLD, [CPU_NICE] = ColorPair(Blue, Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue, Black), [CPU_NORMAL] = ColorPair(Green, Black), [CPU_SYSTEM] = ColorPair(Red, Black), [CPU_IOWAIT] = A_BOLD | ColorPairGrayBlack, [CPU_IRQ] = ColorPair(Yellow, Black), [CPU_SOFTIRQ] = ColorPair(Magenta, Black), [CPU_STEAL] = ColorPair(Cyan, Black), [CPU_GUEST] = ColorPair(Cyan, Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan, Black), [PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Black), [ZFS_MFU] = ColorPair(Blue, Black), [ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_ANON] = ColorPair(Magenta, Black), [ZFS_HEADER] = ColorPair(Cyan, Black), [ZFS_OTHER] = ColorPair(Magenta, Black), [ZFS_COMPRESSED] = ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), }, [COLORSCHEME_MONOCHROME] = { [RESET_COLOR] = A_NORMAL, [DEFAULT_COLOR] = A_NORMAL, [FUNCTION_BAR] = A_REVERSE, [FUNCTION_KEY] = A_NORMAL, [PANEL_HEADER_FOCUS] = A_REVERSE, [PANEL_HEADER_UNFOCUS] = A_REVERSE, [PANEL_SELECTION_FOCUS] = A_REVERSE, [PANEL_SELECTION_FOLLOW] = A_REVERSE, [PANEL_SELECTION_UNFOCUS] = A_BOLD, [FAILED_SEARCH] = A_REVERSE | A_BOLD, [FAILED_READ] = A_BOLD, [PAUSED] = A_BOLD | A_REVERSE, [UPTIME] = A_BOLD, [BATTERY] = A_BOLD, [LARGE_NUMBER] = A_BOLD, [METER_TEXT] = A_NORMAL, [METER_VALUE] = A_BOLD, [METER_VALUE_ERROR] = A_BOLD, [METER_VALUE_IOREAD] = A_NORMAL, [METER_VALUE_IOWRITE] = A_NORMAL, [METER_VALUE_NOTICE] = A_BOLD, [METER_VALUE_OK] = A_NORMAL, [METER_VALUE_WARN] = A_BOLD, [LED_COLOR] = A_NORMAL, [TASKS_RUNNING] = A_BOLD, [PROCESS] = A_NORMAL, [PROCESS_SHADOW] = A_DIM, [PROCESS_TAG] = A_BOLD, [PROCESS_MEGABYTES] = A_BOLD, [PROCESS_GIGABYTES] = A_BOLD, [PROCESS_BASENAME] = A_BOLD, [PROCESS_TREE] = A_BOLD, [PROCESS_R_STATE] = A_BOLD, [PROCESS_D_STATE] = A_BOLD, [PROCESS_HIGH_PRIORITY] = A_BOLD, [PROCESS_LOW_PRIORITY] = A_DIM, [PROCESS_NEW] = A_BOLD, [PROCESS_TOMB] = A_DIM, [PROCESS_THREAD] = A_BOLD, [PROCESS_THREAD_BASENAME] = A_REVERSE, [PROCESS_COMM] = A_BOLD, [PROCESS_THREAD_COMM] = A_REVERSE, [BAR_BORDER] = A_BOLD, [BAR_SHADOW] = A_DIM, [SWAP] = A_BOLD, [GRAPH_1] = A_BOLD, [GRAPH_2] = A_NORMAL, [MEMORY_USED] = A_BOLD, [MEMORY_BUFFERS] = A_NORMAL, [MEMORY_BUFFERS_TEXT] = A_NORMAL, [MEMORY_CACHE] = A_NORMAL, [LOAD_AVERAGE_FIFTEEN] = A_DIM, [LOAD_AVERAGE_FIVE] = A_NORMAL, [LOAD_AVERAGE_ONE] = A_BOLD, [LOAD] = A_BOLD, [HELP_BOLD] = A_BOLD, [CLOCK] = A_BOLD, [DATE] = A_BOLD, [DATETIME] = A_BOLD, [CHECK_BOX] = A_BOLD, [CHECK_MARK] = A_NORMAL, [CHECK_TEXT] = A_NORMAL, [HOSTNAME] = A_BOLD, [CPU_NICE] = A_NORMAL, [CPU_NICE_TEXT] = A_NORMAL, [CPU_NORMAL] = A_BOLD, [CPU_SYSTEM] = A_BOLD, [CPU_IOWAIT] = A_NORMAL, [CPU_IRQ] = A_BOLD, [CPU_SOFTIRQ] = A_BOLD, [CPU_STEAL] = A_DIM, [CPU_GUEST] = A_DIM, [PRESSURE_STALL_THREEHUNDRED] = A_DIM, [PRESSURE_STALL_SIXTY] = A_NORMAL, [PRESSURE_STALL_TEN] = A_BOLD, [ZFS_MFU] = A_NORMAL, [ZFS_MRU] = A_NORMAL, [ZFS_ANON] = A_DIM, [ZFS_HEADER] = A_BOLD, [ZFS_OTHER] = A_DIM, [ZFS_COMPRESSED] = A_BOLD, [ZFS_RATIO] = A_BOLD, [ZRAM] = A_NORMAL, }, [COLORSCHEME_BLACKONWHITE] = { [RESET_COLOR] = ColorPair(Black, White), [DEFAULT_COLOR] = ColorPair(Black, White), [FUNCTION_BAR] = ColorPair(Black, Cyan), [FUNCTION_KEY] = ColorPair(Black, White), [PANEL_HEADER_FOCUS] = ColorPair(Black, Green), [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green), [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan), [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow), [PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, White), [FAILED_SEARCH] = ColorPair(Red, Cyan), [FAILED_READ] = ColorPair(Red, White), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [UPTIME] = ColorPair(Yellow, White), [BATTERY] = ColorPair(Yellow, White), [LARGE_NUMBER] = ColorPair(Red, White), [METER_TEXT] = ColorPair(Blue, White), [METER_VALUE] = ColorPair(Black, White), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, White), [METER_VALUE_IOREAD] = ColorPair(Green, White), [METER_VALUE_IOWRITE] = ColorPair(Yellow, White), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, White), [METER_VALUE_OK] = ColorPair(Green, White), [METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, White), [LED_COLOR] = ColorPair(Green, White), [TASKS_RUNNING] = ColorPair(Green, White), [PROCESS] = ColorPair(Black, White), [PROCESS_SHADOW] = A_BOLD | ColorPair(Black, White), [PROCESS_TAG] = ColorPair(White, Blue), [PROCESS_MEGABYTES] = ColorPair(Blue, White), [PROCESS_GIGABYTES] = ColorPair(Green, White), [PROCESS_BASENAME] = ColorPair(Blue, White), [PROCESS_TREE] = ColorPair(Green, White), [PROCESS_R_STATE] = ColorPair(Green, White), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, White), [PROCESS_LOW_PRIORITY] = ColorPair(Green, White), [PROCESS_NEW] = ColorPair(White, Green), [PROCESS_TOMB] = ColorPair(White, Red), [PROCESS_THREAD] = ColorPair(Blue, White), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White), [PROCESS_COMM] = ColorPair(Magenta, White), [PROCESS_THREAD_COMM] = ColorPair(Green, White), [BAR_BORDER] = ColorPair(Blue, White), [BAR_SHADOW] = ColorPair(Black, White), [SWAP] = ColorPair(Red, White), [GRAPH_1] = A_BOLD | ColorPair(Blue, White), [GRAPH_2] = ColorPair(Blue, White), [MEMORY_USED] = ColorPair(Green, White), [MEMORY_BUFFERS] = ColorPair(Cyan, White), [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, White), [MEMORY_CACHE] = ColorPair(Yellow, White), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, White), [LOAD_AVERAGE_FIVE] = ColorPair(Black, White), [LOAD_AVERAGE_ONE] = ColorPair(Black, White), [LOAD] = ColorPair(Black, White), [HELP_BOLD] = ColorPair(Blue, White), [CLOCK] = ColorPair(Black, White), [DATE] = ColorPair(Black, White), [DATETIME] = ColorPair(Black, White), [CHECK_BOX] = ColorPair(Blue, White), [CHECK_MARK] = ColorPair(Black, White), [CHECK_TEXT] = ColorPair(Black, White), [HOSTNAME] = ColorPair(Black, White), [CPU_NICE] = ColorPair(Cyan, White), [CPU_NICE_TEXT] = ColorPair(Cyan, White), [CPU_NORMAL] = ColorPair(Green, White), [CPU_SYSTEM] = ColorPair(Red, White), [CPU_IOWAIT] = A_BOLD | ColorPair(Black, White), [CPU_IRQ] = ColorPair(Blue, White), [CPU_SOFTIRQ] = ColorPair(Blue, White), [CPU_STEAL] = ColorPair(Cyan, White), [CPU_GUEST] = ColorPair(Cyan, White), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White), [PRESSURE_STALL_SIXTY] = ColorPair(Black, White), [PRESSURE_STALL_TEN] = ColorPair(Black, White), [ZFS_MFU] = ColorPair(Cyan, White), [ZFS_MRU] = ColorPair(Yellow, White), [ZFS_ANON] = ColorPair(Magenta, White), [ZFS_HEADER] = ColorPair(Yellow, White), [ZFS_OTHER] = ColorPair(Magenta, White), [ZFS_COMPRESSED] = ColorPair(Cyan, White), [ZFS_RATIO] = ColorPair(Magenta, White), [ZRAM] = ColorPair(Yellow, White) }, [COLORSCHEME_LIGHTTERMINAL] = { [RESET_COLOR] = ColorPair(Black, Black), [DEFAULT_COLOR] = ColorPair(Black, Black), [FUNCTION_BAR] = ColorPair(Black, Cyan), [FUNCTION_KEY] = ColorPair(Black, Black), [PANEL_HEADER_FOCUS] = ColorPair(Black, Green), [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green), [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan), [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow), [PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, Black), [FAILED_SEARCH] = ColorPair(Red, Cyan), [FAILED_READ] = ColorPair(Red, Black), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [UPTIME] = ColorPair(Yellow, Black), [BATTERY] = ColorPair(Yellow, Black), [LARGE_NUMBER] = ColorPair(Red, Black), [METER_TEXT] = ColorPair(Blue, Black), [METER_VALUE] = ColorPair(Black, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_IOREAD] = ColorPair(Green, Black), [METER_VALUE_IOWRITE] = ColorPair(Yellow, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPairWhiteDefault, [METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black), [LED_COLOR] = ColorPair(Green, Black), [TASKS_RUNNING] = ColorPair(Green, Black), [PROCESS] = ColorPair(Black, Black), [PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack, [PROCESS_TAG] = ColorPair(White, Blue), [PROCESS_MEGABYTES] = ColorPair(Blue, Black), [PROCESS_GIGABYTES] = ColorPair(Green, Black), [PROCESS_BASENAME] = ColorPair(Green, Black), [PROCESS_TREE] = ColorPair(Blue, Black), [PROCESS_R_STATE] = ColorPair(Green, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_NEW] = ColorPair(Black, Green), [PROCESS_TOMB] = ColorPair(Black, Red), [PROCESS_THREAD] = ColorPair(Blue, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black), [PROCESS_COMM] = ColorPair(Magenta, Black), [PROCESS_THREAD_COMM] = ColorPair(Yellow, Black), [BAR_BORDER] = ColorPair(Blue, Black), [BAR_SHADOW] = ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), [MEMORY_USED] = ColorPair(Green, Black), [MEMORY_BUFFERS] = ColorPair(Cyan, Black), [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, Black), [LOAD_AVERAGE_FIVE] = ColorPair(Black, Black), [LOAD_AVERAGE_ONE] = ColorPair(Black, Black), [LOAD] = ColorPairWhiteDefault, [HELP_BOLD] = ColorPair(Blue, Black), [CLOCK] = ColorPairWhiteDefault, [DATE] = ColorPairWhiteDefault, [DATETIME] = ColorPairWhiteDefault, [CHECK_BOX] = ColorPair(Blue, Black), [CHECK_MARK] = ColorPair(Black, Black), [CHECK_TEXT] = ColorPair(Black, Black), [HOSTNAME] = ColorPairWhiteDefault, [CPU_NICE] = ColorPair(Cyan, Black), [CPU_NICE_TEXT] = ColorPair(Cyan, Black), [CPU_NORMAL] = ColorPair(Green, Black), [CPU_SYSTEM] = ColorPair(Red, Black), [CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue, Black), [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Black, Black), [CPU_GUEST] = ColorPair(Black, Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Black, Black), [PRESSURE_STALL_TEN] = ColorPair(Black, Black), [ZFS_MFU] = ColorPair(Cyan, Black), [ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_ANON] = A_BOLD | ColorPair(Magenta, Black), [ZFS_HEADER] = ColorPair(Black, Black), [ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Black), [ZFS_COMPRESSED] = ColorPair(Cyan, Black), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), }, [COLORSCHEME_MIDNIGHT] = { [RESET_COLOR] = ColorPair(White, Blue), [DEFAULT_COLOR] = ColorPair(White, Blue), [FUNCTION_BAR] = ColorPair(Black, Cyan), [FUNCTION_KEY] = A_NORMAL, [PANEL_HEADER_FOCUS] = ColorPair(Black, Cyan), [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Cyan), [PANEL_SELECTION_FOCUS] = ColorPair(Black, White), [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow), [PANEL_SELECTION_UNFOCUS] = A_BOLD | ColorPair(Yellow, Blue), [FAILED_SEARCH] = ColorPair(Red, Cyan), [FAILED_READ] = A_BOLD | ColorPair(Red, Blue), [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan), [UPTIME] = A_BOLD | ColorPair(Yellow, Blue), [BATTERY] = A_BOLD | ColorPair(Yellow, Blue), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue), [METER_TEXT] = ColorPair(Cyan, Blue), [METER_VALUE] = A_BOLD | ColorPair(Cyan, Blue), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Blue), [METER_VALUE_IOREAD] = ColorPair(Green, Blue), [METER_VALUE_IOWRITE] = ColorPair(Black, Blue), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Blue), [METER_VALUE_OK] = ColorPair(Green, Blue), [METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black), [LED_COLOR] = ColorPair(Green, Blue), [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Blue), [PROCESS] = ColorPair(White, Blue), [PROCESS_SHADOW] = A_BOLD | ColorPair(Black, Blue), [PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Blue), [PROCESS_MEGABYTES] = ColorPair(Cyan, Blue), [PROCESS_GIGABYTES] = ColorPair(Green, Blue), [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Blue), [PROCESS_TREE] = ColorPair(Cyan, Blue), [PROCESS_R_STATE] = ColorPair(Green, Blue), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue), [PROCESS_NEW] = ColorPair(Blue, Green), [PROCESS_TOMB] = ColorPair(Blue, Red), [PROCESS_THREAD] = ColorPair(Green, Blue), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue), [PROCESS_COMM] = ColorPair(Magenta, Blue), [PROCESS_THREAD_COMM] = ColorPair(Black, Blue), [BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue), [BAR_SHADOW] = ColorPair(Cyan, Blue), [SWAP] = ColorPair(Red, Blue), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Blue), [GRAPH_2] = ColorPair(Cyan, Blue), [MEMORY_USED] = A_BOLD | ColorPair(Green, Blue), [MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan, Blue), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan, Blue), [MEMORY_CACHE] = A_BOLD | ColorPair(Yellow, Blue), [LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(Black, Blue), [LOAD_AVERAGE_FIVE] = A_NORMAL | ColorPair(White, Blue), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Blue), [LOAD] = A_BOLD | ColorPair(White, Blue), [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Blue), [CLOCK] = ColorPair(White, Blue), [DATE] = ColorPair(White, Blue), [DATETIME] = ColorPair(White, Blue), [CHECK_BOX] = ColorPair(Cyan, Blue), [CHECK_MARK] = A_BOLD | ColorPair(White, Blue), [CHECK_TEXT] = A_NORMAL | ColorPair(White, Blue), [HOSTNAME] = ColorPair(White, Blue), [CPU_NICE] = A_BOLD | ColorPair(Cyan, Blue), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan, Blue), [CPU_NORMAL] = A_BOLD | ColorPair(Green, Blue), [CPU_SYSTEM] = A_BOLD | ColorPair(Red, Blue), [CPU_IOWAIT] = A_BOLD | ColorPair(Black, Blue), [CPU_IRQ] = A_BOLD | ColorPair(Black, Blue), [CPU_SOFTIRQ] = ColorPair(Black, Blue), [CPU_STEAL] = ColorPair(White, Blue), [CPU_GUEST] = ColorPair(White, Blue), [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue), [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue), [ZFS_MFU] = A_BOLD | ColorPair(White, Blue), [ZFS_MRU] = A_BOLD | ColorPair(Yellow, Blue), [ZFS_ANON] = A_BOLD | ColorPair(Magenta, Blue), [ZFS_HEADER] = A_BOLD | ColorPair(Yellow, Blue), [ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Blue), [ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue), [ZRAM] = A_BOLD | ColorPair(Yellow, Blue), }, [COLORSCHEME_BLACKNIGHT] = { [RESET_COLOR] = ColorPair(Cyan, Black), [DEFAULT_COLOR] = ColorPair(Cyan, Black), [FUNCTION_BAR] = ColorPair(Black, Green), [FUNCTION_KEY] = ColorPair(Cyan, Black), [PANEL_HEADER_FOCUS] = ColorPair(Black, Green), [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green), [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan), [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow), [PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White), [FAILED_SEARCH] = ColorPair(Red, Green), [FAILED_READ] = A_BOLD | ColorPair(Red, Black), [PAUSED] = A_BOLD | ColorPair(Yellow, Green), [UPTIME] = ColorPair(Green, Black), [BATTERY] = ColorPair(Green, Black), [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black), [METER_TEXT] = ColorPair(Cyan, Black), [METER_VALUE] = ColorPair(Green, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_IOREAD] = ColorPair(Green, Black), [METER_VALUE_IOWRITE] = ColorPair(Blue, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black), [METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black), [LED_COLOR] = ColorPair(Green, Black), [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black), [PROCESS] = ColorPair(Cyan, Black), [PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack, [PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black), [PROCESS_MEGABYTES] = A_BOLD | ColorPair(Green, Black), [PROCESS_GIGABYTES] = A_BOLD | ColorPair(Yellow, Black), [PROCESS_BASENAME] = A_BOLD | ColorPair(Green, Black), [PROCESS_TREE] = ColorPair(Cyan, Black), [PROCESS_THREAD] = ColorPair(Green, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black), [PROCESS_COMM] = ColorPair(Magenta, Black), [PROCESS_THREAD_COMM] = ColorPair(Yellow, Black), [PROCESS_R_STATE] = ColorPair(Green, Black), [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black), [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black), [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_NEW] = ColorPair(Black, Green), [PROCESS_TOMB] = ColorPair(Black, Red), [BAR_BORDER] = A_BOLD | ColorPair(Green, Black), [BAR_SHADOW] = ColorPair(Cyan, Black), [SWAP] = ColorPair(Red, Black), [GRAPH_1] = A_BOLD | ColorPair(Green, Black), [GRAPH_2] = ColorPair(Green, Black), [MEMORY_USED] = ColorPair(Green, Black), [MEMORY_BUFFERS] = ColorPair(Blue, Black), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Green, Black), [LOAD_AVERAGE_FIVE] = ColorPair(Green, Black), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Green, Black), [LOAD] = A_BOLD, [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black), [CLOCK] = ColorPair(Green, Black), [CHECK_BOX] = ColorPair(Green, Black), [CHECK_MARK] = A_BOLD | ColorPair(Green, Black), [CHECK_TEXT] = ColorPair(Cyan, Black), [HOSTNAME] = ColorPair(Green, Black), [CPU_NICE] = ColorPair(Blue, Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue, Black), [CPU_NORMAL] = ColorPair(Green, Black), [CPU_SYSTEM] = ColorPair(Red, Black), [CPU_IOWAIT] = ColorPair(Yellow, Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue, Black), [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Cyan, Black), [CPU_GUEST] = ColorPair(Cyan, Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Green, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black), [ZFS_MFU] = ColorPair(Blue, Black), [ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_ANON] = ColorPair(Magenta, Black), [ZFS_HEADER] = ColorPair(Yellow, Black), [ZFS_OTHER] = ColorPair(Magenta, Black), [ZFS_COMPRESSED] = ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), }, [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. }; int CRT_cursorX = 0; int CRT_scrollHAmount = 5; int CRT_scrollWheelVAmount = 10; ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT; ATTR_NORETURN static void CRT_handleSIGTERM(int sgn) { (void) sgn; CRT_done(); exit(0); } #ifdef HAVE_SETUID_ENABLED static int CRT_euid = -1; static int CRT_egid = -1; void CRT_dropPrivileges() { CRT_egid = getegid(); CRT_euid = geteuid(); if (setegid(getgid()) == -1) { CRT_fatalError("Fatal error: failed dropping group privileges"); } if (seteuid(getuid()) == -1) { CRT_fatalError("Fatal error: failed dropping user privileges"); } } void CRT_restorePrivileges() { if (CRT_egid == -1 || CRT_euid == -1) { CRT_fatalError("Fatal error: internal inconsistency"); } if (setegid(CRT_egid) == -1) { CRT_fatalError("Fatal error: failed restoring group privileges"); } if (seteuid(CRT_euid) == -1) { CRT_fatalError("Fatal error: failed restoring user privileges"); } } #endif /* HAVE_SETUID_ENABLED */ static struct sigaction old_sig_handler[32]; // TODO: pass an instance of Settings instead. void CRT_init(const int* delay, int colorScheme, bool allowUnicode) { initscr(); noecho(); CRT_delay = delay; CRT_colors = CRT_colorSchemes[colorScheme]; CRT_colorScheme = colorScheme; for (int i = 0; i < LAST_COLORELEMENT; i++) { unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i]; CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White, Black) : color; } halfdelay(*CRT_delay); nonl(); intrflush(stdscr, false); keypad(stdscr, true); mouseinterval(0); curs_set(0); if (has_colors()) { start_color(); } const char* termType = getenv("TERM"); if (termType && String_eq(termType, "linux")) { CRT_scrollHAmount = 20; } else { CRT_scrollHAmount = 5; } if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) { define_key("\033[H", KEY_HOME); define_key("\033[F", KEY_END); define_key("\033[7~", KEY_HOME); define_key("\033[8~", KEY_END); define_key("\033OP", KEY_F(1)); define_key("\033OQ", KEY_F(2)); define_key("\033OR", KEY_F(3)); define_key("\033OS", KEY_F(4)); define_key("\033[11~", KEY_F(1)); define_key("\033[12~", KEY_F(2)); define_key("\033[13~", KEY_F(3)); define_key("\033[14~", KEY_F(4)); define_key("\033[14;2~", KEY_F(15)); define_key("\033[17;2~", KEY_F(18)); char sequence[3] = "\033a"; for (char c = 'a'; c <= 'z'; c++) { sequence[1] = c; define_key(sequence, KEY_ALT('A' + (c - 'a'))); } } struct sigaction act; sigemptyset (&act.sa_mask); act.sa_flags = (int)SA_RESETHAND | SA_NODEFER; act.sa_handler = CRT_handleSIGSEGV; sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]); sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]); sigaction (SIGILL, &act, &old_sig_handler[SIGILL]); sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]); sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]); sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]); sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]); signal(SIGTERM, CRT_handleSIGTERM); signal(SIGQUIT, CRT_handleSIGTERM); use_default_colors(); if (!has_colors()) CRT_colorScheme = COLORSCHEME_MONOCHROME; CRT_setColors(CRT_colorScheme); #ifdef HAVE_LIBNCURSESW if (allowUnicode && String_eq(nl_langinfo(CODESET), "UTF-8")) { CRT_utf8 = true; } else { CRT_utf8 = false; } #else (void) allowUnicode; #endif CRT_treeStr = #ifdef HAVE_LIBNCURSESW CRT_utf8 ? CRT_treeStrUtf8 : #endif CRT_treeStrAscii; #if NCURSES_MOUSE_VERSION > 1 mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL); #else mousemask(BUTTON1_RELEASED, NULL); #endif CRT_degreeSign = initDegreeSign(); } void CRT_done() { curs_set(1); endwin(); } void CRT_fatalError(const char* note) { const char* sysMsg = strerror(errno); CRT_done(); fprintf(stderr, "%s: %s\n", note, sysMsg); exit(2); } int CRT_readKey() { nocbreak(); cbreak(); nodelay(stdscr, FALSE); int ret = getch(); halfdelay(*CRT_delay); return ret; } void CRT_disableDelay() { nocbreak(); cbreak(); nodelay(stdscr, TRUE); } void CRT_enableDelay() { halfdelay(*CRT_delay); } void CRT_setColors(int colorScheme) { CRT_colorScheme = colorScheme; for (short int i = 0; i < 8; i++) { for (short int j = 0; j < 8; j++) { if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault) { short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? (j == 0 ? -1 : j) : j; init_pair(ColorIndex(i, j), i, bg); } } } short int grayBlackFg = COLORS > 8 ? 8 : 0; short int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? -1 : 0; init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg); init_pair(ColorIndexWhiteDefault, White, -1); CRT_colors = CRT_colorSchemes[colorScheme]; } void CRT_handleSIGSEGV(int signal) { CRT_done(); fprintf(stderr, "\n\n" "FATAL PROGRAM ERROR DETECTED\n" "============================\n" "Please check at https://htop.dev/issues whether this issue has already been reported.\n" "If no similar issue has been reported before, please create a new issue with the following information:\n" "\n" "- Your htop version (htop --version)\n" "- Your OS and kernel version (uname -a)\n" "- Your distribution and release (lsb_release -a)\n" "- Likely steps to reproduce (How did it happened?)\n" ); #ifdef HAVE_EXECINFO_H fprintf(stderr, "- Backtrace of the issue (see below)\n"); #endif fprintf(stderr, "\n" ); const char* signal_str = strsignal(signal); if (!signal_str) { signal_str = "unknown reason"; } fprintf(stderr, "Error information:\n" "------------------\n" "A signal %d (%s) was received.\n" "\n", signal, signal_str ); #ifdef HAVE_EXECINFO_H fprintf(stderr, "Backtrace information:\n" "----------------------\n" "The following function calls were active when the issue was detected:\n" "---\n" ); void *backtraceArray[256]; size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray)); backtrace_symbols_fd(backtraceArray, size, 2); fprintf(stderr, "---\n" "\n" "To make the above information more practical to work with,\n" "you should provide a disassembly of your binary.\n" "This can usually be done by running the following command:\n" "\n" ); #ifdef HTOP_DARWIN fprintf(stderr, " otool -tvV `which htop` > ~/htop.otool\n"); #else fprintf(stderr, " objdump -d -S -w `which htop` > ~/htop.objdump\n"); #endif fprintf(stderr, "\n" "Please include the generated file in your report.\n" "\n" ); #endif fprintf(stderr, "Running this program with debug symbols or inside a debugger may provide further insights.\n" "\n" "Thank you for helping to improve htop!\n" "\n" "htop " VERSION " aborting.\n" "\n" ); /* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */ if (sigaction (signal, &old_sig_handler[signal], NULL) < 0) { /* This avoids an infinite loop in case the handler could not be reset. */ fprintf(stderr, "!!! Chained handler could not be restored. Forcing exit.\n" ); _exit(1); } /* Trigger the previous signal handler. */ raise(signal); // Always terminate, even if installed handler returns fprintf(stderr, "!!! Chained handler did not exit. Forcing exit.\n" ); _exit(1); } htop-3.0.5/CRT.h000066400000000000000000000063731377712513700133200ustar00rootroot00000000000000#ifndef HEADER_CRT #define HEADER_CRT /* htop - CRT.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" #include #include "Macros.h" #include "ProvideCurses.h" typedef enum TreeStr_ { TREE_STR_VERT, TREE_STR_RTEE, TREE_STR_BEND, TREE_STR_TEND, TREE_STR_OPEN, TREE_STR_SHUT, TREE_STR_ASC, TREE_STR_DESC, LAST_TREE_STR } TreeStr; typedef enum ColorScheme_ { COLORSCHEME_DEFAULT, COLORSCHEME_MONOCHROME, COLORSCHEME_BLACKONWHITE, COLORSCHEME_LIGHTTERMINAL, COLORSCHEME_MIDNIGHT, COLORSCHEME_BLACKNIGHT, COLORSCHEME_BROKENGRAY, LAST_COLORSCHEME } ColorScheme; typedef enum ColorElements_ { RESET_COLOR, DEFAULT_COLOR, FUNCTION_BAR, FUNCTION_KEY, FAILED_SEARCH, FAILED_READ, PAUSED, PANEL_HEADER_FOCUS, PANEL_HEADER_UNFOCUS, PANEL_SELECTION_FOCUS, PANEL_SELECTION_FOLLOW, PANEL_SELECTION_UNFOCUS, LARGE_NUMBER, METER_TEXT, METER_VALUE, METER_VALUE_ERROR, METER_VALUE_IOREAD, METER_VALUE_IOWRITE, METER_VALUE_NOTICE, METER_VALUE_OK, METER_VALUE_WARN, LED_COLOR, UPTIME, BATTERY, TASKS_RUNNING, SWAP, PROCESS, PROCESS_SHADOW, PROCESS_TAG, PROCESS_MEGABYTES, PROCESS_GIGABYTES, PROCESS_TREE, PROCESS_R_STATE, PROCESS_D_STATE, PROCESS_BASENAME, PROCESS_HIGH_PRIORITY, PROCESS_LOW_PRIORITY, PROCESS_NEW, PROCESS_TOMB, PROCESS_THREAD, PROCESS_THREAD_BASENAME, PROCESS_COMM, PROCESS_THREAD_COMM, BAR_BORDER, BAR_SHADOW, GRAPH_1, GRAPH_2, MEMORY_USED, MEMORY_BUFFERS, MEMORY_BUFFERS_TEXT, MEMORY_CACHE, LOAD, LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_ONE, CHECK_BOX, CHECK_MARK, CHECK_TEXT, CLOCK, DATE, DATETIME, HELP_BOLD, HOSTNAME, CPU_NICE, CPU_NICE_TEXT, CPU_NORMAL, CPU_SYSTEM, CPU_IOWAIT, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, PRESSURE_STALL_TEN, PRESSURE_STALL_SIXTY, PRESSURE_STALL_THREEHUNDRED, ZFS_MFU, ZFS_MRU, ZFS_ANON, ZFS_HEADER, ZFS_OTHER, ZFS_COMPRESSED, ZFS_RATIO, ZRAM, LAST_COLORELEMENT } ColorElements; void CRT_fatalError(const char* note) ATTR_NORETURN; void CRT_handleSIGSEGV(int signal) ATTR_NORETURN; #define KEY_WHEELUP KEY_F(20) #define KEY_WHEELDOWN KEY_F(21) #define KEY_RECLICK KEY_F(22) #define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A')) extern const char* CRT_degreeSign; #ifdef HAVE_LIBNCURSESW extern bool CRT_utf8; #endif extern const char* const* CRT_treeStr; extern const int* CRT_colors; extern int CRT_cursorX; extern int CRT_scrollHAmount; extern int CRT_scrollWheelVAmount; extern ColorScheme CRT_colorScheme; #ifdef HAVE_SETUID_ENABLED void CRT_dropPrivileges(void); void CRT_restorePrivileges(void); #else /* HAVE_SETUID_ENABLED */ /* Turn setuid operations into NOPs */ static inline void CRT_dropPrivileges(void) { } static inline void CRT_restorePrivileges(void) { } #endif /* HAVE_SETUID_ENABLED */ void CRT_init(const int* delay, int colorScheme, bool allowUnicode); void CRT_done(void); int CRT_readKey(void); void CRT_disableDelay(void); void CRT_enableDelay(void); void CRT_setColors(int colorScheme); #endif htop-3.0.5/CategoriesPanel.c000066400000000000000000000111441377712513700157200ustar00rootroot00000000000000/* htop - CategoriesPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "CategoriesPanel.h" #include #include #include #include "AvailableColumnsPanel.h" #include "AvailableMetersPanel.h" #include "ColorsPanel.h" #include "ColumnsPanel.h" #include "DisplayOptionsPanel.h" #include "FunctionBar.h" #include "ListItem.h" #include "MetersPanel.h" #include "Object.h" #include "ProvideCurses.h" #include "Vector.h" static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static void CategoriesPanel_delete(Object* object) { Panel* super = (Panel*) object; CategoriesPanel* this = (CategoriesPanel*) object; Panel_done(super); free(this); } void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr); MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr); leftMeters->rightNeighbor = rightMeters; rightMeters->leftNeighbor = leftMeters; Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl); ScreenManager_add(this->scr, (Panel*) leftMeters, 20); ScreenManager_add(this->scr, (Panel*) rightMeters, 20); ScreenManager_add(this->scr, availableMeters, -1); } static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) { Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(this->settings, this->scr); ScreenManager_add(this->scr, displayOptions, -1); } static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) { Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr); ScreenManager_add(this->scr, colors, -1); } static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) { Panel* columns = (Panel*) ColumnsPanel_new(this->settings); Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns); ScreenManager_add(this->scr, columns, 20); ScreenManager_add(this->scr, availableColumns, -1); } static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { CategoriesPanel* this = (CategoriesPanel*) super; HandlerResult result = IGNORED; int selected = Panel_getSelectedIndex(super); switch (ch) { case EVENT_SET_SELECTED: result = HANDLED; break; case KEY_UP: case KEY_CTRL('P'): case KEY_DOWN: case KEY_CTRL('N'): case KEY_NPAGE: case KEY_PPAGE: case KEY_HOME: case KEY_END: { int previous = selected; Panel_onKey(super, ch); selected = Panel_getSelectedIndex(super); if (previous != selected) result = HANDLED; break; } default: if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) result = Panel_selectByTyping(super, ch); if (result == BREAK_LOOP) result = IGNORED; break; } if (result == HANDLED) { int size = ScreenManager_size(this->scr); for (int i = 1; i < size; i++) ScreenManager_remove(this->scr, 1); switch (selected) { case 0: CategoriesPanel_makeMetersPage(this); break; case 1: CategoriesPanel_makeDisplayOptionsPage(this); break; case 2: CategoriesPanel_makeColorsPage(this); break; case 3: CategoriesPanel_makeColumnsPage(this); break; } } return result; } const PanelClass CategoriesPanel_class = { .super = { .extends = Class(Panel), .delete = CategoriesPanel_delete }, .eventHandler = CategoriesPanel_eventHandler }; CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl) { CategoriesPanel* this = AllocThis(CategoriesPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(CategoriesFunctions, NULL, NULL); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); this->scr = scr; this->settings = settings; this->header = header; this->pl = pl; Panel_setHeader(super, "Setup"); Panel_add(super, (Object*) ListItem_new("Meters", 0)); Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel_add(super, (Object*) ListItem_new("Colors", 0)); Panel_add(super, (Object*) ListItem_new("Columns", 0)); return this; } htop-3.0.5/CategoriesPanel.h000066400000000000000000000013221377712513700157220ustar00rootroot00000000000000#ifndef HEADER_CategoriesPanel #define HEADER_CategoriesPanel /* htop - CategoriesPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Header.h" #include "Panel.h" #include "ProcessList.h" #include "ScreenManager.h" #include "Settings.h" typedef struct CategoriesPanel_ { Panel super; ScreenManager* scr; Settings* settings; Header* header; ProcessList* pl; } CategoriesPanel; void CategoriesPanel_makeMetersPage(CategoriesPanel* this); extern const PanelClass CategoriesPanel_class; CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); #endif htop-3.0.5/ChangeLog000066400000000000000000000724351377712513700142730ustar00rootroot00000000000000What's new in version 3.0.5 * BUGFIX / SECURITY: InfoScreen: fix uncontrolled format string * BUGFIX: Improve white text in the Light Terminal colour scheme (both of the above thanks to V) * Enable the function bar on the main screen to be hidden (see Setup -> Display options) * BUGFIX: Reduce layout issues esp. around printing wide characters (not complete yet) * BUGFIX: Make the follow function exit cleanly after followed process died * Solaris: make Process callbacks static * Update help and man page for improved -t / -s options * Drop usage of formatted error messages from * Show arrow indicating order of sorted process column * Lots of plumbing around the internal Hashtable, hardening and code cleanups * LibSensors: add support for Ryzen CPUs (vor 5 Tagen) (thanks to Matej Dian) * BUGFIX: Fix CPU percentage on M1 silicon Macs (thanks to Luke Groeninger) * LoadMeter: dynamically adjust color and total of bar * Find libsensors.so.4 for Fedora and friends * Add support to display CPU frequencies on Solarish platforms (thanks to Dominik Hassler) * Enable going back to previous search matches (Shift-F3) * Added keybind 'N' for sorting by PID (drops 'n'/'N' as not used before much) (thanks to Jake Mannens) What's new in version 3.0.4 * Separate tree and list sort orders * Invert Process_compare so that superclass matches run first (thanks to Hisham Muhammad) * Unhardcode Mac OS tick-to-milliseconds conversion (thanks to Alexander Momchilov) * Check if clock_gettime needs linking of librt * Define O_PATH if not already defined (thanks to Chris Burr) * Add column on Mac for processes running under translation (thanks to Dániel Bakai) * Configure check for additional linker flags for keypad(3) * PSI Meter: constant width and only print ten-duration as bar * Sort in paused mode after inverting sort order * Handle absence of package CPU temperature * Meter: restore non-wide-character build * LibSensors: restore temperature for Raspberry Pi * MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE * BarMeter: rework text padding * Panel: rework drawing of FunctionBar * Meter: fix artifacts with very tiny width * DragonFlyBSD updates * BUGFIX: Fix dlopen issue for libsensors5 for some platforms * BUGFIX: Fix broken tree display on inverted sort order * BUGFIX: Fix pause mode ("Z") in tree view * BUGFIX: Correct timebase for non-x86 CPUs on Darwin * BUGFIX: Avoid NULL dereference on zombie processes * Document dynamic bindings and assumed external configuration * Update key mapping documentation for sorting What's new in version 3.0.3 * Process sorting in 'tree' mode (thanks to Maxim Zhiburt) * Improved command display/sort functionality (thanks to Narendran Gopalakrishnan) * Add screen for active file locks (thanks to Fynn J. Wulf) * Calculate library size (M_LRS column) from maps file (thanks to Fynn J. Wulf) * Add a Zram meter (thanks to Murloc Knight) * Add Linux cwd process column * Dynamically load libsensors at runtime * Improve PressureStall Meter display strings * Hide process selection on ESC * Fully support non-ascii characters in Meter-Bar * Add support to change numeric options in settings screen * Rename virtual memory column from M_SIZE to M_VIRT * Add process column for normalized CPU usage * Show CPU temperature in CPU meter * Drop hideThreads Setting * Add a systemd meter * Add a network IO meter * Add a SELinux meter * Compress size of default FunctionBar * Updates to the OpenFiles screen * Continue updating header data in paused mode * BUGFIX: Handle data wraparounds in IO meters * BUGFIX: Update InfoScreen content on resize * Add security attribute process column * Add DiskIOMeter for IO read/write usage * Read CPU frequency from sysfs by default * Add Linux process column for context switches * Several FreeBSD and Mac OS X platform updates (thanks to Christian Göttsche) * Add process environment for FreeBSD (thanks to Ross Williams) * Parse POWER_SUPPLY_CAPACITY for Linux Battery meter (thanks to Jan Palus) * Add octuple-column CPU meters. * BUGFIX: On Linux consider ZFS ARC to be cache (thanks to @multi) * BUGFIX: Limit screen title length to window width * Show selected command wrapped in a separate window (thanks to @ryenus) * Allow to pass '/' for item search * Document implicit incremental search * Handle 'q' as quit if first character * Avoid expensive build of process tree when not using it * Include documentation for COMM and EXE * Distinguish display of no permissions for reading M_LRS * Only calculate M_LRS size every 2 seconds * Improvements to comm / cmdline display functionality * Merged view for COMM, EXE and cmdline (thanks to Narendran Gopalakrishnan and Benny Baumann) * Consistent kernel thread display for COMM/EXE columns * Central fault handling for all platforms * Handle parsing envID & VPid from process status file * Use threshold for display of guest/steal/irq meters * Enhance highlighting of semi-large and large numbers * Documentation on the repository style guide (thanks to Benny Baumann) * Align processor identifier to the right (thanks to Christian Hesse) * Document M_PSS, M_PSSWP, M_SWAP in man page * Add Date and DateTime meters (thanks to Michael F. Schönitzer) * BUGFIX: Fix Solaris 11.4 due to missing ZFS ARC kstats (thanks to @senjan) * Code hardening, speedups, fd and memory leak fixes (thanks to Christian Göttsche and Benny Baumann) * Number CPUs from zero by default (thanks to Zev Weiss) * Remove residual python checks during the build process (thanks to Stephen Gregoratto) What's new in version 3.0.2 * BUGFIX: Drop 'vim_mode' - several issues, needs rethink * BUGFIX: fix regression in -u optional-argument handling * Build system rework to remove python, header generation (thanks to Zev Weiss and Hugo Musso Gualandi) * BUGFIX: report nice level correctly on Solaris (thanks to Dominik Hassler) * CI, code quality improvements (thanks to Tobias Kortkamp, Christian Hesse, Benny Baumann) What's new in version 3.0.1 * Coverity fixes, CI improvements, documentation updates * BUGFIX: Fix early exit with longer sysfs battery paths * BUGFIX: Improve OOM output, fix sorting (thanks to Christian Göttsche) * Rework check buttons and tree open/closed (thanks to Bert Wesarg) * Add -U/--no-unicode option to disable unicode (thanks to Christian Hesse) * Improvements to the affinity panel (thanks to Bert Wesarg) What's new in version 3.0.0 * New maintainers - after a prolonged period of inactivity from Hisham, the creator and original maintainer, a team of community maintainers have volunteered to take over a fork at https://htop.dev and https://github.com/htop-dev to keep the project going. * Support ZFS ARC statistics (thanks to Ross Williams) * Support more than 2 smaller CPU meter columns (thanks to Christoph Budziszewski) * Support Linux proportional set size metrics (thanks to @linvinus, @ntninja and @himikof) * Support Linux pressure stall information metrics (thanks to Ran Benita) * New display option to show CPU frequency in CPU meters (thanks to Arnav Singh) * Update Linux sysfs battery discovery for recent kernels (thanks to @smattie) * Add hardware topology information in the affinity panel (thanks to Bert Wesarg) * Add timestamp reporting to the strace screen (thanks to Mario Harjac) * Add simple, optional vim key mapping mode (thanks to Daniel Flanagan) * Added an option to disable the mouse (thanks to MartinJM) * Add Solaris11 compatibility (thanks to Jan Senolt) * Without an argument -u uses $USER value automatically (thanks to @solanav) * Support less(1) search navigation shortcuts (thanks to @syrrim) * Update the FreeBSD maximum PID to match FreeBSD change (thanks to @multiplexd) * Report values larger than 100 terabytes (thanks to @adrien1018) * Widen ST_UID (UID) column to allow for UIDs > 9999 (thanks to DLange) * BUGFIX: fix makefiles for building with clang (thanks to Jorge Pereira) * BUGFIX: fix major() usage (thanks to @wataash and Kang-Che Sung) * BUGFIX: fix the STARTTIME column on FreeBSD (thanks to Rob Crowston) * BUGFIX: truncate overwide jail names on FreeBSD (thanks to Rob Crowston) * BUGFIX: fix reported memory values on FreeBSD (thanks to Tobias Kortkamp) * BUGFIX: fix reported CPU meter values on OpenBSD (thanks to @motet-a) * BUGFIX: correctly identify other types of zombie process (thanks to @joder) * BUGFIX: improve follow-process handling in some situations (thanks to @wangqr) * BUGFIX: fix custom meters reverting to unexpected setting (thanks to @wangqr) * BUGFIX: close pipe after running lsof(1) (thanks to Jesin) * BUGFIX: meters honour setting of counting CPUs from 0/1 (thanks to @rnsanchez) What's new in version 2.2.0 * Solaris/Illumos/OpenIndiana support (thanks to Guy M. Broome) * -t/--tree flag for starting in tree-view mode (thanks to Daniel Flanagan) * macOS: detects High Sierra version to avoid OS bug (thanks to Pierre Malhaire) * OpenBSD: read battery data (thanks to @nerd972) * Various automake and build improvements (thanks to Kang-Che Sung) * Check for pkg-config when building with --enable-delayacct (thanks to @florian2833z for the report) * Avoid some bashisms in configure script (thanks to Jesin) * Use CFLAGS from ncurses*-config if present (thanks to Michael Klein) * Header generator supports non-UTF-8 environments (thanks to @volkov-am) * Linux: changed detection of kernel threads * Collapse current subtree pressing Backspace * BUGFIX: fix behavior of SYSCR column (thanks to Marc Kleine-Budde) * BUGFIX: obtain exit code of lsof correctly (thanks to @wangqr) * BUGFIX: fix crash with particular keycodes (thanks to Wellington Torrejais da Silva for the report) * BUGFIX: fix issue with small terminals (thanks to Daniel Elf for the report) * BUGFIX: fix terminal color issues (thanks to Kang-Che Sung for the report) * BUGFIX: preserve LDFLAGS when building (thanks to Lance Frederickson for the report) * BUGFIX: fixed overflow for systems with >= 100 signals What's new in version 2.1.0 * Linux: Delay accounting metrics (thanks to AndrĂ© Carvalho) * DragonFlyBSD support (thanks to Diederik de Groot) * Support for real-time signals (thanks to Kang-Che Sung) * 'c' key now works with threads as well * Session column renamed from SESN to SID (thanks to Kamyar Rasta) * Improved UI for meter style selection (thanks to Kang-Che Sung) * Improved code for constructing process tree (thanks to wangqr) * Compile-time option to disable setuid * Error checking of various standard library operations * Replacement of sprintf with snprintf (thanks to Tomasz Kramkowski) * Linux: performance improvements in battery meter * Linux: update process TTY device * Linux: add support for sorting TASK_IDLE (thanks to Vladimir Panteleev) * Linux: add upper-bound to running process counter (thanks to Lucas Correia Villa Real) * BUGFIX: avoid crash when battery is removed (thanks to Jan Chren) * BUGFIX: macOS: fix infinite loop in tree view (thanks to Wataru Ashihara) What's new in version 2.0.2 * Mac OS X: stop trying when task_for_pid fails for a process, stops spamming logs with errors. * Add Ctrl+A and Ctrl+E to go to beginning and end of line * FreeBSD: fixes for CPU calculation (thanks to Tim Creech, Andy Pilate) * Usability: auto-follow process after a search. * Use Linux backend on GNU Hurd * Improvement for reproducible builds. * BUGFIX: Fix behavior of Alt-key combinations (thanks to Kang-Che Sung) * Various code tweaks and cleanups (thanks to Kang-Che Sung) What's new in version 2.0.1 * OpenBSD: Various fixes and improvements (thanks to Michael McConville and Juan Francisco Cantero Hurtado) * FreeBSD: fix CPU and memory readings (thanks to Tim Creech, Hung-Yi Chen, Bernard Spil, Greg V) * FreeBSD: add battery support (thanks to Greg V) * Linux: Retain last-obtained name of a zombie process * Mac OS X: Improve portability for OS X versions (thanks to Michael Klein) * Mac OS X: Fix reading command-line arguments and basename * Mac OS X: Fix process state information * Mac OS X: Fix tree view collapsing/expanding * Mac OS X: Fix tree organization * Mac OS X: Fix memory accounting * Fix crash when emptying a column of meters * Make Esc key more responsive What's new in version 2.0.0 * Platform abstraction layer * Initial FreeBSD support * Initial Mac OS X support (thanks to David Hunt) * Swap meter for Mac OSX (thanks to Čtefan Rusu) * OpenBSD port (thanks to Michael McConville) * FreeBSD support improvements (thanks to Martin Misuth) * Support for NCurses 6 ABI, including mouse wheel support * Much improved mouse responsiveness * Process environment variables screen (thanks to Michael Klein) * Higher-resolution UTF-8 based Graph mode (Thanks to James Hall from vtop for the idea!) * Show program path settings (thanks to Tobias Geerinckx-Rice) * BUGFIX: Fix crash when scrolling an empty filtered list. * Use dynamic units for text display, and several fixes (thanks to Christian Hesse) * BUGFIX: fix error caused by overflow in usertime calculation. (thanks to Patrick Marlier) * Catch all memory allocation errors (thanks to Michael McConville for the push) * Several tweaks and bugfixes (See the Git log for details and contributors!) What's new in version 1.0.3 * Tag all children ('c' key) * Fixes in accounting of guest time when using virtualization (thanks to Patrick Marlier) * Performance improvements (thanks to Jann Horn) * Further performance improvements due to conditional parsing of IO data depending on selected fields. * Better consistency in coloring. * Increase limit of buffer when tracing a deep nested process tree. * Display pagefault stats. * BUGFIX: Fix crash when adding meters and toggling detailed CPU time. (thanks to Dawid Gajownik) * Add column to track the OOM-killer score of processes (thanks to Leigh Simpson) What's new in version 1.0.2 * Add IO priority support ('i' key) * Avoid deleting .htoprc if it is a symlink * Fail gracefully when /proc is not mounted (thanks to Philipp Hagemeister) * Option to update process names on every refresh (thanks to Rob Hoelz) * BUGFIX: Fix crashes when process list is empty What's new in version 1.0.1 * Move .htoprc to XDG-compliant path ~/.config/htop/htoprc, respecting $XDG_CONFIG_HOME (thanks to Hadzhimurad Ustarkhan for the suggestion.) * Safer behavior on the kill screen, to make it harder to kill the wrong process. * Fix for building in FreeBSD 8.2 (thanks to Trond Endrestol) * BUGFIX: behavior of 'F' (follow) key was broken, also affecting the persistence of mouse selections. * BUGFIX: keep main panel up-to-date when running the screen manager, to fix crash when processes die while on the F9/Kill screen. What's new in version 1.0 * Performance improvements * Support for splitting CPU meters into two or four columns (thanks to Wim Heirman) * Switch from PLPA, which is now deprecated, to HWLOC. * Bring back support for native Linux sched_setaffinity, so we don't have to use HWLOC where we don't need to. * Support for typing in user names and column fields in selection panels. * Support for UTF-8 tree drawing (thanks to Bin Guo) * Option for counting CPUs from zero (thanks to Sean Noonan) * Meters update in every screen (no longer halting while on Setup, etc.) * Stricter checks for command-line options (thanks to Sebastian Pipping) * Incremental filtering (thanks to Seth Heeren for the idea and initial implementation) * Try harder to find the ncurses header (thanks to Moritz Barsnick) * Man page updates (thanks to Vincent Launchbury) * BUGFIX: Support larger numbers for process times. (thanks to Tristan Nakagawa for the report.) * BUGFIX: Segfault in BarMeterMode_draw() for small terminal widths (patch by Sebastian Pipping) What's new in version 0.9 * Add support for "steal"/guest CPU time measurement in virtualization environments * Expand and collapse subtrees using '+' and '-' when in tree-view * Support for cgroups (thanks to Guillaume Zitta and Daniel Lezcano) * Show custom thread names (thanks to Anders Torger) * Add support for STARTTIME field * Upgrade PLPA to version 1.3.2 * Fix license terms with regard to PLPA (thanks to Tom Callaway) * getopt-based long options and --no-color (thanks to Vincent Launchbury) * BUGFIX: Fix display of nan% in CPU meters (thanks to Steven Hampson) * BUGFIX: Fix memory leak (thanks to Pavol Rusnak) * Add Bash/emacs style navigation keys (thanks to Daniel Schuler) * Improve battery meter support (thanks to Richard W.) * BUGFIX: Fix IO-wait color in "Black on White" scheme * BUGFIX: Fix search by process name when list is filtered by user. (thanks to Sergej Pupykin for the report.) * BUGFIX: Fix alignment for display of memory values above 100G (sign of the times!) (thanks to Jan van Haarst for the report.) What's new in version 0.8.3 * BUGFIX: Fix crash on F6 key (thanks to Rainer Suhm) * BUGFIX: Fix a minor bug which affected the build process. What's new in version 0.8.2 * Integrated lsof (press 'l') * Fix display of gigabyte-sized values (thanks to Andika Triwidada) * Option to display hostname in the meters area * Rename VEID to CTID in OpenVZ systems (thanks to Thorsten Schifferdecker) * Corrections to the desktop entry file (thanks by Samuli Suominen) * BUGFIX: Correct page size calculation for FreeBSD systems (thanks to Andrew Paulsen) * Allow compilation without PLPA on systems that don't support it (thanks to Timothy Redaelli) * BUGFIX: Fix missing tree view when userland threads are hidden (thanks to Josh Stone) * BUGFIX: Fix for VPID on OpenVZ systems (thanks to Wolfgang Frisch) What's new in version 0.8.1 * Linux-VServer support (thanks to Jonathan Sambrook and Benedikt Bohm) * Battery meter (thanks to Ian Page Hands) * BUGFIX: Fix collection of IO stats in multithreaded processes (thanks to Gerhard Heift) * Remove assertion that fails on hardened kernels (thanks to Wolfram Schlich for the report) What's new in version 0.8 * Ability to change sort column with the mouse by clicking column titles (click again to invert order) * Add support for Linux per-process IO statistics, enabled with the --enable-taskstats flag, which requires a kernel compiled with taskstats support. (thanks to Tobias Oetiker) * Add Unicode support, enabled with the --enable-unicode flag, which requires libncursesw. (thanks to Sergej Pupykin) * BUGFIX: Fix display of CPU count for threaded processes. When user threads are hidden, process now shows the sum of processor usage for all processors. When user threads are displayed, each thread shows its own processor usage, including the root thread. (thanks to Bert Wesarg for the report) * BUGFIX: avoid crashing when using many meters (thanks to David Cho for the report) What's new in version 0.7 * CPU affinity configuration ('a' key) * Improve display of tree view, properly nesting threads of the same app based on TGID. * IO-wait time now counts as idle time, which is a more accurate description. It is still available in split time, now called detailed CPU time. (thanks to Samuel Thibault for the report) * BUGFIX: Correct display of TPGID field * Add TGID field * BUGFIX: Don't crash with invalid command-line flags (thanks to Nico Golde for the report) * Fix GCC 4.3 compilation issues (thanks to Martin Michlmayr for the report) * OpenVZ support, enabled at compile-time with the --enable-openvz flag. (thanks to Sergey Lychko) What's new in version 0.6.6 * Add support of NLWP field (thanks to Bert Wesarg) * BUGFIX: Fix use of configurable /proc location (thanks to Florent Thoumie) * Fix memory percentage calculation and make it saner (thanks to Olev Kartau for the report) * Added display of DRS, DT, LRS and TRS (thanks to Matthias Lederhofer) * BUGFIX: LRS and DRS memory values were flipped (thanks to Matthias Lederhofer) * BUGFIX: Don't crash on very high UIDs (thanks to Egmont Koblinger) What's new in version 0.6.5 * Add hardened-debug flags for debugging with Hardened GCC * BUGFIX: Handle error condition when a directory vanishes from /proc * BUGFIX: Fix leak of process command line * BUGFIX: Collect orphaned items when arranging the tree view. (thanks to Wolfram Schlich for assistance with debugging) * Separate proc and memory debugging into separate #defines. * BUGFIX: Fix message when configure fails due to missing libraries (thanks to Jon) * BUGFIX: Don't truncate value when displaying a very large process (thanks to Bo Liu) What's new in version 0.6.4 * Add an option to split the display of kernel time in the CPU meter into system, IO-wait, IRQ and soft-IRQ. (thanks to Philipp Richter) * --sort-key flag in the command-line, overriding the saved setting in .htoprc for the session. (thanks to Rodolfo Borges) * BUGFIX: Fixed string overflow on uptime display. (thanks to Marc Cahalan) What's new in version 0.6.3 * Performance improvements: uses much less CPU than the previous release with the default setup. * Use 64-bit values when storing processor times to avoid overflow. * Memory consumption improvements, compensating storage of 64-bit values. * Internal change: rename TypedVector to Vector and ListBox (and related classes) to Panel. * Have configure actually fail when needed libraries or headers are not found. * Horizontally scroll in larger increments when on the Linux console because of slow update of unaccelerated fb * No longer untag processes after sending a signal (useful for when SIGTERM fails and one wants to try again with SIGKILL). All processes can be untagged at once with 'U'. (thanks to A. Costa for the suggestion) What's new in version 0.6.2 * BUGFIX: Fixed crash when using some .htoprc files from 0.6 (thanks to Wolfram Schlich and John Thomas for the reports) * BUGFIX: Ensure changes to color scheme are saved * BUGFIX: Make configure behave correctly with --with-proc * Minor addition to .desktop file. What's new in version 0.6.1 * New meter type: "All CPUs", which dynamically adjusts to the number of CPUs present in the machine. Note that because of this, older versions of htop may crash when using an .htoprc file modified my the newer version. * Accept --with-proc= in configure, to specify alternative procfs locations (making htop friendlier to the Linux compatibility layer in FreeBSD) * Included icon .desktop and desktop entry (thanks to Peter Hyman) * Added a check to make sure that a root-user htop closes when its parent non-root terminal is closed. (thanks to Ilya Evseev for the report) * BUGFIX: does not crash anymore when $HOME is not set (thanks to Henning Schild for the report) * Wait for strace child process to die properly. (thanks to Marcus Fritzsch) * Support $HTOPRC (thanks to Luis Limon) What's new in version 0.6 * Configuration of columns merged into the Setup screen * Integrated strace (press 's') (thanks to Marinho Barcellos for the help) * BUGFIX: some fixes, aided by Valgrind (thanks to Wolfram Schlich for the report) * BUGFIX: fixed bug when switching meter modes (thanks to Eduardo Righes for the report) * Show processes of a single user * "SortBy" function now menu-based * Improved mouse handling * ...and on top of that reduced memory consumption! What's new in version 0.5.4 * Color schemes * -d flag, to configure delay between updates. Note that the delay value is saved in ~/.htoprc. * BUGFIX: Update of meters was halting after help screen. (thanks to Matt Moore) * BUGFIX: No longer display incorrect information in first frame. * BUGFIX: Fix auto-detection of /proc/stat, correcting CPU usage information on multiprocessor systems. What's new in version 0.5.3 * Read new field "steal" on newer /proc/stat files * Auto-detects format of /proc/stat, to cope with patched 2.4 kernels which display 2.6-style information (most notably those on RHEL 3) (thanks to Fernando Dotta for the report) * Support $HOME_ETC initiative (see http://www.pld-linux.org/Docs/home-etc) (thanks to Roman Barczynski for the tip) * The configure script now tests for /proc, so that it fails early on unsupported platforms instead of during compilation/execution. * Made presentation of the function keys in the status bar consistent across views (thanks to David Mathog for the report) * Minor changes to make the codebase more friendly to possible future ports (thanks to Jari Aalto and David Mathog for the reports) What's new in version 0.5.2 * BUGFIX: Correct display of user field (thanks to Marcin Miroslaw for the report) * Keyboard support improvements (thanks to Aury Fink Filho for the report) What's new in version 0.5.1 * BUGFIX: Correctly displays NPTL threads from /proc//task subdirectories (thanks to Mike Pot for the report) * BUGFIX: Fixes key handling on Signals listbox (thanks to Ondrej Vlach) * Renicing no longer displays temporary illegal values (thanks to Ondrej Vlach) * 'Hide userland threads' feature for NPTL threads What's new in version 0.5 * Tree view * New column, TIME (user + system time, like in top, 'T' switches to "sort by time") * Major reorganization of the underlying code of the setup screen, to manage setup pages * New setup page: Display options * Hide kernel threads ('K' key) * Colorized memory numbers * Vastly improved support for monochromatic terminals * Shadow processes that do not belong to user ('U' key) * Header margin configuration accessible via setup screen * Visual feedback on failing incremental search * BUGFIX: fixed keyboard input issues on 64-bit machines * BUGFIX: hopefully fixed the incorrect values that show on status bars in some systems * BUGFIX: doesn't mess with fields list anymore when canceling after changing the number of items * Uptime meter no longer says "1 days" ;) What's new in version 0.4.1 * BUGFIX: compiles on 64-bit architectures again (thanks to Bartosz Fenski for the report) * BUGFIX: multi-processor support fixed on kernels 2.6 (thanks to Wolfram Schlich for the report) What's new in version 0.4 * Support for multiple processors! * Basic mouse support * Modular header based on configurable meters; supports 4 view modes: bar, text, LED, graph * Uptime, load average meters (thanks to Marc Calahan) * Meters setup screen; should eventually evolve into a general setup screen, with column setup, keybindings, etc. * Thread hiding toggleable (press 'T' to hide the nonstandard dotfiles in /proc) * BUGFIX: Do not flicker screen on column configuration screen * Clock and load average meters (thanks to Marc Calahan) * BUGFIX: numeric swap indicator was printing bogus value * BUGFIX: internal fixes on Panel widget * Clear the bottom line when exiting * Press "F3" during search to walk through the results * Improved navigation on column configuration screen * BUGFIX: fix segfault on kernels with restricted /proc enabled * BUGFIX: a few last-minute bugfixes in the setup UI (thanks to Gaspare Bruno for the reports) What's new in version 0.3.3 * Saves column and sorting configuration in ~/.htoprc * Displays "hidden" threads on RedHat 9 (Thanks to Leonardo Godinho) * BUGFIX: supports process names with spaces (Thanks to Marc Calahan) * BUGFIX: ...and parentheses :) * BUGFIX: long process names overflowed RichString (Thanks to Marc Calahan) What's new in version 0.3.2 * Performance and memory usage improvements, aided by gprof * BUGFIX: quite a few fixes, aided by Valgrind * Header preview on column configuration screen (Thanks to Marc Calahan) What's new in version 0.3.1 * BUGFIX: crash fixes related to process list handling (thanks to Marc Calahan) * Man page (thanks to Bartosz Fenski) * Tag processes with the space bar * Kill multiple process based on tag * BUGFIX: corrected processing order of updates in list * Screen refresh function on Ctrl-L * Large numbers are shown in MB/GB notation in order to fit screen (thanks to Marc Calahan) * Realtime priority is correctly displayed (thanks to Marc Calahan) * Preliminary support for configurable columns, with 'C' (thanks to Marc Calahan) -- not all columns display properly yet What's new in version 0.3 * BUGFIX: no dirt left on screen on horizontal scrolling * Signal selection on "kill" command * Color-coding for users, nice and process status * "Follow" function * Fully selectable sort order * Function bar on last line * Build system now uses autotools What's new in version 0.2.1 * Sorting by process or memory usage ('P' and 'M', like top) * Quicker default update (1.5 second, not yet configurable) * Now the order of the elements in the process list stay 'locked' for a while after you move the cursor to ease selecting a process * Corrected the installation instructions in README (Thanks to Jeremy Eglen) * Should now compile cleanly on Conectiva 9 and similar systems (Thanks to Adriano Frare for the report) * Friendlier Makefile * Help screen ('h') What's new in version 0.2 * Memory indicators in header now show used and total, in MB * Preliminary support for sorting (CPU% only) * Memory percentage field (resident memory / used memory) * BUGFIX: identified source of spurious crashes * Can search names containing numbers (Thanks to Rafael Jeffman) * Correctly calculates memory page size (Thanks to Rafael Jeffman) What's new in version 0.13 * Handles terminal resize * Display all user names (not only those in /etc/passwd) (Thanks to Julio Biason) What's new in version 0.12 * Support for 2.6 kernels * Uses terminal default colors as a background What's new in version 0.11 * BUGFIX: does not crash when UID is not in /etc/passwd What's new in version 0.1 * Everything! htop-3.0.5/ClockMeter.c000066400000000000000000000017011377712513700147010ustar00rootroot00000000000000/* htop - ClockMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "ClockMeter.h" #include #include "CRT.h" #include "Object.h" static const int ClockMeter_attributes[] = { CLOCK }; static void ClockMeter_updateValues(Meter* this, char* buffer, size_t size) { time_t t = time(NULL); struct tm result; struct tm* lt = localtime_r(&t, &result); this->values[0] = lt->tm_hour * 60 + lt->tm_min; strftime(buffer, size, "%H:%M:%S", lt); } const MeterClass ClockMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete }, .updateValues = ClockMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 1440, /* 24*60 */ .attributes = ClockMeter_attributes, .name = "Clock", .uiName = "Clock", .caption = "Time: ", }; htop-3.0.5/ClockMeter.h000066400000000000000000000004271377712513700147120ustar00rootroot00000000000000#ifndef HEADER_ClockMeter #define HEADER_ClockMeter /* htop - ClockMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass ClockMeter_class; #endif htop-3.0.5/ColorsPanel.c000066400000000000000000000053011377712513700150720ustar00rootroot00000000000000/* htop - ColorsPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ColorsPanel.h" #include #include #include "CRT.h" #include "FunctionBar.h" #include "Header.h" #include "Object.h" #include "OptionItem.h" #include "ProvideCurses.h" #include "RichString.h" #include "Vector.h" // TO ADD A NEW SCHEME: // * Increment the size of bool check in ColorsPanel.h // * Add the entry in the ColorSchemeNames array below in the file // * Add a define in CRT.h that matches the order of the array // * Add the colors in CRT_setColors static const char* const ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static const char* const ColorSchemeNames[] = { "Default", "Monochromatic", "Black on White", "Light Terminal", "MC", "Black Night", "Broken Gray", NULL }; static void ColorsPanel_delete(Object* object) { Panel* super = (Panel*) object; ColorsPanel* this = (ColorsPanel*) object; Panel_done(super); free(this); } static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) { ColorsPanel* this = (ColorsPanel*) super; HandlerResult result = IGNORED; int mark = Panel_getSelectedIndex(super); switch(ch) { case 0x0a: case 0x0d: case KEY_ENTER: case KEY_MOUSE: case KEY_RECLICK: case ' ': assert(mark >= 0); assert(mark < LAST_COLORSCHEME); for (int i = 0; ColorSchemeNames[i] != NULL; i++) CheckItem_set((CheckItem*)Panel_get(super, i), false); CheckItem_set((CheckItem*)Panel_get(super, mark), true); this->settings->colorScheme = mark; this->settings->changed = true; CRT_setColors(mark); clear(); result = HANDLED | REDRAW; } return result; } const PanelClass ColorsPanel_class = { .super = { .extends = Class(Panel), .delete = ColorsPanel_delete }, .eventHandler = ColorsPanel_eventHandler }; ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) { ColorsPanel* this = AllocThis(ColorsPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(ColorsFunctions, NULL, NULL); Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar); this->settings = settings; this->scr = scr; assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1); Panel_setHeader(super, "Colors"); for (int i = 0; ColorSchemeNames[i] != NULL; i++) { Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false)); } CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true); return this; } htop-3.0.5/ColorsPanel.h000066400000000000000000000010001377712513700150670ustar00rootroot00000000000000#ifndef HEADER_ColorsPanel #define HEADER_ColorsPanel /* htop - ColorsPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Panel.h" #include "ScreenManager.h" #include "Settings.h" typedef struct ColorsPanel_ { Panel super; Settings* settings; ScreenManager* scr; } ColorsPanel; extern const PanelClass ColorsPanel_class; ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr); #endif htop-3.0.5/ColumnsPanel.c000066400000000000000000000075331377712513700152620ustar00rootroot00000000000000/* htop - ColumnsPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ColumnsPanel.h" #include #include #include "CRT.h" #include "FunctionBar.h" #include "ListItem.h" #include "Object.h" #include "Process.h" #include "ProvideCurses.h" #include "XUtils.h" static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL}; static void ColumnsPanel_delete(Object* object) { Panel* super = (Panel*) object; ColumnsPanel* this = (ColumnsPanel*) object; Panel_done(super); free(this); } static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { ColumnsPanel* const this = (ColumnsPanel*) super; int selected = Panel_getSelectedIndex(super); HandlerResult result = IGNORED; int size = Panel_size(super); switch(ch) { case 0x0a: case 0x0d: case KEY_ENTER: case KEY_MOUSE: case KEY_RECLICK: { if (selected < size - 1) { this->moving = !(this->moving); Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS); ListItem* selectedItem = (ListItem*) Panel_getSelected(super); if (selectedItem) selectedItem->moving = this->moving; result = HANDLED; } break; } case KEY_UP: { if (!this->moving) { break; } } /* else fallthrough */ case KEY_F(7): case '[': case '-': { if (selected < size - 1) Panel_moveSelectedUp(super); result = HANDLED; break; } case KEY_DOWN: { if (!this->moving) { break; } } /* else fallthrough */ case KEY_F(8): case ']': case '+': { if (selected < size - 2) Panel_moveSelectedDown(super); result = HANDLED; break; } case KEY_F(9): case KEY_DC: { if (selected < size - 1) { Panel_remove(super, selected); } result = HANDLED; break; } default: { if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) result = Panel_selectByTyping(super, ch); if (result == BREAK_LOOP) result = IGNORED; break; } } if (result == HANDLED) ColumnsPanel_update(super); return result; } const PanelClass ColumnsPanel_class = { .super = { .extends = Class(Panel), .delete = ColumnsPanel_delete }, .eventHandler = ColumnsPanel_eventHandler }; ColumnsPanel* ColumnsPanel_new(Settings* settings) { ColumnsPanel* this = AllocThis(ColumnsPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); this->settings = settings; this->moving = false; Panel_setHeader(super, "Active Columns"); ProcessField* fields = this->settings->fields; for (; *fields; fields++) { if (Process_fields[*fields].name) { Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields)); } } return this; } void ColumnsPanel_update(Panel* super) { ColumnsPanel* this = (ColumnsPanel*) super; int size = Panel_size(super); this->settings->changed = true; this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size + 1)); this->settings->flags = 0; for (int i = 0; i < size; i++) { int key = ((ListItem*) Panel_get(super, i))->key; this->settings->fields[i] = key; this->settings->flags |= Process_fields[key].flags; } this->settings->fields[size] = 0; } htop-3.0.5/ColumnsPanel.h000066400000000000000000000010211377712513700152510ustar00rootroot00000000000000#ifndef HEADER_ColumnsPanel #define HEADER_ColumnsPanel /* htop - ColumnsPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Panel.h" #include "Settings.h" typedef struct ColumnsPanel_ { Panel super; Settings* settings; bool moving; } ColumnsPanel; extern const PanelClass ColumnsPanel_class; ColumnsPanel* ColumnsPanel_new(Settings* settings); void ColumnsPanel_update(Panel* super); #endif htop-3.0.5/CommandScreen.c000066400000000000000000000033551377712513700153760ustar00rootroot00000000000000#include "config.h" // IWYU pragma: keep #include "CommandScreen.h" #include #include #include "Macros.h" #include "Panel.h" #include "ProvideCurses.h" #include "XUtils.h" static void CommandScreen_scan(InfoScreen* this) { Panel* panel = this->display; int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0); Panel_prune(panel); const char* p = Process_getCommand(this->process); char line[COLS + 1]; int line_offset = 0, last_spc = -1, len; for (; *p != '\0'; p++, line_offset++) { assert(line_offset >= 0 && (size_t)line_offset < sizeof(line)); line[line_offset] = *p; if (*p == ' ') { last_spc = line_offset; } if (line_offset == COLS) { len = (last_spc == -1) ? line_offset : last_spc; line[len] = '\0'; InfoScreen_addLine(this, line); line_offset -= len; last_spc = -1; memcpy(line, p - line_offset, line_offset + 1); } } if (line_offset > 0) { line[line_offset] = '\0'; InfoScreen_addLine(this, line); } Panel_setSelected(panel, idx); } static void CommandScreen_draw(InfoScreen* this) { InfoScreen_drawTitled(this, "Command of process %d - %s", this->process->pid, Process_getCommand(this->process)); } const InfoScreenClass CommandScreen_class = { .super = { .extends = Class(Object), .delete = CommandScreen_delete }, .scan = CommandScreen_scan, .draw = CommandScreen_draw }; CommandScreen* CommandScreen_new(Process* process) { CommandScreen* this = AllocThis(CommandScreen); return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " "); } void CommandScreen_delete(Object* this) { free(InfoScreen_done((InfoScreen*)this)); } htop-3.0.5/CommandScreen.h000066400000000000000000000005361377712513700154010ustar00rootroot00000000000000#ifndef HEADER_CommandScreen #define HEADER_CommandScreen #include "InfoScreen.h" #include "Object.h" #include "Process.h" typedef struct CommandScreen_ { InfoScreen super; } CommandScreen; extern const InfoScreenClass CommandScreen_class; CommandScreen* CommandScreen_new(Process* process); void CommandScreen_delete(Object* this); #endif htop-3.0.5/Compat.c000066400000000000000000000057231377712513700141040ustar00rootroot00000000000000/* htop - Compat.c (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Compat.h" #include #include // IWYU pragma: keep #include #include #include #include // IWYU pragma: keep #include "XUtils.h" // IWYU pragma: keep #ifdef HAVE_HOST_GET_CLOCK_SERVICE #include #include #endif int Compat_faccessat(int dirfd, const char* pathname, int mode, int flags) { int ret; #ifdef HAVE_FACCESSAT // Implementation note: AT_SYMLINK_NOFOLLOW unsupported on FreeBSD, fallback to lstat in that case errno = 0; ret = faccessat(dirfd, pathname, mode, flags); if (!ret || errno != EINVAL) return ret; #endif // Error out on unsupported configurations if (dirfd != AT_FDCWD || mode != F_OK) { errno = EINVAL; return -1; } // Fallback to stat(2)/lstat(2) depending on flags struct stat statinfo; if(flags) { ret = lstat(pathname, &statinfo); } else { ret = stat(pathname, &statinfo); } return ret; } int Compat_fstatat(int dirfd, const char* dirpath, const char* pathname, struct stat* statbuf, int flags) { #ifdef HAVE_FSTATAT (void)dirpath; return fstatat(dirfd, pathname, statbuf, flags); #else (void)dirfd; char path[4096]; xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname); if (flags & AT_SYMLINK_NOFOLLOW) return lstat(path, statbuf); return stat(path, statbuf); #endif } #ifndef HAVE_OPENAT int Compat_openat(const char* dirpath, const char* pathname, int flags) { char path[4096]; xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname); return open(path, flags); } #endif /* !HAVE_OPENAT */ ssize_t Compat_readlinkat(int dirfd, const char* dirpath, const char* pathname, char* buf, size_t bufsize) { #ifdef HAVE_READLINKAT (void)dirpath; return readlinkat(dirfd, pathname, buf, bufsize); #else (void)dirfd; char path[4096]; xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname); return readlink(path, buf, bufsize); #endif } int Compat_clock_monotonic_gettime(struct timespec *tp) { #if defined(HAVE_CLOCK_GETTIME) return clock_gettime(CLOCK_MONOTONIC, tp); #elif defined(HAVE_HOST_GET_CLOCK_SERVICE) clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); tp->tv_sec = mts.tv_sec; tp->tv_nsec = mts.tv_nsec; return 0; #else #error No Compat_clock_monotonic_gettime() implementation! #endif } htop-3.0.5/Compat.h000066400000000000000000000026701377712513700141070ustar00rootroot00000000000000#ifndef HEADER_Compat #define HEADER_Compat /* htop - Compat.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include #include // IWYU pragma: keep int Compat_faccessat(int dirfd, const char* pathname, int mode, int flags); int Compat_fstatat(int dirfd, const char* dirpath, const char* pathname, struct stat* statbuf, int flags); #ifdef HAVE_OPENAT typedef int openat_arg_t; static inline void Compat_openatArgClose(openat_arg_t dirfd) { close(dirfd); } static inline int Compat_openat(openat_arg_t dirfd, const char* pathname, int flags) { return openat(dirfd, pathname, flags); } #else /* HAVE_OPENAT */ typedef const char* openat_arg_t; static inline void Compat_openatArgClose(openat_arg_t dirpath) { (void)dirpath; } int Compat_openat(openat_arg_t dirpath, const char* pathname, int flags); #endif /* HAVE_OPENAT */ ssize_t Compat_readlinkat(int dirfd, const char* dirpath, const char* pathname, char* buf, size_t bufsize); int Compat_clock_monotonic_gettime(struct timespec *tp); #endif /* HEADER_Compat */ htop-3.0.5/DateMeter.c000066400000000000000000000021231377712513700145220ustar00rootroot00000000000000/* htop - DateMeter.c (C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "DateMeter.h" #include #include "CRT.h" #include "Object.h" static const int DateMeter_attributes[] = { DATE }; static void DateMeter_updateValues(Meter* this, char* buffer, size_t size) { time_t t = time(NULL); struct tm result; struct tm* lt = localtime_r(&t, &result); this->values[0] = lt->tm_yday; int year = lt->tm_year + 1900; if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { this->total = 366; } else { this->total = 365; } strftime(buffer, size, "%F", lt); } const MeterClass DateMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete }, .updateValues = DateMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 365, .attributes = DateMeter_attributes, .name = "Date", .uiName = "Date", .caption = "Date: ", }; htop-3.0.5/DateMeter.h000066400000000000000000000004211377712513700145260ustar00rootroot00000000000000#ifndef HEADER_DateMeter #define HEADER_DateMeter /* htop - DateMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass DateMeter_class; #endif htop-3.0.5/DateTimeMeter.c000066400000000000000000000022201377712513700153370ustar00rootroot00000000000000/* htop - DateTimeMeter.c (C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "DateTimeMeter.h" #include #include "CRT.h" #include "Object.h" static const int DateTimeMeter_attributes[] = { DATETIME }; static void DateTimeMeter_updateValues(Meter* this, char* buffer, size_t size) { time_t t = time(NULL); struct tm result; struct tm* lt = localtime_r(&t, &result); int year = lt->tm_year + 1900; if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { this->total = 366; } else { this->total = 365; } this->values[0] = lt->tm_yday; strftime(buffer, size, "%F %H:%M:%S", lt); } const MeterClass DateTimeMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete }, .updateValues = DateTimeMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 365, .attributes = DateTimeMeter_attributes, .name = "DateTime", .uiName = "Date and Time", .caption = "Date & Time: ", }; htop-3.0.5/DateTimeMeter.h000066400000000000000000000004411377712513700153470ustar00rootroot00000000000000#ifndef HEADER_DateTimeMeter #define HEADER_DateTimeMeter /* htop - DateTimeMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass DateTimeMeter_class; #endif htop-3.0.5/DiskIOMeter.c000066400000000000000000000077211377712513700150000ustar00rootroot00000000000000/* htop - DiskIOMeter.c (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "DiskIOMeter.h" #include #include #include #include "CRT.h" #include "Macros.h" #include "Object.h" #include "Platform.h" #include "RichString.h" #include "XUtils.h" static const int DiskIOMeter_attributes[] = { METER_VALUE_NOTICE, METER_VALUE_IOREAD, METER_VALUE_IOWRITE, }; static bool hasData = false; static unsigned long int cached_read_diff = 0; static unsigned long int cached_write_diff = 0; static double cached_utilisation_diff = 0.0; static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) { static unsigned long long int cached_last_update = 0; struct timeval tv; gettimeofday(&tv, NULL); unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000; unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update; /* update only every 500ms */ if (passedTimeInMs > 500) { static unsigned long int cached_read_total = 0; static unsigned long int cached_write_total = 0; static unsigned long int cached_msTimeSpend_total = 0; cached_last_update = timeInMilliSeconds; DiskIOData data; hasData = Platform_getDiskIO(&data); if (!hasData) { this->values[0] = 0; xSnprintf(buffer, len, "no data"); return; } if (data.totalBytesRead > cached_read_total) { cached_read_diff = (data.totalBytesRead - cached_read_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ } else { cached_read_diff = 0; } cached_read_total = data.totalBytesRead; if (data.totalBytesWritten > cached_write_total) { cached_write_diff = (data.totalBytesWritten - cached_write_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ } else { cached_write_diff = 0; } cached_write_total = data.totalBytesWritten; if (data.totalMsTimeSpend > cached_msTimeSpend_total) { cached_utilisation_diff = 100 * (double)(data.totalMsTimeSpend - cached_msTimeSpend_total) / passedTimeInMs; } else { cached_utilisation_diff = 0.0; } cached_msTimeSpend_total = data.totalMsTimeSpend; } this->values[0] = cached_utilisation_diff; this->total = MAXIMUM(this->values[0], 100.0); /* fix total after (initial) spike */ char bufferRead[12], bufferWrite[12]; Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead)); Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite)); snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); } static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { if (!hasData) { RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); return; } char buffer[16]; int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE; xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); RichString_writeAscii(out, CRT_colors[color], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: "); Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: "); Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); } const MeterClass DiskIOMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = DIskIOMeter_display }, .updateValues = DiskIOMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 100.0, .attributes = DiskIOMeter_attributes, .name = "DiskIO", .uiName = "Disk IO", .caption = "Disk IO: " }; htop-3.0.5/DiskIOMeter.h000066400000000000000000000007121377712513700147760ustar00rootroot00000000000000#ifndef HEADER_DiskIOMeter #define HEADER_DiskIOMeter /* htop - DiskIOMeter.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" typedef struct DiskIOData_ { unsigned long int totalBytesRead; unsigned long int totalBytesWritten; unsigned long int totalMsTimeSpend; } DiskIOData; extern const MeterClass DiskIOMeter_class; #endif /* HEADER_DiskIOMeter */ htop-3.0.5/DisplayOptionsPanel.c000066400000000000000000000142501377712513700166150ustar00rootroot00000000000000/* htop - DisplayOptionsPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "DisplayOptionsPanel.h" #include #include #include "CRT.h" #include "FunctionBar.h" #include "Header.h" #include "Object.h" #include "OptionItem.h" #include "ProvideCurses.h" static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; static void DisplayOptionsPanel_delete(Object* object) { Panel* super = (Panel*) object; DisplayOptionsPanel* this = (DisplayOptionsPanel*) object; Panel_done(super); free(this); } static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) { DisplayOptionsPanel* this = (DisplayOptionsPanel*) super; HandlerResult result = IGNORED; OptionItem* selected = (OptionItem*) Panel_getSelected(super); switch (ch) { case '\n': case '\r': case KEY_ENTER: case KEY_MOUSE: case KEY_RECLICK: case ' ': switch (OptionItem_kind(selected)) { case OPTION_ITEM_CHECK: CheckItem_toggle((CheckItem*)selected); result = HANDLED; break; case OPTION_ITEM_NUMBER: NumberItem_toggle((NumberItem*)selected); result = HANDLED; break; } break; case '-': if (OptionItem_kind(selected) == OPTION_ITEM_NUMBER) { NumberItem_decrease((NumberItem*)selected); result = HANDLED; } break; case '+': if (OptionItem_kind(selected) == OPTION_ITEM_NUMBER) { NumberItem_increase((NumberItem*)selected); result = HANDLED; } break; } if (result == HANDLED) { this->settings->changed = true; Header* header = this->scr->header; Header_calculateHeight(header); Header_reinit(header); Header_draw(header); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); } return result; } const PanelClass DisplayOptionsPanel_class = { .super = { .extends = Class(Panel), .delete = DisplayOptionsPanel_delete }, .eventHandler = DisplayOptionsPanel_eventHandler }; DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr) { DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(DisplayOptionsFunctions, NULL, NULL); Panel_init(super, 1, 1, 1, 1, Class(OptionItem), true, fuBar); this->settings = settings; this->scr = scr; Panel_setHeader(super, "Display options"); Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView))); Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Display threads in a different color", &(settings->highlightThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames))); Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName))); Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight large numbers in memory counters", &(settings->highlightMegabytes))); Panel_add(super, (Object*) CheckItem_newByRef("Leave a margin around header", &(settings->headerMargin))); Panel_add(super, (Object*) CheckItem_newByRef("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)", &(settings->detailedCPUTime))); Panel_add(super, (Object*) CheckItem_newByRef("Count CPUs from 1 instead of 0", &(settings->countCPUsFromOne))); Panel_add(super, (Object*) CheckItem_newByRef("Update process names on every refresh", &(settings->updateProcessNames))); Panel_add(super, (Object*) CheckItem_newByRef("Add guest time in CPU meter percentage", &(settings->accountGuestInCPUMeter))); Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU percentage numerically", &(settings->showCPUUsage))); Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU frequency", &(settings->showCPUFrequency))); #ifdef HAVE_SENSORS_SENSORS_H Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU temperature (requires libsensors)", &(settings->showCPUTemperature))); Panel_add(super, (Object*) CheckItem_newByRef("- Show temperature in degree Fahrenheit instead of Celsius", &(settings->degreeFahrenheit))); #endif Panel_add(super, (Object*) CheckItem_newByRef("Enable the mouse", &(settings->enableMouse))); Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255)); Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges))); Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60)); Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2)); #ifdef HAVE_LIBHWLOC Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity))); #endif return this; } htop-3.0.5/DisplayOptionsPanel.h000066400000000000000000000011001377712513700166100ustar00rootroot00000000000000#ifndef HEADER_DisplayOptionsPanel #define HEADER_DisplayOptionsPanel /* htop - DisplayOptionsPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Panel.h" #include "ScreenManager.h" #include "Settings.h" typedef struct DisplayOptionsPanel_ { Panel super; Settings* settings; ScreenManager* scr; } DisplayOptionsPanel; extern const PanelClass DisplayOptionsPanel_class; DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr); #endif htop-3.0.5/EnvScreen.c000066400000000000000000000027611377712513700145500ustar00rootroot00000000000000#include "config.h" // IWYU pragma: keep #include "EnvScreen.h" #include #include #include "CRT.h" #include "Macros.h" #include "Panel.h" #include "Platform.h" #include "ProvideCurses.h" #include "Vector.h" #include "XUtils.h" EnvScreen* EnvScreen_new(Process* process) { EnvScreen* this = xMalloc(sizeof(EnvScreen)); Object_setClass(this, Class(EnvScreen)); return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " "); } void EnvScreen_delete(Object* this) { free(InfoScreen_done((InfoScreen*)this)); } static void EnvScreen_draw(InfoScreen* this) { InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process)); } static void EnvScreen_scan(InfoScreen* this) { Panel* panel = this->display; int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0); Panel_prune(panel); CRT_dropPrivileges(); char* env = Platform_getProcessEnv(this->process->pid); CRT_restorePrivileges(); if (env) { for (char* p = env; *p; p = strrchr(p, 0) + 1) InfoScreen_addLine(this, p); free(env); } else { InfoScreen_addLine(this, "Could not read process environment."); } Vector_insertionSort(this->lines); Vector_insertionSort(panel->items); Panel_setSelected(panel, idx); } const InfoScreenClass EnvScreen_class = { .super = { .extends = Class(Object), .delete = EnvScreen_delete }, .scan = EnvScreen_scan, .draw = EnvScreen_draw }; htop-3.0.5/EnvScreen.h000066400000000000000000000004751377712513700145550ustar00rootroot00000000000000#ifndef HEADER_EnvScreen #define HEADER_EnvScreen #include "InfoScreen.h" #include "Object.h" #include "Process.h" typedef struct EnvScreen_ { InfoScreen super; } EnvScreen; extern const InfoScreenClass EnvScreen_class; EnvScreen* EnvScreen_new(Process* process); void EnvScreen_delete(Object* this); #endif htop-3.0.5/FunctionBar.c000066400000000000000000000102321377712513700150620ustar00rootroot00000000000000/* htop - FunctionBar.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "FunctionBar.h" #include #include #include "CRT.h" #include "Macros.h" #include "ProvideCurses.h" #include "XUtils.h" static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL}; static const char* const FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL}; static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)}; static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL}; static const int FunctionBar_EnterEscEvents[] = {13, 27}; static int currentLen = 0; FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) { const char* functions[] = {enter, esc, NULL}; return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents); } FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events) { FunctionBar* this = xCalloc(1, sizeof(FunctionBar)); this->functions = xCalloc(16, sizeof(char*)); if (!functions) { functions = FunctionBar_FLabels; } for (int i = 0; i < 15 && functions[i]; i++) { this->functions[i] = xStrdup(functions[i]); } if (keys && events) { this->staticData = false; this->keys.keys = xCalloc(15, sizeof(char*)); this->events = xCalloc(15, sizeof(int)); int i = 0; while (i < 15 && functions[i]) { this->keys.keys[i] = xStrdup(keys[i]); this->events[i] = events[i]; i++; } this->size = i; } else { this->staticData = true; this->keys.constKeys = FunctionBar_FKeys; this->events = FunctionBar_FEvents; this->size = ARRAYSIZE(FunctionBar_FEvents); } return this; } void FunctionBar_delete(FunctionBar* this) { for (int i = 0; i < 15 && this->functions[i]; i++) { free(this->functions[i]); } free(this->functions); if (!this->staticData) { for (int i = 0; i < this->size; i++) { free(this->keys.keys[i]); } free(this->keys.keys); free(this->events); } free(this); } void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) { for (int i = 0; i < this->size; i++) { if (this->events[i] == event) { free(this->functions[i]); this->functions[i] = xStrdup(text); break; } } } void FunctionBar_draw(const FunctionBar* this) { FunctionBar_drawExtra(this, NULL, -1, false); } void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) { attrset(CRT_colors[FUNCTION_BAR]); mvhline(LINES - 1, 0, ' ', COLS); int x = 0; for (int i = 0; i < this->size; i++) { attrset(CRT_colors[FUNCTION_KEY]); mvaddstr(LINES - 1, x, this->keys.constKeys[i]); x += strlen(this->keys.constKeys[i]); attrset(CRT_colors[FUNCTION_BAR]); mvaddstr(LINES - 1, x, this->functions[i]); x += strlen(this->functions[i]); } if (buffer) { if (attr == -1) { attrset(CRT_colors[FUNCTION_BAR]); } else { attrset(attr); } mvaddstr(LINES - 1, x, buffer); x += strlen(buffer); } attrset(CRT_colors[RESET_COLOR]); if (setCursor) { CRT_cursorX = x; curs_set(1); } else { curs_set(0); } currentLen = x; } void FunctionBar_append(const char* buffer, int attr) { if (attr == -1) { attrset(CRT_colors[FUNCTION_BAR]); } else { attrset(attr); } mvaddstr(LINES - 1, currentLen + 1, buffer); attrset(CRT_colors[RESET_COLOR]); currentLen += strlen(buffer) + 1; } int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) { int x = 0; for (int i = 0; i < this->size; i++) { x += strlen(this->keys.constKeys[i]); x += strlen(this->functions[i]); if (pos < x) { return this->events[i]; } } return ERR; } htop-3.0.5/FunctionBar.h000066400000000000000000000017601377712513700150750ustar00rootroot00000000000000#ifndef HEADER_FunctionBar #define HEADER_FunctionBar /* htop - FunctionBar.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include typedef struct FunctionBar_ { int size; char** functions; union { char** keys; const char* const* constKeys; } keys; int* events; bool staticData; } FunctionBar; FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc); FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events); void FunctionBar_delete(FunctionBar* this); void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); void FunctionBar_draw(const FunctionBar* this); void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor); void FunctionBar_append(const char* buffer, int attr); int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos); #endif htop-3.0.5/Hashtable.c000066400000000000000000000171411377712513700145510ustar00rootroot00000000000000/* htop - Hashtable.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Hashtable.h" #include #include #include #include #include #include "CRT.h" #include "Macros.h" #include "XUtils.h" typedef struct HashtableItem_ { ht_key_t key; size_t probe; void* value; } HashtableItem; struct Hashtable_ { size_t size; HashtableItem* buckets; size_t items; bool owner; }; #ifndef NDEBUG static void Hashtable_dump(const Hashtable* this) { fprintf(stderr, "Hashtable %p: size=%zu items=%zu owner=%s\n", (const void*)this, this->size, this->items, this->owner ? "yes" : "no"); size_t items = 0; for (size_t i = 0; i < this->size; i++) { fprintf(stderr, " item %5zu: key = %5u probe = %2zu value = %p\n", i, this->buckets[i].key, this->buckets[i].probe, this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)"); if (this->buckets[i].value) items++; } fprintf(stderr, "Hashtable %p: items=%zu counted=%zu\n", (const void*)this, this->items, items); } static bool Hashtable_isConsistent(const Hashtable* this) { size_t items = 0; for (size_t i = 0; i < this->size; i++) { if (this->buckets[i].value) items++; } bool res = items == this->items; if (!res) Hashtable_dump(this); return res; } size_t Hashtable_count(const Hashtable* this) { size_t items = 0; for (size_t i = 0; i < this->size; i++) { if (this->buckets[i].value) items++; } assert(items == this->items); return items; } #endif /* NDEBUG */ /* https://oeis.org/A014234 */ static const uint64_t OEISprimes[] = { 2, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647, 4294967291, 8589934583, 17179869143, 34359738337, 68719476731, 137438953447 }; static uint64_t nextPrime(size_t n) { /* on 32-bit make sure we do not return primes not fitting in size_t */ for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) { if (n <= OEISprimes[i]) return OEISprimes[i]; } CRT_fatalError("Hashtable: no prime found"); } Hashtable* Hashtable_new(size_t size, bool owner) { Hashtable* this; this = xMalloc(sizeof(Hashtable)); this->items = 0; this->size = size ? nextPrime(size) : 13; this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem)); this->owner = owner; assert(Hashtable_isConsistent(this)); return this; } void Hashtable_delete(Hashtable* this) { Hashtable_clear(this); free(this->buckets); free(this); } void Hashtable_clear(Hashtable* this) { assert(Hashtable_isConsistent(this)); if (this->owner) for (size_t i = 0; i < this->size; i++) free(this->buckets[i].value); memset(this->buckets, 0, this->size * sizeof(HashtableItem)); this->items = 0; assert(Hashtable_isConsistent(this)); } static void insert(Hashtable* this, ht_key_t key, void* value) { size_t index = key % this->size; size_t probe = 0; #ifndef NDEBUG size_t origIndex = index; #endif for (;;) { if (!this->buckets[index].value) { this->items++; this->buckets[index].key = key; this->buckets[index].probe = probe; this->buckets[index].value = value; return; } if (this->buckets[index].key == key) { if (this->owner && this->buckets[index].value != value) free(this->buckets[index].value); this->buckets[index].value = value; return; } /* Robin Hood swap */ if (probe > this->buckets[index].probe) { HashtableItem tmp = this->buckets[index]; this->buckets[index].key = key; this->buckets[index].probe = probe; this->buckets[index].value = value; key = tmp.key; probe = tmp.probe; value = tmp.value; } index = (index + 1) % this->size; probe++; assert(index != origIndex); } } void Hashtable_setSize(Hashtable* this, size_t size) { assert(Hashtable_isConsistent(this)); if (size <= this->items) return; HashtableItem* oldBuckets = this->buckets; size_t oldSize = this->size; this->size = nextPrime(size); this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem)); this->items = 0; /* rehash */ for (size_t i = 0; i < oldSize; i++) { if (!oldBuckets[i].value) continue; insert(this, oldBuckets[i].key, oldBuckets[i].value); } free(oldBuckets); assert(Hashtable_isConsistent(this)); } void Hashtable_put(Hashtable* this, ht_key_t key, void* value) { assert(Hashtable_isConsistent(this)); assert(this->size > 0); assert(value); /* grow on load-factor > 0.7 */ if (10 * this->items > 7 * this->size) { if (SIZE_MAX / 2 < this->size) CRT_fatalError("Hashtable: size overflow"); Hashtable_setSize(this, 2 * this->size); } insert(this, key, value); assert(Hashtable_isConsistent(this)); assert(Hashtable_get(this, key) != NULL); assert(this->size > this->items); } void* Hashtable_remove(Hashtable* this, ht_key_t key) { size_t index = key % this->size; size_t probe = 0; #ifndef NDEBUG size_t origIndex = index; #endif assert(Hashtable_isConsistent(this)); void* res = NULL; while (this->buckets[index].value) { if (this->buckets[index].key == key) { if (this->owner) { free(this->buckets[index].value); } else { res = this->buckets[index].value; } size_t next = (index + 1) % this->size; while (this->buckets[next].value && this->buckets[next].probe > 0) { this->buckets[index] = this->buckets[next]; this->buckets[index].probe -= 1; index = next; next = (index + 1) % this->size; } /* set empty after backward shifting */ this->buckets[index].value = NULL; this->items--; break; } if (this->buckets[index].probe < probe) break; index = (index + 1) % this->size; probe++; assert(index != origIndex); } assert(Hashtable_isConsistent(this)); assert(Hashtable_get(this, key) == NULL); /* shrink on load-factor < 0.125 */ if (8 * this->items < this->size) Hashtable_setSize(this, this->size / 2); return res; } void* Hashtable_get(Hashtable* this, ht_key_t key) { size_t index = key % this->size; size_t probe = 0; void* res = NULL; #ifndef NDEBUG size_t origIndex = index; #endif assert(Hashtable_isConsistent(this)); while (this->buckets[index].value) { if (this->buckets[index].key == key) { res = this->buckets[index].value; break; } if (this->buckets[index].probe < probe) break; index = (index + 1) != this->size ? (index + 1) : 0; probe++; assert(index != origIndex); } return res; } void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) { assert(Hashtable_isConsistent(this)); for (size_t i = 0; i < this->size; i++) { HashtableItem* walk = &this->buckets[i]; if (walk->value) f(walk->key, walk->value, userData); } assert(Hashtable_isConsistent(this)); } htop-3.0.5/Hashtable.h000066400000000000000000000016541377712513700145600ustar00rootroot00000000000000#ifndef HEADER_Hashtable #define HEADER_Hashtable /* htop - Hashtable.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include typedef unsigned int ht_key_t; typedef void(*Hashtable_PairFunction)(ht_key_t key, void* value, void* userdata); typedef struct Hashtable_ Hashtable; #ifndef NDEBUG size_t Hashtable_count(const Hashtable* this); #endif /* NDEBUG */ Hashtable* Hashtable_new(size_t size, bool owner); void Hashtable_delete(Hashtable* this); void Hashtable_clear(Hashtable* this); void Hashtable_setSize(Hashtable* this, size_t size); void Hashtable_put(Hashtable* this, ht_key_t key, void* value); void* Hashtable_remove(Hashtable* this, ht_key_t key); void* Hashtable_get(Hashtable* this, ht_key_t key); void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData); #endif htop-3.0.5/Header.c000066400000000000000000000117601377712513700140470ustar00rootroot00000000000000/* htop - Header.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Header.h" #include #include #include #include #include "CRT.h" #include "Macros.h" #include "Object.h" #include "Platform.h" #include "ProvideCurses.h" #include "XUtils.h" Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) { Header* this = xCalloc(1, sizeof(Header)); this->columns = xCalloc(nrColumns, sizeof(Vector*)); this->settings = settings; this->pl = pl; this->nrColumns = nrColumns; Header_forEachColumn(this, i) { this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); } return this; } void Header_delete(Header* this) { Header_forEachColumn(this, i) { Vector_delete(this->columns[i]); } free(this->columns); free(this); } void Header_populateFromSettings(Header* this) { Header_forEachColumn(this, col) { MeterColumnSettings* colSettings = &this->settings->columns[col]; for (int i = 0; i < colSettings->len; i++) { Header_addMeterByName(this, colSettings->names[i], col); if (colSettings->modes[i] != 0) { Header_setMode(this, i, colSettings->modes[i], col); } } } Header_calculateHeight(this); } void Header_writeBackToSettings(const Header* this) { Header_forEachColumn(this, col) { MeterColumnSettings* colSettings = &this->settings->columns[col]; String_freeArray(colSettings->names); free(colSettings->modes); Vector* vec = this->columns[col]; int len = Vector_size(vec); colSettings->names = xCalloc(len + 1, sizeof(char*)); colSettings->modes = xCalloc(len, sizeof(int)); colSettings->len = len; for (int i = 0; i < len; i++) { Meter* meter = (Meter*) Vector_get(vec, i); char* name; if (meter->param) { xAsprintf(&name, "%s(%d)", As_Meter(meter)->name, meter->param); } else { xAsprintf(&name, "%s", As_Meter(meter)->name); } colSettings->names[i] = name; colSettings->modes[i] = meter->mode; } } } MeterModeId Header_addMeterByName(Header* this, char* name, int column) { Vector* meters = this->columns[column]; char* paren = strchr(name, '('); int param = 0; if (paren) { int ok = sscanf(paren, "(%10d)", ¶m); if (!ok) param = 0; *paren = '\0'; } MeterModeId mode = TEXT_METERMODE; for (const MeterClass* const* type = Platform_meterTypes; *type; type++) { if (String_eq(name, (*type)->name)) { Meter* meter = Meter_new(this->pl, param, *type); Vector_add(meters, meter); mode = meter->mode; break; } } if (paren) *paren = '('; return mode; } void Header_setMode(Header* this, int i, MeterModeId mode, int column) { Vector* meters = this->columns[column]; if (i >= Vector_size(meters)) return; Meter* meter = (Meter*) Vector_get(meters, i); Meter_setMode(meter, mode); } Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column) { Vector* meters = this->columns[column]; Meter* meter = Meter_new(this->pl, param, type); Vector_add(meters, meter); return meter; } int Header_size(Header* this, int column) { Vector* meters = this->columns[column]; return Vector_size(meters); } MeterModeId Header_readMeterMode(Header* this, int i, int column) { Vector* meters = this->columns[column]; Meter* meter = (Meter*) Vector_get(meters, i); return meter->mode; } void Header_reinit(Header* this) { Header_forEachColumn(this, col) { for (int i = 0; i < Vector_size(this->columns[col]); i++) { Meter* meter = (Meter*) Vector_get(this->columns[col], i); if (Meter_initFn(meter)) { Meter_init(meter); } } } } void Header_draw(const Header* this) { int height = this->height; int pad = this->pad; attrset(CRT_colors[RESET_COLOR]); for (int y = 0; y < height; y++) { mvhline(y, 0, ' ', COLS); } int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1; int x = pad; Header_forEachColumn(this, col) { Vector* meters = this->columns[col]; for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { Meter* meter = (Meter*) Vector_get(meters, i); meter->draw(meter, x, y, width); y += meter->h; } x += width + pad; } } int Header_calculateHeight(Header* this) { int pad = this->settings->headerMargin ? 2 : 0; int maxHeight = pad; Header_forEachColumn(this, col) { Vector* meters = this->columns[col]; int height = pad; for (int i = 0; i < Vector_size(meters); i++) { Meter* meter = (Meter*) Vector_get(meters, i); height += meter->h; } maxHeight = MAXIMUM(maxHeight, height); } this->height = maxHeight; this->pad = pad; return maxHeight; } htop-3.0.5/Header.h000066400000000000000000000022711377712513700140510ustar00rootroot00000000000000#ifndef HEADER_Header #define HEADER_Header /* htop - Header.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" #include "ProcessList.h" #include "Settings.h" #include "Vector.h" typedef struct Header_ { Vector** columns; Settings* settings; ProcessList* pl; int nrColumns; int pad; int height; } Header; #define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns); void Header_delete(Header* this); void Header_populateFromSettings(Header* this); void Header_writeBackToSettings(const Header* this); MeterModeId Header_addMeterByName(Header* this, char* name, int column); void Header_setMode(Header* this, int i, MeterModeId mode, int column); Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column); int Header_size(Header* this, int column); MeterModeId Header_readMeterMode(Header* this, int i, int column); void Header_reinit(Header* this); void Header_draw(const Header* this); int Header_calculateHeight(Header* this); #endif htop-3.0.5/HostnameMeter.c000066400000000000000000000015211377712513700154240ustar00rootroot00000000000000/* htop - HostnameMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "HostnameMeter.h" #include #include "CRT.h" #include "Object.h" static const int HostnameMeter_attributes[] = { HOSTNAME }; static void HostnameMeter_updateValues(Meter* this, char* buffer, size_t size) { (void) this; gethostname(buffer, size - 1); } const MeterClass HostnameMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete }, .updateValues = HostnameMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 0, .total = 100.0, .attributes = HostnameMeter_attributes, .name = "Hostname", .uiName = "Hostname", .caption = "Hostname: ", }; htop-3.0.5/HostnameMeter.h000066400000000000000000000004431377712513700154330ustar00rootroot00000000000000#ifndef HEADER_HostnameMeter #define HEADER_HostnameMeter /* htop - HostnameMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass HostnameMeter_class; #endif htop-3.0.5/IncSet.c000066400000000000000000000141651377712513700140460ustar00rootroot00000000000000/* htop - IncSet.c (C) 2005-2012 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "IncSet.h" #include #include #include #include "CRT.h" #include "ListItem.h" #include "Object.h" #include "ProvideCurses.h" #include "XUtils.h" static void IncMode_reset(IncMode* mode) { mode->index = 0; mode->buffer[0] = 0; } void IncSet_reset(IncSet* this, IncType type) { IncMode_reset(&this->modes[type]); } static const char* const searchFunctions[] = {"Next ", "Prev ", "Cancel ", " Search: ", NULL}; static const char* const searchKeys[] = {"F3", "S-F3", "Esc", " "}; static const int searchEvents[] = {KEY_F(3), KEY_F(15), 27, ERR}; static inline void IncMode_initSearch(IncMode* search) { memset(search, 0, sizeof(IncMode)); search->bar = FunctionBar_new(searchFunctions, searchKeys, searchEvents); search->isFilter = false; } static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL}; static const char* const filterKeys[] = {"Enter", "Esc", " "}; static const int filterEvents[] = {13, 27, ERR}; static inline void IncMode_initFilter(IncMode* filter) { memset(filter, 0, sizeof(IncMode)); filter->bar = FunctionBar_new(filterFunctions, filterKeys, filterEvents); filter->isFilter = true; } static inline void IncMode_done(IncMode* mode) { FunctionBar_delete(mode->bar); } IncSet* IncSet_new(FunctionBar* bar) { IncSet* this = xMalloc(sizeof(IncSet)); IncMode_initSearch(&(this->modes[INC_SEARCH])); IncMode_initFilter(&(this->modes[INC_FILTER])); this->active = NULL; this->defaultBar = bar; this->filtering = false; this->found = false; return this; } void IncSet_delete(IncSet* this) { IncMode_done(&(this->modes[0])); IncMode_done(&(this->modes[1])); free(this); } static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) { Object* selected = Panel_getSelected(panel); Panel_prune(panel); if (this->filtering) { int n = 0; const char* incFilter = this->modes[INC_FILTER].buffer; for (int i = 0; i < Vector_size(lines); i++) { ListItem* line = (ListItem*)Vector_get(lines, i); if (String_contains_i(line->value, incFilter)) { Panel_add(panel, (Object*)line); if (selected == (Object*)line) { Panel_setSelected(panel, n); } n++; } } } else { for (int i = 0; i < Vector_size(lines); i++) { Object* line = Vector_get(lines, i); Panel_add(panel, line); if (selected == line) { Panel_setSelected(panel, i); } } } } static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { int size = Panel_size(panel); for (int i = 0; i < size; i++) { if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { Panel_setSelected(panel, i); return true; } } return false; } static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) { int size = Panel_size(panel); int here = Panel_getSelectedIndex(panel); int i = here; for (;;) { i += step; if (i == size) { i = 0; } if (i == -1) { i = size - 1; } if (i == here) { return false; } if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { Panel_setSelected(panel, i); return true; } } } bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { if (ch == ERR) return true; IncMode* mode = this->active; int size = Panel_size(panel); bool filterChanged = false; bool doSearch = true; if (ch == KEY_F(3) || ch == KEY_F(15)) { if (size == 0) return true; IncMode_find(mode, panel, getPanelValue, ch == KEY_F(3) ? 1 : -1); doSearch = false; } else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) { if (mode->index < INCMODE_MAX) { mode->buffer[mode->index] = ch; mode->index++; mode->buffer[mode->index] = 0; if (mode->isFilter) { filterChanged = true; if (mode->index == 1) { this->filtering = true; } } } } else if (ch == KEY_BACKSPACE || ch == 127) { if (mode->index > 0) { mode->index--; mode->buffer[mode->index] = 0; if (mode->isFilter) { filterChanged = true; if (mode->index == 0) { this->filtering = false; IncMode_reset(mode); } } } else { doSearch = false; } } else if (ch == KEY_RESIZE) { doSearch = (mode->index > 0); } else { if (mode->isFilter) { filterChanged = true; if (ch == 27) { this->filtering = false; IncMode_reset(mode); } } else { if (ch == 27) { IncMode_reset(mode); } } this->active = NULL; Panel_setDefaultBar(panel); doSearch = false; } if (doSearch) { this->found = search(mode, panel, getPanelValue); } if (filterChanged && lines) { updateWeakPanel(this, panel, lines); } return filterChanged; } const char* IncSet_getListItemValue(Panel* panel, int i) { ListItem* l = (ListItem*) Panel_get(panel, i); return l ? l->value : ""; } void IncSet_activate(IncSet* this, IncType type, Panel* panel) { this->active = &(this->modes[type]); panel->currentBar = this->active->bar; } void IncSet_drawBar(const IncSet* this) { if (this->active) { FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true); } else { FunctionBar_draw(this->defaultBar); } } int IncSet_synthesizeEvent(IncSet* this, int x) { if (this->active) { return FunctionBar_synthesizeEvent(this->active->bar, x); } else { return FunctionBar_synthesizeEvent(this->defaultBar, x); } } htop-3.0.5/IncSet.h000066400000000000000000000024301377712513700140430ustar00rootroot00000000000000#ifndef HEADER_IncSet #define HEADER_IncSet /* htop - IncSet.h (C) 2005-2012 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include "FunctionBar.h" #include "Panel.h" #include "Vector.h" #define INCMODE_MAX 40 typedef enum { INC_SEARCH = 0, INC_FILTER = 1 } IncType; typedef struct IncMode_ { char buffer[INCMODE_MAX + 1]; int index; FunctionBar* bar; bool isFilter; } IncMode; typedef struct IncSet_ { IncMode modes[2]; IncMode* active; FunctionBar* defaultBar; bool filtering; bool found; } IncSet; static inline const char* IncSet_filter(const IncSet* this) { return this->filtering ? this->modes[INC_FILTER].buffer : NULL; } typedef const char* (*IncMode_GetPanelValue)(Panel*, int); void IncSet_reset(IncSet* this, IncType type); IncSet* IncSet_new(FunctionBar* bar); void IncSet_delete(IncSet* this); bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); const char* IncSet_getListItemValue(Panel* panel, int i); void IncSet_activate(IncSet* this, IncType type, Panel* panel); void IncSet_drawBar(const IncSet* this); int IncSet_synthesizeEvent(IncSet* this, int x); #endif htop-3.0.5/InfoScreen.c000066400000000000000000000107531377712513700147130ustar00rootroot00000000000000#include "config.h" // IWYU pragma: keep #include "InfoScreen.h" #include #include #include #include #include "CRT.h" #include "IncSet.h" #include "ListItem.h" #include "Object.h" #include "ProvideCurses.h" #include "XUtils.h" static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL}; static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"}; static const int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27}; InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) { this->process = process; if (!bar) { bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents); } this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar); this->inc = IncSet_new(bar); this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE); Panel_setHeader(this->display, panelHeader); return this; } InfoScreen* InfoScreen_done(InfoScreen* this) { Panel_delete((Object*)this->display); IncSet_delete(this->inc); Vector_delete(this->lines); return this; } void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) { va_list ap; va_start(ap, fmt); char title[COLS + 1]; int len = vsnprintf(title, sizeof(title), fmt, ap); va_end(ap); if (len > COLS) { memset(&title[COLS - 3], '.', 3); } attrset(CRT_colors[METER_TEXT]); mvhline(0, 0, ' ', COLS); mvaddstr(0, 0, title); attrset(CRT_colors[DEFAULT_COLOR]); Panel_draw(this->display, true, true, true, false); IncSet_drawBar(this->inc); } void InfoScreen_addLine(InfoScreen* this, const char* line) { Vector_add(this->lines, (Object*) ListItem_new(line, 0)); const char* incFilter = IncSet_filter(this->inc); if (!incFilter || String_contains_i(line, incFilter)) { Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1)); } } void InfoScreen_appendLine(InfoScreen* this, const char* line) { ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1); ListItem_append(last, line); const char* incFilter = IncSet_filter(this->inc); if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter)) { Panel_add(this->display, (Object*)last); } } void InfoScreen_run(InfoScreen* this) { Panel* panel = this->display; if (As_InfoScreen(this)->scan) InfoScreen_scan(this); InfoScreen_draw(this); bool looping = true; while (looping) { Panel_draw(panel, false, true, true, false); IncSet_drawBar(this->inc); if (this->inc->active) { (void) move(LINES - 1, CRT_cursorX); } set_escdelay(25); int ch = getch(); if (ch == ERR) { if (As_InfoScreen(this)->onErr) { InfoScreen_onErr(this); continue; } } if (ch == KEY_MOUSE) { MEVENT mevent; int ok = getmouse(&mevent); if (ok == OK) { if (mevent.y >= panel->y && mevent.y < LINES - 1) { Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV); ch = 0; } else if (mevent.y == LINES - 1) { ch = IncSet_synthesizeEvent(this->inc, mevent.x); } } } if (this->inc->active) { IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines); continue; } switch(ch) { case ERR: continue; case KEY_F(3): case '/': IncSet_activate(this->inc, INC_SEARCH, panel); break; case KEY_F(4): case '\\': IncSet_activate(this->inc, INC_FILTER, panel); break; case KEY_F(5): clear(); if (As_InfoScreen(this)->scan) InfoScreen_scan(this); InfoScreen_draw(this); break; case '\014': // Ctrl+L clear(); InfoScreen_draw(this); break; case 27: case 'q': case KEY_F(10): looping = false; break; case KEY_RESIZE: Panel_resize(panel, COLS, LINES - 2); if (As_InfoScreen(this)->scan) InfoScreen_scan(this); InfoScreen_draw(this); break; default: if (As_InfoScreen(this)->onKey && InfoScreen_onKey(this, ch)) { continue; } Panel_onKey(panel, ch); } } } htop-3.0.5/InfoScreen.h000066400000000000000000000031771377712513700147220ustar00rootroot00000000000000#ifndef HEADER_InfoScreen #define HEADER_InfoScreen #include #include "FunctionBar.h" #include "IncSet.h" #include "Macros.h" #include "Object.h" #include "Panel.h" #include "Process.h" #include "Vector.h" typedef struct InfoScreen_ { Object super; const Process* process; Panel* display; IncSet* inc; Vector* lines; } InfoScreen; typedef void(*InfoScreen_Scan)(InfoScreen*); typedef void(*InfoScreen_Draw)(InfoScreen*); typedef void(*InfoScreen_OnErr)(InfoScreen*); typedef bool(*InfoScreen_OnKey)(InfoScreen*, int); typedef struct InfoScreenClass_ { const ObjectClass super; const InfoScreen_Scan scan; const InfoScreen_Draw draw; const InfoScreen_OnErr onErr; const InfoScreen_OnKey onKey; } InfoScreenClass; #define As_InfoScreen(this_) ((const InfoScreenClass*)(((InfoScreen*)(this_))->super.klass)) #define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_)) #define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_)) #define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_)) #define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_) InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader); InfoScreen* InfoScreen_done(InfoScreen* this); ATTR_FORMAT(printf, 2, 3) void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...); void InfoScreen_addLine(InfoScreen* this, const char* line); void InfoScreen_appendLine(InfoScreen* this, const char* line); void InfoScreen_run(InfoScreen* this); #endif htop-3.0.5/ListItem.c000066400000000000000000000033441377712513700144100ustar00rootroot00000000000000/* htop - ListItem.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "ListItem.h" #include #include #include #include "CRT.h" #include "RichString.h" #include "XUtils.h" static void ListItem_delete(Object* cast) { ListItem* this = (ListItem*)cast; free(this->value); free(this); } static void ListItem_display(const Object* cast, RichString* out) { const ListItem* const this = (const ListItem*)cast; assert (this != NULL); if (this->moving) { RichString_writeWide(out, CRT_colors[DEFAULT_COLOR], #ifdef HAVE_LIBNCURSESW CRT_utf8 ? "↕ " : #endif "+ "); } else { RichString_prune(out); } RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value); } ListItem* ListItem_new(const char* value, int key) { ListItem* this = AllocThis(ListItem); this->value = xStrdup(value); this->key = key; this->moving = false; return this; } void ListItem_append(ListItem* this, const char* text) { size_t oldLen = strlen(this->value); size_t textLen = strlen(text); size_t newLen = oldLen + textLen; this->value = xRealloc(this->value, newLen + 1); memcpy(this->value + oldLen, text, textLen); this->value[newLen] = '\0'; } static int ListItem_compare(const void* cast1, const void* cast2) { const ListItem* obj1 = (const ListItem*) cast1; const ListItem* obj2 = (const ListItem*) cast2; return strcmp(obj1->value, obj2->value); } const ObjectClass ListItem_class = { .display = ListItem_display, .delete = ListItem_delete, .compare = ListItem_compare }; htop-3.0.5/ListItem.h000066400000000000000000000011261377712513700144110ustar00rootroot00000000000000#ifndef HEADER_ListItem #define HEADER_ListItem /* htop - ListItem.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Object.h" typedef struct ListItem_ { Object super; char* value; int key; bool moving; } ListItem; extern const ObjectClass ListItem_class; ListItem* ListItem_new(const char* value, int key); void ListItem_append(ListItem* this, const char* text); static inline const char* ListItem_getRef(const ListItem* this) { return this->value; } #endif htop-3.0.5/LoadAverageMeter.c000066400000000000000000000072441377712513700160300ustar00rootroot00000000000000/* htop - LoadAverageMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "LoadAverageMeter.h" #include "CRT.h" #include "Object.h" #include "Platform.h" #include "RichString.h" #include "XUtils.h" static const int LoadAverageMeter_attributes[] = { LOAD_AVERAGE_ONE, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIFTEEN }; static const int LoadMeter_attributes[] = { LOAD }; static const int OK_attributes[] = { METER_VALUE_OK }; static const int Medium_attributes[] = { METER_VALUE_WARN }; static const int High_attributes[] = { METER_VALUE_ERROR }; static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) { Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]); // only show bar for 1min value this->curItems = 1; // change bar color and total based on value if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; this->total = 1.0; } else if (this->values[0] < this->pl->cpuCount) { this->curAttributes = Medium_attributes; this->total = this->pl->cpuCount; } else { this->curAttributes = High_attributes; this->total = 2 * this->pl->cpuCount; } xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]); } static void LoadAverageMeter_display(const Object* cast, RichString* out) { const Meter* this = (const Meter*)cast; char buffer[20]; xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); RichString_writeAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]); RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]); RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer); } static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) { double five, fifteen; Platform_getLoadAverage(&this->values[0], &five, &fifteen); // change bar color and total based on value if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; this->total = 1.0; } else if (this->values[0] < this->pl->cpuCount) { this->curAttributes = Medium_attributes; this->total = this->pl->cpuCount; } else { this->curAttributes = High_attributes; this->total = 2 * this->pl->cpuCount; } xSnprintf(buffer, size, "%.2f", this->values[0]); } static void LoadMeter_display(const Object* cast, RichString* out) { const Meter* this = (const Meter*)cast; char buffer[20]; xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); RichString_writeAscii(out, CRT_colors[LOAD], buffer); } const MeterClass LoadAverageMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = LoadAverageMeter_display, }, .updateValues = LoadAverageMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 3, .total = 100.0, .attributes = LoadAverageMeter_attributes, .name = "LoadAverage", .uiName = "Load average", .description = "Load averages: 1 minute, 5 minutes, 15 minutes", .caption = "Load average: " }; const MeterClass LoadMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = LoadMeter_display, }, .updateValues = LoadMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 100.0, .attributes = LoadMeter_attributes, .name = "Load", .uiName = "Load", .description = "Load: average of ready processes in the last minute", .caption = "Load: " }; htop-3.0.5/LoadAverageMeter.h000066400000000000000000000005311377712513700160250ustar00rootroot00000000000000#ifndef HEADER_LoadAverageMeter #define HEADER_LoadAverageMeter /* htop - LoadAverageMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass LoadAverageMeter_class; extern const MeterClass LoadMeter_class; #endif htop-3.0.5/Macros.h000066400000000000000000000034431377712513700141070ustar00rootroot00000000000000#ifndef HEADER_Macros #define HEADER_Macros #include // IWYU pragma: keep #ifndef MINIMUM #define MINIMUM(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAXIMUM #define MAXIMUM(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef CLAMP #define CLAMP(x, low, high) (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low)) #endif #ifndef ARRAYSIZE #define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) #endif #ifndef SPACESHIP_NUMBER #define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b))) #endif #ifndef SPACESHIP_NULLSTR #define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "") #endif #ifdef __GNUC__ // defined by GCC and Clang #define ATTR_FORMAT(type, index, check) __attribute__((format (type, index, check))) #define ATTR_NONNULL __attribute__((nonnull)) #define ATTR_NORETURN __attribute__((noreturn)) #define ATTR_UNUSED __attribute__((unused)) #else /* __GNUC__ */ #define ATTR_FORMAT(type, index, check) #define ATTR_NONNULL #define ATTR_NORETURN #define ATTR_UNUSED #endif /* __GNUC__ */ // ignore casts discarding const specifier, e.g. // const char [] -> char * / void * // const char *[2]' -> char *const * #ifdef __clang__ #define IGNORE_WCASTQUAL_BEGIN _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wcast-qual\"") #define IGNORE_WCASTQUAL_END _Pragma("clang diagnostic pop") #elif defined(__GNUC__) #define IGNORE_WCASTQUAL_BEGIN _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") #define IGNORE_WCASTQUAL_END _Pragma("GCC diagnostic pop") #else #define IGNORE_WCASTQUAL_BEGIN #define IGNORE_WCASTQUAL_END #endif #endif htop-3.0.5/MainPanel.c000066400000000000000000000142071377712513700145220ustar00rootroot00000000000000/* htop - ColumnsPanel.c (C) 2004-2015 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "MainPanel.h" #include #include #include "CRT.h" #include "FunctionBar.h" #include "Platform.h" #include "Process.h" #include "ProcessList.h" #include "ProvideCurses.h" #include "Settings.h" #include "XUtils.h" static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL}; void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { FunctionBar* bar = MainPanel_getFunctionBar(this); FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree "); } void MainPanel_pidSearch(MainPanel* this, int ch) { Panel* super = (Panel*) this; pid_t pid = ch - 48 + this->pidSearch; for (int i = 0; i < Panel_size(super); i++) { Process* p = (Process*) Panel_get(super, i); if (p && p->pid == pid) { Panel_setSelected(super, i); break; } } this->pidSearch = pid * 10; if (this->pidSearch > 10000000) { this->pidSearch = 0; } } static const char* MainPanel_getValue(Panel* this, int i) { const Process* p = (const Process*) Panel_get(this, i); return Process_getCommand(p); } static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { MainPanel* this = (MainPanel*) super; HandlerResult result = IGNORED; Htop_Reaction reaction = HTOP_OK; /* Let supervising ScreenManager handle resize */ if (ch == KEY_RESIZE) return IGNORED; /* reset on every normal key */ if (ch != ERR) this->state->hideProcessSelection = false; if (EVENT_IS_HEADER_CLICK(ch)) { int x = EVENT_HEADER_CLICK_GET_X(ch); const ProcessList* pl = this->state->pl; Settings* settings = this->state->settings; int hx = super->scrollH + x + 1; ProcessField field = ProcessList_keyAt(pl, hx); if (settings->treeView && settings->treeViewAlwaysByPID) { settings->treeView = false; settings->direction = 1; reaction |= Action_setSortKey(settings, field); } else if (field == Settings_getActiveSortKey(settings)) { Settings_invertSortOrder(settings); } else { reaction |= Action_setSortKey(settings, field); } reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS; result = HANDLED; } else if (ch != ERR && this->inc->active) { bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL); if (filterChanged) { this->state->pl->incFilter = IncSet_filter(this->inc); reaction = HTOP_REFRESH | HTOP_REDRAW_BAR; } if (this->inc->found) { reaction |= Action_follow(this->state); reaction |= HTOP_KEEP_FOLLOWING; } result = HANDLED; } else if (ch == 27) { this->state->hideProcessSelection = true; return HANDLED; } else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) { reaction |= (this->keys[ch])(this->state); result = HANDLED; } else if (0 < ch && ch < 255 && isdigit((unsigned char)ch)) { MainPanel_pidSearch(this, ch); } else { if (ch != ERR) { this->pidSearch = 0; } else { reaction |= HTOP_KEEP_FOLLOWING; } } if (reaction & HTOP_REDRAW_BAR) { MainPanel_updateTreeFunctions(this, this->state->settings->treeView); } if (reaction & HTOP_UPDATE_PANELHDR) { result |= REDRAW; } if (reaction & HTOP_REFRESH) { result |= REFRESH; } if (reaction & HTOP_RECALCULATE) { result |= RESCAN; } if (reaction & HTOP_SAVE_SETTINGS) { this->state->settings->changed = true; } if (reaction & HTOP_QUIT) { return BREAK_LOOP; } if (!(reaction & HTOP_KEEP_FOLLOWING)) { this->state->pl->following = -1; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); } return result; } int MainPanel_selectedPid(MainPanel* this) { Process* p = (Process*) Panel_getSelected((Panel*)this); if (p) { return p->pid; } return -1; } bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) { Panel* super = (Panel*) this; bool ok = true; bool anyTagged = false; for (int i = 0; i < Panel_size(super); i++) { Process* p = (Process*) Panel_get(super, i); if (p->tag) { ok = fn(p, arg) && ok; anyTagged = true; } } if (!anyTagged) { Process* p = (Process*) Panel_getSelected(super); if (p) { ok &= fn(p, arg); } } if (wasAnyTagged) *wasAnyTagged = anyTagged; return ok; } static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) { MainPanel* this = (MainPanel*) super; // Do not hide active search and filter bar. if (hideFunctionBar && !this->inc->active) return; IncSet_drawBar(this->inc); if (this->state->pauseProcessUpdate) { FunctionBar_append("PAUSED", CRT_colors[PAUSED]); } } static void MainPanel_printHeader(Panel* super) { MainPanel* this = (MainPanel*) super; ProcessList_printHeader(this->state->pl, &super->header); } const PanelClass MainPanel_class = { .super = { .extends = Class(Panel), .delete = MainPanel_delete }, .eventHandler = MainPanel_eventHandler, .drawFunctionBar = MainPanel_drawFunctionBar, .printHeader = MainPanel_printHeader }; MainPanel* MainPanel_new() { MainPanel* this = AllocThis(MainPanel); Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(MainFunctions, NULL, NULL)); this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action)); this->inc = IncSet_new(MainPanel_getFunctionBar(this)); Action_setBindings(this->keys); Platform_setBindings(this->keys); return this; } void MainPanel_setState(MainPanel* this, State* state) { this->state = state; } void MainPanel_delete(Object* object) { Panel* super = (Panel*) object; MainPanel* this = (MainPanel*) object; Panel_done(super); IncSet_delete(this->inc); free(this->keys); free(this); } htop-3.0.5/MainPanel.h000066400000000000000000000022061377712513700145230ustar00rootroot00000000000000#ifndef HEADER_MainPanel #define HEADER_MainPanel /* htop - ColumnsPanel.h (C) 2004-2015 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include "Action.h" #include "IncSet.h" #include "Object.h" #include "Panel.h" #include "Process.h" typedef struct MainPanel_ { Panel super; State* state; IncSet* inc; Htop_Action* keys; pid_t pidSearch; } MainPanel; typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg); #define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar) void MainPanel_updateTreeFunctions(MainPanel* this, bool mode); void MainPanel_pidSearch(MainPanel* this, int ch); int MainPanel_selectedPid(MainPanel* this); bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged); extern const PanelClass MainPanel_class; MainPanel* MainPanel_new(void); void MainPanel_setState(MainPanel* this, State* state); void MainPanel_delete(Object* object); #endif htop-3.0.5/Makefile.am000066400000000000000000000145631377712513700145530ustar00rootroot00000000000000AUTOMAKE_OPTIONS = subdir-objects bin_PROGRAMS = htop dist_man_MANS = htop.1 EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png htop.svg \ install-sh autogen.sh missing applicationsdir = $(datadir)/applications applications_DATA = htop.desktop pixmapdir = $(datadir)/pixmaps pixmap_DATA = htop.png appicondir = $(datadir)/icons/hicolor/scalable/apps appicon_DATA = htop.svg AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)" AM_LDFLAGS = myhtopsources = \ Action.c \ Affinity.c \ AffinityPanel.c \ AvailableColumnsPanel.c \ AvailableMetersPanel.c \ BatteryMeter.c \ CategoriesPanel.c \ ClockMeter.c \ ColorsPanel.c \ ColumnsPanel.c \ CommandScreen.c \ Compat.c \ CPUMeter.c \ CRT.c \ DateMeter.c \ DateTimeMeter.c \ DiskIOMeter.c \ DisplayOptionsPanel.c \ EnvScreen.c \ FunctionBar.c \ Hashtable.c \ Header.c \ HostnameMeter.c \ htop.c \ IncSet.c \ InfoScreen.c \ ListItem.c \ LoadAverageMeter.c \ MainPanel.c \ MemoryMeter.c \ Meter.c \ MetersPanel.c \ NetworkIOMeter.c \ Object.c \ OpenFilesScreen.c \ OptionItem.c \ Panel.c \ Process.c \ ProcessList.c \ ProcessLocksScreen.c \ RichString.c \ ScreenManager.c \ Settings.c \ SignalsPanel.c \ SwapMeter.c \ TasksMeter.c \ TraceScreen.c \ UptimeMeter.c \ UsersTable.c \ Vector.c \ XUtils.c myhtopheaders = \ Action.h \ Affinity.h \ AffinityPanel.h \ AvailableColumnsPanel.h \ AvailableMetersPanel.h \ BatteryMeter.h \ CPUMeter.h \ CRT.h \ CategoriesPanel.h \ ClockMeter.h \ ColorsPanel.h \ ColumnsPanel.h \ CommandScreen.h \ Compat.h \ DateMeter.h \ DateTimeMeter.h \ DiskIOMeter.h \ DisplayOptionsPanel.h \ EnvScreen.h \ FunctionBar.h \ Hashtable.h \ Header.h \ HostnameMeter.h \ IncSet.h \ InfoScreen.h \ ListItem.h \ LoadAverageMeter.h \ Macros.h \ MainPanel.h \ MemoryMeter.h \ Meter.h \ MetersPanel.h \ NetworkIOMeter.h \ Object.h \ OpenFilesScreen.h \ OptionItem.h \ Panel.h \ Process.h \ ProcessList.h \ ProcessLocksScreen.h \ ProvideCurses.h \ RichString.h \ ScreenManager.h \ Settings.h \ SignalsPanel.h \ SwapMeter.h \ TasksMeter.h \ TraceScreen.h \ UptimeMeter.h \ UsersTable.h \ Vector.h \ XUtils.h # Linux # ----- linux_platform_headers = \ linux/IOPriority.h \ linux/IOPriorityPanel.h \ linux/LibSensors.h \ linux/LinuxProcess.h \ linux/LinuxProcessList.h \ linux/Platform.h \ linux/PressureStallMeter.h \ linux/ProcessField.h \ linux/SELinuxMeter.h \ linux/SystemdMeter.h \ linux/ZramMeter.h \ linux/ZramStats.h \ zfs/ZfsArcMeter.h \ zfs/ZfsArcStats.h \ zfs/ZfsCompressedArcMeter.h if HTOP_LINUX AM_LDFLAGS += -rdynamic myhtopplatsources = \ linux/IOPriorityPanel.c \ linux/LibSensors.c \ linux/LinuxProcess.c \ linux/LinuxProcessList.c \ linux/Platform.c \ linux/PressureStallMeter.c \ linux/SELinuxMeter.c \ linux/SystemdMeter.c \ linux/ZramMeter.c \ zfs/ZfsArcMeter.c \ zfs/ZfsArcStats.c \ zfs/ZfsCompressedArcMeter.c myhtopplatheaders = $(linux_platform_headers) endif # FreeBSD # ------- freebsd_platform_headers = \ freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcess.h \ freebsd/Platform.h \ freebsd/ProcessField.h \ zfs/ZfsArcMeter.h \ zfs/ZfsCompressedArcMeter.h \ zfs/ZfsArcStats.h \ zfs/openzfs_sysctl.h if HTOP_FREEBSD myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \ freebsd/FreeBSDProcess.c \ zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c myhtopplatheaders = $(freebsd_platform_headers) endif # DragonFlyBSD # ------------ dragonflybsd_platform_headers = \ dragonflybsd/DragonFlyBSDProcessList.h \ dragonflybsd/DragonFlyBSDProcess.h \ dragonflybsd/Platform.h \ dragonflybsd/ProcessField.h if HTOP_DRAGONFLYBSD myhtopplatsources = \ dragonflybsd/Platform.c \ dragonflybsd/DragonFlyBSDProcessList.c \ dragonflybsd/DragonFlyBSDProcess.c myhtopplatheaders = $(dragonflybsd_platform_headers) endif # OpenBSD # ------- openbsd_platform_headers = \ openbsd/OpenBSDProcessList.h \ openbsd/OpenBSDProcess.h \ openbsd/Platform.h \ openbsd/ProcessField.h if HTOP_OPENBSD myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ openbsd/OpenBSDProcess.c myhtopplatheaders = $(openbsd_platform_headers) endif # Darwin # ------ darwin_platform_headers = \ darwin/DarwinProcess.h \ darwin/DarwinProcessList.h \ darwin/Platform.h \ darwin/ProcessField.h \ zfs/ZfsArcMeter.h \ zfs/ZfsCompressedArcMeter.h \ zfs/ZfsArcStats.h \ zfs/openzfs_sysctl.h if HTOP_DARWIN AM_LDFLAGS += -framework IOKit -framework CoreFoundation myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \ darwin/DarwinProcessList.c \ zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c myhtopplatheaders = $(darwin_platform_headers) endif # Solaris # ------- solaris_platform_headers = \ solaris/Platform.h \ solaris/ProcessField.h \ solaris/SolarisProcess.h \ solaris/SolarisProcessList.h \ zfs/ZfsArcMeter.h \ zfs/ZfsCompressedArcMeter.h \ zfs/ZfsArcStats.h if HTOP_SOLARIS myhtopplatsources = solaris/Platform.c \ solaris/SolarisProcess.c solaris/SolarisProcessList.c \ zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c myhtopplatheaders = $(solaris_platform_headers) endif # Unsupported # ----------- unsupported_platform_headers = \ unsupported/Platform.h \ unsupported/ProcessField.h \ unsupported/UnsupportedProcess.h \ unsupported/UnsupportedProcessList.h if HTOP_UNSUPPORTED myhtopplatsources = unsupported/Platform.c \ unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c myhtopplatheaders = $(unsupported_platform_headers) endif # ---- htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) nodist_htop_SOURCES = config.h target: echo $(htop_SOURCES) profile: $(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG" debug: $(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG" coverage: $(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov" cppcheck: cppcheck -q -v . --enable=all -DHAVE_OPENVZ dist-hook: $(top_distdir)/configure @if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \ echo 'configure is generated without pkg.m4. Please supply pkg.m4 and run ./autogen.sh to rebuild the configure script.'>&2; \ (exit 1); \ else :; \ fi .PHONY: lcov lcov: mkdir -p lcov lcov --capture --directory . --output-file coverage.info genhtml coverage.info --output-directory lcov htop-3.0.5/MemoryMeter.c000066400000000000000000000037141377712513700151240ustar00rootroot00000000000000/* htop - MemoryMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "MemoryMeter.h" #include "CRT.h" #include "Object.h" #include "Platform.h" #include "RichString.h" static const int MemoryMeter_attributes[] = { MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE }; static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) { int written; Platform_setMemoryValues(this); written = Meter_humanUnit(buffer, this->values[0], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); Meter_humanUnit(buffer, this->total, size); } static void MemoryMeter_display(const Object* cast, RichString* out) { char buffer[50]; const Meter* this = (const Meter*)cast; RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer); Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:"); RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); Meter_humanUnit(buffer, this->values[2], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer); } const MeterClass MemoryMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = MemoryMeter_display, }, .updateValues = MemoryMeter_updateValues, .defaultMode = BAR_METERMODE, .maxItems = 3, .total = 100.0, .attributes = MemoryMeter_attributes, .name = "Memory", .uiName = "Memory", .caption = "Mem" }; htop-3.0.5/MemoryMeter.h000066400000000000000000000004331377712513700151240ustar00rootroot00000000000000#ifndef HEADER_MemoryMeter #define HEADER_MemoryMeter /* htop - MemoryMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass MemoryMeter_class; #endif htop-3.0.5/Meter.c000066400000000000000000000322601377712513700137310ustar00rootroot00000000000000/* htop - Meter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Meter.h" #include #include #include #include #include #include "CRT.h" #include "Macros.h" #include "Object.h" #include "ProvideCurses.h" #include "RichString.h" #include "Settings.h" #include "XUtils.h" #define GRAPH_HEIGHT 4 /* Unit: rows (lines) */ const MeterClass Meter_class = { .super = { .extends = Class(Object) } }; Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* type) { Meter* this = xCalloc(1, sizeof(Meter)); Object_setClass(this, type); this->h = 1; this->param = param; this->pl = pl; this->curItems = type->maxItems; this->curAttributes = NULL; this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL; this->total = type->total; this->caption = xStrdup(type->caption); if (Meter_initFn(this)) { Meter_init(this); } Meter_setMode(this, type->defaultMode); return this; } int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) { const char* prefix = "KMGTPEZY"; unsigned long int powi = 1; unsigned int powj = 1, precision = 2; for (;;) { if (value / 1024 < powi) break; if (prefix[1] == '\0') break; powi *= 1024; ++prefix; } if (*prefix == 'K') precision = 0; for (; precision > 0; precision--) { powj *= 10; if (value / powi < powj) break; } return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix); } void Meter_delete(Object* cast) { if (!cast) return; Meter* this = (Meter*) cast; if (Meter_doneFn(this)) { Meter_done(this); } free(this->drawData); free(this->caption); free(this->values); free(this); } void Meter_setCaption(Meter* this, const char* caption) { free(this->caption); this->caption = xStrdup(caption); } static inline void Meter_displayBuffer(const Meter* this, const char* buffer, RichString* out) { if (Object_displayFn(this)) { Object_display(this, out); } else { RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer); } } void Meter_setMode(Meter* this, int modeIndex) { if (modeIndex > 0 && modeIndex == this->mode) { return; } if (!modeIndex) { modeIndex = 1; } assert(modeIndex < LAST_METERMODE); if (Meter_defaultMode(this) == CUSTOM_METERMODE) { this->draw = Meter_drawFn(this); if (Meter_updateModeFn(this)) { Meter_updateMode(this, modeIndex); } } else { assert(modeIndex >= 1); free(this->drawData); this->drawData = NULL; const MeterMode* mode = Meter_modes[modeIndex]; this->draw = mode->draw; this->h = mode->h; } this->mode = modeIndex; } ListItem* Meter_toListItem(Meter* this, bool moving) { char mode[20]; if (this->mode) { xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode]->uiName); } else { mode[0] = '\0'; } char number[10]; if (this->param > 0) { xSnprintf(number, sizeof(number), " %d", this->param); } else { number[0] = '\0'; } char buffer[50]; xSnprintf(buffer, sizeof(buffer), "%s%s%s", Meter_uiName(this), number, mode); ListItem* li = ListItem_new(buffer, 0); li->moving = moving; return li; } /* ---------- TextMeterMode ---------- */ static void TextMeterMode_draw(Meter* this, int x, int y, int w) { char buffer[METER_BUFFER_LEN]; Meter_updateValues(this, buffer, sizeof(buffer)); attrset(CRT_colors[METER_TEXT]); mvaddnstr(y, x, this->caption, w - 1); attrset(CRT_colors[RESET_COLOR]); int captionLen = strlen(this->caption); x += captionLen; w -= captionLen; if (w <= 0) return; RichString_begin(out); Meter_displayBuffer(this, buffer, &out); RichString_printoffnVal(out, y, x, 0, w - 1); RichString_end(out); } /* ---------- BarMeterMode ---------- */ static const char BarMeterMode_characters[] = "|#*@$%&."; static void BarMeterMode_draw(Meter* this, int x, int y, int w) { char buffer[METER_BUFFER_LEN]; Meter_updateValues(this, buffer, sizeof(buffer)); w -= 2; attrset(CRT_colors[METER_TEXT]); int captionLen = 3; mvaddnstr(y, x, this->caption, captionLen); x += captionLen; w -= captionLen; attrset(CRT_colors[BAR_BORDER]); mvaddch(y, x, '['); mvaddch(y, x + MAXIMUM(w, 0), ']'); attrset(CRT_colors[RESET_COLOR]); w--; x++; if (w < 1) return; // The text in the bar is right aligned; // Pad with maximal spaces and then calculate needed starting position offset RichString_begin(bar); RichString_appendChr(&bar, ' ', w); RichString_appendWide(&bar, 0, buffer); int startPos = RichString_sizeVal(bar) - w; if (startPos > w) { // Text is too large for bar // Truncate meter text at a space character for (int pos = 2 * w; pos > w; pos--) { if (RichString_getCharVal(bar, pos) == ' ') { while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ') pos--; startPos = pos - w; break; } } // If still to large, print the start not the end startPos = MINIMUM(startPos, w); } assert(startPos >= 0); assert(startPos <= w); assert(startPos + w <= RichString_sizeVal(bar)); int blockSizes[10]; // First draw in the bar[] buffer... int offset = 0; for (uint8_t i = 0; i < this->curItems; i++) { double value = this->values[i]; value = CLAMP(value, 0.0, this->total); if (value > 0) { blockSizes[i] = ceil((value / this->total) * w); } else { blockSizes[i] = 0; } int nextOffset = offset + blockSizes[i]; // (Control against invalid values) nextOffset = CLAMP(nextOffset, 0, w); for (int j = offset; j < nextOffset; j++) if (RichString_getCharVal(bar, startPos + j) == ' ') { if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]); } else { RichString_setChar(&bar, startPos + j, '|'); } } offset = nextOffset; } // ...then print the buffer. offset = 0; for (uint8_t i = 0; i < this->curItems; i++) { int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i]; RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]); RichString_printoffnVal(bar, y, x + offset, startPos + offset, MINIMUM(blockSizes[i], w - offset)); offset += blockSizes[i]; offset = CLAMP(offset, 0, w); } if (offset < w) { RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset); RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset); } RichString_end(bar); move(y, x + w + 1); attrset(CRT_colors[RESET_COLOR]); } /* ---------- GraphMeterMode ---------- */ #ifdef HAVE_LIBNCURSESW #define PIXPERROW_UTF8 4 static const char* const GraphMeterMode_dotsUtf8[] = { /*00*/" ", /*01*/"⢀", /*02*/"⢠", /*03*/"⢰", /*04*/ "⢸", /*10*/"⡀", /*11*/"⣀", /*12*/"⣠", /*13*/"⣰", /*14*/ "⣸", /*20*/"⡄", /*21*/"⣄", /*22*/"⣤", /*23*/"⣴", /*24*/ "⣼", /*30*/"⡆", /*31*/"⣆", /*32*/"⣦", /*33*/"⣶", /*34*/ "⣾", /*40*/"⡇", /*41*/"⣇", /*42*/"⣧", /*43*/"⣷", /*44*/ "⣿" }; #endif #define PIXPERROW_ASCII 2 static const char* const GraphMeterMode_dotsAscii[] = { /*00*/" ", /*01*/".", /*02*/":", /*10*/".", /*11*/".", /*12*/":", /*20*/":", /*21*/":", /*22*/":" }; static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { if (!this->drawData) { this->drawData = xCalloc(1, sizeof(GraphData)); } GraphData* data = this->drawData; const int nValues = METER_BUFFER_LEN; const char* const* GraphMeterMode_dots; int GraphMeterMode_pixPerRow; #ifdef HAVE_LIBNCURSESW if (CRT_utf8) { GraphMeterMode_dots = GraphMeterMode_dotsUtf8; GraphMeterMode_pixPerRow = PIXPERROW_UTF8; } else #endif { GraphMeterMode_dots = GraphMeterMode_dotsAscii; GraphMeterMode_pixPerRow = PIXPERROW_ASCII; } attrset(CRT_colors[METER_TEXT]); int captionLen = 3; mvaddnstr(y, x, this->caption, captionLen); x += captionLen; w -= captionLen; struct timeval now; gettimeofday(&now, NULL); if (!timercmp(&now, &(data->time), <)) { int globalDelay = this->pl->settings->delay; struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 }; timeradd(&now, &delay, &(data->time)); for (int i = 0; i < nValues - 1; i++) data->values[i] = data->values[i + 1]; char buffer[METER_BUFFER_LEN]; Meter_updateValues(this, buffer, sizeof(buffer)); double value = 0.0; for (uint8_t i = 0; i < this->curItems; i++) value += this->values[i]; value /= this->total; data->values[nValues - 1] = value; } int i = nValues - (w * 2) + 2, k = 0; if (i < 0) { k = -i / 2; i = 0; } for (; i < nValues - 1; i += 2, k++) { int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT; int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix); int v2 = CLAMP((int) lround(data->values[i + 1] * pix), 1, pix); int colorIdx = GRAPH_1; for (int line = 0; line < GRAPH_HEIGHT; line++) { int line1 = CLAMP(v1 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow); int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow); attrset(CRT_colors[colorIdx]); mvaddstr(y + line, x + k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]); colorIdx = GRAPH_2; } } attrset(CRT_colors[RESET_COLOR]); } /* ---------- LEDMeterMode ---------- */ static const char* const LEDMeterMode_digitsAscii[] = { " __ ", " ", " __ ", " __ ", " ", " __ ", " __ ", " __ ", " __ ", " __ ", "| |", " |", " __|", " __|", "|__|", "|__ ", "|__ ", " |", "|__|", "|__|", "|__|", " |", "|__ ", " __|", " |", " __|", "|__|", " |", "|__|", " __|" }; #ifdef HAVE_LIBNCURSESW static const char* const LEDMeterMode_digitsUtf8[] = { "┌──â”", " â” ", "╶──â”", "╶──â”", "â•· â•·", "┌──╴", "┌──╴", "╶──â”", "┌──â”", "┌──â”", "│ │", " │ ", "┌──â”", " ──┤", "└──┤", "└──â”", "├──â”", " │", "├──┤", "└──┤", "└──â”", " ╵ ", "└──╴", "╶──â”", " ╵", "╶──â”", "└──â”", " ╵", "└──â”", " ──â”" }; #endif static const char* const* LEDMeterMode_digits; static void LEDMeterMode_drawDigit(int x, int y, int n) { for (int i = 0; i < 3; i++) mvaddstr(y+i, x, LEDMeterMode_digits[i * 10 + n]); } static void LEDMeterMode_draw(Meter* this, int x, int y, int w) { (void) w; #ifdef HAVE_LIBNCURSESW if (CRT_utf8) LEDMeterMode_digits = LEDMeterMode_digitsUtf8; else #endif LEDMeterMode_digits = LEDMeterMode_digitsAscii; char buffer[METER_BUFFER_LEN]; Meter_updateValues(this, buffer, sizeof(buffer)); RichString_begin(out); Meter_displayBuffer(this, buffer, &out); int yText = #ifdef HAVE_LIBNCURSESW CRT_utf8 ? y + 1 : #endif y + 2; attrset(CRT_colors[LED_COLOR]); mvaddstr(yText, x, this->caption); int xx = x + strlen(this->caption); int len = RichString_sizeVal(out); for (int i = 0; i < len; i++) { int c = RichString_getCharVal(out, i); if (c >= '0' && c <= '9') { LEDMeterMode_drawDigit(xx, y, c - 48); xx += 4; } else { mvaddch(yText, xx, c); xx += 1; } } attrset(CRT_colors[RESET_COLOR]); RichString_end(out); } static MeterMode BarMeterMode = { .uiName = "Bar", .h = 1, .draw = BarMeterMode_draw, }; static MeterMode TextMeterMode = { .uiName = "Text", .h = 1, .draw = TextMeterMode_draw, }; static MeterMode GraphMeterMode = { .uiName = "Graph", .h = GRAPH_HEIGHT, .draw = GraphMeterMode_draw, }; static MeterMode LEDMeterMode = { .uiName = "LED", .h = 3, .draw = LEDMeterMode_draw, }; const MeterMode* const Meter_modes[] = { NULL, &BarMeterMode, &TextMeterMode, &GraphMeterMode, &LEDMeterMode, NULL }; /* Blank meter */ static void BlankMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) { if (size > 0) { *buffer = 0; } } static void BlankMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { RichString_prune(out); } static const int BlankMeter_attributes[] = { DEFAULT_COLOR }; const MeterClass BlankMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = BlankMeter_display, }, .updateValues = BlankMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 0, .total = 100.0, .attributes = BlankMeter_attributes, .name = "Blank", .uiName = "Blank", .caption = "" }; htop-3.0.5/Meter.h000066400000000000000000000107401377712513700137350ustar00rootroot00000000000000#ifndef HEADER_Meter #define HEADER_Meter /* htop - Meter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include #include "ListItem.h" #include "Object.h" #include "ProcessList.h" #define METER_BUFFER_LEN 256 #define METER_BUFFER_CHECK(buffer, size, written) \ do { \ if ((written) < 0 || (size_t)(written) >= (size)) { \ return; \ } \ (buffer) += (written); \ (size) -= (size_t)(written); \ } while (0) #define METER_BUFFER_APPEND_CHR(buffer, size, c) \ do { \ if ((size) < 2) { \ return; \ } \ *(buffer)++ = c; \ *(buffer) = '\0'; \ (size)--; \ if ((size) == 0) { \ return; \ } \ } while (0) struct Meter_; typedef struct Meter_ Meter; typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Done)(Meter*); typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*Meter_UpdateValues)(Meter*, char*, size_t); typedef void(*Meter_Draw)(Meter*, int, int, int); typedef struct MeterClass_ { const ObjectClass super; const Meter_Init init; const Meter_Done done; const Meter_UpdateMode updateMode; const Meter_Draw draw; const Meter_UpdateValues updateValues; const int defaultMode; const double total; const int* const attributes; const char* const name; /* internal name of the meter, must not contain any space */ const char* const uiName; /* display name in header setup menu */ const char* const caption; /* prefix in the actual header */ const char* const description; /* optional meter description in header setup menu */ const uint8_t maxItems; } MeterClass; #define As_Meter(this_) ((const MeterClass*)((this_)->super.klass)) #define Meter_initFn(this_) As_Meter(this_)->init #define Meter_init(this_) As_Meter(this_)->init((Meter*)(this_)) #define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_)) #define Meter_updateModeFn(this_) As_Meter(this_)->updateMode #define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) #define Meter_drawFn(this_) As_Meter(this_)->draw #define Meter_doneFn(this_) As_Meter(this_)->done #define Meter_updateValues(this_, buf_, sz_) \ As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_) #define Meter_defaultMode(this_) As_Meter(this_)->defaultMode #define Meter_attributes(this_) As_Meter(this_)->attributes #define Meter_name(this_) As_Meter(this_)->name #define Meter_uiName(this_) As_Meter(this_)->uiName typedef struct GraphData_ { struct timeval time; double values[METER_BUFFER_LEN]; } GraphData; struct Meter_ { Object super; Meter_Draw draw; char* caption; int mode; int param; GraphData* drawData; int h; const ProcessList* pl; uint8_t curItems; const int* curAttributes; double* values; double total; void* meterData; }; typedef struct MeterMode_ { Meter_Draw draw; const char* uiName; int h; } MeterMode; typedef enum { CUSTOM_METERMODE = 0, BAR_METERMODE, TEXT_METERMODE, GRAPH_METERMODE, LED_METERMODE, LAST_METERMODE } MeterModeId; extern const MeterClass Meter_class; Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type); int Meter_humanUnit(char* buffer, unsigned long int value, size_t size); void Meter_delete(Object* cast); void Meter_setCaption(Meter* this, const char* caption); void Meter_setMode(Meter* this, int modeIndex); ListItem* Meter_toListItem(Meter* this, bool moving); extern const MeterMode* const Meter_modes[]; extern const MeterClass BlankMeter_class; #endif htop-3.0.5/MetersPanel.c000066400000000000000000000151661377712513700151020ustar00rootroot00000000000000/* htop - MetersPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "MetersPanel.h" #include #include "CRT.h" #include "FunctionBar.h" #include "Header.h" #include "ListItem.h" #include "Meter.h" #include "Object.h" #include "ProvideCurses.h" // Note: In code the meters are known to have bar/text/graph "Modes", but in UI // we call them "Styles". static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL}; static const char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"}; static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)}; // We avoid UTF-8 arrows ↠→ here as they might display full-width on Chinese // terminals, breaking our aligning. // In , arrows (U+2019..U+2199) are // considered "Ambiguous characters". static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL}; static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"}; static const int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)}; static FunctionBar* Meters_movingBar = NULL; void MetersPanel_cleanup() { if (Meters_movingBar) { FunctionBar_delete(Meters_movingBar); Meters_movingBar = NULL; } } static void MetersPanel_delete(Object* object) { Panel* super = (Panel*) object; MetersPanel* this = (MetersPanel*) object; Panel_done(super); free(this); } void MetersPanel_setMoving(MetersPanel* this, bool moving) { Panel* super = (Panel*) this; this->moving = moving; ListItem* selected = (ListItem*)Panel_getSelected(super); if (selected) { selected->moving = moving; } if (!moving) { Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); Panel_setDefaultBar(super); } else { Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW); super->currentBar = Meters_movingBar; } } static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) { Panel* super = (Panel*) this; if (this->moving) { if (neighbor) { if (selected < Vector_size(this->meters)) { MetersPanel_setMoving(this, false); Meter* meter = (Meter*) Vector_take(this->meters, selected); Panel_remove(super, selected); Vector_insert(neighbor->meters, selected, meter); Panel_insert(&(neighbor->super), selected, (Object*) Meter_toListItem(meter, false)); Panel_setSelected(&(neighbor->super), selected); MetersPanel_setMoving(neighbor, true); return true; } } } return false; } static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { MetersPanel* this = (MetersPanel*) super; int selected = Panel_getSelectedIndex(super); HandlerResult result = IGNORED; bool sideMove = false; switch(ch) { case 0x0a: case 0x0d: case KEY_ENTER: { if (!Vector_size(this->meters)) break; MetersPanel_setMoving(this, !(this->moving)); result = HANDLED; break; } case ' ': case KEY_F(4): case 't': { if (!Vector_size(this->meters)) break; Meter* meter = (Meter*) Vector_get(this->meters, selected); int mode = meter->mode + 1; if (mode == LAST_METERMODE) mode = 1; Meter_setMode(meter, mode); Panel_set(super, selected, (Object*) Meter_toListItem(meter, this->moving)); result = HANDLED; break; } case KEY_UP: { if (!this->moving) { break; } } /* else fallthrough */ case KEY_F(7): case '[': case '-': { Vector_moveUp(this->meters, selected); Panel_moveSelectedUp(super); result = HANDLED; break; } case KEY_DOWN: { if (!this->moving) { break; } } /* else fallthrough */ case KEY_F(8): case ']': case '+': { Vector_moveDown(this->meters, selected); Panel_moveSelectedDown(super); result = HANDLED; break; } case KEY_RIGHT: { sideMove = moveToNeighbor(this, this->rightNeighbor, selected); if (this->moving && !sideMove) { // lock user here until it exits positioning-mode result = HANDLED; } // if user is free, don't set HANDLED; // let ScreenManager handle focus. break; } case KEY_LEFT: { sideMove = moveToNeighbor(this, this->leftNeighbor, selected); if (this->moving && !sideMove) { result = HANDLED; } break; } case KEY_F(9): case KEY_DC: { if (!Vector_size(this->meters)) break; if (selected < Vector_size(this->meters)) { Vector_remove(this->meters, selected); Panel_remove(super, selected); } MetersPanel_setMoving(this, false); result = HANDLED; break; } } if (result == HANDLED || sideMove) { Header* header = this->scr->header; this->settings->changed = true; Header_calculateHeight(header); Header_draw(header); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); } return result; } const PanelClass MetersPanel_class = { .super = { .extends = Class(Panel), .delete = MetersPanel_delete }, .eventHandler = MetersPanel_eventHandler }; MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr) { MetersPanel* this = AllocThis(MetersPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(MetersFunctions, MetersKeys, MetersEvents); if (!Meters_movingBar) { Meters_movingBar = FunctionBar_new(MetersMovingFunctions, MetersMovingKeys, MetersMovingEvents); } Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); this->settings = settings; this->meters = meters; this->scr = scr; this->moving = false; this->rightNeighbor = NULL; this->leftNeighbor = NULL; Panel_setHeader(super, header); for (int i = 0; i < Vector_size(meters); i++) { Meter* meter = (Meter*) Vector_get(meters, i); Panel_add(super, (Object*) Meter_toListItem(meter, false)); } return this; } htop-3.0.5/MetersPanel.h000066400000000000000000000014701377712513700151000ustar00rootroot00000000000000#ifndef HEADER_MetersPanel #define HEADER_MetersPanel /* htop - MetersPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Panel.h" #include "ScreenManager.h" #include "Settings.h" #include "Vector.h" struct MetersPanel_; typedef struct MetersPanel_ MetersPanel; struct MetersPanel_ { Panel super; Settings* settings; Vector* meters; ScreenManager* scr; MetersPanel* leftNeighbor; MetersPanel* rightNeighbor; bool moving; }; void MetersPanel_cleanup(void); void MetersPanel_setMoving(MetersPanel* this, bool moving); extern const PanelClass MetersPanel_class; MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr); #endif htop-3.0.5/NEWS000066400000000000000000000002031377712513700132000ustar00rootroot00000000000000 See the commit history for news of the past. See the bug tracker for news of the future. Run the program for news of the present. htop-3.0.5/NetworkIOMeter.c000066400000000000000000000104471377712513700155360ustar00rootroot00000000000000#include "NetworkIOMeter.h" #include #include #include #include "CRT.h" #include "Macros.h" #include "Object.h" #include "Platform.h" #include "RichString.h" #include "XUtils.h" static const int NetworkIOMeter_attributes[] = { METER_VALUE_IOREAD, METER_VALUE_IOWRITE, }; static bool hasData = false; static unsigned long int cached_rxb_diff = 0; static unsigned long int cached_rxp_diff = 0; static unsigned long int cached_txb_diff = 0; static unsigned long int cached_txp_diff = 0; static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) { static unsigned long long int cached_last_update = 0; struct timeval tv; gettimeofday(&tv, NULL); unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000; unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update; /* update only every 500ms */ if (passedTimeInMs > 500) { static unsigned long int cached_rxb_total = 0; static unsigned long int cached_rxp_total = 0; static unsigned long int cached_txb_total = 0; static unsigned long int cached_txp_total = 0; cached_last_update = timeInMilliSeconds; unsigned long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted; hasData = Platform_getNetworkIO(&bytesReceived, &packetsReceived, &bytesTransmitted, &packetsTransmitted); if (!hasData) { xSnprintf(buffer, len, "no data"); return; } if (bytesReceived > cached_rxb_total) { cached_rxb_diff = (bytesReceived - cached_rxb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ cached_rxb_diff = 1000.0 * cached_rxb_diff / passedTimeInMs; /* convert to per second */ } else { cached_rxb_diff = 0; } cached_rxb_total = bytesReceived; if (packetsReceived > cached_rxp_total) { cached_rxp_diff = packetsReceived - cached_rxp_total; } else { cached_rxp_diff = 0; } cached_rxp_total = packetsReceived; if (bytesTransmitted > cached_txb_total) { cached_txb_diff = (bytesTransmitted - cached_txb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ cached_txb_diff = 1000.0 * cached_txb_diff / passedTimeInMs; /* convert to per second */ } else { cached_txb_diff = 0; } cached_txb_total = bytesTransmitted; if (packetsTransmitted > cached_txp_total) { cached_txp_diff = packetsTransmitted - cached_txp_total; } else { cached_txp_diff = 0; } cached_txp_total = packetsTransmitted; } char bufferBytesReceived[12], bufferBytesTransmitted[12]; Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived)); Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted)); xSnprintf(buffer, len, "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted); } static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { if (!hasData) { RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); return; } char buffer[64]; RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: "); Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: "); Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff); RichString_appendAscii(out, CRT_colors[METER_TEXT], buffer); } const MeterClass NetworkIOMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = NetworkIOMeter_display }, .updateValues = NetworkIOMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 0, .total = 100.0, .attributes = NetworkIOMeter_attributes, .name = "NetworkIO", .uiName = "Network IO", .caption = "Network: " }; htop-3.0.5/NetworkIOMeter.h000066400000000000000000000002431377712513700155340ustar00rootroot00000000000000#ifndef HEADER_NetworkIOMeter #define HEADER_NetworkIOMeter #include "Meter.h" extern const MeterClass NetworkIOMeter_class; #endif /* HEADER_NetworkIOMeter */ htop-3.0.5/Object.c000066400000000000000000000011111377712513700140520ustar00rootroot00000000000000/* htop - Object.c (C) 2004-2012 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Object.h" #include const ObjectClass Object_class = { .extends = NULL }; #ifndef NDEBUG bool Object_isA(const Object* o, const ObjectClass* klass) { if (!o) return false; for (const ObjectClass* type = o->klass; type; type = type->extends) { if (type == klass) { return true; } } return false; } #endif /* NDEBUG */ htop-3.0.5/Object.h000066400000000000000000000035041377712513700140670ustar00rootroot00000000000000#ifndef HEADER_Object #define HEADER_Object /* htop - Object.h (C) 2004-2012 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include "RichString.h" #include "XUtils.h" // IWYU pragma: keep #ifndef NDEBUG #include #endif struct Object_; typedef struct Object_ Object; typedef void(*Object_Display)(const Object*, RichString*); typedef int(*Object_Compare)(const void*, const void*); typedef void(*Object_Delete)(Object*); #define Object_getClass(obj_) ((const Object*)(obj_))->klass #define Object_setClass(obj_, class_) (((Object*)(obj_))->klass = (const ObjectClass*) (class_)) #define Object_delete(obj_) (assert(Object_getClass(obj_)->delete), Object_getClass(obj_)->delete((Object*)(obj_))) #define Object_displayFn(obj_) Object_getClass(obj_)->display #define Object_display(obj_, str_) (assert(Object_getClass(obj_)->display), Object_getClass(obj_)->display((const Object*)(obj_), str_)) #define Object_compare(obj_, other_) (assert(Object_getClass(obj_)->compare), Object_getClass(obj_)->compare((const void*)(obj_), other_)) #define Class(class_) ((const ObjectClass*)(&(class_ ## _class))) #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_)) typedef struct ObjectClass_ { const void* const extends; const Object_Display display; const Object_Delete delete; const Object_Compare compare; } ObjectClass; struct Object_ { const ObjectClass* klass; }; typedef union { int i; void* v; } Arg; extern const ObjectClass Object_class; #ifndef NDEBUG bool Object_isA(const Object* o, const ObjectClass* klass); #endif /* NDEBUG */ #endif htop-3.0.5/OpenFilesScreen.c000066400000000000000000000152041377712513700157000ustar00rootroot00000000000000/* htop - OpenFilesScreen.c (C) 2005-2006 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "OpenFilesScreen.h" #include #include #include #include #include #include #include #include "Macros.h" #include "Panel.h" #include "ProvideCurses.h" #include "Vector.h" #include "XUtils.h" typedef struct OpenFiles_Data_ { char* data[7]; } OpenFiles_Data; typedef struct OpenFiles_ProcessData_ { OpenFiles_Data data; int error; struct OpenFiles_FileData_* files; } OpenFiles_ProcessData; typedef struct OpenFiles_FileData_ { OpenFiles_Data data; struct OpenFiles_FileData_* next; } OpenFiles_FileData; static size_t getIndexForType(char type) { switch (type) { case 'f': return 0; case 'a': return 1; case 'D': return 2; case 'i': return 3; case 'n': return 4; case 's': return 5; case 't': return 6; } /* should never reach here */ abort(); } static const char* getDataForType(const OpenFiles_Data* data, char type) { size_t index = getIndexForType(type); return data->data[index] ? data->data[index] : ""; } OpenFilesScreen* OpenFilesScreen_new(const Process* process) { OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen)); Object_setClass(this, Class(OpenFilesScreen)); if (Process_isThread(process)) { this->pid = process->tgid; } else { this->pid = process->pid; } return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE NODE NAME"); } void OpenFilesScreen_delete(Object* this) { free(InfoScreen_done((InfoScreen*)this)); } static void OpenFilesScreen_draw(InfoScreen* this) { InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, Process_getCommand(this->process)); } static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData)); int fdpair[2] = {0, 0}; if (pipe(fdpair) == -1) { pdata->error = 1; return pdata; } pid_t child = fork(); if (child == -1) { close(fdpair[1]); close(fdpair[0]); pdata->error = 1; return pdata; } if (child == 0) { close(fdpair[0]); dup2(fdpair[1], STDOUT_FILENO); close(fdpair[1]); int fdnull = open("/dev/null", O_WRONLY); if (fdnull < 0) { exit(1); } dup2(fdnull, STDERR_FILENO); close(fdnull); char buffer[32] = {0}; xSnprintf(buffer, sizeof(buffer), "%d", pid); execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL); exit(127); } close(fdpair[1]); OpenFiles_Data* item = &(pdata->data); OpenFiles_FileData* fdata = NULL; FILE* fd = fdopen(fdpair[0], "r"); if (!fd) { pdata->error = 1; return pdata; } for (;;) { char* line = String_readLine(fd); if (!line) { break; } unsigned char cmd = line[0]; switch (cmd) { case 'f': /* file descriptor */ { OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData)); if (fdata == NULL) { pdata->files = nextFile; } else { fdata->next = nextFile; } fdata = nextFile; item = &(fdata->data); } /* FALLTHRU */ case 'a': /* file access mode */ case 'D': /* file's major/minor device number */ case 'i': /* file's inode number */ case 'n': /* file name, comment, Internet address */ case 's': /* file's size */ case 't': /* file's type */ { size_t index = getIndexForType(cmd); free(item->data[index]); item->data[index] = xStrdup(line + 1); break; } case 'c': /* process command name */ case 'd': /* file's device character code */ case 'g': /* process group ID */ case 'G': /* file flags */ case 'k': /* link count */ case 'l': /* file's lock status */ case 'L': /* process login name */ case 'o': /* file's offset */ case 'p': /* process ID */ case 'P': /* protocol name */ case 'R': /* parent process ID */ case 'T': /* TCP/TPI information, identified by prefixes */ case 'u': /* process user ID */ /* ignore */ break; } free(line); } fclose(fd); int wstatus; if (waitpid(child, &wstatus, 0) == -1) { pdata->error = 1; return pdata; } if (!WIFEXITED(wstatus)) { pdata->error = 1; } else { pdata->error = WEXITSTATUS(wstatus); } return pdata; } static void OpenFiles_Data_clear(OpenFiles_Data* data) { for (size_t i = 0; i < ARRAYSIZE(data->data); i++) free(data->data[i]); } static void OpenFilesScreen_scan(InfoScreen* this) { Panel* panel = this->display; int idx = Panel_getSelectedIndex(panel); Panel_prune(panel); OpenFiles_ProcessData* pdata = OpenFilesScreen_getProcessData(((OpenFilesScreen*)this)->pid); if (pdata->error == 127) { InfoScreen_addLine(this, "Could not execute 'lsof'. Please make sure it is available in your $PATH."); } else if (pdata->error == 1) { InfoScreen_addLine(this, "Failed listing open files."); } else { OpenFiles_FileData* fdata = pdata->files; while (fdata) { OpenFiles_Data* data = &fdata->data; size_t lenN = strlen(getDataForType(data, 'n')); size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/; char entry[sizeEntry]; xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s", getDataForType(data, 'f'), getDataForType(data, 't'), getDataForType(data, 'a'), getDataForType(data, 'D'), getDataForType(data, 's'), getDataForType(data, 'i'), getDataForType(data, 'n')); InfoScreen_addLine(this, entry); OpenFiles_Data_clear(data); OpenFiles_FileData* old = fdata; fdata = fdata->next; free(old); } OpenFiles_Data_clear(&pdata->data); } free(pdata); Vector_insertionSort(this->lines); Vector_insertionSort(panel->items); Panel_setSelected(panel, idx); } const InfoScreenClass OpenFilesScreen_class = { .super = { .extends = Class(Object), .delete = OpenFilesScreen_delete }, .scan = OpenFilesScreen_scan, .draw = OpenFilesScreen_draw }; htop-3.0.5/OpenFilesScreen.h000066400000000000000000000010721377712513700157030ustar00rootroot00000000000000#ifndef HEADER_OpenFilesScreen #define HEADER_OpenFilesScreen /* htop - OpenFilesScreen.h (C) 2005-2006 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "InfoScreen.h" #include "Object.h" #include "Process.h" typedef struct OpenFilesScreen_ { InfoScreen super; pid_t pid; } OpenFilesScreen; extern const InfoScreenClass OpenFilesScreen_class; OpenFilesScreen* OpenFilesScreen_new(const Process* process); void OpenFilesScreen_delete(Object* this); #endif htop-3.0.5/OptionItem.c000066400000000000000000000113651377712513700147470ustar00rootroot00000000000000/* htop - OptionItem.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "OptionItem.h" #include #include #include #include "CRT.h" #include "Macros.h" #include "RichString.h" #include "XUtils.h" static void OptionItem_delete(Object* cast) { OptionItem* this = (OptionItem*)cast; assert (this != NULL); free(this->text); free(this); } static void CheckItem_display(const Object* cast, RichString* out) { const CheckItem* this = (const CheckItem*)cast; assert (this != NULL); RichString_writeAscii(out, CRT_colors[CHECK_BOX], "["); if (CheckItem_get(this)) { RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x"); } else { RichString_appendAscii(out, CRT_colors[CHECK_MARK], " "); } RichString_appendAscii(out, CRT_colors[CHECK_BOX], "] "); RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text); } static void NumberItem_display(const Object* cast, RichString* out) { const NumberItem* this = (const NumberItem*)cast; assert (this != NULL); char buffer[12]; RichString_writeAscii(out, CRT_colors[CHECK_BOX], "["); int written; if (this->scale < 0) { written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this)); } else if (this->scale > 0) { written = xSnprintf(buffer, sizeof(buffer), "%d", (int) (pow(10, this->scale) * NumberItem_get(this))); } else { written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this)); } RichString_appendAscii(out, CRT_colors[CHECK_MARK], buffer); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]"); for (int i = written; i < 5; i++) { RichString_appendAscii(out, CRT_colors[CHECK_BOX], " "); } RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text); } const OptionItemClass OptionItem_class = { .super = { .extends = Class(Object), .delete = OptionItem_delete } }; const OptionItemClass CheckItem_class = { .super = { .extends = Class(OptionItem), .delete = OptionItem_delete, .display = CheckItem_display }, .kind = OPTION_ITEM_CHECK }; const OptionItemClass NumberItem_class = { .super = { .extends = Class(OptionItem), .delete = OptionItem_delete, .display = NumberItem_display }, .kind = OPTION_ITEM_NUMBER }; CheckItem* CheckItem_newByRef(const char* text, bool* ref) { CheckItem* this = AllocThis(CheckItem); this->super.text = xStrdup(text); this->value = false; this->ref = ref; return this; } CheckItem* CheckItem_newByVal(const char* text, bool value) { CheckItem* this = AllocThis(CheckItem); this->super.text = xStrdup(text); this->value = value; this->ref = NULL; return this; } bool CheckItem_get(const CheckItem* this) { if (this->ref) { return *(this->ref); } else { return this->value; } } void CheckItem_set(CheckItem* this, bool value) { if (this->ref) { *(this->ref) = value; } else { this->value = value; } } void CheckItem_toggle(CheckItem* this) { if (this->ref) { *(this->ref) = !*(this->ref); } else { this->value = !this->value; } } NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max) { assert(min <= max); NumberItem* this = AllocThis(NumberItem); this->super.text = xStrdup(text); this->value = 0; this->ref = ref; this->scale = scale; this->min = min; this->max = max; return this; } NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max) { assert(min <= max); NumberItem* this = AllocThis(NumberItem); this->super.text = xStrdup(text); this->value = CLAMP(value, min, max); this->ref = NULL; this->scale = scale; this->min = min; this->max = max; return this; } int NumberItem_get(const NumberItem* this) { if (this->ref) { return *(this->ref); } else { return this->value; } } void NumberItem_decrease(NumberItem* this) { if (this->ref) { *(this->ref) = CLAMP(*(this->ref) - 1, this->min, this->max); } else { this->value = CLAMP(this->value - 1, this->min, this->max); } } void NumberItem_increase(NumberItem* this) { if (this->ref) { *(this->ref) = CLAMP(*(this->ref) + 1, this->min, this->max); } else { this->value = CLAMP(this->value + 1, this->min, this->max); } } void NumberItem_toggle(NumberItem* this) { if (this->ref) { if (*(this->ref) >= this->max) *(this->ref) = this->min; else *(this->ref) += 1; } else { if (this->value >= this->max) this->value = this->min; else this->value += 1; } } htop-3.0.5/OptionItem.h000066400000000000000000000032131377712513700147450ustar00rootroot00000000000000#ifndef HEADER_OptionItem #define HEADER_OptionItem /* htop - OptionItem.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Object.h" enum OptionItemType { OPTION_ITEM_CHECK, OPTION_ITEM_NUMBER, }; typedef struct OptionItemClass_ { const ObjectClass super; enum OptionItemType kind; } OptionItemClass; #define As_OptionItem(this_) ((const OptionItemClass*)((this_)->super.klass)) #define OptionItem_kind(this_) As_OptionItem(this_)->kind typedef struct OptionItem_ { Object super; char* text; } OptionItem; typedef struct CheckItem_ { OptionItem super; bool* ref; bool value; } CheckItem; typedef struct NumberItem_ { OptionItem super; char* text; int* ref; int value; int scale; int min; int max; } NumberItem; extern const OptionItemClass OptionItem_class; extern const OptionItemClass CheckItem_class; extern const OptionItemClass NumberItem_class; CheckItem* CheckItem_newByRef(const char* text, bool* ref); CheckItem* CheckItem_newByVal(const char* text, bool value); bool CheckItem_get(const CheckItem* this); void CheckItem_set(CheckItem* this, bool value); void CheckItem_toggle(CheckItem* this); NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max); NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max); int NumberItem_get(const NumberItem* this); void NumberItem_decrease(NumberItem* this); void NumberItem_increase(NumberItem* this); void NumberItem_toggle(NumberItem* this); #endif htop-3.0.5/Panel.c000066400000000000000000000303541377712513700137160ustar00rootroot00000000000000/* htop - Panel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Panel.h" #include #include #include #include #include #include #include "CRT.h" #include "ListItem.h" #include "Macros.h" #include "ProvideCurses.h" #include "RichString.h" #include "XUtils.h" const PanelClass Panel_class = { .super = { .extends = Class(Object), .delete = Panel_delete }, .eventHandler = Panel_selectByTyping, }; Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) { Panel* this; this = xMalloc(sizeof(Panel)); Object_setClass(this, Class(Panel)); Panel_init(this, x, y, w, h, type, owner, fuBar); return this; } void Panel_delete(Object* cast) { Panel* this = (Panel*)cast; Panel_done(this); free(this); } void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) { this->x = x; this->y = y; this->w = w; this->h = h; this->eventHandlerState = NULL; this->items = Vector_new(type, owner, DEFAULT_SIZE); this->scrollV = 0; this->scrollH = 0; this->selected = 0; this->oldSelected = 0; this->selectedLen = 0; this->needsRedraw = true; this->wasFocus = false; RichString_beginAllocated(this->header); this->defaultBar = fuBar; this->currentBar = fuBar; this->selectionColorId = PANEL_SELECTION_FOCUS; } void Panel_done(Panel* this) { assert (this != NULL); free(this->eventHandlerState); Vector_delete(this->items); FunctionBar_delete(this->defaultBar); RichString_end(this->header); } void Panel_setSelectionColor(Panel* this, ColorElements colorId) { this->selectionColorId = colorId; } inline void Panel_setHeader(Panel* this, const char* header) { RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header); this->needsRedraw = true; } void Panel_move(Panel* this, int x, int y) { assert (this != NULL); this->x = x; this->y = y; this->needsRedraw = true; } void Panel_resize(Panel* this, int w, int h) { assert (this != NULL); this->w = w; this->h = h; this->needsRedraw = true; } void Panel_prune(Panel* this) { assert (this != NULL); Vector_prune(this->items); this->scrollV = 0; this->selected = 0; this->oldSelected = 0; this->needsRedraw = true; } void Panel_add(Panel* this, Object* o) { assert (this != NULL); Vector_add(this->items, o); this->needsRedraw = true; } void Panel_insert(Panel* this, int i, Object* o) { assert (this != NULL); Vector_insert(this->items, i, o); this->needsRedraw = true; } void Panel_set(Panel* this, int i, Object* o) { assert (this != NULL); Vector_set(this->items, i, o); } Object* Panel_get(Panel* this, int i) { assert (this != NULL); return Vector_get(this->items, i); } Object* Panel_remove(Panel* this, int i) { assert (this != NULL); this->needsRedraw = true; Object* removed = Vector_remove(this->items, i); if (this->selected > 0 && this->selected >= Vector_size(this->items)) { this->selected--; } return removed; } Object* Panel_getSelected(Panel* this) { assert (this != NULL); if (Vector_size(this->items) > 0) { return Vector_get(this->items, this->selected); } else { return NULL; } } void Panel_moveSelectedUp(Panel* this) { assert (this != NULL); Vector_moveUp(this->items, this->selected); if (this->selected > 0) { this->selected--; } } void Panel_moveSelectedDown(Panel* this) { assert (this != NULL); Vector_moveDown(this->items, this->selected); if (this->selected + 1 < Vector_size(this->items)) { this->selected++; } } int Panel_getSelectedIndex(Panel* this) { assert (this != NULL); return this->selected; } int Panel_size(Panel* this) { assert (this != NULL); return Vector_size(this->items); } void Panel_setSelected(Panel* this, int selected) { assert (this != NULL); int size = Vector_size(this->items); if (selected >= size) { selected = size - 1; } if (selected < 0) { selected = 0; } this->selected = selected; if (Panel_eventHandlerFn(this)) { Panel_eventHandler(this, EVENT_SET_SELECTED); } } void Panel_splice(Panel* this, Vector* from) { assert (this != NULL); assert (from != NULL); Vector_splice(this->items, from); this->needsRedraw = true; } void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) { assert (this != NULL); int size = Vector_size(this->items); int scrollH = this->scrollH; int y = this->y; int x = this->x; int h = this->h; if (hideFunctionBar) h++; const int header_attr = focus ? CRT_colors[PANEL_HEADER_FOCUS] : CRT_colors[PANEL_HEADER_UNFOCUS]; if (force_redraw) { if (Panel_printHeaderFn(this)) Panel_printHeader(this); else RichString_setAttr(&this->header, header_attr); } int headerLen = RichString_sizeVal(this->header); if (headerLen > 0) { attrset(header_attr); mvhline(y, x, ' ', this->w); if (scrollH < headerLen) { RichString_printoffnVal(this->header, y, x, scrollH, MINIMUM(headerLen - scrollH, this->w)); } attrset(CRT_colors[RESET_COLOR]); y++; h--; } // ensure scroll area is on screen if (this->scrollV < 0) { this->scrollV = 0; this->needsRedraw = true; } else if (this->scrollV >= size) { this->scrollV = MAXIMUM(size - 1, 0); this->needsRedraw = true; } // ensure selection is on screen if (this->selected < this->scrollV) { this->scrollV = this->selected; this->needsRedraw = true; } else if (this->selected >= this->scrollV + h) { this->scrollV = this->selected - h + 1; this->needsRedraw = true; } int first = this->scrollV; int upTo = MINIMUM(first + h, size); int selectionColor = focus ? CRT_colors[this->selectionColorId] : CRT_colors[PANEL_SELECTION_UNFOCUS]; if (this->needsRedraw || force_redraw) { int line = 0; for (int i = first; line < h && i < upTo; i++) { Object* itemObj = Vector_get(this->items, i); RichString_begin(item); Object_display(itemObj, &item); int itemLen = RichString_sizeVal(item); int amt = MINIMUM(itemLen - scrollH, this->w); if (highlightSelected && i == this->selected) { item.highlightAttr = selectionColor; } if (item.highlightAttr) { attrset(item.highlightAttr); RichString_setAttr(&item, item.highlightAttr); this->selectedLen = itemLen; } mvhline(y + line, x, ' ', this->w); if (amt > 0) RichString_printoffnVal(item, y + line, x, scrollH, amt); if (item.highlightAttr) attrset(CRT_colors[RESET_COLOR]); RichString_end(item); line++; } while (line < h) { mvhline(y + line, x, ' ', this->w); line++; } } else { Object* oldObj = Vector_get(this->items, this->oldSelected); RichString_begin(old); Object_display(oldObj, &old); int oldLen = RichString_sizeVal(old); Object* newObj = Vector_get(this->items, this->selected); RichString_begin(new); Object_display(newObj, &new); int newLen = RichString_sizeVal(new); this->selectedLen = newLen; mvhline(y + this->oldSelected - first, x + 0, ' ', this->w); if (scrollH < oldLen) RichString_printoffnVal(old, y + this->oldSelected - first, x, scrollH, MINIMUM(oldLen - scrollH, this->w)); attrset(selectionColor); mvhline(y + this->selected - first, x + 0, ' ', this->w); RichString_setAttr(&new, selectionColor); if (scrollH < newLen) RichString_printoffnVal(new, y + this->selected - first, x, scrollH, MINIMUM(newLen - scrollH, this->w)); attrset(CRT_colors[RESET_COLOR]); RichString_end(new); RichString_end(old); } if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) { if (Panel_drawFunctionBarFn(this)) Panel_drawFunctionBar(this, hideFunctionBar); else if (!hideFunctionBar) FunctionBar_draw(this->currentBar); } this->oldSelected = this->selected; this->wasFocus = focus; this->needsRedraw = false; move(0, 0); } static int Panel_headerHeight(const Panel* this) { return RichString_sizeVal(this->header) > 0 ? 1 : 0; } bool Panel_onKey(Panel* this, int key) { assert (this != NULL); const int size = Vector_size(this->items); #define PANEL_SCROLL(amount) \ do { \ this->selected += (amount); \ this->scrollV = CLAMP(this->scrollV + (amount), 0, MAXIMUM(0, (size - this->h - Panel_headerHeight(this)))); \ this->needsRedraw = true; \ } while (0) switch (key) { case KEY_DOWN: case KEY_CTRL('N'): #ifdef KEY_C_DOWN case KEY_C_DOWN: #endif this->selected++; break; case KEY_UP: case KEY_CTRL('P'): #ifdef KEY_C_UP case KEY_C_UP: #endif this->selected--; break; case KEY_LEFT: case KEY_CTRL('B'): if (this->scrollH > 0) { this->scrollH -= MAXIMUM(CRT_scrollHAmount, 0); this->needsRedraw = true; } break; case KEY_RIGHT: case KEY_CTRL('F'): this->scrollH += CRT_scrollHAmount; this->needsRedraw = true; break; case KEY_PPAGE: PANEL_SCROLL(-(this->h - Panel_headerHeight(this))); break; case KEY_NPAGE: PANEL_SCROLL(+(this->h - Panel_headerHeight(this))); break; case KEY_WHEELUP: PANEL_SCROLL(-CRT_scrollWheelVAmount); break; case KEY_WHEELDOWN: PANEL_SCROLL(+CRT_scrollWheelVAmount); break; case KEY_HOME: this->selected = 0; break; case KEY_END: this->selected = size - 1; break; case KEY_CTRL('A'): case '^': this->scrollH = 0; this->needsRedraw = true; break; case KEY_CTRL('E'): case '$': this->scrollH = MAXIMUM(this->selectedLen - this->w, 0); this->needsRedraw = true; break; default: return false; } #undef PANEL_SCROLL // ensure selection within bounds if (this->selected < 0 || size == 0) { this->selected = 0; this->needsRedraw = true; } else if (this->selected >= size) { this->selected = size - 1; this->needsRedraw = true; } return true; } HandlerResult Panel_selectByTyping(Panel* this, int ch) { int size = Panel_size(this); if (!this->eventHandlerState) this->eventHandlerState = xCalloc(100, sizeof(char)); char* buffer = this->eventHandlerState; if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) { int len = strlen(buffer); if (!len) { if ('/' == ch) { ch = '\001'; } else if ('q' == ch) { return BREAK_LOOP; } } else if (1 == len && '\001' == buffer[0]) { len--; } if (len < 99) { buffer[len] = ch; buffer[len+1] = '\0'; } for (int try = 0; try < 2; try++) { len = strlen(buffer); for (int i = 0; i < size; i++) { const char* cur = ((ListItem*) Panel_get(this, i))->value; while (*cur == ' ') cur++; if (strncasecmp(cur, buffer, len) == 0) { Panel_setSelected(this, i); return HANDLED; } } // if current word did not match, // retry considering the character the start of a new word. buffer[0] = ch; buffer[1] = '\0'; } return HANDLED; } else if (ch != ERR) { buffer[0] = '\0'; } if (ch == 13) { return BREAK_LOOP; } return IGNORED; } htop-3.0.5/Panel.h000066400000000000000000000071221377712513700137200ustar00rootroot00000000000000#ifndef HEADER_Panel #define HEADER_Panel /* htop - Panel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "CRT.h" #include "FunctionBar.h" #include "Object.h" #include "RichString.h" #include "Vector.h" struct Panel_; typedef struct Panel_ Panel; typedef enum HandlerResult_ { HANDLED = 0x01, IGNORED = 0x02, BREAK_LOOP = 0x04, REFRESH = 0x08, REDRAW = 0x10, RESCAN = 0x20, SYNTH_KEY = 0x40, } HandlerResult; #define EVENT_SET_SELECTED (-1) #define EVENT_HEADER_CLICK(x_) (-10000 + (x_)) #define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000) #define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000) typedef HandlerResult (*Panel_EventHandler)(Panel*, int); typedef void (*Panel_DrawFunctionBar)(Panel*, bool); typedef void (*Panel_PrintHeader)(Panel*); typedef struct PanelClass_ { const ObjectClass super; const Panel_EventHandler eventHandler; const Panel_DrawFunctionBar drawFunctionBar; const Panel_PrintHeader printHeader; } PanelClass; #define As_Panel(this_) ((const PanelClass*)((this_)->super.klass)) #define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler #define Panel_eventHandler(this_, ev_) (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_)) #define Panel_drawFunctionBarFn(this_) As_Panel(this_)->drawFunctionBar #define Panel_drawFunctionBar(this_, hideFB_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_), hideFB_)) #define Panel_printHeaderFn(this_) As_Panel(this_)->printHeader #define Panel_printHeader(this_) (assert(As_Panel(this_)->printHeader), As_Panel(this_)->printHeader((Panel*)(this_))) struct Panel_ { Object super; int x, y, w, h; Vector* items; int selected; int oldSelected; int selectedLen; void* eventHandlerState; int scrollV; short scrollH; bool needsRedraw; bool wasFocus; FunctionBar* currentBar; FunctionBar* defaultBar; RichString header; ColorElements selectionColorId; }; #define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0) #define KEY_CTRL(l) ((l)-'A'+1) extern const PanelClass Panel_class; Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar); void Panel_delete(Object* cast); void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar); void Panel_done(Panel* this); void Panel_setSelectionColor(Panel* this, ColorElements colorId); void Panel_setHeader(Panel* this, const char* header); void Panel_move(Panel* this, int x, int y); void Panel_resize(Panel* this, int w, int h); void Panel_prune(Panel* this); void Panel_add(Panel* this, Object* o); void Panel_insert(Panel* this, int i, Object* o); void Panel_set(Panel* this, int i, Object* o); Object* Panel_get(Panel* this, int i); Object* Panel_remove(Panel* this, int i); Object* Panel_getSelected(Panel* this); void Panel_moveSelectedUp(Panel* this); void Panel_moveSelectedDown(Panel* this); int Panel_getSelectedIndex(Panel* this); int Panel_size(Panel* this); void Panel_setSelected(Panel* this, int selected); void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar); void Panel_splice(Panel* this, Vector* from); bool Panel_onKey(Panel* this, int key); HandlerResult Panel_selectByTyping(Panel* this, int ch); #endif htop-3.0.5/Process.c000066400000000000000000000461701377712513700143000ustar00rootroot00000000000000/* htop - Process.c (C) 2004-2015 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Process.h" #include #include #include #include #include #include #include #include #include #include #include "CRT.h" #include "Macros.h" #include "Platform.h" #include "ProcessList.h" #include "RichString.h" #include "Settings.h" #include "XUtils.h" #if defined(MAJOR_IN_MKDEV) #include #elif defined(MAJOR_IN_SYSMACROS) #include #endif static uid_t Process_getuid = (uid_t)-1; int Process_pidDigits = 7; void Process_setupColumnWidths() { int maxPid = Platform_getMaxPid(); if (maxPid == -1) return; Process_pidDigits = ceil(log10(maxPid)); assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS); } void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) { char buffer[10]; int len; int largeNumberColor = CRT_colors[LARGE_NUMBER]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processGigabytesColor = CRT_colors[PROCESS_GIGABYTES]; int processColor = CRT_colors[PROCESS]; if (!coloring) { largeNumberColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS]; processGigabytesColor = CRT_colors[PROCESS]; } if (number < 1000) { //Plain number, no markings len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number); RichString_appendnAscii(str, processColor, buffer, len); } else if (number < 100000) { //2 digit MB, 3 digit KB len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000); RichString_appendnAscii(str, processMegabytesColor, buffer, len); number %= 1000; len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number); RichString_appendnAscii(str, processColor, buffer, len); } else if (number < 1000 * ONE_K) { //3 digit MB number /= ONE_K; len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number); RichString_appendnAscii(str, processMegabytesColor, buffer, len); } else if (number < 10000 * ONE_K) { //1 digit GB, 3 digit MB number /= ONE_K; len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000); RichString_appendnAscii(str, processGigabytesColor, buffer, len); number %= 1000; len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number); RichString_appendnAscii(str, processMegabytesColor, buffer, len); } else if (number < 100000 * ONE_K) { //2 digit GB, 1 digit MB number /= 100 * ONE_K; len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10); RichString_appendnAscii(str, processGigabytesColor, buffer, len); number %= 10; len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); RichString_appendnAscii(str, processMegabytesColor, buffer, len); RichString_appendAscii(str, processGigabytesColor, "G "); } else if (number < 1000 * ONE_M) { //3 digit GB number /= ONE_M; len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number); RichString_appendnAscii(str, processGigabytesColor, buffer, len); } else if (number < 10000ULL * ONE_M) { //1 digit TB, 3 digit GB number /= ONE_M; len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000); RichString_appendnAscii(str, largeNumberColor, buffer, len); number %= 1000; len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number); RichString_appendnAscii(str, processGigabytesColor, buffer, len); } else { //2 digit TB and above len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G); RichString_appendnAscii(str, largeNumberColor, buffer, len); } } void Process_colorNumber(RichString* str, unsigned long long number, bool coloring) { char buffer[13]; int largeNumberColor = CRT_colors[LARGE_NUMBER]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processColor = CRT_colors[PROCESS]; int processShadowColor = CRT_colors[PROCESS_SHADOW]; if (!coloring) { largeNumberColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS]; processShadowColor = CRT_colors[PROCESS]; } if (number == ULLONG_MAX) { RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A "); } else if (number >= 100000LL * ONE_DECIMAL_T) { xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G); RichString_appendnAscii(str, largeNumberColor, buffer, 12); } else if (number >= 100LL * ONE_DECIMAL_T) { xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M); RichString_appendnAscii(str, largeNumberColor, buffer, 8); RichString_appendnAscii(str, processMegabytesColor, buffer+8, 4); } else if (number >= 10LL * ONE_DECIMAL_G) { xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K); RichString_appendnAscii(str, largeNumberColor, buffer, 5); RichString_appendnAscii(str, processMegabytesColor, buffer+5, 3); RichString_appendnAscii(str, processColor, buffer+8, 4); } else { xSnprintf(buffer, sizeof(buffer), "%11llu ", number); RichString_appendnAscii(str, largeNumberColor, buffer, 2); RichString_appendnAscii(str, processMegabytesColor, buffer+2, 3); RichString_appendnAscii(str, processColor, buffer+5, 3); RichString_appendnAscii(str, processShadowColor, buffer+8, 4); } } void Process_printTime(RichString* str, unsigned long long totalHundredths) { unsigned long long totalSeconds = totalHundredths / 100; unsigned long long hours = totalSeconds / 3600; int minutes = (totalSeconds / 60) % 60; int seconds = totalSeconds % 60; int hundredths = totalHundredths - (totalSeconds * 100); char buffer[10]; if (hours >= 100) { xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours); RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer); } else { if (hours) { xSnprintf(buffer, sizeof(buffer), "%2lluh", hours); RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer); xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds); } else { xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths); } RichString_appendAscii(str, CRT_colors[DEFAULT_COLOR], buffer); } } void Process_fillStarttimeBuffer(Process* this) { struct tm date; (void) localtime_r(&this->starttime_ctime, &date); strftime(this->starttime_show, sizeof(this->starttime_show) - 1, (this->starttime_ctime > (time(NULL) - 86400)) ? "%R " : "%b%d ", &date); } static inline void Process_writeCommand(const Process* this, int attr, int baseattr, RichString* str) { int start = RichString_size(str); int len = 0; const char* comm = this->comm; if (this->settings->highlightBaseName || !this->settings->showProgramPath) { int basename = 0; for (int i = 0; i < this->basenameOffset; i++) { if (comm[i] == '/') { basename = i + 1; } else if (comm[i] == ':') { len = i + 1; break; } } if (len == 0) { if (this->settings->showProgramPath) { start += basename; } else { comm += basename; } len = this->basenameOffset - basename; } } RichString_appendWide(str, attr, comm); if (this->settings->highlightBaseName) { RichString_setAttrn(str, baseattr, start, len); } } void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring) { int largeNumberColor = CRT_colors[LARGE_NUMBER]; int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processColor = CRT_colors[PROCESS]; if (!coloring) { largeNumberColor = CRT_colors[PROCESS]; processMegabytesColor = CRT_colors[PROCESS]; } if (isnan(rate)) { RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A "); } else if (rate < ONE_K) { int len = snprintf(buffer, n, "%7.2f B/s ", rate); RichString_appendnAscii(str, processColor, buffer, len); } else if (rate < ONE_M) { int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K); RichString_appendnAscii(str, processColor, buffer, len); } else if (rate < ONE_G) { int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M); RichString_appendnAscii(str, processMegabytesColor, buffer, len); } else if (rate < ONE_T) { int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G); RichString_appendnAscii(str, largeNumberColor, buffer, len); } else { int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T); RichString_appendnAscii(str, largeNumberColor, buffer, len); } } void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) { int c = RichString_appendnWide(str, attr, content, MINIMUM(width, strlen(content))); RichString_appendChr(str, ' ', width + 1 - c); } void Process_writeField(const Process* this, RichString* str, ProcessField field) { char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int baseattr = CRT_colors[PROCESS_BASENAME]; size_t n = sizeof(buffer) - 1; bool coloring = this->settings->highlightMegabytes; switch (field) { case PERCENT_CPU: case PERCENT_NORM_CPU: { float cpuPercentage = this->percent_cpu; if (field == PERCENT_NORM_CPU) { cpuPercentage /= this->processList->cpuCount; } if (cpuPercentage > 999.9) { xSnprintf(buffer, n, "%4u ", (unsigned int)cpuPercentage); } else if (cpuPercentage > 99.9) { xSnprintf(buffer, n, "%3u. ", (unsigned int)cpuPercentage); } else { xSnprintf(buffer, n, "%4.1f ", cpuPercentage); } break; } case PERCENT_MEM: { if (this->percent_mem > 99.9) { xSnprintf(buffer, n, "100. "); } else { xSnprintf(buffer, n, "%4.1f ", this->percent_mem); } break; } case COMM: { if (this->settings->highlightThreads && Process_isThread(this)) { attr = CRT_colors[PROCESS_THREAD]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } if (!this->settings->treeView || this->indent == 0) { Process_writeCommand(this, attr, baseattr, str); return; } else { char* buf = buffer; int maxIndent = 0; bool lastItem = (this->indent < 0); int indent = (this->indent < 0 ? -this->indent : this->indent); for (int i = 0; i < 32; i++) { if (indent & (1U << i)) { maxIndent = i+1; } } for (int i = 0; i < maxIndent - 1; i++) { int written, ret; if (indent & (1 << i)) { ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); } else { ret = snprintf(buf, n, " "); } if (ret < 0 || (size_t)ret >= n) { written = n; } else { written = ret; } buf += written; n -= written; } const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE]; xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer); Process_writeCommand(this, attr, baseattr, str); return; } } case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return; case MINFLT: Process_colorNumber(str, this->minflt, coloring); return; case M_RESIDENT: Process_humanNumber(str, this->m_resident, coloring); return; case M_VIRT: Process_humanNumber(str, this->m_virt, coloring); return; case NICE: { xSnprintf(buffer, n, "%3ld ", this->nice); attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY] : this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY] : attr; break; } case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break; case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break; case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break; case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break; case PRIORITY: { if(this->priority <= -100) xSnprintf(buffer, n, " RT "); else xSnprintf(buffer, n, "%3ld ", this->priority); break; } case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break; case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break; case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break; case STATE: { xSnprintf(buffer, n, "%c ", this->state); switch(this->state) { case 'R': attr = CRT_colors[PROCESS_R_STATE]; break; case 'D': attr = CRT_colors[PROCESS_D_STATE]; break; } break; } case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break; case TIME: Process_printTime(str, this->time); return; case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid); break; case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break; case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break; case USER: { if (Process_getuid != this->st_uid) attr = CRT_colors[PROCESS_SHADOW]; if (this->user) { Process_printLeftAlignedField(str, attr, this->user, 9); return; } xSnprintf(buffer, n, "%-9d ", this->st_uid); break; } default: xSnprintf(buffer, n, "- "); } RichString_appendWide(str, attr, buffer); } void Process_display(const Object* cast, RichString* out) { const Process* this = (const Process*) cast; const ProcessField* fields = this->settings->fields; RichString_prune(out); for (int i = 0; fields[i]; i++) As_Process(this)->writeField(this, out, fields[i]); if (this->settings->shadowOtherUsers && this->st_uid != Process_getuid) { RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); } if (this->tag == true) { RichString_setAttr(out, CRT_colors[PROCESS_TAG]); } if (this->settings->highlightChanges) { if (Process_isTomb(this)) { out->highlightAttr = CRT_colors[PROCESS_TOMB]; } else if (Process_isNew(this)) { out->highlightAttr = CRT_colors[PROCESS_NEW]; } } assert(out->chlen > 0); } void Process_done(Process* this) { assert (this != NULL); free(this->comm); } static const char* Process_getCommandStr(const Process* p) { return p->comm ? p->comm : ""; } const ProcessClass Process_class = { .super = { .extends = Class(Object), .display = Process_display, .delete = Process_delete, .compare = Process_compare }, .writeField = Process_writeField, .getCommandStr = Process_getCommandStr, }; void Process_init(Process* this, const struct Settings_* settings) { this->settings = settings; this->tag = false; this->showChildren = true; this->show = true; this->updated = false; this->basenameOffset = -1; if (Process_getuid == (uid_t)-1) { Process_getuid = getuid(); } } void Process_toggleTag(Process* this) { this->tag = !this->tag; } bool Process_isNew(const Process* this) { assert(this->processList); if (this->processList->scanTs >= this->seenTs) { return this->processList->scanTs - this->seenTs <= 1000 * this->processList->settings->highlightDelaySecs; } return false; } bool Process_isTomb(const Process* this) { return this->tombTs > 0; } bool Process_setPriority(Process* this, int priority) { CRT_dropPrivileges(); int old_prio = getpriority(PRIO_PROCESS, this->pid); int err = setpriority(PRIO_PROCESS, this->pid, priority); CRT_restorePrivileges(); if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) { this->nice = priority; } return (err == 0); } bool Process_changePriorityBy(Process* this, Arg delta) { return Process_setPriority(this, this->nice + delta.i); } bool Process_sendSignal(Process* this, Arg sgn) { CRT_dropPrivileges(); bool ok = (kill(this->pid, sgn.i) == 0); CRT_restorePrivileges(); return ok; } int Process_pidCompare(const void* v1, const void* v2) { const Process* p1 = (const Process*)v1; const Process* p2 = (const Process*)v2; return SPACESHIP_NUMBER(p1->pid, p2->pid); } int Process_compare(const void* v1, const void* v2) { const Process *p1, *p2; const Settings *settings = ((const Process*)v1)->settings; if (Settings_getActiveDirection(settings) == 1) { p1 = (const Process*)v1; p2 = (const Process*)v2; } else { p2 = (const Process*)v1; p1 = (const Process*)v2; } ProcessField key = Settings_getActiveSortKey(settings); int result = Process_compareByKey(p1, p2, key); // Implement tie-breaker (needed to make tree mode more stable) if (!result) result = SPACESHIP_NUMBER(p1->pid, p2->pid); return result; } int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) { int r; switch (key) { case PERCENT_CPU: case PERCENT_NORM_CPU: return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu); case PERCENT_MEM: return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident); case COMM: return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2)); case MAJFLT: return SPACESHIP_NUMBER(p2->majflt, p1->majflt); case MINFLT: return SPACESHIP_NUMBER(p2->minflt, p1->minflt); case M_RESIDENT: return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident); case M_VIRT: return SPACESHIP_NUMBER(p2->m_virt, p1->m_virt); case NICE: return SPACESHIP_NUMBER(p1->nice, p2->nice); case NLWP: return SPACESHIP_NUMBER(p1->nlwp, p2->nlwp); case PGRP: return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp); case PID: return SPACESHIP_NUMBER(p1->pid, p2->pid); case PPID: return SPACESHIP_NUMBER(p1->ppid, p2->ppid); case PRIORITY: return SPACESHIP_NUMBER(p1->priority, p2->priority); case PROCESSOR: return SPACESHIP_NUMBER(p1->processor, p2->processor); case SESSION: return SPACESHIP_NUMBER(p1->session, p2->session); case STARTTIME: r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime); return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid); case STATE: return SPACESHIP_NUMBER(Process_sortState(p1->state), Process_sortState(p2->state)); case ST_UID: return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid); case TIME: return SPACESHIP_NUMBER(p2->time, p1->time); case TGID: return SPACESHIP_NUMBER(p1->tgid, p2->tgid); case TPGID: return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid); case TTY_NR: return SPACESHIP_NUMBER(p1->tty_nr, p2->tty_nr); case USER: return SPACESHIP_NULLSTR(p1->user, p2->user); default: return SPACESHIP_NUMBER(p1->pid, p2->pid); } } htop-3.0.5/Process.h000066400000000000000000000124501377712513700142770ustar00rootroot00000000000000#ifndef HEADER_Process #define HEADER_Process /* htop - Process.h (C) 2004-2015 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include #include "Object.h" #include "ProcessField.h" #include "RichString.h" #define PROCESS_FLAG_IO 0x0001 #define DEFAULT_HIGHLIGHT_SECS 5 typedef enum ProcessField_ { NULL_PROCESSFIELD = 0, PID = 1, COMM = 2, STATE = 3, PPID = 4, PGRP = 5, SESSION = 6, TTY_NR = 7, TPGID = 8, MINFLT = 10, MAJFLT = 12, PRIORITY = 18, NICE = 19, STARTTIME = 21, PROCESSOR = 38, M_VIRT = 39, M_RESIDENT = 40, ST_UID = 46, PERCENT_CPU = 47, PERCENT_MEM = 48, USER = 49, TIME = 50, NLWP = 51, TGID = 52, PERCENT_NORM_CPU = 53, /* Platform specific fields, defined in ${platform}/ProcessField.h */ PLATFORM_PROCESS_FIELDS LAST_PROCESSFIELD } ProcessField; struct Settings_; typedef struct Process_ { Object super; const struct ProcessList_* processList; const struct Settings_* settings; unsigned long long int time; pid_t pid; pid_t ppid; pid_t tgid; char* comm; /* use Process_getCommand() for Command actually displayed */ int commLen; int indent; int basenameOffset; bool updated; char state; bool tag; bool showChildren; bool show; bool wasShown; unsigned int pgrp; unsigned int session; unsigned int tty_nr; int tpgid; uid_t st_uid; unsigned long int flags; int processor; float percent_cpu; float percent_mem; const char* user; long int priority; long int nice; long int nlwp; char starttime_show[8]; time_t starttime_ctime; long m_virt; long m_resident; int exit_signal; time_t seenTs; time_t tombTs; unsigned long int minflt; unsigned long int majflt; unsigned int tree_left; unsigned int tree_right; unsigned int tree_depth; unsigned int tree_index; } Process; typedef struct ProcessFieldData_ { const char* name; const char* title; const char* description; uint32_t flags; bool pidColumn; } ProcessFieldData; // Implemented in platform-specific code: void Process_writeField(const Process* this, RichString* str, ProcessField field); int Process_compare(const void* v1, const void* v2); void Process_delete(Object* cast); bool Process_isThread(const Process* this); extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; #define PROCESS_MAX_PID_DIGITS 19 extern int Process_pidDigits; typedef Process*(*Process_New)(const struct Settings_*); typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField); typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField); typedef const char* (*Process_GetCommandStr)(const Process*); typedef struct ProcessClass_ { const ObjectClass super; const Process_WriteField writeField; const Process_CompareByKey compareByKey; const Process_GetCommandStr getCommandStr; } ProcessClass; #define As_Process(this_) ((const ProcessClass*)((this_)->super.klass)) #define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm) #define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_)) static inline pid_t Process_getParentPid(const Process* this) { return this->tgid == this->pid ? this->ppid : this->tgid; } static inline bool Process_isChildOf(const Process* this, pid_t pid) { return pid == Process_getParentPid(this); } #define Process_sortState(state) ((state) == 'I' ? 0x100 : (state)) #define ONE_K 1024UL #define ONE_M (ONE_K * ONE_K) #define ONE_G (ONE_M * ONE_K) #define ONE_T (1ULL * ONE_G * ONE_K) #define ONE_DECIMAL_K 1000UL #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) #define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K) void Process_setupColumnWidths(void); void Process_humanNumber(RichString* str, unsigned long long number, bool coloring); void Process_colorNumber(RichString* str, unsigned long long number, bool coloring); void Process_printTime(RichString* str, unsigned long long totalHundredths); void Process_fillStarttimeBuffer(Process* this); void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring); void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width); void Process_display(const Object* cast, RichString* out); void Process_done(Process* this); extern const ProcessClass Process_class; void Process_init(Process* this, const struct Settings_* settings); void Process_toggleTag(Process* this); bool Process_isNew(const Process* this); bool Process_isTomb(const Process* this); bool Process_setPriority(Process* this, int priority); bool Process_changePriorityBy(Process* this, Arg delta); bool Process_sendSignal(Process* this, Arg sgn); int Process_pidCompare(const void* v1, const void* v2); int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key); #endif htop-3.0.5/ProcessList.c000066400000000000000000000505421377712513700151320ustar00rootroot00000000000000/* htop - ProcessList.c (C) 2004,2005 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ProcessList.h" #include #include #include #include "Compat.h" #include "CRT.h" #include "Hashtable.h" #include "Macros.h" #include "Vector.h" #include "XUtils.h" ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { this->processes = Vector_new(klass, true, DEFAULT_SIZE); this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer this->processTable = Hashtable_new(200, false); this->displayTreeSet = Hashtable_new(200, false); this->draftingTreeSet = Hashtable_new(200, false); this->usersTable = usersTable; this->pidMatchList = pidMatchList; this->userId = userId; // set later by platform-specific code this->cpuCount = 0; this->scanTs = 0; #ifdef HAVE_LIBHWLOC this->topologyOk = false; if (hwloc_topology_init(&this->topology) == 0) { this->topologyOk = #if HWLOC_API_VERSION < 0x00020000 /* try to ignore the top-level machine object type */ 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) && /* ignore caches, which don't add structure */ 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) && 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) && 0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) && #else 0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) && #endif 0 == hwloc_topology_load(this->topology); } #endif this->following = -1; return this; } void ProcessList_done(ProcessList* this) { #ifdef HAVE_LIBHWLOC if (this->topologyOk) { hwloc_topology_destroy(this->topology); } #endif Hashtable_delete(this->draftingTreeSet); Hashtable_delete(this->displayTreeSet); Hashtable_delete(this->processTable); Vector_delete(this->processes2); Vector_delete(this->processes); } void ProcessList_setPanel(ProcessList* this, Panel* panel) { this->panel = panel; } static const char* alignedProcessFieldTitle(ProcessField field) { const char* title = Process_fields[field].title; if (!title) return "- "; if (!Process_fields[field].pidColumn) return title; static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1]; xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title); return titleBuffer; } void ProcessList_printHeader(ProcessList* this, RichString* header) { RichString_prune(header); const Settings* settings = this->settings; const ProcessField* fields = settings->fields; ProcessField key = Settings_getActiveSortKey(settings); for (int i = 0; fields[i]; i++) { int color; if (settings->treeView && settings->treeViewAlwaysByPID) { color = CRT_colors[PANEL_HEADER_FOCUS]; } else if (key == fields[i]) { color = CRT_colors[PANEL_SELECTION_FOCUS]; } else { color = CRT_colors[PANEL_HEADER_FOCUS]; } RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i])); if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') { header->chlen--; // rewind to override space RichString_appendnWide(header, CRT_colors[PANEL_SELECTION_FOCUS], CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_DESC : TREE_STR_ASC], 1); } if (COMM == fields[i] && settings->showMergedCommand) { RichString_appendAscii(header, color, "(merged)"); } } } void ProcessList_add(ProcessList* this, Process* p) { assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1); assert(Hashtable_get(this->processTable, p->pid) == NULL); p->processList = this; // highlighting processes found in first scan by first scan marked "far in the past" p->seenTs = this->scanTs; Vector_add(this->processes, p); Hashtable_put(this->processTable, p->pid, p); assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); assert(Hashtable_get(this->processTable, p->pid) != NULL); assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); } void ProcessList_remove(ProcessList* this, Process* p) { assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1); assert(Hashtable_get(this->processTable, p->pid) != NULL); Process* pp = Hashtable_remove(this->processTable, p->pid); assert(pp == p); (void)pp; pid_t pid = p->pid; int idx = Vector_indexOf(this->processes, p, Process_pidCompare); assert(idx != -1); if (idx >= 0) { Vector_remove(this->processes, idx); } if (this->following != -1 && this->following == pid) { this->following = -1; Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS); } assert(Hashtable_get(this->processTable, pid) == NULL); assert(Hashtable_count(this->processTable) == Vector_count(this->processes)); } Process* ProcessList_get(ProcessList* this, int idx) { return (Process*)Vector_get(this->processes, idx); } int ProcessList_size(ProcessList* this) { return Vector_size(this->processes); } // ProcessList_updateTreeSetLayer sorts this->displayTreeSet, // relying only on itself. // // Algorithm // // The algorithm is based on `depth-first search`, // even though `breadth-first search` approach may be more efficient on first glance, // after comparison it may be not, as it's not safe to go deeper without first updating the tree structure. // If it would be safe that approach would likely bring an advantage in performance. // // Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth. // First it sorts a list. Then it runs the function recursively for each element of the sorted list. // After that it updates the settings of processes. // // It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'. // // It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree // and at the same time make changes in it. // static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftBound, unsigned int rightBound, unsigned int deep, unsigned int left, unsigned int right, unsigned int* index, unsigned int* treeIndex, int indent) { // It's guaranteed that layer_size is enough space // but most likely it needs less. Specifically on first iteration. int layerSize = (right - left) / 2; // check if we reach `children` of `leaves` if (layerSize == 0) return; Vector* layer = Vector_new(this->processes->type, false, layerSize); // Find all processes on the same layer (process with the same `deep` value // and included in a range from `leftBound` to `rightBound`). // // This loop also keeps track of left_bound and right_bound of these processes // in order not to lose this information once the list is sorted. // // The variables left_bound and right_bound are different from what the values lhs and rhs represent. // While left_bound and right_bound define a range of processes to look at, the values given by lhs and rhs are indices into an array // // In the below example note how filtering a range of indices i is different from filtering for processes in the bounds left_bound < x < right_bound … // // The nested tree set is sorted by left value, which is guaranteed upon entry/exit of this function. // // i | l | r // 1 | 1 | 9 // 2 | 2 | 8 // 3 | 4 | 5 // 4 | 6 | 7 for (unsigned int i = leftBound; i < rightBound; i++) { Process* proc = (Process*)Hashtable_get(this->displayTreeSet, i); assert(proc); if (proc && proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) { if (Vector_size(layer) > 0) { Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1); // Make a 'right_bound' of previous_process in a layer the current process's index. // // Use 'tree_depth' as a temporal variable. // It's safe to do as later 'tree_depth' will be renovated. previous_process->tree_depth = proc->tree_index; } Vector_add(layer, proc); } } // The loop above changes just up to process-1. // So the last process of the layer isn't updated by the above code. // // Thus, if present, set the `rightBound` to the last process on the layer if (Vector_size(layer) > 0) { Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1); previous_process->tree_depth = rightBound; } Vector_quickSort(layer); int size = Vector_size(layer); for (int i = 0; i < size; i++) { Process* proc = (Process*)Vector_get(layer, i); unsigned int idx = (*index)++; int newLeft = (*treeIndex)++; int level = deep == 0 ? 0 : (int)deep - 1; int currentIndent = indent == -1 ? 0 : indent | (1 << level); int nextIndent = indent == -1 ? 0 : ((i < size - 1) ? currentIndent : indent); unsigned int newLeftBound = proc->tree_index; unsigned int newRightBound = proc->tree_depth; ProcessList_updateTreeSetLayer(this, newLeftBound, newRightBound, deep + 1, proc->tree_left, proc->tree_right, index, treeIndex, nextIndent); int newRight = (*treeIndex)++; proc->tree_left = newLeft; proc->tree_right = newRight; proc->tree_index = idx; proc->tree_depth = deep; if (indent == -1) { proc->indent = 0; } else if (i == size - 1) { proc->indent = -currentIndent; } else { proc->indent = currentIndent; } Hashtable_put(this->draftingTreeSet, proc->tree_index, proc); // It's not strictly necessary to do this, but doing so anyways // allows for checking the correctness of the inner workings. Hashtable_remove(this->displayTreeSet, newLeftBound); } Vector_delete(layer); } static void ProcessList_updateTreeSet(ProcessList* this) { unsigned int index = 0; unsigned int tree_index = 1; const int vsize = Vector_size(this->processes); assert(Hashtable_count(this->draftingTreeSet) == 0); assert((int)Hashtable_count(this->displayTreeSet) == vsize); ProcessList_updateTreeSetLayer(this, 0, vsize, 0, 0, vsize * 2 + 1, &index, &tree_index, -1); Hashtable* tmp = this->draftingTreeSet; this->draftingTreeSet = this->displayTreeSet; this->displayTreeSet = tmp; assert(Hashtable_count(this->draftingTreeSet) == 0); assert((int)Hashtable_count(this->displayTreeSet) == vsize); } static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) { Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE); for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { Process* process = (Process*)Vector_get(this->processes, i); if (process->show && Process_isChildOf(process, pid)) { process = (Process*)Vector_take(this->processes, i); Vector_add(children, process); } } int size = Vector_size(children); for (int i = 0; i < size; i++) { int index = (*node_index)++; Process* process = (Process*)Vector_get(children, i); int lft = (*node_counter)++; if (!show) { process->show = false; } int s = Vector_size(this->processes2); if (direction == 1) { Vector_add(this->processes2, process); } else { Vector_insert(this->processes2, 0, process); } assert(Vector_size(this->processes2) == s + 1); (void)s; int nextIndent = indent | (1 << level); ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false, node_counter, node_index); if (i == size - 1) { process->indent = -nextIndent; } else { process->indent = nextIndent; } int rht = (*node_counter)++; process->tree_left = lft; process->tree_right = rht; process->tree_depth = level + 1; process->tree_index = index; Hashtable_put(this->displayTreeSet, index, process); } Vector_delete(children); } static int ProcessList_treeProcessCompare(const void* v1, const void* v2) { const Process *p1 = (const Process*)v1; const Process *p2 = (const Process*)v2; return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left); } static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) { const Process *p1 = (const Process*)v1; const Process *p2 = (const Process*)v2; return SPACESHIP_NUMBER(p1->pid, p2->pid); } // Builds a sorted tree from scratch, without relying on previously gathered information static void ProcessList_buildTree(ProcessList* this) { int node_counter = 1; int node_index = 0; int direction = Settings_getActiveDirection(this->settings); // Sort by PID Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID); int vsize = Vector_size(this->processes); // Find all processes whose parent is not visible int size; while ((size = Vector_size(this->processes))) { int i; for (i = 0; i < size; i++) { Process* process = (Process*)Vector_get(this->processes, i); // Immediately consume processes hidden from view if (!process->show) { process = (Process*)Vector_take(this->processes, i); process->indent = 0; process->tree_depth = 0; process->tree_left = node_counter++; process->tree_index = node_index++; Vector_add(this->processes2, process); ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index); process->tree_right = node_counter++; Hashtable_put(this->displayTreeSet, process->tree_index, process); break; } pid_t ppid = Process_getParentPid(process); // Bisect the process vector to find parent int l = 0; int r = size; // If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0) // on Mac OS X 10.11.6) cancel bisecting and regard this process as // root. if (process->pid == ppid) r = 0; // On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2) // use a ppid of 0. As that PID can't exist, we can skip searching for it. if (!ppid) r = 0; while (l < r) { int c = (l + r) / 2; pid_t pid = ((Process*)Vector_get(this->processes, c))->pid; if (ppid == pid) { break; } else if (ppid < pid) { r = c; } else { l = c + 1; } } // If parent not found, then construct the tree with this node as root if (l >= r) { process = (Process*)Vector_take(this->processes, i); process->indent = 0; process->tree_depth = 0; process->tree_left = node_counter++; process->tree_index = node_index++; Vector_add(this->processes2, process); Hashtable_put(this->displayTreeSet, process->tree_index, process); ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, process->showChildren, &node_counter, &node_index); process->tree_right = node_counter++; break; } } // There should be no loop in the process tree assert(i < size); } // Swap listings around Vector* t = this->processes; this->processes = this->processes2; this->processes2 = t; // Check consistency of the built structures assert(Vector_size(this->processes) == vsize); (void)vsize; assert(Vector_size(this->processes2) == 0); } void ProcessList_sort(ProcessList* this) { if (this->settings->treeView) { ProcessList_updateTreeSet(this); Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare); } else { Vector_insertionSort(this->processes); } } ProcessField ProcessList_keyAt(const ProcessList* this, int at) { int x = 0; const ProcessField* fields = this->settings->fields; ProcessField field; for (int i = 0; (field = fields[i]); i++) { int len = strlen(alignedProcessFieldTitle(field)); if (at >= x && at <= x + len) { return field; } x += len; } return COMM; } void ProcessList_expandTree(ProcessList* this) { int size = Vector_size(this->processes); for (int i = 0; i < size; i++) { Process* process = (Process*) Vector_get(this->processes, i); process->showChildren = true; } } void ProcessList_rebuildPanel(ProcessList* this) { const char* incFilter = this->incFilter; int currPos = Panel_getSelectedIndex(this->panel); int currScrollV = this->panel->scrollV; Panel_prune(this->panel); int size = ProcessList_size(this); int idx = 0; for (int i = 0; i < size; i++) { Process* p = ProcessList_get(this, i); if ( (!p->show) || (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter))) || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) ) continue; Panel_set(this->panel, idx, (Object*)p); if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == this->following)) { Panel_setSelected(this->panel, idx); this->panel->scrollV = currScrollV; } idx++; } } Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) { Process* proc = (Process*) Hashtable_get(this->processTable, pid); *preExisting = proc; if (proc) { assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1); assert(proc->pid == pid); } else { proc = constructor(this->settings); assert(proc->comm == NULL); proc->pid = pid; } return proc; } void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { struct timespec now; // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { ProcessList_goThroughEntries(this, true); return; } // mark all process as "dirty" for (int i = 0; i < Vector_size(this->processes); i++) { Process* p = (Process*) Vector_get(this->processes, i); p->updated = false; p->wasShown = p->show; p->show = true; } this->totalTasks = 0; this->userlandThreads = 0; this->kernelThreads = 0; this->runningTasks = 0; // set scanTs static bool firstScanDone = false; if (!firstScanDone) { this->scanTs = 0; firstScanDone = true; } else if (Compat_clock_monotonic_gettime(&now) == 0) { // save time in millisecond, so with a delay in deciseconds // there are no irregularities this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000; } ProcessList_goThroughEntries(this, false); for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { Process* p = (Process*) Vector_get(this->processes, i); if (p->tombTs > 0) { // remove tombed process if (this->scanTs >= p->tombTs) { ProcessList_remove(this, p); } } else if (p->updated == false) { // process no longer exists if (this->settings->highlightChanges && p->wasShown) { // mark tombed p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs; } else { // immediately remove ProcessList_remove(this, p); } } else { p->updated = false; } } if (this->settings->treeView) { // Clear out the hashtable to avoid any left-over processes from previous build // // The sorting algorithm relies on the fact that // len(this->displayTreeSet) == len(this->processes) Hashtable_clear(this->displayTreeSet); ProcessList_buildTree(this); } } htop-3.0.5/ProcessList.h000066400000000000000000000047741377712513700151450ustar00rootroot00000000000000#ifndef HEADER_ProcessList #define HEADER_ProcessList /* htop - ProcessList.h (C) 2004,2005 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include "Hashtable.h" #include "Object.h" #include "Panel.h" #include "Process.h" #include "RichString.h" #include "Settings.h" #include "UsersTable.h" #include "Vector.h" #ifdef HAVE_LIBHWLOC #include #endif #ifndef MAX_NAME #define MAX_NAME 128 #endif #ifndef MAX_READ #define MAX_READ 2048 #endif typedef struct ProcessList_ { const Settings* settings; Vector* processes; Vector* processes2; Hashtable* processTable; UsersTable* usersTable; Hashtable* displayTreeSet; Hashtable* draftingTreeSet; Panel* panel; int following; uid_t userId; const char* incFilter; Hashtable* pidMatchList; #ifdef HAVE_LIBHWLOC hwloc_topology_t topology; bool topologyOk; #endif int totalTasks; int runningTasks; int userlandThreads; int kernelThreads; unsigned long long int totalMem; unsigned long long int usedMem; unsigned long long int buffersMem; unsigned long long int cachedMem; unsigned long long int totalSwap; unsigned long long int usedSwap; unsigned long long int freeSwap; int cpuCount; time_t scanTs; } ProcessList; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_done(ProcessList* this); void ProcessList_setPanel(ProcessList* this, Panel* panel); void ProcessList_printHeader(ProcessList* this, RichString* header); void ProcessList_add(ProcessList* this, Process* p); void ProcessList_remove(ProcessList* this, Process* p); Process* ProcessList_get(ProcessList* this, int idx); int ProcessList_size(ProcessList* this); void ProcessList_sort(ProcessList* this); ProcessField ProcessList_keyAt(const ProcessList* this, int at); void ProcessList_expandTree(ProcessList* this); void ProcessList_rebuildPanel(ProcessList* this); Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate); #endif htop-3.0.5/ProcessLocksScreen.c000066400000000000000000000066231377712513700164330ustar00rootroot00000000000000/* htop - ProcessLocksScreen.c (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "ProcessLocksScreen.h" #include #include #include #include "Panel.h" #include "Platform.h" #include "ProvideCurses.h" #include "Vector.h" #include "XUtils.h" ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) { ProcessLocksScreen* this = xMalloc(sizeof(ProcessLocksScreen)); Object_setClass(this, Class(ProcessLocksScreen)); if (Process_isThread(process)) this->pid = process->tgid; else this->pid = process->pid; return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME"); } void ProcessLocksScreen_delete(Object* this) { free(InfoScreen_done((InfoScreen*)this)); } static void ProcessLocksScreen_draw(InfoScreen* this) { InfoScreen_drawTitled(this, "Snapshot of file locks of process %d - %s", ((ProcessLocksScreen*)this)->pid, Process_getCommand(this->process)); } static inline void FileLocks_Data_clear(FileLocks_Data* data) { free(data->locktype); free(data->exclusive); free(data->readwrite); free(data->filename); } static void ProcessLocksScreen_scan(InfoScreen* this) { Panel* panel = this->display; int idx = Panel_getSelectedIndex(panel); Panel_prune(panel); FileLocks_ProcessData* pdata = Platform_getProcessLocks(((ProcessLocksScreen*)this)->pid); if (!pdata) { InfoScreen_addLine(this, "This feature is not supported on your platform."); } else if (pdata->error) { InfoScreen_addLine(this, "Could not determine file locks."); } else { FileLocks_LockData* ldata = pdata->locks; if (!ldata) { InfoScreen_addLine(this, "No locks have been found for the selected process."); } while (ldata) { FileLocks_Data* data = &ldata->data; char entry[512]; if (ULLONG_MAX == data->end) { xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20s %s", data->id, data->locktype, data->exclusive, data->readwrite, data->dev[0], data->dev[1], data->inode, data->start, "", data->filename ? data->filename : "" ); } else { xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20"PRIu64" %s", data->id, data->locktype, data->exclusive, data->readwrite, data->dev[0], data->dev[1], data->inode, data->start, data->end, data->filename ? data->filename : "" ); } InfoScreen_addLine(this, entry); FileLocks_Data_clear(&ldata->data); FileLocks_LockData* old = ldata; ldata = ldata->next; free(old); } } free(pdata); Vector_insertionSort(this->lines); Vector_insertionSort(panel->items); Panel_setSelected(panel, idx); } const InfoScreenClass ProcessLocksScreen_class = { .super = { .extends = Class(Object), .delete = ProcessLocksScreen_delete }, .scan = ProcessLocksScreen_scan, .draw = ProcessLocksScreen_draw }; htop-3.0.5/ProcessLocksScreen.h000066400000000000000000000021011377712513700164230ustar00rootroot00000000000000#ifndef HEADER_ProcessLocksScreen #define HEADER_ProcessLocksScreen /* htop - ProcessLocksScreen.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include #include "InfoScreen.h" #include "Object.h" #include "Process.h" typedef struct ProcessLocksScreen_ { InfoScreen super; pid_t pid; } ProcessLocksScreen; typedef struct FileLocks_Data_ { char* locktype; char* exclusive; char* readwrite; char* filename; int id; unsigned int dev[2]; uint64_t inode; uint64_t start; uint64_t end; } FileLocks_Data; typedef struct FileLocks_LockData_ { FileLocks_Data data; struct FileLocks_LockData_* next; } FileLocks_LockData; typedef struct FileLocks_ProcessData_ { bool error; struct FileLocks_LockData_* locks; } FileLocks_ProcessData; extern const InfoScreenClass ProcessLocksScreen_class; ProcessLocksScreen* ProcessLocksScreen_new(const Process* process); void ProcessLocksScreen_delete(Object* this); #endif htop-3.0.5/ProvideCurses.h000066400000000000000000000012701377712513700154540ustar00rootroot00000000000000#ifndef HEADER_ProvideCurses #define HEADER_ProvideCurses /* htop - RichString.h (C) 2004,2011 Hisham H. Muhammad Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: begin_exports #ifdef HAVE_NCURSESW_CURSES_H #include #elif defined(HAVE_NCURSES_NCURSES_H) #include #elif defined(HAVE_NCURSES_CURSES_H) #include #elif defined(HAVE_NCURSES_H) #include #elif defined(HAVE_CURSES_H) #include #endif #ifdef HAVE_LIBNCURSESW #include #include #endif // IWYU pragma: end_exports #endif // HEADER_ProvideCurses htop-3.0.5/README000066400000000000000000000070121377712513700133660ustar00rootroot00000000000000# [![htop](htop.png)](https://htop.dev) [![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions) [![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665) [![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop) [![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://webchat.freenode.net/#htop) [![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest) [![Download](https://api.bintray.com/packages/htop/source/htop/images/download.svg)](https://bintray.com/htop/source/htop/_latestVersion) ![Screenshot of htop](docs/images/screenshot.png?raw=true) ## Introduction `htop` is a cross-platform interactive process viewer. `htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption. The information displayed is configurable through a graphical setup and can be sorted and filtered interactively. Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs. Running `htop` requires `ncurses` libraries (typically named libncursesw*). For more information and details on how to contribute to `htop` visit [htop.dev](https://htop.dev). ## Build instructions This program is distributed as a standard GNU autotools-based package. Compiling `htop` requires the header files for `ncurses` (libncursesw*-dev). Install these and other required packages for C development from your package manager. Then, when compiling from a [release tarball](https://bintray.com/htop/source/htop), run: ~~~ shell ./configure && make ~~~ Alternatively, for compiling sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)), install the header files for `ncurses` (libncursesw*-dev) and other required development packages from your distribution's package manager. Then run: ~~~ shell ./autogen.sh && ./configure && make ~~~ By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`. See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands. ## Support If you have trouble running `htop` please consult your Operating System / Linux distribution documentation for getting support and filing bugs. ## Bugs, development feedback We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop. You can also join our IRC channel #htop on freenode and talk to the developers there. If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues). If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix. ## History `htop` was invented, developed and maintained by Hisham Muhammad from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history. In 2020 a [team](https://github.com/orgs/htop-dev/people) took over the development amicably and continues to maintain `htop` collaboratively. ## License GNU General Public License, version 2 (GPL-2.0) htop-3.0.5/README.md000077700000000000000000000000001377712513700146402READMEustar00rootroot00000000000000htop-3.0.5/RichString.c000066400000000000000000000120071377712513700147260ustar00rootroot00000000000000/* htop - RichString.c (C) 2004,2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "RichString.h" #include #include #include #include "Macros.h" #include "XUtils.h" #define charBytes(n) (sizeof(CharType) * (n)) static void RichString_extendLen(RichString* this, int len) { if (this->chlen <= RICHSTRING_MAXLEN) { if (len > RICHSTRING_MAXLEN) { this->chptr = xMalloc(charBytes(len + 1)); memcpy(this->chptr, this->chstr, charBytes(this->chlen)); } } else { if (len <= RICHSTRING_MAXLEN) { memcpy(this->chstr, this->chptr, charBytes(len)); free(this->chptr); this->chptr = this->chstr; } else { this->chptr = xRealloc(this->chptr, charBytes(len + 1)); } } RichString_setChar(this, len, 0); this->chlen = len; } static void RichString_setLen(RichString* this, int len) { if (len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this, len, 0); this->chlen = len; } else { RichString_extendLen(this, len); } } #ifdef HAVE_LIBNCURSESW static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) { wchar_t data[len + 1]; len = mbstowcs(data, data_c, len); if (len <= 0) return 0; int newLen = from + len; RichString_setLen(this, newLen); for (int i = from, j = 0; i < newLen; i++, j++) { this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } }; } return wcswidth(data, len); } static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) { int newLen = from + len; RichString_setLen(this, newLen); for (int i = from, j = 0; i < newLen; i++, j++) { this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } }; } return len; } inline void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) { int end = CLAMP(start + charcount, 0, this->chlen); for (int i = start; i < end; i++) { this->chptr[i].attr = attrs; } } int RichString_findChar(RichString* this, char c, int start) { wchar_t wc = btowc(c); cchar_t* ch = this->chptr + start; for (int i = start; i < this->chlen; i++) { if (ch->chars[0] == wc) return i; ch++; } return -1; } #else /* HAVE_LIBNCURSESW */ static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) { int newLen = from + len; RichString_setLen(this, newLen); for (int i = from, j = 0; i < newLen; i++, j++) { this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs; } this->chptr[newLen] = 0; return len; } static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) { return RichString_writeFromWide(this, attrs, data_c, from, len); } void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) { int end = CLAMP(start + charcount, 0, this->chlen); for (int i = start; i < end; i++) { this->chptr[i] = (this->chptr[i] & 0xff) | attrs; } } int RichString_findChar(RichString* this, char c, int start) { chtype* ch = this->chptr + start; for (int i = start; i < this->chlen; i++) { if ((*ch & 0xff) == (chtype) c) return i; ch++; } return -1; } #endif /* HAVE_LIBNCURSESW */ void RichString_prune(RichString* this) { if (this->chlen > RICHSTRING_MAXLEN) free(this->chptr); memset(this, 0, sizeof(RichString)); this->chptr = this->chstr; } void RichString_appendChr(RichString* this, char c, int count) { int from = this->chlen; int newLen = from + count; RichString_setLen(this, newLen); for (int i = from; i < newLen; i++) { RichString_setChar(this, i, c); } } void RichString_setAttr(RichString* this, int attrs) { RichString_setAttrn(this, attrs, 0, this->chlen); } int RichString_appendWide(RichString* this, int attrs, const char* data) { return RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data)); } int RichString_appendnWide(RichString* this, int attrs, const char* data, int len) { return RichString_writeFromWide(this, attrs, data, this->chlen, len); } int RichString_writeWide(RichString* this, int attrs, const char* data) { return RichString_writeFromWide(this, attrs, data, 0, strlen(data)); } int RichString_appendAscii(RichString* this, int attrs, const char* data) { return RichString_writeFromAscii(this, attrs, data, this->chlen, strlen(data)); } int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len) { return RichString_writeFromAscii(this, attrs, data, this->chlen, len); } int RichString_writeAscii(RichString* this, int attrs, const char* data) { return RichString_writeFromAscii(this, attrs, data, 0, strlen(data)); } htop-3.0.5/RichString.h000066400000000000000000000044261377712513700147410ustar00rootroot00000000000000#ifndef HEADER_RichString #define HEADER_RichString /* htop - RichString.h (C) 2004,2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" #include "ProvideCurses.h" #define RichString_size(this) ((this)->chlen) #define RichString_sizeVal(this) ((this).chlen) #define RichString_begin(this) RichString (this); RichString_beginAllocated(this) #define RichString_beginAllocated(this) do { memset(&(this), 0, sizeof(RichString)); (this).chptr = (this).chstr; } while(0) #define RichString_end(this) RichString_prune(&(this)) #ifdef HAVE_LIBNCURSESW #define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr) #define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + (off), n) #define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255) #define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while (0) #define CharType cchar_t #else #define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr) #define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + (off), n) #define RichString_getCharVal(this, i) ((this).chptr[i]) #define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = ch; } while (0) #define CharType chtype #endif #define RICHSTRING_MAXLEN 350 typedef struct RichString_ { int chlen; CharType* chptr; CharType chstr[RICHSTRING_MAXLEN + 1]; int highlightAttr; } RichString; void RichString_setAttrn(RichString* this, int attrs, int start, int charcount); int RichString_findChar(RichString* this, char c, int start); void RichString_prune(RichString* this); void RichString_setAttr(RichString* this, int attrs); void RichString_appendChr(RichString* this, char c, int count); int RichString_appendWide(RichString* this, int attrs, const char* data); int RichString_appendnWide(RichString* this, int attrs, const char* data, int len); int RichString_writeWide(RichString* this, int attrs, const char* data); int RichString_appendAscii(RichString* this, int attrs, const char* data); int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len); int RichString_writeAscii(RichString* this, int attrs, const char* data); #endif htop-3.0.5/ScreenManager.c000066400000000000000000000214151377712513700153670ustar00rootroot00000000000000/* htop - ScreenManager.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ScreenManager.h" #include #include #include #include #include "CRT.h" #include "FunctionBar.h" #include "Object.h" #include "ProcessList.h" #include "ProvideCurses.h" #include "XUtils.h" ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner) { ScreenManager* this; this = xMalloc(sizeof(ScreenManager)); this->x1 = 0; this->y1 = header->height; this->x2 = 0; this->y2 = -1; this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE); this->panelCount = 0; this->header = header; this->settings = settings; this->state = state; this->owner = owner; this->allowFocusChange = true; return this; } void ScreenManager_delete(ScreenManager* this) { Vector_delete(this->panels); free(this); } inline int ScreenManager_size(ScreenManager* this) { return this->panelCount; } void ScreenManager_add(ScreenManager* this, Panel* item, int size) { int lastX = 0; if (this->panelCount > 0) { Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1); lastX = last->x + last->w + 1; } int height = LINES - this->y1 + this->y2; if (size > 0) { Panel_resize(item, size, height); } else { Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height); } Panel_move(item, lastX, this->y1); Vector_add(this->panels, item); item->needsRedraw = true; this->panelCount++; } Panel* ScreenManager_remove(ScreenManager* this, int idx) { assert(this->panelCount > idx); Panel* panel = (Panel*) Vector_remove(this->panels, idx); this->panelCount--; return panel; } void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) { this->x1 = x1; this->y1 = y1; this->x2 = x2; this->y2 = y2; int panels = this->panelCount; int lastX = 0; for (int i = 0; i < panels - 1; i++) { Panel* panel = (Panel*) Vector_get(this->panels, i); Panel_resize(panel, panel->w, LINES - y1 + y2); Panel_move(panel, lastX, y1); lastX = panel->x + panel->w + 1; } Panel* panel = (Panel*) Vector_get(this->panels, panels - 1); Panel_resize(panel, COLS - x1 + x2 - lastX, LINES - y1 + y2); Panel_move(panel, lastX, y1); } static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) { ProcessList* pl = this->header->pl; struct timeval tv; gettimeofday(&tv, NULL); double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000); *timedOut = (newTime - *oldTime > this->settings->delay); *rescan |= *timedOut; if (newTime < *oldTime) { *rescan = true; // clock was adjusted? } if (*rescan) { *oldTime = newTime; ProcessList_scan(pl, this->state->pauseProcessUpdate); if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) { ProcessList_sort(pl); *sortTimeout = 1; } *redraw = true; } if (*redraw) { ProcessList_rebuildPanel(pl); Header_draw(this->header); } *rescan = false; } static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) { const int nPanels = this->panelCount; for (int i = 0; i < nPanels; i++) { Panel* panel = (Panel*) Vector_get(this->panels, i); Panel_draw(panel, force_redraw, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection), State_hideFunctionBar(this->state)); mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0)); } } void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { bool quit = false; int focus = 0; Panel* panelFocus = (Panel*) Vector_get(this->panels, focus); double oldTime = 0.0; int ch = ERR; int closeTimeout = 0; bool timedOut = true; bool redraw = true; bool force_redraw = true; bool rescan = false; int sortTimeout = 0; int resetSortTimeout = 5; while (!quit) { if (this->header) { checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut); } if (redraw || force_redraw) { ScreenManager_drawPanels(this, focus, force_redraw); force_redraw = false; } int prevCh = ch; set_escdelay(25); ch = getch(); HandlerResult result = IGNORED; if (ch == KEY_MOUSE && this->settings->enableMouse) { ch = ERR; MEVENT mevent; int ok = getmouse(&mevent); if (ok == OK) { if (mevent.bstate & BUTTON1_RELEASED) { if (mevent.y == LINES - 1) { ch = FunctionBar_synthesizeEvent(panelFocus->currentBar, mevent.x); } else { for (int i = 0; i < this->panelCount; i++) { Panel* panel = (Panel*) Vector_get(this->panels, i); if (mevent.x >= panel->x && mevent.x <= panel->x + panel->w) { if (mevent.y == panel->y) { ch = EVENT_HEADER_CLICK(mevent.x - panel->x); break; } else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) { ch = KEY_MOUSE; if (panel == panelFocus || this->allowFocusChange) { focus = i; panelFocus = panel; Object* oldSelection = Panel_getSelected(panel); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); if (Panel_getSelected(panel) == oldSelection) { ch = KEY_RECLICK; } } break; } } } } #if NCURSES_MOUSE_VERSION > 1 } else if (mevent.bstate & BUTTON4_PRESSED) { ch = KEY_WHEELUP; } else if (mevent.bstate & BUTTON5_PRESSED) { ch = KEY_WHEELDOWN; #endif } } } if (ch == ERR) { sortTimeout--; if (prevCh == ch && !timedOut) { closeTimeout++; if (closeTimeout == 100) { break; } } else { closeTimeout = 0; } redraw = false; continue; } switch (ch) { case KEY_ALT('H'): ch = KEY_LEFT; break; case KEY_ALT('J'): ch = KEY_DOWN; break; case KEY_ALT('K'): ch = KEY_UP; break; case KEY_ALT('L'): ch = KEY_RIGHT; break; } redraw = true; if (Panel_eventHandlerFn(panelFocus)) { result = Panel_eventHandler(panelFocus, ch); } if (result & SYNTH_KEY) { ch = result >> 16; } if (result & REFRESH) { sortTimeout = 0; } if (result & REDRAW) { force_redraw = true; } if (result & RESCAN) { rescan = true; sortTimeout = 0; } if (result & HANDLED) { continue; } else if (result & BREAK_LOOP) { quit = true; continue; } switch (ch) { case KEY_RESIZE: { ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2); continue; } case KEY_LEFT: case KEY_CTRL('B'): if (this->panelCount < 2) { goto defaultHandler; } if (!this->allowFocusChange) { break; } tryLeft: if (focus > 0) { focus--; } panelFocus = (Panel*) Vector_get(this->panels, focus); if (Panel_size(panelFocus) == 0 && focus > 0) { goto tryLeft; } break; case KEY_RIGHT: case KEY_CTRL('F'): case 9: if (this->panelCount < 2) { goto defaultHandler; } if (!this->allowFocusChange) { break; } tryRight: if (focus < this->panelCount - 1) { focus++; } panelFocus = (Panel*) Vector_get(this->panels, focus); if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) { goto tryRight; } break; case 27: case 'q': case KEY_F(10): quit = true; continue; default: defaultHandler: sortTimeout = resetSortTimeout; Panel_onKey(panelFocus, ch); break; } } if (lastFocus) { *lastFocus = panelFocus; } if (lastKey) { *lastKey = ch; } } htop-3.0.5/ScreenManager.h000066400000000000000000000020771377712513700153770ustar00rootroot00000000000000#ifndef HEADER_ScreenManager #define HEADER_ScreenManager /* htop - ScreenManager.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Action.h" #include "Header.h" #include "Panel.h" #include "Settings.h" #include "Vector.h" typedef struct ScreenManager_ { int x1; int y1; int x2; int y2; Vector* panels; int panelCount; Header* header; const Settings* settings; const State* state; bool owner; bool allowFocusChange; } ScreenManager; ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner); void ScreenManager_delete(ScreenManager* this); int ScreenManager_size(ScreenManager* this); void ScreenManager_add(ScreenManager* this, Panel* item, int size); Panel* ScreenManager_remove(ScreenManager* this, int idx); void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2); void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey); #endif htop-3.0.5/Settings.c000066400000000000000000000421471377712513700144620ustar00rootroot00000000000000/* htop - Settings.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Settings.h" #include #include #include #include #include "CRT.h" #include "Macros.h" #include "Meter.h" #include "Platform.h" #include "XUtils.h" void Settings_delete(Settings* this) { free(this->filename); free(this->fields); for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) { String_freeArray(this->columns[i].names); free(this->columns[i].modes); } free(this); } static void Settings_readMeters(Settings* this, char* line, int column) { char* trim = String_trim(line); char** ids = String_split(trim, ' ', NULL); free(trim); this->columns[column].names = ids; } static void Settings_readMeterModes(Settings* this, char* line, int column) { char* trim = String_trim(line); char** ids = String_split(trim, ' ', NULL); free(trim); int len = 0; for (int i = 0; ids[i]; i++) { len++; } this->columns[column].len = len; int* modes = xCalloc(len, sizeof(int)); for (int i = 0; i < len; i++) { modes[i] = atoi(ids[i]); } String_freeArray(ids); this->columns[column].modes = modes; } static void Settings_defaultMeters(Settings* this, int initialCpuCount) { int sizes[] = { 3, 3 }; if (initialCpuCount > 4) { sizes[1]++; } for (int i = 0; i < 2; i++) { this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); this->columns[i].modes = xCalloc(sizes[i], sizeof(int)); this->columns[i].len = sizes[i]; } int r = 0; if (initialCpuCount > 8) { this->columns[0].names[0] = xStrdup("LeftCPUs2"); this->columns[0].modes[0] = BAR_METERMODE; this->columns[1].names[r] = xStrdup("RightCPUs2"); this->columns[1].modes[r++] = BAR_METERMODE; } else if (initialCpuCount > 4) { this->columns[0].names[0] = xStrdup("LeftCPUs"); this->columns[0].modes[0] = BAR_METERMODE; this->columns[1].names[r] = xStrdup("RightCPUs"); this->columns[1].modes[r++] = BAR_METERMODE; } else { this->columns[0].names[0] = xStrdup("AllCPUs"); this->columns[0].modes[0] = BAR_METERMODE; } this->columns[0].names[1] = xStrdup("Memory"); this->columns[0].modes[1] = BAR_METERMODE; this->columns[0].names[2] = xStrdup("Swap"); this->columns[0].modes[2] = BAR_METERMODE; this->columns[1].names[r] = xStrdup("Tasks"); this->columns[1].modes[r++] = TEXT_METERMODE; this->columns[1].names[r] = xStrdup("LoadAverage"); this->columns[1].modes[r++] = TEXT_METERMODE; this->columns[1].names[r] = xStrdup("Uptime"); this->columns[1].modes[r++] = TEXT_METERMODE; } static void readFields(ProcessField* fields, uint32_t* flags, const char* line) { char* trim = String_trim(line); char** ids = String_split(trim, ' ', NULL); free(trim); int i, j; *flags = 0; for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) { // This "+1" is for compatibility with the older enum format. int id = atoi(ids[i]) + 1; if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) { fields[j] = id; *flags |= Process_fields[id].flags; j++; } } fields[j] = NULL_PROCESSFIELD; String_freeArray(ids); } static bool Settings_read(Settings* this, const char* fileName, int initialCpuCount) { FILE* fd; CRT_dropPrivileges(); fd = fopen(fileName, "r"); CRT_restorePrivileges(); if (!fd) return false; bool didReadMeters = false; bool didReadFields = false; for (;;) { char* line = String_readLine(fd); if (!line) { break; } size_t nOptions; char** option = String_split(line, '=', &nOptions); free (line); if (nOptions < 2) { String_freeArray(option); continue; } if (String_eq(option[0], "fields")) { readFields(this->fields, &(this->flags), option[1]); didReadFields = true; } else if (String_eq(option[0], "sort_key")) { // This "+1" is for compatibility with the older enum format. this->sortKey = atoi(option[1]) + 1; } else if (String_eq(option[0], "tree_sort_key")) { // This "+1" is for compatibility with the older enum format. this->treeSortKey = atoi(option[1]) + 1; } else if (String_eq(option[0], "sort_direction")) { this->direction = atoi(option[1]); } else if (String_eq(option[0], "tree_sort_direction")) { this->treeDirection = atoi(option[1]); } else if (String_eq(option[0], "tree_view")) { this->treeView = atoi(option[1]); } else if (String_eq(option[0], "tree_view_always_by_pid")) { this->treeViewAlwaysByPID = atoi(option[1]); } else if (String_eq(option[0], "hide_kernel_threads")) { this->hideKernelThreads = atoi(option[1]); } else if (String_eq(option[0], "hide_userland_threads")) { this->hideUserlandThreads = atoi(option[1]); } else if (String_eq(option[0], "shadow_other_users")) { this->shadowOtherUsers = atoi(option[1]); } else if (String_eq(option[0], "show_thread_names")) { this->showThreadNames = atoi(option[1]); } else if (String_eq(option[0], "show_program_path")) { this->showProgramPath = atoi(option[1]); } else if (String_eq(option[0], "highlight_base_name")) { this->highlightBaseName = atoi(option[1]); } else if (String_eq(option[0], "highlight_megabytes")) { this->highlightMegabytes = atoi(option[1]); } else if (String_eq(option[0], "highlight_threads")) { this->highlightThreads = atoi(option[1]); } else if (String_eq(option[0], "highlight_changes")) { this->highlightChanges = atoi(option[1]); } else if (String_eq(option[0], "highlight_changes_delay_secs")) { this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24*60*60); } else if (String_eq(option[0], "find_comm_in_cmdline")) { this->findCommInCmdline = atoi(option[1]); } else if (String_eq(option[0], "strip_exe_from_cmdline")) { this->stripExeFromCmdline = atoi(option[1]); } else if (String_eq(option[0], "show_merged_command")) { this->showMergedCommand = atoi(option[1]); } else if (String_eq(option[0], "header_margin")) { this->headerMargin = atoi(option[1]); } else if (String_eq(option[0], "expand_system_time")) { // Compatibility option. this->detailedCPUTime = atoi(option[1]); } else if (String_eq(option[0], "detailed_cpu_time")) { this->detailedCPUTime = atoi(option[1]); } else if (String_eq(option[0], "cpu_count_from_one")) { this->countCPUsFromOne = atoi(option[1]); } else if (String_eq(option[0], "cpu_count_from_zero")) { // old (inverted) naming also supported for backwards compatibility this->countCPUsFromOne = !atoi(option[1]); } else if (String_eq(option[0], "show_cpu_usage")) { this->showCPUUsage = atoi(option[1]); } else if (String_eq(option[0], "show_cpu_frequency")) { this->showCPUFrequency = atoi(option[1]); #ifdef HAVE_SENSORS_SENSORS_H } else if (String_eq(option[0], "show_cpu_temperature")) { this->showCPUTemperature = atoi(option[1]); } else if (String_eq(option[0], "degree_fahrenheit")) { this->degreeFahrenheit = atoi(option[1]); #endif } else if (String_eq(option[0], "update_process_names")) { this->updateProcessNames = atoi(option[1]); } else if (String_eq(option[0], "account_guest_in_cpu_meter")) { this->accountGuestInCPUMeter = atoi(option[1]); } else if (String_eq(option[0], "delay")) { this->delay = CLAMP(atoi(option[1]), 1, 255); } else if (String_eq(option[0], "color_scheme")) { this->colorScheme = atoi(option[1]); if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) { this->colorScheme = 0; } } else if (String_eq(option[0], "enable_mouse")) { this->enableMouse = atoi(option[1]); } else if (String_eq(option[0], "left_meters")) { Settings_readMeters(this, option[1], 0); didReadMeters = true; } else if (String_eq(option[0], "right_meters")) { Settings_readMeters(this, option[1], 1); didReadMeters = true; } else if (String_eq(option[0], "left_meter_modes")) { Settings_readMeterModes(this, option[1], 0); didReadMeters = true; } else if (String_eq(option[0], "right_meter_modes")) { Settings_readMeterModes(this, option[1], 1); didReadMeters = true; } else if (String_eq(option[0], "hide_function_bar")) { this->hideFunctionBar = atoi(option[1]); #ifdef HAVE_LIBHWLOC } else if (String_eq(option[0], "topology_affinity")) { this->topologyAffinity = !!atoi(option[1]); #endif } String_freeArray(option); } fclose(fd); if (!didReadMeters) { Settings_defaultMeters(this, initialCpuCount); } return didReadFields; } static void writeFields(FILE* fd, ProcessField* fields, const char* name) { fprintf(fd, "%s=", name); const char* sep = ""; for (int i = 0; fields[i]; i++) { // This "-1" is for compatibility with the older enum format. fprintf(fd, "%s%d", sep, (int) fields[i] - 1); sep = " "; } fprintf(fd, "\n"); } static void writeMeters(Settings* this, FILE* fd, int column) { const char* sep = ""; for (int i = 0; i < this->columns[column].len; i++) { fprintf(fd, "%s%s", sep, this->columns[column].names[i]); sep = " "; } fprintf(fd, "\n"); } static void writeMeterModes(Settings* this, FILE* fd, int column) { const char* sep = ""; for (int i = 0; i < this->columns[column].len; i++) { fprintf(fd, "%s%d", sep, this->columns[column].modes[i]); sep = " "; } fprintf(fd, "\n"); } bool Settings_write(Settings* this) { FILE* fd; CRT_dropPrivileges(); fd = fopen(this->filename, "w"); CRT_restorePrivileges(); if (fd == NULL) { return false; } fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n"); fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n"); writeFields(fd, this->fields, "fields"); // This "-1" is for compatibility with the older enum format. fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1); fprintf(fd, "sort_direction=%d\n", (int) this->direction); fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1); fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers); fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames); fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath); fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName); fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes); fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads); fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges); fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs); fprintf(fd, "find_comm_in_cmdline=%d\n", (int) this->findCommInCmdline); fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline); fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand); fprintf(fd, "tree_view=%d\n", (int) this->treeView); fprintf(fd, "tree_view_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne); fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage); fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency); #ifdef HAVE_SENSORS_SENSORS_H fprintf(fd, "show_cpu_temperature=%d\n", (int) this->showCPUTemperature); fprintf(fd, "degree_fahrenheit=%d\n", (int) this->degreeFahrenheit); #endif fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse); fprintf(fd, "delay=%d\n", (int) this->delay); fprintf(fd, "left_meters="); writeMeters(this, fd, 0); fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar); #ifdef HAVE_LIBHWLOC fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity); #endif fclose(fd); return true; } Settings* Settings_new(int initialCpuCount) { Settings* this = xCalloc(1, sizeof(Settings)); this->sortKey = PERCENT_CPU; this->treeSortKey = PID; this->direction = 1; this->treeDirection = 1; this->shadowOtherUsers = false; this->showThreadNames = false; this->hideKernelThreads = false; this->hideUserlandThreads = false; this->treeView = false; this->highlightBaseName = false; this->highlightMegabytes = false; this->detailedCPUTime = false; this->countCPUsFromOne = false; this->showCPUUsage = true; this->showCPUFrequency = false; #ifdef HAVE_SENSORS_SENSORS_H this->showCPUTemperature = false; this->degreeFahrenheit = false; #endif this->updateProcessNames = false; this->showProgramPath = true; this->highlightThreads = true; this->highlightChanges = false; this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS; this->findCommInCmdline = true; this->stripExeFromCmdline = true; this->showMergedCommand = false; this->hideFunctionBar = 0; #ifdef HAVE_LIBHWLOC this->topologyAffinity = false; #endif this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField)); // TODO: turn 'fields' into a Vector, // (and ProcessFields into proper objects). this->flags = 0; const ProcessField* defaults = Platform_defaultFields; for (int i = 0; defaults[i]; i++) { this->fields[i] = defaults[i]; this->flags |= Process_fields[defaults[i]].flags; } char* legacyDotfile = NULL; char* rcfile = getenv("HTOPRC"); if (rcfile) { this->filename = xStrdup(rcfile); } else { const char* home = getenv("HOME"); if (!home) home = ""; const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); char* configDir = NULL; char* htopDir = NULL; if (xdgConfigHome) { this->filename = String_cat(xdgConfigHome, "/htop/htoprc"); configDir = xStrdup(xdgConfigHome); htopDir = String_cat(xdgConfigHome, "/htop"); } else { this->filename = String_cat(home, "/.config/htop/htoprc"); configDir = String_cat(home, "/.config"); htopDir = String_cat(home, "/.config/htop"); } legacyDotfile = String_cat(home, "/.htoprc"); CRT_dropPrivileges(); (void) mkdir(configDir, 0700); (void) mkdir(htopDir, 0700); free(htopDir); free(configDir); struct stat st; int err = lstat(legacyDotfile, &st); if (err || S_ISLNK(st.st_mode)) { free(legacyDotfile); legacyDotfile = NULL; } CRT_restorePrivileges(); } this->colorScheme = 0; this->enableMouse = true; this->changed = false; this->delay = DEFAULT_DELAY; bool ok = false; if (legacyDotfile) { ok = Settings_read(this, legacyDotfile, initialCpuCount); if (ok) { // Transition to new location and delete old configuration file if (Settings_write(this)) { unlink(legacyDotfile); } } free(legacyDotfile); } if (!ok) { ok = Settings_read(this, this->filename, initialCpuCount); } if (!ok) { this->changed = true; // TODO: how to get SYSCONFDIR correctly through Autoconf? char* systemSettings = String_cat(SYSCONFDIR, "/htoprc"); ok = Settings_read(this, systemSettings, initialCpuCount); free(systemSettings); } if (!ok) { Settings_defaultMeters(this, initialCpuCount); this->hideKernelThreads = true; this->highlightMegabytes = true; this->highlightThreads = true; this->findCommInCmdline = true; this->stripExeFromCmdline = true; this->showMergedCommand = false; this->headerMargin = true; } return this; } void Settings_invertSortOrder(Settings* this) { int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction); *attr = (*attr == 1) ? -1 : 1; } void Settings_setSortKey(Settings* this, ProcessField sortKey) { if (this->treeViewAlwaysByPID || !this->treeView) { this->sortKey = sortKey; this->direction = 1; this->treeView = false; } else { this->treeSortKey = sortKey; this->treeDirection = 1; } } htop-3.0.5/Settings.h000066400000000000000000000042471377712513700144660ustar00rootroot00000000000000#ifndef HEADER_Settings #define HEADER_Settings /* htop - Settings.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include "Process.h" #define DEFAULT_DELAY 15 typedef struct { int len; char** names; int* modes; } MeterColumnSettings; typedef struct Settings_ { char* filename; MeterColumnSettings columns[2]; ProcessField* fields; uint32_t flags; int colorScheme; int delay; int direction; int treeDirection; ProcessField sortKey; ProcessField treeSortKey; bool countCPUsFromOne; bool detailedCPUTime; bool showCPUUsage; bool showCPUFrequency; #ifdef HAVE_SENSORS_SENSORS_H bool showCPUTemperature; bool degreeFahrenheit; #endif bool treeView; bool treeViewAlwaysByPID; bool showProgramPath; bool shadowOtherUsers; bool showThreadNames; bool hideKernelThreads; bool hideUserlandThreads; bool highlightBaseName; bool highlightMegabytes; bool highlightThreads; bool highlightChanges; int highlightDelaySecs; bool findCommInCmdline; bool stripExeFromCmdline; bool showMergedCommand; bool updateProcessNames; bool accountGuestInCPUMeter; bool headerMargin; bool enableMouse; int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently #ifdef HAVE_LIBHWLOC bool topologyAffinity; #endif bool changed; } Settings; #define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu)) static inline ProcessField Settings_getActiveSortKey(const Settings* this) { return (this->treeView) ? (this->treeViewAlwaysByPID ? PID : this->treeSortKey) : this->sortKey; } static inline int Settings_getActiveDirection(const Settings* this) { return this->treeView ? this->treeDirection : this->direction; } void Settings_delete(Settings* this); bool Settings_write(Settings* this); Settings* Settings_new(int initialCpuCount); void Settings_invertSortOrder(Settings* this); void Settings_setSortKey(Settings* this, ProcessField sortKey); #endif htop-3.0.5/SignalsPanel.c000066400000000000000000000026231377712513700152350ustar00rootroot00000000000000/* htop - SignalsPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "SignalsPanel.h" #include #include #include "FunctionBar.h" #include "ListItem.h" #include "Object.h" #include "Panel.h" #include "Platform.h" #include "XUtils.h" Panel* SignalsPanel_new() { Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Send ", "Cancel ")); const int defaultSignal = SIGTERM; int defaultPosition = 15; unsigned int i; for (i = 0; i < Platform_numberOfSignals; i++) { Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number)); // signal 15 is not always the 15th signal in the table if (Platform_signals[i].number == defaultSignal) { defaultPosition = i; } } #if (defined(SIGRTMIN) && defined(SIGRTMAX)) if (SIGRTMAX - SIGRTMIN <= 100) { static char buf[16]; for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) { int n = sig - SIGRTMIN; xSnprintf(buf, sizeof(buf), "%2d SIGRTMIN%-+3d", sig, n); if (n == 0) { buf[11] = '\0'; } Panel_set(this, i, (Object*) ListItem_new(buf, sig)); } } #endif Panel_setHeader(this, "Send signal:"); Panel_setSelected(this, defaultPosition); return this; } htop-3.0.5/SignalsPanel.h000066400000000000000000000005421377712513700152400ustar00rootroot00000000000000#ifndef HEADER_SignalsPanel #define HEADER_SignalsPanel /* htop - SignalsPanel.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Panel.h" typedef struct SignalItem_ { const char* name; int number; } SignalItem; Panel* SignalsPanel_new(void); #endif htop-3.0.5/SwapMeter.c000066400000000000000000000027741377712513700145730ustar00rootroot00000000000000/* htop - SwapMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "SwapMeter.h" #include "CRT.h" #include "Object.h" #include "Platform.h" #include "RichString.h" static const int SwapMeter_attributes[] = { SWAP }; static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) { int written; Platform_setSwapValues(this); written = Meter_humanUnit(buffer, this->values[0], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); Meter_humanUnit(buffer, this->total, size); } static void SwapMeter_display(const Object* cast, RichString* out) { char buffer[50]; const Meter* this = (const Meter*)cast; RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); } const MeterClass SwapMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = SwapMeter_display, }, .updateValues = SwapMeter_updateValues, .defaultMode = BAR_METERMODE, .maxItems = 1, .total = 100.0, .attributes = SwapMeter_attributes, .name = "Swap", .uiName = "Swap", .caption = "Swp" }; htop-3.0.5/SwapMeter.h000066400000000000000000000004231377712513700145650ustar00rootroot00000000000000#ifndef HEADER_SwapMeter #define HEADER_SwapMeter /* htop - SwapMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass SwapMeter_class; #endif htop-3.0.5/TESTPLAN000066400000000000000000000054231377712513700136670ustar00rootroot00000000000000 Main screen: For all views, all modes: Mouse click header - nothing happens. Mouse click on ProcessList title bar - exit Tree view, update FunctionBar, title bar updates, sort by clicked field. *** FAILING: wrong FB update depending on mode; does not change sort in wip branch click on same entry - invert sort. click on another entry - sort another field. Mouse click on a process - select that process. for each entry in FunctionBar: Mouse click entry - perform action of associated key. In Normal mode, Sorted view: <+> or <-> - do nothing. - enter SortBy screen. In Normal mode, Tree view: select process - update F6 in FunctionBar if subtree is collapsed or expanded. , <+> or <-> - expand/collapse subtree. In Normal mode, either Sorted or Tree view: , - activate Search mode. , <\> - activate Filter mode. , <]> - as root only, decrease process NICE value. , <[> - increase process NICE value. - enter Affinity screen. - do nothing. - select process and all its children. , , , - do nothing. , , - enter Help screen. - on Linux, enter IOPriority screen. - do nothing. , - enter Kill screen. - enter LSOF screen. , , ,

- enter Sorted view, update function bar, sort by CPU%. , - do nothing. - enter Sorted view, update function bar, sort by TIME. - untag all processes. , , , , - do nothing. <<>, <>>, <,>, <.> - enter SortBy screen. space - tag current process, move down cursor. numbers - incremental PID search. In Search mode: TODO In Filter mode: TODO Setup screen: TODO SortBy screen: TODO User screen: TODO Kill screen: TODO Affinity screen: TODO Help screen: any key - back to Main screen. IOPriority screen: TODO STrace screen: TODO LSOF screen: TODO htop-3.0.5/TasksMeter.c000066400000000000000000000055041377712513700147400ustar00rootroot00000000000000/* htop - TasksMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "TasksMeter.h" #include "CRT.h" #include "Macros.h" #include "Object.h" #include "ProcessList.h" #include "RichString.h" #include "Settings.h" #include "XUtils.h" static const int TasksMeter_attributes[] = { CPU_SYSTEM, PROCESS_THREAD, PROCESS, TASKS_RUNNING }; static void TasksMeter_updateValues(Meter* this, char* buffer, size_t len) { const ProcessList* pl = this->pl; this->values[0] = pl->kernelThreads; this->values[1] = pl->userlandThreads; this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads; this->values[3] = MINIMUM(pl->runningTasks, pl->cpuCount); if (pl->totalTasks > this->total) { this->total = pl->totalTasks; } if (pl->settings->hideKernelThreads) { this->values[0] = 0; } xSnprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total); } static void TasksMeter_display(const Object* cast, RichString* out) { const Meter* this = (const Meter*)cast; const Settings* settings = this->pl->settings; char buffer[20]; int processes = (int) this->values[2]; xSnprintf(buffer, sizeof(buffer), "%d", processes); RichString_writeAscii(out, CRT_colors[METER_VALUE], buffer); int threadValueColor = CRT_colors[METER_VALUE]; int threadCaptionColor = CRT_colors[METER_TEXT]; if (settings->highlightThreads) { threadValueColor = CRT_colors[PROCESS_THREAD_BASENAME]; threadCaptionColor = CRT_colors[PROCESS_THREAD]; } if (!settings->hideUserlandThreads) { RichString_appendAscii(out, CRT_colors[METER_TEXT], ", "); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]); RichString_appendAscii(out, threadValueColor, buffer); RichString_appendAscii(out, threadCaptionColor, " thr"); } if (!settings->hideKernelThreads) { RichString_appendAscii(out, CRT_colors[METER_TEXT], ", "); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]); RichString_appendAscii(out, threadValueColor, buffer); RichString_appendAscii(out, threadCaptionColor, " kthr"); } RichString_appendAscii(out, CRT_colors[METER_TEXT], "; "); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]); RichString_appendAscii(out, CRT_colors[TASKS_RUNNING], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " running"); } const MeterClass TasksMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = TasksMeter_display, }, .updateValues = TasksMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 4, .total = 100.0, .attributes = TasksMeter_attributes, .name = "Tasks", .uiName = "Task counter", .caption = "Tasks: " }; htop-3.0.5/TasksMeter.h000066400000000000000000000004271377712513700147440ustar00rootroot00000000000000#ifndef HEADER_TasksMeter #define HEADER_TasksMeter /* htop - TasksMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass TasksMeter_class; #endif htop-3.0.5/TraceScreen.c000066400000000000000000000121101377712513700150430ustar00rootroot00000000000000/* htop - TraceScreen.c (C) 2005-2006 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "TraceScreen.h" #include #include #include #include #include #include #include #include #include #include #include #include "CRT.h" #include "FunctionBar.h" #include "IncSet.h" #include "Panel.h" #include "ProvideCurses.h" #include "XUtils.h" static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL}; static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"}; static const int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27}; TraceScreen* TraceScreen_new(Process* process) { // This initializes all TraceScreen variables to "false" so only default = true ones need to be set below TraceScreen* this = xCalloc(1, sizeof(TraceScreen)); Object_setClass(this, Class(TraceScreen)); this->tracing = true; FunctionBar* fuBar = FunctionBar_new(TraceScreenFunctions, TraceScreenKeys, TraceScreenEvents); CRT_disableDelay(); return (TraceScreen*) InfoScreen_init(&this->super, process, fuBar, LINES - 2, ""); } void TraceScreen_delete(Object* cast) { TraceScreen* this = (TraceScreen*) cast; if (this->child > 0) { kill(this->child, SIGTERM); waitpid(this->child, NULL, 0); } if (this->strace) { fclose(this->strace); } CRT_enableDelay(); free(InfoScreen_done((InfoScreen*)this)); } static void TraceScreen_draw(InfoScreen* this) { InfoScreen_drawTitled(this, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process)); } bool TraceScreen_forkTracer(TraceScreen* this) { int fdpair[2] = {0, 0}; if (pipe(fdpair) == -1) return false; if (fcntl(fdpair[0], F_SETFL, O_NONBLOCK) < 0) goto err; if (fcntl(fdpair[1], F_SETFL, O_NONBLOCK) < 0) goto err; pid_t child = fork(); if (child == -1) goto err; if (child == 0) { close(fdpair[0]); dup2(fdpair[1], STDOUT_FILENO); dup2(fdpair[1], STDERR_FILENO); close(fdpair[1]); CRT_dropPrivileges(); char buffer[32] = {0}; xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL); // Should never reach here, unless execlp fails ... const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; ssize_t written = write(STDERR_FILENO, message, strlen(message)); (void) written; exit(127); } FILE* fd = fdopen(fdpair[0], "r"); if (!fd) goto err; close(fdpair[1]); this->child = child; this->strace = fd; return true; err: close(fdpair[1]); close(fdpair[0]); return false; } static void TraceScreen_updateTrace(InfoScreen* super) { TraceScreen* this = (TraceScreen*) super; char buffer[1025]; int fd_strace = fileno(this->strace); assert(fd_strace != -1); fd_set fds; FD_ZERO(&fds); // FD_SET(STDIN_FILENO, &fds); FD_SET(fd_strace, &fds); struct timeval tv = { .tv_sec = 0, .tv_usec = 500 }; int ready = select(fd_strace + 1, &fds, NULL, NULL, &tv); size_t nread = 0; if (ready > 0 && FD_ISSET(fd_strace, &fds)) nread = fread(buffer, 1, sizeof(buffer) - 1, this->strace); if (nread && this->tracing) { const char* line = buffer; buffer[nread] = '\0'; for (size_t i = 0; i < nread; i++) { if (buffer[i] == '\n') { buffer[i] = '\0'; if (this->contLine) { InfoScreen_appendLine(&this->super, line); this->contLine = false; } else { InfoScreen_addLine(&this->super, line); } line = buffer + i + 1; } } if (line < buffer + nread) { InfoScreen_addLine(&this->super, line); buffer[nread] = '\0'; this->contLine = true; } if (this->follow) { Panel_setSelected(this->super.display, Panel_size(this->super.display) - 1); } } } static bool TraceScreen_onKey(InfoScreen* super, int ch) { TraceScreen* this = (TraceScreen*) super; switch(ch) { case 'f': case KEY_F(8): this->follow = !(this->follow); if (this->follow) Panel_setSelected(super->display, Panel_size(super->display)-1); return true; case 't': case KEY_F(9): this->tracing = !this->tracing; FunctionBar_setLabel(super->display->defaultBar, KEY_F(9), this->tracing?"Stop Tracing ":"Resume Tracing "); InfoScreen_draw(this); return true; } this->follow = false; return false; } const InfoScreenClass TraceScreen_class = { .super = { .extends = Class(Object), .delete = TraceScreen_delete }, .draw = TraceScreen_draw, .onErr = TraceScreen_updateTrace, .onKey = TraceScreen_onKey, }; htop-3.0.5/TraceScreen.h000066400000000000000000000012611377712513700150550ustar00rootroot00000000000000#ifndef HEADER_TraceScreen #define HEADER_TraceScreen /* htop - TraceScreen.h (C) 2005-2006 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include #include "InfoScreen.h" #include "Object.h" #include "Process.h" typedef struct TraceScreen_ { InfoScreen super; bool tracing; pid_t child; FILE* strace; bool contLine; bool follow; } TraceScreen; extern const InfoScreenClass TraceScreen_class; TraceScreen* TraceScreen_new(Process* process); void TraceScreen_delete(Object* cast); bool TraceScreen_forkTracer(TraceScreen* this); #endif htop-3.0.5/UptimeMeter.c000066400000000000000000000027771377712513700151270ustar00rootroot00000000000000/* htop - UptimeMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "UptimeMeter.h" #include "CRT.h" #include "Object.h" #include "Platform.h" #include "XUtils.h" static const int UptimeMeter_attributes[] = { UPTIME }; static void UptimeMeter_updateValues(Meter* this, char* buffer, size_t len) { int totalseconds = Platform_getUptime(); if (totalseconds == -1) { xSnprintf(buffer, len, "(unknown)"); return; } int seconds = totalseconds % 60; int minutes = (totalseconds / 60) % 60; int hours = (totalseconds / 3600) % 24; int days = (totalseconds / 86400); this->values[0] = days; if (days > this->total) { this->total = days; } char daysbuf[32]; if (days > 100) { xSnprintf(daysbuf, sizeof(daysbuf), "%d days(!), ", days); } else if (days > 1) { xSnprintf(daysbuf, sizeof(daysbuf), "%d days, ", days); } else if (days == 1) { xSnprintf(daysbuf, sizeof(daysbuf), "1 day, "); } else { daysbuf[0] = '\0'; } xSnprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds); } const MeterClass UptimeMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete }, .updateValues = UptimeMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 100.0, .attributes = UptimeMeter_attributes, .name = "Uptime", .uiName = "Uptime", .caption = "Uptime: " }; htop-3.0.5/UptimeMeter.h000066400000000000000000000004331377712513700151170ustar00rootroot00000000000000#ifndef HEADER_UptimeMeter #define HEADER_UptimeMeter /* htop - UptimeMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass UptimeMeter_class; #endif htop-3.0.5/UsersTable.c000066400000000000000000000020371377712513700147250ustar00rootroot00000000000000/* htop - UsersTable.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "UsersTable.h" #include #include #include #include #include "XUtils.h" UsersTable* UsersTable_new() { UsersTable* this; this = xMalloc(sizeof(UsersTable)); this->users = Hashtable_new(10, true); return this; } void UsersTable_delete(UsersTable* this) { Hashtable_delete(this->users); free(this); } char* UsersTable_getRef(UsersTable* this, unsigned int uid) { char* name = Hashtable_get(this->users, uid); if (name == NULL) { const struct passwd* userData = getpwuid(uid); if (userData != NULL) { name = xStrdup(userData->pw_name); Hashtable_put(this->users, uid, name); } } return name; } inline void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData) { Hashtable_foreach(this->users, f, userData); } htop-3.0.5/UsersTable.h000066400000000000000000000010231377712513700147240ustar00rootroot00000000000000#ifndef HEADER_UsersTable #define HEADER_UsersTable /* htop - UsersTable.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Hashtable.h" typedef struct UsersTable_ { Hashtable* users; } UsersTable; UsersTable* UsersTable_new(void); void UsersTable_delete(UsersTable* this); char* UsersTable_getRef(UsersTable* this, unsigned int uid); void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData); #endif htop-3.0.5/Vector.c000066400000000000000000000205141377712513700141160ustar00rootroot00000000000000/* htop - Vector.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Vector.h" #include #include #include #include "XUtils.h" Vector* Vector_new(const ObjectClass* type, bool owner, int size) { Vector* this; if (size == DEFAULT_SIZE) { size = 10; } assert(size > 0); this = xMalloc(sizeof(Vector)); this->growthRate = size; this->array = (Object**) xCalloc(size, sizeof(Object*)); this->arraySize = size; this->items = 0; this->type = type; this->owner = owner; return this; } void Vector_delete(Vector* this) { if (this->owner) { for (int i = 0; i < this->items; i++) { if (this->array[i]) { Object_delete(this->array[i]); } } } free(this->array); free(this); } #ifndef NDEBUG static bool Vector_isConsistent(const Vector* this) { assert(this->items <= this->arraySize); if (this->owner) { for (int i = 0; i < this->items; i++) { if (!this->array[i]) { return false; } } } return true; } unsigned int Vector_count(const Vector* this) { unsigned int items = 0; for (int i = 0; i < this->items; i++) { if (this->array[i]) { items++; } } assert(items == (unsigned int)this->items); return items; } Object* Vector_get(const Vector* this, int idx) { assert(idx >= 0 && idx < this->items); assert(this->array[idx]); assert(Object_isA(this->array[idx], this->type)); return this->array[idx]; } int Vector_size(const Vector* this) { assert(Vector_isConsistent(this)); return this->items; } #endif /* NDEBUG */ void Vector_prune(Vector* this) { assert(Vector_isConsistent(this)); if (this->owner) { for (int i = 0; i < this->items; i++) if (this->array[i]) { Object_delete(this->array[i]); //this->array[i] = NULL; } } this->items = 0; } //static int comparisons = 0; static void swap(Object** array, int indexA, int indexB) { assert(indexA >= 0); assert(indexB >= 0); Object* tmp = array[indexA]; array[indexA] = array[indexB]; array[indexB] = tmp; } static int partition(Object** array, int left, int right, int pivotIndex, Object_Compare compare) { const Object* pivotValue = array[pivotIndex]; swap(array, pivotIndex, right); int storeIndex = left; for (int i = left; i < right; i++) { //comparisons++; if (compare(array[i], pivotValue) <= 0) { swap(array, i, storeIndex); storeIndex++; } } swap(array, storeIndex, right); return storeIndex; } static void quickSort(Object** array, int left, int right, Object_Compare compare) { if (left >= right) return; int pivotIndex = (left + right) / 2; int pivotNewIndex = partition(array, left, right, pivotIndex, compare); quickSort(array, left, pivotNewIndex - 1, compare); quickSort(array, pivotNewIndex + 1, right, compare); } // If I were to use only one sorting algorithm for both cases, it would probably be this one: /* static void combSort(Object** array, int left, int right, Object_Compare compare) { int gap = right - left; bool swapped = true; while ((gap > 1) || swapped) { if (gap > 1) { gap = (int)((double)gap / 1.247330950103979); } swapped = false; for (int i = left; gap + i <= right; i++) { comparisons++; if (compare(array[i], array[i+gap]) > 0) { swap(array, i, i+gap); swapped = true; } } } } */ static void insertionSort(Object** array, int left, int right, Object_Compare compare) { for (int i = left + 1; i <= right; i++) { Object* t = array[i]; int j = i - 1; while (j >= left) { //comparisons++; if (compare(array[j], t) <= 0) break; array[j + 1] = array[j]; j--; } array[j + 1] = t; } } void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare) { assert(compare); assert(Vector_isConsistent(this)); quickSort(this->array, 0, this->items - 1, compare); assert(Vector_isConsistent(this)); } void Vector_insertionSort(Vector* this) { assert(this->type->compare); assert(Vector_isConsistent(this)); insertionSort(this->array, 0, this->items - 1, this->type->compare); assert(Vector_isConsistent(this)); } static void Vector_checkArraySize(Vector* this) { assert(Vector_isConsistent(this)); if (this->items >= this->arraySize) { //int i; //i = this->arraySize; this->arraySize = this->items + this->growthRate; this->array = (Object**) xRealloc(this->array, sizeof(Object*) * this->arraySize); //for (; i < this->arraySize; i++) // this->array[i] = NULL; } assert(Vector_isConsistent(this)); } void Vector_insert(Vector* this, int idx, void* data_) { Object* data = data_; assert(idx >= 0); assert(Object_isA(data, this->type)); assert(Vector_isConsistent(this)); if (idx > this->items) { idx = this->items; } Vector_checkArraySize(this); //assert(this->array[this->items] == NULL); if (idx < this->items) { memmove(&this->array[idx + 1], &this->array[idx], (this->items - idx) * sizeof(this->array[0])); } this->array[idx] = data; this->items++; assert(Vector_isConsistent(this)); } Object* Vector_take(Vector* this, int idx) { assert(idx >= 0 && idx < this->items); assert(Vector_isConsistent(this)); Object* removed = this->array[idx]; assert(removed); this->items--; if (idx < this->items) { memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx) * sizeof(this->array[0])); } //this->array[this->items] = NULL; assert(Vector_isConsistent(this)); return removed; } Object* Vector_remove(Vector* this, int idx) { Object* removed = Vector_take(this, idx); if (this->owner) { Object_delete(removed); return NULL; } else { return removed; } } void Vector_moveUp(Vector* this, int idx) { assert(idx >= 0 && idx < this->items); assert(Vector_isConsistent(this)); if (idx == 0) return; Object* temp = this->array[idx]; this->array[idx] = this->array[idx - 1]; this->array[idx - 1] = temp; } void Vector_moveDown(Vector* this, int idx) { assert(idx >= 0 && idx < this->items); assert(Vector_isConsistent(this)); if (idx == this->items - 1) return; Object* temp = this->array[idx]; this->array[idx] = this->array[idx + 1]; this->array[idx + 1] = temp; } void Vector_set(Vector* this, int idx, void* data_) { Object* data = data_; assert(idx >= 0); assert(Object_isA(data, this->type)); assert(Vector_isConsistent(this)); Vector_checkArraySize(this); if (idx >= this->items) { this->items = idx + 1; } else { if (this->owner) { Object* removed = this->array[idx]; assert (removed != NULL); Object_delete(removed); } } this->array[idx] = data; assert(Vector_isConsistent(this)); } /* static void Vector_merge(Vector* this, Vector* v2) { int i; assert(Vector_isConsistent(this)); for (i = 0; i < v2->items; i++) Vector_add(this, v2->array[i]); v2->items = 0; Vector_delete(v2); assert(Vector_isConsistent(this)); } */ void Vector_add(Vector* this, void* data_) { Object* data = data_; assert(Object_isA(data, this->type)); assert(Vector_isConsistent(this)); int i = this->items; Vector_set(this, this->items, data); assert(this->items == i + 1); (void)(i); assert(Vector_isConsistent(this)); } int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compare) { const Object* search = search_; assert(Object_isA(search, this->type)); assert(compare); assert(Vector_isConsistent(this)); for (int i = 0; i < this->items; i++) { const Object* o = this->array[i]; assert(o); if (compare(search, o) == 0) { return i; } } return -1; } void Vector_splice(Vector* this, Vector* from) { assert(Vector_isConsistent(this)); assert(Vector_isConsistent(from)); assert(!(this->owner && from->owner)); int olditems = this->items; this->items += from->items; Vector_checkArraySize(this); for (int j = 0; j < from->items; j++) { this->array[olditems + j] = from->array[j]; } } htop-3.0.5/Vector.h000066400000000000000000000031721377712513700141240ustar00rootroot00000000000000#ifndef HEADER_Vector #define HEADER_Vector /* htop - Vector.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Object.h" #include #ifndef DEFAULT_SIZE #define DEFAULT_SIZE (-1) #endif typedef struct Vector_ { Object** array; const ObjectClass* type; int arraySize; int growthRate; int items; bool owner; } Vector; Vector* Vector_new(const ObjectClass* type, bool owner, int size); void Vector_delete(Vector* this); void Vector_prune(Vector* this); void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare); static inline void Vector_quickSort(Vector* this) { Vector_quickSortCustomCompare(this, this->type->compare); } void Vector_insertionSort(Vector* this); void Vector_insert(Vector* this, int idx, void* data_); Object* Vector_take(Vector* this, int idx); Object* Vector_remove(Vector* this, int idx); void Vector_moveUp(Vector* this, int idx); void Vector_moveDown(Vector* this, int idx); void Vector_set(Vector* this, int idx, void* data_); #ifndef NDEBUG Object* Vector_get(const Vector* this, int idx); int Vector_size(const Vector* this); unsigned int Vector_count(const Vector* this); #else /* NDEBUG */ static inline Object* Vector_get(const Vector* this, int idx) { return this->array[idx]; } static inline int Vector_size(const Vector* this) { return this->items; } #endif /* NDEBUG */ void Vector_add(Vector* this, void* data_); int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compare); void Vector_splice(Vector* this, Vector* from); #endif htop-3.0.5/XUtils.c000066400000000000000000000133321377712513700141040ustar00rootroot00000000000000/* htop - StringUtils.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "XUtils.h" #include #include #include #include #include #include #include #include #include "CRT.h" void fail() { CRT_done(); abort(); _exit(1); // Should never reach here } void* xMalloc(size_t size) { assert(size > 0); void* data = malloc(size); if (!data) { fail(); } return data; } void* xMallocArray(size_t nmemb, size_t size) { assert(nmemb > 0); assert(size > 0); if (SIZE_MAX / nmemb < size) { fail(); } return xMalloc(nmemb * size); } void* xCalloc(size_t nmemb, size_t size) { assert(nmemb > 0); assert(size > 0); if (SIZE_MAX / nmemb < size) { fail(); } void* data = calloc(nmemb, size); if (!data) { fail(); } return data; } void* xRealloc(void* ptr, size_t size) { assert(size > 0); void* data = realloc(ptr, size); // deepcode ignore MemoryLeakOnRealloc: this goes to fail() if (!data) { free(ptr); fail(); } return data; } void* xReallocArray(void* ptr, size_t nmemb, size_t size) { assert(nmemb > 0); assert(size > 0); if (SIZE_MAX / nmemb < size) { fail(); } return xRealloc(ptr, nmemb * size); } char* String_cat(const char* s1, const char* s2) { const size_t l1 = strlen(s1); const size_t l2 = strlen(s2); char* out = xMalloc(l1 + l2 + 1); memcpy(out, s1, l1); memcpy(out + l1, s2, l2); out[l1 + l2] = '\0'; return out; } char* String_trim(const char* in) { while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') { in++; } size_t len = strlen(in); while (len > 0 && (in[len - 1] == ' ' || in[len - 1] == '\t' || in[len - 1] == '\n')) { len--; } return xStrndup(in, len); } char** String_split(const char* s, char sep, size_t* n) { const unsigned int rate = 10; char** out = xCalloc(rate, sizeof(char*)); size_t ctr = 0; unsigned int blocks = rate; const char* where; while ((where = strchr(s, sep)) != NULL) { size_t size = (size_t)(where - s); out[ctr] = xStrndup(s, size); ctr++; if (ctr == blocks) { blocks += rate; out = (char**) xRealloc(out, sizeof(char*) * blocks); } s += size + 1; } if (s[0] != '\0') { out[ctr] = xStrdup(s); ctr++; } out = xRealloc(out, sizeof(char*) * (ctr + 1)); out[ctr] = NULL; if (n) *n = ctr; return out; } void String_freeArray(char** s) { if (!s) { return; } for (size_t i = 0; s[i] != NULL; i++) { free(s[i]); } free(s); } char* String_getToken(const char* line, const unsigned short int numMatch) { const size_t len = strlen(line); char inWord = 0; unsigned short int count = 0; char match[50]; size_t foundCount = 0; for (size_t i = 0; i < len; i++) { char lastState = inWord; inWord = line[i] == ' ' ? 0 : 1; if (lastState == 0 && inWord == 1) count++; if (inWord == 1) { if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) { match[foundCount] = line[i]; foundCount++; } } } match[foundCount] = '\0'; return xStrdup(match); } char* String_readLine(FILE* fd) { const unsigned int step = 1024; unsigned int bufSize = step; char* buffer = xMalloc(step + 1); char* at = buffer; for (;;) { char* ok = fgets(at, step + 1, fd); if (!ok) { free(buffer); return NULL; } char* newLine = strrchr(at, '\n'); if (newLine) { *newLine = '\0'; return buffer; } else { if (feof(fd)) { return buffer; } } bufSize += step; buffer = xRealloc(buffer, bufSize + 1); at = buffer + bufSize - step; } } int xAsprintf(char** strp, const char* fmt, ...) { va_list vl; va_start(vl, fmt); int r = vasprintf(strp, fmt, vl); va_end(vl); if (r < 0 || !*strp) { fail(); } return r; } int xSnprintf(char* buf, size_t len, const char* fmt, ...) { va_list vl; va_start(vl, fmt); int n = vsnprintf(buf, len, fmt, vl); va_end(vl); if (n < 0 || (size_t)n >= len) { fail(); } return n; } char* xStrdup(const char* str) { char* data = strdup(str); if (!data) { fail(); } return data; } char* xStrndup(const char* str, size_t len) { char* data = strndup(str, len); if (!data) { fail(); } return data; } static ssize_t readfd_internal(int fd, void* buffer, size_t count) { if (!count) { close(fd); return -EINVAL; } ssize_t alreadyRead = 0; count--; // reserve one for null-terminator for (;;) { ssize_t res = read(fd, buffer, count); if (res == -1) { if (errno == EINTR) continue; close(fd); return -errno; } if (res > 0) { buffer = ((char*)buffer) + res; count -= (size_t)res; alreadyRead += res; } if (count == 0 || res == 0) { close(fd); *((char*)buffer) = '\0'; return alreadyRead; } } } ssize_t xReadfile(const char* pathname, void* buffer, size_t count) { int fd = open(pathname, O_RDONLY); if (fd < 0) return -errno; return readfd_internal(fd, buffer, count); } ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count) { int fd = Compat_openat(dirfd, pathname, O_RDONLY); if (fd < 0) return -errno; return readfd_internal(fd, buffer, count); } htop-3.0.5/XUtils.h000066400000000000000000000035501377712513700141120ustar00rootroot00000000000000#ifndef HEADER_XUtils #define HEADER_XUtils /* htop - StringUtils.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include // IWYU pragma: keep #include // IWYU pragma: keep #include #include "Compat.h" #include "Macros.h" void fail(void) ATTR_NORETURN; void* xMalloc(size_t size); void* xMallocArray(size_t nmemb, size_t size); void* xCalloc(size_t nmemb, size_t size); void* xRealloc(void* ptr, size_t size); void* xReallocArray(void* ptr, size_t nmemb, size_t size); /* * String_startsWith gives better performance if strlen(match) can be computed * at compile time (e.g. when they are immutable string literals). :) */ static inline bool String_startsWith(const char* s, const char* match) { return strncmp(s, match, strlen(match)) == 0; } static inline bool String_contains_i(const char* s1, const char* s2) { return strcasestr(s1, s2) != NULL; } static inline bool String_eq(const char* s1, const char* s2) { return strcmp(s1, s2) == 0; } char* String_cat(const char* s1, const char* s2); char* String_trim(const char* in); char** String_split(const char* s, char sep, size_t* n); void String_freeArray(char** s); char* String_getToken(const char* line, unsigned short int numMatch); char* String_readLine(FILE* fd); ATTR_FORMAT(printf, 2, 3) int xAsprintf(char** strp, const char* fmt, ...); ATTR_FORMAT(printf, 3, 4) int xSnprintf(char* buf, size_t len, const char* fmt, ...); char* xStrdup(const char* str) ATTR_NONNULL; char* xStrndup(const char* str, size_t len) ATTR_NONNULL; ssize_t xReadfile(const char* pathname, void* buffer, size_t count); ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count); #endif htop-3.0.5/autogen.sh000077500000000000000000000000671377712513700145120ustar00rootroot00000000000000#!/bin/sh autoreconf --force --install --verbose -Wall htop-3.0.5/configure.ac000066400000000000000000000375271377712513700150120ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.65) AC_INIT([htop],[3.0.5],[htop@groups.io]) AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_HEADERS([config.h]) # Required by hwloc scripts AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([1.11]) # Checks for programs. # ---------------------------------------------------------------------- AC_PROG_CC AM_PROG_CC_C_O # Required by hwloc scripts AC_USE_SYSTEM_EXTENSIONS # Checks for platform. # ---------------------------------------------------------------------- case "$target_os" in linux*|gnu*) my_htop_platform=linux AC_DEFINE([HTOP_LINUX], [], [Building for Linux]) ;; freebsd*|kfreebsd*) my_htop_platform=freebsd AC_DEFINE([HTOP_FREEBSD], [], [Building for FreeBSD]) ;; openbsd*) my_htop_platform=openbsd AC_DEFINE([HTOP_OPENBSD], [], [Building for OpenBSD]) ;; dragonfly*) my_htop_platform=dragonflybsd AC_DEFINE([HTOP_DRAGONFLYBSD], [], [Building for DragonFlyBSD]) ;; darwin*) my_htop_platform=darwin AC_DEFINE([HTOP_DARWIN], [], [Building for Darwin]) ;; solaris*) my_htop_platform=solaris AC_DEFINE([HTOP_SOLARIS], [], [Building for Solaris]) ;; *) my_htop_platform=unsupported AC_DEFINE([HTOP_UNSUPPORTED], [], [Building for an unsupported platform]) ;; esac # Checks for libraries. # ---------------------------------------------------------------------- AC_CHECK_LIB([m], [ceil], [], [missing_libraries="$missing_libraries libm"]) # Checks for header files. # ---------------------------------------------------------------------- AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/param.h sys/time.h unistd.h],[:],[ missing_headers="$missing_headers $ac_header" ]) AC_CHECK_HEADERS([execinfo.h],[:],[:]) AC_HEADER_MAJOR dnl glibc 2.25 deprecates 'major' and 'minor' in and requires to dnl include . However the logic in AC_HEADER_MAJOR has not yet dnl been updated in Autoconf 2.69, so use a workaround: m4_version_prereq([2.70], [], [if test "x$ac_cv_header_sys_mkdev_h" != xyes; then AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1, [Define to 1 if `major', `minor', and `makedev' are declared in .])]) fi]) # Checks for typedefs, structures, and compiler characteristics. # ---------------------------------------------------------------------- AC_HEADER_STDBOOL AC_C_CONST AC_TYPE_PID_T AC_TYPE_UID_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T # Checks for library functions and compiler features. # ---------------------------------------------------------------------- AC_FUNC_CLOSEDIR_VOID AC_FUNC_STAT AC_CHECK_FUNCS([\ clock_gettime\ faccessat\ fstatat\ host_get_clock_service\ openat\ readlinkat\ ]) AC_SEARCH_LIBS([dlopen], [dl dld]) AC_SEARCH_LIBS([clock_gettime], [rt]) save_cflags="${CFLAGS}" CFLAGS="${CFLAGS} -std=c99" AC_MSG_CHECKING([whether cc -std=c99 option works]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [AC_INCLUDES_DEFAULT], [[char *a; a = strdup("foo"); int i = 0; i++; // C99]])], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([htop is written in C99. A newer compiler is required.])]) CFLAGS="$save_cflags" # Add -lexecinfo if needed AC_SEARCH_LIBS([backtrace], [execinfo]) # Add -ldevstat if needed AC_SEARCH_LIBS([devstat_checkversion], [devstat]) # Checks for features and flags. # ---------------------------------------------------------------------- PROCDIR=/proc AC_ARG_WITH(proc, [AS_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])], if test -n "$withval"; then AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem]) PROCDIR="$withval" fi, AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem])) AC_ARG_ENABLE(openvz, [AS_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no") if test "x$enable_openvz" = xyes; then AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.]) fi AC_ARG_ENABLE(vserver, [AS_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no") if test "x$enable_vserver" = xyes; then AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.]) fi AC_ARG_ENABLE(ancient_vserver, [AS_HELP_STRING([--enable-ancient-vserver], [enable ancient VServer support (implies --enable-vserver)])], ,enable_ancient_vserver="no") if test "x$enable_ancient_vserver" = xyes; then AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.]) AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.]) fi # HTOP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART) m4_define([HTOP_CHECK_SCRIPT], [ if test ! -z "m4_toupper($HTOP_[$1]_CONFIG_SCRIPT)"; then # to be used to set the path to ncurses*-config when cross-compiling htop_config_script_libs=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null) htop_config_script_cflags=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --cflags 2> /dev/null) else htop_config_script_libs=$([$4] --libs 2> /dev/null) htop_config_script_cflags=$([$4] --cflags 2> /dev/null) fi htop_script_success=no htop_save_LDFLAGS="$LDFLAGS" htop_save_CFLAGS="$CFLAGS" if test ! "x$htop_config_script_libs" = x; then LDFLAGS="$htop_config_script_libs $LDFLAGS" CFLAGS="$htop_config_script_cflags $CFLAGS" AC_CHECK_LIB([$1], [$2], [ AC_DEFINE([$3], 1, [The library is present.]) LIBS="$htop_config_script_libs $LIBS " htop_script_success=yes ], [ CFLAGS="$htop_save_CFLAGS" ]) LDFLAGS="$htop_save_LDFLAGS" fi if test "x$htop_script_success" = xno; then [$5] fi ]) # HTOP_CHECK_LIB(LIBNAME, FUNCTION, DEFINE, ELSE_PART) m4_define([HTOP_CHECK_LIB], [ AC_CHECK_LIB([$1], [$2], [ AC_DEFINE([$3], 1, [The library is present.]) LIBS="-l[$1] $LIBS " ], [$4]) ]) dnl https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS AC_ARG_ENABLE(unicode, [AS_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes") if test "x$enable_unicode" = xyes; then HTOP_CHECK_SCRIPT([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config", HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config", HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw5-config", HTOP_CHECK_SCRIPT([ncurses], [addnwstr], [HAVE_LIBNCURSESW], "ncurses5-config", HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW], missing_libraries="$missing_libraries libncursesw" AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.]) ))))))) AC_CHECK_HEADERS([ncursesw/curses.h],[:], [AC_CHECK_HEADERS([ncurses/ncurses.h],[:], [AC_CHECK_HEADERS([ncurses/curses.h],[:], [AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])]) # check if additional linker flags are needed for keypad(3) # (at this point we already link against a working ncurses library with wide character support) AC_SEARCH_LIBS([keypad], [tinfow tinfo]) else HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config", HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config", HTOP_CHECK_LIB([ncurses6], [refresh], [HAVE_LIBNCURSES], HTOP_CHECK_LIB([ncurses], [refresh], [HAVE_LIBNCURSES], missing_libraries="$missing_libraries libncurses" )))) AC_CHECK_HEADERS([curses.h],[:], [AC_CHECK_HEADERS([ncurses/curses.h],[:], [AC_CHECK_HEADERS([ncurses/ncurses.h],[:], [AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])]) # check if additional linker flags are needed for keypad(3) # (at this point we already link against a working ncurses library) AC_SEARCH_LIBS([keypad], [tinfo]) fi if test "$my_htop_platform" = "darwin"; then AC_CHECK_HEADERS([mach/mach_time.h]) AC_CHECK_FUNCS([mach_timebase_info]) fi if test "$my_htop_platform" = "dragonflybsd"; then AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"]) fi if test "$my_htop_platform" = "freebsd"; then AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"]) fi if test "$my_htop_platform" = "openbsd"; then AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"]) fi if test "$my_htop_platform" = "solaris"; then AC_CHECK_LIB([kstat], [kstat_open], [], [missing_libraries="$missing_libraries libkstat"]) AC_CHECK_LIB([proc], [Pgrab_error], [], [missing_libraries="$missing_libraries libproc"]) AC_CHECK_LIB([malloc], [free], [], [missing_libraries="$missing_libraries libmalloc"]) fi AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no") if test "x$enable_hwloc" = xyes; then AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"]) AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"]) fi AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, conflicts with hwloc])], ,enable_linux_affinity="check") if test "x$enable_linux_affinity" = xcheck; then if test "x$enable_hwloc" = xyes; then enable_linux_affinity=no else AC_MSG_CHECKING([for usable sched_setaffinity]) AC_RUN_IFELSE([ AC_LANG_PROGRAM([[ #include #include static cpu_set_t cpuset; ]], [[ CPU_ZERO(&cpuset); sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); if (errno == ENOSYS) return 1; ]])], [enable_linux_affinity=yes AC_MSG_RESULT([yes])], [enable_linux_affinity=no AC_MSG_RESULT([no])], [AC_MSG_RESULT([yes (assumed while cross compiling)])]) fi fi if test "x$enable_linux_affinity" = xyes; then AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.]) fi if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes; then AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.]) fi AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no") if test "x$enable_setuid" = xyes; then AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) fi AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no") if test "x$enable_delayacct" = xyes; then m4_ifdef([PKG_PROG_PKG_CONFIG], [ PKG_PROG_PKG_CONFIG() PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"]) PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"]) CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.]) ], [ pkg_m4_absent=1 m4_warning([configure is generated without pkg.m4. 'make dist' target will be disabled.]) AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.]) ]) fi AC_ARG_WITH(sensors, [AS_HELP_STRING([--with-sensors], [Compile with libsensors support for reading temperature data. Only requires libsensors headers at compile time, at runtime libsensors is loaded via dlopen.])],, with_sensors="check") if test "x$with_sensors" = xyes; then AC_CHECK_HEADERS([sensors/sensors.h], [], [missing_headers="$missing_headers $ac_header"]) elif test "x$with_sensors" = xcheck; then with_sensors=yes AC_CHECK_HEADERS([sensors/sensors.h], [], [with_sensors=no]) fi AM_CFLAGS="\ -Wall\ -Wcast-align\ -Wcast-qual\ -Wextra\ -Wfloat-equal\ -Wformat=2\ -Wmissing-format-attribute\ -Wmissing-noreturn\ -Wmissing-prototypes\ -Wpointer-arith\ -Wshadow\ -Wstrict-prototypes\ -Wundef\ -Wunused\ -Wwrite-strings" AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [AM_CFLAGS="$AM_CFLAGS -Wnull-dereference"], , [-Werror]) AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat warnings as errors (default: warnings are not errors)])], [enable_werror="$enableval"], [enable_werror=no]) AS_IF([test "x$enable_werror" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -Werror"]) AC_SUBST([AM_CFLAGS]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [Enable asserts (default: asserts are disabled)])], [enable_debug="$enableval"], [enable_debug=no]) AS_IF([test "x$enable_debug" = "xyes"], , [AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"]) AC_SUBST([AM_CPPFLAGS]) # Bail out on errors. # ---------------------------------------------------------------------- if test ! -z "$missing_libraries"; then AC_MSG_ERROR([missing libraries: $missing_libraries]) fi if test ! -z "$missing_headers"; then AC_MSG_ERROR([missing headers: $missing_headers]) fi AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2019 Hisham Muhammad. (C) 2020 htop dev team.", [Copyright message.]) # We're done, let's go! # ---------------------------------------------------------------------- AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux]) AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd]) AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd]) AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd]) AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin]) AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris]) AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported]) AC_SUBST(my_htop_platform) AC_CONFIG_FILES([Makefile htop.1]) AC_OUTPUT if test "$my_htop_platform" = "unsupported"; then echo "" echo "****************************************************************" echo "WARNING! This platform is not currently supported by htop." echo "" echo "The code will build, but it will produce a dummy version of htop" echo "which shows no processes, using the files from the unsupported/" echo "directory. This is meant to be a skeleton, to be used as a" echo "starting point if you are porting htop to a new platform." echo "****************************************************************" echo "" fi AC_MSG_RESULT([ ${PACKAGE_NAME} ${VERSION} platform: $my_htop_platform (Linux) proc directory: $PROCDIR (Linux) openvz: $enable_openvz (Linux) vserver: $enable_vserver (Linux) ancient vserver: $enable_ancient_vserver (Linux) affinity: $enable_linux_affinity (Linux) delay accounting: $enable_delayacct (Linux) sensors: $with_sensors unicode: $enable_unicode hwloc: $enable_hwloc setuid: $enable_setuid debug: $enable_debug ]) htop-3.0.5/darwin/000077500000000000000000000000001377712513700137725ustar00rootroot00000000000000htop-3.0.5/darwin/DarwinProcess.c000066400000000000000000000335161377712513700167310ustar00rootroot00000000000000/* htop - DarwinProcess.c (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "DarwinProcess.h" #include #include #include #include #include #include "CRT.h" #include "Platform.h" #include "Process.h" const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, [TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, }, }; Process* DarwinProcess_new(const Settings* settings) { DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess)); Object_setClass(this, Class(DarwinProcess)); Process_init(&this->super, settings); this->utime = 0; this->stime = 0; this->taskAccess = true; this->translated = false; return &this->super; } void Process_delete(Object* cast) { DarwinProcess* this = (DarwinProcess*) cast; Process_done(&this->super); // free platform-specific fields here free(this); } static void DarwinProcess_writeField(const Process* this, RichString* str, ProcessField field) { const DarwinProcess* dp = (const DarwinProcess*) this; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; switch (field) { // add Platform-specific fields here case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break; default: Process_writeField(this, str, field); return; } RichString_appendWide(str, attr, buffer); } static int DarwinProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { const DarwinProcess* p1 = (const DarwinProcess*)v1; const DarwinProcess* p2 = (const DarwinProcess*)v2; switch (key) { // add Platform-specific fields here case TRANSLATED: return SPACESHIP_NUMBER(p1->translated, p2->translated); default: return Process_compareByKey_Base(v1, v2, key); } } bool Process_isThread(const Process* this) { (void) this; return false; } static char* DarwinProcess_getCmdLine(const struct kinfo_proc* k, int* basenameOffset) { /* This function is from the old Mac version of htop. Originally from ps? */ int mib[3], argmax, nargs, c = 0; size_t size; char *procargs, *sp, *np, *cp, *retval; /* Get the maximum process arguments size. */ mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; size = sizeof( argmax ); if ( sysctl( mib, 2, &argmax, &size, NULL, 0 ) == -1 ) { goto ERROR_A; } /* Allocate space for the arguments. */ procargs = (char*)xMalloc(argmax); if ( procargs == NULL ) { goto ERROR_A; } /* * Make a sysctl() call to get the raw argument space of the process. * The layout is documented in start.s, which is part of the Csu * project. In summary, it looks like: * * /---------------\ 0x00000000 * : : * : : * |---------------| * | argc | * |---------------| * | arg[0] | * |---------------| * : : * : : * |---------------| * | arg[argc - 1] | * |---------------| * | 0 | * |---------------| * | env[0] | * |---------------| * : : * : : * |---------------| * | env[n] | * |---------------| * | 0 | * |---------------| <-- Beginning of data returned by sysctl() is here. * | argc | * |---------------| * | exec_path | * |:::::::::::::::| * | | * | String area. | * | | * |---------------| <-- Top of stack. * : : * : : * \---------------/ 0xffffffff */ mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; mib[2] = k->kp_proc.p_pid; size = ( size_t ) argmax; if ( sysctl( mib, 3, procargs, &size, NULL, 0 ) == -1 ) { goto ERROR_B; } memcpy( &nargs, procargs, sizeof( nargs ) ); cp = procargs + sizeof( nargs ); /* Skip the saved exec_path. */ for ( ; cp < &procargs[size]; cp++ ) { if ( *cp == '\0' ) { /* End of exec_path reached. */ break; } } if ( cp == &procargs[size] ) { goto ERROR_B; } /* Skip trailing '\0' characters. */ for ( ; cp < &procargs[size]; cp++ ) { if ( *cp != '\0' ) { /* Beginning of first argument reached. */ break; } } if ( cp == &procargs[size] ) { goto ERROR_B; } /* Save where the argv[0] string starts. */ sp = cp; *basenameOffset = 0; for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) { if ( *cp == '\0' ) { c++; if ( np != NULL ) { /* Convert previous '\0'. */ *np = ' '; } /* Note location of current '\0'. */ np = cp; if (*basenameOffset == 0) { *basenameOffset = cp - sp; } } } /* * sp points to the beginning of the arguments/environment string, and * np should point to the '\0' terminator for the string. */ if ( np == NULL || np == sp ) { /* Empty or unterminated string. */ goto ERROR_B; } if (*basenameOffset == 0) { *basenameOffset = np - sp; } /* Make a copy of the string. */ retval = xStrdup(sp); /* Clean up. */ free( procargs ); return retval; ERROR_B: free( procargs ); ERROR_A: retval = xStrdup(k->kp_proc.p_comm); *basenameOffset = strlen(retval); return retval; } void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) { DarwinProcess* dp = (DarwinProcess*)proc; const struct extern_proc* ep = &ps->kp_proc; /* UNSET HERE : * * processor * user (set at ProcessList level) * nlwp * percent_cpu * percent_mem * m_virt * m_resident * minflt * majflt */ /* First, the "immutable" parts */ if (!exists) { /* Set the PID/PGID/etc. */ proc->pid = ep->p_pid; proc->ppid = ps->kp_eproc.e_ppid; proc->pgrp = ps->kp_eproc.e_pgid; proc->session = 0; /* TODO Get the session id */ proc->tpgid = ps->kp_eproc.e_tpgid; proc->tgid = proc->pid; proc->st_uid = ps->kp_eproc.e_ucred.cr_uid; /* e_tdev = (major << 24) | (minor & 0xffffff) */ /* e_tdev == -1 for "no device" */ proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */ dp->translated = ps->kp_proc.p_flag & P_TRANSLATED; proc->starttime_ctime = ep->p_starttime.tv_sec; Process_fillStarttimeBuffer(proc); proc->comm = DarwinProcess_getCmdLine(ps, &(proc->basenameOffset)); } /* Mutable information */ proc->nice = ep->p_nice; proc->priority = ep->p_priority; proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?'; /* Make sure the updated flag is set */ proc->updated = true; } void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval) { struct proc_taskinfo pti; if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { uint64_t total_existing_time = proc->stime + proc->utime; uint64_t total_current_time = pti.pti_total_system + pti.pti_total_user; if (total_existing_time && 1E-6 < time_interval) { uint64_t total_time_diff = total_current_time - total_existing_time; proc->super.percent_cpu = ((double)total_time_diff / time_interval) * 100.0; } else { proc->super.percent_cpu = 0.0; } proc->super.time = total_current_time / 10000000; proc->super.nlwp = pti.pti_threadnum; proc->super.m_virt = pti.pti_virtual_size / ONE_K; proc->super.m_resident = pti.pti_resident_size / ONE_K; proc->super.majflt = pti.pti_faults; proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 / (double)dpl->host_info.max_mem; proc->stime = pti.pti_total_system; proc->utime = pti.pti_total_user; dpl->super.kernelThreads += 0; /*pti.pti_threads_system;*/ dpl->super.userlandThreads += pti.pti_threadnum; /*pti.pti_threads_user;*/ dpl->super.totalTasks += pti.pti_threadnum; dpl->super.runningTasks += pti.pti_numrunning; } } /* * Scan threads for process state information. * Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread * and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c */ void DarwinProcess_scanThreads(DarwinProcess* dp) { Process* proc = (Process*) dp; kern_return_t ret; if (!dp->taskAccess) { return; } if (proc->state == 'Z') { return; } task_t port; ret = task_for_pid(mach_task_self(), proc->pid, &port); if (ret != KERN_SUCCESS) { dp->taskAccess = false; return; } task_info_data_t tinfo; mach_msg_type_number_t task_info_count = TASK_INFO_MAX; ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count); if (ret != KERN_SUCCESS) { dp->taskAccess = false; return; } thread_array_t thread_list; mach_msg_type_number_t thread_count; ret = task_threads(port, &thread_list, &thread_count); if (ret != KERN_SUCCESS) { dp->taskAccess = false; mach_port_deallocate(mach_task_self(), port); return; } integer_t run_state = 999; for (unsigned int i = 0; i < thread_count; i++) { thread_info_data_t thinfo; mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT; ret = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count); if (ret == KERN_SUCCESS) { thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo; if (basic_info_th->run_state < run_state) { run_state = basic_info_th->run_state; } mach_port_deallocate(mach_task_self(), thread_list[i]); } } vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count); mach_port_deallocate(mach_task_self(), port); char state = '?'; switch (run_state) { case TH_STATE_RUNNING: state = 'R'; break; case TH_STATE_STOPPED: state = 'S'; break; case TH_STATE_WAITING: state = 'W'; break; case TH_STATE_UNINTERRUPTIBLE: state = 'U'; break; case TH_STATE_HALTED: state = 'H'; break; } proc->state = state; } const ProcessClass DarwinProcess_class = { .super = { .extends = Class(Process), .display = Process_display, .delete = Process_delete, .compare = Process_compare }, .writeField = DarwinProcess_writeField, .compareByKey = DarwinProcess_compareByKey, }; htop-3.0.5/darwin/DarwinProcess.h000066400000000000000000000022661377712513700167340ustar00rootroot00000000000000#ifndef HEADER_DarwinProcess #define HEADER_DarwinProcess /* htop - DarwinProcess.h (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "DarwinProcessList.h" #include "Settings.h" typedef struct DarwinProcess_ { Process super; uint64_t utime; uint64_t stime; bool taskAccess; bool translated; } DarwinProcess; extern const ProcessClass DarwinProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; Process* DarwinProcess_new(const Settings* settings); void Process_delete(Object* cast); bool Process_isThread(const Process* this); void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists); void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval); /* * Scan threads for process state information. * Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread * and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c */ void DarwinProcess_scanThreads(DarwinProcess* dp); #endif htop-3.0.5/darwin/DarwinProcessList.c000066400000000000000000000154201377712513700175570ustar00rootroot00000000000000/* htop - DarwinProcessList.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "DarwinProcessList.h" #include #include #include #include #include #include #include #include #include #include #include "CRT.h" #include "DarwinProcess.h" #include "Platform.h" #include "ProcessList.h" #include "zfs/openzfs_sysctl.h" #include "zfs/ZfsArcStats.h" struct kern { short int version[3]; }; static void GetKernelVersion(struct kern* k) { static short int version_[3] = {0}; if (!version_[0]) { // just in case it fails someday version_[0] = version_[1] = version_[2] = -1; char str[256] = {0}; size_t size = sizeof(str); int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0); if (ret == 0) { sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]); } } memcpy(k->version, version_, sizeof(version_)); } /* compare the given os version with the one installed returns: 0 if equals the installed version positive value if less than the installed version negative value if more than the installed version */ static int CompareKernelVersion(short int major, short int minor, short int component) { struct kern k; GetKernelVersion(&k); if (k.version[0] != major) { return k.version[0] - major; } if (k.version[1] != minor) { return k.version[1] - minor; } if (k.version[2] != component) { return k.version[2] - component; } return 0; } static void ProcessList_getHostInfo(host_basic_info_data_t* p) { mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { CRT_fatalError("Unable to retrieve host info"); } } static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) { if (NULL != p && NULL != *p) { if (0 != munmap(*p, vm_page_size)) { CRT_fatalError("Unable to free old CPU load information"); } *p = NULL; } } static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) { mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t); unsigned cpu_count; // TODO Improving the accuracy of the load counts woule help a lot. if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) { CRT_fatalError("Unable to retrieve CPU info"); } return cpu_count; } static void ProcessList_getVMStats(vm_statistics_t p) { mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) { CRT_fatalError("Unable to retrieve VM statistics"); } } static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; struct kinfo_proc* processes = NULL; for (int retry = 3; retry > 0; retry--) { size_t size = 0; if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) { CRT_fatalError("Unable to get size of kproc_infos"); } processes = xRealloc(processes, size); if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) { *count = size / sizeof(struct kinfo_proc); return processes; } if (errno != ENOMEM) break; } CRT_fatalError("Unable to get kinfo_procs"); } ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId); /* Initialize the CPU information */ this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load); ProcessList_getHostInfo(&this->host_info); ProcessList_allocateCPULoadInfo(&this->curr_load); /* Initialize the VM statistics */ ProcessList_getVMStats(&this->vm_stats); /* Initialize the ZFS kstats, if zfs.kext loaded */ openzfs_sysctl_init(&this->zfs); openzfs_sysctl_updateArcStats(&this->zfs); this->super.kernelThreads = 0; this->super.userlandThreads = 0; this->super.totalTasks = 0; this->super.runningTasks = 0; return &this->super; } void ProcessList_delete(ProcessList* this) { ProcessList_done(this); free(this); } static double ticksToNanoseconds(const double ticks) { const double nanos_per_sec = 1e9; return (ticks / Platform_timebaseToNS) * (nanos_per_sec / (double) Platform_clockTicksPerSec); } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { DarwinProcessList* dpl = (DarwinProcessList*)super; bool preExisting = true; struct kinfo_proc* ps; size_t count; DarwinProcess* proc; /* Update the global data (CPU times and VM stats) */ ProcessList_freeCPULoadInfo(&dpl->prev_load); dpl->prev_load = dpl->curr_load; ProcessList_allocateCPULoadInfo(&dpl->curr_load); ProcessList_getVMStats(&dpl->vm_stats); openzfs_sysctl_updateArcStats(&dpl->zfs); // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { return; } /* Get the time difference */ dpl->global_diff = 0; for (int i = 0; i < dpl->super.cpuCount; ++i) { for (size_t j = 0; j < CPU_STATE_MAX; ++j) { dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; } } const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount; /* Clear the thread counts */ super->kernelThreads = 0; super->userlandThreads = 0; super->totalTasks = 0; super->runningTasks = 0; /* We use kinfo_procs for initial data since : * * 1) They always succeed. * 2) The contain the basic information. * * We attempt to fill-in additional information with libproc. */ ps = ProcessList_getKInfoProcs(&count); for (size_t i = 0; i < count; ++i) { proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new); DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting); DarwinProcess_setFromLibprocPidinfo(proc, dpl, time_interval); // Disabled for High Sierra due to bug in macOS High Sierra bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0); if (isScanThreadSupported) { DarwinProcess_scanThreads(proc); } super->totalTasks += 1; if (!preExisting) { proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid); ProcessList_add(super, &proc->super); } } free(ps); } htop-3.0.5/darwin/DarwinProcessList.h000066400000000000000000000015751377712513700175720ustar00rootroot00000000000000#ifndef HEADER_DarwinProcessList #define HEADER_DarwinProcessList /* htop - DarwinProcessList.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include "ProcessList.h" #include "zfs/ZfsArcStats.h" typedef struct DarwinProcessList_ { ProcessList super; host_basic_info_data_t host_info; vm_statistics_data_t vm_stats; processor_cpu_load_info_t prev_load; processor_cpu_load_info_t curr_load; uint64_t kernel_threads; uint64_t user_threads; uint64_t global_diff; ZfsArcStats zfs; } DarwinProcessList; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); #endif htop-3.0.5/darwin/Platform.c000066400000000000000000000270301377712513700157240ustar00rootroot00000000000000/* htop - darwin/Platform.c (C) 2014 Hisham H. Muhammad (C) 2015 David C. Hunt Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Platform.h" #include #include #include #include #include #include #include #include #include "ClockMeter.h" #include "CPUMeter.h" #include "CRT.h" #include "DarwinProcessList.h" #include "DateMeter.h" #include "DateTimeMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" #include "MemoryMeter.h" #include "ProcessLocksScreen.h" #include "SwapMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" #ifdef HAVE_MACH_MACH_TIME_H #include #endif const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, { .name = " 2 SIGINT", .number = 2 }, { .name = " 3 SIGQUIT", .number = 3 }, { .name = " 4 SIGILL", .number = 4 }, { .name = " 5 SIGTRAP", .number = 5 }, { .name = " 6 SIGABRT", .number = 6 }, { .name = " 6 SIGIOT", .number = 6 }, { .name = " 7 SIGEMT", .number = 7 }, { .name = " 8 SIGFPE", .number = 8 }, { .name = " 9 SIGKILL", .number = 9 }, { .name = "10 SIGBUS", .number = 10 }, { .name = "11 SIGSEGV", .number = 11 }, { .name = "12 SIGSYS", .number = 12 }, { .name = "13 SIGPIPE", .number = 13 }, { .name = "14 SIGALRM", .number = 14 }, { .name = "15 SIGTERM", .number = 15 }, { .name = "16 SIGURG", .number = 16 }, { .name = "17 SIGSTOP", .number = 17 }, { .name = "18 SIGTSTP", .number = 18 }, { .name = "19 SIGCONT", .number = 19 }, { .name = "20 SIGCHLD", .number = 20 }, { .name = "21 SIGTTIN", .number = 21 }, { .name = "22 SIGTTOU", .number = 22 }, { .name = "23 SIGIO", .number = 23 }, { .name = "24 SIGXCPU", .number = 24 }, { .name = "25 SIGXFSZ", .number = 25 }, { .name = "26 SIGVTALRM", .number = 26 }, { .name = "27 SIGPROF", .number = 27 }, { .name = "28 SIGWINCH", .number = 28 }, { .name = "29 SIGINFO", .number = 29 }, { .name = "30 SIGUSR1", .number = 30 }, { .name = "31 SIGUSR2", .number = 31 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, &LoadAverageMeter_class, &LoadMeter_class, &MemoryMeter_class, &SwapMeter_class, &TasksMeter_class, &BatteryMeter_class, &HostnameMeter_class, &UptimeMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, &AllCPUs4Meter_class, &AllCPUs8Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, &LeftCPUs4Meter_class, &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, &BlankMeter_class, NULL }; double Platform_timebaseToNS = 1.0; long Platform_clockTicksPerSec = -1; void Platform_init(void) { // Check if we can determine the timebase used on this system. // If the API is unavailable assume we get our timebase in nanoseconds. #ifdef HAVE_MACH_TIMEBASE_INFO mach_timebase_info_data_t info; mach_timebase_info(&info); Platform_timebaseToNS = (double)info.numer / (double)info.denom; #else Platform_timebaseToNS = 1.0; #endif // Determine the number of clock ticks per second errno = 0; Platform_clockTicksPerSec = sysconf(_SC_CLK_TCK); if (errno || Platform_clockTicksPerSec < 1) { CRT_fatalError("Unable to retrieve clock tick rate"); } } void Platform_done(void) { /* no platform-specific cleanup needed */ } void Platform_setBindings(Htop_Action* keys) { /* no platform-specific key bindings */ (void) keys; } int Platform_getUptime() { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); int err = sysctl(mib, 2, &bootTime, &size, NULL, 0); if (err) { return -1; } gettimeofday(&currTime, NULL); return (int) difftime(currTime.tv_sec, bootTime.tv_sec); } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { double results[3]; if (3 == getloadavg(results, 3)) { *one = results[0]; *five = results[1]; *fifteen = results[2]; } else { *one = 0; *five = 0; *fifteen = 0; } } int Platform_getMaxPid() { /* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */ return 99999; } static double Platform_setCPUAverageValues(Meter* mtr) { const ProcessList* dpl = mtr->pl; int cpus = dpl->cpuCount; double sumNice = 0.0; double sumNormal = 0.0; double sumKernel = 0.0; double sumPercent = 0.0; for (int i = 1; i <= cpus; i++) { sumPercent += Platform_setCPUValues(mtr, i); sumNice += mtr->values[CPU_METER_NICE]; sumNormal += mtr->values[CPU_METER_NORMAL]; sumKernel += mtr->values[CPU_METER_KERNEL]; } mtr->values[CPU_METER_NICE] = sumNice / cpus; mtr->values[CPU_METER_NORMAL] = sumNormal / cpus; mtr->values[CPU_METER_KERNEL] = sumKernel / cpus; return sumPercent / cpus; } double Platform_setCPUValues(Meter* mtr, int cpu) { if (cpu == 0) { return Platform_setCPUAverageValues(mtr); } const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl; const processor_cpu_load_info_t prev = &dpl->prev_load[cpu - 1]; const processor_cpu_load_info_t curr = &dpl->curr_load[cpu - 1]; double total = 0; /* Take the sums */ for (size_t i = 0; i < CPU_STATE_MAX; ++i) { total += (double)curr->cpu_ticks[i] - (double)prev->cpu_ticks[i]; } mtr->values[CPU_METER_NICE] = ((double)curr->cpu_ticks[CPU_STATE_NICE] - (double)prev->cpu_ticks[CPU_STATE_NICE]) * 100.0 / total; mtr->values[CPU_METER_NORMAL] = ((double)curr->cpu_ticks[CPU_STATE_USER] - (double)prev->cpu_ticks[CPU_STATE_USER]) * 100.0 / total; mtr->values[CPU_METER_KERNEL] = ((double)curr->cpu_ticks[CPU_STATE_SYSTEM] - (double)prev->cpu_ticks[CPU_STATE_SYSTEM]) * 100.0 / total; mtr->curItems = 3; /* Convert to percent and return */ total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL]; mtr->values[CPU_METER_FREQUENCY] = NAN; mtr->values[CPU_METER_TEMPERATURE] = NAN; return CLAMP(total, 0.0, 100.0); } void Platform_setMemoryValues(Meter* mtr) { const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl; const struct vm_statistics* vm = &dpl->vm_stats; double page_K = (double)vm_page_size / (double)1024; mtr->total = dpl->host_info.max_mem / 1024; mtr->values[0] = (double)(vm->active_count + vm->wire_count) * page_K; mtr->values[1] = (double)vm->purgeable_count * page_K; mtr->values[2] = (double)vm->inactive_count * page_K; } void Platform_setSwapValues(Meter* mtr) { int mib[2] = {CTL_VM, VM_SWAPUSAGE}; struct xsw_usage swapused; size_t swlen = sizeof(swapused); sysctl(mib, 2, &swapused, &swlen, NULL, 0); mtr->total = swapused.xsu_total / 1024; mtr->values[0] = swapused.xsu_used / 1024; } void Platform_setZfsArcValues(Meter* this) { const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl; ZfsArcMeter_readStats(this, &(dpl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl; ZfsCompressedArcMeter_readStats(this, &(dpl->zfs)); } char* Platform_getProcessEnv(pid_t pid) { char* env = NULL; int argmax; size_t bufsz = sizeof(argmax); int mib[3]; mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; if (sysctl(mib, 2, &argmax, &bufsz, 0, 0) == 0) { char* buf = xMalloc(argmax); if (buf) { mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; mib[2] = pid; bufsz = argmax; if (sysctl(mib, 3, buf, &bufsz, 0, 0) == 0) { if (bufsz > sizeof(int)) { char *p = buf, *endp = buf + bufsz; int argc = *(int*)(void*)p; p += sizeof(int); // skip exe p = strchr(p, 0) + 1; // skip padding while (!*p && p < endp) ++p; // skip argv for (; argc-- && p < endp; p = strrchr(p, 0) + 1) ; // skip padding while (!*p && p < endp) ++p; size_t size = endp - p; env = xMalloc(size + 2); memcpy(env, p, size); env[size] = 0; env[size + 1] = 0; } } free(buf); } } return env; } char* Platform_getInodeFilename(pid_t pid, ino_t inode) { (void)pid; (void)inode; return NULL; } FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; } bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; return false; } bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted) { // TODO *bytesReceived = 0; *packetsReceived = 0; *bytesTransmitted = 0; *packetsTransmitted = 0; return false; } void Platform_getBattery(double* percent, ACPresence* isOnAC) { CFTypeRef power_sources = IOPSCopyPowerSourcesInfo(); *percent = NAN; *isOnAC = AC_ERROR; if (NULL == power_sources) return; CFArrayRef list = IOPSCopyPowerSourcesList(power_sources); CFDictionaryRef battery = NULL; int len; if (NULL == list) { CFRelease(power_sources); return; } len = CFArrayGetCount(list); /* Get the battery */ for (int i = 0; i < len && battery == NULL; ++i) { CFDictionaryRef candidate = IOPSGetPowerSourceDescription(power_sources, CFArrayGetValueAtIndex(list, i)); /* GET rule */ CFStringRef type; if (NULL != candidate) { type = (CFStringRef) CFDictionaryGetValue(candidate, CFSTR(kIOPSTransportTypeKey)); /* GET rule */ if (kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPSInternalType), 0)) { CFRetain(candidate); battery = candidate; } } } if (NULL != battery) { /* Determine the AC state */ CFStringRef power_state = CFDictionaryGetValue(battery, CFSTR(kIOPSPowerSourceStateKey)); *isOnAC = (kCFCompareEqualTo == CFStringCompare(power_state, CFSTR(kIOPSACPowerValue), 0)) ? AC_PRESENT : AC_ABSENT; /* Get the percentage remaining */ double current; double max; CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey)), kCFNumberDoubleType, ¤t); CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey)), kCFNumberDoubleType, &max); *percent = (current * 100.0) / max; CFRelease(battery); } CFRelease(list); CFRelease(power_sources); } htop-3.0.5/darwin/Platform.h000066400000000000000000000033211377712513700157260ustar00rootroot00000000000000#ifndef HEADER_Platform #define HEADER_Platform /* htop - darwin/Platform.h (C) 2014 Hisham H. Muhammad (C) 2015 David C. Hunt Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include "Action.h" #include "BatteryMeter.h" #include "CPUMeter.h" #include "DarwinProcess.h" #include "DiskIOMeter.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" extern const ProcessField Platform_defaultFields[]; extern double Platform_timebaseToNS; extern long Platform_clockTicksPerSec; extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; extern const MeterClass* const Platform_meterTypes[]; void Platform_init(void); void Platform_done(void); void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(void); double Platform_setCPUValues(Meter* mtr, int cpu); void Platform_setMemoryValues(Meter* mtr); void Platform_setSwapValues(Meter* mtr); void Platform_setZfsArcValues(Meter* this); void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); char* Platform_getInodeFilename(pid_t pid, ino_t inode); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted); void Platform_getBattery(double *percent, ACPresence *isOnAC); #endif htop-3.0.5/darwin/ProcessField.h000066400000000000000000000005421377712513700165260ustar00rootroot00000000000000#ifndef HEADER_DarwinProcessField #define HEADER_DarwinProcessField /* htop - darwin/ProcessField.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define PLATFORM_PROCESS_FIELDS \ TRANSLATED = 100, \ // End of list #endif /* HEADER_DarwinProcessField */ htop-3.0.5/docs/000077500000000000000000000000001377712513700134365ustar00rootroot00000000000000htop-3.0.5/docs/images/000077500000000000000000000000001377712513700147035ustar00rootroot00000000000000htop-3.0.5/docs/images/screenshot.png000066400000000000000000002360511377712513700175750ustar00rootroot00000000000000‰PNG  IHDRĎ:Ö,¸%»zTXtRaw profile type exifxÚÍśY’%·‘E˙±Š^f–ѬwĐËďsÉ"%QeÖ]YŬzů^÷;8<čÎ˙ü÷u˙ĹŻfľą\¬Ő^«çWîąÇÁ_š˙ý×ůůţ˝|~˙ýíWřůopüWţ~E^I|Oß?-˙Ľž~^˙íBő×w.ô'?ĺď>~Ý?ţńĆ6~Ý8ţíR ţŹżÚďîÝíŢóÍnäĘ2ÔoRß-Üo—ፓUJďc•/ăOáďöľ:_ÍżBöŰ/?ůZˇ‡’ż!‡íÂ7ś°ůľÂbŚ9žh|ŹqĹô^kÉbŹ+ůRÖW¸ŃRO;1…´âI)e—⯱„wßţî·BăÎ;đÖ¸Xŕ#˙ňËý»7ü•Ż{—gŤBĐb˛őáŰŕµAËô_ŢƆ„űłoĺ-đo_ż~ą?llbË[ćƇźß%f żÇVzx_áű_Á¶v-ľ(ÉÜ»0Ř_C*ˇo1Z9ĹĆ FSŽ“ĄÄÍ cN©˛7Ť8âŢ|ĆÂ{o,ń{ťTaJŞÉŘ›ž›•s!~,7bh”Tr)Ą+­ô2\M5×RkµŞś–,[±jfÍşŤ–ZnĄŐf­µŢFŹ=‘’Ą×n˝őŢÇŕž#»QźĽcŚgšy–Y§Í6ű‹đYy•U—­¶ú;î´ó.»nŰm÷=N8„’;ů”SŹťvú—X»éć[n˝vŰíwüÚµź]ý‡Ż˙`×ÂϮŷSzźýÚ5^5Ó…Ţ%‚p¦hĎر;nÚ:jĎ| 9GíśöĚ÷HV”Č ‹öf?\¨la>!–~íÝď;÷—÷ͱÖ˙nßâ_Ů9§­ű?ŘąčNú»}ű“]ŰBÂővěËB­©OdźV)›­łsZžŰřu€ÎťNd,m¶˘‰ú^Y•ĺklšÜ÷¶4j&9ĆŞ}„Ľ:8jqm†zNuOŢWIKłqóIť\2oŘc__ç΄…M[ą®bWËSl[çŇ78†´HąžĘüęĽL#‡Űjg2žq|ŕÓ€Mţv×!'ą hĽ…ß XumÄĆÄňÝďěiŤk;Zą×t2îĆň”´Č¶¦|kíf‡ëĄŤ o“ťHŚnÝQŞ §Íz Č7{ óôÝ›‹ľ­Ř÷*íćŔŰű¨sŐ&[KxqÓ•Â&×*uĺ~ŢĐĘ ýňąĚŃřkśîpaď‹qŰ>Ť%mŚk«‡ŐvĚ­*Vů­ c•fn––…ÝŚ%Ú«]Ž”´˝ÖÖ’D¦ö-Ëúoľ~­––™I”˝;snńVÝv&ĽŹß)Nn׬ÔĚĎ‚ěDGT¶Çak¦^o·Sîşý°ýuBGÍÇ{:ˇ}ű6h±4¨y÷QöŠŮöHőěD|w–P ^Ç&×,ŰĎČťIŘy¶1±sJľ„ü8eĎF6p§qěîAşĺ¸ňMyĹ2cť3śb~ {KKĽßBFo…鎽´Ô™(\őŚ3ŇŽä]Č­ŹhW r„EZ˛ëîMHřwóÄÂt;U‹kŘą‹iěmdz_Ůú=Go/ hĚÚc·'Ö«5Í[VŇĎ]ŃÂ&–ć-ŚÜş $zŞ÷4Č6 věQÚ!_ň\“ ÇŮ+íŽ DQśŰgC`”÷,b¸łv3r÷ó&Đ…×3rSřçvcí„~OŃúv÷»2é9ŘëË ů4&°z–cź‘ą|šsŚ4‹MxłZ úť+Üą‰ě©ˇaeXč…11-pö´už™V&śXLťyω|z;Mj¦ëŇč=Ö Ń\PfDĐ:šŚ•理µ:jéŢy2RJ8 ě`cÖŽ ×eIŚÍ]DËŢAiĽ·CÜ;1•śWŕmÇ&´ŃđF.ÝĐ™Ë Ă dő$ę“ë Z)aމÔcJÜcnĎÖ6?Ń‹“»mH žh ^8„ˇÁwë’!Ľ‹y˛Â!™‹mľ6VÚźĐ٬vµ‹Ęž™¶\NĽüe­dĚĐj$Üžł3a¨uN(€É…­ŔHĹ“5kř˝!«Hę9ú>Ú5‚%ßP}$Í»#Ż,÷©Ş -Ídß5í¶JzŘs”Î׼®—ŞÚL,g«yô÷En×*×ěÁé1÷ŐťQđsľ3$|{;čĐ= HŘ?§›\rK H… ň\·Î$`°:qš6ÂŞxb8č¨?K”€É%<šgűš!dÎÖźKn#l"ˇ¨4˘Łµc=r˛ Î´ťüşJ`·űuÜť=őäî™R&Ř$?'ń9¶ 5ű~„ęp4‚ ÔˇÄ'ň´4iSŐ®‘B ÜI^pQ±'*xM€5É8 IPtݨĚü$d5ŘxÇî ţÉ‚°NŠW*ÁłřŠXDžŕ,Č• Dq”MWlÚ]q‚Ůą".X’‚?ü®Ć; FÄHn±Ô6˘q´Č •ďűňýŁĄäŹ5ďR™€eęŕő¸IÁÄŢ đ‘-\˘uvŕűĐkÂüÜî°°2»“zÁ#°§ˇm´0á%±N×.Ŕń\ÝŕĐL2>ě ű™guI' ź¸ťîzÂcŁ5. ź/‰p÷b¦ÉV:39 p˘ŕJqě ť±ďZw«;Ű! ’KĹčI……ÄćÜ„ţy@‚mT –ůFÄąwĎ`ă]”LŐß<ńÁV3ŻűÄdpsá’üFź˝·!~!×ƶyß­ß}ŔŘ8îŽŔôdü˝(˙y|Éů»zA+ŘćˇM }„ęÁ*+Ó\µJ\^d˛˛((„näcú°âE‹ŽK2áŞăá)–34.˛„{𔡗PD0᎒‰±A=y9"•Kb顰‡UFaĄ1k>đa +‘Ç.ř×ϨQ&ĺr;oxÇ,B'X$-ÓR*"<Áuo{Żç»řÍŹVR{໲é˘^eéa–ăKÉÇx±"O6:ťěýÎ1uYă˛C« ¬±>eŰ®Z_b9WŢE.Ű$Ăăj(î×^‚c†˛ř;“TMbQjY[…Ä[ŔĚŘL6Ú¨V”O•;B|;Ë:M‰–Ł-îäEäÎL1óŮČ'—<iĚ=Ńdpsĺs¶¦dˇÜ{±RáÚXŕ0ܕķ1atš{ô×…vXĄF>ě¦ËŤ”bş zdäZĺ%±f*RS†×‚Ţ*š§H˘mÂś5/G9ŁEČ7‹RMy 8ëä]Fâ` ś­É=PÚ• ‹@r‘¨×€ÔŤ=Xhűáŕ†Ř·Y^6 t¸ý—ăě©ŕ÷4/,&ŇńšµŔ[E E€3K•ńŐ­śTĐĹU¨Š‰¤7- j’ )I >†Ň‡Ú—tü}iÎ"‰)Á, ”11ëPJC÷˘5A0<Śo˘n„dÂa&0ď4Y؉d/MăŰvi±1 ›ËťU­Ą|5I‚"G+@lõB˝úĎŐ h„ýZcŃ;ó  qt‚Aü—KqW%v±ôѦ§Ž¬ŕ(KâDëłDĘŽ,.ĺNâÁ‹n,č^Ap=x5]ëÉŕ :FÖËÂ+ň.ś×%˝ŕ5ĄZz˙—˧T=[ăÓő[¶ÚČE¸‚pB=WĐůŽţÇâHg@™$I)ŁZşá÷NƵŔŢxíô­@Öďo!¬ď ÉÁNîCäëô[~n°ÜQ…Á‡¶đ\őĂr!P=Ôü.ü¬á6÷/ďÉËäTÖËpm\ÉÇcí‡LxďŁ÷WW¨ýÍ…tůyTĐbFôŻWV<&‚ɨš8ń¸%ÜUČU‚$K– „đ$Ň’5Ńč?¦B>T1ß&äM‘"†łl¬'®4„5röËĚ%ěŇŁú<*Ę{±ł  ´†:\€W$±r…6qËĄ3”S™Uh¸ÓÜäR€Ć”‹ółˇ¸ÚX =ů)wÍŤĄ/đŹčü÷uHŮ’ąËzl?ňząU<9÷•I0ľRAbd|<h „ p,«=oËłŚó XeGsěJÍÖ ićT0ŞBÂz$t¬$Ăá°„\H ŚvŤÉŽĘš¶YŞ,†ŞÂ +ÍOđeŞ@Ř0Ą€¸†ßó¨“ŔS<ÜŰü40dŃ÷)îľ \DwņaܰkٱŰVU„PPš\0-h• ;™N»îDFÎÎ`H¶+áű%°a#KPŠĚ-úęr© ˇÄŘĹ ˇ3wčE"ÝKjĽ… dIS?¸ÝˇáŁ{9r˘ .ę^ĺ69ݵYvhŐÍ4ĄµĘÇ FȨŠBř`Ůř—öş{ˇ:W€‹n7ůU,đŕň&éÓrEV’Ú÷ ˘"LąŔPщbÝ$”U ULĐBŚmŃǸ g”@d‚BŐ xM.•¤X:ŃZ Ů™¶FŰ8‘ĺ<2 »ŁÂśb~˝Ş†GâŃşŚ©Cěc•ĘúRň‹Śţ©H†z±d‡ŰaK1ZŘtÁϤ ×B"wI)zDĽx e˛z$áe®{ŔŮy;˘;7 Čdŕ?4Ćż¬ hăPú`±Ťµá=„%ě eÂŹ¤/Űş6§‹˛‰ t3qYH"’˝“­a:,˘yyhűˇj$’Ć3d/Ś·‚ô_J,d‘: ŕ_íÜ]q‚˙Ŕ÷ł!†]F"°1lťęž=Y„Ż*TÖč6ťŚˇqTKzo&ŃŰĐUŮvV&+şÉŕŁňęë&A‹ă2ŕ"ť +«ᬮWu…Ż@Ńŕ'–Đ»çęHŹ·ŞjŔB OČ—ě/¨=tč’„cL^u $É®ZÚô>gß9ÉÔ“öBzŔm˝`Źqć3bśŃ{ ą~…1Áĺ9pż˙”)ú{JbmL¶(€6ü ›(r#ěµ€9’Yj·ĆÚTö ‘5 b±l,~`©’b#ËYnÁUs<Ç?١g¬rSf ˛řđ9aÜá¦ü… t”Ř"âŃkš”W'ŽÍ`¨|řÄ‘¤C•nTđ+ÝęěĹ»bY>"üöDş {tY>Ö·UśGÓ˘ř3\Ű#çtŇ“rí¨Ŕ[ÉUTtQT*,ą‚}A~6bŁIëDp2‡Í?zßpŢb¸T6öN΢±+˘hťĹ°ź,´Š)č:¸Ä{AÔ«ŃO´©`R7A‘§ĽěŻşHŰš5qÇR §.ń7f0Óß0{ÚcÉř±$éŹ@A ěaŐáÂe}CÔČ˝¬ŕÍ­‡$Ë+ĐZ µT6aë1ëqWmđA@ÔÉą$2ŽłëÝ©a"T\Äîś^›`´€ÔDűšUp:X2ŕqŽĹĚČlťĂ)Îům¸Řoéś{ ^u‚…*ýaZⶉ#źpź¨1^–`N;Jäa#Eą ).PřăGÂĂŃĺ~’qć—ů,oΆď‚3ńXĹd!†Ži0¶„;~j\á:v%ŢVV]:ÁsâĚóæu%©Fćĺ"xŠLşÎ37Ψ—ÉČ=ŮjĂ úTąŠVéGŇ!VĽô˘`ňŚÄtS™ \HDß0•pL…iLé–đľëKZ`—ÓŁŽĂú‡Ľ"ŕ-‘›†°Ś8„™Ö7Š*fUŇ:§(ZÓ©$“jŘL¬üäş‚Xc™ďî:ąű{FjÂzK´ %ÂÁRżBS“ťH Gv—ßҦg j DÖŐ‰&†ďE7Éĺ Š>NRˇ)ôCčËĽűwÚ-ŔúE:Ĺ Ś¦IG@Y‡cŞx, b€ľ %rUÇ5tuâckĹM öÎ;H Ż&Ţż‡3Ůg1ŹÍęĚ]’ŔEg5`φÉ?ťáśčkł¤“;\:©>AI8?žęe `4ŇpĆÁă$ „y¦ 6ľu"|fmę+:;?RyW2EđQVćŞg& « ˘$)Bh"‚а(şJŻâ¤´`—@EťZúÂÇPÂťlđ†łťTU˝2[0-ČXŕQ€U@ňb/qbŤ›óQMK•şź˛ :ľ KGŔŃEˇ_CźßAę$VUš§yp(KĺáŚĘ;$ę †÷öL§V¨Ď·-Żúx%ŰG"¬%ĆA(ý čĂ«XO8¤BV+eŞ%Bµ úĂ”ťŹŰ®0Ů~:`P§ŹŘâN{d˘'ć=ŇŃ‚Î|ŰŞőU+‰ś‘Î'T´ŘNř5¸°ú‘+ąŕ1?  đ>Ä öeŃç„–Őö 4XĽŞäX,25 GYąÓa§Ř!WDĘ=‘y×Ŕłş<ÓSŮğܠ"cŽçČ)d-űŠV 0™áX '݇GCŔ,g`#:Jm/¤,ć‚›RŇtU®Şö˘@\Ş# óG”¨S;“\ŔąÉ2›ßR^zťÄYPc»I­ }M#H3BLuŕ„]ŞÍ«~ |ßŢܵ\]glKÇ0÷€ńĂä\T‘Aö (ŰMG(°luę A âIŘ6ŁěVCú˘ ĎîŞ#BÔîÄŃÉrŕ˛ŢĐjł@„vŢÄËŔ(Ôt€^źŔÂňЏzöĘFřt‚ýąWÝEYU[Lĺ=PďJ÷;%zT†^ĐŇ‘QIŰ€ľ”•ëď–Đ‘N#Ô“"s9Fó,rDz4pÔĐ1ĺčpĺ qKéěfL]°Ő'vĄbĐ#"Ç öŃb„ź őáy•P± ŕ‰¬OăÓŕ§caüŤe¤ŞFŃ­HŇ©a1ů'i âi®ł—Ҥ/ËÎů©5ś1ÁëŇŘC LŇ·®Ă1tKŢ^ÓÁŐa**¦ TuxÝÄó˛l|îťĚŐŽÎ'੭ă›Ŕ+jĚ"Űřx#÷2:€[’#dnóBqÜ.2Ý‹dmwÉnO&®59( {łĂ«¨ĽĂ9Đr-›0#QŇ>YĄH˛hMîHr1[®:ymi~Ęź©’<Ú?ttÉeΗxŕ†Ĺ 'fťA«đă!}Žo^—G%nŕA$AvDĺ lcfBő•<4o‚M^úë}dyˇ*éń×íŁµW‹×m#áÇsLřĚĄ±…”°Őäx`dU4dd*:DµŻ¤ěnň° @`«*n°ëą~\¤jjÚ^4śš›đ –aÇtD]1}…é •„XTÍ‹Ś%A‹Î3&Ă%Ş,âѧ°µ¶I¶2¨4Şh˘›jqŔŽ/M •UF8˘AÔa…”đ‹¸¨ sĂbe Ţ%žţÜíIQ2Ŕ˛ÄŹŠz:ad–Ş%˛‹ďp…ąú´¸ŰŇP×+ ¦2=÷ÇxM‡ `]‘3QíVTjĐů=vAż‘ńŔÓ–ą!FPĚĎXť‚®cˇş‘ĚŐcŐńO•°1W 'Đů;`FönÄ~Ć`–&]YÄ`9ć뾆‚5]ÍꨑňA€É˛6*Ă Ł‰˘«#€8ŤÉNT˝ÂeáĄhUµd…ěćĐ…M7Ô„xÔż€Â·ą——:YľËČb¦±é¤©~ăßďńí”4žNŐĎ’QuGRN•KÖbce“ŤňŽÚuxěůÄ|ÄÜíč+3Őd÷PăYpAJ†»"ył(âQE$ŕ ‰ęqúŐŘ w\Eę˝a>˘]ĺżńÚŐ.µüpdqRCÇDXBŔ3K˙ˇ+†— edŁj ľĘyU’ŘXć&‹^Č86ĎĄĘ<Ô {–‹]vÔűAgć55/µ>x5LA7MѬBđ^ph#éjˇCŔťz2^łüŠ,«˛WrҤÜ%áŔt‚łC‘®Ŕ—+`•ń&1:6jä×qŤĂó í˘â‡¨Q\hsÔ%b&`«T‚‡ß«RGÝ0cx/3‡“«ó95=˛˘=ő˝ŕŘäkwV[$şúŘÄŹ"đ–˝ ńŁŔ¸ŽĐ“¸C@ ŔŐs¤YÄ €C DÜ,™1#L·óP%Pm†| şA©¬ăÝDźźI~ݧ!übW«WÁ<¶1"HC DVmŞ«;Ů‹!Ů·oQŚĂDU™`W˙x˝{© Ňĺ¦ c«É:lű/C05’Öέrś­ÓX÷^ŻÝ”ĹH ôŐ‰č4őBĘYęµW ŠěB–ýVńţ5s€hÎă70Ýs/1óęřUuKťĺ—”®:ě1ŃűV[ĂŇ‹ě ÷”@P$7ęÓ¨.ÍŔljť…~ĆrP ŐÄśŘYŻÎęŘ׿‘UęŔ’…äŞÚUźŮĎľO.$+łńbIolWg*Mup p6ś&Z󪡢ö„<7ő”-§î3âąc÷ŮkßČKÖý™!fDÖUWĺŐaŻúŢ ř´t|5" w€u¦Łrkw: ť+S®ŕß‹4Ńt0××Y QńJúŞó.Â’­©Ş[‘E’—HÖ¨p/ŞĐadTŮÚí¬w yŐT˘H¨ň3#ű™"n`ĘhÇeRR˝YęúA†fÂACýj–}đŻjŐŚjKxŤ,TdÚrÁG˝ !íá78*ú0é˙ÎYŠ,Bxíbr $6™ŹWÖáŘé‘´ůJßµn5šĺ0™4I]^ĎWRŹSXđÍëEQ_ –Q帮đůřłŞ2…ctöăÔäńĽŞ{Q†×;>Ý uli!´2eő-™UD0,˛*m*Ş'‡EQžG)·tlLäŞ şĘ‡ČŮ37uX^°gĚÝpčVeq@ŤŔźĄ#÷ęnvőÁˇĘďż.@ĺ–ž @Ö:Ş+HŃőޞŤ¨"cA°#îu¸…|!=Đ„1“Ľ‘zĹç/Gx1™ » ]^Ňů#fu©5.l7ŐęThCq÷©°Ž| Ô:¬{řT÷Â˙$=&ÔÔiŤ*N:€OWŐÓD"šĺ±çą‚‰ŠY» ]ÔdQŐ'¶c‘Nj)óÂב)J…Á8ŚQŞŇ{A°¬Ăś öâČÎÂÔ䀸­ÂiíësAđ©ŻáâdŽăĽŽvN)˘¦ŽW%ś ¦ňę`)cQ{śEşľ4hś·{ťzŞ&wbҨŞCr:>ÖnAĺ}V4X´8ň ŻíŕĺŐ\[#Ú^sŇ;ěƉmvä:艰©ri*ĐbŮ)jÁĂ6¸u*¶Ţ9PpŞŤd˘LYŤ‡$Ú}'_~čÉ–+g2TMî’Q Ůúúŕ–cçŞJI¦ľT@ 0Ü2ôŁÍ¨_.­†?O«Ršt"„Te÷»ZKcĂ*»ůPĚÔ‘‘E,WµëÉSqČŐB¶´±("â“Ëł;·bÁŠ ú5ä´âŐ?6Uk­ę2’«¬u€3g(Ŕô ŇžęľŐSKŕ3âśm­X(=úŕžLG](ßJĐę$Š Ľóćš« YŮnRüö{jÔBĹN§Ă;lq†ďzF xŔ’`LdT)č4´D”Ěqž –"ţVq¤źŞčŘqm,„ÔUŐGĎÁű ,#Ż\I|SʶěLSmŔd*˘Ó‡č’UÂÍŠŃ=Uµşwž‚C˝Ç€!*i‚„%lKë´¨–W ěn~A%€ ;Ő‹Ž%#Ü`&8PĄh$–ŕV©ľ¨IŇǨ®őç¨Rin¨š«“Đź”ˇşjG?ĘöYX`Α},~«Ź ¨ięaEą! ß!ó–Ët}¤’_§aŐuĐ­âJCĹr“"r€€łĽż*Ő¦“é:7»†ĄŠđ_p€)­‡z@ʦ<)Ą4j"j ĺXˇŁÖ‹|eUB'ňŐú 3±â뺢ǜt€®s&›D2X§W5 ¬9»ŐŐŢš˝BR4U˛ &6t16!ëĐ~őĄ0žĐEŞ»U–RóVżY´Ş‡Y+łN€©0›: ©]/:]WW$ßćŮ$Ě{ĚĺÔ•Ő/ďŐc ¤ť†đzR~7µ@“%·!wYPUŘ-ŞWťLA«aëąJݲ=H°śtüÁVaľVl˙gßÝ?űÁúý˙÷…€ťÔ˘0">_ąÂżG!‰ă ±*ç_ŻF|Äéd)ítĄrśzSŃ2ÁŹĐŁ4scÇwđďđŔEűđ8^íB2D Y›É+Şß]©ßyL’ÎQ¦n!?UaĂŚzB ßЍžxüCаú0őČ*U6Ŕ©O‰,ÉA˝ę9B_Ťűž-9Ç«˝㬇˘ÔĂöŞčĄ„«Ľ@|ž­Ł´ŕ‘5nB Â/Ä4PŹ ×c@WŹźČŐĹ˝ĄgĹ¶ŻŁ­j!u\ßę@&™ssĄ›î¨Ô9âőö’%<×n ŰŻ“¦6äEöˇ"ožW˝ M>IĄ7şžWűú­Jýś¤ÇłúX>îŕŞ=¨ëzN-”F®WŚ""×^C.«čĽÜ¦Şě`Ż/p€jmbĂ›:)`Z, Tű*ç6I:,ˇéa7´&Ů«$Ő™ëĂ{ÚH2ÓŞÚ1ő+™QČ|†Dše5Ôé[¸”eŃ#VÄ!|豇U§> ć =Քώ{ÎčôdžĘBň$Ţ"†)'=1ĐÇăó+ćcżô(“¤‚˘ş=đőkO§ş ToÁk”¸L`{°Ó75¬µű§ŻťI’ž[O`ş×§ "nů­ Őß˙čb@š*ŤrĐâ^=C– ¦Ä{ –ťJüý&÷ę!ďţ ë€eÚŃe„iCCPICC profilexś}‘=HĂP…OSµR*vqČPť,JqÔ*ˇB¨Zu0yé41$).Ž‚kÁÁźĹŞ‹ł®®‚ řâäč¤č"%Ţ—ZÄxáń>λçđŢ}€Đ¨2Íę4Ý63©¤ËݎW„@€Ě,cV’Ňđ­Ż{ꥺ‹ó,˙ľ?«O-X Ä3Ě0mâuâĦmpŢ'ޞ˛¬źŹ™tAâG®+żq.ą,đ̨™ÍĚG‰ĹR+ĚʦFF€Đ.Ь;Î÷±ă4O€ŕ3pĄ·ý `ú“ôz[‹ýŰŔĹu[Sö€Ë`đÉMŮ•‚´„bx?ŁoĘ·@xŐ›[ë§@–f•ľŃeŻůĽ»·sn˙ö´ć÷¤r„§Á bKGD˙˙˙ ˝§“ pHYs  šśtIMEä $ÜŮ" IDATxÚěÝyxŐýÇń÷ĚÝs“A@@ę bQDQęRĄ.uAAŠE ÚĆZ)R[—ZQqA‘ţT¨(n„Ąŕ‚PY„¸ `A#R kr“»Íüţä’7á&$áózžű$3÷Ü™3g–sľłś1Ů'SE """"""˘ŕYDDDDDDDÁł‚gĎ""""""" žEDDDDDD<‹(xQđ,""""""" žEDDDDDD<‹(xQđ,""""""˘ŕYDDDDDDDÁł‚gĎ"""""""˘ŕYDDDDDDDÁł‚gĎ""""""" žEDDDDDD<‹(xQđ,""""""" "x^łf yyyäĺĺ1~üřJÓxÚCËe`x÷=ť†Ár· ݶ ‘ĂŇś9sJ드śśĂnůSUźŠęÓ†XźşSŤ·l‰÷wżĂuňÉmÚ}űmŠďżżĆ¤°°ěěl6oެ­UDDŞeěر´jŐŠ‡~řçĹwŰmxoą%aśťźOÁĹ«>Ő§ !xĆďÇޱČóĎă2¤Ö$Ť2oŢJ ¦?˙śôYłđ\q‘_T‰Ô2˝çYDD¤°wěŔÚ´ łM†‚g©Ś‘™‰Ů˛ĄnŮ9DR۶ib¶mëüďóa4j„yâ‰c­[§I‚Ü8â `ýďYYxoĽ;#úöŰ*‘†<Á Á)SĘb飏Ć}ţůXßOaŻ^5ş0™™™DŁQŠŠŠ´fED¤Ę‚Á éééu"/†iâűýď15Â޵‹ř—_;kăĆź·ęSi(őiťžíÝ»ŮÝĄK­/HăĆŤůěłĎ9s&Ç×+""U6}útÚîą{ę+5ęĚWő©4¤ú´NχÂŔK˙ĎĎĎ×Ö*""Ő2bġPč°[~Ő§""˘ú´ĎK—.Ő*""mĹŠ‡őň«>Ő§űćî;,Ö „ ś ŘűNríý&9|%YźŠ4Ä*PUźČ~č=Ď""""""" žEDDDDDD<łfÍňňňČËËcüřń•¦ń´‡–ËŔđî{: ĺnűI’T©źćĚ™SZźääävËźŞú4Uu®¨>­KRŢ۶çŠ+đ\ućqÇ`­^Mř™gţyŤ.Haa!ŮŮŮlŢĽY[«TËرciŐŞ?üpťČŹëä“ńýîw;–Ö©EwÜ]X¨úTDDTźÖ÷ŕŮ}ńĹÄ–,Áš8;Á{Ă ¤M@áő×c­][c ŤF™7ož¶T©¶Ĺ‹Ó¬Ył:‘óřăI›8‘ŘGQ"""‡˘z®É‰{o˝÷YgşůfDTÚ"""É0ťsŰń/ż$ňŇKDVŻĆuÖYx®¸‚Řż˙­2©í깦&ě:oŻ^„† Áúá•´H˛"ěÂB¬uëF[?ü€Ů˘…ĘGDD䨑+Ďľ;ďÄsńĹ„~ó¬őëUĘ"""Udý÷żmÚ$Ś3[µÂúńGŽČ!ň+Ďľ»îÂÓ«ĹŹ<ć‰'bžx"Fóć*m‘$EŢxw÷îxűőĂ<úh$ Ҹqc>űě3fÎśÉđáõŊH•Mź>ť¶mŰÖ™üD_ťč믫>Ő§ 1x>Xú~~ľ¶V©–#F…B‡Ýň«>Ő§ Ý7pßa± á\ŔŢw˛kď7IRiDDDŰú4…u®H]ŞUg‰ě‡©"Qđ,""""""˘ŕyÍš5äĺĺ‘——Çřńă+Măi-—áÝ÷t:ËÝ.ĽÚ.DDKsćĚ)­Orrr»ĺO¦>uµč@Förpď»¶L¦ÎU˝,"˘ú´ľIyoŰŢţýń\~9ĆQG`}ó áçź'^Ă=x’ťťÍćÍ›µµŠHµŚ;–V­ZńđĂňĽdäćV:>¶p!EwŢ©úTDDTźÖ÷ŕŮŽDLťŠµq#Ř6ž«®"müx  Ŕúć›[h4ĘĽyó´ĄŠHµ-^ĽfÍšŐ‰Ľöďź0l¤=ű,±®ëTźŠHCŞOëtđ}㍄ář—_âąôRÜ]ş©ÁŕYDD¤!±VŻNööë‡]XHT­HĂžË3ŇŇđôî †A|ĺJ•¶Hµ*TOź>Dß}"•‡HC žÍăŹ'řŻibďÚEŃw˙ňK•¶Hu*ëłÎÂüŮĎľů¦ CDDä©‘޶­ (Ľî:Bżů ±ůóńß{/ćŃG«´EDDŞÁÓ·/ńe˰ľ˙^…!""Ň‚g"¬Ľ<âź~JńرŘ;wâ4HĄ-""RŐŠşukÜgźMdŻ>EDDD¤!Ďćbb*m‘*ň\{-ööíÄ>úH…!""rĄö™gÓ$đ׿űđCçUU~?žË.Ă<ţxÂĎ>«Ň© żĎ•Wť6 b1•‡H žm;Â{ë­Í›cGŁXëÖQ4j±>¨ń…ÉĚĚ$ŤRTT¤5+""U IOOŻ3ůń\z)F0H¤–; S}*"" ©>­łÁsń}÷’iܸ1ź}ö3gÎdřđáÚbED¤Ę¦OźN۶mëL~˘oľYë=l«>‘†VźÖÍŕů8p`é˙ůůůÚZED¤ZFŚA ¤ŹŽP(tŘ-żęSQ}ÚŔçĄK—j ‘¶bĹŠĂzůUźŠęÓ}s÷kĐBÎě}' ąö~“ľ °Ł!ârÁ¶«_çŞ^‘zRŞ>ŮSE """"""˘ŕYDDDDDDDÁóš5kČËË#//ŹńăÇWšĆÓZ.Ă»ďét0 –»]xµ]–ćĚ™SZźääävËźL}ęjŃŚěĺŕŢwm©:WDDőiC¬Ok´·m÷Eřë_‰ýç?ÝqGŤ.Haa!ŮŮŮlŢĽY[«TËرciŐŞ?üđˇĎŚ×‹ď¶Űđ\|1F“&X›7ť>ťČ+ݍ>Ő§ )x6[·Ć÷»ßa}űm­,H4eŢĽyÚRED¤Ú/^LłfÍęD^|C‡âąę*Šďľ›x^î®]ńŹ˝kŃwßU}*""ŞOkYÍܶírá7ŽđřńŘ۶ië©jUzę©Ä—.%¶x1ö–-Dsr°6lŔuňÉ*‘†<űnżëű˙ľJXDD¤âË–á:őTĚ6mś`ú´Ó0Ź<’Ř‚*‘C ĺ·m»»uĂÝł'ˇľ}Uş"""Ő~î90 ‚oľ ¶ ±Ĺ>HěăŹU8"""ő=x6š4Á˙—żPtď˝Ř*]‘jň\z)ž+ݤčž{°Ö®ĹŐą3ľ»ďĆŢą“ŘţŁ©ĎÁłŮ®FÓ¦¤=ńDą98łČXĽÂ^˝°6mR©‹€/;›Č«Ż+éĽËúö[Ü]şŕŐ§űćî;,Ö „ ś ŘűNríý&9|`GCÄ7äVč \u®4ôRu–Č~*Ď""""""" ž×¬YC^^yyyŚ?ľŇ4žöĐrŢ}OGi”¦¦Ó¸Zt #{9¸˝•¦C{řtx÷3/Ą©_i¤n3gNi}’““sŘ,·űĽóČČÍĹHKÓFp5ş˛Ć«j‹ŃĽ9ąą¸:tPaČëËTNĚsŐUřGŹ&¶d E·ß^ž›¤Ď™Ń´)ˇ‰ŻZU# RXXHvv6›7oÖZ‘zăÓe•Ź_¸îřCňÓńzáöˇpńEФ lţ¦ż /ż’îĘ+ŕ–› E X»˙,ý$1Í©?‡ßŢ ;B<+WÂÓĎŔŞŻţú;v,­Zµâá‡>¤ů<ôî‹/Ţç÷áçź'ňÜs ·qŇ­ŢnŔěŘ#$ţĹ„nľąâvßż?ž~ý0›5ĂZżžđ„ ÄćĎŻ‘<űAđ:0›A|ěžĹUoZ-ŢÝĎ@hzÝ_·‚˙—ŕjá Ç˙ˇwˇp* Ł×śâb˘99Ř;v¨B‘Ú žť=Śë„0š5ĂŢşw·nŘ‘F /H4eŢĽyZŁ"RŻô»~Żzž{ćľWµé ý-\u%Śşľűşv…1ŁaçNxw†“¦űŮθ ĎÂG  ˙uđÄăpíu°~“¦Ëi0á)řđ#ř}6Bű“ C‡Ă#x^Ľx1Íš5;äůŹOxňd§˘îÜß˙HQv6Ö¦MŘ?ýÔ ×ٱ#®ÓOÇÚş#¬4Ťç׿Ɨť Ń(ÖÚµmŰxäBĄüD}ŕrČüŘQ­Ď ĐäařéF6đýÂŰ 7D×€áωů{°#zŁţ/ź˝kĹcƨ2‘äę§”„,‹č{ďáąä§Ňżě2błfU¬ô.»Śŕ´id,^Lđí·ńŢpNv\§śBƲeřÇŽ%ýĂńLŕ±ÇHź?ďŤ7j­‰H±zMâ礡 ćU1x>őçÎäĹKŕÇ-0#Ç ;ž\–¦˙u°<^xľů»wCŻ^eiîʆիaÔݰ|9|ý5ĽůĽ1]ëŞ6Y›6a­^í|6ntĆ­[W:ÎÎĎwęËNť<ý4éďżOĆ’%§M+­ ëŻ'řÎ;d,^Lúż˙Ť˙Á÷ß8hÝšŕŚřGŽèňtŚ`Łyój?«}ë- ~ń Âűɧ§_?Š}”Â~ýľń&žľ}Sľ>‚×9wý~ę_rĹŘ„ŕµUhpe8Źö´\f#h4Şl8íŞŠéÓosˇĹ<ȸ­â÷i×8W°˝§ĂŻÂ‘‹ ůLđtHíşČ[®†üÁđÓ (śâŚw·®ú´<˝{“ţÁ¸Î8ŕ”)d,YBúěٸNvTţŃŁ <őTârľň ľaĂJ‡ýŁG“6aľ?ţ‘ôąsI˙}|{îv¬BóřăÉČÍ-ýTvŰv2ÓÁĺrľ˙čŁŇ6kđťwÔ^QđśĽŘĚ™x.» #- ÷Yg}˙ýÄçUWá9’ČK/QŘ»7á‡Ć{ýőxŻ-W ™&±ůó‰LžŚočPb ~ě1Ľ—Ů"" ‰a@ź>0cD"UűíňĺNݦŤ3|Zghy¤s…yŹŽáłĎʆăqXń%śR`·h'¶śYZőf›iŢśř’% Fá5×yă ü÷ߏ«S§˛¶ý™gâ»óNÂăÇSpĺ•„î¸kíÚ}7 Ž>š´^ 6oĹű[黜«2Ď€¤ĎžŤűěł«µ\v~>vQŃľx˝¸Ú¶u¶ăŹ?vÚ :ůěŘ1µeěŹ3+Âά(^X˛śU•µ6ťá|¬ť°óŻeá·÷ZĽSÁlů·AÁ$Hż Ľť+É›2~ ;„/„÷€]ÚuAÜÇÂS ůŰĽÂË ŕŐj–§ßŹočPŠ|‚óϧhÔ(ě‚‚*MĂuĆXß}GÁŻ~EѨQxoľWçÎUJcĺĺ±»K *9ŮT•éxoĽĎe—Q«ŔąĘűÖy¶8ľąň@Ýđ9颫Ŕ.‚ČűľĘŰďś@pµ»âëÁ.¨ćÄ|>ÂŹ=F|ĺJě˘"â_|ő}Ő2mýđŃéÓÁ˛/[†µ~=®SN©ršTĚËŰ·/‘©S‰ÍźŹőÝw?ôPÂÝ"Ұ¸kjÂŃYłđÝvEÇ'€ł˛0Z´Ŕ7lXÂm8@âYfË‚X Âag8.=űmř| ˘Ź ‘ňú^ Ë–Ă÷ë«ţŰK/q:»űONG`§u‚{FÁ®]°đ?em9—ËyŽyófÇSqZ{®zGŁĐ·żó,őŻzjýÔEFf&ŢoÄŐµ+fÓ¦ŕradfb}őUY€=>ŢAľó±O>ÁZą’čÜąĄ·~—xňIŚ´4¬5k*ęUNäąçj¶CłrÁ‰˝y3±O>©±źĘ‡AńÍYV ÷-ű°Ę5‰¶™Y1ť…č÷?­T¬‹č×Îróh4ÂąeÜŽ8{Ő'%ţőÁ=(nýď‰ĺPXčśP©bšť—‘žŽŃ´)ÖęŐeß˙ôööí:8‰(x®â±ńí·!"¶d f«Vľ/Ęή~Ź:Ł'" LëÖN‡^Łî©ŢďďúĽ2ĄěYéoż…ÓşŔŕ›ťŕ9užoÎĘ‚)SťqŤö’ŘgSIńČ#K6äĺ9W§ĄnňʇэáÇÇÚ°âqŹ?ž\ćçSxÍ5¸N; wçÎxúöĹ;x0…}ú`ďµrĂ˙;ćQGáżď> űöĹ޲ĄZÓ©iöŽÎIvÓÄŢľťâ?˙w÷îÎw)΋µł$5ť yÇđ•ÜmŐÔbÇĹď)‡Pb]Ó¬źśŰČýçďŚj®»PČYwÉ6ů*{TĎN⊝˘Ë,Ő™ŽÚ©" VŤ=°};ĚßĎ«o22 UKđűǻݖV±ŤgĹÓ~ő•Ó›ö.śz*|µŇţq‹Ó‘Řçk}ÔŹÜÄݵ+‘—^"ž›‹˝e ö®]­+éÉ)%ľt)ágź%týő™™žÍ›GřŮg±ÖŻ'0n\Ĺ>F’śŽ‘‘áś8ß{cM•H„xÉŐq×9ç8űAŹÄË]u/ÍO €ŃĽ9F PőöLÄéiŔ×˝ä¤EÉ]ńŃŻŞŮFŠ8=X׆Yv#­äYër± ·Są“ ©n;îÚ•ř®qÓÄhѢÎî‚vAv~>®“N*+ł¦Mőxˇ‚çÔ Oś·woĽ7Ý„yĚ1'ž·_żŠ=Šü~ç–ë·ßqžVŮ—ţý ç]çŮč„€'‹CżľĐă\çąé+~ ^ŕĽrjŹ)˙r:»ĺ&8áç¶î€?ńëż˙Ă™ţ}c sgčĐNďzV¦˛,¬ pźy¦äşÝřîĽ#3ńţ^÷…âąöZĚăŹÇhŃÂé_ͱľý¶ňéĆăßsf»vř† ©Öt<ýűś1w·nŐZ4ß°a§NĹ7j”ÓXi׎ŕÔ©§NĹ(y•XtęTg˙ąë.§—ńk®Ë":mZ…éą/»Ěé4ë˛ËŞ•źÂלż™ŮĐě_ÎmËXPřzőV]ü{'7›8’Ődkě`–ÝL‡¦Ď;˝z1š˝î”@('őyŤŻ\ét¸uÜq`xű÷ݰ=×5‘×^ĂsÝu¸{ôŔ<úh§‡úýČE¤^sŠ™Fß|Š‹ń€oČěâb¬oľ!ňÚk5ÝĚĚL˘Ń(EűëˇSD¤ŽąôçýÎÓߪţ4ţ|Ü6îMĂŹ?:ݤš4ą,ÍÇ‹ŕ/ŔÍ7ÁßŔşupçĘŢń Îë®~{Ü:žzÂąűđËŻ`ä݇Ǻ¤§§×›üÝsţ‘#Iź3»¸Řż˙MüÓOŇŘxű÷ÇwŰmŕń`­[GŃűí¤ÉÚ¸‘đ_˙ŠěXbË–9W¶«1ťę2[µÂl×®tŘ0J† Źćä`dfâéßó裱֮%ůd&NśČ§ź~ĘŞU«5k§ź~zĄiÇŚSz⨪ÜÝş‘6aé ‘›KÚ¤IŐĘŻďŽ;NťJú‚¤ĎźOÚ¤I¸{ô¨¸oôďOpĆ 2–,!8mîóΫz¸eKü>Hđí·ÉČÍĹ?ztµËŮsĺ•N»wÉ‚S§â:óĚ„ďŤĚLü÷ŢKúś9d,^LÚ+ŻŕęÚU»‚gđ˙ńŹx®şŠđÓOS8`EcÇbĺçcdeŐč‚DŁQć͛Ǘ_~©µ*""Ő˛xńb,X ‚Cćç?˙9Ýşuc×®]5ťO<‘iÓ¦qÁ°nÝ:.\išüěg?«4`8p`ő“;â:ýtěÝ»*Ďž>}Ŕç#ľhÖš5¸N=•ŔcŹá:í´˛4żţ5ľělĚfͰ֮Ĺ<î8Ź<‚«C‡*6XýŘ;vyţy¬ Şťgw÷îř˙üg˘3fP8p ńŻż&í˙Ŕ,WÎţ1cpuíJŃźţDáµ×_˛„´'žHH#"ő‡ťĘOú‡ÚŢţý÷ýý¬Y¶çšk*ýÎ?r¤xâ ç˙°O?mű˙ň;ýěôąsmď A•ţnÍš5vnn®ťęeŃG}ôŃçđű4kÖĚÎËËłsrri>ĽÚŞŰ ĂöŢt“ť>k–ť±t©ś>ÝöüęW i\ť:٧ź¶ÓßßÎX˛ÄN›f{.ą$q:Źí5ĘN˙č#§<ŘľóŽí˝ńĆ*ĺů@ó2š7·3–/·]]ş$.ëu×Ůéóćٸ\Ąă<—]f§Mł3/¶ożm{o¸ÁĆ4~çéÝŰN˙ŕŰuĆvpĘ;cÉ;}ölŰuňÉÉ/»ËeűţřÇóp?/ IDAT.{2ůl#´ŤćÍmĽŢjoiiivŹ=켼<{Ú´iŐšÎSO=eçĺĺŮýúőKĚźa$ 7nÜŘ^´h‘ýĘ+ŻŘyyyv^^^•çe4mj€í>űl;#7×N›4©Zy6O<1a8đČ#vFn®í6¬t\Ú”)vFn®íéÝŰi/ŽegäćÚţ±c«˝Ż§NµýŁGWë·§ž˛Óž>a{Jź;×öÝy§3ěvŰK—Úž>}çůÖ[ ËĄŹ>úÔŹOĘŻ<Ű;w:g}ľJżŹ}ţ9®SN©üĚĺ)§˙üó˛łyÝş˙â .Ľ˘;îŔ;xpµnÍiŃhâ#\'ź¬H=“ňŕąřpťr éď˝G`üxĽ`4kVvLůüóŇŠyě±řÇŤżżô€X>x¶6n$:}:Ř6ńŻż&öţűÎ-="""‚·o_˘łgÍÉÁZżžČ‹/_ąOż~Ąibsçyůeâ«V9őę´iÄWŻĆ}îąeÓéÓ‡čkŻ[°+/ŹâqăŔ0Şśźdćť=÷/ n·ÓhŐ ×ĎNlöěŇ4ľ!CLšDtöl¬Ť‰-ZDä•Wđ\}uĹ™ú|„{ŚřĘ•ŘEEÄżřëűď“_öľ}‰LťJlţ|¬ďľŁřˇ‡*,{•ňSřý~7nŚËĺbŕŔ¬Zµ €Ţ˝{3räČŇt˝zőâ’K.áž{îaëÖ­ujÜ^»{w¬ Íśéś iÔČ Dł}{ŇçĚ)},°¦¬<“nŚŚ ¬íŰq˙ęW¤ĎźŹ«}{ěňŹ+F"ÄW¬ŔÓ»7Fóć`šx.żóŘc1š4ŃAL¤žq§z‚ńĺË)¸ür\ť;ăęÜ÷Ą—â2„˘;ď$ţé§Ä?˙sÄŚôtÜ矏űżŔÝĄ‹s–ض‰Ż\Y<ďő е~=ž.]´ÖDDDŁM¬wßM¬+WŻĆ<餲4™™xoĽW×®M›‚Ë…‘™‰őŐWÎ÷Mš_ł¦ô7öŹ?bďÜYőü`^±÷ŢĂ?r$î3Ď$öńǸ{öÄúáâ{ň“•…ѢľaĂđ –0ýJŻnFŁÄżţşZů1ŇÓ1š6M¸âi˙ôööíeÓ¨b~"Ď=Gäąçévaše×FĆŽËkŻ˝F‡1c={ödôčŃ´hŃ‚1cĆđúëŻ3wî\|ű¸cđPÎqă°6o¦čö۱÷\‰/wRĂŢĽ™Ř'ź`ďŘqw@'?†Ë…]X˝y3v8 OB˛âűîĂ?v,éłge˙⠢gëŘcu9ÜçŇŠě“Oň ‘çź'đä“ř~óBC‡b}ű-va!®Žqźu‘—^ÂuöŮŘ›6_µ "‘˛c’{Żěą\Ő:.""ŇPٶ]±A_nśÜ8ŚFŤ?ţ¸sR:'đřăeőéž´±XâtöNÂçŘ»w—ͱŹ?Ćó«_%\uŢŁ(;›Řüů^ţP,«ÚůŮ_PTťüÔˇP˘˘"«KN |ýő×X–EăĆŤ1 N8ôôtúôéCź˝îęËËËă’K.aMą*µĹséĄřÇŽĹúţ{B·Ý†˝eKٺޱĂYצ‰˝};Ĺţ3îîÝťď¶m«ý‚ŽF±wďĆČĘ"6eJé-äFăĆ ů±6l tóÍii`çç?«Ü˛‰HýPóďy¶měŤaĎ36–E|Ĺ \gž‰‘•Eô_˙Â}ƸözŢŔ<î8'`Ţ;·k‡˝~˝Öš`oŘ€ëÄëÎvíĘîÜ2MÜ]»yé%âąąŘ[¶`ďÚ…ŮşuŮ4 °·oÇlÓ¦,vLOŻúm°IĚ«4ć5 ÷yçáęĐó„– žím۰·l9řWů$»ěůů¸Ę_©oÚŁqăjçÇČČpž÷űk|ýßtÓMŚ=šË.»¬Âw+Kîä;±dű8餓0M“Ť7bŰ6»ví"77·ôóiąçvsss)JÁ3Ě•ńöďŹď®»p_|qĹŔąW/ücÇ_µŠĐ-·$΀s tI@ď:ç€ŇWYĹËÝÝjű[§ńŻľJč — W§N•ćÇ…ś[ş[´púőůäÄDę™”_yN{ćbě<{TP€«sg[š´$ç%ť<Ů6ţ1cśç‘KžQŢ#í4ڬ,ÂO?ťŇüD^{ ďŔĄůđÝ~{…«îUÉŹ§|C†TűJő#čŃŁGiÇ^íŰ·'''€›oľ™-ĺĘË/żśNť:‘––ĆĚ’ç‚÷xőŐW9ýôÓ3f ]t§žz*˙ú׿řňË/ą¶\‡g>źŻôŮčk+ém|Æ9¤•äŮl׎ŕÔ©„†Ç.÷<µ»gO\; Íť[¶ť·i˙OrÚ™™¤=űlŮÉ–> 2q˘ó˙Ô©¸ĆŽĹ×]xűôq.´XŃŞľŰ41K:ĂçĂhÔČŮ×Âa¬uë’^§Ń©Sť>~nľ™Ř‚xűőĂđű‰ľůfY<}ňÉíŰc­Y‘•…ď¶Ű°6o&úÖ[:Ŕ‰îÁsěăŹq_tŢÁÁăÁŢ´‰đsĎůż˙+ ž?ű ă¶ŰĘzJüĎđť~z…+ϱE‹0‚AҦLÂBÂĎ>Kěö9ďĚĚL˘ŃhŤť-‘†- ’žž^·ň4eJ…q…×\µnŃwŢÁ8â|·Ý†qÄX?ü@ń1ÄW¬(M[tĎ=řGŽ$}Îěâbb˙ţwbďŔ@dŇ$̬,‚Ó¦aďÜIěý÷±6n¬r^“™—3ñ>pN®˙ýホľů&ă0ß!ŘĹĹXß|Cäµ×RžźČ?˙‰Ń´)ţ€xśČ?˙é<3^®wäTĺ'GuíŰ·/NKK+ö–ô>ťŚwß}—ŚŚ nşé&Î9ç6mÚÄäÉ“yá…R‚§U+ĚvíJ‡Ť@ŁdŘđx°“™HąeŰűýÇfIPÍÉÁČĚÄÓż?ćŃGc­]Kx„„>s’a ű–yôѸĎ?ëűď)ěŐ«JíŢâűďÇ{óÍř† ÁZ·ŽĐťwb•»SŇŽÇńöîŤyôŃÎv¸x1á'ž({–[Dę ’;¦Ő6˙`eg0íš5kp•ÜŢ=sćL†®5+""U6gÎÚ–\Ťúú믹üňËU(‡—‹Ś… )şçžzóŚłÔwCX–ţźźźŻµ*""Ő2bÄ@J޵+uźŮş5®Îť‰-] ‘ŢA°wí"ľt© GDD^đĽTś¤ŔŠr·;Ëá=›xúöĹ7r$ÄăX«VşýöĘ_‹%""‡µ:{۶H]aŞDDDDDDD<‹(xQđ,"""5ÎhŢśŚÜ\\:¨0DDDöâVÔ=é˙ů‘—_&ňÜsĄă?ŚqÄ„nľąęÓűŕÂ&}ăŤ}'*.&š“˝cG˝(#o˙ţxúőĂlÖ kýzÂ&$Ľ›ŮwǸ»uĂhÝ, ë»ďLžLlÁ‚Ş5–şuĂ{Ă ;bÄżř˘Â:đŹ‹§’÷‚özuťś—(x‘j°wí˘xĚz‘WĎŻŤ/;˘Q¬µk1۶%đČ#„ "ľj•“¦Oě-[/Z„‘•…«KŹ=FčÖ[‰úiŇó2;vÄuúéX[·bűMkĺĺA<žpB˘*js^""˘ŕYDDä°ŕŕŚ&M°óóqźsÄbD^}•Č?˙ €‘‘Azą+±ţ»ďĆ÷ÝNśu˙ýDß~Ű ŘŽ?žŕ´iĄéB– ®SN!mŇ$˘łfáîŃČ«ŻâjßW—.D&O&2yrYP{Ůex ÂlÓëljľů&‘W^ËJČ» B0č\ĺŽDŞ<÷ëç,ÇŁŹ}ă üŁFáéÓOßľÄKN„ną%áJlŕ‘Gp_pîîÝ+Ďž>}0Ź<’řĘ•Ä>ř á»č[o}őU\ť;xňÉýć+4t(v~ţţó^‹ó‘ÔŃ3Ď"""ő»[7â_|AÁ…RtÇxĆ}ŢyŘ»wł»Kvwé‚˝s'Ĺ=T:Ľ'pç ćî.](¸ä’ý´Lbóç™<ßСÄ, üŘcxÓiNx®ş ßČ‘D^z‰ÂŢ˝ ?ü0ŢëŻÇ{íµČHź=÷ŮgW}ˇ˝^\mŰ˙řcb :~ÇŽe˵÷-Ě^Ż3~óćŠůąürĽ7Ţ»{÷ ßŮůůŘEEIe-řúë¤/\HÚ¤I¸N?˝ňŕąç%"" žEDD°6n$:}:Ř6ńŻż&öţűxúô©‘yĹ.$¶d‰ó˙‚Ä–.Ĺ07Ŕ7d‘I“ΞŤµq#±E‹Ľň ž«ŻNi>ŚFŤJvł}{ŇçĚÁČĘrľ+ů[á$Ă…âîŢkĂb3gÖHůŘ[¶`ĺĺaďŘëÔSI{ć\'ź\ďç%""%u‰Š@DD¤Ď6$Ż_ʧK—‘±„ĂÎp8 ¶í¬>dea´hoŘ0|Æ%z•\IŤ<÷\BghU‹žŤ˛ioŢLě“OöŰÉ™ű Ś‡µy3E·ßŽ UH4č Š'üÄoŰć &ţ»ďĆÓ«Ţn häČC6/I]y©‹b±¤Ćî˝Î»\ Áe­(7ż˘ěěŇ[Ă÷| Î9'Ąłłwě(}†ÚŢľťâ?˙ąlxOPYÂséĄţúW¬ Ý|3ÖĆŤ5R óµ,˘ď˝ç4´Ú´©×óĎ"""uš˝kWĹŢ–Aěť;+ňăŽsć=±s»vŘë×Wś`$R1ĐNq@goŮ‚«k×äâíŚ ĚV­ŔďŻúĚ"âkÖ8Ë[»{ô ţŐWesŻ^řÇŽ%ľjˇ[nÁ޲eź“ôöďŹď®»p_|qőT­Z•0ń\pŰVň|umÎKDDRG·m‹ÔAńŹ?ĆsĹÄW­Âúî;ܧźŽűŚ3(~čˇÄ ´qc|ŮŮD_{ W§N¸{ô ¨¤Gíň¬ďżÇuÎ9sçb8W°÷ęű`…'NÄ?jöÖ­Ä>ü|>ܧť†‘•Eřé§Ňzú÷Ç7dEŮŮ ďfNVtęT\cÇâżë.Ľ}ú8',‹hIŻáf›6ř˙ô'§Ś23I{öٲß~đ‘‰D={âęŘ‘h @lîÜ„ď|Æ9›•śĚ0۵#8u*ˇáñ·n%8}:ÖćÍŘ۶a¶l‰Ń˘ÄăD^~ąbă«ç%"" žEDD´âÇÇWP€ďwżĂlÚë˙s^ËôÖ[ éb‹a¤M™……„ź}¶ÂëŹÂ˙řľ{î!}ćLđz^Uxę)ÜgťUš6­$łňň(¬¤§ě}´oľ ĹĹx Ŕ7dvq1Ö7ßyíµ”—O4'#3O˙ţGŤµv-á Ż\é$(éYŔüŮĎ~k–{ W2ĚV­0۵+;a`” 6}˙}\ť:áęĐ» Ŕé,íŮgËňSç%""Uc¶ŠADD¤ţń?đF @Qv¶ CDD¤†é™gĎ"""""""G·m‹€®<‹(xQđ,""""""˘ŕYDDDDDDDÁł‚gĎ""""""" žEDDDDDD<‹‚gĎ""""""" žEDDDDDD<‹(xQđ,""""""˘ŕYDDDDDDDÁł(xIž ĺ2„Ôž3·Ţĺ9Ýę˘'""rÚt†óWWžEDDDDDD@Áł‚gIĆ–«ˇ`˛ĘAD¤2îşš±ťAčͲßŐ|gAĆoÁĚrFďz ˙Uń·ŤÇ@ŕr ›ş9ă ÍŔw6d łiŐňłą@ƲqŰG‚•M_p†íL‚˘µĚĆŕů9dWËJ–«ď©eÓŮÜÝ™€‘îc ýđźßđ7ČdÖ{2ĺsŔm#…6ť†šĎtÖůžů‡Cł7j7?…S`×x8ň}0‚eăŁ_ÁO7AÖđť‘\~TÎ;î˘yűÎKúo÷—Ú̶‘Ě>Ěľ\›ź’c]ć دܲ>á\hţVjŹ-©,{ËÂO?MlŃ"ě‚ĚÖ­ń€çŞ«(7Žčôé%ÇŁysÜgźŤočPŚ,ç PpÎ9xÄwë­ĄÓ-1ű§źH›4 {ËŠ˙ňâ_Ťë¤“đýéO­ZUy=„ކť‚ďLČzŞd¤?^ęÔGüxÚxKf:fSŘy?D˙ ž“ ŃÝŕjučŽÍÉîď:¶¤Ş …řŘ=‹Ŕ.Wk^iWŞ1)"˘ŕąp YO:ktµS)Ç6@Ógśď!đk,‡]ŹC“ż«…ó»„JýFđ_ ńďˇŕ%řéF8â0Ą6ż»…â…NÖsÄ‚đ°¶'6(]-śĽ–g‡×AÚ5`‡śęö‘ĐtxOořĺÖ{2ĺ“ě¶‘˛ŕ&ˇé~Kĺß×V~üç9Ó/˙/ĘƇ—€™ ľÓŞ–źý•sĆ0Ţ褋|ćl˙M)k໚ší'™}ç@ű`˛űrmIe~RqlIU~ěť;)Ľé&ŚŚ ü÷Ţ‹yä‘Äżůëűď×Í‘G’öôÓŹ_˝šđŁŹb­_OÚsĎ%5źđřń¸N?ťŔřńD¦M#ü÷żxôŃj­ ĂѸŹrN¨~U˛˝ ůťe /t†H.opNô'“ ň9Ľŕ|g‚ëgÎw{ćÎť'ńMN=Z4° íjȸ­\–ٰëďP4 3ńΩ´ú®G™ÝsĆ9rp kďĎ!üIęóg4‚ȧ`‡S7M;âÜ*gívÎÖ–é~Ö{])_w0P<ďĐ——˙YZ®ŚBNĚŢáµLžkb_nHÇ–Tĺ'¶`îîÝKç¤ç(9(D’« |Ç[ş”‚_ţ’Řüůř˛ł!!2e Ĺ#GbmÝZĄů.…Đ,gß /˙Ő[ţýM'cD>/†âůů{gťN…íwµ•z-UeX[ KŽóűŮTC3 `˘sbľŮ4'Ţ1Úyl¦”ĺŁ`÷sŕ;׹Ł`’ó]˛iâ[Á× ˛ĆCł×!x ěřłT'ä{9¸Ź…łˇń8çî»ČgĺN ĽĹsˇń_ éD|UvâCDD*r׋\Zý ¦:•€yÄAcG@¬^1ÚřŘ>Úiěx;Ż«s»¸«Ybşřeď Ű#82ď(ŢýĽóŮSQşZCÚµ‡ŮÖąźő^Ë'xťÓ° \rçóś+ń˙9W˘ĂËŔp9ĎŽWU}Ü“ÉóöÁd÷ĺÚ’l~výÝů”ç:*őë4UĺcmÚ„űÜs«đ ë›oL™‚yě±G$W-Z6a‚3Ť}űmÂ}„çškđ>ńD•ׇ§=ÝĎ8WÜŤ´Ši’9Îďo:®ćeĎŰQ(zÇąC ­d=^˙ď©*ĂÚßľsöź&ôšsĺwOżé7;ë¬đ5hܱÜÉśsÁőťsG•˙\gýÚEΕídŇYew}ě‘ÖBďBř?Î>YÚČkíl3ŕôwánăóŢÎ%y~ĂŮ˙ý%»aŁ»aËŻŐ8©—ÁsüŘtfY ĺ=ŤNÁ„MŔN}~˝§Cówť3ż‘Ď h¶ÓHÍúGYE•?Çe6Ů«ĐÇą˝*¶ '—fË–X›6x^-Zöä“ŕra4k† îuŢÇşdĽ•—GńŁŹâéŮ“´§ž"ľr%…boŮ‚»GüŁFÇSĄĺO»Ň96ůşA¬˛Ű\“|^w_Ó‰}çtĘĺďéÜšY ? rz|öź ™#ťuPźLúz@ł)%eő˝ÓńÝŻ8Ďí¸Rܧ„ëHçęs•l›ä;1ł“OłăON'f™w8Ëj¸`[6e·~ď/ŕ·÷úÎ]źZ†""‡¸ťY§sç)iä“ÂŔyDVT˝s#Óé”#ˇţ),ëŔj_•¨«5X…ťç\5,|ů0Ů*«¸ŢëRů¤] ᏓldŐpđů❏˙ÜLłn‡)ËsŠöĺÔEŇuěŘRÍü¸Ď9‡ř˘EŘ»v P÷`<ć1ÇT śÁé4¬0qćvAAY'dGEÚřńÎëŻL“â{ďĹ˙‡?>s&vq1‘iÓŞ^y6uŐ8ŘŞö5WkhňDIҦóČąĺyŰPçř]ăôTż»Üťi}ś^«‹rś×+L‚č*Hë›ú–›»MIǧ–óŞÄÝă«WĆi˝ť7EÄ·:Ó*(ׂTTŻoÎ)| ˙U6ĽýÎßĆcĘ:ě'€(xĹéĚĆ×2Wý™žĚ;ťł´»ź+ßyލQ¶s[li€ÝŠŢs*L;ęHÁůçł»KŠďż?ĺۆԮCuÜ8ÔÜu5c»»Tţ>M÷ąçřÇ?śH„đÓOť7{űvĚ#ŹÄÓ«Ţ~}ç"“&aýř#ć±ÇâżóN\gžYçWNÁŕ»í6<˝{k-ż>ß}—čŰoc}÷ć‰'â:W§Nuz˝§"?Ö¦M„źz kĺJ¬ đ\uţŃŁ«<ŻČ”)Dsr°7lĂŔlŰďo~»[·Şí@|ĺJ'O_}Uş>O< Öú1!eŰF’Ç–ÚÚž“Yﵹ_ě>ë,D0ŇÓ1Ź9ď A¸/¸ F÷ˇŘĽyŤ…űśs<ńÄ!Ůk{^ 5Ď©WxÂ"/ľ0ÎČĘ"}ŢĽ„q5rڞ, {őÂsůĺx®ÉAĺ'ŔsůĺŤ׍üĆRŐö«kmČŘÂ…DsrH›4 ó¨Ł0ĽŢÔďIÔ•É7´Ť‚ăF=QgŻ<§LIř¤Mś.î‹.*Űžy†č;ďŕ=šô·˙ź˝3kâjŰřťLBXÄ­âŽňşánE‹P­Ö­P) ⊠˘ű«˘bÝiĹ +ŻŐ‚h+*uCĐĘŞFP”M˘(ÖÖĎ ¶9ßh4`CŔöü®ë\™<ĚąĎyÎóĚ93“™cąş˘hŰ6?ţ.XccQ¸z5„66Đ Ó©ňçÎ…âţ}šˇ?QJΞóĹűř@wÓ&đÄbäĎś ĹÝ»uÖďÓSXľ‘DӦ߲eµëâ‰DŤ ]??čúůߢ ćĚâömN1¨É?u*xFFeţŘľÂáĂk-'hjl°É-ÚϬü®ĺ¸Ť ˝°0čţď`şwGÁ˘E(˝zµćÖţ‰˘íŰÁ˙Ďj7µX×?Uł&ŰĹoŰV5/üüłę¸©ˇU|ęČóç:9Ő‰ăâÇęáBĽjřÍšŐ =”Šű÷ÁoÜL§Nŕ::šŻ„ű’MŢ ÔNŢřdÚ €HëţDĺ@ľkôϜޜ­Ęź2ĽFŤ »aŇ.ĎÎŚą9Ä^^€‚Ył@ärHví*3(-ĹëoľđŰoˇ3w.·Á‘#(úß˙ űăŹ(ܲŠ;wŔ«_ş7‚éÜ ň={ ?|äéSđ[´€hęTŐu6äŐ+Ľţę+µő‹˝˝!5ŠFëׯńú«Ż 3gŽňĘ &ý® jBOŢر`ĚĚĘťU­V]%%xeať Ş`˛Ł. ĽĽ@rs!ٱŁNäMŤ 6ąE›ă™ŤßµŻ,, ăęŞrEéőŔŽť… 5ß)ĄĄČź2˘‰!?r<‘ó•gmćMŐUW5“Ľ<ŕőkđę×/{löŁ©výď(Ť‹dßľŠŰU9Jˇ@žC†@ÇÝýÝĽ}ÍGŹ »}»r[ţřń`úö…ާç»Ü‚âĐP(ţď˙Ŕ34Ó»7t×­ă6ß`ŁçűďAž>ĎŘ%§OşşŤŁ· ™ yŽŽĘĎ’ŕ`0ffŞë‘5k řë/0mÚ řěY@ˇ€ĐÎ:śúĹĹ(Ü´ %§N|>DÎÎ(>yÂŃŁ!š<ମĺˡxö üFŤPŤѤIďW. `Áč_şžDĹź˘ŔÝŚ•Ä‹<PZŠ˘Í›Q|ňdY]ăơřÄ •ş´~ĚŞÄďlç~ĄÉÉ(Úµ ŠŚ Ľ<đ[µ‚ČĹÂoľá´MŽŐ*׳kÖ řرrŰU® łšš#±ÉÚ„U|±Y_T5·ŕ°ľ¨*˙°É¬beśGD@ľw/99ŕ7iRv'ŕřńźĎ)g~ěx~{dť˝m[ŐăŇChk«r fĚÍQ^Ö™-[˘ôÚ5(ţţ:ýűżóÝÍ›Žón_ ¦kW”ľą}‹ł”ÂBíŘń˛eŕ·m Ef&xúúeŽ;~E»vAĽlnÝPrî —-żE 0]ş°˛áŕíÉ zŰ6űĹ)›ČŐß?zîŻE=\ë"ůů(>r ¤ü¤¬Š,˝r˘1cP°`JŻ_ďłĎ ˛·‡ĐήVr‚¦Ć›Ü˘Íń\§ăB.GÉ… Ż^ŐĚ•Eţţŕ·n Á×_C~äČ?.?5Íň}ű ßµ ş›6AđÁdŚÍ~4ٮһwńzŕ@@ cfťŮłÁ75­ŃU|îČ˙ýDÎÎÜő&&˘hëVčúř€éÖ ŠgĎPzéçů[=%‰‰9:B‚Ň”®] ~«V  ŕ›šÂ@*yôŻżý¶bÝW®@řő×Đ?sĄR)ňÝÝ!č×LĎž¬őíŢŤ’3g ^łüćÍQäçĹÇjűH¸t)Ä«Vˇ4=Ó¦•Ĺżš‰żâŢ=ä»»CřÍ7Đ™=űÝÝłĹeWĹZµB‘ż?ýUkó”ŞüÎvîGţď˙ °°3kxőęˇ46…ŢŢŕ7m ¦GŤÎ!ąŚŐĘ{{Cěí]ć“ß~^xxůĎrlhĚUä ­ŹŹ*â‹Íú˘*¸ŽŤĘň›ĽÁ&ŮŘ;†˘M› łtiŮ8Ľw…«V"Qą‹=•iÖÔx>‘†•ÄÇCq˙~ąťÎôéÚÚ"ĎÎŻúôAţ¬Y/[Ąĺ›,yő Ľ P|ú4^őJÓŇŔkŘäŮłjÎ⊠ł`ÎťÁÓŐÓ˝;ř­[— ‚C‡ üć­­ËÎşş‚éÜĹĽ(,l(çî;v€ß¦ „o&5â÷ŹŔZÔá.…L†Wććxýĺ—A×ĎL×®ěc°¤äůsČ÷íÓĄ $ţţÚŘ ĐÇĹgÎÔJNĐÔب2·hs<×Ѹ(ÚąŻĚÍńĘÂ^^ŕ7kŃ{g˘5ć넟9ťE‹ţq1řŻÔ¬Áv1ť;Cěí ÝíŰ!^¶ äŮ3仺‚<~\s9ŠČ!3řďí(»ë†<{¦C‡wu5jTí‡É÷íCŃ{?Ńýé'•‡k˛±áä÷ĘBőĺKČ÷ěAéĺËP<}Ş<EX^}¬‰±Ę¦*m‡±ˇ‘vU•7´<6ŘÄ۵ĂÇú‚kţ©Đ§,b•Íłg ˙÷(úé'ýô“jéęrҬ©ü$ĎŠ?˙DI\t}|ĘO7m‚hÜ8ĺř˙ůJ¤RČĘ&¸B!x ĎžAŕě Á—_–9#7Ľ ŞĄ‡'‘¨Üc_Ţ€W~2ńá666”Ş;v řŘ1HvíżE •ŕŃ´ß? męáR—H¤ĽM‰éŐ yNNďÝ ńŠěbP$O"ßÄDux·hĹĺ˵’456ŞĚ-ÚĎu4.xőëojZ6†JKQ0g$GŽ€'kÎ×™™ Oź"˙ý×í””(űݵŢŃŁŕ0éř¤bđߨąŰĹ32żiS(rrj,GBhoĎz!FŢ,ŕ•u7l˝°0”HĄ(MJBńˇC@/4´Ü$®ĘůF5ôT÷jŹĎ˙¸ţy;Ç|0í”ꆒ71®¤´T­nťůóˇxđ…+WBďŕAŤźĽSk[[•ĽŻ®žŞl¸ř˝2 —.yń:óć•ĺwóćiä*muÇ*›ţ©|p±5AąĽˇĺ±Á:ľX¬>ÚóOŤóf\«ű‰WÍšŠAŕ¸m»84ĽúőËwZIIŮ;ţ>L RXřîc—.(˝vM% —&'WüŰÎŹ Ŕ–-ˇČČ(7ůă˝w•ŤÍű‹“rJŮzëV?É/ż”›iŰďlЦžjץP€°ŹAüNťĘ=!—<|Ţgźi?'hjl°Ě-ÚĎu=. äżţŞŃ~áwď˝°0č8 ,L—.`zö„Ţŕ7n¬:ö^˝‚âáCµ~Ş«1řoÔ¬©şĘŮľ| Ĺ_÷޸ĐdŽ*ąxŠěě _YÇ34T}÷°Bňč‘Ú“ ‚/ľ€ÎŚ„„€Ľ|‰ŇädŤëPö´ýŇŇw]ť•őQ9¦şzxúúŕŐŻŻ˛@y{婜ć;wT5gf‚תUůĽóćdüV­P°l™ňNžľ>x  ôý»ţž>ÉÍ­ŢÜÎĐ|eQwĹ‹Ť kżW4÷S(Prů2D..`>˙Ľěꬡ!ţ©^x%sHMŽUVmݬ9ŚŤYź©ÉZX_pŞ«†×lb•MÆŕ}öJ4uAFCą·N/žIaaŮ­UŁF©=#%°°€üŕA”üń÷ďŁřřq”ś?Ż2©Ž‹Ňë×ËŢ'™•…ÂőëA Ë~[ aDŽŽ(>u Ĺ'OBq˙>ä(˝u ˘÷ŚÂĆFéśÖ­QS–@ärĺá_żp޸ĹaaeOŐ,(€"#ŁěÉ“ď%~múťUĽjJŹBˇl/ŠŠ@^Ľ(űśťÍľ.…‹ˇřÔ)”޸ŇË—Qřý÷PČdľ÷Ú§*c€ČŢ%qq8Ĺ˝{(ţí7”ÄĆ–=ČKŰ9AScenŃćxfă÷ZŤ Żěé¨ÁÁŐž ŞÝ­X¬2ŕ›b1xşşe0äű÷#ĎĆĄ 5Ž÷ŁfMŐU°t)ŠOźFéŤeO`ž=<†Qyj¬&s”<0";» ŻR0ť;Ł4-­lńGäű÷Ľ|©şŔŚŚ,{Ú«Lň芏xĽj˝‚­*=@Ůý˘Í›ËÚ~ň$JÎźŻ‘g°Ń#tp@ńáĂeż-U( ßąSíĽ†äć˘pÓ¦2Íáá(ůă*z°Ă@Ľ~=(ÚąóťßťśPü6‡ß»‡ÂÔÚ•Lµ'8ř˝Âąź~Ë–(IL,ŰVR‚˘­[ËŤ16sHmŹŐ*ó˱ˇ‰9›ĽQ×ŕ˛v`µřÓÂú‚M ˛±Ń™:ĹGŽ@Ev68€"˙‹Á*/ÔĺERÉďżäĺUřP ńęŐ(ň÷GˇŹHn.řMš”˝>eĘ”w ´´,{ĘßîÝ(Úµ |čnŮľšłü#G‚í …BˇP( …BˇPčâ™BˇP( …BˇP(şx¦P( …Bů·all ™L†®]»Öy­ŃŃŃÉdÉdXąreµm8Mrů|¬[·R©2™ >>>µÚ‡lôP(şx®óçĎW&Đ·%11QŦyóćŘşu+˘˘˘*LBööö ERR’’’‚Ţ˝{ΑJĄp~ďeďn>upp@tt4ŇÓÓqňäIXZZÖŞnMéa:w†dÇč_şýK— OOď]P7m ńşuĐ;v R)ÄŢŢŐŇĂ&ľ {÷îFJJ RRRpđŕAčëësj“ÎĚ™0JUŠţŮł*6B[[Hvď†ţ… Đżp’ť;ÁôčÁYŹ6ŰĄ)ź~ ß–_~ů…“OE"ĽĽĽÔÔTś?nnnśőęčč`ŢĽyŽŽFjj*bccńÓO?ˇyóću:v´™4Q—‹‹ Nś87nŕúőë8t謬¬ę|ŽŇTnáÂ# “É ˛ťmě|ŞÇ÷‚‚=zĎź?ŻóšSSSÄĹĹ}”  ;;;Lš4 ={öÄŞU«jµŮčˇP(tń\m˛˛˛`ccŁ,ăÇŹWů^,ăŮłgضmîÝ»Wá5&&sćĚ»»; °wď^ššRńé€đĂ?ŕČ‘#5jnŢĽ‰_~ů&&&µ˘YSzř¦¦üň Hn. —,AÁ¬Y(>} äýŔÉÍ…|×.(rrŞ­‡M|µoßű÷ďÇóçĎáéé‰É“'ăřńă „űswî ĎŮYYňgĚPů^0t(JPčĺ…‚ @ !ůß˙ŔoÓ†“m·K>}?ÚŘŘ`ěر(--Ĺďż˙ÎɧóçĎ‡ŁŁ#–.]ŠAÁßß‹/†˝˝='Í+V¬€ŁŁ#6nÜQŁFańâĹxňä 6lXgcG›9ASuÉĺrěŮłnnnpssCNNѱcÇOşźŮÄ Z¶l‰… "##ŁÜwlbçSćĹ‹řď˙‹ĐÉLLLđ÷ßăćÍ›xůň% kµŮčˇP(uRËüůóIxx8kű“'Oź*í HVVqssă¬ÉŮŮ™HĄRbaaANś8AŇÓÓI\\éÖ­@x<™1c‰‰‰!äěŮłÄÖÖVeUŮ™L¦¶Ś3†ÔUŐfQçÓ   ˘üĚ0 IHH K–,©ŤšŇŁëăC$;v°¶×;p€˝˝?ZOEńµmŰ6üŃýŁ3s&‘ěŰÇéxúúÄŕęU"?ľÚzjş]5áSdňäÉD*•N> %Ű·oWŮ׹sçČš5k8Ő/•J‰‹‹Ë';ÚĚ 5U—@ ·oß&“&MŞUÍúúúÄŘŘXeüi:·°- ð°02|řpL8Ǜһwo˛wď^rőęU’žžNNť:EFŽ©błyóf˛wď^˛qăF"•JIBB™6mZŤßŰ·oŻň˙]»v-găăăC~ýőW˛bĹ ’H®^˝J,XPÎnĘ”)$::š¤ĄĄ‘řřx˛uëVΚŮôĎŰLV®\YiűŘŘTV|||Ôj~¬±éC6s?dôčŃäôéÓ$--ŤDEE‘©S§>źĎIOUóC@@Ž9B¶l٢Ü&‰ČńăÇɦM›8÷QUš«\ú‡ZţIĄN_y655…T*ĹĺË—€víÚ}ô> ŔăńđôéÓjýżX,ĆĽyó°lŮ2ôęŐ žžžxýú5€˛Ű‰gĎžŤÍ›7cذaÇ–-[Đă˝[K«˛yůň%LMMajjŠÜÜ\x{{+?:tžęaéÓ=zŕęŐ«ĘĎĄĄĄHNNVń…6Ń”ĆÜ%R)t7m‚~d$ôöď‡ĐήÖôXXX 11;wîÄ•+WpâÄ 899Uď l›6ĐŹŽ†ţąsĐő󿪻Côő$7Wăz4Ů.Mű”Çăaüřń CQQ'źĆÇÇŁWŻ^hÝş5 Oź>hÖ¬"##9_íęÓ§Äbń';ÚĚ 5Q—D"‹‹ x<®_ż^«š]]]‹ţýű×j?ŔÂ… q÷î]ś>}şJŰŠb‡ Mš4ÁĄK—ŕââ‚!C†`˙ţýŘ´iSąź YYYA*•˘wďŢpss§§'† ˘ńă{ff&LMM«Ľµţ‹/ľŔíŰ·aaaOOOĚś9ćććĘď---áĺĺ… 6૯ľ‚››˛˛˛”ßłŐ̶´…——LMMáëë‹ű÷ď+5{yyqîĂŞć~cĆŚÁŞU«°cÇ :+W®Ä”)S0aÂNzŞš–””`öěŮčßż?”ű‹Ĺđ®ŕçYÁFsUcm˙P(˙4ęěâůĆŤđňň‚‹‹ –.]ŠFŤ!44ź}öŮGíwţüůČĘĘBDDDµĎk׮ō7źź©TŠ;wî&Nśđđp=zŮŮŮđ÷÷ÇŤ70iŇ$ĺ˙ł±ˇ|śO ńôéSŘŘŘ )) ]ştÁ“'OШQ#­ëÓĽúő!š0Ą7o"ßĂĹ'N@ěĺÁ°aZ×#Đ A¸ąą!99“&MBXXÖ¬YkkkN}Tzë ׬AÁ¬Y(\·Ľ Żqă ˙GgĆ (îŢEń›ßFkJŹ&ŰU>ýňË/abb‚pö©źźŽ9‚ČČHdff"((Ë—/ÇĹ‹9É^şt)zôč+W®`÷îÝpuuýčÜ\ÓcU[9AÓuµk×™™™HII»»;\]]‘śśüŻďç·±`mmÍúwŁęb‡-@JJ rrrŚÔÔT 4HĹ.''!7oŢÄ©S§ĘýäL›ĽŐŁP(Źěěl•­ZµBaa!.^ĽGŹáÖ­[Řľ}{ŤőϧJesżŮłgĂßßáááČÉÉÁüŔŔ@Ś3†Slć‡>Ä˙ű_¬X±S§N…<==‘źźĎ©.6šąŚŤĘú‡Bů§!¨«Â>Ľ"•JřűűWkźsćĚ••!—Ë«µŹââbÜĽySíw&&&8|ř°Ę¶ÔÔTtîÜ™“ ĺă|Ęăń Ă //>Daa!„Ba­hÔ~Ůą®Ň”Č‚ňŚ 0ÚÚ˘äĚ­ęáżŃ“””„;v(ÇňŰłâ'Ožd˝Ż’oĄÉÉĐ˙ýwmm! ,g/š> äO™Ľń»¦ôh˛]5áÓ‰'">>wďŢĺěÓ‘#GÂÁÁsçÎEVVĚÍͱzőjäćć"::šµě„„ôďßććć077ÇČ‘#1gθąąáňĺËu.v´™4]×˝{÷`mm ###|÷ÝwđńńÁ¸qăTüŻmÍ~~~đóó«Ő~nĐ |}}1ţ|ĽzőŠőâäĂŘa‹‘‘¦OźŽ~ýúˇqăĆ`őęŐ+w"#;;»Üçľ}űÖÚ1ňĂßńľ~ýFFF*s­éÓ§#::±±±¸qă"""đřńăéźO•Šć~ 6„±±1-Z„E‹©|ÇuAËv~…ÇcÉ’%đööVű[WWW,^ĽXĺóĄK—8ić26*›S(tń\K<ţţů§ňvC®Ě›7ŽŽŽx#ď IDAT7nîßż_myyyP(śţ‡ÍC†jâAD˙t*ňiqq1^ľ|‰† "((QQQ€úőëăÉ“'µrĐŐą$/Š&gŠ čÓGëzär9ňňňĘť]ľwďúőë÷Q}Frsˇřë/đ[¶,÷ťÎŚŽ…üiÓ xob¨)=5Ů®ŹőiË–-1`ŔĚž=»Z>őööF`` ň.ŤŚŚ ôéÓśĎo댋‹C\\¶mۆݻwĂÓÓSĺ¶żş;ÚĚ š®K.—#33půňeDDD`úôéX˛dÉżşź;uę„ĆŤ#đ˝“kAŮ”&-- ĆźţYeě°eË–-¨_ż>ÖŻ_Ź{÷´;wîTžl{ˇ' †QžT¨ ÔÍWŢ×óřńc 2}űö…ąą9&NśYłfařđáś|ƶ>U*šű˝íKwwwś;w®Fęţp~(Đ˝{wČĺr››c˙ţýĺţ',, .\P~ţ믿8kć26Ş37¦P>U>™¬fdd„ćÍ›ăŃŁGś˙wÉ’%°··Çرc!“ÉjLcvv6ĚĚĚT¶™™™©Ö§&LŔÓ§OŐNxŞň©@ €Ţ{Żżzr­««űŃ“»ÔČ«Ľ¸ŚUCCC´hŃBm{´™4ĄYíA›Ď‡D"ů×ôsEu]»v C‡…µµµ˛\ż~—/_†µµuąąBe±Ă¦Ď---±cÇ$&&âďż˙Fnn.ZµjUζ]»v`Fe‘ź­&WŐĄă{qq1bbb°eËŘÚÚ˘^˝zj«\‘f.ý”]ݬęy llęOž<ÁŁGŹ4rb•íüĐËË őęŐĂčŃŁ1hĐ µĎăČÍÍUy5ŰűW”ąhf;6(şx®řůůÁÖÖ={öÄŕÁ€ŇŇR„††Ş”ÍĚĚ`ff±X ###™™©Ľ˛ČŰŰÎÎÎXµj$‰ŇŢŘŘXăš÷íۇ‘#GÂÎÎ&&&đđđ@·nݰwď^N6oą{÷.† BGGGĺ€üo†ŤO÷ěŮ^˝zaćĚ™čСÖ¬Y‰DR­ßşiMé‘9Ą%Dcǂߺ5„ŁGC`i‰’'ޟɀߡř:::ŕŐ«Wöů˝Ĺ =lâk˙ţý0`&OžŚ¶mŰÂÉÉ  @XX§v‰×݇přp0ÝşAđŐWĐݶ ¤¤ĹÇŽ˝[8/\ˇť }}]]eyďýÖ–Ťm¶Kc> «« „††˘¤¤„ó+))ÁüI“&á믿†‰‰ ěíí1|řpÎ Šŕŕ`¸ąąÁÜÜ;vÄ„ đÝwßáüůóµ;...¸xń˘Úw!k3'hB3Ă0đ÷÷ÇČ‘#ŃłgOôë׾ľľhßľ}µźŮQWú™M VUWAAAąw7(·ż#UĹN•'¸ dggĂŇŇ|>@ą€ů###,_ľm۶…ľţúk„„„ÔŮăű7ß| & }űöhÚ´)śśś@QŢíŔF3—ţĘ~3`ŔtîÜŤ7V»HfcSWضmĆŤ‡3fŔÔÔfffx˝‘şWU±‰ťŞŠ™™9|ř0IHH .\ , !!!*ǝ͛7“ŔŔ@âëëKnŢĽIÉŚ3jäř¤¶Oź>­ňz¤   •˙ '‹-R~¶˛˛"‡RĆňńăÇÉŕÁ9kfÓ?o‹D"![·n%)))D&“ggçjٰ)îîî$::şÚ}Čf5Šś8q‚¤ĄĄ©TJBBBČđáĂ9é©j~زeK’śśL¦L™˘ňëÖ­#‘‘‘DOOŹSßTĄ™íŘ`Ó?´ĐňO*Ľ7P( …BˇPŞÉćÍ›ˇ§§‡éÓ§ÓΠP(”(|Ú …BˇP( …BˇĐĹ3…BˇP( …BˇP(˝m›BˇP( …BˇP(”* Wž) …BˇP( …Bˇ‹g …BˇP( …BˇPčâ™BˇP( …BˇP(şx¦P( …B©-|||D;‚BˇPčâąnâââ‚'NŕĆŤ¸~ý::+++Î6"‘^^^‰‰Ajj*Îź?77·OÂ9R©ÎÎÎt”~€˝˝=BCC‘””„¤¤$„„„ wďŢĺěŤôôtśd;Ua 4˝RVšDŤ‚ńŔş;ÚĚ TsĺűŃD]Í›7ÇÖ­[™Lźr6FFFX·nâââ––†đđpôë׏») …RmH],ăĆŤ#ß}÷éÓ§177'7n$¤cÇŽśl–,YB’’’H˙ţý‰±±1±łł#·oß&ööö¤®¶ým‘JĄÄŮŮąÎëÔv "łgĎ&ýű÷'}űö%äÖ­[ÄÔÔTi3`Ŕ"“ɇ‡éر#ůá‡Hjj*111©ÍÚÔŁ3s&Ń;|đ;txWŢë¶zŘÄŽ6Ű%´·'BÂôęEž=‰xŐ*bHříÚ•łmٲ%‰ŽŽ&ż˙ţ; P»żĘl´Ů.ÝíۉhÚ4"°° Ěçź]??bGřmÚ(mĚĚĚTJź>}Hff&=z4§|Č&vŘăXĂ ˇá<¦—AD˝ëněhÓ§TsĺűŃT]¦¦¦dĹŠdÔ¨Q$**Šřřř”łůůçźItt4éŰ·/iÝş5Y¸p!IKKă\—ŹŹ ˘ÇnZhˇ…Z> ˇ€Üľ}›Lš4‰“Mhh(Ůľ}»ŠÝąsçČš5k8kpvv&R©”XXX'NôôtGşuëFŹGfĚAbbbHFF9{ö,±µµUŮGU6†††D&“©-cĆŚˇVM100 YYYÄÍÍMe‘˘üĚ0 IHH K–,©µż¶ôčĚśI$űö}´6±S«ý,+WČÉIe;Ă0$,,Ś >ś«]tč˝OB ŕńxxúô©r[Ź=pőęUĺçŇŇR$''«řB›h[żMčGGC˙Ü9čúůojĘY›Ř©­~ćI$9;<JoÝRůnáÂ…¸{÷.Nź>]á˙WeS«ăG_ŕń@rsŐ·ťÇĂřńㆢ˘˘jçĚŠb‡ł/D€î€o˘ş;Úôé?]ł««+bccŃż˙jíG[íb|>ĹĹĹ*Ű‹ŠŠĐ­[7ÎűëÝ»75j„ńăÇĂßß3g΄ąą9§c7›yXYYA*•˘wďŢpss§§'† Bř …RËÔéĹs»ví™™‰””¸»»ĂŐŐÉÉÉślüüüpäČDFF"33AAAXľ|9.^ĽXíĹóÚµkqăĆ äççC*•âÎť;€‰'"<<GŹEvv6üýýqăĆ Lš4Iů˙ll(Ü?>˛˛˛044ÄÓ§Oacc¤¤$téŇOžŤŇ[·Prá <=Á‰ ´µĺ¤§ŞŘ©Ť~Vää ĎÉ ůS§˘ä—/˙Í•ń Ŕ×׋/Ć«WŻÔţ?›Ú?˘éÓ!°°@ÁÜąŔ›ń¬nňŹ»wďV+gV;\Č; K–,amăííŤŔŔ@ĺ••ŚŚ ôéÓŽŽć>aĚËă|Đ%„hƢʼyóŕččqăĆáţýű*'8^ľ|‰† "((QQQ€úőëăÉ“'Z×YŰzHn.ý~Ë–śôT;µŇ.ą ™ Pzíô„hŇ$®^ŤNť:ˇqăĆ |—ŕe).-- †‰‰I•6ţůg­řKgĆ GŤBţ´iP:O0řđdĂ0«‹BˇP(Őç“zĎ3źĎ‡D"am# §§W~ҧP@WWWăú˛łłaff¦˛ÍĚĚ ÷îÝăd󖢢"ĺÄž˘Ę’%K`ooʱcÇBöf1ő>ÉÉÉčűŢkچÁçź^éď?k’ÚÔĂ34żiSÇŹYëa;µŢĎ|>xoô\»v C‡…µµµ˛\ż~—/_†µµ5=zÄʦ6ÚĄ3w.„66Čź:Š®8˝Ď„ đôéÓrWü¸äĚŞb§:^H 7ˇvcÇĐĐ-Z´P›ßµéSŞąňýhŞ..äççăńăÇhÚ´)¬¬¬Wci©˛c7Ű9@»víŔ0Śňs§NťĘ]Ť¦P(Šöa¬¬s˘Ű·o‡P(„D"A۶m1{ölXYYaëÖ­Éd¬l şwďŽaÆáÎť; „`đŕÁ6mBCC‘ŔIW×®]ŃŻ_?ěÚµKí÷ …îîîř믿PPP€qăĆaÔ¨QX¶l™ň,7›· 4­ZµBLLŚňj7˝B]vEÔÉÉ Ë–-CAA7nŚĆŤaĺĂŰrssáî’Ľ|ůóćÍC·nݰdÉäV𠦚D›zÄë×'‚'‘€éÔ b//đęŐCŃşu onU®JŰŘŃZ»ř|čţř#xx ř­[CgÚ4ľřE?˙ ĹÝ»())ÁóçĎUŠŤŤ ±wď^( V6Úö—ÎÂ…Ž…Âuë@ ÁkÔĽFŤ†ňň”vşşşŘĽy38€ŘŘXÎ9“mě°Aß'•%%€Át ˙fŻ>sń…»»;~ţůgܸqCů,ŠÚAŞąňýhŞ.†a”wśŘŘŘ ??wďŢ…ˇˇ!ž?čŢ˝;ľţúktíÚ?üđ ±téŇr«ŚÁC__áááĘmcÇŽENNNąx¬ěŘÍf0lŘ0tëÖ őë×GNN 777lذAc'Ľ( …R}ęÜ#Ŕy<ٰa‰ŠŠ"·nÝ"ÉÉÉ$,,ŚŚ1‚“ bddDVŻ^MbccIjj*‰ŠŠ"łfÍ" ĂTűUU•éöđđ ±±±$33“DFFŞĽ‡•­ÍŰŇĄKrěŘ1’ššJ_Uő^ILLTű:µkתŘ9::’ččh’žžN"""••U­ęÖ–Ý~ úg΄˘ö,ŃÝ´IĺťÁlő°Ť­´‹Ç#â•+‰Ţ±cÄ .Žč_ĽH${÷Á!•ţ_EŻŞbcŁ-éź=K ¤ŇrEĽt©Šť““ą}ű6iÖ¬Yµr&—ŘA5^UĹ49b8żöbgÎś9D&“‘!Ś m檹ňýh˘®Š^uîÜ9ĄMçÎťIDDIMM%R©”řůů‘¦M›rîuďyWűʫʎÝlć›7o&Ä××—ÜĽy“$&&’3fĐă?-´ĐBK]X§ľůBˇP( …RËlŢĽzzz>}:í …B©cđiP( …BˇP( …BĎ …BˇP( …Bˇ|ô¶m …BˇP( …BˇPŞ€^y¦P( …BˇP( ….ž) …BˇP( …Bˇ‹g …BˇP( …BˇPčâ™BˇP( Ą.ăëë‹€€V¶ĆĆĆÉdčÚµkťĐó±řřř (( …Bϵ…‹‹ Nś87nŕúőë8t謬¬8ŰaÝşu‹‹CZZÂĂĂŃŻ_żOÂ9R©ÎÎÎt”~€˝˝=BCC‘””„¤¤$„„„ wďŢĺěŤôôtś}úĚĚL2zôhNľĐf»4=6Z¶lI˘ŁŁÉďż˙NjdlčnßNDÓ¦…a>˙śčúů¸8ÂoÓ†“/ŘäĂöíŰ“[·n‘m۶‘ŻľúŠ|ţůçÄŮŮ™čééqŇĽnÝ:OFŚAÚ·oO¬¬¬Č÷ßOşuëVgcG›1H5WľMŐU›Ç‚ŃŁG“äädb`` Üćëë[cy˘®ëńńń!AAAtŽB -´üʧ!T Ű·o“I“&±¶$##Ś?^ĹîüůódѢEś58;;©TJ,,,ȉ'Hzz:‰‹‹SNy<™1c‰‰‰!äěŮłÄÖÖVeUŮ™L¦¶Ś3†X5ĹŔŔ€dee777•IUHHň3Ă0$!!,Y˛¤Öüµ¦G W®‘““r[hh(Ůľ}»ŠÝąsçČš5k*ÜĎäÉ“‰T*%:::ś|ˇÍvirl0 CÂÂÂČđáĂIpp°Ö&ˇ<}}bpő*}·¸úB]Îܶm Öȉ=—O*v´˙tÍúúúÄŘŘXířcłźšj—¶Ž|>źś={–Ěť;We»ŻŻ/ "ëÖ­#)))äňĺËÄĂĂCŦ}űö*Çő®]»Ş]ţúëŻdĹŠ$11‘\˝z•,X° FôôîÝ›ěÝ»—\˝z•¤§§“S§N‘‘#G–«cĘ”)$::š¤ĄĄ‘řřx˛uëVµ‹grĺĘ•*5ÓB -´|Şĺ“řÍłD"‹‹ x<®_żÎÚ†ađů|«Řˇ[·nŐŇ"‹1oŢ<,[¶ ˝ző‚§§'^ż~­Ľ…löěŮŘĽy3† †đđplٲ=Ţ»ł*›—/_ÂÔÔ¦¦¦ČÍÍ…···ňóˇC‡č}j000ŹÇĂÓ§O•Űzôč«WŻ*?—––"99YĹÚ¤¶ôđ$śťĄ·n)·ÇÇÇŁWŻ^hÝş5 Oź>hÖ¬"##Őď‡ÇĂřńㆢ˘"NľĐf»496.\»wďâôéÓÚ,úúŹ’›[m_T”3-,,ť;wâĘ•+8qâśśś8K|ńâúôé±XüÉÄŽ6cđź®ŮŐŐ±±±čßżµöSSíŇÖ±ŕŰoż…±±1öěŮSî;KKKČĺrŚ9>>>đôôÄ#”ßgffÂÔÔ´Ę[Çżřâ Üľ}đôôÄĚ™3ann®q=Mš4ÁĄK—ŕââ‚!C†`˙ţýŘ´i“Ęíď–––đňň† đŐW_ÁÍÍ YYYĺęęÝ»75j„ńăÇĂßßżRÍ …ň©R§ĎíÚµCff&RRRŕîîWWW$''ł¶)**µk×ŕěěŚ&Mš€aŘŮŮá?˙ů4hPíĹóÚµkqăĆ äççC*•âÎť;€‰'"<<GŹEvv6üýýqăĆ Lš4Iů˙ll(Ü?>˛˛˛044ÄÓ§Oacc¤¤$téŇOžhĐ ÜÜÜśśŚI“&!,, kÖ¬µµ5'ŤK—.EŹ=pĺĘěŢ˝®®®řěłĎęlěh3©ćĘ÷S“íŇƱ€ÇăÁĂĂżţú+rŐśŕĘÍÍĹÚµkqçÎüöŰo8}ú4&LŔąžśś8p …ńńńČÎÎV»ŕ˙X=@JJ rrrŚÔÔT 4HiÓŞU+ââĹ‹xôčnÝş…íŰ·—«ëĺË—Xłf 222„‡ÖÚ k …BůW.žďÝ»kkkŚ;‘‘‘đńńA›6m8Ů,Z´rą\ůŔ0GGGś:u …˘ZšŠ‹‹qóćMµß™ 55Ue[jjŞŠ66öĚ™3VVV:u*ärąr2”Ýy——‡‡˘°°Bˇ°V4Ö†ENňśś?u*J.\€xůrđß\e€‘#GÂÁÁsçÎ…ŤŤ |||°zőj 8Píţ&NśřřxÜ˝{—“/´Ý.MŚŤ Ŕ×׋/Ć«WŻ´:VDÓ§C`a‚ąs ú°2_T–ůü˛tź””„;v 55{öěÁĄK—ŕŕŕŔIgBBú÷ďŹéÓ§ăúőë9r$"##ѧOź:;ÚŚÁf???ššâÜąsś÷SSíŇÖ±`ذahѢŐ~ź••…ŇŇRĺçôôtp®çÁ*ź_ż~ ###Ťë122ÂâĹ‹ޏ¸8$&&˘}űö*ŚŚŚÄÓ§OŤÍ›7cňäÉhܸqąşîÜąŁ2·zöě™ZÍ …BĎ5„\.Gff&._ľŚĹ‹ăůóç>}:'›{÷îÁÁÁ]»v…ĄĄ%śśś```€ż˙ţ»Zšňňň8/Ľ !±ˇ¨2oŢ<899aܸq¸˙ľĘ Ž—/_˘aĂ†ŠŠÂ#••…úőëăÉ“'Z×Y+zär(d2”^»†ÂU«@^Ľ€č˝»Ľ˝˝±oß>DDD ##űöíCtt´Ú'µ¶lŮ Ŕţýű9űBŰíŇÄŘčÔ©7nŚŔŔ@¤ĄĄ!-- _|ń €´´´{˘´ÎŚŮŮ!uÚÜ IDATÚ4(>8łőEeůP.—#//Oy§Ěű9ŇŘظZă:..~~~9r$¤R)<==ëděh3©ćĘ÷SíŇć±ŔĂĂ!!!>%űĂcůŰ<ç…jćęöő±z¶lŮ ¬_żvvv°¶¶FjjŞňd<~üC† Á‚ ““‰'âÔ©Sĺ®Ţ—””°ŇLˇP(tń¬-±|>$Iµlňóóńřńc4mÚVVV‹‹Ó¸ľěěl™™©l333Ă˝{÷8ŮĽĄ¨¨€ŽR5,Y˛ööö;v,d2Yąď“““Ń÷˝×1 Ď?˙ĽÂßĚ×4µ®‡ĎOW@Ů­Śzzzj'kşolŢg„ xúôią«Ll}ˇ­vijl\»v C‡…µµµ˛\ż~—/_†µµ5=z¤ů…óÜąÚŘ ęT(˛ł+´«ĘUĺĂ”””rWÁZ´hQ퓉ďOĐ,--±cÇ$&&âďż˙Fnn.ZµjĄöÄHLL ¶lŮ[[[Ô«WŻĆ^KHˇP(tń̆aŕď#G˘gĎžčׯ|}}Ńľ}{ĺo™ŘŘeď4uvvFĎž=1dČěŢ˝>¬‘‡oíŰ·#GŽ„ťťLLLŕáánÝşaďŢ˝ślŢr÷î] 8 6„ŽŽŽĘđߌ··7śťť±jŐ*H$™™ÁĚĚLĺęŮž={Đ«W/Ěś9:tŔš5k ‘HŞüÍnMˇ5=|>t7l€đ›oŔtë¦OW­ßÔĹo\%%%řăŹ?0iŇ$|ýő×011˝˝=†^nQ¦«« „††Ş˝ŞŔĆÚj—¦ĆFAAAą÷)(·«ë‡ŹZ8/\ˇť }}]]đ;tżCđ>ř qeľ`›÷ďߏ`ňäÉh۶-śśś0`Ŕ„……qŇ 777››ŁcÇŽ0aľűî;ś?ľVcÇĹĹ/^Tű~km檹ňýhŞ.m <<@ĆŹŻ|ĺĚąsçČ„ 8kvuu%aaa$))‰ÜĽy“ś9s†¸»»>ź_«±3gÎ"“ÉČĆEm䪹ňýh˘.m  @ŇŇŇČgź}VˇÍŰWC­_ż^ůj¨™3g–{•—:ͧOź®ôťÉááá*ŻŘÔ”333rřđa’@.\¸@,X@BBBTúĐĘĘŠ:tHďÇŹ'®ň=Ďj¦…Zhů'Ţ›?( …BˇP(j8ränÝş…ďż˙žęˇP(”1 €•´( …BˇPĘcdd„úőë#((yyyT…Bˇü‹ˇWž) …BˇP( …B©>í …BˇP( …BˇPčâ™BˇP( …BˇP(şx¦P( …BˇP( ….ž) …BˇP( …Bˇ‹g …BˇP(”şItt4d2d2V®\ůIhöńńAPPuž066†L&C×®]kŐľľľřÇőď?e¬FGGĂÝÝťö!]<×>#FŚ€L&S›0şwďŽŕŕ`¤¤¤ %%„ľľľň{‘H///ÄÄÄ 55çĎź‡››Ű'á©T ggg:J?ŔŢޡˇˇHJJBRRBBBĐ»wďrvŽŽFzz:Nž< KKËZŐ­ =ü¦M!^·zÇŽÁ@*…ŘŰ»Ľ‘Hťąsˇ˙űď0ʇŢożA4~<ç>d;Úęg©TmŃÝşµÚz*Ë-Új—ÎĚ™ĺÚ¤ö¬ŠŤ‹‹ Nś87nŕúőë8t謬¬jĄ]۶mS."Ô•9sćÔÉŘŃvN š+ߏ¶ęjŢĽ9¶nÝŠ¨¨(Čd2řřřT«žÂÔÔqqq˙Čă*›ůF]ť“0 ¨¨(xxx|”MeŕčŃŁxţü9ťłiÁ_tnLű.ž?‚–-[báÂ…ČČČ(÷]űöí±˙~<ţžžžd;Úěç|8 &*ßo۶ŤWşŹĐĐP˛}űv•mçÎť#kÖ¬á¬ÇŮŮ™HĄRbaaANś8AŇÓÓI\\éÖ­@x<™1c‰‰‰!äěŮłÄÖÖVeUŮ™L¦¶Ś3†ÔeŐV100 YYYÄÍÍMą-((„„„¨ŚĄ„„˛dÉ’ZŃXzô boďrŰ%»wÝTµ=z”˝Ľ8ő!›Ř©Í~ŤKôŁŁ D"ÎzŞĘ-Úl—ÎĚ™D˛o§˙äöíŰdҤIµÚ®A™LFLMMë|ěhÓ§˙tÍúúúÄŘŘčččTk?µŐ?'Ož$>>>ŐOÁÁÁdĺĘ•ĺ¶×Ż_ꤧ§“oľůFeű·ß~K222HÆ âăăC~ýőW˛bĹ ’H®^˝J,X ŰGŽ![¶ly—ëD"rüřq˛iÓ&NZ}||HPPńđđ W®\)W›ůŰ9ÉćÍ›ÉŢ˝{ÉĆŤ‰T*% dÚ´iĺ4M™2…DGG“´´4O¶nÝŞV{ecěýÂçóÉŮłgÉÜąs9Ű0 Cľ˙ţ{’śśL¤R)ńđđ ŃŃŃÄÝÝ]iÓľ}{•6wíÚUelű§*_Ľoóţ¶đđp˛hŃ"ĺg___DÖ­[GRRRČĺË—‰‡‡GµĆrăĆŤ‰ŻŻ/ą|ů2IMM%ÇŹ'_ý5ëůjŹ=Hff¦ŇçłfÍ"?˙ü3IJJRéC6ľĐÄXe;g;V«*Bˇ¬ZµJ9~fÍšUnü ŁGŹ&§Oź&iii$**ŠLťú˙ěťyTS×ú÷żI@™‹"^¬`ŃüśĐZµ‚EQę¬DŻhDE˝ ­Xq˘-­ÓŐĄ®Š8á^Q±^A‚–AĐ„A˘¨­·¶¨ÔYž÷Ź–ĽĆ8@»?k=kĺśódďg?{8{ďsÎŢł‰Ď粲˛˘ŇŇR8p Âüüü(;;›Á_Ú‡-Ušő“çĄK—âÖ­[8{ö¬Ę뎎ŽČĘĘÂÎť;qůňeś:u ŢŢŢ :™™™čßż?>řŕ€Ţ˙}$''7Ę&„„„ŕ‹/ľ@˙ţýڧOźřăUŘ `Æ 3f °qăFôíŰWţ˙†t?~ ˇPˇPŠŠ „‡‡ËŹŹ9¦zT`jj ʇČĎőíŰW®\‘WWW#77W!/´‰6í©ľ|‚Ź>ßĆ čß|++T]Ľ¨–ąÔťů™Çľ—^ź< TVŞmOCm‹¶ÓĹďÜ&b1L’’`¸y3řoĽđ6FFFŹÇC^^^łNWsŞ;ÚLű_ÝćYłf!==C‡mT8-Ń? ńčŃ#ś;wNé­¶I“&!99Yˇmýä“OpăĆ 8::"88óćÍ˝˝= ŞŞ ,ŔСCáĺĺ ÂU}¦Ó @»víŕëë‹m۶)ÄĹĄżˇNźÄÉÉ ‰ @`` ‚1jÔ(ůőÁ#,, ß|ó >ýôS˘´´TĄÝő•±7ůěłĎ`ee…={ö¨­„ &`éŇĄ2e úôéŽ;*č”””@(ÖůY:ţ©//ÔađŕÁ¨¬¬„»»;"## µÂ044ġC‡ĐłgO,Z´ăĆŤĂŽ;ä÷{®}Z@€sçÎaçÎť Áůóç±víZD"đů|µň«©e•«Í\Ę*ćÍ›777,Y˛ŢŢŢčׯźRů™2e """°}űvŚ=«V­ÂĚ™31}útŔ/żü‚ěěl¸ąą)=YNLLDuuő_Ú‡-•f;x2d\]]対ŤžžÚ¶m‹ŔŔ@äććbĆŚŹŹÇš5kŕęę*×ŰĽy3âââśśŚ’’ÄÄÄ`ĹŠ¸páBŁĎk׮ŵk×đüůsH$ÜĽyŕç燄„?~eeeضm®]»†3fČ˙ĎE‡ˇ‹/Fii)ĺeĂĚĚ <€››rrrĐ»wo”——Ł]»vZ·OŰöĽÚąŻOž„ńńă0ÍΆŃÖ­xąnŞŇÓ9űKÝŃĄźőÁďÔ ŻŹWŰĎ\Úm¦«ş /׬Á‹ůóńrÝ:đÚ¶…Qt4x–– z]»vEII ňóó„Yłf!77·Ů¦«9Őm¦ťŮ\8-Ń?\9rä† ‚öíŰ,--1dČĄAÔÝ»wqčĐ!ÔÔÔ 33eee Ó{÷îá_˙úV®\‰ŮłgĂËË ÁÁÁxţüąÚ6=~ükÖ¬Aqq1bbbpďŢ˝w6ąP›."Âőë×ńĂ?Ŕ÷Ťő6:uę„—/_âÂ… ¸˙> °uëÖ&̡ň ‰°oß>TTT¨­ăçç‡={ö 99ĄĄĄŹÇ{gĺCSyQQQµk×âćÍ›řĎţłgĎĘc\qqqA§Nť„ŚŚ Üľ}gÎśÁîÝ»Ő¤¤ -- pţüy¤§§ĂČČmÚ´Q+ż4á®67TVąŕëë‹}űöáüůó¸qăV¬Xˇ4a°`Ál۶ ¸{÷..^ĽččhL™2E®sňäIŚ7zzzkkkôë× j—ů–ćC6xÖ m۶ETT–/_Ž'Ož¨6üĎš““íŰ·C*•bĎž=řńÇĺłµŕîî///,Z´nnnŚŚÄęŐ«1lذFŮöúők\ż~]ĺ5[[[HĄR…sR©ť;wVK‡Áť… ÂÉÉ łgĎFĺźO ko~Ďž=Ă˝{÷đňĺKčëëëÄFmŰŁ˙ŮgĐwwÇ‹Ď?Ç3ĽÜ¸­Ă WÇSŞ|ČĄîčŇĎúS¦ úňeÔĽń #{¸´-ÚNWŐ… ¨:{Ő¨JMĹ‹ŕ`đZµRřFnßľ WWWLť:ÉÉÉŚŚ”·Í1]Í©îh3í›7oŢ ˇP¤7ÖŕNKôW222đóĎ?ĂĂĂ0aÂÜż_>°¨ĺíďfź>} sssĄɱcÇŠőë׫\Ă€ 7oŢDMMŤüřáÇJqiО˛2Ąă7ż?Ż}/‹±aĂřűűĂň­I†ĘŘ›Ś3ÖÖÖŽŽV[ÇÔÔíÚµSčŹýúëŻxřđá;+šĘ‹ŇŇR…'’EEEjŻĐ«W/Üąs§Ţo¸ąôW«««QUU…WŻ^^ľ|)˙m`` V~iÂ?\űŘ •UŕŹ·JJJä2dČů5333´mŰ………ňs˙űß˙´°˛˛Â˛eËŐ ÍźoŔ™3g`ll,_ÔÍÍ wîÜQo©>ü«˘×ŤęŮł',-- GíŚLaa!FމźţĎž=“?ő}ł9hĐ ůqxx8˘ŁŁĺOÔŠ‹‹áŕŕ‘H±X¬¶mĎž=S(\xsł¦č0 ÁäÉ“1mÚ4ÜąsGa‚ăńăǰ°°@LL RRRmÚ´AyyąÖíÔ¶=­—,Ael¬|!­šŇRč}ü1Z˘ę­N\]>äRwtĺg~ÇŽĐ4/BCŐö3×¶E—ĺ‡**Póż˙É_»ŻĄ˛˛%%%€ěěl$&&bÎś9 méŇeÝŃfYe6×NKôŹ:÷ú¸¸8xzzbÇŽ4iŽ=ŞÔgPŐ‡xű‰§žž>úč#TVVÂŢŢľŃ ńUUU5—Ć&5ßš¸ qýöŰo5j{{{řůůaţüů;vlŁňL$!66¶Ţ°ąčhĂ7\ňBU?PŐ«Ďoë5ÖćĆö;ąţďm»Ę‹wYVß¶ąˇ˛ ńńńHMMUżŢŰ6ż~ýZÉö   z'?~ŚÔÔT¸şş"55nnn*ź:·DţUi–OžŻ^˝ŠŃŁGĂŐŐU.yyyČÎΆ««+îßżČĎĎWšĺ°¶¶Ć/żü"żů+…_SSCCĂw2ëjgg§pÎÎÎNa…O.:µĽzőJŢf( OOOLť:2™Léznn.¨P©?ţřcĄoDµ…ÖěŃÓĎČx«‘ŁęjđŢš®Ď‡\ëŽ.ü¬?y2čŃ#T©řô˘!{¸¶-ş,?<33đ;týö[ýŤ7ź##Ł“.mÔ333X[[«lßµ™vfsýá´D˙ÔňüůsĄ'jorěŘ1tęÔ ţţţ …‹‹kT\i=MĂĄżŃN×®]!äÇ={öTz:őúők¤ĄĄaăĆŤ?~<Ţ{ď=•[MÖ—ď0räHtéŇĄŢ=ŹëÓyňä ĘËËŃ«W/ů9KKKĄWŤ5íĂúxüř±ÂV«|>:thĐĎÝ»wWzÔ đÁ(}Ł«nź–+\ňK~ćj3—˛ZQQˇđÄřÍĎ&ž}ú`ďŢ˝jéÔrëÖ- 6 hÝşµB!ý;DDDŔČČvvv°łł•••\gĎž=čßż?ćÍ›‡îÝ»cÍš5022¡C‡tbłĆěáóÁďŢüîÝÖ­Á{ď˝?Žk'‘ŞŞP•™‰VŢŢĐ:üNť ?~<ôGŚ@Ő3¨ ůkÝŃşź  ďîŽ×'N*fX˛‡kۢÍt¬_ý±c!čÓzź~ Ă-[@UU¤ńĎÁ¶mŰŕîîŽ~ýúaĐ AŠŠB·nÝäo4Çté˘îŕÂ… *÷ŔÖfÚ™Íő‡ŁÍ¸Ľ}300€ąą9ěěě¶ĺS‡śś8;;ŁWŻ^°´´TH˙ú믋ŠCzzşÂÓ*®Ś3>>>X°`ŠŠŠđůçźcĺĘ•čŮłç;«g\ú é››cĹŠčŇĄ ĽĽĽ0bÄÄĆĆĘŻŹ7Ó§OG·nÝСCx{{äoÔpÍwŕŹ'p‡®÷‰uC:űöí“ßă:wîŚU«V©|r§IÖǵk×Đ»woüß˙ýx<`ff¦¤÷¦ź'Nś±cÇâŔjů011wîÜÁöíŰáččŚ1~~~ŤęŻ6—üŇ„źąÚÜPYĺÚç÷őőEűöíÁçó±páBĄ<߲e ¦M›†ąsçB(ÂÎÎţţţX˛d‰‚^JJ jjjđő×_#??_ĺdČ_ч-™±,¸ŞmWŻŻŻ|ۤ¤$š>}şÂusssZ˝z5Ą§§“T*Ą””š?ľĘĺßÁq«Şş®óx<‰D”žžN%%%”śśL'NT[§Vz÷îM'Nś ©TʶŞzC˛˛˛T.µżvíZ˝É“'“X,¦˘˘"JLL$'''ťÚ­ {x¦¦d*‘(‰ńńă˙_ÇĚŚ ÂÂČäĚ2ÍĚ$ă'¨U` áŤ2ĎŇ\ëŽ6ý¬ďáA¦—/ĎĘJc~®«mŃVş żúŠLţű_2˝t‰LÎť#Ăďľ#~çÎ mĆ7ß|C)))TPP@ąąąO...:O—6¶ŞRÇć… ’L&ŁQŁF˝ł:ČlÖŚÍÚŠ«®íY’’’ĺ####Ú´iĺçç“L&#%ť‘#G’LM·:·ŹBŰŮŘŘPnn.Íś9SAgÝşu”śśLĆĆĆjoU…z¶>R§żQźÎ† (::š˘˘˘čúő딕•EsçÎUřż““9r„rrrčúőëtňäI9r¤ÚůîěěL………Ôľ}ű:ÓÎEGOOŹľüňKĘËËŁ«WŻŇś9s(55UÁ÷111*ËĎŮłgŐň—Ľ´víZşző*]¸pBBBęÜŞjýúőň­Şć͛רşS»UŐĺË—I*•ŇéÓ§ňَţjíVUH(’L&####jÓ¦ Éd2˛¶¶ćśš*«\úŘ\Ę*8nUµzőjşző*Ą¤¤Đ˛eË(55Ui«Ş &Đ©S§¨°°$ ĹĆĆŇرc•ÂűúëŻI&“)Őýż˛[°°Á&L0a„‰&¤v˘˝ˇ=Š˙J˛aĂÚąs§V⊋‹Ł&ëĽ-€ ęl2ywůőW-«Ě‡MaÔ2 Á`4cccX[[cîÜą8vě|Őa†ć077ÇŹ?ţÇ7Illl`ooŹ´´4TVVbÎś9řý÷ß‘^Ď¶Ž Íçů°ĄÁĎ Á`0MdÆ řôÓO‘ššŠ-[¶0‡Ľ***°yóć&ë,Čĺçç‡TUU!??ţţţŤÚO›Ń´Ľ`0¶$xřóÝmÁ`0 Á`0Şá30 Á`0 ÁĎ Á`0 Á`°Á3Á`0 Á`0lđĚ`0 ˇ#Äb1d2d2V­ZĹÂ|Č`0Řŕąůâââ™L†]»v)]ű裏°˙~äçç#??‡†‰‰‰üzmCý¶|˙ý÷Í>݉>>>¬”ľ…§§'Ž=Šśśäää 66 PŇóňň‚X,FQQNź>ŤÁëÔnMŘĂďĐëÖÁřÄ J$0WVjŐ ­-‚É™30ÍĚ„ńţVľľjű°U«V CZZ¤R)Îź?ŹŔŔ@ťůŮT"Q)†›65ÚžúÚutšBëyó”Ňdrîś‚N@@Nť:…k×®!//GŽ“““Ú6sÍÓúزeKťíŞL&ĂÂ… ›eÝŃv›Ŕl®?mĹŐ±cGlÚ´ )))ÉdŚŚlT<Æ P(DFF» 7ćCńWˇYoDmccCb±Îś9C»víR¸Ö­[7*(( -[¶Đ§ź~Jü1ůřř±±±\ÇÎÎNA¨¤¤„&NśŘě7á–H$äăăó·Ý„Ľ.‰‰‰ˇ ĐСCiŕŔ´k×.*(( ˇP(×qvv&™LF"‘zôčA_}őIĄR˛µµŐ‰Íš˛‡ßą3µţ׿H˙łĎČřÄ 2WŇi˝p!™¤¦’žŁ#ńÚ·'}WW2˝|™ôÇŹWˇˇˇˇ”““CC‡%+++ňđđ 7n§§§NüĚďŢ]Aýű“iv6黸4ĘžúÚutš*­çÍ#ăcÇÓ÷F> iӦѤI“ČÁÁěííéŰożĄââbęŃŁ‡Z6sÉÓ†¤cÇŽňöÔĎĎŹd2Ť5J~ÎŇҲYÖm–Ufsýáh3.ˇPH+W®¤ &PJJ EFF6ÉOű÷ď§U«V±{1ó!&LţľŇ|ŤOcÇŽĄýű÷+u·lŮBű÷ďW+L’H$Ôşukµíńńń!‰DBŽŽŽtęÔ)***˘ŚŚ ęÓ§ ŹGsçÎĄ´´4*..¦sçÎŃř7,\tĚĚĚH&“©”)S¦°«BLMM©´´”‡±±± eéŇĄKŞłż¦í1>tHĺŕŮh÷n2üúkEÝăÇÉ ,L-=z”¶nÝŞ —””DkÖ¬i~n5u*™Ĺ„V­Ô¶§ˇ¶…«Ž¦ĎF¨ő===şqăÍ1C-›ąä©:2|řp’Éd “.͵îhł¬ţŐm611!+++•÷Q.áčĘ?§Oź~gç6mÚPQQŤ7Náügź}FĹĹĹdaaA(22’öíŰG+W®¤¬¬,şrĺ -Y˛DˇnÇĹĹŃĆŤ˙[תť>Ż^˝j”M Á_|ţýű#88Oź>đÇ«° ,Ŕ† 0fĚ$$$`ăĆŤčŰ·Żü˙ é<~üBˇBˇ—9r„˝'ˇSSSđx<888ŕý÷ßGrr˛îýĚăAßË ŻOž*+Ő¶§ˇ¶…«Ž¦ŕwî ±&II0ÜĽ|ˇ°N]###€Çă!//O-›ąäisCSeL›eőŻnó¬YłžžŽˇC‡6*ś–蟆xôčÎť;OOO…ó“&MBrr˛BŰúÉ'źŕĆŤpttDpp0ćÍ›{{{@UU,X€ˇC‡ÂËË „«úL§‚‚‚0aÂ,]şS¦LAź>}бcGMô[¸ĆĹ…)S¦ ""Ű·oÇčŃŁ±jŐ*Ěś9Ó§OW+CCC:t={öĢE‹0nÜ8ěرCŢöqM—@ Ŕąsç°sçN„„„ŕüůóX»v-D"ř|¶lńw¤ŮÖü!C†ŔŐŐ*Żëééˇm۶ Dnn.fĚřřx¬Ył®®®u†ikk‹C‡5Ú.¬]»×®]ĂóçĎ!‘HpóćM€źźpüřq”••a۶m¸vífĚ!˙?†z,^ĽĄĄĄHLL”— 333mŰójçNĽ>yĆÇŹĂ4;F[·âĺşu¨JOçěCŘĽy3âââśśŚ’’ÄÄÄ`ĹŠ¸pá‚Îý¬çč~§Nx}ü¸Ú~n¨m᪣±ÉŽ‚Ľ\ł/ćĎÇËuëŔkŰFŃŃŕYZ*čuíÚ%%%ČĎĎGPPfÍš…ÜÜ\µln(O›š*cÚ,«ĚćúĂi‰ţáĘ‘#G0dČ´oß`ii‰!C†(M|ß˝{‡BMM 233QVV¦0`»wďţőŻaĺĘ•={6ĽĽĽŚçĎź«eŹźźöěŮääd”––"<<<OIGý.qqaÁ‚ضmp÷î]\ĽxŃŃŃ2eŠZḸ¸ S§N BFFnßľŤ3gÎ`÷îÝj÷ÇRRR––8ţ<ŇÓÓadd„6mÚ°ń7DŻ9Ő¶m[DEEańâĹxňä‰ęQ˙ź3~999Řľ};@*•ĘgkOź>­ňF’™™‰[·n5ڶׯ_ăúőë*ŻŮÚÚâرc ç¤R)ző꥖; .„““&OžŚĘ?ź@Öްž={†{÷îáĺË—Đ××׉ŤÚ¶G˙łĎ ďîŽźŽš[· č×­ĂÂ@ż˙ŽŞ?; ůÜÝÝáĺĺ…E‹ˇ´´öööX˝z5*** ‹uęgý)SP}ů2jnßVËĎ\Ú.:š¤ę­kun.LÎśţřń¨ŚŽ–źż}ű6\]]annŽI“&!22Ó¦MĂ­[·8ŰÜPž674UĆ´YV˙6oŢĽ›7onT8-Ń?\ÉČČŔĎ?˙ ěر&LŔýű÷ĺŻZ~úé'…ă§OźÂÜÜ\iŔvěŘ1„††"<<ĹĹĹjŮbjjŠvíÚA*•ĘĎýúëŻxřđˇĆű-\ăj XYYaٲeX¶l™Â5u'zőę…;wî(ůZÝ´WWWŁŞŞJţ¶âË—/ADţxÂ`0ŘŕąYĐłgOXZZ"úŤŽŁžŢ¦bäČ‘řůçźńěŮ3ůSß7; R ÓĆĆÎÎÎX°`A“l{öějjjÔúOmCŰT†"!!!lmmţkmmŤ_~ůE)ĚéÓ§ăÁi뢬¬ vvv çěěěpűŤ§c\tjyőꕼĚP$44žžž:u*d2™ŇőÜÜ\ 8P~,đńÇ+}#Ş-´fŹžxFFŔ[7Ş®ď­Yňú|¨§§cccĄŕkjj`hh¨S?ëOž zôHé‰-{¸´-\ŰźwĎĚ ü@żýVăÍçĂČČsş¸ćisCť2fffkkk•éŃfYe6×NKôO-Ďź?Ż÷‰ă±cÇĐ©S'řűűC("..®Qń„……á˝÷ŢĂĉ1|řđz×sQĹ“'OP^^®đŐŇŇRéUcMô[¸ĆŐËËËq˙ţ}•@TQ_Ů(((Ŕ|Pďw×ęôÇ Cˇ_ÝDŐʱ...TZZJţţţÔĄKňöö¦’’ĄU )''‡/^Ü$jWŰ®ëú”)S¨°°<<<ČÖÖ–D"Éd2ęׯźZ:µK111daaA­[·&@ŔVą(<<ś®]»FŁGŹV؆ĚĘĘJi “yóćQ÷îÝiýúőTXX¨ó­Şšlź/ßÎČřřq2üöŰ?ŽßÇpÓ&2IJ"˝ˇC‰ß©éŹO¦—.Që7VĺâĂďż˙ž˛łłiÄdkkKžžžTTTD‹-Ňťź Č$5•ZĎ›§1?sYIű]®¶m°~=éŹK‚>}HďÓOÉhĎ2ąx‘ř;ĘW±Ý¶mą»»Sż~ýhĐ AE2™ŚĆŚŁ–Í\ňÍlµmuňtáÂ…ň­łŢYd67ŮfmĆ%äí[rr2mßľťěěě]f‚‚(33“zőęE–––d`` ¤łsçN*..¦={ö(]‹ŚŚ¤…s ´lŮ2ůń1c¨°°ěěěä}©TJ={öTËV‘HD9994bÄęÜą3m۶Ť VŠÖTż…K\\|XŰŹ›;w. …B˛łł#…Éą” CCCJNN¦“'O’ŁŁ#ŮŘŘĐ#ČĎĎŹsşjWŰĆź[žÉd2222˘6mÚL&#kkkÖ'c„mUŐ˛ĎČ××—Äb1RRRMź>]IÇŰŰ›nܸAďż˙ţ;<óx<‰D”žžN%%%”śś¬´ź4ťZéÝ»7ť8q‚¤R)ŰŞę ÉĘĘRą•×Úµkô&OžLb±ŠŠŠ(11‘śśśtj·&ěᙚ’©D˘$ĆÇŹ˙332 #“3gČ43“ŚOś V„7&_¸řĐÜÜśVŻ^Mééé$•J)%%…ćĎźŻ4‰ŁM?ë{xéĺËÄ{cßT?ëzđlřŐWdňß˙’éĄKdrî~÷ń;wVh3ľůćJIIˇ‚‚ĘÍÍĄřřxryck®6sÍÓć4xV'OëëLk»¬2›ëG[qŐµýcRRRŁ|dddD›6m˘üü|’Édäă㣤3räH’Édäćć¦öŕŮĆƆrssićĚ™ :ëÖ­Łääd266VkK»/żü’ňňňčęŐ«4gÎJMMU[Sý.qqőá„ čÔ©STXXH‰„bcciěرj—ŤÚ­Ş._ľLR©”Nź>M#GŽäś.6xf„I‹<3a„ &L4w©ťhWµ¶.E PAAAťÍ–&LhSص Á`0MÄŘŘÖÖÖ;w.Ž;&_ˇYWŘŘŘŔŢŢiii¨¬¬Äś9sđűďż#˝žm [B\ ˇKŘď Á`0MdÆ 8yň$¤R)¶l٢űź???ś?b±˝ző‚żżżÚŰ>5·¸ C—đđç»Ű Á`0 Á`0TĂž<3 Á`0 ÁĎ Á`0 Á`°Á3Á`0 Á`0lđĚ`0 Ńś‰ŠŠÂ®]»8éZYYA&“áĂ?lö4•ČČHÄÄİBŔ`0Ř๥ӱcGlÚ´ )))ÉdŚŚl1¶K$řřř°Rúžžž8zô(rrr““ŘŘX 0@IĎËË b±EEE8}ú4¬S»µeO+<“‹arń"Ś˘Ł!8PQ§U+„……!-- R©çĎźG`` ‚ŽL&S)ß˙˝Úyˇ­tq±' §NťÂµk×——‡#GŽŔÉÉ©QeL菣ݻa’š “ÔTíÜ Aßľjç…6ó«uëÖ X,†T*Ezz:ţýďŁcÇŽÍşîhłM`6׎&âârçR/@ @JJ D"QŁţ˙âĹ ?~Ź=ŇHŢ5ŐÁ`pç/˝‘µP(¤•+WŇ„ (%%…"##[Śí‰„|||؆äoILL -X°€†J¤]»vQAA …BąŽłł3Éd2‰DÔŁGúꫯH*•’­­­NlÖ¦=úžž¤ďćF‚ţýIĐŻDDiVń»v•넆†RNN :”¬¬¬ČĂĂnܸAžžžr;;;qpp ’’š8q˘ZyˇÍtq±gÚ´i4iŇ$rpp {{{úöŰo©¸¸zô術tnÝJ­ţůOŇst$ÁÇ“áćÍdš‘AüÎťŐĘ mć׺uë(33“\\\¨[·näääD_~ů%őéÓ§ŮÖmÖAfsýáh*..÷w.ő˘12qâDĘÍÍ%SSSůą¨¨(Úµk—Nî1ş¶'22’bbbX… &ůű$öôéÓM<űřřD"!GGG:uęQFF†ĽĂČăńhîÜą”––FĹĹĹtîÜ9?~ĽB é™™‘L&S)S¦LaV…ššRii)* bccĺÇ€.]şDˇˇˇ:đëĚ==2˝|™Zy{ËĎ=z”¶nÝŞ —””DkÖ¬©3’H$ÔşukµňB›éjŚ=zzztăĆ š1cFłHĎÄ„LŻ\ˇVľľMĘ‹w™_‰„ZTÝŃfü«ŰlbbBVVV*Ë—pŢEş¸ŢßąÔ‹†„ĎçÓąsçhѢE 磢˘(&&†Ö­[Gůůů”ťťM"‘HA§[·n ÷ő?üPĺ@tßľ}´rĺJĘĘʢ+W®Đ’%Kމ=  ˝{÷Ň•+W¨¨¨~řárwwWŠcćĚ™$‹©°°233iÓ¦M*Ď"‘._ľÜ ÍL0aŇR…}ó¬& Á_|ţýű#88Oź>•żą`ÁlذcĆŚABB6nÜľoĽ‚ŮÎăÇŹ! ! QQQđđpůń‘#GX¨ŔÔÔ<<źëŰ·/®\ą"?®®®Fnn®B^h]ŮĂ32B+€ÇCuAü|ff&ú÷ďŹ>řŕŕŕ€÷ßÉÉÉŞĂáńŕëë‹řřxĽzőJ­ĽĐfşÔµÇČČŕńxČËËÓyş&&Źިht^Ľëüúý÷ßáŕŕSw´Y˙ę6Ďš5 ééé:thŁÂŃY{ȱ^4Ägź}+++ěŮłGéÚŕÁQYY wwwDFF"88...ňë%%% … ľ¦ţÉ'źŕĆŤpttDpp0ćÍ›{{{ŤŰóŹü?ţř#0jÔ(T¨ŕި¨ŔÚµkqóćMüç?˙ÁŮłg1}útµăą{÷.:„ššdff˘¬¬LĺäBSíILLÄ®]»źźŹ»wďb˙ţýJĄ>|¸\§S§Nxůň%.\¸€ű÷ [·nUŠëńăÇXłf Š‹‹{÷îélšÁ`0Řŕą™đúők\ż~]ĺ5[[[HĄR…sR©ť;wVK‡Áť… ÂÉÉ łgĎFeeĄĽ3ü±€ĘłgĎpďŢ=Ľ|ůúúú:±QöÔÜ˝‹gŢŢx>{6ŞRSa°bř>ewwwxyyaѢEpssCdd$VŻ^ŤaÆ© ĎĎĎ™™™¸uë–ZyˇítqµçöíŰpuuĹÔ©S‘śśŚČČČ:ë 6ŇUK«9s çč‹uÄĹ%/Ţu~]şt C‡Ĺś9s——www$''ĂÁÁˇYÖmÖÁżÍ›7o†P(DRR’Úáč˛}ćR/bĚ1°¶¶Ftt´Ę륥Ą¨®®–ÁÖÖVíx~úé'…ă§OźÂÜÜ\ăö››cůňĺHHH@FF˛˛˛Đ­[7Ču’““ńŕÁĹblذţţţ°´´TŠëćÍ›¨©©‘?|řPĄÍ ÁĎ#ž={¦psŕiD‡ˇHHHĽ˝˝1mÚ4ÜąsGa‚ăńăǰ°°@JJ \\\PZZŠ6mÚ ĽĽ\'.Z·§˛52ŞŻ^ĹËĐ ̄Őo7„‡‡ăŔHLLDqq18±X¬rĄV8;;ăŕÁjç…¶ÓĹŐžĘĘJ””” ;;Ë—/ÇŁGŹ0gÎÝĄ ŚŚŚt–®Ö‹AßÍ ĎgĎFMYYťz ĺ…®ň‹đÓO?)<­ŇEÝ133µµµĘr¬Í:Čl®?MĹĄ\ęNCŚ9]şt©wďä®]»B ČŹ»wď.˙´KÓ4Ő>źŹÁcűöíČĘĘÂ/żü‚ŠŠ tęÔIĺÄHZZ6nÜńăÇă˝÷Ţ{gŰ÷1 <ë@;;;ŘŮŮÁŔŔććć°łłP(Ôx\€»»;<<<`kk ‘H„>}ú`ďŢ˝jéÔrëÖ- 6 hÝşµÂ đďLxx8|||###yţZYYÉuöěŮţýűcŢĽyčŢ˝;Ö¬Y##Ł&}ëÖ´fźĂoľţ¸qôé ""Ŕ ńúĎNcUU.^Ľ3f`ݵµ…§§'ĆŽ«Ô±444„——Ž=Şň©—ĽĐVş¸Ř#°mŰ6¸»»Ł_ż~4h˘˘˘Đ­[7…ď~µ–.­—.…ľ‡^FE††ŕwď~÷îŕ˝ő qCyˇÍüÚż?aooŹ=z`úôé4iÎź?ŻÓş€ .(íŰ­í6Ů\8šŠ‹ëýťKÝá‚H$ÂáÇë}BnnnŽ+V K—.8q"ĆŽ‹Ľ“űJSí©©©AYY >ź===„……á˝÷ŢScܸq>}:şuë†:ŔŰŰD„’’Ö!a0KţŇˉ׵őSRRRŁ·ŞŞë:ŹÇ#‘HDéééTRRBÉÉÉJ{IrŃ©•Ţ˝{Ó‰'H*•˛­ŞŢ¬¬,•yşvíZ˝É“'“X,¦˘˘"JLL$'''ťÚ­{x<2XµŠŚOś ÓŚ 2ąpŚöî%˝QŁôĚÍÍiőęŐ”žžNR©”RRRhţüů$ôĽ˝˝éĆŤôţűď7)/´•®†ěáńxôÍ7ßPJJ Pnn.ĹÇÇ“‹‹‹nŇÉąsd*‘(‰Áçź«•ÚĚŻYłfQ||<ĺääĐőë×éż˙ý/źĎ×iÝY¸p!Éd2őVąĐE›Ŕl®?MÄĹőţÎĄî4$ÎÎÎTXXHíŰ·ŻS§vk¨őë×Ë·†š7ožŇV^Şl>{öl˝{&'$$вeË4nŹťť;vŚ.]şD©©©´dÉŠŤŤUhśśśčČ‘#ňú~ňäI9rdű<żm3&Lü„÷çÁ`0 † âââPPP€/żü’ŮĂ`0cV170 Á`(cnnŽ6mÚ &&Ďž=cö0 Ćßöä™Á`0 Á`0Ś`«m3 Á`0 ÁĎ Á`0 Á`°Á3Á`0 Á`0lđĚ`0 Á`0 <3 Á`´ Äb1‚‚‚ÔúϨQŁ “É`ddÔblŽŠŠÂ®]»š_–ĎÇşuë ‘H “ÉÉ Ąšţ±˛˛‚L&Ç~¨˛¬Čd2Čd2¬ZµŠ9”µQďŚúĘ!<˙IÇŽ±iÓ&¤¤¤ÔYˇpęÔ)\»v yyy8räśśś”ô>úč#ěßżůůůČĎĎÇáÇabb"żîé鉣GŹ"''999ŤŤĹ€ZDćH$řřř°Ú˙\óÔËË b±EEE8}ú4¬S»µeŹ©D˘R 7m’ë,^ĽX~S¬•¬¬¬:ĂtqqL&SŮjN~nŐŞ––©TŠóçĎ#00PAÇÜÜëÖ­CFF ‘€A©Ý¶h“·óŞVľ˙ţ{µŇĄ©ö°uëÖ X,†T*Ezz:ţýďŁcÇŽÍşîhł¬2›ë§%ĆĹh^ >1cúőë‡ÖŻSÓ?/^ĽŔńăÇńčŃ#ĄkÆ P(DFF†Öý$’’‘HÔ,úĆőŮĂúüM§ľrŘý¬“Áł>|-[¶ŕöíŰ*u*++±gĎ"00wďŢEtt4zôč!×éÖ­> ň|öl şUII zĄĄĄpss“‹ŻŻŻĘđlll°téRë4]\XĽx1&OžŚĎ?˙ÇǶm۰|ůrxzzĘuľúę+ 4!!!;v,~üńGDGG+ŘĚĄmŃ&oć“››¦NťŠęęjś9sF­tiŞ=\ąr%&OžŚożý&LŔňĺËQ^^ ‹f[w´YV™Íő‡Óăb4?lmmńË/żŕúőëxüř1^ľ|Éś˘¦~˙ýwüë_˙ÂO?ýÔ¬l?~<Ú¶m‹}űö1{ţ4×rX¤K9}ú4EFF6¨§§§G7nÜ 3fČĎmٲ…öď߯V|¦¦¦TZZJjŰęăăC‰„éÔ©STTTDÔ§O@<ŹćÎťKiiiT\\LçÎťŁńăÇ+„ŃŽ™™Éd2•2eĘŇu~5GQ•§111+?téŇ% Ő‰Ťş´§ŐÔ©d"Zµ’ź[Ľx1%$$4ř_@@ńńń4věXÚż?íÚµ«Ů¤K•=z”¶nÝŞp.))‰Ö¬Y#oGŠ‹‹É××WAçüůó´lٲ&µ-Ú’H$ÔşukµŇĄ©öP"‘P@@@‹Ş;Ú,«u›MLLČĘĘJ^ţÔ G›ţŃT\úúúAąąą$‘Hhţüů$‹)((H®łaĂÚ»w/}űí·$‘HčŇĄKôĎţS!śQŁF‘L&####@666táÂZµjńx<ąŤ_~ůĄ<.‘H¤—¦l@'N¤łgĎRaa!Ą¤¤ĐěŮł‰ĎçËŻGEEQLL ­[·Žňóó);;›D"‘ÎÚżČČH•}¤·ű’3gÎ$±XL………”™™I›6m’_kÓ¦ ѸqăţóŮgźQqq1YXXp ‡‹pí× 0€öîÝKW®\ˇ˘˘"úá‡ČÝÝ]íľ(˙tëÖMáÚ‡~X§ýű÷ď§U«VŐy˝ˇňŁ®đ‰´Î IDATů|:wî-Z´H-öíŰ—JJJäőoţüů´cÇĘÉÉQ»ž6d×üj(.Möů[bĹĄFFFŇľ}űhĺĘ•”••EW®\ˇ%K–(éŐWO5][Ä7ĎFFFŹÇC^^žüĽŁŁ#˛˛˛°sçN\ľ|§Nť‚··w˝a™šš‚ÇăáÁŤ~j‚/ľřýű÷Gpp0ž>} ŕŹW",X€ 6`Ě1HHHŔĆŤŃ·o_ů˙Ňyüř1„B!„B!***.?>r䛞â§}űöĹ•+WäÇŐŐŐČÍÍUČ m˘3{x<č{yáőÉ“@eĄÂ%ˇP‰D‚ěělěÚµ ]»vUúűŇĄKqëÖ-ś={¶yĄ«233Ńż|đÁĽ˙ţűHNNđÇ«W|>Ż_żVřß«WŻĐ§Oź&µ-Ú‚ÇăÁ××ńńńxőę•ZéŇT{řűďżĂÁÁ-¦îhł¬ţŐmž5kŇÓÓ1tčĐF…ŁM˙h*®yóćÁÍÍ K–,··7úőë§ň3'''H$ 0ƨQŁT†ŮĄK9rgÎśÁŞU«äo¶a„ Xşt)¦L™‚>}ú4ę“.6O™2Řľ};FŹŤU«VaćĚ™>}ş‚ŢŕÁQYY wwwDFF"88...:iĂ  …;wîČűHaaa ö†……á›oľÁ§ź~ŠŔŔ@”––ĘŻ?zôçÎťSx+ &Mš„äädy›ŘP8\ŕÚŻűÇ?ţü5j<ďľűNĺ§5őőEąř§¤¤Bˇ°Éź0p-?ęđŮgźÁĘĘ {öěQۇçÎťĂÎť;‚óçĎcíÚµ‰DŕóůŤŞ§ŞěQ'żę‹K“}ţ–ŘFq-‡ź|ň nܸGGGcŢĽy°··ç\ß5]›őŕąk×®())A~~>‚‚‚0kÖ,äććôôôжm["773fĚ@||<Ö¬YWW×:Ă\Ľx1JKK‘ŘčÁóÚµkqíÚ5<ţ‰7oŢřůů!!!ÇŹGYY¶mۆk×®aĆŚň˙sŃa¨ÇŰyާ§333]ÚŁçč~§Nx}ü¸Âůk×®!,, řüóĎŃ®];=zíŰ·—ë 2®®®u~GÖÜü ›7oF\\’““QRR‚¬X±.\&Ż^˝ üă˙€@ €‡‡ţď˙ţm۶mRۢ-† [[[:tHaÜPş4Ů~ţůçčŰ·/._ľŚÝ»wcÖ¬Y e§ąŐm–Ufsýá´Ä¸Ŕ××űöíĂůóçqăĆ ¬X±Bˇ3^ËÝ»wqčĐ!®_żŽ~řAĺ'1Ý»wÇáÇqčĐ!|ýő× ×üüü°gĎ$''Ł´´áááŕńxjç+›,X€m۶!!!wďŢĹĹ‹Ť)S¦(čUTT`íÚµ¸yó&ţóź˙ŕěŮłM ˝k:uę„—/_âÂ… ¸˙> °uëVť#GŽ`Č!ň¶ËŇŇC† QčLs GS$&&b×®]ČĎĎÇÝ»w±˙~HĄR >\­ľ¨6áZ~Ô™‰DŘ·o***FJJ ŇŇŇçĎźGzz:ŚŚŚĐ¦Mµëi}öpÍ/®q5•–ŘFqĄÖćššdff˘¬¬LaTSő”kž6ëÁóíŰ·áęꊩS§"99‘‘‘čÜąó†˙Y rrr°}űvHĄRěŮł?ţř#ĽĽĽT†·páB899aöě٨|ë)W^ż~Ťëׯ«Ľfkk ©TŞpN*•ĘmćŞĂப<­­ŔĎž=Ă˝{÷đňĺKčëëëÄF]ÚŁ?e Ş/_FÍ[k $''ËäKNNF@@Zµj%Ż;m۶ETT–/_Ž'Ož4»tŐ…»»;ĽĽĽ°hŃ"¸ąą!22«WŻĆ°aĂä:Ë–-CeeĄ|a­É“'ă‡~@MMMŁŰmâçç‡ĚĚLÜşuKá|CéŇd{xéŇ% :sćĚA^^ÜÝÝ‘śś ‡fYw´YV˙6oŢĽBˇIo­ŁŔ%múGSq™™™ˇm۶(,,”źűß˙ţ§˛s_VV¦t¬ęűę´iÓF!LŕŹ·AÚµk§ĐOřő×_ńđáCĄ0fÍš…’’ą 2D-›-,,`ee…eË–),D…¸JKKQ]]-?.**jÔwăő٬ŽNCÔ>=‹Ĺذaüýýaii© “‘‘źţ€ &ŕţýűň×p4ełąą9–/_Ž„„ddd ++ ÝşuSąPe}}QmˇNůáĘ1c`mmŤčččFýżşşUUUň·˛^ľ|)˙ýć›R\ëi}öpÍ/®q5Ąî´Ä6JŢţúéÓ§077W»žjŞę5çQee%JJJŮŮŮHLLÄś9sŠĘĘJ<{öLi¦íöíŰ*WÎ ÁäÉ“1mÚ4Üąs§Ń6={ö¬ÎÎh]pYdHW µdęĘÓׯ_ăńăǰ°°@LL RRRmÚ´AyyąÖíÔ•=üގ7h^„†6¨űčŃ#üüóĎňם{öě KKK…†žŢÍEaa!FމźţąYůÂĂĂ-’Z\\ D"Ĺbyáĺĺ###ă·ß~ĂîÝ»ńË/żČŰuÚmbccggg,X°@ĺdc}éŇt{řúőkddd ##[¶lÁîÝ»¬Ń§QšŞ;Ú¬ĚćúĂi‰qŐŢź«ŞŞ”lPš°|k`.T>‘Y·n:uꄯżţ...*멪ɂ7‰ŹŹGjjŞBgY›kĂ Rši¨ŹŇاLő٬ŽNCüöŰo5j{{{řůůaţüů;v¬<ď‰qqqđôôÄŽ;0iŇ$=zTˇŹÇ%MŮĽqăF´iÓëׯÇíŰ·Q]]Ťť;wŞ|zؾ軚tăR~¸"‰űNV]~łĚr­§őŮĂ5ż¸ĆŐ”şÓŰ(uPUÖߌŹk=ŐTlQű<óů|…}Çňóó•fK¬­­•2844žžž:u*d2Ů;łŻ¬¬ vvv çěěěVç˘SË«WŻäÔĘÓÜÜ\ 8Pˇqřř㏾™×&ş°GňdĐŁG¨úó•ĺ†fŰ:věű÷ď®^˝ŠŃŁGĂŐŐU.yyyČÎΆ«««\Ż9ůYOOĆĆĆ*]CCCĄóĎź?Çożý†:ŔÉÉIa;®m‹¶™>}:ŚíŰ·#++ żüň ***Đ©S'ť÷·ž?®r} uĘFމ.]ş4¸§¸&úĆ\ęi}ö¨“_\Ű„†ŇŐÔúŢÜÚ(MĂĄžjŞędđ,`gg;;;ŔÜÜvvvňíR¶mŰwwwôë× BTTşuë¦đmŢÁáěě téŇŢŢŢpvvF||Ľ\'<<>>>€‘‘‘<^+++Ť§ëŔpww‡‡‡lmm!‰Đ§OěÝ»W-ťZnÝş…aÆÁ­[·V(ěg¸äéž={п̛7Ý»wÇš5k`dd¤đť¨6Ńş=ĐwwÇë'€·f"?^»?~<úő뇑#Gb×®]¨®®ĆŃŁGü±çŢŰ{ żxńB~ľvvł9ůąŞŞ /^ÄŚ30bÄŘÚÚÂÓÓcÇŽUl~ôŃGđńńAż~ý0jÔ(ěŢ˝÷îÝSřÎŤKۢm áĺĺ…ŁGŹ*Í.sM—¦ÚĂýű÷#00öööčŃŁ¦OźŽI“&áüůó:­;¸páśśśtZ™Íő‡Ó\ăjčţîëë‹öíŰĎçcáÂ…*ďÉćććX±bşté///Ś1±±±*ì®®Ć˘E‹`gg§đ6Éľ}űäíXçÎť±jŐ*•u^6oٲÓ¦MĂÜąs! agg,Y˛¤ÎtMś8cÇŽĹšm?aܸq>}:şuë†:ŔŰŰD$›±–_ýb±aaaHOOWz2Ć5®ÔŐŻ«©©AYY >ź===„……á˝÷ŢÓą/srrŕěěŚ^˝zÁŇŇRa ͵üpA$áđáĂ >)ÔDßK=­Ďuň‹k›ĐÔtµÄ6JŰő]“uPëŰ ÔµdxRR’|;§oľů†RRR¨  €rss)>>ž\\\”Âňőő•/Mž””DÓ§OW¸ž••Ą2®µk×6z«Şş®óx<‰D”žžN%%%”śśL'NT[§Vz÷îM'Nś ©TʶŞjDžNž<™Äb1Qbb"999éÔnmÚŁďáA¦—/ĎĘJĺő-[¶Pff&QVVíر„Ba˝aŞÚŞŞąůŮÜÜśVŻ^Mééé$•J)%%…ćĎźO@®Ó«W/JLL$©TJ‰„6oŢL:tP»mѶx{{ÓŤ7čý÷ßWyťKş4ŐΚ5‹âăă)''‡®_żN˙ýď)((¨IŰ“h˘î,\¸d2Ť5JçuŮ\8Í1.4° ĚęŐ«éęŐ«”’’BË–-ŁÔÔTĄm`˘ŁŁ)**Š®_żNYYY4wîÜz·@&L 7nĐŔĺ[Ď}ůĺ—”——GWŻ^Ą9sćPjj*Íś9Să6×ĆęÔ)*,,$‰DB±±±4věXĄ­ŞÖŻ_/ߪjŢĽy:ď ‘X,VyÍÉɉŽ9"oŁNž7Ôĺâź•÷‚łgĎ*éѦM›(??źd2ůřř¨U~¸łł3RűöííĂÚ­ŞP(”׳6mÚL&#kkkÎő”‹=\ň‹K\šęó·Ä6ŠK9ŚŚŚ¤…˙%$$(lĂ©N=ŐDŚ1a„ &L4M6lŘ@;wîÔx¸€ ęś@`Ň4©ŚŞÚ·ś‰v$..Ž"""šM=Ő”=ďŞM`m”n…}PË`0 ŃL°±±˝˝=ŇŇŇPYY‰9sćŕ÷ßGzz:sŽ166†µµ5ćÎť‹cÇŽÉWefhsssüřăŹ8|ř0ł‡µQ-6xf0 Áh&đů|řůů!""UUUČĎχżż˙;]lçďȆ đé§ź"55[¶laŃŘĽy3ł‡µQ-ţ|w›Á`0 Á`0 †jřĚ Á`0 Á`°Á3Á`0 Á`0lđĚ`0 Á`0 <3 Á`4#Äb1‚‚‚ÔúϨQŁ “É`ddÔblŽŠŠÂ®]»š_–ĎÇşuë ‘H “ÉÉ Ąšţ±˛˛‚L&Ç~¨˛¬Čd2Čd2¬ZµŠ9”µQďŚúĘ!<˙IÇŽ±iÓ&¤¤¤ÔYˇpęÔ)\»v yyy8räśśś”ô>úč#ěßżůůůČĎĎÇáÇabb˘v8͉DVűßÂÓÓGŹENNrrr‹(éyyyA,ٍ¨§OźĆŕÁuj·¶ě1•HTŠá¦MrťĹ‹Ëoе’••Ug...Éd*;PÍÉĎÚl[Z*šČŻÖ­[#$$b±R©éééř÷ż˙ŤŽ;6뺣ͲĘl®?ś–Ły1|řpxxx`ĆŚčׯ"""XżNM˙ĽxńÇŹÇŁGŹ”® 6 BˇZ÷“@ @JJ D"Qłč×gëó7ťúĘasôłNĎxřđ!¶lق۷o«Ô©¬¬Äž={ŔŔ@Ü˝{ŃŃŃčŃŁ‡\§[·n8xđ =z„ŕŕ`řűűăäÉ“ "µÂa´,\\\––†… "((/^ĽŔŢ˝{! ĺ:ÎÎÎřꫯ‡ &ŕúőëřţűďakk«›µiĎ3y>{6P]ŤŞ¤$˝ŇŇR¸ąąÉĹ××Wex666Xşt)Š‹‹uš®ćÖ¶´D4•_+W®ÄäÉ“ńí·ßb„ Xľ|9ĘËËaaaŃlmÖfYe6˙żöî<.ŞŞ˙řgq T\QQR“ĘÔR·°sAqĂ}ŔD÷TJź$52\yňg–„K¸Ó*iŠŕ*&‚˘‚Č(f–»¸ 00śßčäŔwp„Á>ď×ëľ^0sçŢ3ß{Ιó˝kÉË©Śë"ÓcooŹëׯăěŮłxđಳłăs˙ţ}|ňÉ'řăŹ?LŞěýúőCÍš5±aĂ–çŔTëaIDEN»wďĄÎ§T*ĹĹ‹ĹرcµŻ‰Ť7´>}Ë‘:Ť1BÄÇÇ‹Ž;Š]»v‰””qôčQŃŞU+@Čd21aÂ#.\¸ öíŰ'úőë§łŚŇć©QنP©Tz§ˇC‡ŠŠŢ^¦8UŻ^]¤ĄĄ ///ík!!!"44TűżBˇÇŹsćĚ©2VdẏŐ˘ŁĚ͵ŻMź>]„‡‡—úY…B!ÂÂÂDĎž=ĹĆŤĹÚµkMć{™ZßR&cmŻřřxáááQ©Ę\žuőe/sµjŐD˝ző„……E™–Sžń1ÖşĚĚĚ„żżżHLLńńńbҤI"::Zx{{kçY¶l™Xż~˝X˛d‰ŹŹÇŹü±Îrśťť…JĄVVV€hذˇ8tč?ľÉdÚ2~ţůçÚuůřřY—±Ę @ 0@ěÝ»W$''‹¨¨(1nÜ8!—˵hŃ"‘””$Nś8!|||*¬ Đ;F*ÜßôŃG"::Z$''‹cÇŽ‰+Vhßł±±)))˘WŻ^:źéÝ»·¸pႨU«–¤ĺH™¤ŽëŢyç±~ýzqňäI‘’’"öěŮ#\]] ‹J‰OóćÍuŢ{óÍ7‹-˙ĆŤĹüůó‹}ż´úcč$—ËĹľ}űÄÔ©S ŠaëÖ­Ejj޶ýMš4I|űí·"!!ÁŕvZZy¤nŻŇÖeĚ1e죤ÔĂ€€±aĂńź˙üGÄĹʼn“'OŠ3f™Ż¤vjě6X)®y¶˛˛‚‡‡d2Nź>­}˝cÇŽ‹‹Ăš5kđŰoża×®]6lÁË1ôČÖ´iÓđŮgźˇm۶đőőĹŁGŹśNĆZ×ĉŃ·o_Ě1Æ C›6mô^¦ĐĄKÄÇÇăťwŢ——|}}áěě¬w™M›6ĹÖ­[ńË/ż`ţüůÚ3[Ľ˝˝ŃżĚś9C‡E«V­ĘtI„”2:ţţţX˝z5zôčůóç㣏>ÂčŃŁućëÜą3Ôj5\]]___ôéÓ§Bú1???888 00ż˙ţ»vŚäçç§S^???|ýő×čÖ­ĽĽĽ––¦}˙Ţ˝{Ř·o¬łěA!22R;ž(m9RH×Ő­[Gއ‡śťť±iÓ&,]şTďei%ŤEĄÄ'55Ď} ÔúcŢ˝{Ł^˝zX·nťÁ1T(Ř·oÖ¬YiÓ¦áŔX¸p!||| —ËËÔNő•ÇíUŇşŚ9毌}”Ôzřď˙/^DÇŽáë닉'˘]»v’Ű»±Ű I'ĎÍš5Cjj*’’’ŕíí OOO$&&”J%jÖ¬ ///$&&běر Ă‚ ŕââ"y9eIž.\3gÎŕńăÇŹŹÇĄK—cĆŚAxx8~úé'¤§§cŐŞU8sć ĆŽ«ýĽ”yČ0Ó§OGZZ"""´uŁFŤ¸sçúöí‹„„ĽńƸ}ű6j×®]îĺ«Čň(;v„ĽQ#äţô“ÎëgÎśźź<<<đé§ź˘víÚضmęÔ©Łť§k×®pqq)ö:2S‹sEô-•‰1·×§ź~ŠÖ­[ă·ß~Ă?üOOOťşcje.ĎşĘ2—ĽśĘ¸.5j6lŘ€ŕâĹ‹;w®Î`ü©«WŻbóćÍBŕěٳسgŹŢKbZ´h-[¶`óćÍXĽx±Î{cĆŚÁşu뉴´4Ě›72™Ěŕí*ĄĚ“'OĆŞU«Ž«WŻâđáĂĆСCućËČČŔÂ… qéŇ%üď˙ĂŢ˝{ź+AzŃ5j„ěěl:t7nÜŔąsçđÍ7ßčĚłuëVtíÚUŰwŮÚÚ˘k×®:i)Ë1–¬]»III¸ző*6nÜóçĎăý÷ß7h,Zž¤ÖéűűeđńńÁ† ‘‘Q¦eDEE!&&pŕŔÄĆĆÂĘĘ 666·Ó’Ę#u{I]×óŞŚ}”TOËśźźŹcÇŽ!==]g¨±Ú©ÔmjŇÉó•+Wŕââ‚áÇ#22hҤIAÁźT„„¬^˝çϟǺuëpäȸąąI^ގrssqöěY˝ďŮŰŰăüůó:Żť?^g]Rć!é¦L™‚.]ş`ܸqP?9˛ú´+ dffâĎ?˙Dvv6ĚĚĚ*¤ŚYłˇCˇůí7äşţ722R{Ó¬ČČHxxxŔÜÜ\ŰvjÖ¬‰ŔŔ@Ěž=>4ąďe*}KebĚíuüřqĽűî»?~ WWWDFF˘}űö&Yćň¬«˙„2Ż\ąŘ_č> R–Sžń1ÖşjÔ¨š5k"99YűÚ_ýĄwpźžž^ä}×W‡„„ŔĆĆFg™@Á™Tµk×Ö'ÜĽywďŢ-˛ OOO¤¦¦j§®]»TćZµjˇ^˝z5k–ÎÍ#ýüüаaCťuĄĄĄAŁŃh˙OII)Óuă%•ŮyJóôčqtt4–-[wwwŘÚÚęĚsôčQ\»v ôďß7nÜĐ&^R—c¬2[[[cöěŮÇŃŁG‡ćÍ›ë˝QeIcŃňbHý‘ęĂ?„ťť‚ËôyŤFĽĽ<ää䲳łµ?{¦”ÔvZRy¤n/©ëzž¶Sű(CľúŃŁG°¶¶6¸ť« *My°§V«‘šš 8qâ"""0~üxĚ™3jµ™™™Eö´]ąrť:u’ĽCeff"??ß ĎHąÉPeżQE6m† ‚‘#Gâ÷ß×ůQyđŕjŐŞ…DEElllpűöír/gE•GŢ ”ť:!KB=żwď®]»†ĆŤZ¶l [[[ť Ą˛ »HNNF÷îÝqíÚ5“ŠsEô-•‰±ëann.Ž=ŠŁGŹ"((?üđ|}}Ťz4ĘXe.Ď6Č2—ĽśĘ¸®§żĎyyyEĘPd‡eˇÄ\ˇPč="łhŃ"4jÔ‹/Fź>}pýúuI; ž†ę – )óÓez{{ŮRÚĄ¬G™J*ł!ó”ćÖ­[pvvF‡Đ®];Ś3“&MBĎž=µŰ^;v`đŕÁřöŰo1hĐ l۶MgŚ'e9Ć*óňĺËacc/żüW®\FŁÁš5kô=,ËXôEít“R¤ňńńAhhč ąëňłuVj;-©jÔ(Ô©SrąS¦LŃű›lmmŤąsç˘iÓ¦pssĂ|€ĐĐP˝ËÔh4:u*1yňdíë6lŔرcńÁ I“&?~‘#JĆ*sPPFމ &ŔÁÁŽŽŽpwwÇŚ3Šý^ @Ďž=ńăŹ?šl_׫W/Ś=Í›7Gýúő1lŘ0!´g=uóćMDGGĂĎϱ±±EŽŚI]ŽTĹŤëňóó‘žžŽÎť;C.—C©TÂĎĎŻĽňJ…Ç2!!NNNxýő×akk«“HK­?Rřřř`Ë–-Ą)4ĆŘXJ;-©<†l/©}Âó~ŻĘŘG•w{7f,÷Ç wËđýű÷kçôő×_‹¨¨(qîÜ9‘(ÂÂÂDź>}Š,kÔ¨QÚ[“ďßż_Ś=Zç±PR—UUÜű2™LřřřŘŘX‘šš*"##Ĺ€ žçéôĆoźţYś?žŹŞzfŠ‹‹Ó[.\¨3ß!CDtt´HII˘K—.Zîň,ŹŮŔ˘úoż Y˝zzß ÇŽ)))"..N|űí·ÂÁÁˇÄeę{T•©ĹąĽú–Ę<c{yzzа°0‘ Ξ=+~ýőWáííý\Ź'1F™§L™"T*•pvv®đ6Č2—ĽS\Jy Ě_|!Nť:%˘˘˘Ä¬YłÄÁ‹<&88XŠłgĎŠ¸¸81a„@ôďß_\ĽxQtčĐAűřĽĎ?˙\ś>}Zś:uJŚ?^jçÎť˘{÷îzçíŢ˝»P©T˘o߾ϵ)SIă:GGG±}űvqüřqqđŕA1cĆ ZdlSÚXTJ|BBBôţVîÝ»·ČĽVVVbĹŠ"))I¨T*1bÄꏔÉÉÉI$''‹:uę”9†OU@888hŰ™ŤŤŤP©TÂÎÎNr;•R)ŰKĘşŚ5毌}””z BBBt>.fÍšU¦vjŚ6&cś8qâĉ'NĎ7-[¶L¬YłĆčËU(âÜąsĹî@ŕô|ÓÓdTßsË9•Ď´cÇáďďo2íÔXĺyQ}ű¨ŠťxA-‘‰hذ!Úµk‡¨ŐjŚ?÷ďßGll,cDU«V…ťť&L€íŰ·kďĘLĺËÚÚGŽÁ–-[XöQ•“g""""!—Ë1fĚřűű#//IIIpwwˇ7Űů'Z¶lşuë†"((© Xąr%ËĂ>ŞŇáÉąŰDDDDDDD¤źś! """"""bňLDDDDDDÄä™É3‘ ‰ŽŽ†···Aźqvv†JĄ‚••UĄ)s`` Ö®]kzXą‹-B||777DGG#%%»wďFçÎť+´ÜĆ(ŹĽ~}X.Z„Ş?˙Śęńń°ś7ŻČ<'˘z|ĽÎTmß>ťyžţ žľ˙ţűÚ_ÖľĄ21F=¬ Ô˙­`Ş Ô,ß3í2—wźŔ2—ĽśĘ¸.2-ďż˙>±cǢM›6đ÷÷ç¸ÎŔřdeeá§ź~½{÷ŠĽ÷Ţ{ďÁÁÁGŹ-÷8) DEEÁÇÇÇ$ĆĆ%•‡cţçWR=4Ĺ8WHňlii‰»wď"((W®\Ń;ŹZ­Ćşuëŕĺĺ///\˝zÁÁÁxíµ×´ó4oŢ›6m½{÷ŕëë wwwěÜąB˝xÆ 1sćL\¸pż8•\ź>})S¦ŔŰŰYYYXż~=´ó899᫯ľÂŽ;Đżś={ß˙=ěíí+¤ĚF+ŹĄ%DFÔß}‡ü«W‹ť-˙Ň%dގťO ó~ßľ}u¦áÇCŁŃŕ—_~yamđeí[*c¶‹Ě-Ŕ-7ŕ® >Ř,Ěß1Ý2—gźŔ2—ĽśĘ¸.2=ööö¸~ý:Ξ=‹ ;;›A10>÷ďßÇ'ź|‚?ţřäĘŢŻ_?Ô¬Y6l`yţLµ–DTä´{÷nPę|JĄR\ĽxQŚ;VűZPPظqc©źU(",,LôěŮSlܸQ¬]»¶Le1b„ŹŹ;v»ví)))âčŃŁ˘U«V€Édb„ "&&F\¸pAěŰ·Oôë×OgĄÍSŁF ˇR©ôNC‡˝˝LqŞ^˝şHKK^^^Ú×BBBDhh¨N8~ü¸3gN…”ńE”§ęćÍÂrŢĽ"Ż[Lś(¬~üŃ eą»»‹řřxaaańBÚŕËÚ·T¶ÉXő°^,D5OÝ×ꀨ1ĂtË\ž}ÂË^ćjŐŞ‰zőęéí/¤,§<ăc¬u™™™ ‘(âăăŤI“Dtt´đööÖÎłlŮ2±~ýz±dÉ/Ž?.>ţřcťĺ8;; •J%¬¬¬ѰaCqčĐ!1ţ|!“É´eüüóϵëňńń)˛.c•€0`€Ř»wŻHNNQQQbܸqB.—kß !!!bѢE"))Iś8qBřřřTX? wŚT¸ż˙裏Dtt´HNNÇŽ+V¬Đľgcc#RRRDŻ^˝t>Ó»woqáÂQ«V-IË‘2I×˝óÎ;býúőâäÉ“"%%EěŮłG¸şş<•źćÍ›ëĽ÷ć›o[ţŤ7Šůóçű~iőÇĐI.—‹}űö‰©S§ĂÖ­[‹ÔÔTmű›4i’řöŰoEBB‚Áí´´ňHÝ^Ą­ËcţĘŘGI©‡bÆ â?˙ůŹ‹‹'Ož3fĚ(2_IíÔŘm°R\óleeČd2ś>}ZűzÇŽ‡5kÖŕ·ß~Ă®]»0lذ"źź9s&._ľŚ˝{÷ĺČÖ´iÓđŮgźˇm۶đőőĹŁGŹśNŇŹůůůÁÁÁřý÷ßµc$???ťňúůůá믿F·nÝŕĺĺ…´´4íű÷îÝĂľ}ű0xđ`ťe4‘‘‘ÚńDiË‘B긮nÝş8rä<<<ŕěěŚM›6aéŇĄz/K+i,*%>©©©pppxîK¤ÖCôîÝőęŐĂşuë ŽˇBˇŔľ}ű°fÍL›6 ŔÂ… áăăą\^¦vŞŻ<†lŻ’ÖeĚ1e죤ÖĂ˙ű߸xń":vě___Lś8íÚµ“ÜŢŤÝM:ynÖ¬RSS‘””oooxzz"11 T*QłfMxyy!11cÇŽEXX,Xí2şví Ł] cii‰… âĚ™3xüř1âăăqéŇ%Ŕ1cŽź~ú éééXµjÎś9±cÇj?/e2ĚôéÓ‘––†mݨQŁîÜąľ}ű"!!oĽńnßľŤÚµk—{ůĘ»<šsç˝`˛&MBö˘EŐ¬ «ŕ`ČlmőÎßµkWŘŰŰcóćÍ/¤ ľ¬}KecězXýc ~\ÁőĎÖ_š?ÇŰLłĚĺŮYć’—SףFŤÂ† pŕŔ\ĽxsçÎŐŚ?uőęUlŢĽBś={{öěÁ¨QŁŠĚעE lٲ›7oĆâĹ‹uŢ3f Ö­[‡ČČH¤ĄĄaŢĽyÉdoW)ežŽ;†ôôtť Ćj§R·©I'ĎW®\‹‹ †ŽČČH I“&R!°zőjś?ëÖ­Ă‘#Gŕćć¨Ył&1{öl<|řĐ(eĘÍÍĹŮłgőľgooŹóçĎëĽvţüym™ĄÎCŇM™2]ştÁ¸qă V«µťÝÓ=™™™řóĎ?‘ťť 33ł )cy—'ďĐ!äíÝ ÍąsČ;xYľľ™›Ă¬_?˝óŹ3ÇŽĂĺË—ŤŢ_ÖľĄ22v=ĚÜÜÜ›¨“€»ÓÍMÓ,sy¶ÁB™W®\ ěßżßŕĺ”g|ڵ®5j fÍšHNNÖľö×_éܧ§§ů_ßőŐ!!!°±±ŃY&Pp&UíÚµuĆ 7oŢÄÝ»w‹,ĂÓÓ©©©Ú©k×®•ąV­Z¨WŻfÍšĄsăH???4lŘPg]iiiĐh4Ú˙SRRĘtÝxIe6džŇ<=zŤeË–ÁÝݶ…v =z×®]ĂŔýű÷ÇŤ7´‰—Ôĺ«ĚÖÖÖ={6ÂĂĂqôčQÄĹšyóćzoTYŇX´ĽR¤úđĂagg‡ŕŕŕ˛<Đh——§=‹.;;[ű·ĄĄĄÁí´¤ňHÝ^R×ő>> }!w]~¶ÎJm§%•Gęö’ş®çi;•±Ź2hĽˇ§®?»>©íÔXm°R=çY.—ëuęzôčítúôiś8q...¸qă†QË—žžGGGť×uîú+ež§rrr´É>éš3gŚáÇCĄRy?11:tĐéŢ~űmťëZËSE–GVŁäőëCÜşUä˝ŃŁGăÎť;’ř m/kßRY˝¨z}Y@ŐŃ[ć5jŔÎÎUŞT©Đ6Č2—ĽS]Wq>|»wď˘qăĆÚתWŻŽZµj™·YłfP(Ú˙[¶lYäHPpzŕŠ+žžŽ+Vh?óđáCÜľ}Żżţşv^[[[ťSNźĘČČĐ9â÷řńcĘ|űömܸqCŇNÁÂß«E‹e:M¸¤22ŹÔ,111Xľ|9úőë‡W^yĄČµ‹Ű·oGŁFŤŕîîěرŁLË‘ZćâĆurąť;wĆęŐ«‡ëׯ###Ť5ŞđߍǏëµ-Ký‘˘{÷îhÚ´i©Ź“5ĆŘXJ;-©<†l/©}BißëyŰ»©őQĆ&Ąť« VHň¬P(ŕččGGGXZZÂÚÚŽŽŽÚG ) ¬Zµ ®®®hÓ¦ :uę„ŔŔ@4oŢ\{]+lÚ´ NNNpwwGÓ¦M1lŘ0899!,, @ÁsĂ ?Ç6++Kűzá=4ĎëÇ„««+{{{řřř U«VXż~˝Aó@“&M0ţü2ŤW¤”9((#GŽÄ„ ŕŕŕGGG¸»»cĆŚĹ~Ż gĎžřńÇM¶ŻëŐ«FŹŤćÍ›Ł~ýú6l„Ú3Žžşyó&˘ŁŁáçç‡ŘŘŘ"GƤ.GŞâĆuůůůHOOGçÎť!—ˡT*áçç‡W^yĄÂc™'''Ľţúë°µµŐI¤ĄÖ)|||°eË–RŹcl,Ąť–TC¶—Ô>áyżWeěŁĘ»˝ł –űcŠ»eřţýűµŹsúúëŻETT”8wîśHLLaaa˘Oź>E–5jÔ(í­É÷ďß/FŹ]⺍ń¨ŞâŢ—ÉdÂÇÇGÄĆĆŠÔÔT) `đúČče~şţ]»v‰ääd/BCCEĎž=‹<ŞęË/żÔ>ŞjâĉŢ—y{{‹ččh˝ďuéŇElÝşU$$$łgĎŠť;wŠîݻ띷{÷îBĄR‰ľ}ű>×r¤L%ŤëĹöíŰĹńăÇĹÁĹŚ3Dhhh‘±MicQ)ń Ńű[ąwďŢ"óZYY‰+V¤¤$ˇR©Ä# Ş?R&'''‘śś,ęÔ©Sć>}Táŕŕ mg666BĄR ;;;ÉíTJy¤l/)ë2Öż2öQRęa@@€ Ńů\xx¸5kV™Ú©1Ú Śqâĉ'Nś8=ß´lŮ2±fÍŁ/WˇPsçλÓóMO“Q}Ď-çT>ÓŽ;„żżżÉ´Sc•çEő ěŁ*vâµDDDD&˘aÆh×®bbb V«1~üxÜż±±± ŽU­Zvvv0a¶oß®˝+3•/kkk9r[¶layŘGU Lž‰L„\.Ç1cŕďĽ<$%%ÁÝÝý…ŢlçźhٲečÖ­<   ¤‚ddd`ĺĘ•,ű¨JC†'çn‘~r†€É3“g"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™<1y&""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰É3“g"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™<1y&""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰<“g"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™É31y&""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰<“g"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™É3“g"""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰<1y&"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™É3“g"""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰É3“g"""""""&ĎDDDDDDDLž‰<1y&""""""bňLDDDDDDÄä™<1y&""""""bňLDDDDDDÄä™É3“g"""""""&ĎDDDDDDDLž‰É3‘Jc.¬z|üK¤‡ożÍř0>ŚăĂř0>ŚăĂřĽ /k ‰čĺęy䙨Lž‰*kňśéꊇożŤ‡ożŤěĹ‹ąĄČde/X€¬I“L®\âĆ <|űmhÎźgű"*ÇöEŚĎ‹îźŮ‡łQĹĐ^óÜ«W/|óÍ7EfP«Őhٲ% Ařä“OĐŞU+4nÜ۶mźźßß3k4xŘľ=,fĚ€ů:É…ćäIT —\°§ó>ž0Á$%%>Ć!CЬY3Ŕůóç±|ůrś Btt4ĽĽĽ´ŻçEF"köě˘+07GőcÇt_«Rf..Y[?Wű*Ź:&%>Ó§O‡ŹŹŹÎű·oßF‡Y~~ČŰ·ŻŘuü1,ĆŹ©ëĎ[o˝…™3g˘uëÖ€sçÎÁËË Ź=ްöURýÉ>ů©©˙™A^§”˝zÁâ㏅¨ńQ©Tz?…qăĆxr=“ą9ŞíŮŁm79K– ďčQTýé'Éí«2ÖsssĚ1}úôAÍš5ń×_aóćÍX»v­vţr|¬­­ńÉ'źŕ˝÷ŢŤŤ RSS±xńb=z´ÄńOÎňĺČ G•U« xýuŁőĎĺ9F2Ęřđ 777Lś8őë×GZZ«»Üuë ŢľâÎČíě`ńńÇPöčQ¶ÂK¨‡¦2Ž"˘J–C† Á”)SššŠNť:ańâĹČČČŔŽ;*}|ÄȋЂ™«+ “źŻľú -Z´Ŕ´iÓpýúu¸ąą!88˝zőBzzşŢĺĺ,Y‚ÜTůö[(^{íĄ®?RƇNNNřꫯ°lŮ28pîîîřţűďŃ»wom s·oGÎŞU°:жm‘÷Ë/ČúôSXŐŞEn^VăLD•,yNKKĂüˇwf•J…/ľřtކ•EnDÔë×#˙ęUČëÖ…ŮŔ05JoPśěąs‘÷.äµk#/&P*a>r$ĚÇŽ}a+)>:˙ź? xď˝÷´És^d$ĚÇŤÓîE•;8@ůďńđ!uëöwPđ6oĚú÷/x}Á7Pĺ™=ĆŹGŤ‚˘CXřúľ–źĐĐPť˙ŕęęŠ:h“gcÄ4‰‰Čůî;ä_¸‘™ yŁF0÷đ€YŻ^şËٱ9˙÷¨˛x1˛—/GţĄKŮŘ Ę’%ÚŁâÎä!ďčQGŹ oŇŢŢPľű®Î˛Ôk×B˝e Ě„EˇŁ¤ĄĹ V¬XĹ‹cäČ‘ĹÎ'oÚňýKď{ů*2‡ Ńţoµq#ŽŽ† 4%Ö1©mą¤8źěělś/ćô;yýúÚPţd€'··‡ÜŢľhý‘°Ý%}/#ôcĆj_“&M©S§t®ń…î^+©} QäČŹů¸q0ëŮSr ĺŤI®?˛ŞUµŰHţę«ČÝąšÓ§ Š’‘G={˘ĘÂ…Pvď®ó=˛>ű Őö쑟ÂőĆÝÝ>,’<+ťś Ţľćîî€RYćö%Ąß0ĄúÓ¶m[ÄĆĆâđáĂ€ź~ú &LŔ[o˝Ą“Jh.\@~r2Ěž zeÖÖPvë†Üť;u’çÜť;ˇtr‚¬fͲŐo™ ŁFŤBXXrrrt;wFţĄKČÝ·fzÚŹ”ö%Ąß0µúsěŘ1¸ąąˇqăƸrĺ Ú·oŹýë_ŚŚüÇÇGˇP@.—ëśé999hŐŞU‘zś€Ľ¨(X­YąŃűçĘŞuëÖذaĂßż› µ—™ĚLä§§é+oľ‰Ľ§§ÇéwĐŘă("ú'χŇůŐŞUX¶l™A ÍYş9K—ęvdvvżżf Ě==µGĺ Ŕ|Ô(äţď˙(Ę4€Ů AÚ=”Ę>€z۶–řźéÓ§#-- Ú×,çÎEÖgźáQ÷îP´iEűö0űđCČž9×ń)ď–źfÍš!"" …đôôDbb˘ŃăSřú(ů!P‡‡#ďČ‘"É3rr`1c†öŁâ­·´oĺíۇü?ţ@ŐźÖé•7lXô‹W«‹3ą毾 uh(4IIE’ç’âÓµkW¸¸¸ ·„UfˇSPÍ==a1qbąw ŐŐâ,%>gÎśźź®\ą‚:uę`ҤIضm>üđCťS ĄľřňHů^ĺÝNKŠŹR©DÍš5áĺĺ…5kÖ`ĺĘ•hßľ=,X€GŹa÷îÝ’Ű—zëVőę3mÝĘ;tą›7C±h‘ÁŰ´Ôť©żţŠÜ€Ľ< ?fýűë\›n>`Ošqëd¶¶wî ďŘ1TYą˛Ěýs×®]aooŹÍ›7ëˬa>|8r7mŇ›J!Ąß0Ąú+W®„L&Cdd$„ČÍÍĹÜąs‹|ćźźśśś:u #FŚŔÁqűöm¸şşâŐW_EZZšÎgÔ7Bܺśoę=ëĄ2{žńˇR©DŤ5pçÎôíŰ_|ńFŹŤŰ·oăŐW_-Hž32´;ÍtŞśµ5Ä˝{±‘i&Ď…Żiąuë–Á 5=ZçôŘśďżGţ“ŁCâî]›7‘óß˙"çż˙Őí «T1<ń+ô+oÔšB§*“ÔřL™2]ştÁ!C V«µŻ+ŢyŐvď†&!y Čýĺ¨×¬A••+ˇhŰÖčń)ď–ź+W®ŔĹĹÖÖÖ4h0räH\ľ|٨ń@˝n4'N ˙Î /âáCBGÎffPčąiä_¸ąť]±§HkcfoŻs*ˇÜĆâţ}Éń©Ył&1}út<|ř°Ô8ľćą"®ç5¸®–g)ő§đ°řřxÄÄÄŔÍÍM{ AŠ)Ź”ďUí´¤řČźÔ˝„„¬^˝@ÁiĘďľű.ÜÜÜ´Ésií ÄŐ«÷ë§[ż[´@ţ“K+ ݦĄţuéRpÍsn.4*r–//¸ćńÉé—Šöí!Ż_ą0wwGnDduę轜Cj˙«gŽ‚–źYłfaÉ’%8zô(4 Nť:…={öŔˇĐ‘eČd¨„¬YłóÝw°0‘›ž–çřGŮ“ëĚ 233ńçź";;fffĎlxQÂŹ,ź¬JD&š<—v͡¤N˛NČ[´řű˙W^)Ň9VYşÔ(G‡E^žî MÉđs’źiÓ¦aČ!9r$~˙ýwýÎöíˇhߌ,__ä|˙=¬VŻ6z|ŠÄKĎ ŞŚĂŇâŁV«‘úäîş'Nś@DDĆŹŹ9sć5>Ůź~ q˙>,¦M+8ëA©DÖ´izż—ĚĘŞŘf!12‰w..>-[¶„­­-‚˙nśO®'LNNF÷îÝńŕŮqD ×<—_ölX]-)ÎeéîÝ»‡k×®ˇqăĆeë§Š+Ź”ďő‚Ű©ˇńQ«ŐČĚĚÄĄK—Šě¬ęÔ©“ôţç™ Č÷Ős“%)Ű´Ôíđě5ĎÍš {É’‚;n[X2Ě\]‘»sgAňĽ{7Ěű÷×»^)ő§aÆprr*ń¦—2KK ő¦M×­[†¦!ŚÚvJÝÔŻ”]şü]ţbĘ\R|ćÍ›‡ŕŕ`í™R.\@űöíáăăS$y®,ńQ:9ˇę“ËňÓÓ‘5kކ†jŻy.Ľă¸¤ř\ąrnnn°˛˛BŐŞUqëÖ-üđø~ýşÎ|ćŁFAŮą3,§MCöâĹP¶o_¦]™˘çćććâÁ¨U«BBB°±±ÁíŰ· ꕍMÁć/´ăYddhß3dlCDô"”ű®E÷ Ę kĐČĚ,S|`n^4ů}şč5 ?~&Xů7nT ĺr9¬¬¬Śźü|äť8s(Ţ~˛şu!«Qů×®\>ĹkŻ!˙Ź?˙×_/4§NťBŹ=ŕâ⢝Nź>Ť'NŔĹĹ7ôl7Ł´ÇRÚWIuĚŕşjdÖÖÖhĐ Ńc#ĺ{Uôw×')) ö…Nµłł+2¸/©}€¬aCäşÓ{~j*dú.WxÎ>޸ůˇŃ@dgë$‡ůWŻB˝y3ň/_†Yˇ#ă†=z4îÜąýű÷—\ڎC‘S¦¶_Zżaěú#«QC{s<ą˝˝ÁGŻ•J%ŞV­Ş§+ÍG•b–Uâ#«^r‡‚éÉĄcÚ˙ ęš?~Ś[·nˇ~ýúčŇĄËߏŞ*´ăÉlđ`(;vDÖÜązĎB2V˙,©7‰‰‰ÚG Gˇß~űmí}OžîHËOJŇůśćěYťKB¤Žm^XEDLž%˙ŕ)ptt„ŁŁ#,--amm GGǢ§-•ÂbÜ8äîŘuHňÓÓ‘áÔ›7#GĎ©—Š7ßD^l,4))wîů‘Č^şůW® 7<y‡ĂĽ‚nú0oŢ<Ś1ţţţ°˛˛ŇĆŞ^˝z˙đNőĆŤĐ$$ ˙âEänŰVpó›®]ËyăĆĐÄÄŻ•+W˘_ż~hÓ¦ şwk×BŁŃ`۶mF_—”ďUžß]ŠM›6ÁÉÉ îîîhÚ´)† '''„……Ծ̇ Aîž=ČÝ˝ůż˙up04çÎÁ|čвýČ”P€żo”ŻR!/:ę~€âŤ7tÎX’ŮÚBٵ+rV¬€˛}űbʬ–¦J•*pssömŰJmS˛şuˇtrBŢ“»O»ß0Ąú“——‡Ă‡cěرřŕ`ooŹÁŁgĎžĹîdř'Ĺ(x†ú#ЦM8;;ă‡~Ŕźţ‰­[·űËĎ?Ôjd?ą ő‹čźĄĚS¤Ś×­[‡¶mŰbâĉhѢ,X+++ť{†Ü}ű Ţşů©©ČY±ů*•ÎuîRÇ6ĆęŁt~Ă ™ąjժصk—ö˙&Mš Gʏtéśťť%/Çlŕ@ŔҲŕ:­ďľĚŇňfÍôÎ̆…ćâEdŤńř1,ýütî¨ěŘČĚDćYY<ęâý÷+$...¨ZµŞöšĂ§6oŢŚąsç”·sgäíßup0Dn.äőëĂbüxŹS¦řXLťŠś/żÄŁ>}µZç Ę÷߇ىxěé YµjPöî Ĺ3§Ó—g óóó‘™™‰)S¦ nÝşČÍÍ…JĄÂäÉ“ń믿ţ]#ŧʗ_"{ńb<ęŮ2KK(?üP{M§!d––°úţ{ä!ËĎŻŕ±Wöö°đö6ŮFť5iňŽű;a=ş`€ŕŕ€Ş…ÍŇÚWiuĚşúĽd2üüü`ccű÷ď#!!łgĎĆŐBw>7)ß«<ż»°±±§§'fĎžŤ?ţř ,ŔÎť; k_®®·o#gŐ*íŁŞ,ýýˇ(|Wa©;"J¨?wřpA¦TBV»6”;ęm_f®®Č;tĘB7Ë3„««+ŞU«¦sň’ʉĽBÉŁ”ö%Ąß0µúóÉ'ź`úôéřâ‹/`ccëׯă›oľ)ň{VYă#oŃB{wĺ˛î`9r$š4i‚¬¬,ÄÄÄ૯ľBć3gméłjŐ*¸IßĚ™Čݶ fC†˝–2Oy2>= 0 && (amtRead = read(buffer, sizeof(buffer))) > 0) { // Parse the information further ... metric = handleBufferContent(buffer, amtRead); } else { metric = -1; } if (fd >= 0) close(fd); ``` While the existing code base isn't fully consistent with this code style yet it is strongly recommended that new code follows these rules. Adapting surrounding code near places you need to touch is encouraged. Try to separate such changes into a single, clean-up only commit to reduce noise while reviewing your changes. When writing your code consistency with the surrounding codebase is favoured. Don't shy away from leaving (single) blank lines to separate different groups of related statements. They can be a great asset to structure the flow of a method. ```c int stuff = 0; // If asked for gives only half the answer ... if (param) stuff = 21; // Compute the answer stuff %= 2; stuff *= 4; stuff *= 5; stuff += !!stuff; stuff *= 2; return stuff; ``` If you want to automate formatting your code, the following command gives you a good baseline of how it should look: ```bash astyle -r -xb -s3 -p -xg -c -k1 -W1 \*.c \*.h ``` Working with System APIs ------------------------ Please try to be considerate when using modern platform features. While they usually provide quite a performance gain or make your life easier, it is beneficial if `htop` runs on rather ancient systems. Thus when you want to use such features you should try to have an alternative available that works as a fallback. An example for this are functions like `fstatat` on Linux that extend the kernel API on modern systems. But even though it has been around for over a decade you are asked to provide a POSIX alternative like emulating such calls by `fstat` if this is doable. If an alternative can not be provided you should gracefully downgrade. That could make a feature that requires this shiny API unavailable on systems that lack support for that API. Make this case visually clear to the user. In general, code written for the project should be able to compile on any C99-compliant compiler. Writing documentation --------------------- The primary user documentation should be the man file which you can find in `htop.1.in`. Additional documentation, like this file, should be written in gh-style markdown. Make each sentence one line. Markdown will combined these in output formats. It does only insert a paragraph if you insert a blank line into the source file. This way git can better diff and present the changes when documentation is altered. Documentation files reside in the `docs/` directory and have a `.md` extension. htop-3.0.5/dragonflybsd/000077500000000000000000000000001377712513700151645ustar00rootroot00000000000000htop-3.0.5/dragonflybsd/DragonFlyBSDProcess.c000066400000000000000000000143741377712513700211160ustar00rootroot00000000000000/* htop - dragonflybsd/DragonFlyBSDProcess.c (C) 2015 Hisham H. Muhammad (C) 2017 Diederik de Groot Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Process.h" #include "ProcessList.h" #include "DragonFlyBSDProcess.h" #include "Platform.h" #include "CRT.h" #include #include #include #include const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, [JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, }, [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, }; Process* DragonFlyBSDProcess_new(const Settings* settings) { DragonFlyBSDProcess* this = xCalloc(1, sizeof(DragonFlyBSDProcess)); Object_setClass(this, Class(DragonFlyBSDProcess)); Process_init(&this->super, settings); return &this->super; } void Process_delete(Object* cast) { DragonFlyBSDProcess* this = (DragonFlyBSDProcess*) cast; Process_done((Process*)cast); free(this->jname); free(this); } static void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; switch (field) { // add Platform-specific fields here case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, (fp->kernel ? -1 : this->pid)); break; case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break; case JAIL: Process_printLeftAlignedField(str, attr, fp->jname, 11); return; default: Process_writeField(this, str, field); return; } RichString_appendWide(str, attr, buffer); } static int DragonFlyBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { const DragonFlyBSDProcess* p1 = (const DragonFlyBSDProcess*)v1; const DragonFlyBSDProcess* p2 = (const DragonFlyBSDProcess*)v2; switch (key) { // add Platform-specific fields here case JID: return SPACESHIP_NUMBER(p1->jid, p2->jid); case JAIL: return SPACESHIP_NULLSTR(p1->jname, p2->jname); default: return Process_compareByKey_Base(v1, v2, key); } } bool Process_isThread(const Process* this) { const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this; if (fp->kernel == 1 ) { return 1; } else { return (Process_isUserlandThread(this)); } } const ProcessClass DragonFlyBSDProcess_class = { .super = { .extends = Class(Process), .display = Process_display, .delete = Process_delete, .compare = Process_compare }, .writeField = DragonFlyBSDProcess_writeField, .compareByKey = DragonFlyBSDProcess_compareByKey }; htop-3.0.5/dragonflybsd/DragonFlyBSDProcess.h000066400000000000000000000015551377712513700211200ustar00rootroot00000000000000#ifndef HEADER_DragonFlyBSDProcess #define HEADER_DragonFlyBSDProcess /* htop - dragonflybsd/DragonFlyBSDProcess.h (C) 2015 Hisham H. Muhammad (C) 2017 Diederik de Groot Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ typedef struct DragonFlyBSDProcess_ { Process super; int kernel; int jid; char* jname; } DragonFlyBSDProcess; #define Process_isKernelThread(_process) (_process->kernel == 1) //#define Process_isUserlandThread(_process) (_process->pid != _process->tgid) #define Process_isUserlandThread(_process) (_process->nlwp > 1) extern const ProcessClass DragonFlyBSDProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; Process* DragonFlyBSDProcess_new(const Settings* settings); void Process_delete(Object* cast); bool Process_isThread(const Process* this); #endif htop-3.0.5/dragonflybsd/DragonFlyBSDProcessList.c000066400000000000000000000434151377712513700217500ustar00rootroot00000000000000/* htop - DragonFlyBSDProcessList.c (C) 2014 Hisham H. Muhammad (C) 2017 Diederik de Groot Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ProcessList.h" #include "DragonFlyBSDProcessList.h" #include "DragonFlyBSDProcess.h" #include #include #include #include #include #include #include #include #include #include "CRT.h" #include "Macros.h" static int MIB_hw_physmem[2]; static int MIB_vm_stats_vm_v_page_count[4]; static int pageSize; static int pageSizeKb; static int MIB_vm_stats_vm_v_wire_count[4]; static int MIB_vm_stats_vm_v_active_count[4]; static int MIB_vm_stats_vm_v_cache_count[4]; static int MIB_vm_stats_vm_v_inactive_count[4]; static int MIB_vm_stats_vm_v_free_count[4]; static int MIB_vfs_bufspace[2]; static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { size_t len; char errbuf[_POSIX2_LINE_MAX]; DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList)); ProcessList* pl = (ProcessList*) dfpl; ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, pidMatchList, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize // usable pagesize : vm.stats.vm.v_page_size len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); len = sizeof(pageSize); if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) CRT_fatalError("Cannot get pagesize by sysctl"); pageSizeKb = pageSize / ONE_K; // usable page count vm.stats.vm.v_page_count // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); int cpus = 1; len = sizeof(cpus); if (sysctlbyname("hw.ncpu", &cpus, &len, NULL, 0) != 0) { cpus = 1; } size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); dfpl->cp_time_o = xCalloc(cpus, sizeof_cp_time_array); dfpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array); len = sizeof_cp_time_array; // fetch initial single (or average) CPU clicks from kernel sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_o, &len, NULL, 0); // on smp box, fetch rest of initial CPU's clicks if (cpus > 1) { len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len); dfpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); dfpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); len = cpus * sizeof_cp_time_array; sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_o, &len, NULL, 0); } pl->cpuCount = MAXIMUM(cpus, 1); if (cpus == 1 ) { dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData)); } else { // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) dfpl->cpus = xRealloc(dfpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData)); } len = sizeof(kernelFScale); if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) { //sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed kernelFScale = 2048; } dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); if (dfpl->kd == NULL) { CRT_fatalError("kvm_openfiles() failed"); } return pl; } void ProcessList_delete(ProcessList* this) { const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this; if (dfpl->kd) { kvm_close(dfpl->kd); } if (dfpl->jails) { Hashtable_delete(dfpl->jails); } free(dfpl->cp_time_o); free(dfpl->cp_time_n); free(dfpl->cp_times_o); free(dfpl->cp_times_n); free(dfpl->cpus); ProcessList_done(this); free(this); } static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) { const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl; int cpus = pl->cpuCount; // actual CPU count int maxcpu = cpus; // max iteration (in case we have average + smp) int cp_times_offset; assert(cpus > 0); size_t sizeof_cp_time_array; unsigned long* cp_time_n; // old clicks state unsigned long* cp_time_o; // current clicks state unsigned long cp_time_d[CPUSTATES]; double cp_time_p[CPUSTATES]; // get averages or single CPU clicks sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_n, &sizeof_cp_time_array, NULL, 0); // get rest of CPUs if (cpus > 1) { // on smp systems DragonFlyBSD kernel concats all CPU states into one long array in // kern.cp_times sysctl OID // we store averages in dfpl->cpus[0], and actual cores after that maxcpu = cpus + 1; sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES; sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_n, &sizeof_cp_time_array, NULL, 0); } for (int i = 0; i < maxcpu; i++) { if (cpus == 1) { // single CPU box cp_time_n = dfpl->cp_time_n; cp_time_o = dfpl->cp_time_o; } else { if (i == 0 ) { // average cp_time_n = dfpl->cp_time_n; cp_time_o = dfpl->cp_time_o; } else { // specific smp cores cp_times_offset = i - 1; cp_time_n = dfpl->cp_times_n + (cp_times_offset * CPUSTATES); cp_time_o = dfpl->cp_times_o + (cp_times_offset * CPUSTATES); } } // diff old vs new unsigned long long total_o = 0; unsigned long long total_n = 0; unsigned long long total_d = 0; for (int s = 0; s < CPUSTATES; s++) { cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; total_o += cp_time_o[s]; total_n += cp_time_n[s]; } // totals total_d = total_n - total_o; if (total_d < 1 ) { total_d = 1; } // save current state as old and calc percentages for (int s = 0; s < CPUSTATES; ++s) { cp_time_o[s] = cp_time_n[s]; cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; } CPUData* cpuData = &(dfpl->cpus[i]); cpuData->userPercent = cp_time_p[CP_USER]; cpuData->nicePercent = cp_time_p[CP_NICE]; cpuData->systemPercent = cp_time_p[CP_SYS]; cpuData->irqPercent = cp_time_p[CP_INTR]; cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR]; // this one is not really used, but we store it anyway cpuData->idlePercent = cp_time_p[CP_IDLE]; } } static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) { DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl; // @etosan: // memory counter relationships seem to be these: // total = active + wired + inactive + cache + free // htop_used (unavail to anybody) = active + wired // htop_cache (for cache meter) = buffers + cache // user_free (avail to procs) = buffers + inactive + cache + free size_t len = sizeof(pl->totalMem); //disabled for now, as it is always smaller than phycal amount of memory... //...to avoid "where is my memory?" questions //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0); //pl->totalMem *= pageSizeKb; sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0); pl->totalMem /= 1024; sysctl(MIB_vm_stats_vm_v_active_count, 4, &(dfpl->memActive), &len, NULL, 0); dfpl->memActive *= pageSizeKb; sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(dfpl->memWire), &len, NULL, 0); dfpl->memWire *= pageSizeKb; sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0); pl->buffersMem /= 1024; sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0); pl->cachedMem *= pageSizeKb; pl->usedMem = dfpl->memActive + dfpl->memWire; struct kvm_swap swap[16]; int nswap = kvm_getswapinfo(dfpl->kd, swap, ARRAYSIZE(swap), 0); pl->totalSwap = 0; pl->usedSwap = 0; for (int i = 0; i < nswap; i++) { pl->totalSwap += swap[i].ksw_total; pl->usedSwap += swap[i].ksw_used; } pl->totalSwap *= pageSizeKb; pl->usedSwap *= pageSizeKb; } static char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) { char** argv = kvm_getargv(kd, kproc, 0); if (!argv) { return xStrdup(kproc->kp_comm); } int len = 0; for (int i = 0; argv[i]; i++) { len += strlen(argv[i]) + 1; } char* comm = xMalloc(len); char* at = comm; *basenameEnd = 0; for (int i = 0; argv[i]; i++) { at = stpcpy(at, argv[i]); if (!*basenameEnd) { *basenameEnd = at - comm; } *at = ' '; at++; } at--; *at = '\0'; return comm; } static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* dfpl) { size_t len; char* jls; /* Jail list */ char* curpos; char* nextpos; if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) { CRT_fatalError("initial sysctlbyname / jail.list failed"); } retry: if (len == 0) return; jls = xMalloc(len); if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) { if (errno == ENOMEM) { free(jls); goto retry; } CRT_fatalError("sysctlbyname / jail.list failed"); } if (dfpl->jails) { Hashtable_delete(dfpl->jails); } dfpl->jails = Hashtable_new(20, true); curpos = jls; while (curpos) { int jailid; char* str_hostname; nextpos = strchr(curpos, '\n'); if (nextpos) { *nextpos++ = 0; } jailid = atoi(strtok(curpos, " ")); str_hostname = strtok(NULL, " "); char* jname = (char*) (Hashtable_get(dfpl->jails, jailid)); if (jname == NULL) { jname = xStrdup(str_hostname); Hashtable_put(dfpl->jails, jailid, jname); } curpos = nextpos; } free(jls); } static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) { char* hostname; char* jname; if (jailid != 0 && dfpl->jails && (hostname = (char*)Hashtable_get(dfpl->jails, jailid))) { jname = xStrdup(hostname); } else { jname = xStrdup("-"); } return jname; } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) super; const Settings* settings = super->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; DragonFlyBSDProcessList_scanMemoryInfo(super); DragonFlyBSDProcessList_scanCPUTime(super); DragonFlyBSDProcessList_scanJails(dfpl); // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { return; } int count = 0; // TODO Kernel Threads seem to be skipped, need to figure out the correct flag const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); for (int i = 0; i < count; i++) { const struct kinfo_proc* kproc = &kprocs[i]; bool preExisting = false; bool ATTR_UNUSED isIdleProcess = false; // note: dragonflybsd kernel processes all have the same pid, so we misuse the kernel thread address to give them a unique identifier Process* proc = ProcessList_getProcess(super, kproc->kp_ktaddr ? (pid_t)kproc->kp_ktaddr : kproc->kp_pid, &preExisting, DragonFlyBSDProcess_new); DragonFlyBSDProcess* dfp = (DragonFlyBSDProcess*) proc; proc->show = ! ((hideKernelThreads && Process_isKernelThread(dfp)) || (hideUserlandThreads && Process_isUserlandThread(proc))); if (!preExisting) { dfp->jid = kproc->kp_jailid; if (kproc->kp_ktaddr && kproc->kp_flags & P_SYSTEM) { // dfb kernel threads all have the same pid, so we misuse the kernel thread address to give them a unique identifier proc->pid = (pid_t)kproc->kp_ktaddr; dfp->kernel = 1; } else { proc->pid = kproc->kp_pid; // process ID dfp->kernel = 0; } proc->ppid = kproc->kp_ppid; // parent process id proc->tpgid = kproc->kp_tpgid; // tty process group id //proc->tgid = kproc->kp_lwp.kl_tid; // thread group id proc->tgid = kproc->kp_pid; // thread group id proc->pgrp = kproc->kp_pgid; // process group id proc->session = kproc->kp_sid; proc->tty_nr = kproc->kp_tdev; // control terminal device number proc->st_uid = kproc->kp_uid; // user ID proc->processor = kproc->kp_lwp.kl_origcpu; proc->starttime_ctime = kproc->kp_start.tv_sec; proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); ProcessList_add(super, proc); proc->comm = DragonFlyBSDProcessList_readProcessName(dfpl->kd, kproc, &proc->basenameOffset); dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid); } else { proc->processor = kproc->kp_lwp.kl_cpuid; if (dfp->jid != kproc->kp_jailid) { // process can enter jail anytime dfp->jid = kproc->kp_jailid; free(dfp->jname); dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid); } // if there are reapers in the system, process can get reparented anytime proc->ppid = kproc->kp_ppid; if (proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs) proc->st_uid = kproc->kp_uid; proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); } if (settings->updateProcessNames) { free(proc->comm); proc->comm = DragonFlyBSDProcessList_readProcessName(dfpl->kd, kproc, &proc->basenameOffset); } } proc->m_virt = kproc->kp_vm_map_size / ONE_K; proc->m_resident = kproc->kp_vm_rssize * pageSizeKb; proc->nlwp = kproc->kp_nthreads; // number of lwp thread proc->time = (kproc->kp_swtime + 5000) / 10000; proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem); if (proc->percent_cpu > 0.1) { // system idle process should own all CPU time left regardless of CPU count if ( strcmp("idle", kproc->kp_comm) == 0 ) { isIdleProcess = true; } } if (kproc->kp_lwp.kl_pid != -1) proc->priority = kproc->kp_lwp.kl_prio; else proc->priority = -kproc->kp_lwp.kl_tdprio; switch(kproc->kp_lwp.kl_rtprio.type) { case RTP_PRIO_REALTIME: proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX + kproc->kp_lwp.kl_rtprio.prio; break; case RTP_PRIO_IDLE: proc->nice = PRIO_MAX + 1 + kproc->kp_lwp.kl_rtprio.prio; break; case RTP_PRIO_THREAD: proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX - kproc->kp_lwp.kl_rtprio.prio; break; default: proc->nice = kproc->kp_nice; break; } // would be nice if we could store multiple states in proc->state (as enum) and have writeField render them switch (kproc->kp_stat) { case SIDL: proc->state = 'I'; isIdleProcess = true; break; case SACTIVE: switch (kproc->kp_lwp.kl_stat) { case LSSLEEP: if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptible wait short/long if (kproc->kp_lwp.kl_slptime >= MAXSLP) { proc->state = 'I'; isIdleProcess = true; } else { proc->state = 'S'; } else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptible lwkt wait proc->state = 'S'; else if (kproc->kp_paddr) // uninterruptible wait proc->state = 'D'; else // uninterruptible lwkt wait proc->state = 'B'; break; case LSRUN: if (kproc->kp_lwp.kl_stat == LSRUN) { if (!(kproc->kp_lwp.kl_tdflags & (TDF_RUNNING | TDF_RUNQ))) proc->state = 'Q'; else proc->state = 'R'; } break; case LSSTOP: proc->state = 'T'; break; default: proc->state = 'A'; break; } break; case SSTOP: proc->state = 'T'; break; case SZOMB: proc->state = 'Z'; break; case SCORE: proc->state = 'C'; break; default: proc->state = '?'; } if (kproc->kp_flags & P_SWAPPEDOUT) { proc->state = 'W'; } if (kproc->kp_flags & P_TRACED) { proc->state = 'T'; } if (kproc->kp_flags & P_JAILED) { proc->state = 'J'; } if (Process_isKernelThread(dfp)) { super->kernelThreads++; } super->totalTasks++; if (proc->state == 'R') super->runningTasks++; proc->updated = true; } } htop-3.0.5/dragonflybsd/DragonFlyBSDProcessList.h000066400000000000000000000024031377712513700217450ustar00rootroot00000000000000#ifndef HEADER_DragonFlyBSDProcessList #define HEADER_DragonFlyBSDProcessList /* htop - DragonFlyBSDProcessList.h (C) 2014 Hisham H. Muhammad (C) 2017 Diederik de Groot Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include #include #include #include #include "Hashtable.h" #include "DragonFlyBSDProcess.h" typedef struct CPUData_ { double userPercent; double nicePercent; double systemPercent; double irqPercent; double idlePercent; double systemAllPercent; } CPUData; typedef struct DragonFlyBSDProcessList_ { ProcessList super; kvm_t* kd; unsigned long long int memWire; unsigned long long int memActive; unsigned long long int memInactive; unsigned long long int memFree; CPUData* cpus; unsigned long* cp_time_o; unsigned long* cp_time_n; unsigned long* cp_times_o; unsigned long* cp_times_n; Hashtable* jails; } DragonFlyBSDProcessList; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); #endif htop-3.0.5/dragonflybsd/Platform.c000066400000000000000000000157761377712513700171340ustar00rootroot00000000000000/* htop - dragonflybsd/Platform.c (C) 2014 Hisham H. Muhammad (C) 2017 Diederik de Groot Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Platform.h" #include "Macros.h" #include "Meter.h" #include "CPUMeter.h" #include "MemoryMeter.h" #include "SwapMeter.h" #include "TasksMeter.h" #include "LoadAverageMeter.h" #include "UptimeMeter.h" #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" #include "HostnameMeter.h" #include "DragonFlyBSDProcess.h" #include "DragonFlyBSDProcessList.h" #include #include #include #include #include #include #include const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, { .name = " 2 SIGINT", .number = 2 }, { .name = " 3 SIGQUIT", .number = 3 }, { .name = " 4 SIGILL", .number = 4 }, { .name = " 5 SIGTRAP", .number = 5 }, { .name = " 6 SIGABRT", .number = 6 }, { .name = " 7 SIGEMT", .number = 7 }, { .name = " 8 SIGFPE", .number = 8 }, { .name = " 9 SIGKILL", .number = 9 }, { .name = "10 SIGBUS", .number = 10 }, { .name = "11 SIGSEGV", .number = 11 }, { .name = "12 SIGSYS", .number = 12 }, { .name = "13 SIGPIPE", .number = 13 }, { .name = "14 SIGALRM", .number = 14 }, { .name = "15 SIGTERM", .number = 15 }, { .name = "16 SIGURG", .number = 16 }, { .name = "17 SIGSTOP", .number = 17 }, { .name = "18 SIGTSTP", .number = 18 }, { .name = "19 SIGCONT", .number = 19 }, { .name = "20 SIGCHLD", .number = 20 }, { .name = "21 SIGTTIN", .number = 21 }, { .name = "22 SIGTTOU", .number = 22 }, { .name = "23 SIGIO", .number = 23 }, { .name = "24 SIGXCPU", .number = 24 }, { .name = "25 SIGXFSZ", .number = 25 }, { .name = "26 SIGVTALRM", .number = 26 }, { .name = "27 SIGPROF", .number = 27 }, { .name = "28 SIGWINCH", .number = 28 }, { .name = "29 SIGINFO", .number = 29 }, { .name = "30 SIGUSR1", .number = 30 }, { .name = "31 SIGUSR2", .number = 31 }, { .name = "32 SIGTHR", .number = 32 }, { .name = "33 SIGLIBRT", .number = 33 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, &LoadAverageMeter_class, &LoadMeter_class, &MemoryMeter_class, &SwapMeter_class, &TasksMeter_class, &UptimeMeter_class, &BatteryMeter_class, &HostnameMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, &AllCPUs4Meter_class, &AllCPUs8Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, &LeftCPUs4Meter_class, &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &BlankMeter_class, NULL }; void Platform_init(void) { /* no platform-specific setup needed */ } void Platform_done(void) { /* no platform-specific cleanup needed */ } void Platform_setBindings(Htop_Action* keys) { /* no platform-specific key bindings */ (void) keys; } int Platform_getUptime() { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); int err = sysctl(mib, 2, &bootTime, &size, NULL, 0); if (err) { return -1; } gettimeofday(&currTime, NULL); return (int) difftime(currTime.tv_sec, bootTime.tv_sec); } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { struct loadavg loadAverage; int mib[2] = { CTL_VM, VM_LOADAVG }; size_t size = sizeof(loadAverage); int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0); if (err) { *one = 0; *five = 0; *fifteen = 0; return; } *one = (double) loadAverage.ldavg[0] / loadAverage.fscale; *five = (double) loadAverage.ldavg[1] / loadAverage.fscale; *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } int Platform_getMaxPid() { int maxPid; size_t size = sizeof(maxPid); int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0); if (err) { return 999999; } return maxPid; } double Platform_setCPUValues(Meter* this, int cpu) { const DragonFlyBSDProcessList* fpl = (const DragonFlyBSDProcessList*) this->pl; int cpus = this->pl->cpuCount; const CPUData* cpuData; if (cpus == 1) { // single CPU box has everything in fpl->cpus[0] cpuData = &(fpl->cpus[0]); } else { cpuData = &(fpl->cpus[cpu]); } double percent; double* v = this->values; v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; if (this->pl->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; percent = v[0] + v[1] + v[2] + v[3]; } else { v[2] = cpuData->systemAllPercent; this->curItems = 3; percent = v[0] + v[1] + v[2]; } percent = isnan(percent) ? 0.0 : CLAMP(percent, 0.0, 100.0); v[CPU_METER_FREQUENCY] = NAN; v[CPU_METER_TEMPERATURE] = NAN; return percent; } void Platform_setMemoryValues(Meter* this) { // TODO const ProcessList* pl = this->pl; this->total = pl->totalMem; this->values[0] = pl->usedMem; this->values[1] = pl->buffersMem; this->values[2] = pl->cachedMem; } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[0] = pl->usedSwap; } char* Platform_getProcessEnv(pid_t pid) { // TODO (void)pid; // prevent unused warning return NULL; } char* Platform_getInodeFilename(pid_t pid, ino_t inode) { (void)pid; (void)inode; return NULL; } FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; } bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; return false; } bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted) { // TODO *bytesReceived = 0; *packetsReceived = 0; *bytesTransmitted = 0; *packetsTransmitted = 0; return false; } void Platform_getBattery(double* percent, ACPresence* isOnAC) { int life; size_t life_len = sizeof(life); if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1) *percent = NAN; else *percent = life; int acline; size_t acline_len = sizeof(acline); if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1) *isOnAC = AC_ERROR; else *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT; } htop-3.0.5/dragonflybsd/Platform.h000066400000000000000000000027731377712513700171320ustar00rootroot00000000000000#ifndef HEADER_Platform #define HEADER_Platform /* htop - dragonflybsd/Platform.h (C) 2014 Hisham H. Muhammad (C) 2017 Diederik de Groot Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" extern const ProcessField Platform_defaultFields[]; extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; extern const MeterClass* const Platform_meterTypes[]; void Platform_init(void); void Platform_done(void); void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(void); double Platform_setCPUValues(Meter* this, int cpu); void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); char* Platform_getInodeFilename(pid_t pid, ino_t inode); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted); void Platform_getBattery(double* percent, ACPresence* isOnAC); #endif htop-3.0.5/dragonflybsd/ProcessField.h000066400000000000000000000006351377712513700177230ustar00rootroot00000000000000#ifndef HEADER_DragonFlyBSDProcessField #define HEADER_DragonFlyBSDProcessField /* htop - dragonflybsd/ProcessField.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define PLATFORM_PROCESS_FIELDS \ JID = 100, \ JAIL = 101, \ // End of list #endif /* HEADER_DragonFlyBSDProcessField */ htop-3.0.5/freebsd/000077500000000000000000000000001377712513700141205ustar00rootroot00000000000000htop-3.0.5/freebsd/FreeBSDProcess.c000066400000000000000000000145351377712513700170450ustar00rootroot00000000000000/* htop - FreeBSDProcess.c (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "FreeBSDProcess.h" #include #include "CRT.h" #include "Macros.h" #include "Process.h" #include "RichString.h" #include "XUtils.h" const char* const nodevStr = "nodev"; const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, [JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, }, [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, }; Process* FreeBSDProcess_new(const Settings* settings) { FreeBSDProcess* this = xCalloc(1, sizeof(FreeBSDProcess)); Object_setClass(this, Class(FreeBSDProcess)); Process_init(&this->super, settings); return &this->super; } void Process_delete(Object* cast) { FreeBSDProcess* this = (FreeBSDProcess*) cast; Process_done((Process*)cast); free(this->jname); free(this); } static void FreeBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { const FreeBSDProcess* fp = (const FreeBSDProcess*) this; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; switch (field) { // add FreeBSD-specific fields here case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break; case JAIL: Process_printLeftAlignedField(str, attr, fp->jname ? fp->jname : "N/A", 11); return; case TTY_NR: if (fp->ttyPath) { if (fp->ttyPath == nodevStr) attr = CRT_colors[PROCESS_SHADOW]; xSnprintf(buffer, n, "%-8s", fp->ttyPath); } else { attr = CRT_colors[PROCESS_SHADOW]; xSnprintf(buffer, n, "? "); } break; default: Process_writeField(this, str, field); return; } RichString_appendWide(str, attr, buffer); } static int FreeBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { const FreeBSDProcess* p1 = (const FreeBSDProcess*)v1; const FreeBSDProcess* p2 = (const FreeBSDProcess*)v2; switch (key) { // add FreeBSD-specific fields here case JID: return SPACESHIP_NUMBER(p1->jid, p2->jid); case JAIL: return SPACESHIP_NULLSTR(p1->jname, p2->jname); case TTY_NR: return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath); default: return Process_compareByKey_Base(v1, v2, key); } } bool Process_isThread(const Process* this) { const FreeBSDProcess* fp = (const FreeBSDProcess*) this; if (fp->kernel == 1 ) { return 1; } else { return Process_isUserlandThread(this); } } const ProcessClass FreeBSDProcess_class = { .super = { .extends = Class(Process), .display = Process_display, .delete = Process_delete, .compare = Process_compare }, .writeField = FreeBSDProcess_writeField, .compareByKey = FreeBSDProcess_compareByKey }; htop-3.0.5/freebsd/FreeBSDProcess.h000066400000000000000000000017611377712513700170470ustar00rootroot00000000000000#ifndef HEADER_FreeBSDProcess #define HEADER_FreeBSDProcess /* htop - FreeBSDProcess.h (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Object.h" #include "Process.h" #include "Settings.h" #define PROCESS_FLAG_FREEBSD_TTY 0x0100 extern const char* const nodevStr; typedef struct FreeBSDProcess_ { Process super; int kernel; int jid; char* jname; const char* ttyPath; } FreeBSDProcess; static inline bool Process_isKernelThread(const Process* this) { return ((const FreeBSDProcess*)this)->kernel == 1; } static inline bool Process_isUserlandThread(const Process* this) { return this->pid != this->tgid; } extern const ProcessClass FreeBSDProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; Process* FreeBSDProcess_new(const Settings* settings); void Process_delete(Object* cast); bool Process_isThread(const Process* this); #endif htop-3.0.5/freebsd/FreeBSDProcessList.c000066400000000000000000000440631377712513700177000ustar00rootroot00000000000000/* htop - FreeBSDProcessList.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "FreeBSDProcessList.h" #include #include #include #include #include #include #include #include // needs to be included before for MAXPATHLEN #include #include #include #include #include #include #include #include #include #include "CRT.h" #include "Compat.h" #include "FreeBSDProcess.h" #include "Macros.h" #include "Object.h" #include "Process.h" #include "ProcessList.h" #include "Settings.h" #include "XUtils.h" #include "zfs/ZfsArcStats.h" #include "zfs/openzfs_sysctl.h" char jail_errmsg[JAIL_ERRMSGLEN]; static int MIB_hw_physmem[2]; static int MIB_vm_stats_vm_v_page_count[4]; static int pageSize; static int pageSizeKb; static int MIB_vm_stats_vm_v_wire_count[4]; static int MIB_vm_stats_vm_v_active_count[4]; static int MIB_vm_stats_vm_v_cache_count[4]; static int MIB_vm_stats_vm_v_inactive_count[4]; static int MIB_vm_stats_vm_v_free_count[4]; static int MIB_vfs_bufspace[2]; static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { size_t len; char errbuf[_POSIX2_LINE_MAX]; FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList)); ProcessList* pl = (ProcessList*) fpl; ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidMatchList, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize // usable pagesize : vm.stats.vm.v_page_size len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); len = sizeof(pageSize); if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) CRT_fatalError("Cannot get pagesize by sysctl"); pageSizeKb = pageSize / ONE_K; // usable page count vm.stats.vm.v_page_count // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); openzfs_sysctl_init(&fpl->zfs); openzfs_sysctl_updateArcStats(&fpl->zfs); int smp = 0; len = sizeof(smp); if (sysctlbyname("kern.smp.active", &smp, &len, NULL, 0) != 0 || len != sizeof(smp)) { smp = 0; } int cpus = 1; len = sizeof(cpus); if (smp) { int err = sysctlbyname("kern.smp.cpus", &cpus, &len, NULL, 0); if (err) { cpus = 1; } } else { cpus = 1; } size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); fpl->cp_time_o = xCalloc(cpus, sizeof_cp_time_array); fpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array); len = sizeof_cp_time_array; // fetch initial single (or average) CPU clicks from kernel sysctl(MIB_kern_cp_time, 2, fpl->cp_time_o, &len, NULL, 0); // on smp box, fetch rest of initial CPU's clicks if (cpus > 1) { len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len); fpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); fpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); len = cpus * sizeof_cp_time_array; sysctl(MIB_kern_cp_times, 2, fpl->cp_times_o, &len, NULL, 0); } pl->cpuCount = MAXIMUM(cpus, 1); if (cpus == 1 ) { fpl->cpus = xRealloc(fpl->cpus, sizeof(CPUData)); } else { // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) fpl->cpus = xRealloc(fpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData)); } len = sizeof(kernelFScale); if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) { //sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed kernelFScale = 2048; } fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); if (fpl->kd == NULL) { CRT_fatalError("kvm_openfiles() failed"); } fpl->ttys = Hashtable_new(20, true); return pl; } void ProcessList_delete(ProcessList* this) { const FreeBSDProcessList* fpl = (FreeBSDProcessList*) this; Hashtable_delete(fpl->ttys); if (fpl->kd) { kvm_close(fpl->kd); } free(fpl->cp_time_o); free(fpl->cp_time_n); free(fpl->cp_times_o); free(fpl->cp_times_n); free(fpl->cpus); ProcessList_done(this); free(this); } static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) { const FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; int cpus = pl->cpuCount; // actual CPU count int maxcpu = cpus; // max iteration (in case we have average + smp) int cp_times_offset; assert(cpus > 0); size_t sizeof_cp_time_array; unsigned long* cp_time_n; // old clicks state unsigned long* cp_time_o; // current clicks state unsigned long cp_time_d[CPUSTATES]; double cp_time_p[CPUSTATES]; // get averages or single CPU clicks sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; sysctl(MIB_kern_cp_time, 2, fpl->cp_time_n, &sizeof_cp_time_array, NULL, 0); // get rest of CPUs if (cpus > 1) { // on smp systems FreeBSD kernel concats all CPU states into one long array in // kern.cp_times sysctl OID // we store averages in fpl->cpus[0], and actual cores after that maxcpu = cpus + 1; sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES; sysctl(MIB_kern_cp_times, 2, fpl->cp_times_n, &sizeof_cp_time_array, NULL, 0); } for (int i = 0; i < maxcpu; i++) { if (cpus == 1) { // single CPU box cp_time_n = fpl->cp_time_n; cp_time_o = fpl->cp_time_o; } else { if (i == 0 ) { // average cp_time_n = fpl->cp_time_n; cp_time_o = fpl->cp_time_o; } else { // specific smp cores cp_times_offset = i - 1; cp_time_n = fpl->cp_times_n + (cp_times_offset * CPUSTATES); cp_time_o = fpl->cp_times_o + (cp_times_offset * CPUSTATES); } } // diff old vs new unsigned long long total_o = 0; unsigned long long total_n = 0; unsigned long long total_d = 0; for (int s = 0; s < CPUSTATES; s++) { cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; total_o += cp_time_o[s]; total_n += cp_time_n[s]; } // totals total_d = total_n - total_o; if (total_d < 1 ) { total_d = 1; } // save current state as old and calc percentages for (int s = 0; s < CPUSTATES; ++s) { cp_time_o[s] = cp_time_n[s]; cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; } CPUData* cpuData = &(fpl->cpus[i]); cpuData->userPercent = cp_time_p[CP_USER]; cpuData->nicePercent = cp_time_p[CP_NICE]; cpuData->systemPercent = cp_time_p[CP_SYS]; cpuData->irqPercent = cp_time_p[CP_INTR]; cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR]; // this one is not really used, but we store it anyway cpuData->idlePercent = cp_time_p[CP_IDLE]; } } static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; // @etosan: // memory counter relationships seem to be these: // total = active + wired + inactive + cache + free // htop_used (unavail to anybody) = active + wired // htop_cache (for cache meter) = buffers + cache // user_free (avail to procs) = buffers + inactive + cache + free // // with ZFS ARC situation becomes bit muddled, as ARC behaves like "user_free" // and belongs into cache, but is reported as wired by kernel // // htop_used = active + (wired - arc) // htop_cache = buffers + cache + arc u_long totalMem; u_int memActive, memWire, cachedMem; long buffersMem; size_t len; //disabled for now, as it is always smaller than phycal amount of memory... //...to avoid "where is my memory?" questions //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0); //pl->totalMem *= pageSizeKb; len = sizeof(totalMem); sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); totalMem /= 1024; pl->totalMem = totalMem; len = sizeof(memActive); sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); memActive *= pageSizeKb; fpl->memActive = memActive; len = sizeof(memWire); sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0); memWire *= pageSizeKb; fpl->memWire = memWire; len = sizeof(buffersMem); sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); buffersMem /= 1024; pl->buffersMem = buffersMem; len = sizeof(cachedMem); sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); cachedMem *= pageSizeKb; pl->cachedMem = cachedMem; if (fpl->zfs.enabled) { fpl->memWire -= fpl->zfs.size; pl->cachedMem += fpl->zfs.size; } pl->usedMem = fpl->memActive + fpl->memWire; struct kvm_swap swap[16]; int nswap = kvm_getswapinfo(fpl->kd, swap, ARRAYSIZE(swap), 0); pl->totalSwap = 0; pl->usedSwap = 0; for (int i = 0; i < nswap; i++) { pl->totalSwap += swap[i].ksw_total; pl->usedSwap += swap[i].ksw_used; } pl->totalSwap *= pageSizeKb; pl->usedSwap *= pageSizeKb; } static void FreeBSDProcessList_scanTTYs(ProcessList* pl) { FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; // scan /dev/tty* { DIR* dirPtr = opendir("/dev"); if (!dirPtr) return; int dirFd = dirfd(dirPtr); if (dirFd < 0) goto err1; const struct dirent* entry; while ((entry = readdir(dirPtr))) { if (!String_startsWith(entry->d_name, "tty")) continue; struct stat info; if (Compat_fstatat(dirFd, "/dev", entry->d_name, &info, 0) < 0) continue; if (!S_ISCHR(info.st_mode)) continue; if (!Hashtable_get(fpl->ttys, info.st_rdev)) Hashtable_put(fpl->ttys, info.st_rdev, xStrdup(entry->d_name)); } err1: closedir(dirPtr); } // scan /dev/pts/* { DIR* dirPtr = opendir("/dev/pts"); if (!dirPtr) return; int dirFd = dirfd(dirPtr); if (dirFd < 0) goto err2; const struct dirent* entry; while ((entry = readdir(dirPtr))) { struct stat info; if (Compat_fstatat(dirFd, "/dev/pts", entry->d_name, &info, 0) < 0) continue; if (!S_ISCHR(info.st_mode)) continue; if (!Hashtable_get(fpl->ttys, info.st_rdev)) { char* path; xAsprintf(&path, "pts/%s", entry->d_name); Hashtable_put(fpl->ttys, info.st_rdev, path); } } err2: closedir(dirPtr); } } static char* FreeBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) { char** argv = kvm_getargv(kd, kproc, 0); if (!argv) { return xStrdup(kproc->ki_comm); } int len = 0; for (int i = 0; argv[i]; i++) { len += strlen(argv[i]) + 1; } char* comm = xMalloc(len); char* at = comm; *basenameEnd = 0; for (int i = 0; argv[i]; i++) { at = stpcpy(at, argv[i]); if (!*basenameEnd) { *basenameEnd = at - comm; } *at = ' '; at++; } at--; *at = '\0'; return comm; } static char* FreeBSDProcessList_readJailName(const struct kinfo_proc* kproc) { char* jname = NULL; char jnamebuf[MAXHOSTNAMELEN]; if (kproc->ki_jid != 0) { struct iovec jiov[6]; memset(jnamebuf, 0, sizeof(jnamebuf)); IGNORE_WCASTQUAL_BEGIN *(const void**)&jiov[0].iov_base = "jid"; jiov[0].iov_len = sizeof("jid"); jiov[1].iov_base = (void*) &kproc->ki_jid; jiov[1].iov_len = sizeof(kproc->ki_jid); *(const void**)&jiov[2].iov_base = "name"; jiov[2].iov_len = sizeof("name"); jiov[3].iov_base = jnamebuf; jiov[3].iov_len = sizeof(jnamebuf); *(const void**)&jiov[4].iov_base = "errmsg"; jiov[4].iov_len = sizeof("errmsg"); jiov[5].iov_base = jail_errmsg; jiov[5].iov_len = JAIL_ERRMSGLEN; IGNORE_WCASTQUAL_END jail_errmsg[0] = 0; int jid = jail_get(jiov, 6, 0); if (jid < 0) { if (!jail_errmsg[0]) { xSnprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno)); } } else if (jid == kproc->ki_jid) { jname = xStrdup(jnamebuf); } } else { jname = xStrdup("-"); } return jname; } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { FreeBSDProcessList* fpl = (FreeBSDProcessList*) super; const Settings* settings = super->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; openzfs_sysctl_updateArcStats(&fpl->zfs); FreeBSDProcessList_scanMemoryInfo(super); FreeBSDProcessList_scanCPUTime(super); // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { return; } if (settings->flags & PROCESS_FLAG_FREEBSD_TTY) { FreeBSDProcessList_scanTTYs(super); } int count = 0; struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count); for (int i = 0; i < count; i++) { struct kinfo_proc* kproc = &kprocs[i]; bool preExisting = false; // TODO: bool isIdleProcess = false; Process* proc = ProcessList_getProcess(super, kproc->ki_pid, &preExisting, FreeBSDProcess_new); FreeBSDProcess* fp = (FreeBSDProcess*) proc; proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); if (!preExisting) { fp->jid = kproc->ki_jid; proc->pid = kproc->ki_pid; if ( ! ((kproc->ki_pid == 0) || (kproc->ki_pid == 1) ) && kproc->ki_flag & P_SYSTEM) { fp->kernel = 1; } else { fp->kernel = 0; } proc->ppid = kproc->ki_ppid; proc->tpgid = kproc->ki_tpgid; proc->tgid = kproc->ki_pid; proc->session = kproc->ki_sid; proc->pgrp = kproc->ki_pgid; proc->st_uid = kproc->ki_uid; proc->starttime_ctime = kproc->ki_start.tv_sec; Process_fillStarttimeBuffer(proc); proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); ProcessList_add(super, proc); proc->comm = FreeBSDProcessList_readProcessName(fpl->kd, kproc, &proc->basenameOffset); fp->jname = FreeBSDProcessList_readJailName(kproc); } else { if (fp->jid != kproc->ki_jid) { // process can enter jail anytime fp->jid = kproc->ki_jid; free(fp->jname); fp->jname = FreeBSDProcessList_readJailName(kproc); } // if there are reapers in the system, process can get reparented anytime proc->ppid = kproc->ki_ppid; if (proc->st_uid != kproc->ki_uid) { // some processes change users (eg. to lower privs) proc->st_uid = kproc->ki_uid; proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); } if (settings->updateProcessNames) { free(proc->comm); proc->comm = FreeBSDProcessList_readProcessName(fpl->kd, kproc, &proc->basenameOffset); } } // from FreeBSD source /src/usr.bin/top/machine.c proc->m_virt = kproc->ki_size / ONE_K; proc->m_resident = kproc->ki_rssize * pageSizeKb; proc->nlwp = kproc->ki_numthreads; proc->time = (kproc->ki_runtime + 5000) / 10000; proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem); /* * TODO * if (proc->percent_cpu > 0.1) { * // system idle process should own all CPU time left regardless of CPU count * if ( strcmp("idle", kproc->ki_comm) == 0 ) { * isIdleProcess = true; * } * } */ proc->priority = kproc->ki_pri.pri_level - PZERO; if (strcmp("intr", kproc->ki_comm) == 0 && kproc->ki_flag & P_SYSTEM) { proc->nice = 0; //@etosan: intr kernel process (not thread) has weird nice value } else if (kproc->ki_pri.pri_class == PRI_TIMESHARE) { proc->nice = kproc->ki_nice - NZERO; } else if (PRI_IS_REALTIME(kproc->ki_pri.pri_class)) { proc->nice = PRIO_MIN - 1 - (PRI_MAX_REALTIME - kproc->ki_pri.pri_level); } else { proc->nice = PRIO_MAX + 1 + kproc->ki_pri.pri_level - PRI_MIN_IDLE; } switch (kproc->ki_stat) { case SIDL: proc->state = 'I'; break; case SRUN: proc->state = 'R'; break; case SSLEEP: proc->state = 'S'; break; case SSTOP: proc->state = 'T'; break; case SZOMB: proc->state = 'Z'; break; case SWAIT: proc->state = 'D'; break; case SLOCK: proc->state = 'L'; break; default: proc->state = '?'; } if (settings->flags & PROCESS_FLAG_FREEBSD_TTY) { fp->ttyPath = (kproc->ki_tdev == NODEV) ? nodevStr : Hashtable_get(fpl->ttys, kproc->ki_tdev); } if (Process_isKernelThread(proc)) super->kernelThreads++; super->totalTasks++; if (proc->state == 'R') super->runningTasks++; proc->updated = true; } } htop-3.0.5/freebsd/FreeBSDProcessList.h000066400000000000000000000024211377712513700176750ustar00rootroot00000000000000#ifndef HEADER_FreeBSDProcessList #define HEADER_FreeBSDProcessList /* htop - FreeBSDProcessList.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" #include "zfs/ZfsArcStats.h" #define JAIL_ERRMSGLEN 1024 extern char jail_errmsg[JAIL_ERRMSGLEN]; typedef struct CPUData_ { double userPercent; double nicePercent; double systemPercent; double irqPercent; double idlePercent; double systemAllPercent; } CPUData; typedef struct FreeBSDProcessList_ { ProcessList super; kvm_t* kd; unsigned long long int memWire; unsigned long long int memActive; unsigned long long int memInactive; unsigned long long int memFree; ZfsArcStats zfs; CPUData* cpus; Hashtable* ttys; unsigned long* cp_time_o; unsigned long* cp_time_n; unsigned long* cp_times_o; unsigned long* cp_times_n; } FreeBSDProcessList; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); #endif htop-3.0.5/freebsd/Platform.c000066400000000000000000000243271377712513700160600ustar00rootroot00000000000000/* htop - freebsd/Platform.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CPUMeter.h" #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" #include "DiskIOMeter.h" #include "FreeBSDProcess.h" #include "FreeBSDProcessList.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" #include "MemoryMeter.h" #include "Meter.h" #include "NetworkIOMeter.h" #include "ProcessList.h" #include "Settings.h" #include "SwapMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, { .name = " 2 SIGINT", .number = 2 }, { .name = " 3 SIGQUIT", .number = 3 }, { .name = " 4 SIGILL", .number = 4 }, { .name = " 5 SIGTRAP", .number = 5 }, { .name = " 6 SIGABRT", .number = 6 }, { .name = " 7 SIGEMT", .number = 7 }, { .name = " 8 SIGFPE", .number = 8 }, { .name = " 9 SIGKILL", .number = 9 }, { .name = "10 SIGBUS", .number = 10 }, { .name = "11 SIGSEGV", .number = 11 }, { .name = "12 SIGSYS", .number = 12 }, { .name = "13 SIGPIPE", .number = 13 }, { .name = "14 SIGALRM", .number = 14 }, { .name = "15 SIGTERM", .number = 15 }, { .name = "16 SIGURG", .number = 16 }, { .name = "17 SIGSTOP", .number = 17 }, { .name = "18 SIGTSTP", .number = 18 }, { .name = "19 SIGCONT", .number = 19 }, { .name = "20 SIGCHLD", .number = 20 }, { .name = "21 SIGTTIN", .number = 21 }, { .name = "22 SIGTTOU", .number = 22 }, { .name = "23 SIGIO", .number = 23 }, { .name = "24 SIGXCPU", .number = 24 }, { .name = "25 SIGXFSZ", .number = 25 }, { .name = "26 SIGVTALRM", .number = 26 }, { .name = "27 SIGPROF", .number = 27 }, { .name = "28 SIGWINCH", .number = 28 }, { .name = "29 SIGINFO", .number = 29 }, { .name = "30 SIGUSR1", .number = 30 }, { .name = "31 SIGUSR2", .number = 31 }, { .name = "32 SIGTHR", .number = 32 }, { .name = "33 SIGLIBRT", .number = 33 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, &LoadAverageMeter_class, &LoadMeter_class, &MemoryMeter_class, &SwapMeter_class, &TasksMeter_class, &UptimeMeter_class, &BatteryMeter_class, &HostnameMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, &AllCPUs4Meter_class, &AllCPUs8Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, &LeftCPUs4Meter_class, &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &BlankMeter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, NULL }; void Platform_init(void) { /* no platform-specific setup needed */ } void Platform_done(void) { /* no platform-specific cleanup needed */ } void Platform_setBindings(Htop_Action* keys) { /* no platform-specific key bindings */ (void) keys; } int Platform_getUptime() { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); int err = sysctl(mib, 2, &bootTime, &size, NULL, 0); if (err) { return -1; } gettimeofday(&currTime, NULL); return (int) difftime(currTime.tv_sec, bootTime.tv_sec); } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { struct loadavg loadAverage; int mib[2] = { CTL_VM, VM_LOADAVG }; size_t size = sizeof(loadAverage); int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0); if (err) { *one = 0; *five = 0; *fifteen = 0; return; } *one = (double) loadAverage.ldavg[0] / loadAverage.fscale; *five = (double) loadAverage.ldavg[1] / loadAverage.fscale; *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } int Platform_getMaxPid() { int maxPid; size_t size = sizeof(maxPid); int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0); if (err) { return 99999; } return maxPid; } double Platform_setCPUValues(Meter* this, int cpu) { const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl; int cpus = this->pl->cpuCount; const CPUData* cpuData; if (cpus == 1) { // single CPU box has everything in fpl->cpus[0] cpuData = &(fpl->cpus[0]); } else { cpuData = &(fpl->cpus[cpu]); } double percent; double* v = this->values; v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; if (this->pl->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; percent = v[0] + v[1] + v[2] + v[3]; } else { v[2] = cpuData->systemAllPercent; this->curItems = 3; percent = v[0] + v[1] + v[2]; } percent = CLAMP(percent, 0.0, 100.0); v[CPU_METER_FREQUENCY] = NAN; v[CPU_METER_TEMPERATURE] = NAN; return percent; } void Platform_setMemoryValues(Meter* this) { // TODO const ProcessList* pl = this->pl; this->total = pl->totalMem; this->values[0] = pl->usedMem; this->values[1] = pl->buffersMem; this->values[2] = pl->cachedMem; } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[0] = pl->usedSwap; } void Platform_setZfsArcValues(Meter* this) { const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl; ZfsArcMeter_readStats(this, &(fpl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl; ZfsCompressedArcMeter_readStats(this, &(fpl->zfs)); } char* Platform_getProcessEnv(pid_t pid) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ENV, pid }; size_t capacity = ARG_MAX; char* env = xMalloc(capacity); int err = sysctl(mib, 4, env, &capacity, NULL, 0); if (err || capacity == 0) { free(env); return NULL; } if (env[capacity - 1] || env[capacity - 2]) { env = xRealloc(env, capacity + 2); env[capacity] = 0; env[capacity + 1] = 0; } return env; } char* Platform_getInodeFilename(pid_t pid, ino_t inode) { (void)pid; (void)inode; return NULL; } FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; } bool Platform_getDiskIO(DiskIOData* data) { if (devstat_checkversion(NULL) < 0) return false; struct devinfo info = { 0 }; struct statinfo current = { .dinfo = &info }; // get number of devices if (devstat_getdevs(NULL, ¤t) < 0) return false; int count = current.dinfo->numdevs; unsigned long int bytesReadSum = 0, bytesWriteSum = 0, timeSpendSum = 0; // get data for (int i = 0; i < count; i++) { uint64_t bytes_read, bytes_write; long double busy_time; devstat_compute_statistics(¤t.dinfo->devices[i], NULL, 1.0, DSM_TOTAL_BYTES_READ, &bytes_read, DSM_TOTAL_BYTES_WRITE, &bytes_write, DSM_TOTAL_BUSY_TIME, &busy_time, DSM_NONE); bytesReadSum += bytes_read; bytesWriteSum += bytes_write; timeSpendSum += 1000 * busy_time; } data->totalBytesRead = bytesReadSum; data->totalBytesWritten = bytesWriteSum; data->totalMsTimeSpend = timeSpendSum; return true; } bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted) { int r; // get number of interfaces int count; size_t countLen = sizeof(count); const int countMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT }; r = sysctl(countMib, ARRAYSIZE(countMib), &count, &countLen, NULL, 0); if (r < 0) return false; unsigned long int bytesReceivedSum = 0, packetsReceivedSum = 0, bytesTransmittedSum = 0, packetsTransmittedSum = 0; for (int i = 1; i <= count; i++) { struct ifmibdata ifmd; size_t ifmdLen = sizeof(ifmd); const int dataMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL }; r = sysctl(dataMib, ARRAYSIZE(dataMib), &ifmd, &ifmdLen, NULL, 0); if (r < 0) continue; if (ifmd.ifmd_flags & IFF_LOOPBACK) continue; bytesReceivedSum += ifmd.ifmd_data.ifi_ibytes; packetsReceivedSum += ifmd.ifmd_data.ifi_ipackets; bytesTransmittedSum += ifmd.ifmd_data.ifi_obytes; packetsTransmittedSum += ifmd.ifmd_data.ifi_opackets; } *bytesReceived = bytesReceivedSum; *packetsReceived = packetsReceivedSum; *bytesTransmitted = bytesTransmittedSum; *packetsTransmitted = packetsTransmittedSum; return true; } void Platform_getBattery(double* percent, ACPresence* isOnAC) { int life; size_t life_len = sizeof(life); if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1) *percent = NAN; else *percent = life; int acline; size_t acline_len = sizeof(acline); if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1) *isOnAC = AC_ERROR; else *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT; } htop-3.0.5/freebsd/Platform.h000066400000000000000000000031471377712513700160620ustar00rootroot00000000000000#ifndef HEADER_Platform #define HEADER_Platform /* htop - freebsd/Platform.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "Meter.h" #include "Process.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" extern const ProcessField Platform_defaultFields[]; extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; extern const MeterClass* const Platform_meterTypes[]; void Platform_init(void); void Platform_done(void); void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(void); double Platform_setCPUValues(Meter* this, int cpu); void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); void Platform_setZfsArcValues(Meter* this); void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); char* Platform_getInodeFilename(pid_t pid, ino_t inode); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted); void Platform_getBattery(double* percent, ACPresence* isOnAC); #endif htop-3.0.5/freebsd/ProcessField.h000066400000000000000000000006111377712513700166510ustar00rootroot00000000000000#ifndef HEADER_FreeBSDProcessField #define HEADER_FreeBSDProcessField /* htop - freebsd/ProcessField.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define PLATFORM_PROCESS_FIELDS \ JID = 100, \ JAIL = 101, \ // End of list #endif /* HEADER_FreeBSDProcessField */ htop-3.0.5/htop.1.in000066400000000000000000000400301377712513700141440ustar00rootroot00000000000000.TH "HTOP" "1" "2020" "@PACKAGE_STRING@" "User Commands" .SH "NAME" htop \- interactive process viewer .SH "SYNOPSIS" .B htop .RB [ \-dCFhpustvH ] .SH "DESCRIPTION" .B htop is a cross-platform ncurses-based process viewer. .LP It is similar to .BR top , but allows you to scroll vertically and horizontally, and interact using a pointing device (mouse). You can observe all processes running on the system, along with their command line arguments, as well as view them in a tree format, select multiple processes and acting on them all at once. .LP Tasks related to processes (killing, renicing) can be done without entering their PIDs. .br .SH "COMMAND-LINE OPTIONS" Mandatory arguments to long options are mandatory for short options too. .TP \fB\-d \-\-delay=DELAY\fR Delay between updates, in tenths of seconds. If the delay value is less than 1 it is increased to 1, i.e. 1/10 second. If the delay value is greater than 100, it is decreased to 100, i.e. 10 seconds. .TP \fB\-C \-\-no-color \-\-no-colour\fR Start .B htop in monochrome mode .TP \fB\-F \-\-filter=FILTER Filter processes by command .TP \fB\-h \-\-help Display a help message and exit .TP \fB\-p \-\-pid=PID,PID...\fR Show only the given PIDs .TP \fB\-s \-\-sort\-key COLUMN\fR Sort by this column (use \-\-sort\-key help for a column list). This will force a list view unless you specify -t at the same time. .TP \fB\-u \-\-user=USERNAME\fR Show only the processes of a given user .TP \fB\-U \-\-no-unicode\fR Do not use unicode but ASCII characters for graph meters .TP \fB\-M \-\-no-mouse\fR Disable support of mouse control .TP \fB\-V \-\-version Output version information and exit .TP \fB\-t \-\-tree Show processes in tree view. This can be used to force a tree view when requesting a sort order with -s. .TP \fB\-H \-\-highlight-changes=DELAY\fR Highlight new and old processes .SH "INTERACTIVE COMMANDS" The following commands are supported while in .BR htop : .TP 5 .B Up, Alt-k Select (highlight) the previous process in the process list. Scroll the list if necessary. .TP .B Down, Alt-j Select (highlight) the next process in the process list. Scroll the list if necessary. .TP .B Left, Alt-h Scroll the process list left. .TP .B Right, Alt-l Scroll the process list right. .TP .B PgUp, PgDn Scroll the process list up or down one window. .TP .B Home Scroll to the top of the process list and select the first process. .TP .B End Scroll to the bottom of the process list and select the last process. .TP .B Ctrl-A, ^ Scroll left to the beginning of the process entry (i.e. beginning of line). .TP .B Ctrl-E, $ Scroll right to the end of the process entry (i.e. end of line). .TP .B Space Tag or untag a process. Commands that can operate on multiple processes, like "kill", will then apply over the list of tagged processes, instead of the currently highlighted one. .TP .B c Tag the current process and its children. Commands that can operate on multiple processes, like "kill", will then apply over the list of tagged processes, instead of the currently highlighted one. .TP .B U Untag all processes (remove all tags added with the Space or c keys). .TP .B s Trace process system calls: if strace(1) is installed, pressing this key will attach it to the currently selected process, presenting a live update of system calls issued by the process. .TP .B l Display open files for a process: if lsof(1) is installed, pressing this key will display the list of file descriptors opened by the process. .TP .B w Display the command line of the selected process in a separate screen, wrapped onto multiple lines as needed. .TP .B x Display the active file locks of the selected process in a separate screen. .TP .B F1, h, ? Go to the help screen .TP .B F2, S Go to the setup screen, where you can configure the meters displayed at the top of the screen, set various display options, choose among color schemes, and select which columns are displayed, in which order. .TP .B F3, / Incrementally search the command lines of all the displayed processes. The currently selected (highlighted) command will update as you type. While in search mode, pressing F3 will cycle through matching occurrences. Pressing Shift-F3 will cycle backwards. Alternatively the search can be started by simply typing the command you are looking for, although for the first character normal key bindings take precedence. .TP .B F4, \\\\ Incremental process filtering: type in part of a process command line and only processes whose names match will be shown. To cancel filtering, enter the Filter option again and press Esc. .TP .B F5, t Tree view: organize processes by parenthood, and layout the relations between them as a tree. Toggling the key will switch between tree and your previously selected sort view. Selecting a sort view will exit tree view. .TP .B F6, <, > Selects a field for sorting, also accessible through < and >. The current sort field is indicated by a highlight in the header. .TP .B F7, ] Increase the selected process's priority (subtract from 'nice' value). This can only be done by the superuser. .TP .B F8, [ Decrease the selected process's priority (add to 'nice' value) .TP .B F9, k "Kill" process: sends a signal which is selected in a menu, to one or a group of processes. If processes were tagged, sends the signal to all tagged processes. If none is tagged, sends to the currently selected process. .TP .B F10, q Quit .TP .B I Invert the sort order: if sort order is increasing, switch to decreasing, and vice-versa. .TP .B +, \- When in tree view mode, expand or collapse subtree. When a subtree is collapsed a "+" sign shows to the left of the process name. .TP .B a (on multiprocessor machines) Set CPU affinity: mark which CPUs a process is allowed to use. .TP .B u Show only processes owned by a specified user. .TP .B N Sort by PID. .TP .B M Sort by memory usage (top compatibility key). .TP .B P Sort by processor usage (top compatibility key). .TP .B T Sort by time (top compatibility key). .TP .B F "Follow" process: if the sort order causes the currently selected process to move in the list, make the selection bar follow it. This is useful for monitoring a process: this way, you can keep a process always visible on screen. When a movement key is used, "follow" loses effect. .TP .B K Hide kernel threads: prevent the threads belonging the kernel to be displayed in the process list. (This is a toggle key.) .TP .B H Hide user threads: on systems that represent them differently than ordinary processes (such as recent NPTL-based systems), this can hide threads from userspace processes in the process list. (This is a toggle key.) .TP .B p Show full paths to running programs, where applicable. (This is a toggle key.) .TP .B Z Pause/resume process updates. .TP .B m Merge exe, comm and cmdline, where applicable. (This is a toggle key.) .TP .B Ctrl-L Refresh: redraw screen and recalculate values. .TP .B Numbers PID search: type in process ID and the selection highlight will be moved to it. .PD .SH "COLUMNS" The following columns can display data about each process. A value of '\-' in all the rows indicates that a column is unsupported on your system, or currently unimplemented in .BR htop . The names below are the ones used in the "Available Columns" section of the setup screen. If a different name is shown in .BR htop 's main screen, it is shown below in parenthesis. .TP 5 .B Command The full command line of the process (i.e. program name and arguments). If the option 'Merge exe, comm and cmdline in Command' (toggled by the 'm' key) is set, and if readable, the executable path (/proc/[pid]/exe) and the command name (/proc/[pid]/comm) are also shown merged with the command line. .TP .B Comm The command name of the process obtained from /proc/[pid]/comm, if readable. .TP .B Exe The abbreviated basename of the executable of the process, obtained from /proc/[pid]/exe, if readable. htop is able to read this file on linux for ALL the processes only if it has the capability CAP_SYS_PTRACE or root privileges. .TP .B PID The process ID. .TP .B STATE (S) The state of the process: \fBS\fR for sleeping (idle) \fBR\fR for running \fBD\fR for disk sleep (uninterruptible) \fBZ\fR for zombie (waiting for parent to read its exit status) \fBT\fR for traced or suspended (e.g by SIGTSTP) \fBW\fR for paging .TP .B PPID The parent process ID. .TP .B PGRP The process's group ID. .TP .B SESSION (SID) The process's session ID. .TP .B TTY_NR (TTY) The controlling terminal of the process. .TP .B TPGID The process ID of the foreground process group of the controlling terminal. .TP .B MINFLT The number of page faults happening in the main memory. .TP .B CMINFLT The number of minor faults for the process's waited-for children (see MINFLT above). .TP .B MAJFLT The number of page faults happening out of the main memory. .TP .B CMAJFLT The number of major faults for the process's waited-for children (see MAJFLT above). .TP .B UTIME (UTIME+) The user CPU time, which is the amount of time the process has spent executing on the CPU in user mode (i.e. everything but system calls), measured in clock ticks. .TP .B STIME (STIME+) The system CPU time, which is the amount of time the kernel has spent executing system calls on behalf of the process, measured in clock ticks. .TP .B CUTIME (CUTIME+) The children's user CPU time, which is the amount of time the process's waited-for children have spent executing in user mode (see UTIME above). .TP .B CSTIME (CSTIME+) The children's system CPU time, which is the amount of time the kernel has spent executing system calls on behalf of all the process's waited-for children (see STIME above). .TP .B PRIORITY (PRI) The kernel's internal priority for the process, usually just its nice value plus twenty. Different for real-time processes. .TP .B NICE (NI) The nice value of a process, from 19 (low priority) to -20 (high priority). A high value means the process is being nice, letting others have a higher relative priority. The usual OS permission restrictions for adjusting priority apply. .TP .B STARTTIME (START) The time the process was started. .TP .B PROCESSOR (CPU) The ID of the CPU the process last executed on. .TP .B M_VIRT (VIRT) The size of the virtual memory of the process. .TP .B M_RESIDENT (RES) The resident set size (text + data + stack) of the process (i.e. the size of the process's used physical memory). .TP .B M_SHARE (SHR) The size of the process's shared pages. .TP .B M_TRS (CODE) The text resident set size of the process (i.e. the size of the process's executable instructions). .TP .B M_DRS (DATA) The data resident set size (data + stack) of the process (i.e. the size of anything except the process's executable instructions). .TP .B M_LRS (LIB) The library size of the process. .TP .B M_DT (DIRTY) The size of the dirty pages of the process. .TP .B M_SWAP (SWAP) The size of the process's swapped pages. .TP .B M_PSS (PSS) The proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it. .TP .B M_M_PSSWP (PSSWP) The proportional swap share of this mapping, unlike M_SWAP this does not take into account swapped out page of underlying shmem objects. .TP .B ST_UID (UID) The user ID of the process owner. .TP .B PERCENT_CPU (CPU%) The percentage of the CPU time that the process is currently using. .TP .B PERCENT_MEM (MEM%) The percentage of memory the process is currently using (based on the process's resident memory size, see M_RESIDENT above). .TP .B USER The username of the process owner, or the user ID if the name can't be determined. .TP .B TIME (TIME+) The time, measured in clock ticks that the process has spent in user and system time (see UTIME, STIME above). .TP .B NLWP The number of threads in the process. .TP .B TGID The thread group ID. .TP .B CTID OpenVZ container ID, a.k.a virtual environment ID. .TP .B VPID OpenVZ process ID. .TP .B VXID VServer process ID. .TP .B RCHAR (RD_CHAR) The number of bytes the process has read. .TP .B WCHAR (WR_CHAR) The number of bytes the process has written. .TP .B SYSCR (RD_SYSC) The number of read(2) syscalls for the process. .TP .B SYSCW (WR_SYSC) The number of write(2) syscalls for the process. .TP .B RBYTES (IO_RBYTES) Bytes of read(2) I/O for the process. .TP .B WBYTES (IO_WBYTES) Bytes of write(2) I/O for the process. .TP .B CNCLWB (IO_CANCEL) Bytes of cancelled write(2) I/O. .TP .B IO_READ_RATE (DISK READ) The I/O rate of read(2) in bytes per second, for the process. .TP .B IO_WRITE_RATE (DISK WRITE) The I/O rate of write(2) in bytes per second, for the process. .TP .B IO_RATE (DISK R/W) The I/O rate, IO_READ_RATE + IO_WRITE_RATE (see above). .TP .B CGROUP Which cgroup the process is in. .TP .B OOM OOM killer score. .TP .B CTXT Incremental sum of voluntary and nonvoluntary context switches. .TP .B IO_PRIORITY (IO) The I/O scheduling class followed by the priority if the class supports it: \fBR\fR for Realtime \fBB\fR for Best-effort \fBid\fR for Idle .TP .B PERCENT_CPU_DELAY (CPUD%) The percentage of time spent waiting for a CPU (while runnable). Requires CAP_NET_ADMIN. .TP .B PERCENT_IO_DELAY (IOD%) The percentage of time spent waiting for the completion of synchronous block I/O. Requires CAP_NET_ADMIN. .TP .B PERCENT_SWAP_DELAY (SWAPD%) The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN. .TP .B COMM The command name for the process. Requires Linux kernel 2.6.33 or newer. .TP .B EXE The executable file of the process as reported by the kernel. Requires CAP_SYS_PTRACE and PTRACE_MODE_READ_FSCRED. .TP .B All other flags Currently unsupported (always displays '-'). .SH "EXTERNAL LIBRARIES" While .B htop depends on most of the libraries it uses at build time there are two noteworthy exceptions to this rule. These exceptions both relate to data displayed in meters displayed in the header of .B htop and were intentionally created as optional runtime dependencies instead. These exceptions are described below: .TP .B libsystemd The bindings for libsystemd are used in the SystemD meter to determine the number of active services and the overall system state. Looking for the functions to determine these information at runtime allows for builds to support these meters without forcing the package manager to install these libraries on systems that otherwise don't use systemd. Summary: no build time dependency, optional runtime dependency on .B libsystemd via dynamic loading, with .B systemctl(1) fallback. .TP .B libsensors The bindings for libsensors are used for the CPU temperature readings in the CPU usage meters if displaying the temperature is enabled through the setup screen. In order for .B htop to show these temperatures correctly though, a proper configuration of libsensors through its usual configuration files is assumed and that all CPU cores correspond to temperature sensors from the .B coretemp driver with core 0 corresponding to a sensor labelled "Core 0". The package temperature may be given as "Package id 0". If missing it is inferred as the maximum value from the available per-core readings. Summary: build time dependency on .B libsensors(3) C header files, optional runtime dependency on .B libsensors(3) via dynamic loading. .SH "CONFIG FILE" By default .B htop reads its configuration from the XDG-compliant path .IR ~/.config/htop/htoprc . The configuration file is overwritten by .BR htop 's in-program Setup configuration, so it should not be hand-edited. If no user configuration exists .B htop tries to read the system-wide configuration from .I @sysconfdir@/htoprc and as a last resort, falls back to its hard coded defaults. .LP You may override the location of the configuration file using the $HTOPRC environment variable (so you can have multiple configurations for different machines that share the same home directory, for example). .SH "MEMORY SIZES" Memory sizes in .B htop are displayed in a human-readable form. Sizes are printed in powers of 1024. (e.g., 1023M = 1072693248 Bytes) .LP The decision to use this convention was made in order to conserve screen space and make memory size representations consistent throughout .BR htop . .SH "SEE ALSO" .BR proc (5), .BR top (1), .BR free (1), .BR ps (1), .BR uptime (1) and .BR limits.conf (5). .SH "AUTHORS" .B htop was originally developed by Hisham Muhammad. Nowadays it is maintained by the community at . htop-3.0.5/htop.c000066400000000000000000000255341377712513700136350ustar00rootroot00000000000000/* htop - htop.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include #include #include #include #include #include #include #include #include "Action.h" #include "CRT.h" #include "Hashtable.h" #include "Header.h" #include "IncSet.h" #include "MainPanel.h" #include "MetersPanel.h" #include "Panel.h" #include "Platform.h" #include "Process.h" #include "ProcessList.h" #include "ProvideCurses.h" #include "ScreenManager.h" #include "Settings.h" #include "UsersTable.h" #include "XUtils.h" static void printVersionFlag(void) { fputs(PACKAGE " " VERSION "\n", stdout); } static void printHelpFlag(void) { fputs(PACKAGE " " VERSION "\n" COPYRIGHT "\n" "Released under the GNU GPLv2.\n\n" "-C --no-color Use a monochrome color scheme\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" "-h --help Print this help screen\n" "-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" "-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" "-U --no-unicode Do not use unicode but plain ASCII\n" "-V --version Print version info\n" "\n" "Long options may be passed with a single dash.\n\n" "Press F1 inside " PACKAGE " for online help.\n" "See 'man " PACKAGE "' for more information.\n", stdout); } // ---------------------------------------- typedef struct CommandLineSettings_ { Hashtable* pidMatchList; char* commFilter; uid_t userId; int sortKey; int delay; bool useColors; bool enableMouse; bool treeView; bool allowUnicode; bool highlightChanges; int highlightDelaySecs; } CommandLineSettings; static CommandLineSettings parseArguments(int argc, char** argv) { CommandLineSettings flags = { .pidMatchList = NULL, .commFilter = NULL, .userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2)) .sortKey = 0, .delay = -1, .useColors = true, .enableMouse = true, .treeView = false, .allowUnicode = true, .highlightChanges = false, .highlightDelaySecs = -1, }; const struct option long_opts[] = { {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"delay", required_argument, 0, 'd'}, {"sort-key", required_argument, 0, 's'}, {"user", optional_argument, 0, 'u'}, {"no-color", no_argument, 0, 'C'}, {"no-colour", no_argument, 0, 'C'}, {"no-mouse", no_argument, 0, 'M'}, {"no-unicode", no_argument, 0, 'U'}, {"tree", no_argument, 0, 't'}, {"pid", required_argument, 0, 'p'}, {"filter", required_argument, 0, 'F'}, {"highlight-changes", optional_argument, 0, 'H'}, {0,0,0,0} }; int opt, opti=0; /* Parse arguments */ while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) { if (opt == EOF) break; switch (opt) { case 'h': printHelpFlag(); exit(0); case 'V': printVersionFlag(); exit(0); case 's': assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */ if (String_eq(optarg, "help")) { for (int j = 1; j < LAST_PROCESSFIELD; j++) { const char* name = Process_fields[j].name; if (name) printf ("%s\n", name); } exit(0); } flags.sortKey = 0; for (int j = 1; j < LAST_PROCESSFIELD; j++) { if (Process_fields[j].name == NULL) continue; if (String_eq(optarg, Process_fields[j].name)) { flags.sortKey = j; break; } } if (flags.sortKey == 0) { fprintf(stderr, "Error: invalid column \"%s\".\n", optarg); exit(1); } break; case 'd': if (sscanf(optarg, "%16d", &(flags.delay)) == 1) { if (flags.delay < 1) flags.delay = 1; if (flags.delay > 100) flags.delay = 100; } else { fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); exit(1); } break; case 'u': { const char *username = optarg; if (!username && optind < argc && argv[optind] != NULL && (argv[optind][0] != '\0' && argv[optind][0] != '-')) { username = argv[optind++]; } if (!username) { flags.userId = geteuid(); } else if (!Action_setUserOnly(username, &(flags.userId))) { fprintf(stderr, "Error: invalid user \"%s\".\n", username); exit(1); } break; } case 'C': flags.useColors = false; break; case 'M': flags.enableMouse = false; break; case 'U': flags.allowUnicode = false; break; case 't': flags.treeView = true; break; case 'p': { assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */ char* argCopy = xStrdup(optarg); char* saveptr; char* pid = strtok_r(argCopy, ",", &saveptr); if(!flags.pidMatchList) { flags.pidMatchList = Hashtable_new(8, false); } while(pid) { unsigned int num_pid = atoi(pid); // deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here Hashtable_put(flags.pidMatchList, num_pid, (void *) 1); pid = strtok_r(NULL, ",", &saveptr); } free(argCopy); break; } case 'F': { assert(optarg); free(flags.commFilter); flags.commFilter = xStrdup(optarg); break; } case 'H': { const char *delay = optarg; if (!delay && optind < argc && argv[optind] != NULL && (argv[optind][0] != '\0' && argv[optind][0] != '-')) { delay = argv[optind++]; } if (delay) { if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) { if (flags.highlightDelaySecs < 1) flags.highlightDelaySecs = 1; } else { fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay); exit(1); } } flags.highlightChanges = true; break; } default: exit(1); } } return flags; } static void millisleep(unsigned long millisec) { struct timespec req = { .tv_sec = 0, .tv_nsec = millisec * 1000000L }; while(nanosleep(&req,&req)==-1) { continue; } } static void setCommFilter(State* state, char** commFilter) { MainPanel* panel = (MainPanel*)state->panel; ProcessList* pl = state->pl; IncSet* inc = panel->inc; size_t maxlen = sizeof(inc->modes[INC_FILTER].buffer) - 1; char* buffer = inc->modes[INC_FILTER].buffer; strncpy(buffer, *commFilter, maxlen); buffer[maxlen] = 0; inc->modes[INC_FILTER].index = strlen(buffer); inc->filtering = true; pl->incFilter = IncSet_filter(inc); free(*commFilter); *commFilter = NULL; } int main(int argc, char** argv) { /* initialize locale */ const char* lc_ctype; if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL"))) setlocale(LC_CTYPE, lc_ctype); else setlocale(LC_CTYPE, ""); CommandLineSettings flags = parseArguments(argc, argv); Platform_init(); Process_setupColumnWidths(); UsersTable* ut = UsersTable_new(); ProcessList* pl = ProcessList_new(ut, flags.pidMatchList, flags.userId); Settings* settings = Settings_new(pl->cpuCount); pl->settings = settings; Header* header = Header_new(pl, settings, 2); Header_populateFromSettings(header); if (flags.delay != -1) settings->delay = flags.delay; if (!flags.useColors) settings->colorScheme = COLORSCHEME_MONOCHROME; if (!flags.enableMouse) settings->enableMouse = false; if (flags.treeView) settings->treeView = true; if (flags.highlightChanges) settings->highlightChanges = true; if (flags.highlightDelaySecs != -1) settings->highlightDelaySecs = flags.highlightDelaySecs; if (flags.sortKey > 0) { // -t -s means "tree sorted by key" // -s means "list sorted by key" (previous existing behavior) if (!flags.treeView) { settings->treeView = false; } Settings_setSortKey(settings, flags.sortKey); } CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode); MainPanel* panel = MainPanel_new(); ProcessList_setPanel(pl, (Panel*) panel); MainPanel_updateTreeFunctions(panel, settings->treeView); State state = { .settings = settings, .ut = ut, .pl = pl, .panel = (Panel*) panel, .header = header, .pauseProcessUpdate = false, .hideProcessSelection = false, }; MainPanel_setState(panel, &state); if (flags.commFilter) setCommFilter(&state, &(flags.commFilter)); ScreenManager* scr = ScreenManager_new(header, settings, &state, true); ScreenManager_add(scr, (Panel*) panel, -1); ProcessList_scan(pl, false); millisleep(75); ProcessList_scan(pl, false); ScreenManager_run(scr, NULL, NULL); attron(CRT_colors[RESET_COLOR]); mvhline(LINES-1, 0, ' ', COLS); attroff(CRT_colors[RESET_COLOR]); refresh(); Platform_done(); CRT_done(); if (settings->changed) Settings_write(settings); Header_delete(header); ProcessList_delete(pl); ScreenManager_delete(scr); MetersPanel_cleanup(); UsersTable_delete(ut); Settings_delete(settings); if (flags.pidMatchList) Hashtable_delete(flags.pidMatchList); return 0; } htop-3.0.5/htop.desktop000066400000000000000000000047621377712513700150640ustar00rootroot00000000000000[Desktop Entry] Type=Application Version=1.0 Name=Htop GenericName=Process Viewer GenericName[ca]=Visualitzador de processos GenericName[da]=Procesfremviser GenericName[de]=Prozessanzeige GenericName[en_GB]=Process Viewer GenericName[es]=Visor de procesos GenericName[fi]=Prosessikatselin GenericName[fr]=Visualiseur de processus GenericName[gl]=Visor de procesos GenericName[it]=Visore dei processi GenericName[ko]=프로세스 ë·°ě–´ GenericName[nb]=Prosessviser GenericName[nl]=Viewer van processen GenericName[nn]=Prosessvisar GenericName[pl]=PrzeglÄ…darka procesĂłw GenericName[pt]=Visualizador de Processos GenericName[pt_BR]=Visualizador de processos GenericName[ru]=Монитор процеŃŃов GenericName[sk]=PrehliadaÄŤ procesov GenericName[sl]=Pregledovalnik opravil GenericName[sr@ijekavian]=Приказивач процеŃа GenericName[sr@ijekavianlatin]=PrikazivaÄŤ procesa GenericName[sr@latin]=PrikazivaÄŤ procesa GenericName[sr]=Приказивач процеŃа GenericName[sv]=Processvisning GenericName[tr]=SĂĽreç GörĂĽntĂĽleyici GenericName[uk]=Перегляд процеŃів GenericName[zh_CN]=进程查看器 GenericName[zh_TW]=行程檢視器 Comment=Show System Processes Comment[ca]=Visualitzeu els processos del sistema Comment[da]=Vis systemprocesser Comment[de]=Systemprozesse anzeigen Comment[en_GB]=Show System Processes Comment[es]=Mostrar procesos del sistema Comment[fi]=Katsele järjestelmän prosesseja Comment[fr]=Affiche les processus système Comment[gl]=Mostrar os procesos do sistema. Comment[it]=Mostra processi di sistema Comment[ko]=시스템 프로세스 보기 Comment[nb]=Vis systemprosesser Comment[nl]=Systeemprocessen tonen Comment[nn]=Vis systemprosessar Comment[pl]=PokaĹĽ procesy systemowe Comment[pt]=Mostrar os Processos do Sistema Comment[pt_BR]=Mostra os processos do sistema Comment[ru]=ПроŃмотр ŃпиŃка процеŃŃов в ŃиŃтеме Comment[sk]=ZobraziĹĄ systĂ©movĂ© procesy Comment[sl]=Prikaz sistemskih opravil Comment[sr@ijekavian]=Приказ ŃиŃтемŃких процеŃа Comment[sr@ijekavianlatin]=Prikaz sistemskih procesa Comment[sr@latin]=Prikaz sistemskih procesa Comment[sr]=Приказ ŃиŃтемŃких процеŃа Comment[sv]=Visa systemprocesser Comment[tr]=Sistem SĂĽreçlerini Göster Comment[uk]=Перегляд ŃиŃтемних процеŃів Comment[zh_CN]=ćľç¤şçł»ç»źčż›ç¨‹ Comment[zh_TW]=顯示系統行程 Icon=htop Exec=htop Terminal=true Categories=System;Monitor;ConsoleOnly; Keywords=system;process;task htop-3.0.5/htop.png000066400000000000000000000067211377712513700141740ustar00rootroot00000000000000‰PNG  IHDR€€ôŕ‘ůPLTEGpL†í†š šwK©KT¶TI..!OŻO™˙™wT·TWşWI§IL«Ld©dQ´QP±Pú{{J¨JMHHWĽWhqhýllHĄH#i#ZŔZU¸UCKCXľX:—:v‡đ‡”H¤HbkbZŔZ‹–‹RXRËKK&‚&9A9\d\AGAą77jtjvv_Ä_ľ==X`X†ě†µ33Ľ;;$$ajaęeneww~–~NUN‘ČHHµ22Ť–ŤĂBB‡ě‡ĆFF»99Y`Y”ĚMMçvs”_g_ÎNNtÚtmwmgÎg?ś?ň˙ň9–944{á{-‰-&& | w{{Á??[c[†í†hĎhnÔn9?9Ţ__¬%%tÚtV€V÷xx=D=…“…±..wŢwirik¤{…{ttJQJw…w9@9 ěmmaÇaq{qRYR¤rďooŤ×ŤźbÇbÔUUçhhRYRFMF–˙€€ZbZ}}Ű[[źs}sSZSV]VRZR9?9“LSLą‰‚q×qęll÷vv–Ś–ŚŚ—ŚSYS‡‘‡pz„z‹•‹9A9Ś–ŚRZRڕڋ•‹P°PM­MYľYR´RJ¨JI¦ISµSVąVX˝XT·TL«LWşWU¸UOŻOK©KZŔZ[Á[H¤HaÇahÎh?ś?nÔn9–9tÚt-‰- } zázçÄCCľ<ý881ő¸¸889;ř ý)=ľő¸öľľÝűľ»ű(űÝ88Ý»»»»»»»»·AľÝµ<şÖ»»ş<¸ţB8<ţş9Xţ)ç¶7ąC§=»˝˝»»ĆţĽáţ=8çőŢţÇ&:ľ=:<7ćŐ¦ţ ŰĺĺʧŕÚˇá+ áIDATxÚíšit”ŐÇŻ8“##AΠF¤H†C4F-&V[×c@ăžj<\¨¤@‚q)íaQ+GŃ*ÔÖ}ß÷KĄnŐŇş/­ű®µ›ĆîĄ÷ţźűÎ}n2oć™đľžzŽľ<çžąĎďCřň;ŻJO˛éî·Bş•RhÜjžY-Űb¤ç٨Ô$—”j˙–KŁš¶2M©–ťiÜ`gý[WK¶ů#36Ef¨Fv˛]µŻ ˛bEŁÚçdó[0żeµ !ŹĚř9I5®uŃĎčü‡Ň¨ą9DďKăý“őĚjAŘödűȱz>éCDĐ5sř™vŐúęż‚,jTgßchĽÇăjAŘödű8í=ä4Ő¸¨pňŐVŐúk—9ęČ»#őţtď2¬„mO¶ŹL×óŃď"G«9ě¤řĺ›:sÔw ď¨gV ¶'ÚGđ˘Ě-{¶UÍďééY·nÝŇŮ:“Ô)·#ó Ť·V 2ʶO1öpę[Č©j’ąµt]ŹÉ|5á/Č”t*•RjĚ­Čćzco5¬dsÚcě#ëůO"{+ĄOĄ§ĐÝ jÂ/‘))üvĚ-hĽe˘_KhöĽŚěŤťÔşŰŕr4^>ŃŻ%´ űHÇýĆd3 p-ŻÍéyŤ#dl;gŔăŘ gŹë °…űm\€$xů¶°‹ÚĺŹHpRihĽ4JÜvŇ>R€î€U&Ŕ5hĽ&éׂTŇöŔ>€7‘g Ŕ*ŕF4Ţôk mŔ>bvńVi€O‘ŕ:4^—ôk mŔ>€‘€îî¤vútµÉ60ěj4^ťôk m3ö‘ľŰŕ¬Xí\€Ć+’®&`ŰIűCľËVk€_ Ŕgům†ĆĎ’~-pŰIűHĆĽ€t·7ŔŕK‘Á€ĆK“~-ŰNÚGBĆ«ń?Gľ€Bş[ŘßÜ„€Ć›~-pŰ űřě_řb6ą Ť—ŔŐ·ť°Źŕ5$ »ŕa`=e¬„mC ŔH€łĂĂS-ŔőhĽ>á×·ť°ŹŕQÄLĄ»ăŐöB,Ŕđ›‘á€Ć›ŕjAě#°Ź€ú—€înŻ1Ů“F]‰€Ć+ ŔpWKÜvÂ>Âvł8kń®B@ăU ż–Đö(`ŔŘ“î€5&>@Mq€@MŔ+H€ł`ŤpoKřµ€¶` X3VŤý3rFEUUUJŤzŤĎŔŐÚ€}o#»©”>UqÝŐgţÝäôčŚUމ¨÷GÓxçh=łZGÚţ´©±ćÖé8{¦Řő÷Č®:YuĐÝČAć·4Ţm~ËjABi{iSYsË^Ő'ţ6ȉYuđČÁzŤ_߲Z¶=Ú>2NĎ'|„ś ˛ěd­Şý§KVz/r¨ ńŢ=łZ¶Ý`ŔÇČÉ*ë..ÔżrÉŞĂ>G34~nX-Űn°Ź€ď‚hvRĚtÉŞĂďCŽ04Ţgl}¸ €m7ŘGđ_ä‡*ËNÖŞeG™ü©UGl‰€Ć- «aŰ öě…h€Ł\–á?e”:ľ9^ď§i¬Në™Ő‚„®¸łłó‚eW\^=ëßĐFc›ZvAç%— Ř—WĎ"?ü~—Ř?…ŔÓř\±«G—U/!?üQW?®xá_,”¸âšKaV/!?üq—çŠŮÉ>®ři®Ć§áŠKÖ ¶ç×KČŇŐź+~‘şâšKaVź÷7Ęą€F㊟ 2SlGéčč¸Hl„€ĆŤŕŠKÖ ¶ç×ç‘Ţë\ĎoçR«R.)uá†Č˝_Mă†Őz©ô[_¨ÇŠ­)z>‹ĆłTÚ;ŮËÚ°9–úë|•]± #)ě»â‘ýąâźţDgecŘ'Áĺ}W\ÂJ-/ꊸâmĎź­ÚW”çŠ%ŽĹ RÂeşâČľ+.HáX\± )‹+–R¸,W<_'˘ďŠ R¸lWŃwĹĚÉĆŕŠK§Ą@ěß3)+–8'+Ž –ŽÁKśŽÁ ¤p ®Xŕśl ®X°{<®Xŕśl ®x`şb €“Â1¸bAŽÁËvŚÇKśŽÁKśŽÁKś“ŤÁ R_ "W, )\Ź+–8'+–Ô—vĹ1}W̤pů®8ŠďŠ R8W,HA ;WÜŐËwvĆů]qA ÇâŠ)Hár]qTߤp,®X“),vĹW\¡®x¦ďŠ;lŚ+ŢŤűB »ZHá0W|QG+N«ű!°ż4îg>fµ ),qĹ˙6—Ó.ďMżIEND®B`‚htop-3.0.5/htop.svg000066400000000000000000000257021377712513700142070ustar00rootroot00000000000000 htop-3.0.5/iwyu/000077500000000000000000000000001377712513700135035ustar00rootroot00000000000000htop-3.0.5/iwyu/htop.imp000066400000000000000000000015351377712513700151700ustar00rootroot00000000000000[ { include: ["", "private", "\"ProvideCurses.h\"", "public"] }, { include: ["", "private", "\"ProvideCurses.h\"", "public"] }, { include: ["", "private", "\"ProvideCurses.h\"", "public"] }, { include: ["", "private", "\"ProvideCurses.h\"", "public"] }, { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, ] htop-3.0.5/iwyu/run_iwyu.sh000077500000000000000000000005001377712513700157160ustar00rootroot00000000000000#!/bin/sh SCRIPT=$(readlink -f "$0") SCRIPTDIR=$(dirname "$SCRIPT") SOURCEDIR="$SCRIPTDIR/.." PKG_NL3=$(pkg-config --cflags libnl-3.0) IWYU=${IWYU:-iwyu} cd "$SOURCEDIR" || exit make clean make -k -s CC="$IWYU" CFLAGS="-Xiwyu --no_comments -Xiwyu --no_fwd_decl -Xiwyu --mapping_file='$SCRIPTDIR/htop.imp' $PKG_NL3" htop-3.0.5/linux/000077500000000000000000000000001377712513700136455ustar00rootroot00000000000000htop-3.0.5/linux/IOPriority.h000066400000000000000000000017401377712513700160710ustar00rootroot00000000000000#ifndef HEADER_IOPriority #define HEADER_IOPriority /* htop - IOPriority.h (C) 2004-2012 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. Based on ionice, Copyright (C) 2005 Jens Axboe Released under the terms of the GNU General Public License version 2 */ enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE, }; #define IOPRIO_WHO_PROCESS 1 #define IOPRIO_CLASS_SHIFT (13) #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) #define IOPriority_class(ioprio_) ((int) ((ioprio_) >> IOPRIO_CLASS_SHIFT) ) #define IOPriority_data(ioprio_) ((int) ((ioprio_) & IOPRIO_PRIO_MASK) ) typedef int IOPriority; #define IOPriority_tuple(class_, data_) (((class_) << IOPRIO_CLASS_SHIFT) | (data_)) #define IOPriority_error 0xffffffff #define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0) #define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7) #endif htop-3.0.5/linux/IOPriorityPanel.c000066400000000000000000000033421377712513700170440ustar00rootroot00000000000000/* htop - IOPriorityPanel.c (C) 2004-2012 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "IOPriorityPanel.h" #include #include #include "FunctionBar.h" #include "ListItem.h" #include "Object.h" #include "XUtils.h" Panel* IOPriorityPanel_new(IOPriority currPrio) { Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Set ", "Cancel ")); Panel_setHeader(this, "IO Priority:"); Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None)); if (currPrio == IOPriority_None) { Panel_setSelected(this, 0); } static const struct { int klass; const char* name; } classes[] = { { .klass = IOPRIO_CLASS_RT, .name = "Realtime" }, { .klass = IOPRIO_CLASS_BE, .name = "Best-effort" }, { .klass = 0, .name = NULL } }; for (int c = 0; classes[c].name; c++) { for (int i = 0; i < 8; i++) { char name[50]; xSnprintf(name, sizeof(name), "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : "")); IOPriority ioprio = IOPriority_tuple(classes[c].klass, i); Panel_add(this, (Object*) ListItem_new(name, ioprio)); if (currPrio == ioprio) { Panel_setSelected(this, Panel_size(this) - 1); } } } Panel_add(this, (Object*) ListItem_new("Idle", IOPriority_Idle)); if (currPrio == IOPriority_Idle) { Panel_setSelected(this, Panel_size(this) - 1); } return this; } IOPriority IOPriorityPanel_getIOPriority(Panel* this) { const ListItem* selected = (ListItem*) Panel_getSelected(this); return selected ? selected->key : IOPriority_None; } htop-3.0.5/linux/IOPriorityPanel.h000066400000000000000000000005751377712513700170560ustar00rootroot00000000000000#ifndef HEADER_IOPriorityPanel #define HEADER_IOPriorityPanel /* htop - IOPriorityPanel.h (C) 2004-2012 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Panel.h" #include "IOPriority.h" Panel* IOPriorityPanel_new(IOPriority currPrio); IOPriority IOPriorityPanel_getIOPriority(Panel* this); #endif htop-3.0.5/linux/LibSensors.c000066400000000000000000000124201377712513700160730ustar00rootroot00000000000000#include "LibSensors.h" #ifdef HAVE_SENSORS_SENSORS_H #include #include #include #include #include "XUtils.h" static int (*sym_sensors_init)(FILE*); static void (*sym_sensors_cleanup)(void); static const sensors_chip_name* (*sym_sensors_get_detected_chips)(const sensors_chip_name*, int*); static int (*sym_sensors_snprintf_chip_name)(char*, size_t, const sensors_chip_name*); static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*); static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type); static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*); static char* (*sym_sensors_get_label)(const sensors_chip_name*, const sensors_feature*); static void* dlopenHandle = NULL; int LibSensors_init(FILE* input) { if (!dlopenHandle) { /* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without matching symlinks (unless people install the -dev packages) */ dlopenHandle = dlopen("libsensors.so", RTLD_LAZY); if (!dlopenHandle) dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY); if (!dlopenHandle) dlopenHandle = dlopen("libsensors.so.4", RTLD_LAZY); if (!dlopenHandle) goto dlfailure; /* Clear any errors */ dlerror(); #define resolve(symbolname) do { \ *(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname); \ if (!sym_##symbolname || dlerror() != NULL) \ goto dlfailure; \ } while(0) resolve(sensors_init); resolve(sensors_cleanup); resolve(sensors_get_detected_chips); resolve(sensors_snprintf_chip_name); resolve(sensors_get_features); resolve(sensors_get_subfeature); resolve(sensors_get_value); resolve(sensors_get_label); #undef resolve } return sym_sensors_init(input); dlfailure: if (dlopenHandle) { dlclose(dlopenHandle); dlopenHandle = NULL; } return -1; } void LibSensors_cleanup(void) { if (dlopenHandle) { sym_sensors_cleanup(); dlclose(dlopenHandle); dlopenHandle = NULL; } } void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) { for (unsigned int i = 0; i <= cpuCount; i++) cpus[i].temperature = NAN; if (!dlopenHandle) return; unsigned int coreTempCount = 0; int n = 0; for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) { char buffer[32]; sym_sensors_snprintf_chip_name(buffer, sizeof(buffer), chip); if (!String_startsWith(buffer, "coretemp") && !String_startsWith(buffer, "cpu_thermal") && !String_startsWith(buffer, "k10temp") && !String_startsWith(buffer, "zenpower")) continue; int m = 0; for (const sensors_feature *feature = sym_sensors_get_features(chip, &m); feature; feature = sym_sensors_get_features(chip, &m)) { if (feature->type != SENSORS_FEATURE_TEMP) continue; char* label = sym_sensors_get_label(chip, feature); if (!label) continue; unsigned int tempId; if (String_startsWith(label, "Package ")) { tempId = 0; } else if (String_startsWith(label, "temp")) { /* Raspberry Pi has only temp1 */ tempId = 0; } else if (String_startsWith(label, "Tdie")) { tempId = 0; } else if (String_startsWith(label, "Core ")) { tempId = 1 + atoi(label + strlen("Core ")); } else { tempId = UINT_MAX; } free(label); if (tempId > cpuCount) continue; const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT); if (sub_feature) { double temp; int r = sym_sensors_get_value(chip, sub_feature->number, &temp); if (r != 0) continue; cpus[tempId].temperature = temp; if (tempId > 0) coreTempCount++; } } } const double packageTemp = cpus[0].temperature; /* Only package temperature - copy to all cpus */ if (coreTempCount == 0 && !isnan(packageTemp)) { for (unsigned int i = 1; i <= cpuCount; i++) cpus[i].temperature = packageTemp; return; } /* No package temperature - set to max core temperature */ if (isnan(packageTemp) && coreTempCount != 0) { double maxTemp = NAN; for (unsigned int i = 1; i <= cpuCount; i++) { const double coreTemp = cpus[i].temperature; if (isnan(coreTemp)) continue; maxTemp = MAXIMUM(maxTemp, coreTemp); } cpus[0].temperature = maxTemp; } /* Half the temperatures, probably HT/SMT - copy to second half */ const unsigned int delta = cpuCount / 2; if (coreTempCount == delta) { for (unsigned int i = 1; i <= delta; i++) cpus[i + delta].temperature = cpus[i].temperature; } } #endif /* HAVE_SENSORS_SENSORS_H */ htop-3.0.5/linux/LibSensors.h000066400000000000000000000004771377712513700161110ustar00rootroot00000000000000#ifndef HEADER_LibSensors #define HEADER_LibSensors #include "config.h" // IWYU pragma: keep #include #include "LinuxProcessList.h" int LibSensors_init(FILE* input); void LibSensors_cleanup(void); void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount); #endif /* HEADER_LibSensors */ htop-3.0.5/linux/LinuxProcess.c000066400000000000000000001131551377712513700164550ustar00rootroot00000000000000/* htop - LinuxProcess.c (C) 2014 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "LinuxProcess.h" #include #include #include #include #include #include #include "CRT.h" #include "Macros.h" #include "Process.h" #include "ProvideCurses.h" #include "RichString.h" #include "XUtils.h" /* semi-global */ int pageSize; int pageSizeKB; /* Used to identify kernel threads in Comm and Exe columns */ static const char *const kthreadID = "KTHREAD"; const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, }, [TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [CMAJFLT] = { .name = "CMAJFLT", .title = " CMAJFLT ", .description = "Children processes' major faults", .flags = 0, }, [UTIME] = { .name = "UTIME", .title = " UTIME+ ", .description = "User CPU time - time the process spent executing in user mode", .flags = 0, }, [STIME] = { .name = "STIME", .title = " STIME+ ", .description = "System CPU time - time the kernel spent running system calls for this process", .flags = 0, }, [CUTIME] = { .name = "CUTIME", .title = " CUTIME+ ", .description = "Children processes' user CPU time", .flags = 0, }, [CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [M_SHARE] = { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, }, [M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, }, [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, }, [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, }, [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, #ifdef HAVE_OPENVZ [CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, }, [VPID] = { .name = "VPID", .title = "VPID", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, .pidColumn = true, }, #endif #ifdef HAVE_VSERVER [VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, }, #endif [RCHAR] = { .name = "RCHAR", .title = " RD_CHAR ", .description = "Number of bytes the process has read", .flags = PROCESS_FLAG_IO, }, [WCHAR] = { .name = "WCHAR", .title = " WR_CHAR ", .description = "Number of bytes the process has written", .flags = PROCESS_FLAG_IO, }, [SYSCR] = { .name = "SYSCR", .title = " RD_SYSC ", .description = "Number of read(2) syscalls for the process", .flags = PROCESS_FLAG_IO, }, [SYSCW] = { .name = "SYSCW", .title = " WR_SYSC ", .description = "Number of write(2) syscalls for the process", .flags = PROCESS_FLAG_IO, }, [RBYTES] = { .name = "RBYTES", .title = " IO_RBYTES ", .description = "Bytes of read(2) I/O for the process", .flags = PROCESS_FLAG_IO, }, [WBYTES] = { .name = "WBYTES", .title = " IO_WBYTES ", .description = "Bytes of write(2) I/O for the process", .flags = PROCESS_FLAG_IO, }, [CNCLWB] = { .name = "CNCLWB", .title = " IO_CANCEL ", .description = "Bytes of cancelled write(2) I/O", .flags = PROCESS_FLAG_IO, }, [IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, }, [IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, }, [IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, }, [CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, #ifdef HAVE_DELAYACCT [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, }, [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, }, [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, }, #endif [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, }, [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, }, [M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, }, [CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, }, [SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, }, [PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, }, [PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, }, [CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, }, }; /* This function returns the string displayed in Command column, so that sorting * happens on what is displayed - whether comm, full path, basename, etc.. So * this follows LinuxProcess_writeField(COMM) and LinuxProcess_writeCommand */ static const char* LinuxProcess_getCommandStr(const Process *this) { const LinuxProcess *lp = (const LinuxProcess *)this; if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) { return this->comm; } return lp->mergedCommand.str; } Process* LinuxProcess_new(const Settings* settings) { LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess)); Object_setClass(this, Class(LinuxProcess)); Process_init(&this->super, settings); return &this->super; } void Process_delete(Object* cast) { LinuxProcess* this = (LinuxProcess*) cast; Process_done((Process*)cast); free(this->cgroup); #ifdef HAVE_OPENVZ free(this->ctid); #endif free(this->cwd); free(this->secattr); free(this->ttyDevice); free(this->procExe); free(this->procComm); free(this->mergedCommand.str); free(this); } /* [1] Note that before kernel 2.6.26 a process that has not asked for an io priority formally uses "none" as scheduling class, but the io scheduler will treat such processes as if it were in the best effort class. The priority within the best effort class will be dynamically derived from the cpu nice level of the process: io_priority = (cpu_nice + 20) / 5. -- From ionice(1) man page */ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) { if (IOPriority_class(this->ioPriority) == IOPRIO_CLASS_NONE) { return IOPriority_tuple(IOPRIO_CLASS_BE, (this->super.nice + 20) / 5); } return this->ioPriority; } #ifdef __ANDROID__ #define SYS_ioprio_get __NR_ioprio_get #define SYS_ioprio_set __NR_ioprio_set #endif IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) { IOPriority ioprio = 0; // Other OSes masquerading as Linux (NetBSD?) don't have this syscall #ifdef SYS_ioprio_get ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, this->super.pid); #endif this->ioPriority = ioprio; return ioprio; } bool LinuxProcess_setIOPriority(Process* this, Arg ioprio) { // Other OSes masquerading as Linux (NetBSD?) don't have this syscall #ifdef SYS_ioprio_set syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->pid, ioprio.i); #endif return (LinuxProcess_updateIOPriority((LinuxProcess*)this) == ioprio.i); } #ifdef HAVE_DELAYACCT static void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) { if (isnan(delay_percent)) { xSnprintf(buffer, n, " N/A "); } else { xSnprintf(buffer, n, "%4.1f ", delay_percent); } } #endif /* TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is not available in an userspace header - so define it. Note: when colorizing a basename with the comm prefix, the entire basename (not just the comm prefix) is colorized for better readability, and it is implicit that only upto (TASK_COMM_LEN - 1) could be comm */ #define TASK_COMM_LEN 16 static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdlineBasenameOffset, int *pCommStart, int *pCommEnd) { /* Try to find procComm in tokenized cmdline - this might in rare cases * mis-identify a string or fail, if comm or cmdline had been unsuitably * modified by the process */ const char *tokenBase; size_t tokenLen; const size_t commLen = strlen(comm); for (const char *token = cmdline + cmdlineBasenameOffset; *token; ) { for (tokenBase = token; *token && *token != '\n'; ++token) { if (*token == '/') { tokenBase = token + 1; } } tokenLen = token - tokenBase; if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) && strncmp(tokenBase, comm, commLen) == 0) { *pCommStart = tokenBase - cmdline; *pCommEnd = token - cmdline; return true; } if (*token) { do { ++token; } while ('\n' == *token); } } return false; } static int matchCmdlinePrefixWithExeSuffix(const char *cmdline, int cmdlineBaseOffset, const char *exe, int exeBaseOffset, int exeBaseLen) { int matchLen; /* matching length to be returned */ char delim; /* delimiter following basename */ /* cmdline prefix is an absolute path: it must match whole exe. */ if (cmdline[0] == '/') { matchLen = exeBaseLen + exeBaseOffset; if (strncmp(cmdline, exe, matchLen) == 0) { delim = cmdline[matchLen]; if (delim == 0 || delim == '\n' || delim == ' ') { return matchLen; } } return 0; } /* cmdline prefix is a relative path: We need to first match the basename at * cmdlineBaseOffset and then reverse match the cmdline prefix with the exe * suffix. But there is a catch: Some processes modify their cmdline in ways * that make htop's identification of the basename in cmdline unreliable. * For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to * "gdm-session-worker [pam/gdm-autologin]" and htop ends up with * procCmdlineBasenameOffset at "gdm-autologin]". This issue could arise with * chrome as well as it stores in cmdline its concatenated argument vector, * without NUL delimiter between the arguments (which may contain a '/') * * So if needed, we adjust cmdlineBaseOffset to the previous (if any) * component of the cmdline relative path, and retry the procedure. */ bool delimFound; /* if valid basename delimiter found */ do { /* match basename */ matchLen = exeBaseLen + cmdlineBaseOffset; if (cmdlineBaseOffset < exeBaseOffset && strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) { delim = cmdline[matchLen]; if (delim == 0 || delim == '\n' || delim == ' ') { int i, j; /* reverse match the cmdline prefix and exe suffix */ for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1; i >= 0 && cmdline[i] == exe[j]; --i, --j) ; /* full match, with exe suffix being a valid relative path */ if (i < 0 && exe[j] == '/') { return matchLen; } } } /* Try to find the previous potential cmdlineBaseOffset - it would be * preceded by '/' or nothing, and delimited by ' ' or '\n' */ for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) { if (delimFound) { if (cmdline[cmdlineBaseOffset - 1] == '/') { break; } } else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') { delimFound = true; } } } while (delimFound); return 0; } /* stpcpy, but also converts newlines to spaces */ static inline char *stpcpyWithNewlineConversion(char *dstStr, const char *srcStr) { for (; *srcStr; ++srcStr) { *dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr; } *dstStr = 0; return dstStr; } /* This function makes the merged Command string. It also stores the offsets of the basename, comm w.r.t the merged Command string - these offsets will be used by LinuxProcess_writeCommand() for coloring. The merged Command string is also returned by LinuxProcess_getCommandStr() for searching, sorting and filtering. */ void LinuxProcess_makeCommandStr(Process* this) { LinuxProcess *lp = (LinuxProcess *)this; LinuxProcessMergedCommand *mc = &lp->mergedCommand; bool showMergedCommand = this->settings->showMergedCommand; bool showProgramPath = this->settings->showProgramPath; bool searchCommInCmdline = this->settings->findCommInCmdline; bool stripExeFromCmdline = this->settings->stripExeFromCmdline; /* lp->mergedCommand.str needs updating only if its state or contents changed. * Its content is based on the fields cmdline, comm, and exe. */ if ( mc->prevMergeSet == showMergedCommand && mc->prevPathSet == showProgramPath && mc->prevCommSet == searchCommInCmdline && mc->prevCmdlineSet == stripExeFromCmdline && !mc->cmdlineChanged && !mc->commChanged && !mc->exeChanged ) { return; } /* The field separtor "│" has been chosen such that it will not match any * valid string used for searching or filtering */ const char *SEPARATOR = CRT_treeStr[TREE_STR_VERT]; const int SEPARATOR_LEN = strlen(SEPARATOR); /* Check for any changed fields since we last built this string */ if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) { free(mc->str); /* Accommodate the column text, two field separators and terminating NUL */ mc->str = xCalloc(1, mc->maxLen + 2*SEPARATOR_LEN + 1); } /* Preserve the settings used in this run */ mc->prevMergeSet = showMergedCommand; mc->prevPathSet = showProgramPath; mc->prevCommSet = searchCommInCmdline; mc->prevCmdlineSet = stripExeFromCmdline; /* Mark everything as unchanged */ mc->cmdlineChanged = false; mc->commChanged = false; mc->exeChanged = false; /* Clear any separators */ mc->sep1 = 0; mc->sep2 = 0; /* Clear any highlighting locations */ mc->baseStart = 0; mc->baseEnd = 0; mc->commStart = 0; mc->commEnd = 0; const char *cmdline = this->comm; const char *procExe = lp->procExe; const char *procComm = lp->procComm; char *strStart = mc->str; char *str = strStart; int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset; int cmdlineBasenameEnd = lp->procCmdlineBasenameEnd; if (!cmdline) { cmdlineBasenameOffset = 0; cmdlineBasenameEnd = 0; cmdline = "(zombie)"; } assert(cmdlineBasenameOffset >= 0); assert(cmdlineBasenameOffset <= (int)strlen(cmdline)); if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */ if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */ if (strncmp(cmdline + cmdlineBasenameOffset, procComm, MINIMUM(TASK_COMM_LEN - 1, strlen(procComm))) != 0) { mc->commStart = 0; mc->commEnd = strlen(procComm); str = stpcpy(str, procComm); mc->sep1 = str - strStart; str = stpcpy(str, SEPARATOR); } } if (showProgramPath) { (void) stpcpyWithNewlineConversion(str, cmdline); mc->baseStart = cmdlineBasenameOffset; mc->baseEnd = cmdlineBasenameEnd; } else { (void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset); mc->baseStart = 0; mc->baseEnd = cmdlineBasenameEnd - cmdlineBasenameOffset; } if (mc->sep1) { mc->baseStart += str - strStart - SEPARATOR_LEN + 1; mc->baseEnd += str - strStart - SEPARATOR_LEN + 1; } return; } int exeLen = lp->procExeLen; int exeBasenameOffset = lp->procExeBasenameOffset; int exeBasenameLen = exeLen - exeBasenameOffset; assert(exeBasenameOffset >= 0); assert(exeBasenameOffset <= (int)strlen(procExe)); /* Start with copying exe */ if (showProgramPath) { str = stpcpy(str, procExe); mc->baseStart = exeBasenameOffset; mc->baseEnd = exeLen; } else { str = stpcpy(str, procExe + exeBasenameOffset); mc->baseStart = 0; mc->baseEnd = exeBasenameLen; } mc->sep1 = 0; mc->sep2 = 0; int commStart = 0; int commEnd = 0; bool commInCmdline = false; /* Try to match procComm with procExe's basename: This is reliable (predictable) */ if (strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0) { commStart = mc->baseStart; commEnd = mc->baseEnd; } else if (searchCommInCmdline) { /* commStart/commEnd will be adjusted later along with cmdline */ commInCmdline = findCommInCmdline(procComm, cmdline, cmdlineBasenameOffset, &commStart, &commEnd); } int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameOffset, procExe, exeBasenameOffset, exeBasenameLen); /* Note: commStart, commEnd are offsets into RichString. But the multibyte * separator (with size SEPARATOR_LEN) has size 1 in RichString. The offset * adjustments below reflect this. */ if (commEnd) { mc->unmatchedExe = !matchLen; if (matchLen) { /* strip the matched exe prefix */ cmdline += matchLen; if (commInCmdline) { commStart += str - strStart - matchLen; commEnd += str - strStart - matchLen; } } else { /* cmdline will be a separate field */ mc->sep1 = str - strStart; str = stpcpy(str, SEPARATOR); if (commInCmdline) { commStart += str - strStart - SEPARATOR_LEN + 1; commEnd += str - strStart - SEPARATOR_LEN + 1; } } mc->separateComm = false; /* procComm merged */ } else { mc->sep1 = str - strStart; str = stpcpy(str, SEPARATOR); commStart = str - strStart - SEPARATOR_LEN + 1; str = stpcpy(str, procComm); commEnd = str - strStart - SEPARATOR_LEN + 1; /* or commStart + strlen(procComm) */ mc->unmatchedExe = !matchLen; if (matchLen) { if (stripExeFromCmdline) { cmdline += matchLen; } } if (*cmdline) { mc->sep2 = str - strStart - SEPARATOR_LEN + 1; str = stpcpy(str, SEPARATOR); } mc->separateComm = true; /* procComm a separate field */ } /* Display cmdline if it hasn't been consumed by procExe */ if (*cmdline) { (void) stpcpyWithNewlineConversion(str, cmdline); } mc->commStart = commStart; mc->commEnd = commEnd; } static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAttr, RichString* str) { const LinuxProcess *lp = (const LinuxProcess *)this; const LinuxProcessMergedCommand *mc = &lp->mergedCommand; int strStart = RichString_size(str); int baseStart = strStart + lp->mergedCommand.baseStart; int baseEnd = strStart + lp->mergedCommand.baseEnd; int commStart = strStart + lp->mergedCommand.commStart; int commEnd = strStart + lp->mergedCommand.commEnd; int commAttr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM]; bool highlightBaseName = this->settings->highlightBaseName; if(lp->procExeDeleted) baseAttr = CRT_colors[FAILED_READ]; RichString_appendWide(str, attr, lp->mergedCommand.str); if (lp->mergedCommand.commEnd) { if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) { /* If it was matched with procExe's basename, make it bold if needed */ if (commEnd > baseEnd) { RichString_setAttrn(str, A_BOLD | baseAttr, baseStart, baseEnd - baseStart); RichString_setAttrn(str, A_BOLD | commAttr, baseEnd, commEnd - baseEnd); } else if (commEnd < baseEnd) { RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - commStart); RichString_setAttrn(str, A_BOLD | baseAttr, commEnd, baseEnd - commEnd); } else { // Actually should be highlighted commAttr, but marked baseAttr to reduce visual noise RichString_setAttrn(str, A_BOLD | baseAttr, commStart, commEnd - commStart); } baseStart = baseEnd; } else { RichString_setAttrn(str, commAttr, commStart, commEnd - commStart); } } if (baseStart < baseEnd && highlightBaseName) { RichString_setAttrn(str, baseAttr, baseStart, baseEnd - baseStart); } if (mc->sep1) RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep1, 1); if (mc->sep2) RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep2, 1); } static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) { /* This code is from Process_writeField for COMM, but we invoke * LinuxProcess_writeCommand to display * /proc/pid/exe (or its basename)│/proc/pid/comm│/proc/pid/cmdline */ int baseattr = CRT_colors[PROCESS_BASENAME]; if (this->settings->highlightThreads && Process_isThread(this)) { attr = CRT_colors[PROCESS_THREAD]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } if (!this->settings->treeView || this->indent == 0) { LinuxProcess_writeCommand(this, attr, baseattr, str); } else { char* buf = buffer; int maxIndent = 0; bool lastItem = (this->indent < 0); int indent = (this->indent < 0 ? -this->indent : this->indent); int vertLen = strlen(CRT_treeStr[TREE_STR_VERT]); for (int i = 0; i < 32; i++) { if (indent & (1U << i)) { maxIndent = i+1; } } for (int i = 0; i < maxIndent - 1; i++) { if (indent & (1 << i)) { if (buf - buffer + (vertLen + 3) > n) { break; } buf = stpcpy(buf, CRT_treeStr[TREE_STR_VERT]); buf = stpcpy(buf, " "); } else { if (buf - buffer + 4 > n) { break; } buf = stpcpy(buf, " "); } } n -= (buf - buffer); const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE]; xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer); LinuxProcess_writeCommand(this, attr, baseattr, str); } } static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) { const LinuxProcess* lp = (const LinuxProcess*) this; bool coloring = this->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; switch (field) { case TTY_NR: { if (lp->ttyDevice) { xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */); } else { attr = CRT_colors[PROCESS_SHADOW]; xSnprintf(buffer, n, "? "); } break; } case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return; case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return; case M_DRS: Process_humanNumber(str, lp->m_drs * pageSizeKB, coloring); return; case M_DT: Process_humanNumber(str, lp->m_dt * pageSizeKB, coloring); return; case M_LRS: if (lp->m_lrs) { Process_humanNumber(str, lp->m_lrs * pageSizeKB, coloring); return; } attr = CRT_colors[PROCESS_SHADOW]; xSnprintf(buffer, n, " N/A "); break; case M_TRS: Process_humanNumber(str, lp->m_trs * pageSizeKB, coloring); return; case M_SHARE: Process_humanNumber(str, lp->m_share * pageSizeKB, coloring); return; case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return; case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return; case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return; case UTIME: Process_printTime(str, lp->utime); return; case STIME: Process_printTime(str, lp->stime); return; case CUTIME: Process_printTime(str, lp->cutime); return; case CSTIME: Process_printTime(str, lp->cstime); return; case RCHAR: Process_colorNumber(str, lp->io_rchar, coloring); return; case WCHAR: Process_colorNumber(str, lp->io_wchar, coloring); return; case SYSCR: Process_colorNumber(str, lp->io_syscr, coloring); return; case SYSCW: Process_colorNumber(str, lp->io_syscw, coloring); return; case RBYTES: Process_colorNumber(str, lp->io_read_bytes, coloring); return; case WBYTES: Process_colorNumber(str, lp->io_write_bytes, coloring); return; case CNCLWB: Process_colorNumber(str, lp->io_cancelled_write_bytes, coloring); return; case IO_READ_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps, coloring); return; case IO_WRITE_RATE: Process_outputRate(str, buffer, n, lp->io_rate_write_bps, coloring); return; case IO_RATE: { double totalRate = NAN; if (!isnan(lp->io_rate_read_bps) && !isnan(lp->io_rate_write_bps)) totalRate = lp->io_rate_read_bps + lp->io_rate_write_bps; else if (!isnan(lp->io_rate_read_bps)) totalRate = lp->io_rate_read_bps; else if (!isnan(lp->io_rate_write_bps)) totalRate = lp->io_rate_write_bps; else totalRate = NAN; Process_outputRate(str, buffer, n, totalRate, coloring); return; } #ifdef HAVE_OPENVZ case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break; case VPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, lp->vpid); break; #endif #ifdef HAVE_VSERVER case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; #endif case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup ? lp->cgroup : ""); break; case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break; case IO_PRIORITY: { int klass = IOPriority_class(lp->ioPriority); if (klass == IOPRIO_CLASS_NONE) { // see note [1] above xSnprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5); } else if (klass == IOPRIO_CLASS_BE) { xSnprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority)); } else if (klass == IOPRIO_CLASS_RT) { attr = CRT_colors[PROCESS_HIGH_PRIORITY]; xSnprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority)); } else if (klass == IOPRIO_CLASS_IDLE) { attr = CRT_colors[PROCESS_LOW_PRIORITY]; xSnprintf(buffer, n, "id "); } else { xSnprintf(buffer, n, "?? "); } break; } #ifdef HAVE_DELAYACCT case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break; case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break; case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break; #endif case CTXT: if (lp->ctxt_diff > 1000) { attr |= A_BOLD; } xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff); break; case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break; case COMM: { if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) { Process_writeField(this, str, field); } else { LinuxProcess_writeCommandField(this, str, buffer, n, attr); } return; } case PROC_COMM: { const char* procComm; if (lp->procComm) { attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM]; procComm = lp->procComm; } else { attr = CRT_colors[PROCESS_SHADOW]; procComm = Process_isKernelThread(lp) ? kthreadID : "N/A"; } /* 15 being (TASK_COMM_LEN - 1) */ Process_printLeftAlignedField(str, attr, procComm, 15); return; } case PROC_EXE: { const char* procExe; if (lp->procExe) { attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME]; if (lp->procExeDeleted) attr = CRT_colors[FAILED_READ]; procExe = lp->procExe + lp->procExeBasenameOffset; } else { attr = CRT_colors[PROCESS_SHADOW]; procExe = Process_isKernelThread(lp) ? kthreadID : "N/A"; } Process_printLeftAlignedField(str, attr, procExe, 15); return; } case CWD: { const char* cwd; if (!lp->cwd) { attr = CRT_colors[PROCESS_SHADOW]; cwd = "N/A"; } else if (String_startsWith(lp->cwd, "/proc/") && strstr(lp->cwd, " (deleted)") != NULL) { attr = CRT_colors[PROCESS_SHADOW]; cwd = "main thread terminated"; } else { cwd = lp->cwd; } Process_printLeftAlignedField(str, attr, cwd, 25); return; } default: Process_writeField(this, str, field); return; } RichString_appendWide(str, attr, buffer); } static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { const LinuxProcess* p1 = (const LinuxProcess*)v1; const LinuxProcess* p2 = (const LinuxProcess*)v2; switch (key) { case M_DRS: return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs); case M_DT: return SPACESHIP_NUMBER(p2->m_dt, p1->m_dt); case M_LRS: return SPACESHIP_NUMBER(p2->m_lrs, p1->m_lrs); case M_TRS: return SPACESHIP_NUMBER(p2->m_trs, p1->m_trs); case M_SHARE: return SPACESHIP_NUMBER(p2->m_share, p1->m_share); case M_PSS: return SPACESHIP_NUMBER(p2->m_pss, p1->m_pss); case M_SWAP: return SPACESHIP_NUMBER(p2->m_swap, p1->m_swap); case M_PSSWP: return SPACESHIP_NUMBER(p2->m_psswp, p1->m_psswp); case UTIME: return SPACESHIP_NUMBER(p2->utime, p1->utime); case CUTIME: return SPACESHIP_NUMBER(p2->cutime, p1->cutime); case STIME: return SPACESHIP_NUMBER(p2->stime, p1->stime); case CSTIME: return SPACESHIP_NUMBER(p2->cstime, p1->cstime); case RCHAR: return SPACESHIP_NUMBER(p2->io_rchar, p1->io_rchar); case WCHAR: return SPACESHIP_NUMBER(p2->io_wchar, p1->io_wchar); case SYSCR: return SPACESHIP_NUMBER(p2->io_syscr, p1->io_syscr); case SYSCW: return SPACESHIP_NUMBER(p2->io_syscw, p1->io_syscw); case RBYTES: return SPACESHIP_NUMBER(p2->io_read_bytes, p1->io_read_bytes); case WBYTES: return SPACESHIP_NUMBER(p2->io_write_bytes, p1->io_write_bytes); case CNCLWB: return SPACESHIP_NUMBER(p2->io_cancelled_write_bytes, p1->io_cancelled_write_bytes); case IO_READ_RATE: return SPACESHIP_NUMBER(p2->io_rate_read_bps, p1->io_rate_read_bps); case IO_WRITE_RATE: return SPACESHIP_NUMBER(p2->io_rate_write_bps, p1->io_rate_write_bps); case IO_RATE: return SPACESHIP_NUMBER(p2->io_rate_read_bps + p2->io_rate_write_bps, p1->io_rate_read_bps + p1->io_rate_write_bps); #ifdef HAVE_OPENVZ case CTID: return SPACESHIP_NULLSTR(p1->ctid, p2->ctid); case VPID: return SPACESHIP_NUMBER(p2->vpid, p1->vpid); #endif #ifdef HAVE_VSERVER case VXID: return SPACESHIP_NUMBER(p2->vxid, p1->vxid); #endif case CGROUP: return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup); case OOM: return SPACESHIP_NUMBER(p2->oom, p1->oom); #ifdef HAVE_DELAYACCT case PERCENT_CPU_DELAY: return SPACESHIP_NUMBER(p2->cpu_delay_percent, p1->cpu_delay_percent); case PERCENT_IO_DELAY: return SPACESHIP_NUMBER(p2->blkio_delay_percent, p1->blkio_delay_percent); case PERCENT_SWAP_DELAY: return SPACESHIP_NUMBER(p2->swapin_delay_percent, p1->swapin_delay_percent); #endif case IO_PRIORITY: return SPACESHIP_NUMBER(LinuxProcess_effectiveIOPriority(p1), LinuxProcess_effectiveIOPriority(p2)); case CTXT: return SPACESHIP_NUMBER(p2->ctxt_diff, p1->ctxt_diff); case SECATTR: return SPACESHIP_NULLSTR(p1->secattr, p2->secattr); case PROC_COMM: { const char *comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : ""); const char *comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : ""); return strcmp(comm1, comm2); } case PROC_EXE: { const char *exe1 = p1->procExe ? (p1->procExe + p1->procExeBasenameOffset) : (Process_isKernelThread(p1) ? kthreadID : ""); const char *exe2 = p2->procExe ? (p2->procExe + p2->procExeBasenameOffset) : (Process_isKernelThread(p2) ? kthreadID : ""); return strcmp(exe1, exe2); } case CWD: return SPACESHIP_NULLSTR(p1->cwd, p2->cwd); default: return Process_compareByKey_Base(v1, v2, key); } } bool Process_isThread(const Process* this) { return (Process_isUserlandThread(this) || Process_isKernelThread(this)); } const ProcessClass LinuxProcess_class = { .super = { .extends = Class(Process), .display = Process_display, .delete = Process_delete, .compare = Process_compare }, .writeField = LinuxProcess_writeField, .getCommandStr = LinuxProcess_getCommandStr, .compareByKey = LinuxProcess_compareByKey }; htop-3.0.5/linux/LinuxProcess.h000066400000000000000000000111631377712513700164560ustar00rootroot00000000000000#ifndef HEADER_LinuxProcess #define HEADER_LinuxProcess /* htop - LinuxProcess.h (C) 2014 Hisham H. Muhammad (C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include #include "IOPriority.h" #include "Object.h" #include "Process.h" #include "Settings.h" #define PROCESS_FLAG_LINUX_IOPRIO 0x00000100 #define PROCESS_FLAG_LINUX_OPENVZ 0x00000200 #define PROCESS_FLAG_LINUX_VSERVER 0x00000400 #define PROCESS_FLAG_LINUX_CGROUP 0x00000800 #define PROCESS_FLAG_LINUX_OOM 0x00001000 #define PROCESS_FLAG_LINUX_SMAPS 0x00002000 #define PROCESS_FLAG_LINUX_CTXT 0x00004000 #define PROCESS_FLAG_LINUX_SECATTR 0x00008000 #define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000 #define PROCESS_FLAG_LINUX_CWD 0x00020000 #define PROCESS_FLAG_LINUX_DELAYACCT 0x00040000 /* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It * contains the merged Command string, and the information needed by * LinuxProcess_writeCommand to color the string. str will be NULL for kernel * threads and zombies */ typedef struct LinuxProcessMergedCommand_ { char *str; /* merged Command string */ int maxLen; /* maximum expected length of Command string */ int baseStart; /* basename's start offset */ int baseEnd; /* basename's end offset */ int commStart; /* comm's start offset */ int commEnd; /* comm's end offset */ int sep1; /* first field separator, used if non-zero */ int sep2; /* second field separator, used if non-zero */ bool separateComm; /* whether comm is a separate field */ bool unmatchedExe; /* whether exe matched with cmdline */ bool cmdlineChanged; /* whether cmdline changed */ bool exeChanged; /* whether exe changed */ bool commChanged; /* whether comm changed */ bool prevMergeSet; /* whether showMergedCommand was set */ bool prevPathSet; /* whether showProgramPath was set */ bool prevCommSet; /* whether findCommInCmdline was set */ bool prevCmdlineSet; /* whether findCommInCmdline was set */ } LinuxProcessMergedCommand; typedef struct LinuxProcess_ { Process super; char *procComm; char *procExe; int procExeLen; int procExeBasenameOffset; bool procExeDeleted; int procCmdlineBasenameOffset; int procCmdlineBasenameEnd; LinuxProcessMergedCommand mergedCommand; bool isKernelThread; IOPriority ioPriority; unsigned long int cminflt; unsigned long int cmajflt; unsigned long long int utime; unsigned long long int stime; unsigned long long int cutime; unsigned long long int cstime; long m_share; long m_pss; long m_swap; long m_psswp; long m_trs; long m_drs; long m_lrs; long m_dt; unsigned long long io_rchar; unsigned long long io_wchar; unsigned long long io_syscr; unsigned long long io_syscw; unsigned long long io_read_bytes; unsigned long long io_write_bytes; unsigned long long io_cancelled_write_bytes; unsigned long long io_rate_read_time; unsigned long long io_rate_write_time; double io_rate_read_bps; double io_rate_write_bps; #ifdef HAVE_OPENVZ char* ctid; pid_t vpid; #endif #ifdef HAVE_VSERVER unsigned int vxid; #endif char* cgroup; unsigned int oom; char* ttyDevice; #ifdef HAVE_DELAYACCT unsigned long long int delay_read_time; unsigned long long cpu_delay_total; unsigned long long blkio_delay_total; unsigned long long swapin_delay_total; float cpu_delay_percent; float blkio_delay_percent; float swapin_delay_percent; #endif unsigned long ctxt_total; unsigned long ctxt_diff; char* secattr; unsigned long long int last_mlrs_calctime; char* cwd; } LinuxProcess; #define Process_isKernelThread(_process) (((const LinuxProcess*)(_process))->isKernelThread) static inline bool Process_isUserlandThread(const Process* this) { return this->pid != this->tgid; } extern int pageSize; extern int pageSizeKB; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; extern const ProcessClass LinuxProcess_class; Process* LinuxProcess_new(const Settings* settings); void Process_delete(Object* cast); IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); bool LinuxProcess_setIOPriority(Process* this, Arg ioprio); /* This function constructs the string that is displayed by * LinuxProcess_writeCommand and also returned by LinuxProcess_getCommandStr */ void LinuxProcess_makeCommandStr(Process *this); bool Process_isThread(const Process* this); #endif htop-3.0.5/linux/LinuxProcessList.c000066400000000000000000001612721377712513700173140ustar00rootroot00000000000000/* htop - LinuxProcessList.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "LinuxProcessList.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_DELAYACCT #include #include #include #include #include #include #include #include #include #endif #include "Compat.h" #include "CRT.h" #include "LinuxProcess.h" #include "Macros.h" #include "Object.h" #include "Process.h" #include "Settings.h" #include "XUtils.h" #ifdef MAJOR_IN_MKDEV #include #elif defined(MAJOR_IN_SYSMACROS) #include #endif #ifdef HAVE_SENSORS_SENSORS_H #include "LibSensors.h" #endif // CentOS 6's kernel doesn't provide a definition of O_PATH // based on definition taken from uapi/asm-generic/fcnth.h in Linux kernel tree #ifndef O_PATH # define O_PATH 010000000 #endif static long long btime; static long jiffy; static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) { assert(String_eq(mode, "r")); /* only currently supported mode */ int fd = Compat_openat(openatArg, pathname, O_RDONLY); if (fd < 0) return NULL; FILE* stream = fdopen(fd, mode); if (!stream) close(fd); return stream; } static int sortTtyDrivers(const void* va, const void* vb) { const TtyDriver* a = (const TtyDriver*) va; const TtyDriver* b = (const TtyDriver*) vb; int r = SPACESHIP_NUMBER(a->major, b->major); if (r) return r; return SPACESHIP_NUMBER(a->minorFrom, b->minorFrom); } static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) { TtyDriver* ttyDrivers; char buf[16384]; ssize_t r = xReadfile(PROCTTYDRIVERSFILE, buf, sizeof(buf)); if (r < 0) return; int numDrivers = 0; int allocd = 10; ttyDrivers = xMallocArray(allocd, sizeof(TtyDriver)); char* at = buf; while (*at != '\0') { at = strchr(at, ' '); // skip first token while (*at == ' ') at++; // skip spaces char* token = at; // mark beginning of path at = strchr(at, ' '); // find end of path *at = '\0'; at++; // clear and skip ttyDrivers[numDrivers].path = xStrdup(token); // save while (*at == ' ') at++; // skip spaces token = at; // mark beginning of major at = strchr(at, ' '); // find end of major *at = '\0'; at++; // clear and skip ttyDrivers[numDrivers].major = atoi(token); // save while (*at == ' ') at++; // skip spaces token = at; // mark beginning of minorFrom while (*at >= '0' && *at <= '9') at++; //find end of minorFrom if (*at == '-') { // if has range *at = '\0'; at++; // clear and skip ttyDrivers[numDrivers].minorFrom = atoi(token); // save token = at; // mark beginning of minorTo at = strchr(at, ' '); // find end of minorTo *at = '\0'; at++; // clear and skip ttyDrivers[numDrivers].minorTo = atoi(token); // save } else { // no range *at = '\0'; at++; // clear and skip ttyDrivers[numDrivers].minorFrom = atoi(token); // save ttyDrivers[numDrivers].minorTo = atoi(token); // save } at = strchr(at, '\n'); // go to end of line at++; // skip numDrivers++; if (numDrivers == allocd) { allocd += 10; ttyDrivers = xReallocArray(ttyDrivers, allocd, sizeof(TtyDriver)); } } numDrivers++; ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * numDrivers); ttyDrivers[numDrivers - 1].path = NULL; qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers); this->ttyDrivers = ttyDrivers; } #ifdef HAVE_DELAYACCT static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { this->netlink_socket = nl_socket_alloc(); if (this->netlink_socket == NULL) { return; } if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { return; } this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); } #endif static int LinuxProcessList_computeCPUcount(void) { FILE* file = fopen(PROCSTATFILE, "r"); if (file == NULL) { CRT_fatalError("Cannot open " PROCSTATFILE); } int cpus = 0; char buffer[PROC_LINE_LENGTH + 1]; while (fgets(buffer, sizeof(buffer), file)) { if (String_startsWith(buffer, "cpu")) { cpus++; } } fclose(file); /* subtract raw cpu entry */ if (cpus > 0) { cpus--; } return cpus; } static void LinuxProcessList_updateCPUcount(LinuxProcessList* this) { ProcessList* pl = &(this->super); int cpus = LinuxProcessList_computeCPUcount(); if (cpus == 0 || cpus == pl->cpuCount) return; pl->cpuCount = cpus; free(this->cpus); this->cpus = xCalloc(cpus + 1, sizeof(CPUData)); for (int i = 0; i <= cpus; i++) { this->cpus[i].totalTime = 1; this->cpus[i].totalPeriod = 1; } } ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); ProcessList* pl = &(this->super); ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId); LinuxProcessList_initTtyDrivers(this); // Initialize page size pageSize = sysconf(_SC_PAGESIZE); if (pageSize == -1) CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); pageSizeKB = pageSize / ONE_K; // Initialize clock ticks jiffy = sysconf(_SC_CLK_TCK); if (jiffy == -1) CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)"); // Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+) this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0); // Read btime (the kernel boot time, as number of seconds since the epoch) { FILE* statfile = fopen(PROCSTATFILE, "r"); if (statfile == NULL) CRT_fatalError("Cannot open " PROCSTATFILE); while (true) { char buffer[PROC_LINE_LENGTH + 1]; if (fgets(buffer, sizeof(buffer), statfile) == NULL) break; if (String_startsWith(buffer, "btime ") == false) continue; if (sscanf(buffer, "btime %lld\n", &btime) == 1) break; CRT_fatalError("Failed to parse btime from " PROCSTATFILE); } fclose(statfile); if (!btime) CRT_fatalError("No btime in " PROCSTATFILE); } // Initialize CPU count { int cpus = LinuxProcessList_computeCPUcount(); pl->cpuCount = MAXIMUM(cpus, 1); this->cpus = xCalloc(cpus + 1, sizeof(CPUData)); for (int i = 0; i <= cpus; i++) { this->cpus[i].totalTime = 1; this->cpus[i].totalPeriod = 1; } } return pl; } void ProcessList_delete(ProcessList* pl) { LinuxProcessList* this = (LinuxProcessList*) pl; ProcessList_done(pl); free(this->cpus); if (this->ttyDrivers) { for (int i = 0; this->ttyDrivers[i].path; i++) { free(this->ttyDrivers[i].path); } free(this->ttyDrivers); } #ifdef HAVE_DELAYACCT if (this->netlink_socket) { nl_close(this->netlink_socket); nl_socket_free(this->netlink_socket); } #endif free(this); } static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) { return t * 100 / jiffy; } static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, int* commLen) { LinuxProcess* lp = (LinuxProcess*) process; const int commLenIn = *commLen; *commLen = 0; char buf[MAX_READ + 1]; ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf)); if (r < 0) return false; assert(process->pid == atoi(buf)); char* location = strchr(buf, ' '); if (!location) return false; location += 2; char* end = strrchr(location, ')'); if (!end) return false; int commsize = MINIMUM(end - location, commLenIn - 1); // deepcode ignore BufferOverflow: commsize is bounded by the allocated length passed in by commLen, saved into commLenIn memcpy(command, location, commsize); command[commsize] = '\0'; *commLen = commsize; location = end + 2; process->state = location[0]; location += 2; process->ppid = strtol(location, &location, 10); location += 1; process->pgrp = strtoul(location, &location, 10); location += 1; process->session = strtoul(location, &location, 10); location += 1; process->tty_nr = strtoul(location, &location, 10); location += 1; process->tpgid = strtol(location, &location, 10); location += 1; process->flags = strtoul(location, &location, 10); location += 1; process->minflt = strtoull(location, &location, 10); location += 1; lp->cminflt = strtoull(location, &location, 10); location += 1; process->majflt = strtoull(location, &location, 10); location += 1; lp->cmajflt = strtoull(location, &location, 10); location += 1; lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); location += 1; lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); location += 1; lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); location += 1; lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); location += 1; process->priority = strtol(location, &location, 10); location += 1; process->nice = strtol(location, &location, 10); location += 1; process->nlwp = strtol(location, &location, 10); location += 1; location = strchr(location, ' ') + 1; if (process->starttime_ctime == 0) { process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100; } else { location = strchr(location, ' ') + 1; } location += 1; for (int i = 0; i < 15; i++) { location = strchr(location, ' ') + 1; } process->exit_signal = strtol(location, &location, 10); location += 1; assert(location != NULL); process->processor = strtol(location, &location, 10); process->time = lp->utime + lp->stime; return true; } static bool LinuxProcessList_statProcessDir(Process* process, openat_arg_t procFd) { struct stat sstat; #ifdef HAVE_OPENAT int statok = fstat(procFd, &sstat); #else int statok = stat(procFd, &sstat); #endif if (statok == -1) return false; process->st_uid = sstat.st_uid; return true; } static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long now) { char buffer[1024]; ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer)); if (r < 0) { process->io_rate_read_bps = NAN; process->io_rate_write_bps = NAN; process->io_rchar = -1LL; process->io_wchar = -1LL; process->io_syscr = -1LL; process->io_syscw = -1LL; process->io_read_bytes = -1LL; process->io_write_bytes = -1LL; process->io_cancelled_write_bytes = -1LL; process->io_rate_read_time = -1LL; process->io_rate_write_time = -1LL; return; } unsigned long long last_read = process->io_read_bytes; unsigned long long last_write = process->io_write_bytes; char* buf = buffer; char* line = NULL; while ((line = strsep(&buf, "\n")) != NULL) { switch (line[0]) { case 'r': if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) { process->io_rchar = strtoull(line + 7, NULL, 10); } else if (String_startsWith(line + 1, "ead_bytes: ")) { process->io_read_bytes = strtoull(line + 12, NULL, 10); process->io_rate_read_bps = ((double)(process->io_read_bytes - last_read)) / (((double)(now - process->io_rate_read_time)) / 1000); process->io_rate_read_time = now; } break; case 'w': if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) { process->io_wchar = strtoull(line + 7, NULL, 10); } else if (String_startsWith(line + 1, "rite_bytes: ")) { process->io_write_bytes = strtoull(line + 13, NULL, 10); process->io_rate_write_bps = ((double)(process->io_write_bytes - last_write)) / (((double)(now - process->io_rate_write_time)) / 1000); process->io_rate_write_time = now; } break; case 's': if (line[4] == 'r' && String_startsWith(line + 1, "yscr: ")) { process->io_syscr = strtoull(line + 7, NULL, 10); } else if (String_startsWith(line + 1, "yscw: ")) { process->io_syscw = strtoull(line + 7, NULL, 10); } break; case 'c': if (String_startsWith(line + 1, "ancelled_write_bytes: ")) { process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10); } } } } typedef struct LibraryData_ { uint64_t size; bool exec; } LibraryData; static inline uint64_t fast_strtoull_dec(char **str, int maxlen) { register uint64_t result = 0; if (!maxlen) --maxlen; while (maxlen-- && **str >= '0' && **str <= '9') { result *= 10; result += **str - '0'; (*str)++; } return result; } static inline uint64_t fast_strtoull_hex(char **str, int maxlen) { register uint64_t result = 0; register int nibble, letter; const long valid_mask = 0x03FF007E; if (!maxlen) --maxlen; while (maxlen--) { nibble = (unsigned char)**str; if (!(valid_mask & (1 << (nibble & 0x1F)))) break; if ((nibble < '0') || (nibble & ~0x20) > 'F') break; letter = (nibble & 0x40) ? 'A' - '9' - 1 : 0; nibble &=~0x20; // to upper nibble ^= 0x10; // switch letters and digits nibble -= letter; nibble &= 0x0f; result <<= 4; result += (uint64_t)nibble; (*str)++; } return result; } static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* value, void* data) { if (!data) return; if (!value) return; LibraryData* v = (LibraryData *)value; uint64_t* d = (uint64_t *)data; if (!v->exec) return; *d += v->size; } static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) { FILE* mapsfile = fopenat(procFd, "maps", "r"); if (!mapsfile) return 0; Hashtable* ht = Hashtable_new(64, true); char buffer[1024]; while (fgets(buffer, sizeof(buffer), mapsfile)) { uint64_t map_start; uint64_t map_end; char map_perm[5]; unsigned int map_devmaj; unsigned int map_devmin; uint64_t map_inode; // Short circuit test: Look for a slash if (!strchr(buffer, '/')) continue; // Parse format: "%Lx-%Lx %4s %x %2x:%2x %Ld" char *readptr = buffer; map_start = fast_strtoull_hex(&readptr, 16); if ('-' != *readptr++) continue; map_end = fast_strtoull_hex(&readptr, 16); if (' ' != *readptr++) continue; memcpy(map_perm, readptr, 4); map_perm[4] = '\0'; readptr += 4; if (' ' != *readptr++) continue; while(*readptr > ' ') readptr++; // Skip parsing this hex value if (' ' != *readptr++) continue; map_devmaj = fast_strtoull_hex(&readptr, 4); if (':' != *readptr++) continue; map_devmin = fast_strtoull_hex(&readptr, 4); if (' ' != *readptr++) continue; //Minor shortcut: Once we know there's no file for this region, we skip if (!map_devmaj && !map_devmin) continue; map_inode = fast_strtoull_dec(&readptr, 20); if (!map_inode) continue; LibraryData* libdata = Hashtable_get(ht, map_inode); if (!libdata) { libdata = xCalloc(1, sizeof(LibraryData)); Hashtable_put(ht, map_inode, libdata); } libdata->size += map_end - map_start; libdata->exec |= 'x' == map_perm[2]; } fclose(mapsfile); uint64_t total_size = 0; Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size); Hashtable_delete(ht); return total_size / pageSize; } static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) { FILE* statmfile = fopenat(procFd, "statm", "r"); if (!statmfile) return false; long tmp_m_lrs = 0; int r = fscanf(statmfile, "%ld %ld %ld %ld %ld %ld %ld", &process->super.m_virt, &process->super.m_resident, &process->m_share, &process->m_trs, &tmp_m_lrs, &process->m_drs, &process->m_dt); fclose(statmfile); if (r == 7) { process->super.m_virt *= pageSizeKB; process->super.m_resident *= pageSizeKB; if (tmp_m_lrs) { process->m_lrs = tmp_m_lrs; } else if (performLookup) { // Check if we really should recalculate the M_LRS value for this process uint64_t passedTimeInMs = now - process->last_mlrs_calctime; uint64_t recheck = ((uint64_t)rand()) % 2048; if(passedTimeInMs > 2000 || passedTimeInMs > recheck) { process->last_mlrs_calctime = now; process->m_lrs = LinuxProcessList_calcLibSize(procFd); } } else { // Keep previous value } } return r == 7; } static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, bool haveSmapsRollup) { //http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719 //kernel will return data in chunks of size PAGE_SIZE or less. FILE* f = fopenat(procFd, haveSmapsRollup ? "smaps_rollup" : "smaps", "r"); if (!f) return false; process->m_pss = 0; process->m_swap = 0; process->m_psswp = 0; char buffer[256]; while (fgets(buffer, sizeof(buffer), f)) { if (!strchr(buffer, '\n')) { // Partial line, skip to end of this line while (fgets(buffer, sizeof(buffer), f)) { if (strchr(buffer, '\n')) { break; } } continue; } if (String_startsWith(buffer, "Pss:")) { process->m_pss += strtol(buffer + 4, NULL, 10); } else if (String_startsWith(buffer, "Swap:")) { process->m_swap += strtol(buffer + 5, NULL, 10); } else if (String_startsWith(buffer, "SwapPss:")) { process->m_psswp += strtol(buffer + 8, NULL, 10); } } fclose(f); return true; } #ifdef HAVE_OPENVZ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t procFd) { if ( (access(PROCDIR "/vz", R_OK) != 0)) { free(process->ctid); process->ctid = NULL; process->vpid = process->super.pid; return; } FILE* file = fopenat(procFd, "status", "r"); if (!file) { free(process->ctid); process->ctid = NULL; process->vpid = process->super.pid; return; } bool foundEnvID = false; bool foundVPid = false; char linebuf[256]; while (fgets(linebuf, sizeof(linebuf), file) != NULL) { if (strchr(linebuf, '\n') == NULL) { // Partial line, skip to end of this line while (fgets(linebuf, sizeof(linebuf), file) != NULL) { if (strchr(linebuf, '\n') != NULL) { break; } } continue; } char* name_value_sep = strchr(linebuf, ':'); if (name_value_sep == NULL) { continue; } int field; if (0 == strncasecmp(linebuf, "envID", name_value_sep - linebuf)) { field = 1; } else if (0 == strncasecmp(linebuf, "VPid", name_value_sep - linebuf)) { field = 2; } else { continue; } do { name_value_sep++; } while (*name_value_sep != '\0' && *name_value_sep <= 32); char* value_end = name_value_sep; while(*value_end > 32) { value_end++; } if (name_value_sep == value_end) { continue; } *value_end = '\0'; switch(field) { case 1: foundEnvID = true; if (!String_eq(name_value_sep, process->ctid ? process->ctid : "")) { free(process->ctid); process->ctid = xStrdup(name_value_sep); } break; case 2: foundVPid = true; process->vpid = strtoul(name_value_sep, NULL, 0); break; default: //Sanity Check: Should never reach here, or the implementation is missing something! assert(false && "OpenVZ handling: Unimplemented case for field handling reached."); } } fclose(file); if (!foundEnvID) { free(process->ctid); process->ctid = NULL; } if (!foundVPid) { process->vpid = process->super.pid; } } #endif static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "cgroup", "r"); if (!file) { if (process->cgroup) { free(process->cgroup); process->cgroup = NULL; } return; } char output[PROC_LINE_LENGTH + 1]; output[0] = '\0'; char* at = output; int left = PROC_LINE_LENGTH; while (!feof(file) && left > 0) { char buffer[PROC_LINE_LENGTH + 1]; char* ok = fgets(buffer, PROC_LINE_LENGTH, file); if (!ok) break; char* group = strchr(buffer, ':'); if (!group) break; if (at != output) { *at = ';'; at++; left--; } int wrote = snprintf(at, left, "%s", group); left -= wrote; } fclose(file); free(process->cgroup); process->cgroup = xStrdup(output); } #ifdef HAVE_VSERVER static void LinuxProcessList_readVServerData(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "status", "r"); if (!file) return; char buffer[PROC_LINE_LENGTH + 1]; process->vxid = 0; while (fgets(buffer, PROC_LINE_LENGTH, file)) { if (String_startsWith(buffer, "VxID:")) { int vxid; int ok = sscanf(buffer, "VxID:\t%32d", &vxid); if (ok >= 1) { process->vxid = vxid; } } #if defined HAVE_ANCIENT_VSERVER else if (String_startsWith(buffer, "s_context:")) { int vxid; int ok = sscanf(buffer, "s_context:\t%32d", &vxid); if (ok >= 1) { process->vxid = vxid; } } #endif } fclose(file); } #endif static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "oom_score", "r"); if (!file) return; char buffer[PROC_LINE_LENGTH + 1]; if (fgets(buffer, PROC_LINE_LENGTH, file)) { unsigned int oom; int ok = sscanf(buffer, "%u", &oom); if (ok >= 1) { process->oom = oom; } } fclose(file); } static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "status", "r"); if (!file) return; char buffer[PROC_LINE_LENGTH + 1]; unsigned long ctxt = 0; while (fgets(buffer, PROC_LINE_LENGTH, file)) { if (String_startsWith(buffer, "voluntary_ctxt_switches:")) { unsigned long vctxt; int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt); if (ok >= 1) { ctxt += vctxt; } } else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) { unsigned long nvctxt; int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt); if (ok >= 1) { ctxt += nvctxt; } } } fclose(file); process->ctxt_diff = (ctxt > process->ctxt_total) ? (ctxt - process->ctxt_total) : 0; process->ctxt_total = ctxt; } static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "attr/current", "r"); if (!file) { free(process->secattr); process->secattr = NULL; return; } char buffer[PROC_LINE_LENGTH + 1]; char* res = fgets(buffer, sizeof(buffer), file); fclose(file); if (!res) { free(process->secattr); process->secattr = NULL; return; } char* newline = strchr(buffer, '\n'); if (newline) { *newline = '\0'; } if (process->secattr && String_eq(process->secattr, buffer)) { return; } free(process->secattr); process->secattr = xStrdup(buffer); } static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) { char pathBuffer[PATH_MAX + 1] = {0}; #if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT) ssize_t r = readlinkat(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1); #else char filename[MAX_NAME + 1]; xSnprintf(filename, sizeof(filename), "%s/cwd", procFd); ssize_t r = readlink(filename, pathBuffer, sizeof(pathBuffer) - 1); #endif if (r < 0) { free(process->cwd); process->cwd = NULL; return; } pathBuffer[r] = '\0'; if (process->cwd && String_eq(process->cwd, pathBuffer)) return; free(process->cwd); process->cwd = xStrdup(pathBuffer); } #ifdef HAVE_DELAYACCT static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { struct nlmsghdr* nlhdr; struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1]; struct nlattr* nlattr; struct taskstats stats; int rem; LinuxProcess* lp = (LinuxProcess*) linuxProcess; nlhdr = nlmsg_hdr(nlmsg); if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { return NL_SKIP; } if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats)); assert(lp->super.pid == (pid_t)stats.ac_pid); unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time; #define BOUNDS(x) (isnan(x) ? 0.0 : ((x) > 100) ? 100.0 : (x)) #define DELTAPERC(x,y) BOUNDS((float) ((x) - (y)) / timeDelta * 100) lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total); lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total); lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total); #undef DELTAPERC #undef BOUNDS lp->swapin_delay_total = stats.swapin_delay_total; lp->blkio_delay_total = stats.blkio_delay_total; lp->cpu_delay_total = stats.cpu_delay_total; lp->delay_read_time = stats.ac_etime * 1000; } return NL_OK; } static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) { struct nl_msg* msg; if (!this->netlink_socket) { LinuxProcessList_initNetlinkSocket(this); if (!this->netlink_socket) { goto delayacct_failure; } } if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) { goto delayacct_failure; } if (! (msg = nlmsg_alloc())) { goto delayacct_failure; } if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { nlmsg_free(msg); } if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) { nlmsg_free(msg); } if (nl_send_sync(this->netlink_socket, msg) < 0) { goto delayacct_failure; } if (nl_recvmsgs_default(this->netlink_socket) < 0) { goto delayacct_failure; } return; delayacct_failure: process->swapin_delay_percent = NAN; process->blkio_delay_percent = NAN; process->cpu_delay_percent = NAN; } #endif static void setCommand(Process* process, const char* command, int len) { if (process->comm && process->commLen >= len) { strncpy(process->comm, command, len + 1); } else { free(process->comm); process->comm = xStrdup(command); } process->commLen = len; } static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) { char command[4096 + 1]; // max cmdline length on Linux ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command)); if (amtRead < 0) return false; if (amtRead == 0) { if (process->state == 'Z') { process->basenameOffset = 0; } else { ((LinuxProcess*)process)->isKernelThread = true; } return true; } int tokenEnd = 0; int tokenStart = 0; int lastChar = 0; bool argSepNUL = false; bool argSepSpace = false; for (int i = 0; i < amtRead; i++) { /* newline used as delimiter - when forming the mergedCommand, newline is * converted to space by LinuxProcess_makeCommandStr */ if (command[i] == '\0') { command[i] = '\n'; } else { /* Record some information for the argument parsing heuristic below. */ if (tokenEnd) argSepNUL = true; if (command[i] <= ' ') argSepSpace = true; } if (command[i] == '\n') { if (tokenEnd == 0) { tokenEnd = i; } } else { /* htop considers the next character after the last / that is before * basenameOffset, as the start of the basename in cmdline - see * Process_writeCommand */ if (!tokenEnd && command[i] == '/') { tokenStart = i + 1; } lastChar = i; } } command[lastChar + 1] = '\0'; if (!argSepNUL && argSepSpace) { /* Argument parsing heuristic. * * This heuristic is used for processes that rewrite their command line. * Normally the command line is split by using NUL bytes between each argument. * But some programs like chrome flatten this using spaces. * * This heuristic tries its best to undo this loss of information. * To achieve this, we treat every character <= 32 as argument separators * (i.e. all of ASCII control sequences and space). * We then search for the basename of the cmdline in the first argument we found that way. * As path names may contain we try to cross-validate if the path we got that way exists. */ tokenStart = tokenEnd = 0; // From initial scan we know there's at least one space. // Check if that's part of a filename for an existing file. if (Compat_faccessat(AT_FDCWD, command, F_OK, AT_SYMLINK_NOFOLLOW) != 0) { // If we reach here the path does not exist. // Thus begin searching for the part of it that actually is. int tokenArg0Start = 0; for (int i = 0; i <= lastChar; i++) { /* Any ASCII control or space used as delimiter */ char tmpCommandChar = command[i]; if (command[i] <= ' ') { if (!tokenEnd) { command[i] = '\0'; bool found = Compat_faccessat(AT_FDCWD, command, F_OK, AT_SYMLINK_NOFOLLOW) == 0; // Restore if this wasn't it command[i] = found ? '\n' : tmpCommandChar; if (found) tokenEnd = i; if (!tokenArg0Start) tokenArg0Start = tokenStart; } else { // Split on every further separator, regardless of path correctness command[i] = '\n'; } } else if (!tokenEnd) { if (command[i] == '/' || (command[i] == '\\' && (!tokenStart || command[tokenStart - 1] == '\\'))) { tokenStart = i + 1; } else if (command[i] == ':' && (command[i + 1] != '/' && command[i + 1] != '\\')) { tokenEnd = i; } } } if (!tokenEnd) { tokenStart = tokenArg0Start; // No token delimiter found, forcibly split for (int i = 0; i <= lastChar; i++) { if (command[i] <= ' ') { command[i] = '\n'; if (!tokenEnd) { tokenEnd = i; } } } } } } if (tokenEnd == 0) { tokenEnd = lastChar + 1; } LinuxProcess *lp = (LinuxProcess *)process; lp->mergedCommand.maxLen = lastChar + 1; /* accommodate cmdline */ if (!process->comm || !String_eq(command, process->comm)) { process->basenameOffset = tokenEnd; setCommand(process, command, lastChar + 1); lp->procCmdlineBasenameOffset = tokenStart; lp->procCmdlineBasenameEnd = tokenEnd; lp->mergedCommand.cmdlineChanged = true; } /* /proc/[pid]/comm could change, so should be updated */ if ((amtRead = xReadfileat(procFd, "comm", command, sizeof(command))) > 0) { command[amtRead - 1] = '\0'; lp->mergedCommand.maxLen += amtRead - 1; /* accommodate comm */ if (!lp->procComm || !String_eq(command, lp->procComm)) { free(lp->procComm); lp->procComm = xStrdup(command); lp->mergedCommand.commChanged = true; } } else if (lp->procComm) { free(lp->procComm); lp->procComm = NULL; lp->mergedCommand.commChanged = true; } char filename[MAX_NAME + 1]; /* execve could change /proc/[pid]/exe, so procExe should be updated */ #if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT) amtRead = readlinkat(procFd, "exe", filename, sizeof(filename) - 1); #else char path[4096]; xSnprintf(path, sizeof(path), "%s/exe", procFd); amtRead = readlink(path, filename, sizeof(filename) - 1); #endif if (amtRead > 0) { filename[amtRead] = 0; lp->mergedCommand.maxLen += amtRead; /* accommodate exe */ if (!lp->procExe || !String_eq(filename, lp->procExe)) { free(lp->procExe); lp->procExe = xStrdup(filename); lp->procExeLen = amtRead; /* exe is guaranteed to contain at least one /, but validate anyway */ while (amtRead && filename[--amtRead] != '/') ; lp->procExeBasenameOffset = amtRead + 1; lp->mergedCommand.exeChanged = true; const char* deletedMarker = " (deleted)"; if (strlen(lp->procExe) > strlen(deletedMarker)) { lp->procExeDeleted = String_eq(lp->procExe + strlen(lp->procExe) - strlen(deletedMarker), deletedMarker); if (lp->procExeDeleted && strlen(lp->procExe) - strlen(deletedMarker) == 1 && lp->procExe[0] == '/') { lp->procExeBasenameOffset = 0; } } } } else if (lp->procExe) { free(lp->procExe); lp->procExe = NULL; lp->procExeLen = 0; lp->procExeBasenameOffset = 0; lp->procExeDeleted = false; lp->mergedCommand.exeChanged = true; } return true; } static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned int tty_nr) { unsigned int maj = major(tty_nr); unsigned int min = minor(tty_nr); int i = -1; for (;;) { i++; if ((!ttyDrivers[i].path) || maj < ttyDrivers[i].major) { break; } if (maj > ttyDrivers[i].major) { continue; } if (min < ttyDrivers[i].minorFrom) { break; } if (min > ttyDrivers[i].minorTo) { continue; } unsigned int idx = min - ttyDrivers[i].minorFrom; struct stat sstat; char* fullPath; for (;;) { xAsprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx); int err = stat(fullPath, &sstat); if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) { return fullPath; } free(fullPath); xAsprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx); err = stat(fullPath, &sstat); if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) { return fullPath; } free(fullPath); if (idx == min) { break; } idx = min; } int err = stat(ttyDrivers[i].path, &sstat); if (err == 0 && tty_nr == sstat.st_rdev) { return xStrdup(ttyDrivers[i].path); } } char* out; xAsprintf(&out, "/dev/%u:%u", maj, min); return out; } static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period, unsigned long long now) { ProcessList* pl = (ProcessList*) this; const struct dirent* entry; const Settings* settings = pl->settings; #ifdef HAVE_OPENAT int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if (dirFd < 0) return false; DIR* dir = fdopendir(dirFd); #else char dirFd[4096]; xSnprintf(dirFd, sizeof(dirFd), "%s/%s", parentFd, dirname); DIR* dir = opendir(dirFd); #endif if (!dir) { Compat_openatArgClose(dirFd); return false; } int cpus = pl->cpuCount; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; while ((entry = readdir(dir)) != NULL) { const char* name = entry->d_name; // Ignore all non-directories if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) { continue; } // The RedHat kernel hides threads with a dot. // I believe this is non-standard. if (name[0] == '.') { name++; } // Just skip all non-number directories. if (name[0] < '0' || name[0] > '9') { continue; } // filename is a number: process directory int pid = atoi(name); if (pid <= 0) continue; if (parent && pid == parent->pid) continue; bool preExisting; Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new); LinuxProcess* lp = (LinuxProcess*) proc; proc->tgid = parent ? parent->pid : pid; #ifdef HAVE_OPENAT int procFd = openat(dirFd, entry->d_name, O_PATH | O_DIRECTORY | O_NOFOLLOW); if (procFd < 0) goto errorReadingProcess; #else char procFd[4096]; xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name); #endif LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period, now); /* * These conditions will not trigger on first occurrence, cause we need to * add the process to the ProcessList and do all one time scans * (e.g. parsing the cmdline to detect a kernel thread) * But it will short-circuit subsequent scans. */ if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) { proc->updated = true; proc->show = false; pl->kernelThreads++; pl->totalTasks++; Compat_openatArgClose(procFd); continue; } if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) { proc->updated = true; proc->show = false; pl->userlandThreads++; pl->totalTasks++; Compat_openatArgClose(procFd); continue; } if (settings->flags & PROCESS_FLAG_IO) LinuxProcessList_readIoFile(lp, procFd, now); if (!LinuxProcessList_readStatmFile(lp, procFd, !!(settings->flags & PROCESS_FLAG_LINUX_LRS_FIX), now)) goto errorReadingProcess; if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) { if (!parent) { // Read smaps file of each process only every second pass to improve performance static int smaps_flag = 0; if ((pid & 1) == smaps_flag) { LinuxProcessList_readSmapsFile(lp, procFd, this->haveSmapsRollup); } if (pid == 1) { smaps_flag = !smaps_flag; } } else { lp->m_pss = ((const LinuxProcess*)parent)->m_pss; } } char command[MAX_NAME + 1]; unsigned long long int lasttimes = (lp->utime + lp->stime); int commLen = sizeof(command); unsigned int tty_nr = proc->tty_nr; if (! LinuxProcessList_readStatFile(proc, procFd, command, &commLen)) goto errorReadingProcess; if (tty_nr != proc->tty_nr && this->ttyDrivers) { free(lp->ttyDevice); lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr); } if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO) { LinuxProcess_updateIOPriority(lp); } /* period might be 0 after system sleep */ float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0); proc->percent_cpu = CLAMP(percent_cpu, 0.0F, cpus * 100.0F); proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0; if (!preExisting) { if (! LinuxProcessList_statProcessDir(proc, procFd)) goto errorReadingProcess; proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid); #ifdef HAVE_OPENVZ if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) { LinuxProcessList_readOpenVZData(lp, procFd); } #endif #ifdef HAVE_VSERVER if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) { LinuxProcessList_readVServerData(lp, procFd); } #endif if (! LinuxProcessList_readCmdlineFile(proc, procFd)) { goto errorReadingProcess; } Process_fillStarttimeBuffer(proc); ProcessList_add(pl, proc); } else { if (settings->updateProcessNames && proc->state != 'Z') { if (! LinuxProcessList_readCmdlineFile(proc, procFd)) { goto errorReadingProcess; } } } /* (Re)Generate the Command string, but only if the process is: * - not a kernel thread, and * - not a zombie or it became zombie under htop's watch, and * - not a user thread or if showThreadNames is not set */ if (!Process_isKernelThread(proc) && (proc->state != 'Z' || lp->mergedCommand.str) && (!Process_isUserlandThread(proc) || !settings->showThreadNames)) { LinuxProcess_makeCommandStr(proc); } #ifdef HAVE_DELAYACCT if (settings->flags & PROCESS_FLAG_LINUX_DELAYACCT) { LinuxProcessList_readDelayAcctData(this, lp); } #endif if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) { LinuxProcessList_readCGroupFile(lp, procFd); } if (settings->flags & PROCESS_FLAG_LINUX_OOM) { LinuxProcessList_readOomData(lp, procFd); } if (settings->flags & PROCESS_FLAG_LINUX_CTXT) { LinuxProcessList_readCtxtData(lp, procFd); } if (settings->flags & PROCESS_FLAG_LINUX_SECATTR) { LinuxProcessList_readSecattrData(lp, procFd); } if (settings->flags & PROCESS_FLAG_LINUX_CWD) { LinuxProcessList_readCwd(lp, procFd); } if (proc->state == 'Z' && (proc->basenameOffset == 0)) { proc->basenameOffset = -1; setCommand(proc, command, commLen); } else if (Process_isThread(proc)) { if (settings->showThreadNames || Process_isKernelThread(proc) || (proc->state == 'Z' && proc->basenameOffset == 0)) { proc->basenameOffset = -1; setCommand(proc, command, commLen); } else if (settings->showThreadNames) { if (! LinuxProcessList_readCmdlineFile(proc, procFd)) { goto errorReadingProcess; } } if (Process_isKernelThread(proc)) { pl->kernelThreads++; } else { pl->userlandThreads++; } } /* Set at the end when we know if a new entry is a thread */ proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); pl->totalTasks++; if (proc->state == 'R') pl->runningTasks++; proc->updated = true; Compat_openatArgClose(procFd); continue; // Exception handler. errorReadingProcess: { #ifdef HAVE_OPENAT if (procFd >= 0) close(procFd); #endif if (preExisting) { ProcessList_remove(pl, proc); } else { Process_delete((Object*)proc); } } } closedir(dir); return true; } static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { unsigned long long int freeMem = 0; unsigned long long int swapFree = 0; unsigned long long int shmem = 0; unsigned long long int sreclaimable = 0; FILE* file = fopen(PROCMEMINFOFILE, "r"); if (file == NULL) { CRT_fatalError("Cannot open " PROCMEMINFOFILE); } char buffer[128]; while (fgets(buffer, 128, file)) { #define tryRead(label, variable) \ if (String_startsWith(buffer, label)) { \ sscanf(buffer + strlen(label), " %32llu kB", variable); \ break; \ } switch (buffer[0]) { case 'M': tryRead("MemTotal:", &this->totalMem); tryRead("MemFree:", &freeMem); break; case 'B': tryRead("Buffers:", &this->buffersMem); break; case 'C': tryRead("Cached:", &this->cachedMem); break; case 'S': switch (buffer[1]) { case 'w': tryRead("SwapTotal:", &this->totalSwap); tryRead("SwapFree:", &swapFree); break; case 'h': tryRead("Shmem:", &shmem); break; case 'R': tryRead("SReclaimable:", &sreclaimable); break; } break; } #undef tryRead } this->usedMem = this->totalMem - freeMem; this->cachedMem = this->cachedMem + sreclaimable - shmem; this->usedSwap = this->totalSwap - swapFree; fclose(file); } static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) { unsigned long long int totalZram = 0; unsigned long long int usedZramComp = 0; unsigned long long int usedZramOrig = 0; char mm_stat[34]; char disksize[34]; unsigned int i = 0; for (;;) { xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i); xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i); i++; FILE* disksize_file = fopen(disksize, "r"); FILE* mm_stat_file = fopen(mm_stat, "r"); if (disksize_file == NULL || mm_stat_file == NULL) { if (disksize_file) { fclose(disksize_file); } if (mm_stat_file) { fclose(mm_stat_file); } break; } unsigned long long int size = 0; unsigned long long int orig_data_size = 0; unsigned long long int compr_data_size = 0; if (!fscanf(disksize_file, "%llu\n", &size) || !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) { fclose(disksize_file); fclose(mm_stat_file); break; } totalZram += size; usedZramComp += compr_data_size; usedZramOrig += orig_data_size; fclose(disksize_file); fclose(mm_stat_file); } this->zram.totalZram = totalZram / 1024; this->zram.usedZramComp = usedZramComp / 1024; this->zram.usedZramOrig = usedZramOrig / 1024; } static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { unsigned long long int dbufSize = 0; unsigned long long int dnodeSize = 0; unsigned long long int bonusSize = 0; FILE* file = fopen(PROCARCSTATSFILE, "r"); if (file == NULL) { lpl->zfs.enabled = 0; return; } char buffer[128]; while (fgets(buffer, 128, file)) { #define tryRead(label, variable) \ if (String_startsWith(buffer, label)) { \ sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ break; \ } #define tryReadFlag(label, variable, flag) \ if (String_startsWith(buffer, label)) { \ (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ break; \ } switch (buffer[0]) { case 'c': tryRead("c_max", &lpl->zfs.max); tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed); break; case 'u': tryRead("uncompressed_size", &lpl->zfs.uncompressed); break; case 's': tryRead("size", &lpl->zfs.size); break; case 'h': tryRead("hdr_size", &lpl->zfs.header); break; case 'd': tryRead("dbuf_size", &dbufSize); tryRead("dnode_size", &dnodeSize); break; case 'b': tryRead("bonus_size", &bonusSize); break; case 'a': tryRead("anon_size", &lpl->zfs.anon); break; case 'm': tryRead("mfu_size", &lpl->zfs.MFU); tryRead("mru_size", &lpl->zfs.MRU); break; } #undef tryRead #undef tryReadFlag } fclose(file); lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0); lpl->zfs.size /= 1024; lpl->zfs.max /= 1024; lpl->zfs.MFU /= 1024; lpl->zfs.MRU /= 1024; lpl->zfs.anon /= 1024; lpl->zfs.header /= 1024; lpl->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024; if ( lpl->zfs.isCompressed ) { lpl->zfs.compressed /= 1024; lpl->zfs.uncompressed /= 1024; } } static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) { FILE* file = fopen(PROCSTATFILE, "r"); if (file == NULL) { CRT_fatalError("Cannot open " PROCSTATFILE); } int cpus = this->super.cpuCount; assert(cpus > 0); for (int i = 0; i <= cpus; i++) { char buffer[PROC_LINE_LENGTH + 1]; unsigned long long int usertime, nicetime, systemtime, idletime; unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice; ioWait = irq = softIrq = steal = guest = guestnice = 0; // Depending on your kernel version, // 5, 7, 8 or 9 of these fields will be set. // The rest will remain at zero. char* ok = fgets(buffer, PROC_LINE_LENGTH, file); if (!ok) { buffer[0] = '\0'; } if (i == 0) { (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); } else { int cpuid; (void) sscanf(buffer, "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); assert(cpuid == i - 1); } // Guest time is already accounted in usertime usertime = usertime - guest; nicetime = nicetime - guestnice; // Fields existing on kernels >= 2.6 // (and RHEL's patched kernel 2.4...) unsigned long long int idlealltime = idletime + ioWait; unsigned long long int systemalltime = systemtime + irq + softIrq; unsigned long long int virtalltime = guest + guestnice; unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; CPUData* cpuData = &(this->cpus[i]); // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() // used in /proc/stat rounds down numbers, it can lead to a case where the // integer overflow. #define WRAP_SUBTRACT(a,b) (((a) > (b)) ? (a) - (b) : 0) cpuData->userPeriod = WRAP_SUBTRACT(usertime, cpuData->userTime); cpuData->nicePeriod = WRAP_SUBTRACT(nicetime, cpuData->niceTime); cpuData->systemPeriod = WRAP_SUBTRACT(systemtime, cpuData->systemTime); cpuData->systemAllPeriod = WRAP_SUBTRACT(systemalltime, cpuData->systemAllTime); cpuData->idleAllPeriod = WRAP_SUBTRACT(idlealltime, cpuData->idleAllTime); cpuData->idlePeriod = WRAP_SUBTRACT(idletime, cpuData->idleTime); cpuData->ioWaitPeriod = WRAP_SUBTRACT(ioWait, cpuData->ioWaitTime); cpuData->irqPeriod = WRAP_SUBTRACT(irq, cpuData->irqTime); cpuData->softIrqPeriod = WRAP_SUBTRACT(softIrq, cpuData->softIrqTime); cpuData->stealPeriod = WRAP_SUBTRACT(steal, cpuData->stealTime); cpuData->guestPeriod = WRAP_SUBTRACT(virtalltime, cpuData->guestTime); cpuData->totalPeriod = WRAP_SUBTRACT(totaltime, cpuData->totalTime); #undef WRAP_SUBTRACT cpuData->userTime = usertime; cpuData->niceTime = nicetime; cpuData->systemTime = systemtime; cpuData->systemAllTime = systemalltime; cpuData->idleAllTime = idlealltime; cpuData->idleTime = idletime; cpuData->ioWaitTime = ioWait; cpuData->irqTime = irq; cpuData->softIrqTime = softIrq; cpuData->stealTime = steal; cpuData->guestTime = virtalltime; cpuData->totalTime = totaltime; } double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file); return period; } static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) { int cpus = this->super.cpuCount; int numCPUsWithFrequency = 0; unsigned long totalFrequency = 0; for (int i = 0; i < cpus; ++i) { char pathBuffer[64]; xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i); FILE* file = fopen(pathBuffer, "r"); if (!file) return -errno; unsigned long frequency; if (fscanf(file, "%lu", &frequency) == 1) { /* convert kHz to MHz */ frequency = frequency / 1000; this->cpus[i + 1].frequency = frequency; numCPUsWithFrequency++; totalFrequency += frequency; } fclose(file); } if (numCPUsWithFrequency > 0) this->cpus[0].frequency = (double)totalFrequency / numCPUsWithFrequency; return 0; } static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) { FILE* file = fopen(PROCCPUINFOFILE, "r"); if (file == NULL) return; int cpus = this->super.cpuCount; int numCPUsWithFrequency = 0; double totalFrequency = 0; int cpuid = -1; while (!feof(file)) { double frequency; char buffer[PROC_LINE_LENGTH]; if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) break; if ( (sscanf(buffer, "processor : %d", &cpuid) == 1) || (sscanf(buffer, "processor: %d", &cpuid) == 1) ) { continue; } else if ( (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) || (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) || (sscanf(buffer, "clock: %lfMHz", &frequency) == 1) ) { if (cpuid < 0 || cpuid > (cpus - 1)) { continue; } CPUData* cpuData = &(this->cpus[cpuid + 1]); /* do not override sysfs data */ if (isnan(cpuData->frequency)) { cpuData->frequency = frequency; } numCPUsWithFrequency++; totalFrequency += frequency; } else if (buffer[0] == '\n') { cpuid = -1; } } fclose(file); if (numCPUsWithFrequency > 0) { this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency; } } static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { int cpus = this->super.cpuCount; assert(cpus > 0); for (int i = 0; i <= cpus; i++) { this->cpus[i].frequency = NAN; } if (scanCPUFreqencyFromSysCPUFreq(this) == 0) { return; } scanCPUFreqencyFromCPUinfo(this); } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { LinuxProcessList* this = (LinuxProcessList*) super; const Settings* settings = super->settings; LinuxProcessList_scanMemoryInfo(super); LinuxProcessList_scanZfsArcstats(this); LinuxProcessList_updateCPUcount(this); LinuxProcessList_scanZramInfo(this); double period = LinuxProcessList_scanCPUTime(this); if (settings->showCPUFrequency) { LinuxProcessList_scanCPUFrequency(this); } #ifdef HAVE_SENSORS_SENSORS_H if (settings->showCPUTemperature) LibSensors_getCPUTemperatures(this->cpus, this->super.cpuCount); #endif // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { return; } struct timeval tv; gettimeofday(&tv, NULL); unsigned long long now = tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL; /* PROCDIR is an absolute path */ assert(PROCDIR[0] == '/'); #ifdef HAVE_OPENAT openat_arg_t rootFd = AT_FDCWD; #else openat_arg_t rootFd = ""; #endif LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period, now); } htop-3.0.5/linux/LinuxProcessList.h000066400000000000000000000050571377712513700173170ustar00rootroot00000000000000#ifndef HEADER_LinuxProcessList #define HEADER_LinuxProcessList /* htop - LinuxProcessList.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" #include #include #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" #include "ZramStats.h" #include "zfs/ZfsArcStats.h" typedef struct CPUData_ { unsigned long long int totalTime; unsigned long long int userTime; unsigned long long int systemTime; unsigned long long int systemAllTime; unsigned long long int idleAllTime; unsigned long long int idleTime; unsigned long long int niceTime; unsigned long long int ioWaitTime; unsigned long long int irqTime; unsigned long long int softIrqTime; unsigned long long int stealTime; unsigned long long int guestTime; unsigned long long int totalPeriod; unsigned long long int userPeriod; unsigned long long int systemPeriod; unsigned long long int systemAllPeriod; unsigned long long int idleAllPeriod; unsigned long long int idlePeriod; unsigned long long int nicePeriod; unsigned long long int ioWaitPeriod; unsigned long long int irqPeriod; unsigned long long int softIrqPeriod; unsigned long long int stealPeriod; unsigned long long int guestPeriod; double frequency; #ifdef HAVE_SENSORS_SENSORS_H double temperature; #endif } CPUData; typedef struct TtyDriver_ { char* path; unsigned int major; unsigned int minorFrom; unsigned int minorTo; } TtyDriver; typedef struct LinuxProcessList_ { ProcessList super; CPUData* cpus; TtyDriver* ttyDrivers; bool haveSmapsRollup; #ifdef HAVE_DELAYACCT struct nl_sock* netlink_socket; int netlink_family; #endif ZfsArcStats zfs; ZramStats zram; } LinuxProcessList; #ifndef PROCDIR #define PROCDIR "/proc" #endif #ifndef PROCCPUINFOFILE #define PROCCPUINFOFILE PROCDIR "/cpuinfo" #endif #ifndef PROCSTATFILE #define PROCSTATFILE PROCDIR "/stat" #endif #ifndef PROCMEMINFOFILE #define PROCMEMINFOFILE PROCDIR "/meminfo" #endif #ifndef PROCARCSTATSFILE #define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats" #endif #ifndef PROCTTYDRIVERSFILE #define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" #endif #ifndef PROC_LINE_LENGTH #define PROC_LINE_LENGTH 4096 #endif ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); #endif htop-3.0.5/linux/Platform.c000066400000000000000000000574241377712513700156110ustar00rootroot00000000000000/* htop - linux/Platform.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" #include "Platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BatteryMeter.h" #include "ClockMeter.h" #include "Compat.h" #include "CPUMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" #include "DiskIOMeter.h" #include "HostnameMeter.h" #include "IOPriority.h" #include "IOPriorityPanel.h" #include "LinuxProcess.h" #include "LinuxProcessList.h" #include "LoadAverageMeter.h" #include "Macros.h" #include "MainPanel.h" #include "Meter.h" #include "MemoryMeter.h" #include "NetworkIOMeter.h" #include "Object.h" #include "Panel.h" #include "PressureStallMeter.h" #include "ProcessList.h" #include "ProvideCurses.h" #include "SELinuxMeter.h" #include "Settings.h" #include "SwapMeter.h" #include "SystemdMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" #include "ZramMeter.h" #include "ZramStats.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsArcStats.h" #include "zfs/ZfsCompressedArcMeter.h" #ifdef HAVE_SENSORS_SENSORS_H #include "LibSensors.h" #endif 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[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, { .name = " 2 SIGINT", .number = 2 }, { .name = " 3 SIGQUIT", .number = 3 }, { .name = " 4 SIGILL", .number = 4 }, { .name = " 5 SIGTRAP", .number = 5 }, { .name = " 6 SIGABRT", .number = 6 }, { .name = " 6 SIGIOT", .number = 6 }, { .name = " 7 SIGBUS", .number = 7 }, { .name = " 8 SIGFPE", .number = 8 }, { .name = " 9 SIGKILL", .number = 9 }, { .name = "10 SIGUSR1", .number = 10 }, { .name = "11 SIGSEGV", .number = 11 }, { .name = "12 SIGUSR2", .number = 12 }, { .name = "13 SIGPIPE", .number = 13 }, { .name = "14 SIGALRM", .number = 14 }, { .name = "15 SIGTERM", .number = 15 }, { .name = "16 SIGSTKFLT", .number = 16 }, { .name = "17 SIGCHLD", .number = 17 }, { .name = "18 SIGCONT", .number = 18 }, { .name = "19 SIGSTOP", .number = 19 }, { .name = "20 SIGTSTP", .number = 20 }, { .name = "21 SIGTTIN", .number = 21 }, { .name = "22 SIGTTOU", .number = 22 }, { .name = "23 SIGURG", .number = 23 }, { .name = "24 SIGXCPU", .number = 24 }, { .name = "25 SIGXFSZ", .number = 25 }, { .name = "26 SIGVTALRM", .number = 26 }, { .name = "27 SIGPROF", .number = 27 }, { .name = "28 SIGWINCH", .number = 28 }, { .name = "29 SIGIO", .number = 29 }, { .name = "29 SIGPOLL", .number = 29 }, { .name = "30 SIGPWR", .number = 30 }, { .name = "31 SIGSYS", .number = 31 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); static enum { BAT_PROC, BAT_SYS, BAT_ERR } Platform_Battery_method = BAT_PROC; static time_t Platform_Battery_cacheTime; static double Platform_Battery_cachePercent = NAN; static ACPresence Platform_Battery_cacheIsOnAC; void Platform_init(void) { 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 } static Htop_Reaction Platform_actionSetIOPriority(State* st) { Panel* panel = st->panel; LinuxProcess* p = (LinuxProcess*) Panel_getSelected(panel); if (!p) return HTOP_OK; IOPriority ioprio1 = p->ioPriority; Panel* ioprioPanel = IOPriorityPanel_new(ioprio1); void* set = Action_pickFromVector(st, ioprioPanel, 21, true); if (set) { IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel); bool ok = MainPanel_foreachProcess((MainPanel*)panel, LinuxProcess_setIOPriority, (Arg) { .i = ioprio2 }, NULL); if (!ok) { beep(); } } Panel_delete((Object*)ioprioPanel); return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } void Platform_setBindings(Htop_Action* keys) { keys['i'] = Platform_actionSetIOPriority; } const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, &LoadAverageMeter_class, &LoadMeter_class, &MemoryMeter_class, &SwapMeter_class, &TasksMeter_class, &UptimeMeter_class, &BatteryMeter_class, &HostnameMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, &AllCPUs4Meter_class, &AllCPUs8Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, &LeftCPUs4Meter_class, &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &BlankMeter_class, &PressureStallCPUSomeMeter_class, &PressureStallIOSomeMeter_class, &PressureStallIOFullMeter_class, &PressureStallMemorySomeMeter_class, &PressureStallMemoryFullMeter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, &ZramMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, &SELinuxMeter_class, &SystemdMeter_class, NULL }; int Platform_getUptime() { double uptime = 0; FILE* fd = fopen(PROCDIR "/uptime", "r"); if (fd) { int n = fscanf(fd, "%64lf", &uptime); fclose(fd); if (n <= 0) { return 0; } } return floor(uptime); } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { int activeProcs, totalProcs, lastProc; *one = 0; *five = 0; *fifteen = 0; FILE* fd = fopen(PROCDIR "/loadavg", "r"); if (fd) { int total = fscanf(fd, "%32lf %32lf %32lf %32d/%32d %32d", one, five, fifteen, &activeProcs, &totalProcs, &lastProc); (void) total; assert(total == 6); fclose(fd); } } int Platform_getMaxPid() { FILE* file = fopen(PROCDIR "/sys/kernel/pid_max", "r"); if (!file) return -1; int maxPid = 4194303; int match = fscanf(file, "%32d", &maxPid); (void) match; fclose(file); return maxPid; } double Platform_setCPUValues(Meter* this, int cpu) { const LinuxProcessList* pl = (const LinuxProcessList*) this->pl; const CPUData* cpuData = &(pl->cpus[cpu]); double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); double percent; double* v = this->values; v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; if (this->pl->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0; v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0; v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0; v[CPU_METER_STEAL] = cpuData->stealPeriod / total * 100.0; v[CPU_METER_GUEST] = cpuData->guestPeriod / total * 100.0; v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0; this->curItems = 8; if (this->pl->settings->accountGuestInCPUMeter) { percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6]; } else { percent = v[0] + v[1] + v[2] + v[3] + v[4]; } } else { v[2] = cpuData->systemAllPeriod / total * 100.0; v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0; this->curItems = 4; percent = v[0] + v[1] + v[2] + v[3]; } percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) { percent = 0.0; } v[CPU_METER_FREQUENCY] = cpuData->frequency; #ifdef HAVE_SENSORS_SENSORS_H v[CPU_METER_TEMPERATURE] = cpuData->temperature; #else v[CPU_METER_TEMPERATURE] = NAN; #endif return percent; } void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; const LinuxProcessList* lpl = (const LinuxProcessList*) pl; long int usedMem = pl->usedMem; long int buffersMem = pl->buffersMem; long int cachedMem = pl->cachedMem; usedMem -= buffersMem + cachedMem; this->total = pl->totalMem; this->values[0] = usedMem; this->values[1] = buffersMem; this->values[2] = cachedMem; if (lpl->zfs.enabled != 0) { this->values[0] -= lpl->zfs.size; this->values[2] += lpl->zfs.size; } } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[0] = pl->usedSwap; } void Platform_setZramValues(Meter* this) { const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; this->total = lpl->zram.totalZram; this->values[0] = lpl->zram.usedZramComp; this->values[1] = lpl->zram.usedZramOrig; } void Platform_setZfsArcValues(Meter* this) { const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; ZfsArcMeter_readStats(this, &(lpl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; ZfsCompressedArcMeter_readStats(this, &(lpl->zfs)); } char* Platform_getProcessEnv(pid_t pid) { char procname[128]; xSnprintf(procname, sizeof(procname), PROCDIR "/%d/environ", pid); FILE* fd = fopen(procname, "r"); if (!fd) return NULL; char* env = NULL; size_t capacity = 0; size_t size = 0; ssize_t bytes = 0; do { size += bytes; capacity += 4096; env = xRealloc(env, capacity); } while ((bytes = fread(env + size, 1, capacity - size, fd)) > 0); fclose(fd); if (bytes < 0) { free(env); return NULL; } size += bytes; env = xRealloc(env, size + 2); env[size] = '\0'; env[size + 1] = '\0'; return env; } /* * Return the absolute path of a file given its pid&inode number * * Based on implementation of lslocks from util-linux: * https://sources.debian.org/src/util-linux/2.36-3/misc-utils/lslocks.c/#L162 */ char* Platform_getInodeFilename(pid_t pid, ino_t inode) { struct stat sb; struct dirent *de; DIR *dirp; ssize_t len; int fd; char path[PATH_MAX]; char sym[PATH_MAX]; char* ret = NULL; memset(path, 0, sizeof(path)); memset(sym, 0, sizeof(sym)); xSnprintf(path, sizeof(path), "%s/%d/fd/", PROCDIR, pid); if (strlen(path) >= (sizeof(path) - 2)) return NULL; if (!(dirp = opendir(path))) return NULL; if ((fd = dirfd(dirp)) < 0 ) goto out; while ((de = readdir(dirp))) { if (String_eq(de->d_name, ".") || String_eq(de->d_name, "..")) continue; /* care only for numerical descriptors */ if (!strtoull(de->d_name, (char **) NULL, 10)) continue; if (!Compat_fstatat(fd, path, de->d_name, &sb, 0) && inode != sb.st_ino) continue; if ((len = Compat_readlinkat(fd, path, de->d_name, sym, sizeof(sym) - 1)) < 1) goto out; sym[len] = '\0'; ret = xStrdup(sym); break; } out: closedir(dirp); return ret; } FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData)); FILE* f = fopen(PROCDIR "/locks", "r"); if (!f) { pdata->error = true; return pdata; } char buffer[1024]; FileLocks_LockData** data_ref = &pdata->locks; while(fgets(buffer, sizeof(buffer), f)) { if (!strchr(buffer, '\n')) continue; int lock_id; char lock_type[16]; char lock_excl[16]; char lock_rw[16]; pid_t lock_pid; unsigned int lock_dev[2]; uint64_t lock_inode; char lock_start[25]; char lock_end[25]; if (10 != sscanf(buffer, "%d: %15s %15s %15s %d %x:%x:%"PRIu64" %24s %24s", &lock_id, lock_type, lock_excl, lock_rw, &lock_pid, &lock_dev[0], &lock_dev[1], &lock_inode, lock_start, lock_end)) continue; if (pid != lock_pid) continue; FileLocks_LockData* ldata = xCalloc(1, sizeof(FileLocks_LockData)); FileLocks_Data* data = &ldata->data; data->id = lock_id; data->locktype = xStrdup(lock_type); data->exclusive = xStrdup(lock_excl); data->readwrite = xStrdup(lock_rw); data->filename = Platform_getInodeFilename(lock_pid, lock_inode); data->dev[0] = lock_dev[0]; data->dev[1] = lock_dev[1]; data->inode = lock_inode; data->start = strtoull(lock_start, NULL, 10); if (!String_eq(lock_end, "EOF")) { data->end = strtoull(lock_end, NULL, 10); } else { data->end = ULLONG_MAX; } *data_ref = ldata; data_ref = &ldata->next; } fclose(f); return pdata; } void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred) { *ten = *sixty = *threehundred = 0; char procname[128]; xSnprintf(procname, sizeof(procname), PROCDIR "/pressure/%s", file); FILE* fd = fopen(procname, "r"); if (!fd) { *ten = *sixty = *threehundred = NAN; return; } int total = fscanf(fd, "some avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred); if (!some) { total = fscanf(fd, "full avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred); } (void) total; assert(total == 3); fclose(fd); } bool Platform_getDiskIO(DiskIOData* data) { FILE* fd = fopen(PROCDIR "/diskstats", "r"); if (!fd) return false; unsigned long int read_sum = 0, write_sum = 0, timeSpend_sum = 0; char lineBuffer[256]; while (fgets(lineBuffer, sizeof(lineBuffer), fd)) { char diskname[32]; unsigned long int read_tmp, write_tmp, timeSpend_tmp; if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %lu %*u %*u %*u %lu %*u %*u %lu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) { if (String_startsWith(diskname, "dm-")) continue; /* only count root disks, e.g. do not count IO from sda and sda1 twice */ if ((diskname[0] == 's' || diskname[0] == 'h') && diskname[1] == 'd' && isalpha((unsigned char)diskname[2]) && isdigit((unsigned char)diskname[3])) continue; /* only count root disks, e.g. do not count IO from mmcblk0 and mmcblk0p1 twice */ if (diskname[0] == 'm' && diskname[1] == 'm' && diskname[2] == 'c' && diskname[3] == 'b' && diskname[4] == 'l' && diskname[5] == 'k' && isdigit((unsigned char)diskname[6]) && diskname[7] == 'p') continue; read_sum += read_tmp; write_sum += write_tmp; timeSpend_sum += timeSpend_tmp; } } fclose(fd); /* multiply with sector size */ data->totalBytesRead = 512 * read_sum; data->totalBytesWritten = 512 * write_sum; data->totalMsTimeSpend = timeSpend_sum; return true; } bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted) { FILE* fd = fopen(PROCDIR "/net/dev", "r"); if (!fd) return false; unsigned long int bytesReceivedSum = 0, packetsReceivedSum = 0, bytesTransmittedSum = 0, packetsTransmittedSum = 0; char lineBuffer[512]; while (fgets(lineBuffer, sizeof(lineBuffer), fd)) { char interfaceName[32]; unsigned long int bytesReceivedParsed, packetsReceivedParsed, bytesTransmittedParsed, packetsTransmittedParsed; if (sscanf(lineBuffer, "%31s %lu %lu %*u %*u %*u %*u %*u %*u %lu %lu", interfaceName, &bytesReceivedParsed, &packetsReceivedParsed, &bytesTransmittedParsed, &packetsTransmittedParsed) != 5) continue; if (String_eq(interfaceName, "lo:")) continue; bytesReceivedSum += bytesReceivedParsed; packetsReceivedSum += packetsReceivedParsed; bytesTransmittedSum += bytesTransmittedParsed; packetsTransmittedSum += packetsTransmittedParsed; } fclose(fd); *bytesReceived = bytesReceivedSum; *packetsReceived = packetsReceivedSum; *bytesTransmitted = bytesTransmittedSum; *packetsTransmitted = packetsTransmittedSum; return true; } // Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). #define MAX_BATTERIES 64 #define PROC_BATTERY_DIR PROCDIR "/acpi/battery" #define PROC_POWERSUPPLY_DIR PROCDIR "/acpi/ac_adapter" #define SYS_POWERSUPPLY_DIR "/sys/class/power_supply" // ---------------------------------------- // READ FROM /proc // ---------------------------------------- static unsigned long int parseBatInfo(const char* fileName, const unsigned short int lineNum, const unsigned short int wordNum) { const char batteryPath[] = PROC_BATTERY_DIR; DIR* batteryDir = opendir(batteryPath); if (!batteryDir) return 0; char* batteries[MAX_BATTERIES]; unsigned int nBatteries = 0; memset(batteries, 0, MAX_BATTERIES * sizeof(char*)); while (nBatteries < MAX_BATTERIES) { struct dirent* dirEntry = readdir(batteryDir); if (!dirEntry) break; char* entryName = dirEntry->d_name; if (!String_startsWith(entryName, "BAT")) continue; batteries[nBatteries] = xStrdup(entryName); nBatteries++; } closedir(batteryDir); unsigned long int total = 0; for (unsigned int i = 0; i < nBatteries; i++) { char infoPath[30]; xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName); FILE* file = fopen(infoPath, "r"); if (!file) break; char* line = NULL; for (unsigned short int j = 0; j < lineNum; j++) { free(line); line = String_readLine(file); if (!line) break; } fclose(file); if (!line) break; char* foundNumStr = String_getToken(line, wordNum); const unsigned long int foundNum = atoi(foundNumStr); free(foundNumStr); free(line); total += foundNum; } for (unsigned int i = 0; i < nBatteries; i++) free(batteries[i]); return total; } static ACPresence procAcpiCheck(void) { ACPresence isOn = AC_ERROR; const char* power_supplyPath = PROC_POWERSUPPLY_DIR; DIR* dir = opendir(power_supplyPath); if (!dir) return AC_ERROR; for (;;) { struct dirent* dirEntry = readdir(dir); if (!dirEntry) break; const char* entryName = dirEntry->d_name; if (entryName[0] != 'A') continue; char statePath[256]; xSnprintf(statePath, sizeof(statePath), "%s/%s/state", power_supplyPath, entryName); FILE* file = fopen(statePath, "r"); if (!file) { isOn = AC_ERROR; continue; } char* line = String_readLine(file); fclose(file); if (!line) continue; char* isOnline = String_getToken(line, 2); free(line); if (String_eq(isOnline, "on-line")) isOn = AC_PRESENT; else isOn = AC_ABSENT; free(isOnline); if (isOn == AC_PRESENT) break; } if (dir) closedir(dir); return isOn; } static double Platform_Battery_getProcBatInfo(void) { const unsigned long int totalFull = parseBatInfo("info", 3, 4); if (totalFull == 0) return NAN; const unsigned long int totalRemain = parseBatInfo("state", 5, 3); if (totalRemain == 0) return NAN; return totalRemain * 100.0 / (double) totalFull; } static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) { *isOnAC = procAcpiCheck(); *percent = AC_ERROR != *isOnAC ? Platform_Battery_getProcBatInfo() : NAN; } // ---------------------------------------- // READ FROM /sys // ---------------------------------------- static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) { *percent = NAN; *isOnAC = AC_ERROR; DIR* dir = opendir(SYS_POWERSUPPLY_DIR); if (!dir) return; unsigned long int totalFull = 0; unsigned long int totalRemain = 0; for (;;) { struct dirent* dirEntry = readdir(dir); if (!dirEntry) break; const char* entryName = dirEntry->d_name; char filePath[256]; xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName); char type[8]; ssize_t r = xReadfile(filePath, type, sizeof(type)); if (r < 3) continue; if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') { xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); char buffer[1024]; r = xReadfile(filePath, buffer, sizeof(buffer)); if (r < 0) { closedir(dir); return; } char* buf = buffer; char* line = NULL; bool full = false; bool now = false; int fullSize = 0; double capacityLevel = NAN; #define match(str,prefix) \ (String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL) while ((line = strsep(&buf, "\n")) != NULL) { const char* ps = match(line, "POWER_SUPPLY_"); if (!ps) continue; const char* capacity = match(ps, "CAPACITY="); if (capacity) capacityLevel = atoi(capacity) / 100.0; const char* energy = match(ps, "ENERGY_"); if (!energy) energy = match(ps, "CHARGE_"); if (!energy) continue; const char* value = (!full) ? match(energy, "FULL=") : NULL; if (value) { fullSize = atoi(value); totalFull += fullSize; full = true; if (now) break; continue; } value = (!now) ? match(energy, "NOW=") : NULL; if (value) { totalRemain += atoi(value); now = true; if (full) break; continue; } } #undef match if (!now && full && !isnan(capacityLevel)) totalRemain += (capacityLevel * fullSize); } else if (entryName[0] == 'A') { if (*isOnAC != AC_ERROR) continue; xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName); char buffer[2]; r = xReadfile(filePath, buffer, sizeof(buffer)); if (r < 1) { closedir(dir); return; } if (buffer[0] == '0') *isOnAC = AC_ABSENT; else if (buffer[0] == '1') *isOnAC = AC_PRESENT; } } closedir(dir); *percent = totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN; } void Platform_getBattery(double* percent, ACPresence* isOnAC) { time_t now = time(NULL); // update battery reading is slow. Update it each 10 seconds only. if (now < Platform_Battery_cacheTime + 10) { *percent = Platform_Battery_cachePercent; *isOnAC = Platform_Battery_cacheIsOnAC; return; } if (Platform_Battery_method == BAT_PROC) { Platform_Battery_getProcData(percent, isOnAC); if (isnan(*percent)) Platform_Battery_method = BAT_SYS; } if (Platform_Battery_method == BAT_SYS) { Platform_Battery_getSysData(percent, isOnAC); if (isnan(*percent)) Platform_Battery_method = BAT_ERR; } if (Platform_Battery_method == BAT_ERR) { *percent = NAN; *isOnAC = AC_ERROR; } else { *percent = CLAMP(*percent, 0.0, 100.0); } Platform_Battery_cachePercent = *percent; Platform_Battery_cacheIsOnAC = *isOnAC; Platform_Battery_cacheTime = now; } htop-3.0.5/linux/Platform.h000066400000000000000000000033771377712513700156140ustar00rootroot00000000000000#ifndef HEADER_Platform #define HEADER_Platform /* htop - linux/Platform.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "Meter.h" #include "Process.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" extern const ProcessField Platform_defaultFields[]; extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; extern const MeterClass* const Platform_meterTypes[]; void Platform_init(void); void Platform_done(void); void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(void); double Platform_setCPUValues(Meter* this, int cpu); void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); void Platform_setZramValues(Meter* this); void Platform_setZfsArcValues(Meter* this); void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); char* Platform_getInodeFilename(pid_t pid, ino_t inode); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred); bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted); void Platform_getBattery(double *percent, ACPresence *isOnAC); #endif htop-3.0.5/linux/PressureStallMeter.c000066400000000000000000000104701377712513700176200ustar00rootroot00000000000000/* htop - PressureStallMeter.c (C) 2004-2011 Hisham H. Muhammad (C) 2019 Ran Benita Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "PressureStallMeter.h" #include #include #include "CRT.h" #include "Meter.h" #include "Object.h" #include "Platform.h" #include "RichString.h" #include "XUtils.h" static const int PressureStallMeter_attributes[] = { PRESSURE_STALL_TEN, PRESSURE_STALL_SIXTY, PRESSURE_STALL_THREEHUNDRED }; static void PressureStallMeter_updateValues(Meter* this, char* buffer, size_t len) { const char* file; if (strstr(Meter_name(this), "CPU")) { file = "cpu"; } else if (strstr(Meter_name(this), "IO")) { file = "io"; } else { file = "memory"; } bool some; if (strstr(Meter_name(this), "Some")) { some = true; } else { some = false; } Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]); /* only print bar for ten (not sixty and threehundred), cause the sum is meaningless */ this->curItems = 1; xSnprintf(buffer, len, "%s %s %5.2lf%% %5.2lf%% %5.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]); } static void PressureStallMeter_display(const Object* cast, RichString* out) { const Meter* this = (const Meter*)cast; char buffer[20]; xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[0]); RichString_writeAscii(out, CRT_colors[PRESSURE_STALL_TEN], buffer); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[1]); RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[2]); RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer); } const MeterClass PressureStallCPUSomeMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = PressureStallMeter_display, }, .updateValues = PressureStallMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 3, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallCPUSome", .uiName = "PSI some CPU", .caption = "PSI some CPU: ", .description = "Pressure Stall Information, some cpu" }; const MeterClass PressureStallIOSomeMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = PressureStallMeter_display, }, .updateValues = PressureStallMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 3, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallIOSome", .uiName = "PSI some IO", .caption = "PSI some IO: ", .description = "Pressure Stall Information, some io" }; const MeterClass PressureStallIOFullMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = PressureStallMeter_display, }, .updateValues = PressureStallMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 3, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallIOFull", .uiName = "PSI full IO", .caption = "PSI full IO: ", .description = "Pressure Stall Information, full io" }; const MeterClass PressureStallMemorySomeMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = PressureStallMeter_display, }, .updateValues = PressureStallMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 3, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallMemorySome", .uiName = "PSI some memory", .caption = "PSI some memory: ", .description = "Pressure Stall Information, some memory" }; const MeterClass PressureStallMemoryFullMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = PressureStallMeter_display, }, .updateValues = PressureStallMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 3, .total = 100.0, .attributes = PressureStallMeter_attributes, .name = "PressureStallMemoryFull", .uiName = "PSI full memory", .caption = "PSI full memory: ", .description = "Pressure Stall Information, full memory" }; htop-3.0.5/linux/PressureStallMeter.h000066400000000000000000000011741377712513700176260ustar00rootroot00000000000000/* Do not edit this file. It was automatically generated. */ #ifndef HEADER_PressureStallMeter #define HEADER_PressureStallMeter /* htop - PressureStallMeter.h (C) 2004-2011 Hisham H. Muhammad (C) 2019 Ran Benita Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass PressureStallCPUSomeMeter_class; extern const MeterClass PressureStallIOSomeMeter_class; extern const MeterClass PressureStallIOFullMeter_class; extern const MeterClass PressureStallMemorySomeMeter_class; extern const MeterClass PressureStallMemoryFullMeter_class; #endif htop-3.0.5/linux/ProcessField.h000066400000000000000000000031551377712513700164040ustar00rootroot00000000000000#ifndef HEADER_LinuxProcessField #define HEADER_LinuxProcessField /* htop - linux/ProcessField.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define PLATFORM_PROCESS_FIELDS \ CMINFLT = 11, \ CMAJFLT = 13, \ UTIME = 14, \ STIME = 15, \ CUTIME = 16, \ CSTIME = 17, \ M_SHARE = 41, \ M_TRS = 42, \ M_DRS = 43, \ M_LRS = 44, \ M_DT = 45, \ CTID = 100, \ VPID = 101, \ VXID = 102, \ RCHAR = 103, \ WCHAR = 104, \ SYSCR = 105, \ SYSCW = 106, \ RBYTES = 107, \ WBYTES = 108, \ CNCLWB = 109, \ IO_READ_RATE = 110, \ IO_WRITE_RATE = 111, \ IO_RATE = 112, \ CGROUP = 113, \ OOM = 114, \ IO_PRIORITY = 115, \ PERCENT_CPU_DELAY = 116, \ PERCENT_IO_DELAY = 117, \ PERCENT_SWAP_DELAY = 118, \ M_PSS = 119, \ M_SWAP = 120, \ M_PSSWP = 121, \ CTXT = 122, \ SECATTR = 123, \ PROC_COMM = 124, \ PROC_EXE = 125, \ CWD = 126, \ // End of list #endif /* HEADER_LinuxProcessField */ htop-3.0.5/linux/SELinuxMeter.c000066400000000000000000000040111377712513700163310ustar00rootroot00000000000000/* htop - SELinuxMeter.c (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "SELinuxMeter.h" #include "CRT.h" #include #include #include #include #include #include #include "Macros.h" #include "Object.h" #include "XUtils.h" static const int SELinuxMeter_attributes[] = { METER_TEXT, }; static bool enabled = false; static bool enforcing = false; static bool hasSELinuxMount(void) { struct statfs sfbuf; int r = statfs("/sys/fs/selinux", &sfbuf); if (r != 0) { return false; } if ((uint32_t)sfbuf.f_type != (uint32_t)SELINUX_MAGIC) { return false; } struct statvfs vfsbuf; r = statvfs("/sys/fs/selinux", &vfsbuf); if (r != 0 || (vfsbuf.f_flag & ST_RDONLY)) { return false; } return true; } static bool isSelinuxEnabled(void) { return hasSELinuxMount() && (0 == access("/etc/selinux/config", F_OK)); } static bool isSelinuxEnforcing(void) { if (!enabled) { return false; } char buf[20]; ssize_t r = xReadfile("/sys/fs/selinux/enforce", buf, sizeof(buf)); if (r < 0) return false; int enforce = 0; if (sscanf(buf, "%d", &enforce) != 1) { return false; } return !!enforce; } static void SELinuxMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) { enabled = isSelinuxEnabled(); enforcing = isSelinuxEnforcing(); xSnprintf(buffer, len, "%s%s", enabled ? "enabled" : "disabled", enabled ? (enforcing ? "; mode: enforcing" : "; mode: permissive") : ""); } const MeterClass SELinuxMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, }, .updateValues = SELinuxMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 0, .total = 100.0, .attributes = SELinuxMeter_attributes, .name = "SELinux", .uiName = "SELinux", .description = "SELinux state overview", .caption = "SELinux: " }; htop-3.0.5/linux/SELinuxMeter.h000066400000000000000000000004571377712513700163500ustar00rootroot00000000000000#ifndef HEADER_SELinuxMeter #define HEADER_SELinuxMeter /* htop - SELinuxMeter.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass SELinuxMeter_class; #endif /* HEADER_SELinuxMeter */ htop-3.0.5/linux/SystemdMeter.c000066400000000000000000000251021377712513700164360ustar00rootroot00000000000000/* htop - SystemdMeter.c (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "SystemdMeter.h" #include #include #include #include #include #include #include #include "CRT.h" #include "Macros.h" #include "Object.h" #include "RichString.h" #include "XUtils.h" #define INVALID_VALUE ((unsigned int)-1) typedef void sd_bus; typedef void sd_bus_error; static int (*sym_sd_bus_open_system)(sd_bus**); static int (*sym_sd_bus_get_property_string)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char**); static int (*sym_sd_bus_get_property_trivial)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char, void*); static sd_bus* (*sym_sd_bus_unref)(sd_bus*); static char* systemState = NULL; static unsigned int nFailedUnits = INVALID_VALUE; static unsigned int nInstalledJobs = INVALID_VALUE; static unsigned int nNames = INVALID_VALUE; static unsigned int nJobs = INVALID_VALUE; static void* dlopenHandle = NULL; static sd_bus* bus = NULL; static void SystemdMeter_done(ATTR_UNUSED Meter* this) { free(systemState); systemState = NULL; if (bus && dlopenHandle) { sym_sd_bus_unref(bus); } bus = NULL; if (dlopenHandle) { dlclose(dlopenHandle); dlopenHandle = NULL; } } static int updateViaLib(void) { if (!dlopenHandle) { dlopenHandle = dlopen("libsystemd.so.0", RTLD_LAZY); if (!dlopenHandle) goto dlfailure; /* Clear any errors */ dlerror(); #define resolve(symbolname) do { \ *(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname); \ if (!sym_##symbolname || dlerror() != NULL) \ goto dlfailure; \ } while(0) resolve(sd_bus_open_system); resolve(sd_bus_get_property_string); resolve(sd_bus_get_property_trivial); resolve(sd_bus_unref); #undef resolve } int r; /* Connect to the system bus */ if (!bus) { r = sym_sd_bus_open_system(&bus); if (r < 0) goto busfailure; } static const char* const busServiceName = "org.freedesktop.systemd1"; static const char* const busObjectPath = "/org/freedesktop/systemd1"; static const char* const busInterfaceName = "org.freedesktop.systemd1.Manager"; r = sym_sd_bus_get_property_string(bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "SystemState", /* property name */ NULL, /* object to return error in */ &systemState); if (r < 0) goto busfailure; r = sym_sd_bus_get_property_trivial(bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NFailedUnits", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ &nFailedUnits); if (r < 0) goto busfailure; r = sym_sd_bus_get_property_trivial(bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NInstalledJobs", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ &nInstalledJobs); if (r < 0) goto busfailure; r = sym_sd_bus_get_property_trivial(bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NNames", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ &nNames); if (r < 0) goto busfailure; r = sym_sd_bus_get_property_trivial(bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NJobs", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ &nJobs); if (r < 0) goto busfailure; /* success */ return 0; busfailure: sym_sd_bus_unref(bus); bus = NULL; return -2; dlfailure: if (dlopenHandle) { dlclose(dlopenHandle); dlopenHandle = NULL; } return -1; } static void updateViaExec(void) { int fdpair[2]; if (pipe(fdpair) < 0) return; pid_t child = fork(); if (child < 0) { close(fdpair[1]); close(fdpair[0]); return; } if (child == 0) { close(fdpair[0]); dup2(fdpair[1], STDOUT_FILENO); close(fdpair[1]); int fdnull = open("/dev/null", O_WRONLY); if (fdnull < 0) exit(1); dup2(fdnull, STDERR_FILENO); close(fdnull); execl("/bin/systemctl", "/bin/systemctl", "show", "--property=SystemState", "--property=NFailedUnits", "--property=NNames", "--property=NJobs", "--property=NInstalledJobs", NULL); exit(127); } close(fdpair[1]); int wstatus; if (waitpid(child, &wstatus, 0) < 0 || !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { close(fdpair[0]); return; } FILE* commandOutput = fdopen(fdpair[0], "r"); if (!commandOutput) { close(fdpair[0]); return; } char lineBuffer[128]; while (fgets(lineBuffer, sizeof(lineBuffer), commandOutput)) { if (String_startsWith(lineBuffer, "SystemState=")) { char* newline = strchr(lineBuffer + strlen("SystemState="), '\n'); if (newline) { *newline = '\0'; } free(systemState); systemState = xStrdup(lineBuffer + strlen("SystemState=")); } else if (String_startsWith(lineBuffer, "NFailedUnits=")) { nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10); } else if (String_startsWith(lineBuffer, "NNames=")) { nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10); } else if (String_startsWith(lineBuffer, "NJobs=")) { nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10); } else if (String_startsWith(lineBuffer, "NInstalledJobs=")) { nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10); } } fclose(commandOutput); } static void SystemdMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) { free(systemState); systemState = NULL; nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE; if (updateViaLib() < 0) updateViaExec(); xSnprintf(buffer, size, "%s", systemState ? systemState : "???"); } static int zeroDigitColor(unsigned int value) { switch (value) { case 0: return CRT_colors[METER_VALUE]; case INVALID_VALUE: return CRT_colors[METER_VALUE_ERROR]; default: return CRT_colors[METER_VALUE_NOTICE]; } } static int valueDigitColor(unsigned int value) { switch (value) { case 0: return CRT_colors[METER_VALUE_NOTICE]; case INVALID_VALUE: return CRT_colors[METER_VALUE_ERROR]; default: return CRT_colors[METER_VALUE]; } } static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { char buffer[16]; int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR; RichString_writeAscii(out, CRT_colors[color], systemState ? systemState : "N/A"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " ("); if (nFailedUnits == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; } else { xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits); } RichString_appendAscii(out, zeroDigitColor(nFailedUnits), buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/"); if (nNames == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; } else { xSnprintf(buffer, sizeof(buffer), "%u", nNames); } RichString_appendAscii(out, valueDigitColor(nNames), buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) ("); if (nJobs == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; } else { xSnprintf(buffer, sizeof(buffer), "%u", nJobs); } RichString_appendAscii(out, zeroDigitColor(nJobs), buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/"); if (nInstalledJobs == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; } else { xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs); } RichString_appendAscii(out, valueDigitColor(nInstalledJobs), buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)"); } static const int SystemdMeter_attributes[] = { METER_VALUE }; const MeterClass SystemdMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = SystemdMeter_display }, .updateValues = SystemdMeter_updateValues, .done = SystemdMeter_done, .defaultMode = TEXT_METERMODE, .maxItems = 0, .total = 100.0, .attributes = SystemdMeter_attributes, .name = "Systemd", .uiName = "Systemd state", .description = "Systemd system state and unit overview", .caption = "Systemd: ", }; htop-3.0.5/linux/SystemdMeter.h000066400000000000000000000004601377712513700164430ustar00rootroot00000000000000#ifndef HEADER_SystemdMeter #define HEADER_SystemdMeter /* htop - SystemdMeter.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Meter.h" extern const MeterClass SystemdMeter_class; #endif /* HEADER_SystemdMeter */ htop-3.0.5/linux/ZramMeter.c000066400000000000000000000035451377712513700157260ustar00rootroot00000000000000#include "ZramMeter.h" #include "CRT.h" #include "Meter.h" #include "Object.h" #include "Platform.h" #include "RichString.h" static const int ZramMeter_attributes[] = { ZRAM }; static void ZramMeter_updateValues(Meter* this, char* buffer, size_t size) { int written; Platform_setZramValues(this); /* on print bar for compressed data size, not uncompressed */ this->curItems = 1; written = Meter_humanUnit(buffer, this->values[0], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '('); written = Meter_humanUnit(buffer, this->values[1], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, ')'); METER_BUFFER_APPEND_CHR(buffer, size, '/'); Meter_humanUnit(buffer, this->total, size); } static void ZramMeter_display(const Object* cast, RichString* out) { char buffer[50]; const Meter* this = (const Meter*)cast; RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " uncompressed:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); } const MeterClass ZramMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = ZramMeter_display, }, .updateValues = ZramMeter_updateValues, .defaultMode = BAR_METERMODE, .maxItems = 2, .total = 100.0, .attributes = ZramMeter_attributes, .name = "Zram", .uiName = "Zram", .caption = "zrm" }; htop-3.0.5/linux/ZramMeter.h000066400000000000000000000001701377712513700157220ustar00rootroot00000000000000#ifndef HEADER_ZramMeter #define HEADER_ZramMeter #include "Meter.h" extern const MeterClass ZramMeter_class; #endif htop-3.0.5/linux/ZramStats.h000066400000000000000000000003311377712513700157430ustar00rootroot00000000000000#ifndef HEADER_ZramStats #define HEADER_ZramStats typedef struct ZramStats_ { unsigned long long int totalZram; unsigned long long int usedZramComp; unsigned long long int usedZramOrig; } ZramStats; #endif htop-3.0.5/openbsd/000077500000000000000000000000001377712513700141405ustar00rootroot00000000000000htop-3.0.5/openbsd/OpenBSDProcess.c000066400000000000000000000140471377712513700171030ustar00rootroot00000000000000/* htop - OpenBSDProcess.c (C) 2015 Hisham H. Muhammad (C) 2015 Michael McConville Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "OpenBSDProcess.h" #include #include "CRT.h" #include "Process.h" #include "RichString.h" #include "XUtils.h" const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, }, [SESSION] = { .name = "SESSION", .title = "SESN", .description = "Process's session ID", .flags = 0, .pidColumn = true, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, }; Process* OpenBSDProcess_new(const Settings* settings) { OpenBSDProcess* this = xCalloc(sizeof(OpenBSDProcess), 1); Object_setClass(this, Class(OpenBSDProcess)); Process_init(&this->super, settings); return &this->super; } void Process_delete(Object* cast) { OpenBSDProcess* this = (OpenBSDProcess*) cast; Process_done((Process*)cast); free(this); } static void OpenBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { //const OpenBSDProcess* op = (const OpenBSDProcess*) this; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; //int n = sizeof(buffer) - 1; switch (field) { // add OpenBSD-specific fields here default: Process_writeField(this, str, field); return; } RichString_appendWide(str, attr, buffer); } static int OpenBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { const OpenBSDProcess* p1 = (const OpenBSDProcess*)v1; const OpenBSDProcess* p2 = (const OpenBSDProcess*)v2; // remove if actually used (void)p1; (void)p2; switch (key) { // add OpenBSD-specific fields here default: return Process_compareByKey_Base(v1, v2, key); } } const ProcessClass OpenBSDProcess_class = { .super = { .extends = Class(Process), .display = Process_display, .delete = Process_delete, .compare = Process_compare }, .writeField = OpenBSDProcess_writeField, .compareByKey = OpenBSDProcess_compareByKey }; bool Process_isThread(const Process* this) { return Process_isKernelThread(this) || Process_isUserlandThread(this); } htop-3.0.5/openbsd/OpenBSDProcess.h000066400000000000000000000014421377712513700171030ustar00rootroot00000000000000#ifndef HEADER_OpenBSDProcess #define HEADER_OpenBSDProcess /* htop - OpenBSDProcess.h (C) 2015 Hisham H. Muhammad (C) 2015 Michael McConville Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Object.h" #include "Process.h" #include "Settings.h" typedef struct OpenBSDProcess_ { Process super; } OpenBSDProcess; #define Process_isKernelThread(_process) (_process->pgrp == 0) #define Process_isUserlandThread(_process) (_process->pid != _process->tgid) extern const ProcessClass OpenBSDProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; Process* OpenBSDProcess_new(const Settings* settings); void Process_delete(Object* cast); bool Process_isThread(const Process* this); #endif htop-3.0.5/openbsd/OpenBSDProcessList.c000066400000000000000000000250751377712513700177420ustar00rootroot00000000000000/* htop - OpenBSDProcessList.c (C) 2014 Hisham H. Muhammad (C) 2015 Michael McConville Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "OpenBSDProcessList.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CRT.h" #include "Macros.h" #include "Object.h" #include "OpenBSDProcess.h" #include "Process.h" #include "ProcessList.h" #include "Settings.h" #include "XUtils.h" static long fscale; static int pageSize; static int pageSizeKB; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { const int mib[] = { CTL_HW, HW_NCPU }; const int fmib[] = { CTL_KERN, KERN_FSCALE }; int r; size_t size; char errbuf[_POSIX2_LINE_MAX]; OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList)); ProcessList* pl = (ProcessList*) opl; ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidMatchList, userId); size = sizeof(pl->cpuCount); r = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0); if (r < 0 || pl->cpuCount < 1) { pl->cpuCount = 1; } opl->cpus = xCalloc(pl->cpuCount + 1, sizeof(CPUData)); size = sizeof(fscale); if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { CRT_fatalError("fscale sysctl call failed"); } if ((pageSize = sysconf(_SC_PAGESIZE)) == -1) CRT_fatalError("pagesize sysconf call failed"); pageSizeKB = pageSize / ONE_K; for (int i = 0; i <= pl->cpuCount; i++) { CPUData* d = opl->cpus + i; d->totalTime = 1; d->totalPeriod = 1; } opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); if (opl->kd == NULL) { CRT_fatalError("kvm_openfiles() failed"); } return pl; } void ProcessList_delete(ProcessList* this) { OpenBSDProcessList* opl = (OpenBSDProcessList*) this; if (opl->kd) { kvm_close(opl->kd); } free(opl->cpus); ProcessList_done(this); free(this); } static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; struct uvmexp uvmexp; size_t size_uvmexp = sizeof(uvmexp); if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { CRT_fatalError("uvmexp sysctl call failed"); } pl->totalMem = uvmexp.npages * pageSizeKB; pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB; // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; struct bcachestats bcstats; size_t size_bcstats = sizeof(bcstats); if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) { CRT_fatalError("cannot get vfs.bcachestat"); } pl->cachedMem = bcstats.numbufpages * pageSizeKB; /* * Copyright (c) 1994 Thorsten Lockert * All rights reserved. * * Taken almost directly from OpenBSD's top(1) * * Originally released under a BSD-3 license * Modified through htop developers applying GPL-2 */ int nswap = swapctl(SWAP_NSWAP, 0, 0); if (nswap > 0) { struct swapent swdev[nswap]; int rnswap = swapctl(SWAP_STATS, swdev, nswap); /* Total things up */ unsigned long long int total = 0, used = 0; for (int i = 0; i < rnswap; i++) { if (swdev[i].se_flags & SWF_ENABLE) { used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); } } pl->totalSwap = total; pl->usedSwap = used; } else { pl->totalSwap = pl->usedSwap = 0; } } static char* OpenBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) { /* * Like OpenBSD's top(1), we try to fall back to the command name * (argv[0]) if we fail to construct the full command. */ char** arg = kvm_getargv(kd, kproc, 500); if (arg == NULL || *arg == NULL) { *basenameEnd = strlen(kproc->p_comm); return xStrdup(kproc->p_comm); } size_t len = 0; for (int i = 0; arg[i] != NULL; i++) { len += strlen(arg[i]) + 1; /* room for arg and trailing space or NUL */ } /* don't use xMalloc here - we want to handle huge argv's gracefully */ char* s; if ((s = malloc(len)) == NULL) { *basenameEnd = strlen(kproc->p_comm); return xStrdup(kproc->p_comm); } *s = '\0'; for (int i = 0; arg[i] != NULL; i++) { size_t n = strlcat(s, arg[i], len); if (i == 0) { *basenameEnd = MINIMUM(n, len - 1); } /* the trailing space should get truncated anyway */ strlcat(s, " ", len); } return s; } /* * Taken from OpenBSD's ps(1). */ static double getpcpu(const struct kinfo_proc* kp) { if (fscale == 0) return 0.0; return 100.0 * (double)kp->p_pctcpu / fscale; } static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { const Settings* settings = this->super.settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; const struct kinfo_proc* kprocs = kvm_getprocs(this->kd, KERN_PROC_KTHREAD, 0, sizeof(struct kinfo_proc), &count); for (int i = 0; i < count; i++) { const struct kinfo_proc* kproc = &kprocs[i]; bool preExisting = false; Process* proc = ProcessList_getProcess(&this->super, kproc->p_pid, &preExisting, OpenBSDProcess_new); //OpenBSDProcess* fp = (OpenBSDProcess*) proc; proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); if (!preExisting) { proc->ppid = kproc->p_ppid; proc->tpgid = kproc->p_tpgid; proc->tgid = kproc->p_pid; proc->session = kproc->p_sid; proc->tty_nr = kproc->p_tdev; proc->pgrp = kproc->p__pgid; proc->st_uid = kproc->p_uid; proc->starttime_ctime = kproc->p_ustart_sec; Process_fillStarttimeBuffer(proc); proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid); ProcessList_add(&this->super, proc); proc->comm = OpenBSDProcessList_readProcessName(this->kd, kproc, &proc->basenameOffset); } else { if (settings->updateProcessNames) { free(proc->comm); proc->comm = OpenBSDProcessList_readProcessName(this->kd, kproc, &proc->basenameOffset); } } proc->m_virt = kproc->p_vm_dsize * pageSizeKB; proc->m_resident = kproc->p_vm_rssize * pageSizeKB; proc->percent_mem = proc->m_resident / (double)(this->super.totalMem) * 100.0; proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount * 100.0); //proc->nlwp = kproc->p_numthreads; proc->nice = kproc->p_nice - 20; proc->time = 100 * (kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000)); proc->priority = kproc->p_priority - PZERO; switch (kproc->p_stat) { case SIDL: proc->state = 'I'; break; case SRUN: proc->state = 'R'; break; case SSLEEP: proc->state = 'S'; break; case SSTOP: proc->state = 'T'; break; case SZOMB: proc->state = 'Z'; break; case SDEAD: proc->state = 'D'; break; case SONPROC: proc->state = 'P'; break; default: proc->state = '?'; } if (Process_isKernelThread(proc)) { this->super.kernelThreads++; } this->super.totalTasks++; // SRUN ('R') means runnable, not running if (proc->state == 'P') { this->super.runningTasks++; } proc->updated = true; } } static unsigned long long saturatingSub(unsigned long long a, unsigned long long b) { return a > b ? a - b : 0; } static void getKernelCPUTimes(int cpuId, u_int64_t* times) { const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId }; size_t length = sizeof(*times) * CPUSTATES; if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { CRT_fatalError("sysctl kern.cp_time2 failed"); } } static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { unsigned long long totalTime = 0; for (int i = 0; i < CPUSTATES; i++) { totalTime += times[i]; } unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; // XXX Not sure if CP_SPIN should be added to sysAllTime. // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c #ifdef CP_SPIN sysAllTime += times[CP_SPIN]; #endif cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); #ifdef CP_SPIN cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime); #endif cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); cpu->totalTime = totalTime; cpu->userTime = times[CP_USER]; cpu->niceTime = times[CP_NICE]; cpu->sysTime = times[CP_SYS]; cpu->sysAllTime = sysAllTime; #ifdef CP_SPIN cpu->spinTime = times[CP_SPIN]; #endif cpu->intrTime = times[CP_INTR]; cpu->idleTime = times[CP_IDLE]; } static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { u_int64_t kernelTimes[CPUSTATES] = {0}; u_int64_t avg[CPUSTATES] = {0}; for (int i = 0; i < this->super.cpuCount; i++) { getKernelCPUTimes(i, kernelTimes); CPUData* cpu = this->cpus + i + 1; kernelCPUTimesToHtop(kernelTimes, cpu); avg[CP_USER] += cpu->userTime; avg[CP_NICE] += cpu->niceTime; avg[CP_SYS] += cpu->sysTime; #ifdef CP_SPIN avg[CP_SPIN] += cpu->spinTime; #endif avg[CP_INTR] += cpu->intrTime; avg[CP_IDLE] += cpu->idleTime; } for (int i = 0; i < CPUSTATES; i++) { avg[i] /= this->super.cpuCount; } kernelCPUTimesToHtop(avg, this->cpus); } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { OpenBSDProcessList* opl = (OpenBSDProcessList*) super; OpenBSDProcessList_scanMemoryInfo(super); OpenBSDProcessList_scanCPUTime(opl); // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { return; } OpenBSDProcessList_scanProcs(opl); } htop-3.0.5/openbsd/OpenBSDProcessList.h000066400000000000000000000025331377712513700177410ustar00rootroot00000000000000#ifndef HEADER_OpenBSDProcessList #define HEADER_OpenBSDProcessList /* htop - OpenBSDProcessList.h (C) 2014 Hisham H. Muhammad (C) 2015 Michael McConville Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" typedef struct CPUData_ { unsigned long long int totalTime; unsigned long long int userTime; unsigned long long int niceTime; unsigned long long int sysTime; unsigned long long int sysAllTime; unsigned long long int spinTime; unsigned long long int intrTime; unsigned long long int idleTime; unsigned long long int totalPeriod; unsigned long long int userPeriod; unsigned long long int nicePeriod; unsigned long long int sysPeriod; unsigned long long int sysAllPeriod; unsigned long long int spinPeriod; unsigned long long int intrPeriod; unsigned long long int idlePeriod; } CPUData; typedef struct OpenBSDProcessList_ { ProcessList super; kvm_t* kd; CPUData* cpus; } OpenBSDProcessList; ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); #endif htop-3.0.5/openbsd/Platform.c000066400000000000000000000226641377712513700161020ustar00rootroot00000000000000/* htop - openbsd/Platform.c (C) 2014 Hisham H. Muhammad (C) 2015 Michael McConville Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CPUMeter.h" #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" #include "MemoryMeter.h" #include "Meter.h" #include "OpenBSDProcess.h" #include "OpenBSDProcessList.h" #include "ProcessList.h" #include "Settings.h" #include "SignalsPanel.h" #include "SwapMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; /* * See /usr/include/sys/signal.h */ const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, { .name = " 2 SIGINT", .number = 2 }, { .name = " 3 SIGQUIT", .number = 3 }, { .name = " 4 SIGILL", .number = 4 }, { .name = " 5 SIGTRAP", .number = 5 }, { .name = " 6 SIGABRT", .number = 6 }, { .name = " 6 SIGIOT", .number = 6 }, { .name = " 7 SIGEMT", .number = 7 }, { .name = " 8 SIGFPE", .number = 8 }, { .name = " 9 SIGKILL", .number = 9 }, { .name = "10 SIGBUS", .number = 10 }, { .name = "11 SIGSEGV", .number = 11 }, { .name = "12 SIGSYS", .number = 12 }, { .name = "13 SIGPIPE", .number = 13 }, { .name = "14 SIGALRM", .number = 14 }, { .name = "15 SIGTERM", .number = 15 }, { .name = "16 SIGURG", .number = 16 }, { .name = "17 SIGSTOP", .number = 17 }, { .name = "18 SIGTSTP", .number = 18 }, { .name = "19 SIGCONT", .number = 19 }, { .name = "20 SIGCHLD", .number = 20 }, { .name = "21 SIGTTIN", .number = 21 }, { .name = "22 SIGTTOU", .number = 22 }, { .name = "23 SIGIO", .number = 23 }, { .name = "24 SIGXCPU", .number = 24 }, { .name = "25 SIGXFSZ", .number = 25 }, { .name = "26 SIGVTALRM", .number = 26 }, { .name = "27 SIGPROF", .number = 27 }, { .name = "28 SIGWINCH", .number = 28 }, { .name = "29 SIGINFO", .number = 29 }, { .name = "30 SIGUSR1", .number = 30 }, { .name = "31 SIGUSR2", .number = 31 }, { .name = "32 SIGTHR", .number = 32 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, &LoadAverageMeter_class, &LoadMeter_class, &MemoryMeter_class, &SwapMeter_class, &TasksMeter_class, &UptimeMeter_class, &BatteryMeter_class, &HostnameMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, &AllCPUs4Meter_class, &AllCPUs8Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, &LeftCPUs4Meter_class, &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &BlankMeter_class, NULL }; void Platform_init(void) { /* no platform-specific setup needed */ } void Platform_done(void) { /* no platform-specific cleanup needed */ } void Platform_setBindings(Htop_Action* keys) { /* no platform-specific key bindings */ (void) keys; } int Platform_getUptime() { struct timeval bootTime, currTime; const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); int err = sysctl(mib, 2, &bootTime, &size, NULL, 0); if (err) { return -1; } gettimeofday(&currTime, NULL); return (int) difftime(currTime.tv_sec, bootTime.tv_sec); } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { struct loadavg loadAverage; const int mib[2] = { CTL_VM, VM_LOADAVG }; size_t size = sizeof(loadAverage); int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0); if (err) { *one = 0; *five = 0; *fifteen = 0; return; } *one = (double) loadAverage.ldavg[0] / loadAverage.fscale; *five = (double) loadAverage.ldavg[1] / loadAverage.fscale; *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } int Platform_getMaxPid() { // this is hard-coded in sys/proc.h - no sysctl exists return 99999; } double Platform_setCPUValues(Meter* this, int cpu) { const OpenBSDProcessList* pl = (const OpenBSDProcessList*) this->pl; const CPUData* cpuData = &(pl->cpus[cpu]); double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; double totalPercent; double* v = this->values; v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; if (this->pl->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->sysPeriod / total * 100.0; v[CPU_METER_IRQ] = cpuData->intrPeriod / total * 100.0; v[CPU_METER_SOFTIRQ] = 0.0; v[CPU_METER_STEAL] = 0.0; v[CPU_METER_GUEST] = 0.0; v[CPU_METER_IOWAIT] = 0.0; v[CPU_METER_FREQUENCY] = NAN; this->curItems = 8; totalPercent = v[0] + v[1] + v[2] + v[3]; } else { v[2] = cpuData->sysAllPeriod / total * 100.0; v[3] = 0.0; // No steal nor guest on OpenBSD totalPercent = v[0] + v[1] + v[2]; this->curItems = 4; } totalPercent = CLAMP(totalPercent, 0.0, 100.0); v[CPU_METER_TEMPERATURE] = NAN; return totalPercent; } void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; long int usedMem = pl->usedMem; long int buffersMem = pl->buffersMem; long int cachedMem = pl->cachedMem; usedMem -= buffersMem + cachedMem; this->total = pl->totalMem; this->values[0] = usedMem; this->values[1] = buffersMem; this->values[2] = cachedMem; } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[0] = pl->usedSwap; } char* Platform_getProcessEnv(pid_t pid) { char errbuf[_POSIX2_LINE_MAX]; char* env; char** ptr; int count; kvm_t* kt; struct kinfo_proc* kproc; size_t capacity = 4096, size = 0; if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) { return NULL; } if ((kproc = kvm_getprocs(kt, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &count)) == NULL) { (void) kvm_close(kt); return NULL; } if ((ptr = kvm_getenvv(kt, kproc, 0)) == NULL) { (void) kvm_close(kt); return NULL; } env = xMalloc(capacity); for (char** p = ptr; *p; p++) { size_t len = strlen(*p) + 1; if (size + len > capacity) { capacity *= 2; env = xRealloc(env, capacity); } strlcpy(env + size, *p, len); size += len; } if (size < 2 || env[size - 1] || env[size - 2]) { if (size + 2 < capacity) env = xRealloc(env, capacity + 2); env[size] = 0; env[size + 1] = 0; } (void) kvm_close(kt); return env; } char* Platform_getInodeFilename(pid_t pid, ino_t inode) { (void)pid; (void)inode; return NULL; } FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; } bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; return false; } bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted) { // TODO *bytesReceived = 0; *packetsReceived = 0; *bytesTransmitted = 0; *packetsTransmitted = 0; return false; } static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) { for (int devn = 0;; devn++) { mib[2] = devn; if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) { if (errno == ENXIO) continue; if (errno == ENOENT) return false; } if (strcmp(name, snsrdev->xname) == 0) { return true; } } } void Platform_getBattery(double* percent, ACPresence* isOnAC) { int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0}; struct sensor s; size_t slen = sizeof(struct sensor); struct sensordev snsrdev; size_t sdlen = sizeof(struct sensordev); bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen); *percent = NAN; if (found) { /* last full capacity */ mib[3] = 7; mib[4] = 0; double last_full_capacity = 0; if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) last_full_capacity = s.value; if (last_full_capacity > 0) { /* remaining capacity */ mib[3] = 7; mib[4] = 3; if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) { double charge = s.value; *percent = 100 * (charge / last_full_capacity); if (charge >= last_full_capacity) { *percent = 100; } } } } found = findDevice("acpiac0", mib, &snsrdev, &sdlen); *isOnAC = AC_ERROR; if (found) { mib[3] = 9; mib[4] = 0; if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) *isOnAC = s.value; } } htop-3.0.5/openbsd/Platform.h000066400000000000000000000031031377712513700160720ustar00rootroot00000000000000#ifndef HEADER_Platform #define HEADER_Platform /* htop - openbsd/Platform.h (C) 2014 Hisham H. Muhammad (C) 2015 Michael McConville Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "Meter.h" #include "Process.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" extern const ProcessField Platform_defaultFields[]; /* see /usr/include/sys/signal.h */ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; extern const MeterClass* const Platform_meterTypes[]; void Platform_init(void); void Platform_done(void); void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(void); double Platform_setCPUValues(Meter* this, int cpu); void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); char* Platform_getInodeFilename(pid_t pid, ino_t inode); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted); void Platform_getBattery(double* percent, ACPresence* isOnAC); #endif htop-3.0.5/openbsd/ProcessField.h000066400000000000000000000005031377712513700166710ustar00rootroot00000000000000#ifndef HEADER_OpenBSDProcessField #define HEADER_OpenBSDProcessField /* htop - openbsd/ProcessField.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define PLATFORM_PROCESS_FIELDS \ // End of list #endif /* HEADER_OpenBSDProcessField */ htop-3.0.5/scripts/000077500000000000000000000000001377712513700141755ustar00rootroot00000000000000htop-3.0.5/scripts/htop_suppressions.valgrind000066400000000000000000000017021377712513700215340ustar00rootroot00000000000000{ Memcheck:Leak match-leak-kinds: reachable ... fun:CRT_init fun:main } { Memcheck:Leak match-leak-kinds: reachable ... fun:CRT_init } { Memcheck:Leak match-leak-kinds: reachable ... fun:wgetch fun:ScreenManager_run fun:Action_runSetup fun:actionSetup fun:MainPanel_eventHandler fun:ScreenManager_run fun:main } { Memcheck:Leak match-leak-kinds: reachable ... fun:wgetch fun:ScreenManager_run fun:main } { Memcheck:Leak match-leak-kinds: reachable ... fun:wrefresh fun:main } { Memcheck:Leak match-leak-kinds: reachable fun:realloc fun:_nc_doalloc fun:_nc_tparm_analyze fun:tparm ... fun:doupdate_sp fun:wrefresh obj:* } htop-3.0.5/scripts/run_valgrind.sh000077500000000000000000000004101377712513700172210ustar00rootroot00000000000000#!/bin/sh SCRIPT=$(readlink -f "$0") SCRIPTDIR=$(dirname "$SCRIPT") valgrind --leak-check=full --show-reachable=yes --show-leak-kinds=all --track-fds=yes --errors-for-leak-kinds=all --suppressions="${SCRIPTDIR}/htop_suppressions.valgrind" "${SCRIPTDIR}/../htop" htop-3.0.5/solaris/000077500000000000000000000000001377712513700141625ustar00rootroot00000000000000htop-3.0.5/solaris/Platform.c000066400000000000000000000211671377712513700161210ustar00rootroot00000000000000/* htop - solaris/Platform.c (C) 2014 Hisham H. Muhammad (C) 2015 David C. Hunt (C) 2017,2018 Guy M. Broome Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Platform.h" #include "Macros.h" #include "Meter.h" #include "CPUMeter.h" #include "MemoryMeter.h" #include "SwapMeter.h" #include "TasksMeter.h" #include "LoadAverageMeter.h" #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" #include "HostnameMeter.h" #include "UptimeMeter.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" #include "SolarisProcess.h" #include "SolarisProcessList.h" #include #include #include #include #include #include #include #include #include #include double plat_loadavg[3] = {0}; const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, { .name = " 2 SIGINT", .number = 2 }, { .name = " 3 SIGQUIT", .number = 3 }, { .name = " 4 SIGILL", .number = 4 }, { .name = " 5 SIGTRAP", .number = 5 }, { .name = " 6 SIGABRT/IOT", .number = 6 }, { .name = " 7 SIGEMT", .number = 7 }, { .name = " 8 SIGFPE", .number = 8 }, { .name = " 9 SIGKILL", .number = 9 }, { .name = "10 SIGBUS", .number = 10 }, { .name = "11 SIGSEGV", .number = 11 }, { .name = "12 SIGSYS", .number = 12 }, { .name = "13 SIGPIPE", .number = 13 }, { .name = "14 SIGALRM", .number = 14 }, { .name = "15 SIGTERM", .number = 15 }, { .name = "16 SIGUSR1", .number = 16 }, { .name = "17 SIGUSR2", .number = 17 }, { .name = "18 SIGCHLD/CLD", .number = 18 }, { .name = "19 SIGPWR", .number = 19 }, { .name = "20 SIGWINCH", .number = 20 }, { .name = "21 SIGURG", .number = 21 }, { .name = "22 SIGPOLL/IO", .number = 22 }, { .name = "23 SIGSTOP", .number = 23 }, { .name = "24 SIGTSTP", .number = 24 }, { .name = "25 SIGCONT", .number = 25 }, { .name = "26 SIGTTIN", .number = 26 }, { .name = "27 SIGTTOU", .number = 27 }, { .name = "28 SIGVTALRM", .number = 28 }, { .name = "29 SIGPROF", .number = 29 }, { .name = "30 SIGXCPU", .number = 30 }, { .name = "31 SIGXFSZ", .number = 31 }, { .name = "32 SIGWAITING", .number = 32 }, { .name = "33 SIGLWP", .number = 33 }, { .name = "34 SIGFREEZE", .number = 34 }, { .name = "35 SIGTHAW", .number = 35 }, { .name = "36 SIGCANCEL", .number = 36 }, { .name = "37 SIGLOST", .number = 37 }, { .name = "38 SIGXRES", .number = 38 }, { .name = "39 SIGJVM1", .number = 39 }, { .name = "40 SIGJVM2", .number = 40 }, { .name = "41 SIGINFO", .number = 41 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const ProcessField Platform_defaultFields[] = { PID, LWPID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, &LoadAverageMeter_class, &LoadMeter_class, &MemoryMeter_class, &SwapMeter_class, &TasksMeter_class, &BatteryMeter_class, &HostnameMeter_class, &UptimeMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, &AllCPUs4Meter_class, &AllCPUs8Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, &LeftCPUs4Meter_class, &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, &BlankMeter_class, NULL }; void Platform_init(void) { /* no platform-specific setup needed */ } void Platform_done(void) { /* no platform-specific cleanup needed */ } void Platform_setBindings(Htop_Action* keys) { /* no platform-specific key bindings */ (void) keys; } int Platform_getUptime() { int boot_time = 0; int curr_time = time(NULL); struct utmpx* ent; while (( ent = getutxent() )) { if ( !strcmp("system boot", ent->ut_line )) { boot_time = ent->ut_tv.tv_sec; } } endutxent(); return (curr_time - boot_time); } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { getloadavg( plat_loadavg, 3 ); *one = plat_loadavg[LOADAVG_1MIN]; *five = plat_loadavg[LOADAVG_5MIN]; *fifteen = plat_loadavg[LOADAVG_15MIN]; } int Platform_getMaxPid() { int vproc = 32778; // Reasonable Solaris default kstat_ctl_t* kc = kstat_open(); if (kc != NULL) { kstat_t* kshandle = kstat_lookup(kc, "unix", 0, "var"); if (kshandle != NULL) { kstat_read(kc, kshandle, NULL); kvar_t* ksvar = kshandle->ks_data; if (ksvar && ksvar->v_proc > 0) { vproc = ksvar->v_proc; } } kstat_close(kc); } return vproc; } double Platform_setCPUValues(Meter* this, int cpu) { const SolarisProcessList* spl = (const SolarisProcessList*) this->pl; int cpus = this->pl->cpuCount; const CPUData* cpuData = NULL; if (cpus == 1) { // single CPU box has everything in spl->cpus[0] cpuData = &(spl->cpus[0]); } else { cpuData = &(spl->cpus[cpu]); } double percent; double* v = this->values; v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; if (this->pl->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; percent = v[0] + v[1] + v[2] + v[3]; } else { v[2] = cpuData->systemAllPercent; this->curItems = 3; percent = v[0] + v[1] + v[2]; } percent = isnan(percent) ? 0.0 : CLAMP(percent, 0.0, 100.0); v[CPU_METER_FREQUENCY] = cpuData->frequency; v[CPU_METER_TEMPERATURE] = NAN; return percent; } void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalMem; this->values[0] = pl->usedMem; this->values[1] = pl->buffersMem; this->values[2] = pl->cachedMem; } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[0] = pl->usedSwap; } void Platform_setZfsArcValues(Meter* this) { const SolarisProcessList* spl = (const SolarisProcessList*) this->pl; ZfsArcMeter_readStats(this, &(spl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { const SolarisProcessList* spl = (const SolarisProcessList*) this->pl; ZfsCompressedArcMeter_readStats(this, &(spl->zfs)); } static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr_t addr, const char* str) { envAccum* accump = accum; (void) Phandle; (void) addr; size_t thissz = strlen(str); if ((thissz + 2) > (accump->capacity - accump->size)) { accump->env = xRealloc(accump->env, accump->capacity *= 2); } if ((thissz + 2) > (accump->capacity - accump->size)) { return 1; } strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size)); strncpy( accump->env + accump->size + thissz + 1, "\n", 1); accump->size = accump->size + thissz + 1; return 0; } char* Platform_getProcessEnv(pid_t pid) { envAccum envBuilder; pid_t realpid = pid / 1024; int graberr; struct ps_prochandle* Phandle; if ((Phandle = Pgrab(realpid, PGRAB_RDONLY, &graberr)) == NULL) { return "Unable to read process environment."; } envBuilder.capacity = 4096; envBuilder.size = 0; envBuilder.env = xMalloc(envBuilder.capacity); (void) Penv_iter(Phandle, Platform_buildenv, &envBuilder); Prelease(Phandle, 0); strncpy( envBuilder.env + envBuilder.size, "\0", 1); return envBuilder.env; } char* Platform_getInodeFilename(pid_t pid, ino_t inode) { (void)pid; (void)inode; return NULL; } FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; } bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; return false; } bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted) { // TODO *bytesReceived = 0; *packetsReceived = 0; *bytesTransmitted = 0; *packetsTransmitted = 0; return false; } void Platform_getBattery(double* percent, ACPresence* isOnAC) { *percent = NAN; *isOnAC = AC_ERROR; } htop-3.0.5/solaris/Platform.h000066400000000000000000000036441377712513700161260ustar00rootroot00000000000000#ifndef HEADER_Platform #define HEADER_Platform /* htop - solaris/Platform.h (C) 2014 Hisham H. Muhammad (C) 2015 David C. Hunt (C) 2017,2018 Guy M. Broome Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include #include #include #include #include #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" #define kill(pid, signal) kill(pid / 1024, signal) typedef struct var kvar_t; typedef struct envAccum_ { size_t capacity; size_t size; size_t bytes; char* env; } envAccum; extern double plat_loadavg[3]; extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; extern const ProcessField Platform_defaultFields[]; extern const MeterClass* const Platform_meterTypes[]; void Platform_init(void); void Platform_done(void); void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(void); double Platform_setCPUValues(Meter* this, int cpu); void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); void Platform_setZfsArcValues(Meter* this); void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); char* Platform_getInodeFilename(pid_t pid, ino_t inode); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted); void Platform_getBattery(double* percent, ACPresence* isOnAC); #endif htop-3.0.5/solaris/ProcessField.h000066400000000000000000000010701377712513700167130ustar00rootroot00000000000000#ifndef HEADER_SolarisProcessField #define HEADER_SolarisProcessField /* htop - solaris/ProcessField.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define PLATFORM_PROCESS_FIELDS \ ZONEID = 100, \ ZONE = 101, \ PROJID = 102, \ TASKID = 103, \ POOLID = 104, \ CONTID = 105, \ LWPID = 106, \ // End of list #endif /* HEADER_SolarisProcessField */ htop-3.0.5/solaris/SolarisProcess.c000066400000000000000000000171671377712513700173150ustar00rootroot00000000000000/* htop - SolarisProcess.c (C) 2015 Hisham H. Muhammad (C) 2017,2018 Guy M. Broome Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Process.h" #include "ProcessList.h" #include "SolarisProcess.h" #include "Platform.h" #include "CRT.h" #include #include #include #include const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, O onproc, Z zombie, T stopped, W waiting)", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, [ZONEID] = { .name = "ZONEID", .title = "ZONEID", .description = "Zone ID", .flags = 0, .pidColumn = true, }, [ZONE] = { .name = "ZONE", .title = "ZONE ", .description = "Zone name", .flags = 0, }, [PROJID] = { .name = "PROJID", .title = "PRJID", .description = "Project ID", .flags = 0, .pidColumn = true, }, [TASKID] = { .name = "TASKID", .title = "TSKID", .description = "Task ID", .flags = 0, .pidColumn = true, }, [POOLID] = { .name = "POOLID", .title = "POLID", .description = "Pool ID", .flags = 0, .pidColumn = true, }, [CONTID] = { .name = "CONTID", .title = "CNTID", .description = "Contract ID", .flags = 0, .pidColumn = true, }, [LWPID] = { .name = "LWPID", .title = "LWPID", .description = "LWP ID", .flags = 0, .pidColumn = true, }, }; Process* SolarisProcess_new(const Settings* settings) { SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess)); Object_setClass(this, Class(SolarisProcess)); Process_init(&this->super, settings); return &this->super; } void Process_delete(Object* cast) { SolarisProcess* sp = (SolarisProcess*) cast; Process_done((Process*)cast); free(sp->zname); free(sp); } static void SolarisProcess_writeField(const Process* this, RichString* str, ProcessField field) { const SolarisProcess* sp = (const SolarisProcess*) this; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; switch (field) { // add Solaris-specific fields here case ZONEID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->zoneid); break; case PROJID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->projid); break; case TASKID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->taskid); break; case POOLID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->poolid); break; case CONTID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->contid); break; case ZONE: Process_printLeftAlignedField(str, attr, sp->zname ? sp->zname : "global", ZONENAME_MAX/4); return; case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realpid); break; case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realppid); break; case LWPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->lwpid); break; default: Process_writeField(this, str, field); return; } RichString_appendWide(str, attr, buffer); } static int SolarisProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { const SolarisProcess* p1 = (const SolarisProcess*)v1; const SolarisProcess* p2 = (const SolarisProcess*)v2; switch (key) { case ZONEID: return SPACESHIP_NUMBER(p1->zoneid, p2->zoneid); case PROJID: return SPACESHIP_NUMBER(p1->projid, p2->projid); case TASKID: return SPACESHIP_NUMBER(p1->taskid, p2->taskid); case POOLID: return SPACESHIP_NUMBER(p1->poolid, p2->poolid); case CONTID: return SPACESHIP_NUMBER(p1->contid, p2->contid); case ZONE: return strcmp(p1->zname ? p1->zname : "global", p2->zname ? p2->zname : "global"); case PID: return SPACESHIP_NUMBER(p1->realpid, p2->realpid); case PPID: return SPACESHIP_NUMBER(p1->realppid, p2->realppid); case LWPID: return SPACESHIP_NUMBER(p1->lwpid, p2->lwpid); default: return Process_compareByKey_Base(v1, v2, key); } } bool Process_isThread(const Process* this) { const SolarisProcess* fp = (const SolarisProcess*) this; if (fp->kernel == 1 ) { return 1; } else if (fp->is_lwp) { return 1; } else { return 0; } } const ProcessClass SolarisProcess_class = { .super = { .extends = Class(Process), .display = Process_display, .delete = Process_delete, .compare = Process_compare }, .writeField = SolarisProcess_writeField, .compareByKey = SolarisProcess_compareByKey }; htop-3.0.5/solaris/SolarisProcess.h000066400000000000000000000020271377712513700173070ustar00rootroot00000000000000#ifndef HEADER_SolarisProcess #define HEADER_SolarisProcess /* htop - SolarisProcess.h (C) 2015 Hisham H. Muhammad (C) 2017,2018 Guy M. Broome Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Settings.h" #include #include #include typedef struct SolarisProcess_ { Process super; int kernel; zoneid_t zoneid; char* zname; taskid_t taskid; projid_t projid; poolid_t poolid; ctid_t contid; bool is_lwp; pid_t realpid; pid_t realppid; pid_t lwpid; } SolarisProcess; #define Process_isKernelThread(_process) (_process->kernel == 1) #define Process_isUserlandThread(_process) (_process->pid != _process->tgid) extern const ProcessClass SolarisProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; Process* SolarisProcess_new(const Settings* settings); void Process_delete(Object* cast); bool Process_isThread(const Process* this); #endif htop-3.0.5/solaris/SolarisProcessList.c000066400000000000000000000372641377712513700201510ustar00rootroot00000000000000/* htop - SolarisProcessList.c (C) 2014 Hisham H. Muhammad (C) 2017,2018 Guy M. Broome Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ProcessList.h" #include "SolarisProcess.h" #include "SolarisProcessList.h" #include #include #include #include #include #include #include #include #include #include #include #include "CRT.h" #define MAXCMDLINE 255 static int pageSize; static int pageSizeKB; char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) { char* zname; if ( sproc->zoneid == 0 ) { zname = xStrdup(GZONE); } else if ( kd == NULL ) { zname = xStrdup(UZONE); } else { kstat_t* ks = kstat_lookup( kd, "zones", sproc->zoneid, NULL ); zname = xStrdup(ks == NULL ? UZONE : ks->ks_name); } return zname; } ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); ProcessList* pl = (ProcessList*) spl; ProcessList_init(pl, Class(SolarisProcess), usersTable, pidMatchList, userId); spl->kd = kstat_open(); pageSize = sysconf(_SC_PAGESIZE); if (pageSize == -1) CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); pageSizeKB = pageSize / 1024; pl->cpuCount = sysconf(_SC_NPROCESSORS_ONLN); if (pl->cpuCount == -1) CRT_fatalError("Cannot get CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); else if (pl->cpuCount == 1) spl->cpus = xRealloc(spl->cpus, sizeof(CPUData)); else spl->cpus = xRealloc(spl->cpus, (pl->cpuCount + 1) * sizeof(CPUData)); return pl; } static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { const SolarisProcessList* spl = (SolarisProcessList*) pl; int cpus = pl->cpuCount; kstat_t* cpuinfo = NULL; kstat_named_t* idletime = NULL; kstat_named_t* intrtime = NULL; kstat_named_t* krnltime = NULL; kstat_named_t* usertime = NULL; kstat_named_t* cpu_freq = NULL; double idlebuf = 0; double intrbuf = 0; double krnlbuf = 0; double userbuf = 0; int arrskip = 0; assert(cpus > 0); if (cpus > 1) { // Store values for the stats loop one extra element up in the array // to leave room for the average to be calculated afterwards arrskip++; } // Calculate per-CPU statistics first for (int i = 0; i < cpus; i++) { if (spl->kd != NULL) { if ((cpuinfo = kstat_lookup(spl->kd, "cpu", i, "sys")) != NULL) { if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { idletime = kstat_data_lookup(cpuinfo, "cpu_nsec_idle"); intrtime = kstat_data_lookup(cpuinfo, "cpu_nsec_intr"); krnltime = kstat_data_lookup(cpuinfo, "cpu_nsec_kernel"); usertime = kstat_data_lookup(cpuinfo, "cpu_nsec_user"); } } } assert( (idletime != NULL) && (intrtime != NULL) && (krnltime != NULL) && (usertime != NULL) ); if (pl->settings->showCPUFrequency) { if (spl->kd != NULL) { if ((cpuinfo = kstat_lookup(spl->kd, "cpu_info", i, NULL)) != NULL) { if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { cpu_freq = kstat_data_lookup(cpuinfo, "current_clock_Hz"); } } } assert( cpu_freq != NULL ); } CPUData* cpuData = &(spl->cpus[i + arrskip]); uint64_t totaltime = (idletime->value.ui64 - cpuData->lidle) + (intrtime->value.ui64 - cpuData->lintr) + (krnltime->value.ui64 - cpuData->lkrnl) + (usertime->value.ui64 - cpuData->luser); // Calculate percentages of deltas since last reading cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0; cpuData->nicePercent = (double)0.0; // Not implemented on Solaris cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0; cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0; cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0; // Store current values to use for the next round of deltas cpuData->luser = usertime->value.ui64; cpuData->lkrnl = krnltime->value.ui64; cpuData->lintr = intrtime->value.ui64; cpuData->lidle = idletime->value.ui64; // Add frequency in MHz cpuData->frequency = pl->settings->showCPUFrequency ? (double)cpu_freq->value.ui64 / 1E6 : NAN; // Accumulate the current percentages into buffers for later average calculation if (cpus > 1) { userbuf += cpuData->userPercent; krnlbuf += cpuData->systemPercent; intrbuf += cpuData->irqPercent; idlebuf += cpuData->idlePercent; } } if (cpus > 1) { CPUData* cpuData = &(spl->cpus[0]); cpuData->userPercent = userbuf / cpus; cpuData->nicePercent = (double)0.0; // Not implemented on Solaris cpuData->systemPercent = krnlbuf / cpus; cpuData->irqPercent = intrbuf / cpus; cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; cpuData->idlePercent = idlebuf / cpus; } } static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { SolarisProcessList* spl = (SolarisProcessList*) pl; static kstat_t *meminfo = NULL; int ksrphyserr = -1; kstat_named_t *totalmem_pgs = NULL; kstat_named_t *freemem_pgs = NULL; kstat_named_t *pages = NULL; struct swaptable *sl = NULL; struct swapent *swapdev = NULL; uint64_t totalswap = 0; uint64_t totalfree = 0; int nswap = 0; char *spath = NULL; char *spathbase = NULL; // Part 1 - physical memory if (spl->kd != NULL && meminfo == NULL) { // Look up the kstat chain just once, it never changes meminfo = kstat_lookup(spl->kd, "unix", 0, "system_pages"); } if (meminfo != NULL) { ksrphyserr = kstat_read(spl->kd, meminfo, NULL); } if (ksrphyserr != -1) { totalmem_pgs = kstat_data_lookup(meminfo, "physmem"); freemem_pgs = kstat_data_lookup(meminfo, "freemem"); pages = kstat_data_lookup(meminfo, "pagestotal"); pl->totalMem = totalmem_pgs->value.ui64 * pageSizeKB; if (pl->totalMem > freemem_pgs->value.ui64 * pageSizeKB) { pl->usedMem = pl->totalMem - freemem_pgs->value.ui64 * pageSizeKB; } else { pl->usedMem = 0; // This can happen in non-global zone (in theory) } // Not sure how to implement this on Solaris - suggestions welcome! pl->cachedMem = 0; // Not really "buffers" but the best Solaris analogue that I can find to // "memory in use but not by programs or the kernel itself" pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * pageSizeKB; } else { // Fall back to basic sysconf if kstat isn't working pl->totalMem = sysconf(_SC_PHYS_PAGES) * pageSize; pl->buffersMem = 0; pl->cachedMem = 0; pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * pageSize); } // Part 2 - swap nswap = swapctl(SC_GETNSWP, NULL); if (nswap > 0) { sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); } if (sl != NULL) { spathbase = xMalloc( nswap * MAXPATHLEN ); } if (spathbase != NULL) { spath = spathbase; swapdev = sl->swt_ent; for (int i = 0; i < nswap; i++, swapdev++) { swapdev->ste_path = spath; spath += MAXPATHLEN; } sl->swt_n = nswap; } nswap = swapctl(SC_LIST, sl); if (nswap > 0) { swapdev = sl->swt_ent; for (int i = 0; i < nswap; i++, swapdev++) { totalswap += swapdev->ste_pages; totalfree += swapdev->ste_free; } } free(spathbase); free(sl); pl->totalSwap = totalswap * pageSizeKB; pl->usedSwap = pl->totalSwap - (totalfree * pageSizeKB); } static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) { SolarisProcessList* spl = (SolarisProcessList*) pl; kstat_t *arcstats = NULL; int ksrphyserr = -1; kstat_named_t *cur_kstat = NULL; if (spl->kd != NULL) { arcstats = kstat_lookup(spl->kd, "zfs", 0, "arcstats"); } if (arcstats != NULL) { ksrphyserr = kstat_read(spl->kd, arcstats, NULL); } if (ksrphyserr != -1) { cur_kstat = kstat_data_lookup( arcstats, "size" ); spl->zfs.size = cur_kstat->value.ui64 / 1024; spl->zfs.enabled = spl->zfs.size > 0 ? 1 : 0; cur_kstat = kstat_data_lookup( arcstats, "c_max" ); spl->zfs.max = cur_kstat->value.ui64 / 1024; cur_kstat = kstat_data_lookup( arcstats, "mfu_size" ); spl->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; cur_kstat = kstat_data_lookup( arcstats, "mru_size" ); spl->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; cur_kstat = kstat_data_lookup( arcstats, "anon_size" ); spl->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; cur_kstat = kstat_data_lookup( arcstats, "hdr_size" ); spl->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; cur_kstat = kstat_data_lookup( arcstats, "other_size" ); spl->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; if ((cur_kstat = kstat_data_lookup( arcstats, "compressed_size" )) != NULL) { spl->zfs.compressed = cur_kstat->value.ui64 / 1024; spl->zfs.isCompressed = 1; cur_kstat = kstat_data_lookup( arcstats, "uncompressed_size" ); spl->zfs.uncompressed = cur_kstat->value.ui64 / 1024; } else { spl->zfs.isCompressed = 0; } } } void ProcessList_delete(ProcessList* pl) { SolarisProcessList* spl = (SolarisProcessList*) pl; ProcessList_done(pl); free(spl->cpus); if (spl->kd) { kstat_close(spl->kd); } free(spl); } /* NOTE: the following is a callback function of type proc_walk_f * and MUST conform to the appropriate definition in order * to work. See libproc(3LIB) on a Solaris or Illumos * system for more info. */ int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, void* listptr) { bool preExisting; pid_t getpid; // Setup process list ProcessList* pl = (ProcessList*) listptr; SolarisProcessList* spl = (SolarisProcessList*) listptr; id_t lwpid_real = _lwpsinfo->pr_lwpid; if (lwpid_real > 1023) { return 0; } pid_t lwpid = (_psinfo->pr_pid * 1024) + lwpid_real; bool onMasterLWP = (_lwpsinfo->pr_lwpid == _psinfo->pr_lwp.pr_lwpid); if (onMasterLWP) { getpid = _psinfo->pr_pid * 1024; } else { getpid = lwpid; } Process* proc = ProcessList_getProcess(pl, getpid, &preExisting, SolarisProcess_new); SolarisProcess* sproc = (SolarisProcess*) proc; // Common code pass 1 proc->show = false; sproc->taskid = _psinfo->pr_taskid; sproc->projid = _psinfo->pr_projid; sproc->poolid = _psinfo->pr_poolid; sproc->contid = _psinfo->pr_contract; proc->priority = _lwpsinfo->pr_pri; proc->nice = _lwpsinfo->pr_nice - NZERO; proc->processor = _lwpsinfo->pr_onpro; proc->state = _lwpsinfo->pr_sname; // NOTE: This 'percentage' is a 16-bit BINARY FRACTIONS where 1.0 = 0x8000 // Source: https://docs.oracle.com/cd/E19253-01/816-5174/proc-4/index.html // (accessed on 18 November 2017) proc->percent_mem = ((uint16_t)_psinfo->pr_pctmem / (double)32768) * (double)100.0; proc->st_uid = _psinfo->pr_euid; proc->pgrp = _psinfo->pr_pgid; proc->nlwp = _psinfo->pr_nlwp; proc->tty_nr = _psinfo->pr_ttydev; proc->m_resident = _psinfo->pr_rssize; // KB proc->m_virt = _psinfo->pr_size; // KB if (!preExisting) { sproc->realpid = _psinfo->pr_pid; sproc->lwpid = lwpid_real; sproc->zoneid = _psinfo->pr_zoneid; sproc->zname = SolarisProcessList_readZoneName(spl->kd, sproc); proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid); proc->comm = xStrdup(_psinfo->pr_fname); proc->commLen = strnlen(_psinfo->pr_fname, PRFNSZ); } // End common code pass 1 if (onMasterLWP) { // Are we on the representative LWP? proc->ppid = (_psinfo->pr_ppid * 1024); proc->tgid = (_psinfo->pr_ppid * 1024); sproc->realppid = _psinfo->pr_ppid; // See note above (in common section) about this BINARY FRACTION proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu / (double)32768) * (double)100.0; proc->time = _psinfo->pr_time.tv_sec; if (!preExisting) { // Tasks done only for NEW processes sproc->is_lwp = false; proc->starttime_ctime = _psinfo->pr_start.tv_sec; } // Update proc and thread counts based on settings if (sproc->kernel && !pl->settings->hideKernelThreads) { pl->kernelThreads += proc->nlwp; pl->totalTasks += proc->nlwp + 1; if (proc->state == 'O') { pl->runningTasks++; } } else if (!sproc->kernel) { if (proc->state == 'O') { pl->runningTasks++; } if (pl->settings->hideUserlandThreads) { pl->totalTasks++; } else { pl->userlandThreads += proc->nlwp; pl->totalTasks += proc->nlwp + 1; } } proc->show = !(pl->settings->hideKernelThreads && sproc->kernel); } else { // We are not in the master LWP, so jump to the LWP handling code proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu / (double)32768) * (double)100.0; proc->time = _lwpsinfo->pr_time.tv_sec; if (!preExisting) { // Tasks done only for NEW LWPs sproc->is_lwp = true; proc->basenameOffset = -1; proc->ppid = _psinfo->pr_pid * 1024; proc->tgid = _psinfo->pr_pid * 1024; sproc->realppid = _psinfo->pr_pid; proc->starttime_ctime = _lwpsinfo->pr_start.tv_sec; } // Top-level process only gets this for the representative LWP if (sproc->kernel && !pl->settings->hideKernelThreads) { proc->show = true; } if (!sproc->kernel && !pl->settings->hideUserlandThreads) { proc->show = true; } } // Top-level LWP or subordinate LWP // Common code pass 2 if (!preExisting) { if ((sproc->realppid <= 0) && !(sproc->realpid <= 1)) { sproc->kernel = true; } else { sproc->kernel = false; } Process_fillStarttimeBuffer(proc); ProcessList_add(pl, proc); } proc->updated = true; // End common code pass 2 return 0; } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { SolarisProcessList_scanCPUTime(super); SolarisProcessList_scanMemoryInfo(super); SolarisProcessList_scanZfsArcstats(super); // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { return; } super->kernelThreads = 1; proc_walk(&SolarisProcessList_walkproc, super, PR_WALK_LWP); } htop-3.0.5/solaris/SolarisProcessList.h000066400000000000000000000031501377712513700201410ustar00rootroot00000000000000#ifndef HEADER_SolarisProcessList #define HEADER_SolarisProcessList /* htop - SolarisProcessList.h (C) 2014 Hisham H. Muhammad (C) 2017,2018 Guy M. Broome Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define MAXCMDLINE 255 #define GZONE "global " #define UZONE "unknown " #include "zfs/ZfsArcStats.h" #include #include #include #include #include #include #include #define ZONE_ERRMSGLEN 1024 extern char zone_errmsg[ZONE_ERRMSGLEN]; typedef struct CPUData_ { double userPercent; double nicePercent; double systemPercent; double irqPercent; double idlePercent; double systemAllPercent; double frequency; uint64_t luser; uint64_t lkrnl; uint64_t lintr; uint64_t lidle; } CPUData; typedef struct SolarisProcessList_ { ProcessList super; kstat_ctl_t* kd; CPUData* cpus; ZfsArcStats zfs; } SolarisProcessList; char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc); ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); /* NOTE: the following is a callback function of type proc_walk_f * and MUST conform to the appropriate definition in order * to work. See libproc(3LIB) on a Solaris or Illumos * system for more info. */ int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, void* listptr); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); #endif htop-3.0.5/test_spec.lua000077500000000000000000000427041377712513700152140ustar00rootroot00000000000000#!/usr/bin/env lua local VISUALDELAY = os.getenv("VISUALDELAY") local visual = VISUALDELAY or false local visual_delay = VISUALDELAY and (tonumber(VISUALDELAY)) or 0.1 local short_delay = 0.3 local long_delay = 1 local unistd = require("posix.unistd") local time = require("posix.time") local curses = require("posix.curses") local rote = require("rote") local rt = rote.RoteTerm(24, 80) --[[ local function os_execread(cmd) local fd = io.popen(cmd, "r") local out = fd:read("*a") fd:close() return (out:gsub("\n$", "")) end ]] --local branch = os_execread("git branch | grep '*'"):sub(3) --print("Running in branch "..branch) os.execute("make coverage") os.execute("rm -f *.gcda */*.gcda") os.execute("rm -f coverage.info test.htoprc") os.execute("rm -rf lcov") os.execute("killall htop") os.execute("ps aux | grep '[s]leep 12345' | awk '{print $2}' | xargs kill 2> /dev/null") os.execute("cp ./default.htoprc ./test.htoprc") rt:forkPty("LC_ALL=C HTOPRC=./test.htoprc ./htop 2> htop-valgrind.txt") local stdscr, term_win -- Curses initialization needed even when not in visual mode -- because luaposix only initializes KEY_* constants after initscr(). stdscr = curses.initscr() if visual then curses.echo(false) curses.start_color() curses.raw(true) curses.halfdelay(1) stdscr:keypad(true) term_win = curses.newwin(24, 80, 0, 0) local function makePair(foreground, background) return background * 8 + 7 - foreground end -- initialize the color pairs the way rt:draw() expects it for foreground = 0, 7 do for background = 0, 7 do if foreground ~= 7 or background ~= 0 then local pair = makePair(foreground, background) curses.init_pair(pair, foreground, background) end end end else curses.endwin() end local function show(key) rt:update() if visual then rt:draw(term_win, 0, 0) if key then term_win:mvaddstr(0, 0, tostring(key)) end term_win:refresh() delay(visual_delay) end end local function send(key, times, quick) if times == 0 then return end for _ = 1, times or 1 do delay(0.003) -- 30ms delay to avoid clobbering Esc sequences if type(key) == "string" then for c in key:gmatch('.') do rt:keyPress(string.byte(c)) end else rt:keyPress(key) end if not quick then show(key) end end if quick then show(key) end end local function string_at(x, y, len) rt:update() local out = {} for i = 1, len do out[#out+1] = rt:cellChar(y-1, x+i-2) end return table.concat(out) end local function is_string_at(x, y, str) return string_at(x, y, #str) == str end local function check_string_at(x, y, str) return { str, string_at(x, y, #str) } end local ESC = "\27\27" function delay(t) time.nanosleep({ tv_sec = math.floor(t), tv_nsec = (t - math.floor(t)) * 1000000000 }) end delay(2) -- give some time for htop to initialize. rt:update() local y_panelhdr = (function() for y = 1, 24 do if is_string_at(3, y, "PID") then return y end end end)() or 1 assert.not_equal(y_panelhdr, 1) local x_metercol2 = 41 show() os.execute("sleep 12345 &") local function terminated() return not os.execute("ps aux | grep -q '\\./[h]top'") end local function running_it(desc, fn) it(desc, function() assert(not terminated()) show() fn() assert(not terminated()) end) end local function check(t) return t[1], t[2] end local attrs = { black_on_cyan = 6, red_on_cyan = 22, white_on_black = 176, yellow_on_black = 112, } local function find_selected_y(from) rt:update() for y = from or (y_panelhdr + 1), rt:rows() - 1 do local attr = rt:cellAttr(y-1, 1) if attr == attrs.black_on_cyan then return y end end return y_panelhdr + 1 end local function find_command_x() for x = 1, 80 do if is_string_at(x, y_panelhdr, "Command") then return x end end return 64 end local function set_display_option(n) send("S") send(curses.KEY_DOWN) send(curses.KEY_RIGHT) send(curses.KEY_DOWN, n, "quick") send("\n") send(curses.KEY_F10) end describe("htop test suite", function() running_it("performs incremental filter", function() send("\\") send("x\127bux\127sted") -- test backspace send("\n") delay(short_delay) rt:update() local pid = (" "..tostring(unistd.getpid())):sub(-5) local ourpid = check_string_at(1, y_panelhdr + 1, pid) send("\\") send(ESC) send(curses.KEY_F5) send(curses.KEY_HOME) delay(short_delay) rt:update() local initpid = check_string_at(1, y_panelhdr + 1, " 1") delay(short_delay) rt:update() send(curses.KEY_F5) assert.equal(check(ourpid)) assert.equal(check(initpid)) end) running_it("performs incremental search", function() send(curses.KEY_HOME) send("/") send("busted") local attr = rt:cellAttr(rt:rows() - 1, 30) delay(short_delay) local line = find_selected_y() local pid = (" "..tostring(unistd.getpid())):sub(-5) assert.equal(attr, attrs.black_on_cyan) local ourpid = check_string_at(1, line, pid) send("\n") send(curses.KEY_HOME) assert.equal(check(ourpid)) end) running_it("performs pid search", function() send(curses.KEY_F5) send(curses.KEY_END) send("1") delay(short_delay) local line = find_selected_y() local initpid = check_string_at(1, line, " 1") send(curses.KEY_F5) assert.equal(check(initpid)) end) running_it("horizontal scroll", function() local h_scroll = 20 send(curses.KEY_F5) delay(short_delay) local str1 = string_at(1+h_scroll, y_panelhdr+1, 5) send(curses.KEY_RIGHT) delay(short_delay) local str2 = string_at(1, y_panelhdr+1, 5) send(curses.KEY_LEFT) delay(short_delay) local str3 = string_at(1+h_scroll, y_panelhdr+1, 5) send(curses.KEY_LEFT) delay(short_delay) local str4 = string_at(1+h_scroll, y_panelhdr+1, 5) send(curses.KEY_F5) assert.equal(str1, str2) assert.equal(str2, str3) assert.equal(str3, str4) end) running_it("kills a process", function() send(curses.KEY_HOME) send("\\") send("sleep 12345") local attr = rt:cellAttr(rt:rows() - 1, 30) assert.equal(attr, attrs.black_on_cyan) send("\n") delay(short_delay) rt:update() local col = find_command_x() local procname = check_string_at(col, y_panelhdr + 1, "sleep 12345") send("k") send("\n") send("\\") send(ESC) delay(short_delay) assert.equal(check(procname)) assert.not_equal((os.execute("ps aux | grep -q '[s]leep 12345'")), true) end) running_it("runs strace", function() send(curses.KEY_HOME) send("/") send("busted") send("\n") send("s") delay(long_delay) send(ESC) end) running_it("runs lsof", function() send(curses.KEY_HOME) send("/") send("busted") send("\n") send("l") delay(long_delay) send(ESC) end) running_it("performs filtering in lsof", function() send(curses.KEY_HOME) send("/") send("htop") send("\n") send("l") send(curses.KEY_F4) send("pipe") delay(long_delay) local pipefd = check_string_at(1, 3, " 3") send(ESC) assert.equal(check(pipefd)) end) running_it("performs search in lsof", function() send(curses.KEY_HOME) send("/") send("htop") send("\n") send("l") send(curses.KEY_F3) send("pipe") delay(long_delay) local line = find_selected_y(3) local pipefd = check_string_at(1, line, " 3") send(ESC) assert.equal(check(pipefd)) end) running_it("cycles through meter modes in the default meters", function() send("S") for _ = 1, 2 do send(curses.KEY_RIGHT) for _ = 1, 3 do send("\n", 4) send(curses.KEY_DOWN) end end send(ESC) end) running_it("show process of a user", function() send(curses.KEY_F5) send("u") send(curses.KEY_DOWN) delay(short_delay) rt:update() local chosen = string_at(1, y_panelhdr + 2, 9) send("\n") send(curses.KEY_HOME) delay(short_delay) rt:update() local shown = string_at(7, y_panelhdr + 1, 9) send("u") send("\n") send(curses.KEY_HOME) delay(short_delay) rt:update() local inituser = string_at(7, y_panelhdr + 1, 9) send(curses.KEY_F5) assert.equal(shown, chosen) assert.equal(inituser, "root ") end) running_it("performs failing search", function() send(curses.KEY_HOME) send("/") send("xxxxxxxxxx") delay(short_delay) rt:update() local attr = rt:cellAttr(rt:rows() - 1, 30) assert.equal(attr, attrs.red_on_cyan) send("\n") end) running_it("cycles through search", function() send(curses.KEY_HOME) send("/") send("sh") local lastpid local pidpairs = {} for _ = 1, 3 do send(curses.KEY_F3) local line = find_selected_y() local pid = string_at(1, line, 5) if lastpid then pidpairs[#pidpairs + 1] = { lastpid, pid } lastpid = pid end end send(curses.KEY_HOME) for _, pair in pairs(pidpairs) do assert.not_equal(pair[1], pair[2]) end end) running_it("visits each setup screen", function() send("S") send(curses.KEY_DOWN, 3) send(curses.KEY_F10) end) running_it("adds and removes PPID column", function() send("S") send(curses.KEY_DOWN, 3) send(curses.KEY_RIGHT, 2) send(curses.KEY_DOWN, 2) send("\n") send(curses.KEY_F10) delay(short_delay) local ppid = check_string_at(2, y_panelhdr, "PPID") send("S") send(curses.KEY_DOWN, 3) send(curses.KEY_RIGHT, 1) send(curses.KEY_DC) send(curses.KEY_F10) delay(short_delay) local not_ppid = check_string_at(2, y_panelhdr, "PPID") assert.equal(check(ppid)) assert.not_equal(check(not_ppid)) end) running_it("changes CPU affinity for a process", function() send("a") send(" \n") send(ESC) end) running_it("renices for a process", function() send("/") send("busted") send("\n") local line = find_selected_y() local before = check_string_at(22, line, " 0") send(curses.KEY_F8) delay(short_delay) local after = check_string_at(22, line, " 1") assert.equal(check(before)) assert.equal(check(after)) end) running_it("tries to lower nice for a process", function() send("/") send("busted") send("\n") local line = find_selected_y() local before = string_at(22, line, 2) send(curses.KEY_F7) delay(short_delay) local after = string_at(22, line, 2) assert.equal(before, after) -- no permissions end) running_it("invert sort order", function() local cpu_col = 45 send("P") send("I") send(curses.KEY_HOME) delay(short_delay) local zerocpu = check_string_at(cpu_col, y_panelhdr + 1, " 0.0") send("I") delay(short_delay) local nonzerocpu = check_string_at(cpu_col, y_panelhdr + 1, " 0.0") assert.equal(check(zerocpu)) assert.not_equal(check(nonzerocpu)) end) running_it("changes IO priority for a process", function() send("/") send("htop") send("\n") send("i") send(curses.KEY_END) send("\n") send(ESC) end) running_it("shows help", function() send(curses.KEY_F1) send("\n") set_display_option(9) send(curses.KEY_F1) send("\n") set_display_option(9) end) running_it("moves meters around", function() send("S") send(curses.KEY_RIGHT) send(curses.KEY_UP) send("\n") send(curses.KEY_DOWN) send(curses.KEY_UP) send(curses.KEY_RIGHT) send(curses.KEY_RIGHT) send(curses.KEY_LEFT) send(curses.KEY_LEFT) send("\n") send(curses.KEY_F10) end) local meters = { { name = "clock", down = 0, string = "Time" }, { name = "load", down = 2, string = "Load" }, { name = "battery", down = 7, string = "Battery" }, { name = "hostname", down = 8, string = "Hostname" }, { name = "memory", down = 3, string = "Mem" }, { name = "CPU average", down = 16, string = "Avg" }, } running_it("checks various CPU meters", function() send("S") send(curses.KEY_RIGHT, 3) send(curses.KEY_DOWN, 9, "quick") for _ = 9, 14 do send("\n") send("\n") send(curses.KEY_DC) send(curses.KEY_RIGHT) send(curses.KEY_DOWN) end end) for _, item in ipairs(meters) do running_it("adds and removes a "..item.name.." widget", function() send("S") send(curses.KEY_RIGHT, 3) send(curses.KEY_DOWN, item.down) send("\n") send(curses.KEY_UP, 4) send("\n") send(curses.KEY_F4, 4) -- cycle through meter modes delay(short_delay) rt:update() local with = check_string_at(x_metercol2, 2, item.string) send(curses.KEY_DC) delay(short_delay) local without = check_string_at(x_metercol2, 2, item.string) send(curses.KEY_F10) assert.equal(check(with)) assert.not_equal(check(without)) end) end running_it("goes through themes", function() send(curses.KEY_F2) send(curses.KEY_DOWN, 2) send(curses.KEY_RIGHT) for _ = 1, 6 do send("\n") send(curses.KEY_DOWN) end send(curses.KEY_UP, 6) send("\n") send(curses.KEY_F10) end) local display_options = { { name = "tree view", down = 0 }, { name = "shadow other user's process", down = 1 }, { name = "hide kernel threads", down = 2 }, { name = "hide userland threads", down = 3 }, { name = "display threads in different color", down = 4 }, { name = "show custom thread names", down = 5 }, { name = "highlight basename", down = 6 }, { name = "highlight large numbers", down = 7 }, { name = "leave margin around header", down = 8 }, { name = "use detailed CPU time", down = 9 }, { name = "count from zero", down = 10 }, { name = "update process names", down = 11 }, { name = "guest time in CPU%", down = 12 }, } for _, item in ipairs(display_options) do running_it("checks display option to "..item.name, function() for _ = 1, 2 do set_display_option(item.down) delay(short_delay) end end) end running_it("shows detailed CPU with guest time", function() for _ = 1, 2 do send("S") send(curses.KEY_DOWN) send(curses.KEY_RIGHT) send(curses.KEY_DOWN, 9) send("\n") send(curses.KEY_DOWN, 3) send("\n") send(curses.KEY_LEFT) send(curses.KEY_UP) send(curses.KEY_RIGHT) send(curses.KEY_F4, 4) -- cycle through CPU meter modes send(curses.KEY_F10) delay(short_delay) end end) running_it("expands and collapses tree", function() send(curses.KEY_F5) -- tree view send(curses.KEY_HOME) send(curses.KEY_DOWN) -- second process in the tree send("-") send("+") send(curses.KEY_F5) end) running_it("sets sort key", function() send(".") send("\n") end) running_it("tags all children", function() send(curses.KEY_F5) -- tree view send(curses.KEY_HOME) -- ensure we're at init send("c") local taggedattrs = {} rt:update() for y = y_panelhdr + 2, 23 do table.insert(taggedattrs, rt:cellAttr(y-1, 4)) end delay(short_delay) send("U") local untaggedattrs = {} rt:update() for y = y_panelhdr + 2, 23 do table.insert(untaggedattrs, rt:cellAttr(y-1, 4)) end send(curses.KEY_F5) for _, taggedattr in ipairs(taggedattrs) do assert.equal(attrs.yellow_on_black, taggedattr) end for _, untaggedattr in ipairs(untaggedattrs) do assert.equal(attrs.white_on_black, untaggedattr) end end) for i = 1, 62 do running_it("show column "..i, function() send("S") send(curses.KEY_END) send(curses.KEY_RIGHT, 1) if i > 1 then send(curses.KEY_DC) end send(curses.KEY_RIGHT, 1) local down = i while down > 13 do send(curses.KEY_NPAGE) down = down - 13 end send(curses.KEY_DOWN, down, "quick") send("\n") send(curses.KEY_F10) if i == 62 then send("S") send(curses.KEY_END) send(curses.KEY_RIGHT, 1) if i > 1 then send(curses.KEY_DC) end send(curses.KEY_F10) end end) end it("finally quits", function() assert(not terminated()) send("q") while not terminated() do unistd.sleep(1) send("q") end assert(terminated()) if visual then curses.endwin() end os.execute("make lcov && xdg-open lcov/index.html") end) end) htop-3.0.5/unsupported/000077500000000000000000000000001377712513700150765ustar00rootroot00000000000000htop-3.0.5/unsupported/Platform.c000066400000000000000000000061551377712513700170350ustar00rootroot00000000000000/* htop - unsupported/Platform.c (C) 2014 Hisham H. Muhammad (C) 2015 David C. Hunt Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include #include "Platform.h" #include "Macros.h" #include "CPUMeter.h" #include "MemoryMeter.h" #include "SwapMeter.h" #include "TasksMeter.h" #include "LoadAverageMeter.h" #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" #include "HostnameMeter.h" #include "UptimeMeter.h" const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, }; const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const MeterClass* const Platform_meterTypes[] = { &CPUMeter_class, &ClockMeter_class, &DateMeter_class, &DateTimeMeter_class, &LoadAverageMeter_class, &LoadMeter_class, &MemoryMeter_class, &SwapMeter_class, &TasksMeter_class, &BatteryMeter_class, &HostnameMeter_class, &UptimeMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, &AllCPUs4Meter_class, &AllCPUs8Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, &LeftCPUs4Meter_class, &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, &BlankMeter_class, NULL }; void Platform_init(void) { /* no platform-specific setup needed */ } void Platform_done(void) { /* no platform-specific cleanup needed */ } void Platform_setBindings(Htop_Action* keys) { /* no platform-specific key bindings */ (void) keys; } int Platform_getUptime() { return 0; } void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *one = 0; *five = 0; *fifteen = 0; } int Platform_getMaxPid() { return 1; } double Platform_setCPUValues(Meter* this, int cpu) { (void) cpu; double* v = this->values; v[CPU_METER_FREQUENCY] = NAN; v[CPU_METER_TEMPERATURE] = NAN; return 0.0; } void Platform_setMemoryValues(Meter* this) { (void) this; } void Platform_setSwapValues(Meter* this) { (void) this; } bool Process_isThread(const Process* this) { (void) this; return false; } char* Platform_getProcessEnv(pid_t pid) { (void) pid; return NULL; } char* Platform_getInodeFilename(pid_t pid, ino_t inode) { (void)pid; (void)inode; return NULL; } FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; } bool Platform_getDiskIO(DiskIOData* data) { (void)data; return false; } bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted) { *bytesReceived = 0; *packetsReceived = 0; *bytesTransmitted = 0; *packetsTransmitted = 0; return false; } void Platform_getBattery(double* percent, ACPresence* isOnAC) { *percent = NAN; *isOnAC = AC_ERROR; } htop-3.0.5/unsupported/Platform.h000066400000000000000000000030251377712513700170330ustar00rootroot00000000000000#ifndef HEADER_Platform #define HEADER_Platform /* htop - unsupported/Platform.h (C) 2014 Hisham H. Muhammad (C) 2015 David C. Hunt Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Action.h" #include "BatteryMeter.h" #include "DiskIOMeter.h" #include "ProcessLocksScreen.h" #include "SignalsPanel.h" #include "UnsupportedProcess.h" extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; extern const ProcessField Platform_defaultFields[]; extern const MeterClass* const Platform_meterTypes[]; void Platform_init(void); void Platform_done(void); void Platform_setBindings(Htop_Action* keys); int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); int Platform_getMaxPid(void); double Platform_setCPUValues(Meter* this, int cpu); void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); bool Process_isThread(const Process* this); char* Platform_getProcessEnv(pid_t pid); char* Platform_getInodeFilename(pid_t pid, ino_t inode); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(unsigned long int* bytesReceived, unsigned long int* packetsReceived, unsigned long int* bytesTransmitted, unsigned long int* packetsTransmitted); void Platform_getBattery(double *percent, ACPresence *isOnAC); #endif htop-3.0.5/unsupported/ProcessField.h000066400000000000000000000005231377712513700176310ustar00rootroot00000000000000#ifndef HEADER_UnsupportedProcessField #define HEADER_UnsupportedProcessField /* htop - unsupported/ProcessField.h (C) 2020 htop dev team Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #define PLATFORM_PROCESS_FIELDS \ // End of list #endif /* HEADER_UnsupportedProcessField */ htop-3.0.5/unsupported/UnsupportedProcess.c000066400000000000000000000075641377712513700211450ustar00rootroot00000000000000/* htop - UnsupportedProcess.c (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Process.h" #include "UnsupportedProcess.h" #include const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, }; Process* UnsupportedProcess_new(Settings* settings) { Process* this = xCalloc(1, sizeof(Process)); Object_setClass(this, Class(Process)); Process_init(this, settings); return this; } void UnsupportedProcess_delete(Object* cast) { Process* this = (Process*) cast; Object_setClass(this, Class(Process)); Process_done((Process*)cast); // free platform-specific fields here free(this); } htop-3.0.5/unsupported/UnsupportedProcess.h000066400000000000000000000007331377712513700211410ustar00rootroot00000000000000#ifndef HEADER_UnsupportedProcess #define HEADER_UnsupportedProcess /* htop - UnsupportedProcess.h (C) 2015 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Settings.h" #define Process_delete UnsupportedProcess_delete extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; Process* UnsupportedProcess_new(Settings* settings); void UnsupportedProcess_delete(Object* cast); #endif htop-3.0.5/unsupported/UnsupportedProcessList.c000066400000000000000000000032771377712513700217760ustar00rootroot00000000000000/* htop - UnsupportedProcessList.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ProcessList.h" #include "UnsupportedProcess.h" #include #include ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* this = xCalloc(1, sizeof(ProcessList)); ProcessList_init(this, Class(Process), usersTable, pidMatchList, userId); return this; } void ProcessList_delete(ProcessList* this) { ProcessList_done(this); free(this); } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { // in pause mode only gather global data for meters (CPU/memory/...) if (pauseProcessUpdate) { return; } bool preExisting = true; Process* proc; proc = ProcessList_getProcess(super, 1, &preExisting, UnsupportedProcess_new); /* Empty values */ proc->time = proc->time + 10; proc->pid = 1; proc->ppid = 1; proc->tgid = 0; proc->comm = ""; proc->basenameOffset = 0; proc->updated = true; proc->state = 'R'; proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */ proc->pgrp = 0; proc->session = 0; proc->tty_nr = 0; proc->tpgid = 0; proc->st_uid = 0; proc->flags = 0; proc->processor = 0; proc->percent_cpu = 2.5; proc->percent_mem = 2.5; proc->user = "nobody"; proc->priority = 0; proc->nice = 0; proc->nlwp = 1; proc->starttime_ctime = 1433116800; // Jun 01, 2015 Process_fillStarttimeBuffer(proc); proc->m_virt = 100; proc->m_resident = 100; proc->minflt = 20; proc->majflt = 20; } htop-3.0.5/unsupported/UnsupportedProcessList.h000066400000000000000000000007231377712513700217740ustar00rootroot00000000000000#ifndef HEADER_UnsupportedProcessList #define HEADER_UnsupportedProcessList /* htop - UnsupportedProcessList.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); #endif htop-3.0.5/zfs/000077500000000000000000000000001377712513700133105ustar00rootroot00000000000000htop-3.0.5/zfs/ZfsArcMeter.c000066400000000000000000000062171377712513700156470ustar00rootroot00000000000000/* htop - ZfsArcMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ZfsArcMeter.h" #include "ZfsArcStats.h" #include "CRT.h" #include "Object.h" #include "Platform.h" #include "RichString.h" static const int ZfsArcMeter_attributes[] = { ZFS_MFU, ZFS_MRU, ZFS_ANON, ZFS_HEADER, ZFS_OTHER }; void ZfsArcMeter_readStats(Meter* this, const ZfsArcStats* stats) { this->total = stats->max; this->values[0] = stats->MFU; this->values[1] = stats->MRU; this->values[2] = stats->anon; this->values[3] = stats->header; this->values[4] = stats->other; // "Hide" the last value so it can // only be accessed by index and is not // displayed by the Bar or Graph style this->curItems = 5; this->values[5] = stats->size; } static void ZfsArcMeter_updateValues(Meter* this, char* buffer, size_t size) { int written; Platform_setZfsArcValues(this); written = Meter_humanUnit(buffer, this->values[5], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); Meter_humanUnit(buffer, this->total, size); } static void ZfsArcMeter_display(const Object* cast, RichString* out) { const Meter* this = (const Meter*)cast; if (this->values[5] > 0) { char buffer[50]; Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); Meter_humanUnit(buffer, this->values[5], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " Used:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " MFU:"); RichString_appendAscii(out, CRT_colors[ZFS_MFU], buffer); Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " MRU:"); RichString_appendAscii(out, CRT_colors[ZFS_MRU], buffer); Meter_humanUnit(buffer, this->values[2], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " Anon:"); RichString_appendAscii(out, CRT_colors[ZFS_ANON], buffer); Meter_humanUnit(buffer, this->values[3], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " Hdr:"); RichString_appendAscii(out, CRT_colors[ZFS_HEADER], buffer); Meter_humanUnit(buffer, this->values[4], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " Oth:"); RichString_appendAscii(out, CRT_colors[ZFS_OTHER], buffer); } else { RichString_writeAscii(out, CRT_colors[METER_TEXT], " "); RichString_appendAscii(out, CRT_colors[FAILED_SEARCH], "Unavailable"); } } const MeterClass ZfsArcMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = ZfsArcMeter_display, }, .updateValues = ZfsArcMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 6, .total = 100.0, .attributes = ZfsArcMeter_attributes, .name = "ZFSARC", .uiName = "ZFS ARC", .caption = "ARC: " }; htop-3.0.5/zfs/ZfsArcMeter.h000066400000000000000000000005711377712513700156510ustar00rootroot00000000000000#ifndef HEADER_ZfsArcMeter #define HEADER_ZfsArcMeter /* htop - ZfsArcMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ZfsArcStats.h" #include "Meter.h" void ZfsArcMeter_readStats(Meter* this, const ZfsArcStats* stats); extern const MeterClass ZfsArcMeter_class; #endif htop-3.0.5/zfs/ZfsArcStats.c000066400000000000000000000003371377712513700156660ustar00rootroot00000000000000/* htop - ZfsArcStats.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "Macros.h" static int make_iso_compilers_happy ATTR_UNUSED; htop-3.0.5/zfs/ZfsArcStats.h000066400000000000000000000011271377712513700156710ustar00rootroot00000000000000#ifndef HEADER_ZfsArcStats #define HEADER_ZfsArcStats /* htop - ZfsArcStats.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ typedef struct ZfsArcStats_ { int enabled; int isCompressed; unsigned long long int max; unsigned long long int size; unsigned long long int MFU; unsigned long long int MRU; unsigned long long int anon; unsigned long long int header; unsigned long long int other; unsigned long long int compressed; unsigned long long int uncompressed; } ZfsArcStats; #endif htop-3.0.5/zfs/ZfsCompressedArcMeter.c000066400000000000000000000050511377712513700176670ustar00rootroot00000000000000/* htop - ZfsCompressedArcMeter.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ZfsCompressedArcMeter.h" #include "CRT.h" #include "Meter.h" #include "Object.h" #include "Platform.h" #include "RichString.h" #include "XUtils.h" #include "zfs/ZfsArcStats.h" static const int ZfsCompressedArcMeter_attributes[] = { ZFS_COMPRESSED }; void ZfsCompressedArcMeter_readStats(Meter* this, const ZfsArcStats* stats) { if ( stats->isCompressed ) { this->total = stats->uncompressed; this->values[0] = stats->compressed; } else { // For uncompressed ARC, report 1:1 ratio this->total = stats->size; this->values[0] = stats->size; } } static void ZfsCompressedArcMeter_printRatioString(const Meter* this, char* buffer, size_t size) { xSnprintf(buffer, size, "%.2f:1", this->total / this->values[0]); } static void ZfsCompressedArcMeter_updateValues(Meter* this, char* buffer, size_t size) { Platform_setZfsCompressedArcValues(this); ZfsCompressedArcMeter_printRatioString(this, buffer, size); } static void ZfsCompressedArcMeter_display(const Object* cast, RichString* out) { const Meter* this = (const Meter*)cast; if (this->values[0] > 0) { char buffer[50]; Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " Uncompressed, "); Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " Compressed, "); ZfsCompressedArcMeter_printRatioString(this, buffer, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], " Ratio"); } else { RichString_writeAscii(out, CRT_colors[METER_TEXT], " "); RichString_appendAscii(out, CRT_colors[FAILED_SEARCH], "Compression Unavailable"); } } const MeterClass ZfsCompressedArcMeter_class = { .super = { .extends = Class(Meter), .delete = Meter_delete, .display = ZfsCompressedArcMeter_display, }, .updateValues = ZfsCompressedArcMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 1, .total = 100.0, .attributes = ZfsCompressedArcMeter_attributes, .name = "ZFSCARC", .uiName = "ZFS CARC", .description = "ZFS CARC: Compressed ARC statistics", .caption = "ARC: " }; htop-3.0.5/zfs/ZfsCompressedArcMeter.h000066400000000000000000000006531377712513700176770ustar00rootroot00000000000000#ifndef HEADER_ZfsCompressedArcMeter #define HEADER_ZfsCompressedArcMeter /* htop - ZfsCompressedArcMeter.h (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "ZfsArcStats.h" #include "Meter.h" void ZfsCompressedArcMeter_readStats(Meter* this, const ZfsArcStats* stats); extern const MeterClass ZfsCompressedArcMeter_class; #endif htop-3.0.5/zfs/openzfs_sysctl.c000066400000000000000000000072431377712513700165470ustar00rootroot00000000000000/* htop - zfs/openzfs_sysctl.c (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "zfs/openzfs_sysctl.h" #include #include // IWYU pragma: keep #include // needs for u_int with gcc #include "zfs/ZfsArcStats.h" static int MIB_kstat_zfs_misc_arcstats_size[5]; static int MIB_kstat_zfs_misc_arcstats_c_max[5]; static int MIB_kstat_zfs_misc_arcstats_mfu_size[5]; static int MIB_kstat_zfs_misc_arcstats_mru_size[5]; static int MIB_kstat_zfs_misc_arcstats_anon_size[5]; static int MIB_kstat_zfs_misc_arcstats_hdr_size[5]; static int MIB_kstat_zfs_misc_arcstats_other_size[5]; static int MIB_kstat_zfs_misc_arcstats_compressed_size[5]; static int MIB_kstat_zfs_misc_arcstats_uncompressed_size[5]; void openzfs_sysctl_init(ZfsArcStats* stats) { size_t len; unsigned long long int arcSize; len = sizeof(arcSize); if (sysctlbyname("kstat.zfs.misc.arcstats.size", &arcSize, &len, NULL, 0) == 0 && arcSize != 0) { stats->enabled = 1; len = 5; sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.c_max", MIB_kstat_zfs_misc_arcstats_c_max, &len); sysctlnametomib("kstat.zfs.misc.arcstats.mfu_size", MIB_kstat_zfs_misc_arcstats_mfu_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.mru_size", MIB_kstat_zfs_misc_arcstats_mru_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.anon_size", MIB_kstat_zfs_misc_arcstats_anon_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.hdr_size", MIB_kstat_zfs_misc_arcstats_hdr_size, &len); sysctlnametomib("kstat.zfs.misc.arcstats.other_size", MIB_kstat_zfs_misc_arcstats_other_size, &len); if (sysctlnametomib("kstat.zfs.misc.arcstats.compressed_size", MIB_kstat_zfs_misc_arcstats_compressed_size, &len) == 0) { stats->isCompressed = 1; sysctlnametomib("kstat.zfs.misc.arcstats.uncompressed_size", MIB_kstat_zfs_misc_arcstats_uncompressed_size, &len); } else { stats->isCompressed = 0; } } else { stats->enabled = 0; } } void openzfs_sysctl_updateArcStats(ZfsArcStats* stats) { size_t len; if (stats->enabled) { len = sizeof(stats->size); sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len, NULL, 0); stats->size /= 1024; len = sizeof(stats->max); sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len, NULL, 0); stats->max /= 1024; len = sizeof(stats->MFU); sysctl(MIB_kstat_zfs_misc_arcstats_mfu_size, 5, &(stats->MFU), &len, NULL, 0); stats->MFU /= 1024; len = sizeof(stats->MRU); sysctl(MIB_kstat_zfs_misc_arcstats_mru_size, 5, &(stats->MRU), &len, NULL, 0); stats->MRU /= 1024; len = sizeof(stats->anon); sysctl(MIB_kstat_zfs_misc_arcstats_anon_size, 5, &(stats->anon), &len, NULL, 0); stats->anon /= 1024; len = sizeof(stats->header); sysctl(MIB_kstat_zfs_misc_arcstats_hdr_size, 5, &(stats->header), &len, NULL, 0); stats->header /= 1024; len = sizeof(stats->other); sysctl(MIB_kstat_zfs_misc_arcstats_other_size, 5, &(stats->other), &len, NULL, 0); stats->other /= 1024; if (stats->isCompressed) { len = sizeof(stats->compressed); sysctl(MIB_kstat_zfs_misc_arcstats_compressed_size, 5, &(stats->compressed), &len, NULL, 0); stats->compressed /= 1024; len = sizeof(stats->uncompressed); sysctl(MIB_kstat_zfs_misc_arcstats_uncompressed_size, 5, &(stats->uncompressed), &len, NULL, 0); stats->uncompressed /= 1024; } } } htop-3.0.5/zfs/openzfs_sysctl.h000066400000000000000000000005511377712513700165470ustar00rootroot00000000000000#ifndef HEADER_openzfs_sysctl #define HEADER_openzfs_sysctl /* htop - zfs/openzfs_sysctl.h (C) 2014 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "zfs/ZfsArcStats.h" void openzfs_sysctl_init(ZfsArcStats* stats); void openzfs_sysctl_updateArcStats(ZfsArcStats* stats); #endif

- do nothing. , - quit program. - do nothing. - enter STrace screen. , - toggle between Tree and Sorted view, update F5 in FunctionBar, follow process - enter User screen. , , , , - do nothing. , - do nothing. , , - enter Setup screen. , - do nothing. - follow process. - do nothing. - toggle show/hide userland threads. - invert sort order. - do nothing. - toggle show/hide kernel threads. - do nothing. - enter Sorted view, update function bar, sort by MEM%. , - do nothing.