/* * Copyright (C) 2014-2021 Firejail Authors * * This file is part of firejail project * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "firemon.h" #include #include #include #include #include #ifdef HAVE_GCOV #include #endif static unsigned pgs_rss = 0; static unsigned pgs_shared = 0; static unsigned clocktick = 0; static unsigned long long sysuptime = 0; static int pgsz = 0; static uid_t cached_uid = 0; static char *cached_user_name = NULL; static char *get_user_name(uid_t uid) { if (cached_user_name == NULL) { cached_uid = uid; cached_user_name = pid_get_user_name(uid); return strdup(cached_user_name); } else if (uid == cached_uid) return strdup(cached_user_name); else return pid_get_user_name(uid); } static char *get_header(void) { char *rv; if (asprintf(&rv, "%-5.5s %-9.9s %-8.8s %-8.8s %-5.5s %-4.4s %-9.9s %s", "PID", "User", "RES(KiB)", "SHR(KiB)", "CPU%", "Prcs", "Uptime", "Command") == -1) errExit("asprintf"); return rv; } static char *firejail_exec = NULL; static int firejail_exec_len = 0; static int firejail_exec_prefix_len = 0; // recursivity!!! static char *print_top(unsigned index, unsigned parent, unsigned *utime, unsigned *stime, unsigned itv, float *cpu, int *cnt) { char *rv = NULL; // Remove unused parameter warning (void)parent; char procdir[20]; snprintf(procdir, 20, "/proc/%u", index); struct stat s; if (stat(procdir, &s) == -1) return NULL; if (pids[index].level == 1) { pgs_rss = 0; pgs_shared = 0; *utime = 0; *stime = 0; *cnt = 0; } (*cnt)++; pid_getmem(index, &pgs_rss, &pgs_shared); unsigned utmp; unsigned stmp; pid_get_cpu_time(index, &utmp, &stmp); *utime += utmp; *stime += stmp; int i; for (i = index + 1; i < max_pids; i++) { if (pids[i].parent == (pid_t)index) print_top(i, index, utime, stime, itv, cpu, cnt); } if (!firejail_exec) { if (asprintf(&firejail_exec, "%s/bin/firejail", PREFIX) == -1) errExit("asprintf"); firejail_exec_len = strlen(firejail_exec); firejail_exec_prefix_len = strlen(PREFIX) + 5; } if (pids[index].level == 1) { // pid char pidstr[10]; snprintf(pidstr, 10, "%u", index); // command char *cmd = pid_proc_cmdline(index); char *ptrcmd; if (cmd == NULL) { if (pids[index].zombie) ptrcmd = "(zombie)"; else ptrcmd = ""; } else if (strncmp(cmd, firejail_exec, firejail_exec_len) == 0) ptrcmd = cmd + firejail_exec_prefix_len; else ptrcmd = cmd; // user char *user = get_user_name(pids[index].uid); char *ptruser; if (user) ptruser = user; else ptruser = ""; // memory if (pgsz == 0) pgsz = getpagesize(); char rss[10]; snprintf(rss, 10, "%u", pgs_rss * pgsz / 1024); char shared[10]; snprintf(shared, 10, "%u", pgs_shared * pgsz / 1024); // uptime unsigned long long uptime = pid_get_start_time(index); if (clocktick == 0) clocktick = sysconf(_SC_CLK_TCK); uptime /= clocktick; uptime = sysuptime - uptime; unsigned sec = uptime % 60; uptime -= sec; uptime /= 60; unsigned min = uptime % 60; uptime -= min; uptime /= 60; unsigned hour = uptime; char uptime_str[50]; snprintf(uptime_str, 50, "%02u:%02u:%02u", hour, min, sec); // cpu itv *= clocktick; float ud = (float) (*utime - pids[index].utime) / itv * 100; float sd = (float) (*stime - pids[index].stime) / itv * 100; float cd = ud + sd; *cpu = cd; char cpu_str[10]; snprintf(cpu_str, 10, "%2.1f", cd); // process count char prcs_str[10]; snprintf(prcs_str, 10, "%d", *cnt); if (asprintf(&rv, "%-5.5s %-9.9s %-8.8s %-8.8s %-5.5s %-4.4s %-9.9s %s", pidstr, ptruser, rss, shared, cpu_str, prcs_str, uptime_str, ptrcmd) == -1) errExit("asprintf"); if (cmd) free(cmd); if (user) free(user); } return rv; } typedef struct node_t { struct node_t *next; char *line; float cpu; } Node; static Node *head = NULL; static void head_clear(void) { Node *ptr = head; while (ptr) { if (ptr->line) free(ptr->line); Node *next = ptr->next; free(ptr); ptr = next; } head = NULL; } static void head_add(float cpu, char *line) { // allocate a new node structure Node *node = malloc(sizeof(Node)); if (!node) errExit("malloc"); node->line = line; node->cpu = cpu; node->next = NULL; // insert in first list position if (head == NULL || head->cpu < cpu) { node->next = head; head = node; return; } // insert in the right place Node *ptr = head; while (1) { // last position Node *current = ptr->next; if (current == NULL) { ptr->next = node; return; } // current position if (current->cpu < cpu) { ptr->next = node; node->next = current; return; } ptr = current; } } void head_print(int col, int row) { Node *ptr = head; int current = 0; while (ptr) { if (current >= row) break; if (strlen(ptr->line) > (size_t)col) ptr->line[col] = '\0'; if (ptr->next == NULL || current == (row - 1)) { printf("%s", ptr->line); fflush(0); } else printf("%s\n", ptr->line); ptr = ptr->next; current++; } } void top(void) { while (1) { // clear linked list head_clear(); // set pid table int i; int itv = 1; // 1 second interval pid_read(0); // start cpu measurements unsigned utime = 0; unsigned stime = 0; for (i = 0; i < max_pids; i++) { if (i == skip_process) continue; if (pids[i].level == 1) pid_store_cpu(i, 0, &utime, &stime); } // wait 1 second firemon_sleep(itv); // grab screen size struct winsize sz; int row = 24; int col = 80; if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &sz)) { if (sz.ws_col > 0 && sz.ws_row > 0) { col = sz.ws_col; row = sz.ws_row; } } // start printing firemon_clrscr(); char *header = get_header(); if (strlen(header) > (size_t)col) header[col] = '\0'; printf("%s\n", header); if (row > 0) row--; free(header); // find system uptime FILE *fp = fopen("/proc/uptime", "r"); if (fp) { float f; int rv = fscanf(fp, "%f", &f); (void) rv; sysuptime = (unsigned long long) f; fclose(fp); } // print processes for (i = 0; i < max_pids; i++) { if (i == skip_process) continue; if (pids[i].level == 1) { float cpu = 0; int cnt = 0; // process count char *line = print_top(i, 0, &utime, &stime, itv, &cpu, &cnt); if (line) head_add(cpu, line); } } head_print(col, row); #ifdef HAVE_GCOV __gcov_flush(); #endif } }