summaryrefslogtreecommitdiffstats
path: root/usr/utils/minips.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/utils/minips.c')
-rw-r--r--usr/utils/minips.c511
1 files changed, 511 insertions, 0 deletions
diff --git a/usr/utils/minips.c b/usr/utils/minips.c
new file mode 100644
index 0000000..f48505f
--- /dev/null
+++ b/usr/utils/minips.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * 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 Library General Public License for more details.
+ */
+
+/* This is a minimal /bin/ps, designed to be smaller than the old ps
+ * while still supporting some of the more important features of the
+ * new ps. (for total size, note that this ps does not need libproc)
+ * It is suitable for Linux-on-a-floppy systems only.
+ *
+ * Maintainers: do not compile or install for normal systems.
+ * Anyone needing this will want to tweak their compiler anyway.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <asm/param.h> /* HZ */
+
+static int P_euid;
+static int P_pid;
+static char P_cmd[16];
+static char P_state;
+static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid;
+static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt,
+ P_utime, P_stime;
+static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value;
+static unsigned long P_start_time, P_vsize;
+static long P_rss;
+static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack,
+ P_kstk_esp, P_kstk_eip;
+static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch;
+static unsigned long P_wchan, P_nswap, P_cnswap;
+
+#if 0
+static int screen_cols = 80;
+static int w_count;
+#endif
+
+static int want_one_pid;
+static const char *want_one_command;
+static int select_notty;
+static int select_all;
+
+static int ps_format;
+static int old_h_option;
+
+/* we only pretend to support this */
+static int show_args; /* implicit with -f and all BSD options */
+static int bsd_c_option; /* this option overrides the above */
+
+static int ps_argc; /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg; /* index into ps_argv */
+static char *flagptr; /* current location in ps_argv[thisarg] */
+
+#ifndef HZ
+#warning HZ not defined, assuming it is 100
+#define HZ 100
+#endif
+
+int page_shift; /* Page size as shift count */
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "-C select by command name (minimal ps only accepts one)\n"
+ "-p select by process ID (minimal ps only accepts one)\n"
+ "-e all processes (same as ax)\n"
+ "a all processes w/ tty, including other users\n"
+ "x processes w/o controlling ttys\n"
+ "-f full format\n"
+ "-j,j job control format\n"
+ "v virtual memory format\n"
+ "-l,l long format\n"
+ "u user-oriented format\n"
+ "-o user-defined format (limited support, only \"ps -o pid=\")\n"
+ "h no header\n"
+/*
+ "-A all processes (same as ax)\n"
+ "c true command name\n"
+ "-w,w wide output\n"
+*/
+ );
+ exit(1);
+}
+
+/*
+ * Return the next argument, or call the usage function.
+ * This handles both: -oFOO -o FOO
+ */
+static const char *get_opt_arg(void)
+{
+ const char *ret;
+ ret = flagptr + 1; /* assume argument is part of ps_argv[thisarg] */
+ if (*ret)
+ return ret;
+ if (++thisarg >= ps_argc)
+ usage(); /* there is nothing left */
+ /* argument is the new ps_argv[thisarg] */
+ ret = ps_argv[thisarg];
+ if (!ret || !*ret)
+ usage();
+ return ret;
+}
+
+/* return the PID, or 0 if nothing good */
+static void parse_pid(const char *str)
+{
+ char *endp;
+ int num;
+ if (!str)
+ goto bad;
+ num = strtol(str, &endp, 0);
+ if (*endp != '\0')
+ goto bad;
+ if (num < 1)
+ goto bad;
+ if (want_one_pid)
+ goto bad;
+ want_one_pid = num;
+ return;
+ bad:
+ usage();
+}
+
+/***************** parse SysV options, including Unix98 *****************/
+static void parse_sysv_option(void)
+{
+ do {
+ switch (*flagptr) {
+ /**** selection ****/
+ case 'C': /* end */
+ if (want_one_command)
+ usage();
+ want_one_command = get_opt_arg();
+ return; /* can't have any more options */
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ case 'A':
+ case 'e':
+ select_all++;
+ select_notty++;
+ case 'w': /* here for now, since the real one is not used */
+ break;
+ /**** output format ****/
+ case 'f':
+ show_args = 1;
+ /* FALL THROUGH */
+ case 'j':
+ case 'l':
+ if (ps_format)
+ usage();
+ ps_format = *flagptr;
+ break;
+ case 'o': /* end */
+ /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */
+ if (strcmp(get_opt_arg(), "pid="))
+ usage();
+ if (ps_format)
+ usage();
+ ps_format = 'o';
+ old_h_option++;
+ return; /* can't have any more options */
+ /**** other stuff ****/
+#if 0
+ case 'w':
+ w_count++;
+ break;
+#endif
+ default:
+ usage();
+ } /* switch */
+ } while (*++flagptr);
+}
+
+/************************* parse BSD options **********************/
+static void parse_bsd_option(void)
+{
+ do {
+ switch (*flagptr) {
+ /**** selection ****/
+ case 'a':
+ select_all++;
+ break;
+ case 'x':
+ select_notty++;
+ break;
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ /**** output format ****/
+ case 'j':
+ case 'l':
+ case 'u':
+ case 'v':
+ if (ps_format)
+ usage();
+ ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */
+ break;
+ /**** other stuff ****/
+ case 'c':
+ bsd_c_option++;
+#if 0
+ break;
+#endif
+ case 'w':
+#if 0
+ w_count++;
+#endif
+ break;
+ case 'h':
+ old_h_option++;
+ break;
+ default:
+ usage();
+ } /* switch */
+ } while (*++flagptr);
+}
+
+#if 0
+/* not used yet */
+static void choose_dimensions(void)
+{
+ struct winsize ws;
+ char *columns;
+ /* screen_cols is 80 by default */
+ if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 30)
+ screen_cols = ws.ws_col;
+ columns = getenv("COLUMNS");
+ if (columns && *columns) {
+ long t;
+ char *endptr;
+ t = strtol(columns, &endptr, 0);
+ if (!*endptr && (t > 30) && (t < (long)999999999))
+ screen_cols = (int)t;
+ }
+ if (w_count && (screen_cols < 132))
+ screen_cols = 132;
+ if (w_count > 1)
+ screen_cols = 999999999;
+}
+#endif
+
+static void arg_parse(int argc, char *argv[])
+{
+ int sel = 0; /* to verify option sanity */
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+ /**** iterate over the args ****/
+ while (++thisarg < ps_argc) {
+ flagptr = ps_argv[thisarg];
+ switch (*flagptr) {
+ case '0'...'9':
+ show_args = 1;
+ parse_pid(flagptr);
+ break;
+ case '-':
+ flagptr++;
+ parse_sysv_option();
+ break;
+ default:
+ show_args = 1;
+ parse_bsd_option();
+ break;
+ }
+ }
+ /**** sanity check and clean-up ****/
+ if (want_one_pid)
+ sel++;
+ if (want_one_command)
+ sel++;
+ if (select_notty || select_all)
+ sel++;
+ if (sel > 1 || select_notty > 1 || select_all > 1 || bsd_c_option > 1
+ || old_h_option > 1)
+ usage();
+ if (bsd_c_option)
+ show_args = 0;
+}
+
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid)
+{
+ char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
+ int num;
+ int fd;
+ char *tmp;
+ struct stat sb; /* stat() used to get EUID */
+
+ snprintf(buf, 32, "/proc/%d/stat", pid);
+ fd = open(buf, O_RDONLY, 0);
+ if (fd == -1)
+ return 0;
+ num = read(fd, buf, sizeof buf - 1);
+ fstat(fd, &sb);
+ P_euid = sb.st_uid;
+ close(fd);
+ if (num < 80)
+ return 0;
+ buf[num] = '\0';
+ tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+ *tmp = '\0'; /* replace trailing ')' with NUL */
+ /* parse these two strings separately, skipping the leading "(". */
+ memset(P_cmd, 0, sizeof P_cmd); /* clear */
+ sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */
+ num = sscanf(tmp + 2, /* skip space after ')' too */
+ "%c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu %lu %lu " "%ld %ld %ld %ld %ld %ld " "%lu %lu " "%ld " "%lu %lu %lu %lu %lu %lu " "%u %u %u %u " /* no use for RT signals */
+ "%lu %lu %lu",
+ &P_state,
+ &P_ppid, &P_pgrp, &P_session, &P_tty, &P_tpgid,
+ &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt,
+ &P_utime, &P_stime, &P_cutime, &P_cstime, &P_priority,
+ &P_nice, &P_timeout, &P_it_real_value, &P_start_time,
+ &P_vsize, &P_rss, &P_rss_rlim, &P_start_code, &P_end_code,
+ &P_start_stack, &P_kstk_esp, &P_kstk_eip, &P_signal,
+ &P_blocked, &P_sigignore, &P_sigcatch, &P_wchan, &P_nswap,
+ &P_cnswap);
+/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+ P_vsize /= 1024;
+ P_rss <<= page_shift - 10;
+ if (num < 30)
+ return 0;
+ if (P_pid != pid)
+ return 0;
+ return 1;
+}
+
+static const char *do_time(unsigned long t)
+{
+ int hh, mm, ss;
+ static char buf[32];
+ int cnt = 0;
+ t /= HZ;
+ ss = t % 60;
+ t /= 60;
+ mm = t % 60;
+ t /= 60;
+ hh = t % 24;
+ t /= 24;
+ if (t)
+ cnt = snprintf(buf, sizeof buf, "%d-", (int)t);
+ snprintf(cnt + buf, sizeof(buf) - cnt, "%02d:%02d:%02d", hh, mm, ss);
+ return buf;
+}
+
+static void print_proc(void)
+{
+ char tty[16];
+ snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty >> 8) & 0xff,
+ P_tty & 0xff);
+ switch (ps_format) {
+ case 0:
+ printf("%5d %s %s", P_pid, tty, do_time(P_utime + P_stime));
+ break;
+ case 'o':
+ printf("%d\n", P_pid);
+ return; /* don't want the command */
+ case 'l':
+ printf("%03x %c %5d %5d %5d - %3d %3d - "
+ "%5ld %06x %s %s",
+ (unsigned)P_flags & 0x777, P_state, P_euid, P_pid,
+ P_ppid, (int)P_priority, (int)P_nice,
+ P_vsize >> (page_shift - 10),
+ (unsigned)(P_wchan & 0xffffff), tty,
+ do_time(P_utime + P_stime)
+ );
+ break;
+ case 'f':
+ printf("%5d %5d %5d - - %s %s",
+ P_euid, P_pid, P_ppid, tty, do_time(P_utime + P_stime)
+ );
+ break;
+ case 'j':
+ printf("%5d %5d %5d %s %s",
+ P_pid, P_pgrp, P_session, tty, do_time(P_utime + P_stime)
+ );
+ break;
+ case 'u' | 0x80:
+ printf("%5d %5d - - %5ld %5ld %s %c - %s",
+ P_euid, P_pid, P_vsize, P_rss, tty, P_state,
+ do_time(P_utime + P_stime)
+ );
+ break;
+ case 'v' | 0x80:
+ printf("%5d %s %c %s %6d - - %5d -",
+ P_pid, tty, P_state, do_time(P_utime + P_stime),
+ (int)P_maj_flt, (int)P_rss);
+ break;
+ case 'j' | 0x80:
+ printf("%5d %5d %5d %5d %s %5d %c %5d %s",
+ P_ppid, P_pid, P_pgrp, P_session, tty, P_tpgid, P_state,
+ P_euid, do_time(P_utime + P_stime)
+ );
+ break;
+ case 'l' | 0x80:
+ printf("%03x %5d %5d %5d %3d %3d "
+ "%5ld %4ld %06x %c %s %s",
+ (unsigned)P_flags & 0x777, P_euid, P_pid, P_ppid,
+ (int)P_priority, (int)P_nice, P_vsize, P_rss,
+ (unsigned)(P_wchan & 0xffffff), P_state, tty,
+ do_time(P_utime + P_stime)
+ );
+ break;
+ default:
+ break;
+ }
+ if (show_args)
+ printf(" [%s]\n", P_cmd);
+ else
+ printf(" %s\n", P_cmd);
+}
+
+int main(int argc, char *argv[])
+{
+ arg_parse(argc, argv);
+
+ page_shift = __getpageshift();
+
+#if 0
+ choose_dimensions();
+#endif
+ if (!old_h_option) {
+ const char *head;
+ switch (ps_format) {
+ default: /* can't happen */
+ case 0:
+ head = " PID TTY TIME CMD";
+ break;
+ case 'l':
+ head =
+ " F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD";
+ break;
+ case 'f':
+ head =
+ " UID PID PPID C STIME TTY TIME CMD";
+ break;
+ case 'j':
+ head = " PID PGID SID TTY TIME CMD";
+ break;
+ case 'u' | 0x80:
+ head =
+ " UID PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND";
+ break;
+ case 'v' | 0x80:
+ head =
+ " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND";
+ break;
+ case 'j' | 0x80:
+ head =
+ " PPID PID PGID SID TTY TPGID S UID TIME COMMAND";
+ break;
+ case 'l' | 0x80:
+ head =
+ " F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND";
+ break;
+ }
+ printf("%s\n", head);
+ }
+ if (want_one_pid) {
+ if (stat2proc(want_one_pid))
+ print_proc();
+ else
+ exit(1);
+ } else {
+ struct dirent *ent; /* dirent handle */
+ DIR *dir;
+ int ouruid;
+ int found_a_proc;
+ found_a_proc = 0;
+ ouruid = getuid();
+ dir = opendir("/proc");
+ if (!dir)
+ exit(1);
+ while ((ent = readdir(dir))) {
+ if (*ent->d_name < '0' || *ent->d_name > '9')
+ continue;
+ if (!stat2proc(atoi(ent->d_name)))
+ continue;
+ if (want_one_command) {
+ if (strcmp(want_one_command, P_cmd))
+ continue;
+ } else {
+ if (!select_notty && P_tty == -1)
+ continue;
+ if (!select_all && P_euid != ouruid)
+ continue;
+ }
+ found_a_proc++;
+ print_proc();
+ }
+ closedir(dir);
+ exit(!found_a_proc);
+ }
+ return 0;
+}