diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README | 39 | ||||
-rw-r--r-- | px.1 | 49 | ||||
-rw-r--r-- | px.c | 171 |
4 files changed, 205 insertions, 56 deletions
diff --git a/Makefile b/Makefile index 59bd863..ae6e47e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ ALL=px CFLAGS=-g -O2 -Wall -Wno-switch -Wextra -Wwrite-strings -LDFLAGS=-lprocps +LDFLAGS=-lproc2 DESTDIR= PREFIX=/usr/local diff --git a/README b/README new file mode 100644 index 0000000..da89814 --- /dev/null +++ b/README @@ -0,0 +1,39 @@ +PX(1) General Commands Manual PX(1) + +NAME + px – report details on matching processes + +SYNOPSIS + px [-f] PATTERNS ... + +DESCRIPTION + px searches the process list for processes matching any of the patterns + PATTERNS and prints details on them, namely PID, user, cpu usage, virtual + and real memory usage, start time, run time and cpu time, as well as the + full command line. + + As PATTERNS may be used: substrings of the process name or numeric PIDs. + + The options are as follows: + + -f Search the whole command line, including arguments. + +EXIT STATUS + The px utility returns 0 when a process was found, 1 if no process was + found, and 2 when other errors occured. + +SEE ALSO + ps(1), pgrep(2) + +AUTHORS + Leah Neukirchen <leah@vuxu.org> + +LICENSE + px is in the public domain. + + To the extent possible under law, the creator of this work has waived all + copyright and related or neighboring rights to this work. + + http://creativecommons.org/publicdomain/zero/1.0/ + +Void Linux May 8, 2023 Void Linux diff --git a/px.1 b/px.1 new file mode 100644 index 0000000..d446cd0 --- /dev/null +++ b/px.1 @@ -0,0 +1,49 @@ +.Dd May 08, 2023 +.Dt PX 1 +.Os +.Sh NAME +.Nm px +.Nd report details on matching processes +.Sh SYNOPSIS +.Nm +.Op Fl f +.Ar PATTERNS ... +.Sh DESCRIPTION +.Nm +searches the process list for processes matching any of the patterns +.Ar PATTERNS +and prints details on them, +namely PID, user, cpu usage, virtual and real memory usage, +start time, run time and cpu time, as well as the full command line. +.Pp +As +.Ar PATTERNS +may be used: +substrings of the process name or numeric PIDs. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f +Search the whole command line, including arguments. +.El +.Sh EXIT STATUS +The +.Nm +utility returns 0 when a process was found, +1 if no process was found, +and 2 when other errors occured. +.Sh SEE ALSO +.Xr ps 1 , +.Xr pgrep 2 +.Sh AUTHORS +.An Leah Neukirchen Aq Mt leah@vuxu.org +.Sh LICENSE +.Nm +is in the public domain. +.Pp +To the extent possible under law, +the creator of this work +has waived all copyright and related or +neighboring rights to this work. +.Pp +.Lk http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/px.c b/px.c index bf74d51..6172ecb 100644 --- a/px.c +++ b/px.c @@ -10,13 +10,18 @@ #include <sys/sysinfo.h> #include <ctype.h> +#include <errno.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> +#include <unistd.h> -#include <proc/readproc.h> +#include <libproc2/pids.h> +#include <libproc2/stat.h> + +int fflag; static void print_human(intmax_t i) @@ -56,70 +61,126 @@ print_time(time_t t) { int main(int argc, char *argv[]) { - int clktck; - if ((clktck = getauxval(AT_CLKTCK)) <= 0) - if ((clktck = sysconf(_SC_CLK_TCK)) <= 0) - clktck = 100; /* educated guess */ - - struct sysinfo si; - sysinfo(&si); - + int r; + pid_t me = getpid(); time_t now = time(0); - proc_t **result; - result = readproctab(PROC_FILLCOM | PROC_FILLUSR | - PROC_FILLSTAT | PROC_FILLSTATUS); + struct stat_info *stat_info = 0; + if ((r = procps_stat_new(&stat_info)) < 0) { + fprintf(stderr, "failed to run procps_stat_new: %s\n", + strerror(-r)); + exit(2); + } + time_t boot_time = STAT_GET(stat_info, STAT_SYS_TIME_OF_BOOT, ul_int); + procps_stat_unref(&stat_info); + + struct pids_info *Pids_info = 0; + enum pids_item items[] = { + PIDS_CMD, + PIDS_CMDLINE_V, + PIDS_ID_EUSER, + PIDS_ID_TID, + PIDS_STATE, + PIDS_TIME_ALL, + PIDS_TIME_ELAPSED, + PIDS_TIME_START, + PIDS_TTY_NAME, + PIDS_UTILIZATION, + PIDS_VM_RSS, + PIDS_VM_SIZE, + }; + enum rel_items { + EU_CMD, + EU_CMDLINE_V, + EU_ID_EUSER, + EU_ID_TID, + EU_STATE, + EU_TIME_ALL, + EU_TIME_ELAPSED, + EU_TIME_START, + EU_TTY_NAME, + EU_UTILIZATION, + EU_VM_RSS, + EU_VM_SIZE, + }; + if ((r = procps_pids_new(&Pids_info, items, 12)) < 0) { + fprintf(stderr, "failed to run procps_pids_new: %s\n", + strerror(-r)); + exit(2); + } + + struct pids_fetch *reap = procps_pids_reap(Pids_info, + PIDS_FETCH_TASKS_ONLY); + if (!reap) { + fprintf(stderr, "failed to run procps_pids_reap: %s\n", + strerror(errno)); + exit(2); + } int matched = 0; - for (; *result; result++) { - proc_t *p = *result; - - if (argc > 1) { - for (int i = 1; i < argc; i++) { - for (size_t j = 0; j < strlen(argv[i]); j++) { - if (!isdigit(argv[i][j])) - goto word; - } - if (p->tid == atoi(argv[i])) - goto match; - else - continue; + int c; + while ((c = getopt(argc, argv, "f")) != -1) + switch (c) { + case 'f': fflag = 1; break; + default: + fprintf(stderr, + "Usage: %s [-f] [PATTERN...]\n", argv[0]); + exit(2); + } + + int total_procs = reap->counts->total; + for (int i = 0; i < total_procs; i++) { +#define PIDS_GETCHR(e) PIDS_VAL(EU_ ## e, s_ch, reap->stacks[i], Pids_info) +#define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, reap->stacks[i], Pids_info) +#define PIDS_GETUNT(e) PIDS_VAL(EU_ ## e, u_int, reap->stacks[i], Pids_info) +#define PIDS_GETULINT(e) PIDS_VAL(EU_ ## e, ul_int, reap->stacks[i], Pids_info) +#define PIDS_GETREAL(e) PIDS_VAL(EU_ ## e, real, reap->stacks[i], Pids_info) +#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, reap->stacks[i], Pids_info) +#define PIDS_GETSTR_V(e) PIDS_VAL(EU_ ## e, strv, reap->stacks[i], Pids_info) + if (argc <= optind) + goto match; + + for (int j = optind; j < argc; j++) { + for (size_t k = 0; k < strlen(argv[j]); k++) + if (!isdigit(argv[j][k])) + goto word; + if (PIDS_GETINT(ID_TID) == atoi(argv[j])) + goto match; + else + continue; word: - if (strstr(p->cmd, argv[i])) - goto match; + if (fflag && PIDS_GETSTR_V(CMDLINE_V)) { + if (PIDS_GETINT(ID_TID) == me) + continue; /* we'd always match ourself */ + for (int k = 0; PIDS_GETSTR_V(CMDLINE_V)[k]; k++) + if (strstr(PIDS_GETSTR_V(CMDLINE_V)[k], argv[j])) + goto match; } - continue; -match: - matched++; - } else { - matched++; + else if (strstr(PIDS_GETSTR(CMD), argv[j])) + goto match; } + continue; + +match: + matched++; if (matched == 1) printf(" PID USER %%CPU VSZ RSS START ELAPSED CPUTIME COMMAND\n"); - printf("%5d", p->tid); + printf("%5d", PIDS_GETINT(ID_TID)); - printf(" %-8.8s", p->euser); + printf(" %-8.8s", PIDS_GETSTR(ID_EUSER)); - double pcpu = 0; - time_t seconds, cputime; - cputime = (p->utime + p->stime) / clktck; - seconds = si.uptime - p->start_time / clktck; - if (seconds) - pcpu = (cputime * 100.0) / seconds; - printf(" %4.1f", pcpu); + printf(" %4.1f", PIDS_GETREAL(UTILIZATION)); - print_human(p->vm_size*1024); + print_human(PIDS_GETULINT(VM_SIZE)*1024); - print_human(p->vm_rss*1024); + print_human(PIDS_GETULINT(VM_RSS)*1024); char buf[7]; - time_t start; - time_t seconds_ago; - start = (now - si.uptime) + p->start_time / clktck; - seconds_ago = now - start; + time_t start = boot_time + PIDS_GETREAL(TIME_START); + time_t seconds_ago = now - start; if (seconds_ago < 0) seconds_ago = 0; if (seconds_ago > 3600*24) @@ -128,16 +189,16 @@ match: strftime(buf, sizeof buf, "%H:%M", localtime(&start)); printf(" %s", buf); - print_time(seconds_ago); + print_time(PIDS_GETREAL(TIME_ELAPSED)); - print_time(cputime); + print_time(PIDS_GETREAL(TIME_ALL)); - if (p->cmdline) - for (int i = 0; p->cmdline[i]; i++) - printf(" %s", p->cmdline[i]); - else // kernel threads - printf(" [%s]", p->cmd); - if (p->state == 'Z') + if (PIDS_GETSTR_V(CMDLINE_V)) + for (int j = 0; PIDS_GETSTR_V(CMDLINE_V)[j]; j++) + printf(" %s", PIDS_GETSTR_V(CMDLINE_V)[j]); + else // kernel threads + printf(" [%s]", PIDS_GETSTR(CMD)); + if (PIDS_GETCHR(STATE) == 'Z') printf(" <defunct>"); printf("\n"); |