diff options
Diffstat (limited to 'libps/spec.c')
-rw-r--r-- | libps/spec.c | 1152 |
1 files changed, 1152 insertions, 0 deletions
diff --git a/libps/spec.c b/libps/spec.c new file mode 100644 index 00000000..1e30d95b --- /dev/null +++ b/libps/spec.c @@ -0,0 +1,1152 @@ +/* Access, formatting, & comparison routines for printing process info. + + Copyright (C) 1995,96,97,99 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + 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, 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. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <pwd.h> +#include <hurd/resource.h> +#include <unistd.h> +#include <string.h> +#include <timefmt.h> +#include <sys/time.h> + +#include "ps.h" +#include "common.h" + +/* XXX */ +static char *get_syscall_name (int num) { return 0; } +static char *get_rpc_name (mach_msg_id_t it) { return 0; } + +/* ---------------------------------------------------------------- */ +/* Getter definitions */ + +typedef void (*vf)(); + +static int +ps_get_pid (struct proc_stat *ps) +{ + return proc_stat_pid (ps); +} +const struct ps_getter ps_pid_getter = +{"pid", PSTAT_PID, (vf) ps_get_pid}; + +static int +ps_get_thread_index (struct proc_stat *ps) +{ + return proc_stat_thread_index (ps); +} +const struct ps_getter ps_thread_index_getter = +{"thread_index", PSTAT_THREAD, (vf) ps_get_thread_index}; + +static struct ps_user * +ps_get_owner (struct proc_stat *ps) +{ + return proc_stat_owner (ps); +} +const struct ps_getter ps_owner_getter = +{"owner", PSTAT_OWNER, (vf) ps_get_owner}; + +static int +ps_get_owner_uid (struct proc_stat *ps) +{ + return proc_stat_owner_uid (ps); +} +const struct ps_getter ps_owner_uid_getter = +{"uid", PSTAT_OWNER_UID, (vf) ps_get_owner_uid}; + +static int +ps_get_ppid (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->ppid; +} +const struct ps_getter ps_ppid_getter = +{"ppid", PSTAT_PROC_INFO, (vf) ps_get_ppid}; + +static int +ps_get_pgrp (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->pgrp; +} +const struct ps_getter ps_pgrp_getter = +{"pgrp", PSTAT_PROC_INFO, (vf) ps_get_pgrp}; + +static int +ps_get_session (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->session; +} +const struct ps_getter ps_session_getter = +{"session", PSTAT_PROC_INFO, (vf) ps_get_session}; + +static int +ps_get_login_col (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->logincollection; +} +const struct ps_getter ps_login_col_getter = +{"login_col", PSTAT_PROC_INFO, (vf) ps_get_login_col}; + +static int +ps_get_num_threads (struct proc_stat *ps) +{ + return proc_stat_num_threads (ps); +} +const struct ps_getter ps_num_threads_getter = +{"num_threads", PSTAT_NUM_THREADS, (vf)ps_get_num_threads}; + +static void +ps_get_args (struct proc_stat *ps, char **args_p, int *args_len_p) +{ + *args_p = proc_stat_args (ps); + *args_len_p = proc_stat_args_len (ps); +} +const struct ps_getter ps_args_getter = +{"args", PSTAT_ARGS, ps_get_args}; + +static void +ps_get_env (struct proc_stat *ps, char **env_p, int *env_len_p) +{ + *env_p = proc_stat_env (ps); + *env_len_p = proc_stat_env_len (ps); +} +const struct ps_getter ps_env_getter = +{"env", PSTAT_ENV, ps_get_env}; + +static int +ps_get_state (struct proc_stat *ps) +{ + return proc_stat_state (ps); +} +const struct ps_getter ps_state_getter = +{"state", PSTAT_STATE, (vf) ps_get_state}; + +static void +ps_get_wait (struct proc_stat *ps, char **wait, int *rpc) +{ + *wait = ps->thread_wait; + *rpc = ps->thread_rpc; +} +const struct ps_getter ps_wait_getter = +{"wait", PSTAT_THREAD_WAIT, ps_get_wait}; + +static int +ps_get_vsize (struct proc_stat *ps) +{ + return proc_stat_task_basic_info (ps)->virtual_size; +} +const struct ps_getter ps_vsize_getter = +{"vsize", PSTAT_TASK_BASIC, (vf) ps_get_vsize}; + +static int +ps_get_rsize (struct proc_stat *ps) +{ + return proc_stat_task_basic_info (ps)->resident_size; +} +const struct ps_getter ps_rsize_getter = +{"rsize", PSTAT_TASK_BASIC, (vf) ps_get_rsize}; + +static int +ps_get_cur_priority (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->cur_priority; +} +const struct ps_getter ps_cur_priority_getter = +{"cur_priority", PSTAT_THREAD_BASIC, (vf) ps_get_cur_priority}; + +static int +ps_get_base_priority (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->base_priority; +} +const struct ps_getter ps_base_priority_getter = +{"base_priority", PSTAT_THREAD_BASIC, (vf) ps_get_base_priority}; + +static int +ps_get_max_priority (struct proc_stat *ps) +{ + return proc_stat_thread_sched_info (ps)->max_priority; +} +const struct ps_getter ps_max_priority_getter = +{"max_priority", PSTAT_THREAD_SCHED, (vf) ps_get_max_priority}; + +static void +ps_get_usr_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t tvt = proc_stat_thread_basic_info (ps)->user_time; + tv->tv_sec = tvt.seconds; + tv->tv_usec = tvt.microseconds; +} +const struct ps_getter ps_usr_time_getter = +{"usr_time", PSTAT_THREAD_BASIC, ps_get_usr_time}; + +static void +ps_get_sys_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t tvt = proc_stat_thread_basic_info (ps)->system_time; + tv->tv_sec = tvt.seconds; + tv->tv_usec = tvt.microseconds; +} +const struct ps_getter ps_sys_time_getter = +{"sys_time", PSTAT_THREAD_BASIC, ps_get_sys_time}; + +static void +ps_get_tot_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t tvt = proc_stat_thread_basic_info (ps)->user_time; + time_value_add (&tvt, &proc_stat_thread_basic_info (ps)->system_time); + tv->tv_sec = tvt.seconds; + tv->tv_usec = tvt.microseconds; +} +const struct ps_getter ps_tot_time_getter = +{"tot_time", PSTAT_THREAD_BASIC, ps_get_tot_time}; + +static void +ps_get_start_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t *const tvt = &proc_stat_task_basic_info (ps)->creation_time; + tv->tv_sec = tvt->seconds; + tv->tv_usec = tvt->microseconds; +} +const struct ps_getter ps_start_time_getter = +{"start_time", PSTAT_TASK_BASIC, ps_get_start_time}; + +static float +ps_get_rmem_frac (struct proc_stat *ps) +{ + static int mem_size = 0; + + if (mem_size == 0) + { + host_basic_info_t info; + error_t err = ps_host_basic_info (&info); + if (err == 0) + mem_size = info->memory_size; + } + + if (mem_size > 0) + return + (float)proc_stat_task_basic_info (ps)->resident_size + / (float)mem_size; + else + return 0.0; +} +const struct ps_getter ps_rmem_frac_getter = +{"rmem_frac", PSTAT_TASK_BASIC, (vf) ps_get_rmem_frac}; + +static float +ps_get_cpu_frac (struct proc_stat *ps) +{ + return (float) proc_stat_thread_basic_info (ps)->cpu_usage + / (float) TH_USAGE_SCALE; +} +const struct ps_getter ps_cpu_frac_getter = +{"cpu_frac", PSTAT_THREAD_BASIC, (vf) ps_get_cpu_frac}; + +static int +ps_get_sleep (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->sleep_time; +} +const struct ps_getter ps_sleep_getter = +{"sleep", PSTAT_THREAD_BASIC, (vf) ps_get_sleep}; + +static int +ps_get_susp_count (struct proc_stat *ps) +{ + return proc_stat_suspend_count (ps); +} +const struct ps_getter ps_susp_count_getter = +{"susp_count", PSTAT_SUSPEND_COUNT, (vf) ps_get_susp_count}; + +static int +ps_get_proc_susp_count (struct proc_stat *ps) +{ + return proc_stat_task_basic_info (ps)->suspend_count; +} +const struct ps_getter ps_proc_susp_count_getter = +{"proc_susp_count", PSTAT_TASK_BASIC, (vf) ps_get_proc_susp_count}; + +static int +ps_get_thread_susp_count (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->suspend_count; +} +const struct ps_getter ps_thread_susp_count_getter = +{"thread_susp_count", PSTAT_SUSPEND_COUNT, (vf) ps_get_thread_susp_count}; + +static struct ps_tty * +ps_get_tty (struct proc_stat *ps) +{ + return proc_stat_tty (ps); +} +const struct ps_getter ps_tty_getter = +{"tty", PSTAT_TTY, (vf)ps_get_tty}; + +static int +ps_get_page_faults (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->faults; +} +const struct ps_getter ps_page_faults_getter = +{"page_faults", PSTAT_TASK_EVENTS, (vf) ps_get_page_faults}; + +static int +ps_get_cow_faults (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->cow_faults; +} +const struct ps_getter ps_cow_faults_getter = +{"cow_faults", PSTAT_TASK_EVENTS, (vf) ps_get_cow_faults}; + +static int +ps_get_pageins (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->pageins; +} +const struct ps_getter ps_pageins_getter = +{"pageins", PSTAT_TASK_EVENTS, (vf) ps_get_pageins}; + +static int +ps_get_msgs_sent (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->messages_sent; +} +const struct ps_getter ps_msgs_sent_getter = +{"msgs_sent", PSTAT_TASK_EVENTS, (vf) ps_get_msgs_sent}; + +static int +ps_get_msgs_rcvd (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->messages_received; +} +const struct ps_getter ps_msgs_rcvd_getter = +{"msgs_rcvd", PSTAT_TASK_EVENTS, (vf) ps_get_msgs_rcvd}; + +static int +ps_get_zero_fills (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->zero_fills; +} +const struct ps_getter ps_zero_fills_getter = +{"zero_fills", PSTAT_TASK_EVENTS, (vf) ps_get_zero_fills}; + +/* ---------------------------------------------------------------- */ +/* some printing functions */ + +/* G () is a helpful macro that just returns the getter G's access function + cast into a function pointer returning TYPE, as how the function should be + called varies depending on the getter. */ +#define G(getter,type) ((type (*)())((getter)->fn)) + +/* Similar to G, but takes a fmt field and uses its getter. */ +#define FG(field,type) G(field->spec->getter, type) + +error_t +ps_emit_int (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + return ps_stream_write_int_field (stream, FG (field, int)(ps), field->width); +} + +error_t +ps_emit_nz_int (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int value = FG (field, int)(ps); + if (value) + return ps_stream_write_int_field (stream, value, field->width); + else + return ps_stream_write_field (stream, "-", field->width); +} + +error_t +ps_emit_priority (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + return + ps_stream_write_int_field (stream, + MACH_PRIORITY_TO_NICE (FG (field, int)(ps)), + field->width); +} + +error_t +ps_emit_num_blocks (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + sprintf(buf, "%d", FG (field, int)(ps) / 1024); + return ps_stream_write_field (stream, buf, field->width); +} + +int +sprint_frac_value (char *buf, + int value, int min_value_len, + int frac, int frac_scale, + int width) +{ + int value_len; + int frac_len; + + if (value >= 100) /* the integer part */ + value_len = 3; + else if (value >= 10) + value_len = 2; + else + value_len = 1; + + while (value_len < min_value_len--) + *buf++ = '0'; + + for (frac_len = frac_scale + ; frac_len > 0 && (width < value_len + 1 + frac_len || frac % 10 == 0) + ; frac_len--) + frac /= 10; + + if (frac_len > 0) + sprintf (buf, "%d.%0*d", value, frac_len, frac); + else + sprintf (buf, "%d", value); + + return strlen (buf); +} + +error_t +ps_emit_percent (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + int width = field->width; + float perc = FG (field, float)(ps) * 100; + + if (width == 0) + sprintf (buf, "%g", perc); + else if (ABS (width) > 3) + sprintf(buf, "%.*f", ABS (width) - 3, perc); + else + sprintf (buf, "%d", (int) perc); + + return ps_stream_write_field (stream, buf, width); +} + +/* prints its value nicely */ +error_t +ps_emit_nice_int (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + int value = FG (field, int)(ps); + char *sfx = " KMG"; + int frac = 0; + + while (value >= 1024) + { + frac = ((value & 0x3FF) * 1000) >> 10; + value >>= 10; + sfx++; + } + + sprintf(buf + + sprint_frac_value (buf, value, 1, frac, 3, ABS (field->width) - 1), + "%c", *sfx); + + return ps_stream_write_field (stream, buf, field->width); +} + +error_t +ps_emit_seconds (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + struct timeval tv; + int width = field->width, prec = field->precision; + + FG (field, void)(ps, &tv); + + if ((field->flags & PS_FMT_FIELD_COLON_MOD) && tv.tv_sec == 0) + strcpy (buf, "-"); + else + fmt_seconds (&tv, !(field->flags & PS_FMT_FIELD_AT_MOD), prec, ABS (width), + buf, sizeof (buf)); + + return ps_stream_write_field (stream, buf, width); +} + +error_t +ps_emit_minutes (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + struct timeval tv; + int width = field->width; + + FG (field, void)(ps, &tv); + + if ((field->flags & PS_FMT_FIELD_COLON_MOD) && tv.tv_sec < 60) + strcpy (buf, "-"); + else + fmt_minutes (&tv, !(field->flags & PS_FMT_FIELD_AT_MOD), ABS (width), + buf, sizeof (buf)); + + return ps_stream_write_field (stream, buf, width); +} + +error_t +ps_emit_past_time (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + static struct timeval now; + char buf[20]; + struct timeval tv; + int width = field->width; + + FG (field, void)(ps, &tv); + + if (now.tv_sec == 0 && gettimeofday (&now, 0) < 0) + return errno; + + fmt_past_time (&tv, &now, ABS (width), buf, sizeof buf); + + return ps_stream_write_field (stream, buf, width); +} + +error_t +ps_emit_uid (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int uid = FG (field, int)(ps); + if (uid < 0) + return ps_stream_write_field (stream, "-", field->width); + else + return ps_stream_write_int_field (stream, uid, field->width); +} + +error_t +ps_emit_uname (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int width = field->width; + struct ps_user *u = FG (field, struct ps_user *)(ps); + if (u) + { + struct passwd *pw = ps_user_passwd (u); + if (pw == NULL) + return ps_stream_write_int_field (stream, ps_user_uid (u), width); + else + return ps_stream_write_field (stream, pw->pw_name, width); + } + else + return ps_stream_write_field (stream, "-", width); +} + +error_t +ps_emit_user_name (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int width = field->width; + struct ps_user *u = FG (field, struct ps_user *)(ps); + if (u) + { + struct passwd *pw = ps_user_passwd (u); + if (pw == NULL) + { + char buf[20]; + sprintf (buf, "(UID %d)", pw->pw_uid); + return ps_stream_write_field (stream, buf, width); + } + else + return ps_stream_write_field (stream, pw->pw_gecos, width); + } + else + return ps_stream_write_field (stream, "-", width); +} + +/* prints a string with embedded nuls as spaces */ +error_t +ps_emit_args (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char *s0, *p, *q; + int s0len; + int width = field->width; + int fwidth = ABS (width); + char static_buf[200]; + char *buf = static_buf; + + FG (field, void)(ps, &s0, &s0len); + + if (!s0 || s0len == 0 ) + strcpy (buf, "-"); + else + { + if (s0len > sizeof static_buf) + { + buf = malloc (s0len + 1); + if (buf == NULL) + return ENOMEM; + } + + if (fwidth == 0 || fwidth > s0len) + fwidth = s0len; + + for (p = buf, q = s0; fwidth-- > 0; p++, q++) + { + int ch = *q; + *p = (ch == '\0' ? ' ' : ch); + } + if (q > s0 && *(q - 1) == '\0') + *--p = '\0'; + else + *p = '\0'; + } + + { + error_t err = ps_stream_write_trunc_field (stream, buf, width); + if (buf != static_buf) + free (buf); + return err; + } +} + +error_t +ps_emit_string (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char *str; + int len; + + FG (field, void)(ps, &str, &len); + + if (!str || len == 0) + str = "-"; + + return ps_stream_write_trunc_field (stream, str, field->width); +} + +error_t +ps_emit_tty_name (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + const char *name = "-"; + struct ps_tty *tty = FG (field, struct ps_tty *)(ps); + + if (tty) + { + name = ps_tty_short_name (tty); + if (name == NULL || *name == '\0') + name = "?"; + } + + return ps_stream_write_field (stream, name, field->width); +} + +struct state_shadow +{ + /* If any states in STATES are set, the states in shadow are suppressed. */ + int states; + int shadow; +}; + +static const struct state_shadow +state_shadows[] = { + /* If the process has no parent, it's not a hurd process, and various hurd + process bits are likely to be noise, so turn them off (but leave the + noparent bit on). */ + { PSTAT_STATE_P_NOPARENT, (PSTAT_STATE_P_ATTRS & ~PSTAT_STATE_P_NOPARENT) }, + /* Don't show sleeping thread if one is running, or the process is stopped.*/ + { PSTAT_STATE_T_RUN | PSTAT_STATE_P_STOP, + PSTAT_STATE_T_SLEEP | PSTAT_STATE_T_IDLE | PSTAT_STATE_T_WAIT }, + /* Only show the longest sleep. */ + { PSTAT_STATE_T_IDLE, PSTAT_STATE_T_SLEEP | PSTAT_STATE_T_WAIT }, + { PSTAT_STATE_T_SLEEP, PSTAT_STATE_T_WAIT }, + /* Turn off the thread stop bits if any thread is not stopped. This is + generally reasonable, as threads are often suspended to be frobed; if + they're all suspended, then something's odd (probably in the debugger, + or crashed). */ + { PSTAT_STATE_T_STATES & ~PSTAT_STATE_T_HALT, + PSTAT_STATE_T_HALT | PSTAT_STATE_T_UNCLEAN }, + { 0 } +}; + +error_t +ps_emit_state (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char *tags; + int raw_state = FG (field, int)(ps); + int state = raw_state; + char buf[20], *p = buf; + const struct state_shadow *shadow = state_shadows; + + while (shadow->states) + { + if (raw_state & shadow->states) + state &= ~shadow->shadow; + shadow++; + } + + for (tags = proc_stat_state_tags + ; state != 0 && *tags != '\0' + ; state >>= 1, tags++) + if (state & 1) + *p++ = *tags; + + *p = '\0'; + + return ps_stream_write_field (stream, buf, field->width); +} + +error_t +ps_emit_wait (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int rpc; + char *wait; + char buf[80]; + + FG (field, void)(ps, &wait, &rpc); + + if (wait == 0) + return ps_stream_write_field (stream, "?", field->width); + else if (*wait == 0) + return ps_stream_write_field (stream, "-", field->width); + else if (strcmp (wait, "kernel") == 0) + /* A syscall. RPC is actually the syscall number. */ + { + extern char *get_syscall_name (int num); + char *name = get_syscall_name (rpc); + if (! name) + { + sprintf (buf, "syscall:%d", -rpc); + name = buf; + } + return ps_stream_write_field (stream, name, field->width); + } + else if (rpc) + /* An rpc (with msg id RPC); WAIT describes the dest port. */ + { + char port_name_buf[20]; + extern char *get_rpc_name (mach_msg_id_t num); + char *name = get_rpc_name (rpc); + + /* See if we should give a more useful name for the port. */ + if (strcmp (wait, "init#0") == 0) + wait = "cwd"; /* Current directory */ + else if (strcmp (wait, "init#1") == 0) + wait = "root"; /* Root directory */ + else if (strcmp (wait, "init#2") == 0) + wait = "auth"; /* Auth port */ + else if (strcmp (wait, "init#3") == 0) + wait = "proc"; /* Proc port */ + else if (strcmp (wait, "init#4") == 0) + wait = "cttyid"; /* Ctty id port */ + else if (strcmp (wait, "init#5") == 0) + wait = "boot"; /* Bootstrap port */ + else + /* See if we can shorten the name to fit better. */ + { + char *abbrev = 0, *num = 0; + if (strncmp (wait, "fd#", 3) == 0) + abbrev = "fd", num = wait + 3; + else if (strncmp (wait, "bgfd#", 5) == 0) + abbrev = "bg", num = wait + 5; + else if (strncmp (wait, "port#", 5) == 0) + abbrev = "", num = wait + 5; + if (abbrev) + { + snprintf (port_name_buf, sizeof port_name_buf, + "%s%s", abbrev, num); + wait = port_name_buf; + } + } + + if (name) + snprintf (buf, sizeof buf, "%s:%s", wait, name); + else + snprintf (buf, sizeof buf, "%s:%d", wait, rpc); + + return ps_stream_write_field (stream, buf, field->width); + } + else + return ps_stream_write_field (stream, wait, field->width); +} +/* ---------------------------------------------------------------- */ +/* comparison functions */ + +/* Evaluates CALL if both s1 & s2 are non-NULL, and otherwise returns -1, 0, + or 1 ala strcmp, considering NULL to be less than non-NULL. */ +#define GUARDED_CMP(s1, s2, call) \ + ((s1) == NULL ? (((s2) == NULL) ? 0 : -1) : ((s2) == NULL ? 1 : (call))) + +int +ps_cmp_ints (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + int (*gf)() = G (getter, int); + int v1 = gf(ps1), v2 = gf (ps2); + return v1 == v2 ? 0 : v1 < v2 ? -1 : 1; +} + +int +ps_cmp_floats (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + float (*gf)() = G (getter, float); + float v1 = gf(ps1), v2 = gf (ps2); + return v1 == v2 ? 0 : v1 < v2 ? -1 : 1; +} + +int +ps_cmp_uids (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + struct ps_user *(*gf)() = G (getter, struct ps_user *); + struct ps_user *u1 = gf (ps1), *u2 = gf (ps2); + return (u1 ? ps_user_uid (u1) : -1) - (u2 ? ps_user_uid (u2) : -1); +} + +int +ps_cmp_unames (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + struct ps_user *(*gf)() = G (getter, struct ps_user *); + struct ps_user *u1 = gf (ps1), *u2 = gf (ps2); + struct passwd *pw1 = u1 ? ps_user_passwd (u1) : 0; + struct passwd *pw2 = u2 ? ps_user_passwd (u2) : 0; + return GUARDED_CMP (pw1, pw2, strcmp (pw1->pw_name, pw2->pw_name)); +} + +int +ps_cmp_strings (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + void (*gf)() = G (getter, void); + char *s1, *s2; + int s1len, s2len; + + /* Get both strings */ + gf (ps1, &s1, &s1len); + gf (ps2, &s2, &s2len); + + return GUARDED_CMP(s1, s2, strncmp(s1, s2, MIN (s1len, s2len))); +} + +int +ps_cmp_times (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + void (*g)() = G (getter, void); + struct timeval tv1, tv2; + + g (ps1, &tv1); + g (ps2, &tv2); + + return + tv1.tv_sec > tv2.tv_sec ? 1 + : tv1.tv_sec < tv2.tv_sec ? -1 + : tv1.tv_usec > tv2.tv_usec ? 1 + : tv2.tv_usec < tv2.tv_usec ? -1 + : 0; +} + +/* ---------------------------------------------------------------- */ +/* `Nominal' functions -- return true for `unexciting' values. */ + +/* For many things, zero is not so interesting. */ +int +ps_nominal_zint (struct proc_stat *ps, const struct ps_getter *getter) +{ + return G (getter, int)(ps) == 0; +} + +/* Neither is an empty string. */ +int +ps_nominal_string (struct proc_stat *ps, const struct ps_getter *getter) +{ + char *str; + size_t len; + G (getter, char *)(ps, &str, &len); + return !str || len == 0 || (len == 1 && *str == '-'); +} + +/* Priorities are similar, but have to be converted to the unix nice scale + first. */ +int +ps_nominal_pri (struct proc_stat *ps, const struct ps_getter *getter) +{ + return MACH_PRIORITY_TO_NICE(G (getter, int)(ps)) == 0; +} + +/* Hurd processes usually have 2 threads; XXX is there someplace we get get + this number from? */ +int +ps_nominal_nth (struct proc_stat *ps, const struct ps_getter *getter) +{ + return G (getter, int)(ps) == 2; +} + +static int own_uid = -2; /* -1 means no uid at all. */ + +/* A user is nominal if it's the current user. */ +int +ps_nominal_user (struct proc_stat *ps, const struct ps_getter *getter) +{ + struct ps_user *u = G (getter, struct ps_user *)(ps); + if (own_uid == -2) + own_uid = getuid (); + return own_uid >= 0 && u && u->uid == own_uid; +} + +/* A uid is nominal if it's that of the current user. */ +int +ps_nominal_uid (struct proc_stat *ps, const struct ps_getter *getter) +{ + uid_t uid = G (getter, uid_t)(ps); + if (own_uid == -2) + own_uid = getuid (); + return own_uid >= 0 && uid == own_uid; +} + +/* ---------------------------------------------------------------- */ + +/* Returns the first entry called NAME in the vector of fmt_specs SPECS. If + the result is in fact an alias entry, returns in ALIASED_TO the name of + the desired source. */ +static const struct ps_fmt_spec * +specv_find (const struct ps_fmt_spec *specs, const char *name, + char **aliased_to) +{ + while (! ps_fmt_spec_is_end (specs)) + { + char *alias = index (specs->name, '='); + if (alias) + { + unsigned name_len = strlen (name); + + if (name_len == alias - specs->name + && strncasecmp (name, specs->name, name_len) == 0) + /* SPECS is an alias, lookup what it refs to. */ + { + *aliased_to = alias + 1; + return specs; + } + } + else + if (strcasecmp (specs->name, name) == 0) + return specs; + specs++; + } + + return 0; +} + +/* Number of specs allocated in each block of expansions. */ +#define EXP_BLOCK_SIZE 20 + +/* A node in a linked list of spec vectors. */ +struct ps_fmt_spec_block +{ + struct ps_fmt_spec_block *next; + struct ps_fmt_spec specs[EXP_BLOCK_SIZE]; +}; + +/* Adds a new alias expansion, using fields from ALIAS, where non-zero, + otherwise SRC, to SPECS. */ +struct ps_fmt_spec * +specs_add_alias (struct ps_fmt_specs *specs, + const struct ps_fmt_spec *alias, + const struct ps_fmt_spec *src) +{ + struct ps_fmt_spec *exp; + struct ps_fmt_spec_block *block; + char *name_end = index (alias->name, '='); + size_t name_len = name_end ? name_end - alias->name : strlen (alias->name); + + for (block = specs->expansions; block; block = block->next) + { + exp = block->specs; + while (! ps_fmt_spec_is_end (exp)) + exp++; + if (exp + 1 < block->specs + EXP_BLOCK_SIZE) + /* Found some empty space at EXP. */ + break; + } + + if (! block) + /* Ran out of blocks, we gotta make a new one. */ + { + block = malloc (sizeof (struct ps_fmt_spec_block)); + if (! block) + return 0; + block->next = specs->expansions; + specs->expansions = block; + exp = block->specs; + } + + /* EXP gets its name from ALIAS, but only the bit before the alias marker. */ + exp->name = malloc (name_len + 1); + if (! exp->name) + return 0; + bcopy ((char *)alias->name, (char *)exp->name, name_len); + ((char *)exp->name)[name_len] = '\0'; + + /* Copy the rest of the fields from ALIAS, but defaulting to SRC. */ + exp->title = alias->title ?: src->title; + exp->width = alias->width ?: src->width; + exp->precision = alias->precision >= 0 ? alias->precision : src->precision; + exp->flags = src->flags ^ alias->flags; + exp->getter = alias->getter ?: src->getter; + exp->output_fn = alias->output_fn ?: src->output_fn; + exp->cmp_fn = alias->cmp_fn ?: src->cmp_fn; + exp->nominal_fn = alias->nominal_fn ?: src->nominal_fn; + + /* Now add the list-end marker. */ + bzero (exp + 1, sizeof (*exp)); + + return exp; +} + +const struct ps_fmt_spec * +ps_fmt_specs_find (struct ps_fmt_specs *specs, const char *name) +{ + if (specs) /* Allow NULL to make recursion more handy. */ + { + struct ps_fmt_spec_block *block; + char *aliased_to = 0; + const struct ps_fmt_spec *s = 0; + + /* If SPECS contains any alias expansions, look there first. */ + for (block = specs->expansions; block && !s; block = block->next) + s = specv_find (block->specs, name, &aliased_to); + + if (! s) + /* Look in the local list of specs. */ + s = specv_find (specs->specs, name, &aliased_to); + + if (s) + { + if (aliased_to) + { + const struct ps_fmt_spec *src; /* What S is an alias to. */ + + if (strcasecmp (name, aliased_to) == 0) + /* An alias to the same name (useful to just change some + property) -- start looking up in the parent. */ + src = ps_fmt_specs_find (specs->parent, aliased_to); + else + src = ps_fmt_specs_find (specs, aliased_to); + + if (! src) + return 0; + + s = specs_add_alias (specs, s, src); + } + } + else + /* Try again with our parent. */ + s = ps_fmt_specs_find (specs->parent, name); + + return s; + } + else + return 0; +} + +/* ---------------------------------------------------------------- */ + +static const struct ps_fmt_spec specs[] = +{ + {"PID", 0, -5, -1, 0, + &ps_pid_getter, ps_emit_int, ps_cmp_ints, 0}, + {"TH", "TH#", -2, -1, 0, + &ps_thread_index_getter,ps_emit_int, ps_cmp_ints, 0}, + {"PPID", 0, -5, -1, 0, + &ps_ppid_getter, ps_emit_int, ps_cmp_ints, 0}, + {"UID", 0, -4, -1, PS_FMT_FIELD_KEEP, + &ps_owner_uid_getter, ps_emit_uid, ps_cmp_ints, ps_nominal_uid}, + {"User", 0, 8, -1, PS_FMT_FIELD_KEEP, + &ps_owner_getter, ps_emit_uname, ps_cmp_unames, ps_nominal_user}, + {"NTh", 0, -2, -1, 0, + &ps_num_threads_getter, ps_emit_int, ps_cmp_ints, ps_nominal_nth}, + {"PGrp", 0, -5, -1, 0, + &ps_pgrp_getter, ps_emit_int, ps_cmp_ints, 0}, + {"Sess", 0, -5, -1, 0, + &ps_session_getter, ps_emit_int, ps_cmp_ints, 0}, + {"LColl", 0, -5, -1, 0, + &ps_login_col_getter, ps_emit_int, ps_cmp_ints, 0}, + {"Args", 0, 0, -1, 0, + &ps_args_getter, ps_emit_args, ps_cmp_strings,ps_nominal_string}, + {"Arg0", 0, 0, -1, 0, + &ps_args_getter, ps_emit_string, ps_cmp_strings,ps_nominal_string}, + {"Env", 0, 0, -1, 0, + &ps_env_getter, ps_emit_args, ps_cmp_strings,ps_nominal_string}, + {"Start", 0, -7, 1, 0, + &ps_start_time_getter, ps_emit_past_time, ps_cmp_times,0}, + {"Time", 0, -8, 2, 0, + &ps_tot_time_getter, ps_emit_seconds, ps_cmp_times, 0}, + {"UTime", 0, -8, 2, 0, + &ps_usr_time_getter, ps_emit_seconds, ps_cmp_times, 0}, + {"STime", 0, -8, 2, 0, + &ps_sys_time_getter, ps_emit_seconds, ps_cmp_times, 0}, + {"VSize", 0, -5, -1, 0, + &ps_vsize_getter, ps_emit_nice_int,ps_cmp_ints, 0}, + {"RSize", 0, -5, -1, 0, + &ps_rsize_getter, ps_emit_nice_int,ps_cmp_ints, 0}, + {"Pri", 0, -3, -1, 0, + &ps_cur_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri}, + {"BPri", 0, -3, -1, 0, + &ps_base_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri}, + {"MPri", 0, -3, -1, 0, + &ps_max_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri}, + {"Mem", "%Mem", -4, -1, 0, + &ps_rmem_frac_getter, ps_emit_percent, ps_cmp_floats, 0}, + {"CPU", "%CPU", -4, -1, 0, + &ps_cpu_frac_getter, ps_emit_percent, ps_cmp_floats, 0}, + {"State", 0, 4, -1, 0, + &ps_state_getter, ps_emit_state, 0, 0}, + {"Wait", 0, 10, -1, 0, + &ps_wait_getter, ps_emit_wait, 0, 0}, + {"Sleep", 0, -2, -1, 0, + &ps_sleep_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"Susp", 0, -2, -1, 0, + &ps_susp_count_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"PSusp", 0, -2, -1, 0, + &ps_proc_susp_count_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"TSusp", 0, -2, -1, 0, + &ps_thread_susp_count_getter, ps_emit_int,ps_cmp_ints, ps_nominal_zint}, + {"TTY", 0, -2, -1, 0, + &ps_tty_getter, ps_emit_tty_name,ps_cmp_strings,0}, + {"PgFlts", 0, -5, -1, 0, + &ps_page_faults_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"COWFlts", 0, -5, -1, 0, + &ps_cow_faults_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"PgIns", 0, -5, -1, 0, + &ps_pageins_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"MsgIn", 0, -5, -1, 0, + &ps_msgs_rcvd_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"MsgOut", 0, -5, -1, 0, + &ps_msgs_sent_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"ZFills", 0, -5, -1, 0, + &ps_zero_fills_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {0} +}; + +struct ps_fmt_specs ps_std_fmt_specs = { specs, 0 }; |