diff options
Diffstat (limited to 'libps')
-rw-r--r-- | libps/filters.c | 74 | ||||
-rw-r--r-- | libps/fmt.c | 385 | ||||
-rw-r--r-- | libps/host.c | 110 | ||||
-rw-r--r-- | libps/proclist.c | 561 | ||||
-rw-r--r-- | libps/procstat.c | 419 | ||||
-rw-r--r-- | libps/ps.h | 605 | ||||
-rw-r--r-- | libps/pshost.h | 53 | ||||
-rw-r--r-- | libps/spec.c | 756 | ||||
-rw-r--r-- | libps/write.c | 133 |
9 files changed, 3096 insertions, 0 deletions
diff --git a/libps/filters.c b/libps/filters.c new file mode 100644 index 00000000..0dff3dfa --- /dev/null +++ b/libps/filters.c @@ -0,0 +1,74 @@ +/* Some ps_filter_t's to restrict proc_stat_list's in various ways. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +static bool +ps_own_p(proc_stat_t ps) +{ + static int own_uid = -1; + if (own_uid < 0) + own_uid = getuid(); + return own_uid == proc_stat_info(ps)->owner; +} +struct ps_filter ps_own_filter = +{"own", PSTAT_INFO, ps_own_p}; + +static bool +ps_not_sess_leader_p(proc_stat_t ps) +{ + return !(proc_stat_state(ps) & PSTAT_STATE_SESSLDR); +} +struct ps_filter ps_not_sess_leader_filter = +{"not-sess-leader", PSTAT_STATE, ps_not_sess_leader_p}; + +static bool +ps_unorphaned_p(proc_stat_t ps) +{ + int state = proc_stat_state(ps); + return !(state & PSTAT_STATE_ORPHANED) || (state & PSTAT_STATE_SESSLDR); +} +struct ps_filter ps_unorphaned_filter = +{"unorphaned", PSTAT_STATE, ps_unorphaned_p}; + +static bool +ps_ctty_p(proc_stat_t ps) +{ + return proc_stat_cttyid(ps) != MACH_PORT_NULL; +} +struct ps_filter ps_ctty_filter = +{"ctty", PSTAT_CTTYID, ps_ctty_p}; + +static bool +ps_parent_p(proc_stat_t ps) +{ + return !(proc_stat_state(ps) & PSTAT_STATE_NOPARENT); +} +struct ps_filter ps_parent_filter = +{"parent", PSTAT_STATE, ps_parent_p}; diff --git a/libps/fmt.c b/libps/fmt.c new file mode 100644 index 00000000..3422107e --- /dev/null +++ b/libps/fmt.c @@ -0,0 +1,385 @@ +/* Implements the type ps_fmt_t, which describes how to output a user-readable + version of a proc_stat_t. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> + +#include "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +/* Make a PS_FMT_T by parsing the string SRC, searching for any named + field specs in FMT_SPECS, and returning the result in FMT. If a memory + allocation error occurs, ENOMEM is returned. If SRC contains an unknown + field name, EINVAL is returned. Otherwise 0 is returned. See ps.h for an + explanation of how FMT is derived from SRC. */ +error_t +ps_fmt_create(char *src, ps_fmt_spec_t fmt_specs, ps_fmt_t *fmt) +{ + ps_fmt_t new_fmt; + int needs = 0; + int fields_alloced = 10; + ps_fmt_field_t fields = NEWVEC(struct ps_fmt_field, fields_alloced); + ps_fmt_field_t field = fields; /* current last field */ + + if (fields == NULL) + return ENOMEM; + + new_fmt = NEW(struct ps_fmt); + if (fmt == NULL) + { + FREE(fields); + return ENOMEM; + } + + /* Make a private copy of SRC so we can mutate it. */ + new_fmt->src = NEWVEC(char, strlen(src) + 1); + if (new_fmt->src == NULL) + { + FREE(fields); + FREE(new_fmt); + return ENOMEM; + } + strcpy(new_fmt->src, src); + src = new_fmt->src; + + while (*src != '\0') + { + if (field - fields == fields_alloced) + /* Time to grow FIELDS to make room for more. */ + { + int offs = field - fields; + + fields_alloced += 10; + fields = GROWVEC(fields, struct ps_fmt_field, fields_alloced); + + if (fields == NULL) + { + FREE(new_fmt); + FREE(new_fmt->src); + return ENOMEM; + } + + field = fields + offs; + } + + /* Find the text to be reproduced verbatim between the last field and + the next one; we'll add this a prefix to FIELD. */ + field->pfx = src; + while (*src != '\0' && *src != '~') + src++; + field->pfx_len = src - field->pfx; + + field->spec = NULL; + field->title = NULL; + field->width = 0; + + if (*src != '\0') + /* Another format-spec. */ + { + char *name; + ps_fmt_spec_t spec; + int sign = 1; + bool explicit_width = FALSE; /* True if the width set from SRC. */ + + src++; + + /* Read an explicit field width. */ + field->width = 0; + if (*src == '-') + sign = -1, src++; + while (isdigit(*src)) + { + field->width = field->width * 10 + (*src++ - '0'); + explicit_width = TRUE; + } + + /* Skip `/' between optional width and spec name. */ + if (*src == '/') + src++; + + /* The name of the spec, or `TITLE=NAME'. */ + name = src; + while (*src != '\0' && !isspace(*src) && *src != '/' && *src != '=') + src++; + + if (*src == '=') + /* A title different from the spec name; the actual name follows + the `='. */ + { + field->title = name; + *src++ = '\0'; + + /* Now read the real name. */ + name = src; + while (*src != '\0' && !isspace(*src) && *src != '/') + src++; + } + + /* we use an explicit loop instead of just calling + find_ps_fmt_spec() because NAME isn't NUL terminated. */ + for (spec = fmt_specs; !ps_fmt_spec_is_end(spec); spec++) + { + char *spec_name = ps_fmt_spec_name(spec); + + if (strncasecmp(spec_name, name, src - name) == 0) + { + field->spec = spec; + + /* Add FIELD's required pstat_flags to FMT's set */ + needs |= ps_getter_needs(ps_fmt_spec_getter(spec)); + + if (field->title == NULL) + /* No explicit title different from the spec name, so use + the name from the spec as our title (unlike name, it's + NUL terminated). */ + { + if (strncmp(name, spec_name, src - name) != 0) + /* XXX Horrible hack: modify the spec's name + to match the capitalization of the users. */ + strncpy(spec_name, name, src - name); + + field->title = spec_name; + } + + if (!explicit_width) + field->width = ps_fmt_spec_default_width(spec); + break; + } + } + + if (ps_fmt_spec_is_end(spec)) + /* Failed to find any named spec called NAME. */ + { + FREE(new_fmt->src); + FREE(fields); + FREE(new_fmt); + return EINVAL; + } + + /* Skip optional trailing `/' after the spec name. */ + if (*src == '/') + src++; + + /* Remember the width's sign (we put it here after possibly using a + default width so that the user may include a `-' with no width + to flip the justification of the default width). */ + field->width *= sign; + + { + /* Force the field to be wide enough to hold the title. */ + int width = field->width; + int tlen = strlen(field->title); + if (width != 0 && tlen > ABS(width)) + field->width = (width > 0 ? tlen : -tlen); + } + } + + field++; + } + + new_fmt->fields = fields; + new_fmt->num_fields = field - fields; + new_fmt->needs = needs; + + *fmt = new_fmt; + + return 0; +} + +/* Free FMT, and any resources it consumes. */ +void +ps_fmt_free(ps_fmt_t fmt) +{ + FREE(fmt->src); + FREE(fmt->fields); + FREE(fmt); +} + +/* ---------------------------------------------------------------- */ + +/* Write an appropiate header line for FMT, containing the titles of all its + fields appropiately aligned with where the values would be printed, to + STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t +ps_fmt_write_titles(ps_fmt_t fmt, FILE *stream, int *count) +{ + error_t err = 0; + ps_fmt_field_t field = ps_fmt_fields(fmt); + int left = ps_fmt_num_fields(fmt); + + while (left-- > 0 && !err) + { + char *pfx = ps_fmt_field_prefix(field); + int pfx_len = ps_fmt_field_prefix_length(field); + + if (pfx_len > 0) + err = ps_write_string(pfx, pfx_len, stream, count); + + if (ps_fmt_field_fmt_spec(field) != NULL && !err) + { + char *title = ps_fmt_field_title(field); + int width = ps_fmt_field_width(field); + + if (title == NULL) + title = "??"; + + err = ps_write_field(title, width, stream, count); + } + + field++; + } + + return err; +} + +/* Format a description as instructed by FMT, of the process described by PS + to STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t +ps_fmt_write_proc_stat(ps_fmt_t fmt, proc_stat_t ps, FILE *stream, int *count) +{ + error_t err = 0; + ps_fmt_field_t field = ps_fmt_fields(fmt); + int nfields = ps_fmt_num_fields(fmt); + int have = proc_stat_flags(ps); + + while (nfields-- > 0 && !err) + { + ps_fmt_spec_t spec = ps_fmt_field_fmt_spec(field); + char *pfx = ps_fmt_field_prefix(field); + int pfx_len = ps_fmt_field_prefix_length(field); + + if (pfx_len > 0) + err = ps_write_string(pfx, pfx_len, stream, count); + + if (spec != NULL && !err) + { + int need = ps_getter_needs(ps_fmt_spec_getter(spec)); + int width = ps_fmt_field_width(field); + + /* do we have the resources to print this field? */ + if ((need & have) == need) + /* Yup */ + { + int (*output_fn)() = (int (*)())ps_fmt_spec_output_fn(spec); + ps_getter_t getter = ps_fmt_spec_getter(spec); + err = output_fn(ps, getter, width, stream, count); + } + else + /* Nope, just print spaces. */ + err = ps_write_spaces(ABS(width), stream, count); + } + + field++; + } + + return err; +} + +/* ---------------------------------------------------------------- */ + +/* Remove those fields from FMT which would need the proc_stat flags FLAGS. + Appropiate inter-field characters are also removed: those *following* + deleted fields at the beginning of the fmt, and those *preceeding* deleted + fields *not* at the beginning. */ +void +ps_fmt_squash(ps_fmt_t fmt, int flags) +{ + int nfields = fmt->num_fields; + ps_fmt_field_t fields = fmt->fields, field = fields; + + while ((field - fields) < nfields) + { + ps_fmt_spec_t spec = field->spec; + + if (spec != NULL) + { + ps_getter_t getter = ps_fmt_spec_getter(spec); + + if (ps_getter_needs(getter) & flags) + /* some of FLAGS are needed -- squash this field! */ + { + /* Save the old prefix, in case we're deleting the first field, + and need to prepend it to the next field. */ + char *beg_pfx = field->pfx; + int beg_pfx_len = field->pfx_len; + + nfields--; + + /* Shift down all following fields over this one. */ + if (nfields > 0) + bcopy(field + 1, field, nfields * sizeof *field); + + if (field == fields) + /* This is the first field, so move its prefix to the + following field (overwriting that field's prefix). This + is to ensure that the beginning of the format string is + preserved in preference to the middle, as it is more + likely to be significant. */ + { + if (nfields == 0) + /* no following fields, so just make a new end field (we're + sure to have room, as we just vacated a space). */ + { + nfields++; + field->pfx = beg_pfx; + field->pfx_len = beg_pfx_len; + field->spec = NULL; + } + else if (field->spec == NULL) + /* One following field with only a prefix -- the suffix + of the format string. Tack the prefix on before the + suffix so we preserve both the beginning and the end + of the format string. We know there's space in our + copy of the source string, because we've just squashed + a field which took at least that much room (as it + previously contained the same prefix). */ + { + field->pfx -= beg_pfx_len; + field->pfx_len += beg_pfx_len; + bcopy(beg_pfx, field->pfx, beg_pfx_len); + } + else + /* otherwise just replace the next field's prefix with + the beginning one */ + { + field->pfx = beg_pfx; + field->pfx_len = beg_pfx_len; + } + } + } + else + /* don't squash this field, just move to the next one */ + field++; + } + } + + fmt->needs &= ~flags; /* we don't need any of them anymore */ + fmt->num_fields = nfields; +} diff --git a/libps/host.c b/libps/host.c new file mode 100644 index 00000000..3dcc9d18 --- /dev/null +++ b/libps/host.c @@ -0,0 +1,110 @@ +/* Routines to get global host info. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 "ps.h" +#include "pshost.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +/* + The Basic & Sched info types are pretty static, so we cache them, but load + info is dynamic so we don't cache that. + + See <mach/host_info.h> for information on the data types these routines + return. +*/ + +/* Return the current host port. */ +host_t +ps_get_host() +{ + static host_t host = MACH_PORT_NULL; + if (host == MACH_PORT_NULL) + host = mach_host_self(); + return host; +} + +/* Return a pointer to basic info about the current host in INFO. Since + this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t +ps_host_basic_info(host_basic_info_t *info) +{ + bool initialized = FALSE; + static host_basic_info_data_t buf; + + if (!initialized) + { + int size = sizeof(buf); + error_t err = host_info(ps_get_host(), HOST_BASIC_INFO, &buf, &size); + if (err) + return err; + initialized = TRUE; + } + + *info = &buf; + return 0; +} + +/* Return a pointer to scheduling info about the current host in INFO. + Since this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t +ps_host_sched_info(host_sched_info_t *info) +{ + bool initialized = FALSE; + static host_sched_info_data_t buf; + + if (!initialized) + { + int size = sizeof(buf); + error_t err = host_info(ps_get_host(), HOST_SCHED_INFO, &buf, &size); + if (err) + return err; + initialized = TRUE; + } + + *info = &buf; + return 0; +} + +/* Return a pointer to load info about the current host in INFO. Since + this is global information we just use a static buffer (if someone desires + to keep old load info, they should copy the buffer we return a pointer + to). If a system error occurs, the error code is returned, otherwise 0. */ +error_t +ps_host_load_info(host_load_info_t *info) +{ + static host_load_info_data_t buf; + int size = sizeof(buf); + error_t err = host_info(ps_get_host(), HOST_LOAD_INFO, &buf, &size); + + if (err) + return err; + + *info = &buf; + return 0; +} diff --git a/libps/proclist.c b/libps/proclist.c new file mode 100644 index 00000000..2b1127e2 --- /dev/null +++ b/libps/proclist.c @@ -0,0 +1,561 @@ +/* The type proc_stat_list_t, which holds lists of proc_stat_t's. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +/* Creates a new proc_stat_list_t for processes from SERVER, which is + returned in PP, and returns 0, or else returns ENOMEM if there wasn't + enough memory. */ +error_t +proc_stat_list_create(process_t server, proc_stat_list_t *pp) +{ + *pp = NEW(struct proc_stat_list); + if (*pp == NULL) + return ENOMEM; + + (*pp)->proc_stats = 0; + (*pp)->num_procs = 0; + (*pp)->alloced = 0; + (*pp)->server = server; + + return 0; +} + +/* Free PP, and any resources it consumes. */ +void +proc_stat_list_free(proc_stat_list_t pp) +{ + int i; + for (i = 0; i < pp->num_procs; i++) + if (pp->proc_stats[i] != NULL) + proc_stat_free(pp->proc_stats[i]); + FREE(pp->proc_stats); + FREE(pp); +} + +/* ---------------------------------------------------------------- */ + +/* Make sure there are at least AMOUNT new locations allocated in PP's + proc_stat_t array (but without changing NUM_PROCS). Returns ENOMEM if a + memory allocation error occurred, 0 otherwise. */ +static error_t +proc_stat_list_grow(proc_stat_list_t pp, int amount) +{ + amount += pp->num_procs; + + if (amount > pp->alloced) + { + proc_stat_t *new_procs = GROWVEC(pp->proc_stats, proc_stat_t, amount); + + if (new_procs == NULL) + return ENOMEM; + + pp->alloced = amount; + pp->proc_stats = new_procs; + } + + return 0; +} + +/* Add proc_stat_t entries to PP for each process with a process id in the + array PIDS (where NUM_PROCS is the length of PIDS). Entries are only + added for processes not already in PP. ENOMEM is returned if a memory + allocation error occurs, otherwise 0. PIDs is not referenced by the + resulting proc_stat_list_t, and so may be subsequently freed. */ +error_t +proc_stat_list_add_pids(proc_stat_list_t pp, int *pids, int num_procs) +{ + error_t err = proc_stat_list_grow(pp, num_procs); + + if (err) + return err; + else + { + proc_stat_t *end = pp->proc_stats + pp->num_procs; + + while (num_procs-- > 0) + { + int pid = *pids++; + + if (proc_stat_list_pid_proc_stat(pp, pid) == NULL) + { + err = proc_stat_create(pid, pp->server, end); + if (err) + /* Whoops, back out what we've done so far. */ + { + while (end > pp->proc_stats + pp->num_procs) + proc_stat_free(*--end); + return err; + } + else + end++; + } + } + + pp->num_procs = end - pp->proc_stats; + + return 0; + } +} + +/* Add a proc_stat_t for the process designated by PID at PP's proc server to + PP. If PID already has an entry in PP, nothing is done. If a memory + allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t +proc_stat_list_add_pid(proc_stat_list_t pp, int pid) +{ + if (proc_stat_list_pid_proc_stat(pp, pid) == NULL) + { + error_t err; + + if (pp->num_procs == pp->alloced) + { + err = proc_stat_list_grow(pp, 32); + if (err) + return err; + } + + err = proc_stat_create(pid, pp->server, &pp->proc_stats[pp->num_procs]); + if (err) + return err; + + pp->num_procs++; + } + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Returns the proc_stat_t in PP with a process-id of PID, if there's one, + otherwise, NULL. */ +proc_stat_t +proc_stat_list_pid_proc_stat(proc_stat_list_t pp, int pid) +{ + int nprocs = pp->num_procs; + proc_stat_t *procs = pp->proc_stats; + + while (nprocs-- > 0) + if (proc_stat_pid(*procs) == pid) + return *procs; + else + procs++; + + return NULL; +} + +/* ---------------------------------------------------------------- */ + +/* Adds all proc_stat_t's in MERGEE to PP that don't correspond to processes + already in PP; the resulting order of proc_stat_t's in PP is undefined. + If MERGEE and PP point to different proc servers, EINVAL is returned. If a + memory allocation error occurs, ENOMEM is returned. Otherwise 0 is + returned, and MERGEE is freed. */ +error_t +proc_stat_list_merge(proc_stat_list_t pp, proc_stat_list_t mergee) +{ + if (pp->server != mergee->server) + return EINVAL; + else + { + /* Make sure there's room for the max number of new elements in PP. */ + error_t err = proc_stat_list_grow(pp, mergee->num_procs); + + if (err) + return err; + else + { + int mnprocs = mergee->num_procs; + proc_stat_t *mprocs = mergee->proc_stats; + int nprocs = pp->num_procs; + proc_stat_t *procs = pp->proc_stats; + + /* Transfer over any proc_stat_t's from MERGEE to PP that don't + already exist there; for each of these, we set its entry in + MERGEE's proc_stat array to NULL, which prevents + proc_list_free() from freeing them. */ + while (mnprocs-- > 0) + if (proc_stat_list_pid_proc_stat(pp, proc_stat_pid(mprocs[mnprocs])) + == NULL) + { + procs[nprocs++] = mprocs[mnprocs]; + mprocs[mnprocs] = NULL; + } + + proc_stat_list_free(mergee); + + return 0; + } + } +} + +/* ---------------------------------------------------------------- */ + +/* the number of max number pids that will fit in our static buffers (above + which mig will vm_allocate space for them) */ +#define STATICPIDS 200 + +/* Add to PP entries for all processes at its server. If an error occurs, + the system error code is returned, otherwise 0. */ +error_t +proc_stat_list_add_all(proc_stat_list_t pp) +{ + error_t err; + pid_t pid_array[STATICPIDS], *pids = pid_array, num_procs = STATICPIDS; + + err = proc_getallpids(pp->server, &pids, &num_procs); + if (err) + return err; + + err = proc_stat_list_add_pids(pp, pids, num_procs); + + if (pids != pid_array) + VMFREE(pids, sizeof(pid_t) * num_procs); + + return err; +} + +/* Add to PP entries for all processes in the login collection LOGIN_ID at + its server. If an error occurs, the system error code is returned, + otherwise 0. */ +error_t +proc_stat_list_add_login_coll(proc_stat_list_t pp, int login_id) +{ + error_t err; + pid_t pid_array[STATICPIDS], *pids = pid_array, num_procs = STATICPIDS; + + err = proc_getloginpids(pp->server, login_id, &pids, &num_procs); + if (err) + return err; + + err = proc_stat_list_add_pids(pp, pids, num_procs); + + if (pids != pid_array) + VMFREE(pids, sizeof(pid_t) * num_procs); + + return err; +} + +/* Add to PP entries for all processes in the session SESSION_ID at its + server. If an error occurs, the system error code is returned, otherwise + 0. */ +error_t +proc_stat_list_add_session(proc_stat_list_t pp, int session_id) +{ + error_t err; + pid_t pid_array[STATICPIDS], *pids = pid_array, num_procs = STATICPIDS; + + err = proc_getsessionpids(pp->server, session_id, &pids, &num_procs); + if (err) + return err; + + err = proc_stat_list_add_pids(pp, pids, num_procs); + + if (pids != pid_array) + VMFREE(pids, sizeof(pid_t) * num_procs); + + return err; +} + +/* ---------------------------------------------------------------- */ + +/* Try to set FLAGS in each proc_stat_t in PP (but they may still not be set + -- you have to check). If a fatal error occurs, the error code is + returned, otherwise 0. */ +error_t +proc_stat_list_set_flags(proc_stat_list_t pp, int flags) +{ + int nprocs = pp->num_procs; + proc_stat_t *procs = pp->proc_stats; + + while (nprocs-- > 0) + { + proc_stat_t ps = *procs++; + + if (!proc_stat_has(ps, flags)) + { + error_t err = proc_stat_set_flags(ps, flags); + if (err) + return err; + } + } + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Destructively modify PP to only include proc_stat_t's for which the + function PREDICATE returns true; if INVERT is true, only proc_stat_t's for + which PREDICATE returns false are kept. FLAGS is the set of pstat_flags + that PREDICATE requires be set as precondition. Regardless of the value + of INVERT, all proc_stat_t's for which the predicate's preconditions can't + be satisfied are kept. If a fatal error occurs, the error code is + returned, it returns 0. */ +error_t +proc_stat_list_filter1(proc_stat_list_t pp, + int (*predicate)(proc_stat_t ps), int flags, + bool invert) +{ + int which = 0; + int num_procs = pp->num_procs; + proc_stat_t *procs = pp->proc_stats; + /* We compact the proc array as we filter, and KEPT points to end of the + compacted part that we've already processed. */ + proc_stat_t *kept = procs; + error_t err = proc_stat_list_set_flags(pp, flags); + + if (err) + return err; + + invert = !!invert; /* Convert to a boolean. */ + + while (which < num_procs) + { + proc_stat_t ps = procs[which++]; + + /* See if we should keep PS; if PS doesn't satisfy the set of flags we + need, we don't attempt to call PREDICATE at all, and keep PS. */ + + if (!proc_stat_has(ps, flags) || !!predicate(ps) != invert) + *kept++ = ps; + else + /* Implicitly delete PS from PP by not putting it in the KEPT + sequence. */ + proc_stat_free(ps); + } + + pp->num_procs = kept - procs; + + return 0; +} + +/* Destructively modify PP to only include proc_stat_t's for which the + predicate function in FILTER returns true; if INVERT is true, only + proc_stat_t's for which the predicate returns false are kept. Regardless + of the value of INVERT, all proc_stat_t's for which the predicate's + preconditions can't be satisfied are kept. If a fatal error occurs, + the error code is returned, it returns 0. */ +error_t +proc_stat_list_filter(proc_stat_list_t pp, ps_filter_t filter, bool invert) +{ + return + proc_stat_list_filter1(pp, + ps_filter_predicate(filter), + ps_filter_needs(filter), + invert); +} + +/* ---------------------------------------------------------------- */ + +/* Destructively sort proc_stats in PP by ascending value of the field + returned by GETTER, and compared by CMP_FN; If REVERSE is true, use the + opposite order. If a fatal error occurs, the error code is returned, it + returns 0. */ +error_t +proc_stat_list_sort1(proc_stat_list_t pp, + ps_getter_t getter, + int (*cmp_fn)(proc_stat_t ps1, proc_stat_t ps2, + ps_getter_t getter), + bool reverse) +{ + int needs = ps_getter_needs(getter); + proc_stat_t *procs = pp->proc_stats; + error_t err = proc_stat_list_set_flags(pp, needs); + + /* Lessp is a nested function so it may use state variables from + proc_stat_list_sort1, which qsort gives no other way of passing in. */ + int lessp(const void *p1, const void *p2) + { + proc_stat_t ps1 = *(proc_stat_t *)p1; + proc_stat_t ps2 = *(proc_stat_t *)p2; + bool is_th_1 = proc_stat_is_thread(ps1); + bool is_th_2 = proc_stat_is_thread(ps2); + + if (!is_th_1 || !is_th_2 + || proc_stat_thread_origin(ps1) != proc_stat_thread_origin(ps2)) + /* Compare the threads' origins to keep them ordered after their + respective processes. The exception is when they're both from the + same process, in which case we want to compare them directly so that + a process's threads are sorted among themselves (in most cases this + just fails because the thread doesn't have the proper fields; this + should just result in the threads remaining in their original + order). */ + { + if (is_th_1) + ps1 = proc_stat_thread_origin(ps1); + if (is_th_2) + ps2 = proc_stat_thread_origin(ps2); + } + + if (!proc_stat_has(ps1, needs) || !proc_stat_has(ps2, needs)) + /* If we can't call CMP_FN on either proc_stat_t due to lack of the + necessary preconditions, then compare their original positions, to + retain the same order. */ + return p1 - p2; + else if (reverse) + return cmp_fn(ps2, ps1, getter); + else + return cmp_fn(ps1, ps2, getter); + } + + if (err) + return err; + + qsort((void *)procs, (size_t)pp->num_procs, sizeof(proc_stat_t), lessp); + + return 0; +} + +/* Destructively sort proc_stats in PP by ascending value of the field KEY; + if REVERSE is true, use the opposite order. If KEY isn't a valid sort + key, EINVAL is returned. If a fatal error occurs the error code is + returned. Otherwise, 0 is returned. */ +error_t +proc_stat_list_sort(proc_stat_list_t pp, ps_fmt_spec_t key, bool reverse) +{ + int (*cmp_fn)() = ps_fmt_spec_compare_fn(key); + if (cmp_fn == NULL) + return EINVAL; + else + return + proc_stat_list_sort1(pp, ps_fmt_spec_getter(key), cmp_fn, reverse); +} + +/* ---------------------------------------------------------------- */ + +/* Format a description as instructed by FMT, of the processes in PP to + STREAM, separated by newlines (and with a terminating newline). If COUNT + is non-NULL, it points to an integer which is incremented by the number of + characters output. If a fatal error occurs, the error code is returned, + otherwise 0. */ +error_t +proc_stat_list_fmt(proc_stat_list_t pp, ps_fmt_t fmt, FILE * stream, int *count) +{ + int nprocs = pp->num_procs; + proc_stat_t *procs = pp->proc_stats; + error_t err = proc_stat_list_set_flags(pp, ps_fmt_needs(fmt)); + + if (err) + return err; + + while (nprocs-- > 0) + { + err = ps_fmt_write_proc_stat(fmt, *procs++, stream, count); + if (err) + return err; + + putc('\n', stream); + if (count != NULL) + (*count)++; + } + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Modifies FLAGS to be the subset which can't be set in any proc_stat_t in + PP (and as a side-effect, adds as many bits from FLAGS to each proc_stat_t + as possible). If a fatal error occurs, the error code is returned, + otherwise 0. */ +error_t +proc_stat_list_find_bogus_flags(proc_stat_list_t pp, int *flags) +{ + int nprocs = pp->num_procs; + proc_stat_t *procs = pp->proc_stats; + error_t err = proc_stat_list_set_flags(pp, *flags); + + if (err) + return err; + + while (nprocs-- > 0 && *flags != 0) + *flags &= ~proc_stat_flags(*procs++); + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Add thread entries for for every process in PP, located immediately after + the containing process in sequence. Subsequent sorting of PP will leave + the thread entries located after the containing process, although the + order of the thread entries themselves may change. If a fatal error + occurs, the error code is returned, otherwise 0. */ +error_t +proc_stat_list_add_threads(proc_stat_list_t pp) +{ + error_t err = proc_stat_list_set_flags(pp, PSTAT_NUM_THREADS); + + if (err) + return err; + else + { + int new_entries = 0; + int nprocs = pp->num_procs; + proc_stat_t *procs = pp->proc_stats; + + /* First, count the number of threads that will be added. */ + while (nprocs-- > 0) + { + proc_stat_t ps = *procs++; + if (proc_stat_has(ps, PSTAT_NUM_THREADS)) + new_entries += proc_stat_num_threads(ps); + } + + /* And make room for them... */ + err = proc_stat_list_grow(pp, new_entries); + if (err) + return err; + else + /* Now add thread entries for every existing entry in PP; we go + through them backwards so we can do it in place. */ + { + proc_stat_t *end = pp->proc_stats + pp->num_procs + new_entries; + + nprocs = pp->num_procs; + procs = pp->proc_stats + nprocs; + + while (nprocs-- > 0) + { + proc_stat_t ps = *--procs; + if (proc_stat_has(ps, PSTAT_NUM_THREADS)) + { + int nthreads = proc_stat_num_threads(ps); + while (nthreads-- > 0) + proc_stat_thread_create(ps, nthreads, --end); + } + *--end = ps; + } + + pp->num_procs += new_entries; + } + } + + return 0; +} diff --git a/libps/procstat.c b/libps/procstat.c new file mode 100644 index 00000000..251af8a1 --- /dev/null +++ b/libps/procstat.c @@ -0,0 +1,419 @@ +/* The type proc_stat_t, which holds information about a hurd process. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +/* These directly correspond to the bits in a state, starting from 0. See + ps.h for an explanation of what each of these means. */ +char *proc_stat_state_tags = "RTHDSIWN<Z+sempo"; + +/* ---------------------------------------------------------------- */ + +/* Return the PSTAT_STATE_ bits describing the state of an individual thread, + from that thread's thread_basic_info_t struct */ +static int +thread_state(thread_basic_info_t bi) +{ + int state = 0; + + switch (bi->run_state) + { + case TH_STATE_RUNNING: + state |= PSTAT_STATE_RUNNING; + break; + case TH_STATE_STOPPED: + state |= PSTAT_STATE_STOPPED; + break; + case TH_STATE_UNINTERRUPTIBLE: + state |= PSTAT_STATE_WAIT; + break; + case TH_STATE_HALTED: + state |= PSTAT_STATE_HALTED; + break; + case TH_STATE_WAITING: + /* Act like unix: waits of less than 20 seconds means a process is + `sleeping' and >= 20 seconds means it's `idle' */ + state |= bi->sleep_time < 20 ? PSTAT_STATE_SLEEPING : PSTAT_STATE_IDLE; + break; + } + + if (bi->flags & TH_FLAGS_SWAPPED) + state |= PSTAT_STATE_SWAPPED; + + return state; +} + +/* ---------------------------------------------------------------- */ + +/* Add FLAGS to PS's flags, fetching information as necessary to validate + the corresponding fields in PS. Afterwards you must still check the flags + field before using new fields, as something might have failed. Returns + a system error code if a fatal error occurred, or 0 if none. */ +error_t +proc_stat_set_flags(proc_stat_t ps, int flags) +{ + error_t err; + int have = ps->flags; /* flags set in ps */ + int need; /* flags not set in ps, but desired to be */ + + /* Implement any inter-flag dependencies: if the new flags in FLAGS depend + on some other set of flags to be set, make sure those are also in + FLAGS. */ + + if (flags & PSTAT_TTY_NAME) + flags |= PSTAT_TTY; + if (flags & PSTAT_TTY) + flags |= PSTAT_CTTYID; + if (flags & (PSTAT_CTTYID | PSTAT_CWDIR | PSTAT_AUTH | PSTAT_UMASK)) + { + flags |= PSTAT_MSGPORT; + flags |= PSTAT_TASK; /* for authentication */ + } + if (flags & (PSTAT_THREAD_INFO | PSTAT_STATE)) + flags |= PSTAT_INFO; + if (flags & PSTAT_TASK_EVENTS_INFO) + flags |= PSTAT_TASK; + + need = flags & ~have; + + + /* MGET: If we're trying to set FLAG, and the preconditions PRECOND are set + in the flags already, then eval CALL and set ERR to the result. + If the resulting ERR is 0 add FLAG to the set of valid flags. ERR is + returned. */ +#define MGET(flag, precond, call) \ + ((!(need & (flag)) || (have & (precond)) != (precond)) \ + ? 0 \ + : ((((err = (call)) == 0) \ + ? (have |= (flag)) \ + : 0), \ + err)) + + /* The process's process port. */ + MGET(PSTAT_PROCESS, PSTAT_PID, + proc_pid2proc(ps->server, ps->pid, &ps->process)); + /* The process's task port. */ + MGET(PSTAT_TASK, PSTAT_PID, + proc_pid2task(ps->server, ps->pid, &ps->task)); + /* The process's libc msg port (see <hurd/msg.defs>). */ + MGET(PSTAT_MSGPORT, PSTAT_PID, + proc_getmsgport(ps->server, ps->pid, &ps->msgport)); + + /* the process's struct procinfo as returned by proc_getprocinfo. */ + if ((need & PSTAT_INFO) && (have & PSTAT_PID)) + { + ps->info_size = 0; + if (proc_getprocinfo(ps->server, + ps->pid, (int **)&ps->info, &ps->info_size) + == 0) + have |= PSTAT_INFO; + } + + /* A summary of the proc's thread_{basic,sched}_info_t structures: sizes + and cumulative times are summed, prioritys and delta time are + averaged. The run_states are added by having running thread take + precedence over waiting ones, and if there are any other incompatible + states, simply using a bogus value of -1 */ + if ((need & PSTAT_THREAD_INFO) && (have & PSTAT_INFO)) + { + int i; + struct procinfo *pi = ps->info; + thread_sched_info_t tsi = &ps->thread_sched_info; + thread_basic_info_t tbi = &ps->thread_basic_info; + + bzero(tbi, sizeof *tbi); + bzero(tsi, sizeof *tsi); + + for (i = 0; i < pi->nthreads; i++) + { + thread_basic_info_t bi = &pi->threadinfos[i].pis_bi; + thread_sched_info_t si = &pi->threadinfos[i].pis_si; + int thread_run_state = bi->run_state; + + /* Construct some aggregate run-state for the process: besides the + defined thread run_states, we use 0 to mean no threads, and -1 + to mean two threads have conflicting run_stats. */ + + if (tbi->run_state == 0) + /* No prior state, just copy this thread's. */ + tbi->run_state = thread_run_state; + else if (tbi->run_state == TH_STATE_RUNNING + || thread_run_state == TH_STATE_RUNNING) + /* If any thread is running, mark the process as running. */ + tbi->run_state = TH_STATE_RUNNING; + else if (tbi->run_state != bi->run_state) + /* Otherwise there are two conflicting non-running states, so + just give up and say we don't know. */ + tbi->run_state = -1; + + tsi->base_priority += si->base_priority; + tsi->cur_priority += si->cur_priority; + + tbi->cpu_usage += bi->cpu_usage; + tbi->sleep_time += bi->sleep_time; + + tbi->user_time.seconds += bi->user_time.seconds; + tbi->user_time.microseconds += bi->user_time.microseconds; + tbi->system_time.seconds += bi->system_time.seconds; + tbi->system_time.microseconds += bi->system_time.microseconds; + } + + tsi->base_priority /= pi->nthreads; + tsi->cur_priority /= pi->nthreads; + + tbi->sleep_time /= pi->nthreads; + + tbi->user_time.seconds += tbi->user_time.microseconds / 1000000; + tbi->user_time.microseconds %= 1000000; + tbi->system_time.seconds += tbi->system_time.microseconds / 1000000; + tbi->system_time.microseconds %= 1000000; + + have |= PSTAT_THREAD_INFO; + } + + /* VM statistics for the task. See <mach/task_info.h>. */ + if ((need & PSTAT_TASK_EVENTS_INFO) && (have & PSTAT_TASK)) + { + ps->task_events_info = &ps->task_events_info_buf; + ps->task_events_info_size = TASK_EVENTS_INFO_COUNT; + if (task_info(ps->task, TASK_EVENTS_INFO, + (task_info_t)&ps->task_events_info, + &ps->task_events_info_size) + == 0) + have |= PSTAT_TASK_EVENTS_INFO; + } + + /* PSTAT_STATE_ bits for the process and all its threads. */ + if ((need & PSTAT_STATE) && (have & PSTAT_INFO)) + { + int i; + int state = 0; + struct procinfo *pi = ps->info; + int pi_flags = pi->state; + + /* The union of all thread state bits... */ + for (i = 0; i < pi->nthreads; i++) + state |= thread_state(&pi->threadinfos[i].pis_bi); + + /* ... and process-only state bits. */ + if (pi_flags & PI_ZOMBIE) + state |= PSTAT_STATE_ZOMBIE; + if (pi_flags & PI_SESSLD) + state |= PSTAT_STATE_SESSLDR; + if (pi_flags & PI_EXECED) + state |= PSTAT_STATE_EXECED; + if (pi_flags & PI_NOMSG) + state |= PSTAT_STATE_NOMSG; + if (pi_flags & PI_NOPARENT) + state |= PSTAT_STATE_NOPARENT; + if (pi_flags & PI_ORPHAN) + state |= PSTAT_STATE_ORPHANED; + + ps->state = state; + have |= PSTAT_STATE; + } + + /* The process's exec arguments */ + if ((need & PSTAT_ARGS) && (have & PSTAT_PID)) + { + ps->args_len = 0; + if (proc_getprocargs(ps->server, ps->pid, &ps->args, &ps->args_len)) + ps->args_len = 0; + else + have |= PSTAT_ARGS; + } + + /* The ctty id port; note that this is just a magic cookie; + we use it to fetch a port to the actual terminal -- it's not useful for + much else. */ + MGET(PSTAT_CTTYID, PSTAT_MSGPORT | PSTAT_TASK, + msg_get_init_port(ps->msgport, ps->task, + INIT_PORT_CTTYID, &ps->cttyid)); + + /* A port to the process's current working directory. */ + MGET(PSTAT_CWDIR, PSTAT_MSGPORT | PSTAT_TASK, + msg_get_init_port(ps->msgport, ps->task, + INIT_PORT_CWDIR, &ps->cwdir)); + + /* The process's auth port, which we can use to determine who the process + is authenticated as. */ + MGET(PSTAT_AUTH, PSTAT_MSGPORT | PSTAT_TASK, + msg_get_init_port(ps->msgport, ps->task, INIT_PORT_AUTH, &ps->auth)); + + /* The process's umask, which controls which protection bits won't be set + when creating a file. */ + MGET(PSTAT_UMASK, PSTAT_MSGPORT | PSTAT_TASK, + msg_get_init_int(ps->msgport, ps->task, INIT_UMASK, &ps->umask)); + + /* A file_t port for the process's controlling terminal, or + MACH_PORT_NULL if the process has no controlling terminal. */ + if ((need & PSTAT_TTY) && (have & PSTAT_CTTYID)) + if (ps->cttyid == MACH_PORT_NULL) + { + /* cttyid == NULL is a positive assertion that there is no + controlling tty */ + ps->tty = MACH_PORT_NULL; + have |= PSTAT_TTY; + } + else if (termctty_open_terminal(ps->cttyid, 0, &ps->tty) == 0) + have |= PSTAT_TTY; + + /* A filename pointing to TTY, or NULL if the process has no controlling + terminal, or "?" if we cannot determine the name. */ + if ((need & PSTAT_TTY_NAME) && (have & PSTAT_TTY)) + { + if (ps->tty == MACH_PORT_NULL) + ps->tty_name = NULL; + else + { + string_t buf; + + if (term_get_nodename(ps->tty, &buf) != 0) + /* There is a terminal there, but we can't figure out its name. */ + strcpy(buf, "?"); + + ps->tty_name = NEWVEC(char, strlen(buf) + 1); + if (ps->tty_name == NULL) + return FALSE; + else + strcpy(ps->tty_name, buf); + } + + have |= PSTAT_TTY_NAME; + } + + ps->flags = have; + + return 0; +} + +/* ---------------------------------------------------------------- */ +/* Discard PS and any resources it holds. */ +void +proc_stat_free(ps) + proc_stat_t ps; +{ + /* Free the mach port PORT if FLAG is set in PS's flags. */ +#define MFREEPORT(flag, port) \ + ((ps->flags & (flag)) \ + ? mach_port_deallocate(mach_task_self(), (ps->port)) : 0) + + /* If FLAG is set in PS's flags, vm_deallocate MEM, consisting of SIZE + elements of type ELTYPE, *unless* MEM == SBUF, which usually means + that MEM points to a static buffer somewhere instead of vm_alloc'd + memory. */ +#define MFREEVM(flag, mem, size, sbuf, eltype) \ + (((ps->flags & (flag)) && ps->mem != sbuf) \ + ? (VMFREE(ps->mem, ps->size * sizeof(eltype))) : 0) + + /* free PS's ports */ + MFREEPORT(PSTAT_PROCESS, process); + MFREEPORT(PSTAT_TASK, task); + MFREEPORT(PSTAT_MSGPORT, msgport); + MFREEPORT(PSTAT_CTTYID, cttyid); + MFREEPORT(PSTAT_CWDIR, cwdir); + MFREEPORT(PSTAT_AUTH, auth); + + /* free any allocated memory pointed to by PS */ + MFREEVM(PSTAT_INFO, info, info_size, 0, char); + MFREEVM(PSTAT_ARGS, args, args_len, 0, char); + MFREEVM(PSTAT_TASK_EVENTS_INFO, + task_events_info, task_events_info_size, + &ps->task_events_info_buf, char); + if (ps->flags & PSTAT_TTY_NAME) + FREE(ps->tty_name); + + FREE(ps); +} + +/* Returns in PS a new proc_stat_t for the process PID at the process server + SERVER. If a memory allocation error occurs, ENOMEM is returned, + otherwise 0. */ +error_t +proc_stat_create(int pid, process_t server, proc_stat_t *ps) +{ + *ps = NEW(struct proc_stat); + if (*ps == NULL) + return ENOMEM; + + (*ps)->pid = pid; + (*ps)->flags = PSTAT_PID; + (*ps)->server = server; + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Returns in THREAD_PS a proc_stat_t for the Nth thread in the proc_stat_t + PS (N should be between 0 and the number of threads in the process). The + resulting proc_stat_t isn't fully functional -- most flags can't be set in + it. It also contains a pointer to PS, so PS shouldn't be freed without + also freeing THREAD_PS. If N was out of range, EINVAL is returned. If a + memory allocation error occured, ENOMEM is returned. Otherwise, 0 is + returned. */ +error_t +proc_stat_thread_create(proc_stat_t ps, int index, proc_stat_t *thread_ps) +{ + error_t err = proc_stat_set_flags(ps, PSTAT_THREAD_INFO); + if (err) + return err; + else if (index < 0 || index >= ps->info->nthreads) + return EINVAL; + else + { + proc_stat_t tps = NEW(struct proc_stat); + + if (tps == NULL) + return ENOMEM; + + /* A value of -1 for the PID indicates that this is a thread. */ + tps->pid = -1; + + /* TPS is initialized with these values; any attempts to set other + flags will fail because PSTAT_PID isn't one of them. */ + tps->flags = PSTAT_THREAD | PSTAT_THREAD_INFO | PSTAT_STATE; + + bcopy(&ps->info->threadinfos[index].pis_bi, + &tps->thread_basic_info, + sizeof(thread_basic_info_data_t)); + bcopy(&ps->info->threadinfos[index].pis_si, + &tps->thread_sched_info, + sizeof(thread_sched_info_data_t)); + + /* Construct the state with only the per-thread bits. */ + tps->state = thread_state(&tps->thread_basic_info); + + tps->thread_origin = ps; + tps->thread_index = index; + + *thread_ps = tps; + + return 0; + } +} diff --git a/libps/ps.h b/libps/ps.h new file mode 100644 index 00000000..7e67bd66 --- /dev/null +++ b/libps/ps.h @@ -0,0 +1,605 @@ +/* Routines to gather and print process information. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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. */ + +#ifndef __PS_H__ +#define __PS_H__ + +#include <hurd/hurd_types.h> +#include <mach/mach.h> + +#ifndef bool +#define bool int +#endif + +/* ---------------------------------------------------------------- */ +/* + A PROC_STAT_T holds lots of info about the process PID at SERVER; exactly + which info is dependent on its FLAGS field. + */ + +typedef struct proc_stat *proc_stat_t; + +struct proc_stat + { + /* Which process server this is from. */ + process_t server; + + /* The proc's process id; if <0 then this is a thread, not a process. */ + int pid; + + /* Flags describing which fields in this structure are valid. */ + int flags; + + /* Thread fields -- these are valid if PID < 0. */ + proc_stat_t thread_origin; /* A proc_stat_t for the task we're in. */ + int thread_index; /* Which thread in our proc we are. */ + + /* A process_t port for the process. */ + process_t process; + + /* The mach task port for the process. */ + task_t task; + + /* A libc msgport for the process. This port is responded to by the + process itself (usually by the c library); see <hurd/msg.defs> for the + standard set of rpcs you can send to this port. Accordingly, you + cannot depend on a timely (or any) reply to messages sent here -- + program carefully! */ + mach_port_t msgport; + + /* A pointer to the process's procinfo structure (as returned by + proc_getinfo; see <hurd/hurd_types.h>). */ + struct procinfo *info; + /* The size of the info structure for deallocation purposes. */ + int info_size; + + /* Summaries of the proc's thread_{basic,sched}_info_t structures: sizes + and cumulative times are summed, prioritys and delta time are + averaged. The run_states are added by having running thread take + precedence over waiting ones, and if there are any other incompatible + states, simply using a bogus value of -1 */ + thread_basic_info_data_t thread_basic_info; + thread_sched_info_data_t thread_sched_info; + + /* A bitmask summarizing the scheduling state of this process and all its + threads. See the PSTAT_STATE_ defines below for a list of bits. */ + int state; + + /* The process's argv, as a string with each element separated by '\0'. */ + char *args; + /* The length of ARGS. */ + int args_len; + + /* Virtual memory statistics for the process, as returned by task_info; + see <mach/task_info.h> for a description of task_events_info_t. */ + task_events_info_t task_events_info; + task_events_info_data_t task_events_info_buf; + int task_events_info_size; + + /* Various libc ports: */ + + /* The process's ctty id port, or MACH_PORT_NULL if the process has no + controlling terminal. Note that this is just a magic cookie; we use + it to fetch a port to the actual terminal -- it's not useful for much + else. */ + mach_port_t cttyid; + + /* A port to the process's current working directory. */ + mach_port_t cwdir; + + /* The process's auth port, which we can use to determine who the process + is authenticated as. */ + mach_port_t auth; + + /* The process's umask, which controls which protection bits won't be set + when creating a file. */ + int umask; + + /* A file_t port for the process's controlling terminal, or + MACH_PORT_NULL if the process has no controlling terminal. */ + mach_port_t tty; + /* A filename pointing to TTY, or NULL if the process has no controlling + terminal, or "?" if we cannot determine the name. */ + char *tty_name; + }; + + +/* Proc_stat flag bits; each bit is set in the FLAGS field if that + information is currently valid. */ +#define PSTAT_PID 0x0001 /* Process ID. */ +#define PSTAT_THREAD 0x0002 /* thread_index & thread_origin */ +#define PSTAT_PROCESS 0x0004 /* The process_t for the process. */ +#define PSTAT_TASK 0x0008 /* The task port for the process. */ +#define PSTAT_MSGPORT 0x0010 /* The process's msgport. */ +#define PSTAT_INFO 0x0020 /* A struct procinfo for the process. */ +#define PSTAT_THREAD_INFO 0x0040 /* Thread summary info. */ +#define PSTAT_ARGS 0x0080 /* The process's args. */ +#define PSTAT_TASK_EVENTS_INFO 0x0100 /* A task_events_info_t for the proc. */ +#define PSTAT_STATE 0x0200 /* A bitmask describing the process's + 0x0400 state (see below). */ +#define PSTAT_CTTYID 0x0800 /* The process's CTTYID port. */ +#define PSTAT_CWDIR 0x1000 /* A file_t for the proc's CWD. */ +#define PSTAT_AUTH 0x2000 /* The proc's auth port. */ +#define PSTAT_TTY 0x4000 /* A file_t for the proc's terminal. */ +#define PSTAT_TTY_NAME 0x8000 /* The name of the proc's terminal. */ +#define PSTAT_UMASK 0x10000 /* The proc's current umask. */ + +#define PSTAT_NUM_THREADS PSTAT_INFO + +/* If the PSTAT_STATE flag is set, then the proc_stat's state field holds a + bitmask of the following bits, describing the process's run state. */ +#define PSTAT_STATE_RUNNING 0x0001 /* R */ +#define PSTAT_STATE_STOPPED 0x0002 /* T stopped (e.g., by ^Z) */ +#define PSTAT_STATE_HALTED 0x0004 /* H */ +#define PSTAT_STATE_WAIT 0x0008 /* D short term (uninterruptable) wait */ +#define PSTAT_STATE_SLEEPING 0x0010 /* S sleeping */ +#define PSTAT_STATE_IDLE 0x0020 /* I idle (sleeping > 20 seconds) */ +#define PSTAT_STATE_SWAPPED 0x0040 /* W */ +#define PSTAT_STATE_NICED 0X0080 /* N lowered priority */ +#define PSTAT_STATE_PRIORITY 0x0100 /* < raised priority */ +#define PSTAT_STATE_ZOMBIE 0x0200 /* Z process exited but not yet reaped */ +#define PSTAT_STATE_FG 0x0400 /* + in foreground process group */ +#define PSTAT_STATE_SESSLDR 0x0800 /* s session leader */ +#define PSTAT_STATE_EXECED 0x1000 /* e has execed */ +#define PSTAT_STATE_NOMSG 0x2000 /* m no msg port */ +#define PSTAT_STATE_NOPARENT 0x4000 /* p no parent */ +#define PSTAT_STATE_ORPHANED 0x8000 /* o orphaned */ + + +/* This is a constant string holding a single character for each possible bit + in a proc_stat's STATE field, in order from bit zero. These are intended + for printint a user-readable summary of a process's state. */ +char *proc_stat_state_tags; + + +/* + Process info accessor functions. + + You must be sure that the associated flag bit is set before accessing a + field in a proc_stat_t! A field FOO (with accessor macro proc_foo()), has + a flag named PSTAT_FOO. If the flag is'nt set, you may attempt to set it + with proc_stat_set_flags (but note that this may not succeed). + */ + +/* FLAGS doesn't have a flag bit; it's always valid */ +#define proc_stat_flags(ps) ((ps)->flags) + +/* These both use the flag PSTAT_THREAD. */ +#define proc_stat_thread_origin(ps) ((ps)->thread_origin) +#define proc_stat_thread_index(ps) ((ps)->thread_index) + +#define proc_stat_pid(ps) ((ps)->pid) +#define proc_stat_process(ps) ((ps)->process) +#define proc_stat_task(ps) ((ps)->task) +#define proc_stat_msgport(ps) ((ps)->msgport) +#define proc_stat_info(ps) ((ps)->info) +#define proc_stat_num_threads(ps) ((ps)->info->nthreads) +#define proc_stat_thread_basic_info(ps) (&(ps)->thread_basic_info) +#define proc_stat_thread_sched_info(ps) (&(ps)->thread_sched_info) +#define proc_stat_args(ps) ((ps)->args) +#define proc_stat_args_len(ps) ((ps)->args_len) +#define proc_stat_state(ps) ((ps)->state) +#define proc_stat_cttyid(ps) ((ps)->cttyid) +#define proc_stat_cwdir(ps) ((ps)->cwdir) +#define proc_stat_auth(ps) ((ps)->auth) +#define proc_stat_umask(ps) ((ps)->umask) +#define proc_stat_tty(ps) ((ps)->tty) +#define proc_stat_tty_name(ps) ((ps)->tty_name) +#define proc_stat_task_events_info(ps) ((ps)->task_events_info) +#define proc_stat_has(ps, needs) (((ps)->flags & needs) == needs) + +/* True if PS refers to a thread and not a process. */ +#define proc_stat_is_thread(ps) ((ps)->pid < 0) + +/* Returns in PS a new proc_stat_t for the process PID at the process server + SERVER. If a memory allocation error occurs, ENOMEM is returned, + otherwise 0. */ +error_t proc_stat_create(int pid, process_t server, proc_stat_t *ps); + +/* Frees PS and any memory/ports it references */ +void proc_stat_free(proc_stat_t ps); + +/* Adds FLAGS to PS's flags, fetching information as necessary to validate + the corresponding fields in PS. Afterwards you must still check the flags + field before using new fields, as something might have failed. Returns + a system error code if a fatal error occurred, and 0 otherwise. */ +error_t proc_stat_set_flags(proc_stat_t ps, int flags); + +/* Returns in THREAD_PS a proc_stat_t for the Nth thread in the proc_stat_t + PS (N should be between 0 and the number of threads in the process). The + resulting proc_stat_t isn't fully functional -- most flags can't be set in + it. If N was out of range, EINVAL is returned. If a memory allocation + error occured, ENOMEM is returned. Otherwise, 0 is returned. */ +error_t proc_stat_thread_create(proc_stat_t ps, int n, proc_stat_t *thread_ps); + +/* ---------------------------------------------------------------- */ +/* + A PS_GETTER_T describes how to get a particular value from a PROC_STAT_T. + + To get a value from a proc_stat_t PS with a getter, you must make sure all + the pstat_flags returned by ps_getter_needs(GETTER) are set in PS, and + then call the function returned ps_getter_function(GETTER) with PS as the + first argument. + + The way the actual value is returned from this funciton is dependent on + the type of the value: + For int's and float's, the value is the return value. + For strings, you must pass in two extra arguments, a char **, which is + filled in with a pointer to the string, or NULL if the string is + NULL, and an int *, which is filled in with the length of the string. +*/ + +typedef struct ps_getter *ps_getter_t; + +struct ps_getter + { + /* The getter's name */ + char *name; + + /* What proc_stat flags need to be set as a precondition to calling this + getter's function. */ + int needs; + + /* A function that will get the value; the protocol between this function + and its caller is type-dependent. */ + void (*fn) (); + }; + +/* Access macros: */ +#define ps_getter_name(g) ((g)->name) +#define ps_getter_needs(g) ((g)->needs) +#define ps_getter_function(g) ((g)->fn) + +/* ---------------------------------------------------------------- */ +/* A PS_FILTER_T describes how to select some subset of a PROC_STAT_LIST_T */ + +typedef struct ps_filter *ps_filter_t; + +struct ps_filter + { + /* Name of this filter. */ + char *name; + + /* The flags that need to be set in each proc_stat_t in the list to + call the filter's predicate function; if these flags can't be set in a + particular proc_stat_t, the function is not called, and it isn't deleted + from the list. */ + int needs; + + /* A function that returns true if called on a proc_stat_t that the + filter accepts, or false if the filter rejects it. */ + bool(*fn) (proc_stat_t ps); + }; + +/* Access macros: */ +#define ps_filter_name(f) ((f)->name) +#define ps_filter_needs(f) ((f)->needs) +#define ps_filter_predicate(f) ((f)->fn) + +/* Some predefined filters. These are structures; you must use the & + operator to get a ps_filter_t from them */ + +/* A filter that retains only process's owned by getuid() */ +struct ps_filter ps_own_filter; +/* A filter that retains only process's that aren't session leaders */ +struct ps_filter ps_not_sess_leader_filter; +/* A filter that retains only process's with a controlling terminal */ +struct ps_filter ps_ctty_filter; +/* A filter that retains only `unorphaned' process. A process is unorphaned + if it's a session leader, or the process's process group is not orphaned */ +struct ps_filter ps_unorphaned_filter; +/* A filter that retains only `parented' process. Typically only hurd + processes have parents. */ +struct ps_filter ps_parent_filter; + +/* ---------------------------------------------------------------- */ +/* + A PS_FMT_SPEC_T describes how to output something from a PROC_STAT_T; it + is a combination of a getter (describing how to get the value), an output + function (which outputs the result of the getter), and a compare function + (which can be used to sort proc_stat_t's according to how they are + output). It also specifies the default width of the field in which the + output should be printed. + */ + +typedef struct ps_fmt_spec *ps_fmt_spec_t; + +struct ps_fmt_spec + { + /* The name of the spec (and it's default title) */ + char *name; + + ps_getter_t getter; + + /* A function that, given a ps, a getter, a field width, and a stream, + will output what the getter gets in some format */ + error_t + (*output_fn)(proc_stat_t ps, ps_getter_t getter, + int width, FILE *str, int *count); + + /* A function that, given two pses and a getter, will compare what + the getter gets for each ps, and return an integer ala qsort */ + int (*cmp_fn)(proc_stat_t ps1, proc_stat_t ps2, ps_getter_t getter); + + /* The default width of the field that this spec will be printed in if not + overridden. */ + int default_width; + }; + +/* Accessor macros: */ +#define ps_fmt_spec_name(spec) ((spec)->name) +#define ps_fmt_spec_output_fn(spec) ((spec)->output_fn) +#define ps_fmt_spec_compare_fn(spec) ((spec)->cmp_fn) +#define ps_fmt_spec_getter(spec) ((spec)->getter) +#define ps_fmt_spec_default_width(spec) ((spec)->default_width) + +/* Returns true if a pointer into an array of struct ps_fmt_specs is at the + end. */ +#define ps_fmt_spec_is_end(spec) ((spec)->name == NULL) + +/* An array of struct ps_fmt_spec, suitable for use with find_ps_fmt_spec, + containing specs for most values in a proc_stat_t. */ +struct ps_fmt_spec ps_std_fmt_specs[]; + +/* Searches for a spec called NAME in SPECS (an array of struct ps_fmt_spec) + and returns it if found, otherwise NULL. */ +ps_fmt_spec_t find_ps_fmt_spec(char *name, ps_fmt_spec_t specs); + +/* ---------------------------------------------------------------- */ +/* A PS_FMT_T describes how to output user-readable version of a proc_stat_t. + It consists of a series of PS_FMT_FIELD_Ts, each describing how to output + one value. */ + +/* PS_FMT_FIELD_T */ +typedef struct ps_fmt_field *ps_fmt_field_t; +struct ps_fmt_field + { + /* A ps_fmt_spec_t describing how to output this field's value, or NULL + if there is no value (in which case this is the last field, and exists + just to output its prefix string). */ + ps_fmt_spec_t spec; + + /* A non-zero-terminated string of characters that should be output + between the previous field and this one. */ + char *pfx; + /* The number of characters from PFX that should be output. */ + int pfx_len; + + /* Returns the number of characters that the value portion of this field + should consume. If this field is negative, then the absolute value is + used, and the field should be right-aligned, otherwise, it is + left-aligned. */ + int width; + + /* Returns the title used when printing a header line for this field. */ + char *title; + }; + +/* Accessor macros: */ +#define ps_fmt_field_fmt_spec(field) ((field)->spec) +#define ps_fmt_field_prefix(field) ((field)->pfx) +#define ps_fmt_field_prefix_length(field) ((field)->pfx_len) +#define ps_fmt_field_width(field) ((field)->width) +#define ps_fmt_field_title(field) ((field)->title) + +/* PS_FMT_T */ +typedef struct ps_fmt *ps_fmt_t; +struct ps_fmt + { + /* A pointer to an array of struct ps_fmt_field's holding the individual + fields to be formatted. */ + ps_fmt_field_t fields; + /* The (valid) length of the fields array. */ + int num_fields; + + /* A set of proc_stat flags describing what a proc_stat_t needs to hold in + order to print out every field in the fmt. */ + int needs; + + /* Storage for various strings pointed to by the fields. */ + char *src; + }; + +/* Accessor macros: */ +#define ps_fmt_fields(fmt) ((fmt)->fields) +#define ps_fmt_num_fields(fmt) ((fmt)->num_fields) +#define ps_fmt_needs(fmt) ((fmt)->needs) + +/* + Make a PS_FMT_T by parsing the string SRC, searching for any named + field specs in FMT_SPECS, and returning the result in FMT. If a memory + allocation error occurs, ENOMEM is returned. If SRC contains an unknown + field name, EINVAL is returned. Otherwise 0 is returned. + + The syntax of SRC is: + SRC: FIELD* [ SUFFIX ] + FIELD: [ PREFIX ] SPEC + SPEC: `~' [ `-' ] [ WIDTH ] [ `/' ] NAME [ `/' ] + WIDTH: `[0-9]+' + NAME: `[^/]*' + + PREFIXes and SUFFIXes are printed verbatim, and specs are replaced by the + output of the named spec with that name (each spec specifies what + proc_stat_t field to print, and how to print it, as well as a default + field width into which put the output). WIDTH is used to override the + spec's default width. If a `-' is included, the output is right-aligned + within this width, otherwise it is left-aligned. + */ +error_t ps_fmt_create(char *src, ps_fmt_spec_t fmt_specs, ps_fmt_t *fmt); + +/* Free FMT, and any resources it consumes. */ +void ps_fmt_free(ps_fmt_t fmt); + +/* Write an appropiate header line for FMT, containing the titles of all its + fields appropiately aligned with where the values would be printed, to + STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t ps_fmt_write_titles(ps_fmt_t fmt, FILE *stream, int *count); + +/* Format a description as instructed by FMT, of the process described by PS + to STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t ps_fmt_write_proc_stat(ps_fmt_t fmt, + proc_stat_t ps, FILE *stream, int *count); + +/* Remove those fields from FMT which would need the proc_stat flags FLAGS. + Appropiate inter-field characters are also removed: those *following* + deleted fields at the beginning of the fmt, and those *preceeding* deleted + fields *not* at the beginning. */ +void ps_fmt_squash(ps_fmt_t fmt, int flags); + +/* ---------------------------------------------------------------- */ +/* A PROC_STAT_LIST_T represents a list of proc_stat_t's */ + +typedef struct proc_stat_list *proc_stat_list_t; + +struct proc_stat_list + { + /* An array of proc_stat_t's for the processes in this list. */ + proc_stat_t *proc_stats; + + /* The number of processes in the list. */ + int num_procs; + + /* The actual allocated length of PROC_STATS (in case we want to add more + processes). */ + int alloced; + + /* Returns the proc server that these processes are from. */ + process_t server; + }; + +/* Accessor macros: */ +#define proc_stat_list_num_procs(pp) ((pp)->num_procs) +#define proc_stat_list_server(pp) ((pp)->server) + +/* Creates a new proc_stat_list_t for processes from SERVER, which is + returned in PP, and returns 0, or else returns ENOMEM if there wasn't + enough memory. */ +error_t proc_stat_list_create(process_t server, proc_stat_list_t *pp); + +/* Free PP, and any resources it consumes. */ +void proc_stat_list_free(proc_stat_list_t pp); + +/* Returns the proc_stat_t in PP with a process-id of PID, if there's one, + otherwise, NULL. */ +proc_stat_t proc_stat_list_pid_proc_stat(proc_stat_list_t pp, int pid); + +/* Add proc_stat_t entries to PP for each process with a process id in the + array PIDS (where NUM_PROCS is the length of PIDS). Entries are only + added for processes not already in PP. ENOMEM is returned if a memory + allocation error occurs, otherwise 0. PIDs is not referenced by the + resulting proc_stat_list_t, and so may be subsequently freed. */ +error_t proc_stat_list_add_pids(proc_stat_list_t pp, int *pids, int num_procs); + +/* Add a proc_stat_t for the process designated by PID at PP's proc server to + PP. If PID already has an entry in PP, nothing is done. If a memory + allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t proc_stat_list_add_pid(proc_stat_list_t pp, int pid); + +/* Adds all proc_stat_t's in MERGEE to PP that don't correspond to processes + already in PP; the resulting order of proc_stat_t's in PP is undefined. + If MERGEE and PP point to different proc servers, EINVAL is returned. If a + memory allocation error occurs, ENOMEM is returned. Otherwise 0 is + returned, and MERGEE is freed. */ +error_t proc_stat_list_merge(proc_stat_list_t pp, proc_stat_list_t mergee); + +/* Add to PP entries for all processes at its server. If an error occurs, + the system error code is returned, otherwise 0. */ +error_t proc_stat_list_add_all(proc_stat_list_t pp); + +/* Add to PP entries for all processes in the login collection LOGIN_ID at + its server. If an error occurs, the system error code is returned, + otherwise 0. */ +error_t proc_stat_list_add_login_coll(proc_stat_list_t pp, int login_id); + +/* Add to PP entries for all processes in the session SESSION_ID at its + server. If an error occurs, the system error code is returned, otherwise + 0. */ +error_t proc_stat_list_add_session(proc_stat_list_t pp, int session_id); + +/* Try to set FLAGS in each proc_stat_t in PP (but they may still not be set + -- you have to check). If a fatal error occurs, the error code is + returned, otherwise 0. */ +error_t proc_stat_list_set_flags(proc_stat_list_t pp, int flags); + +/* Destructively modify PP to only include proc_stat_t's for which the + function PREDICATE returns true; if INVERT is true, only proc_stat_t's for + which PREDICATE returns false are kept. FLAGS is the set of pstat_flags + that PREDICATE requires be set as precondition. Regardless of the value + of INVERT, all proc_stat_t's for which the predicate's preconditions can't + be satisfied are kept. If a fatal error occurs, the error code is + returned, it returns 0. */ +error_t proc_stat_list_filter1(proc_stat_list_t pp, + int (*predicate)(proc_stat_t ps), int flags, + bool invert); + +/* Destructively modify PP to only include proc_stat_t's for which the + predicate function in FILTER returns true; if INVERT is true, only + proc_stat_t's for which the predicate returns false are kept. Regardless + of the value of INVERT, all proc_stat_t's for which the predicate's + preconditions can't be satisfied are kept. If a fatal error occurs, + the error code is returned, it returns 0. */ +error_t proc_stat_list_filter(proc_stat_list_t pp, + ps_filter_t filter, bool invert); + +/* Destructively sort proc_stats in PP by ascending value of the field + returned by GETTER, and compared by CMP_FN; If REVERSE is true, use the + opposite order. If a fatal error occurs, the error code is returned, it + returns 0. */ +error_t proc_stat_list_sort1(proc_stat_list_t pp, + ps_getter_t getter, + int (*cmp_fn)(proc_stat_t ps1, proc_stat_t ps2, + ps_getter_t getter), + bool reverse); + +/* Destructively sort proc_stats in PP by ascending value of the field KEY; + if REVERSE is true, use the opposite order. If KEY isn't a valid sort + key, EINVAL is returned. If a fatal error occurs the error code is + returned. Otherwise, 0 is returned. */ +error_t proc_stat_list_sort(proc_stat_list_t pp, + ps_fmt_spec_t key, bool reverse); + +/* Format a description as instructed by FMT, of the processes in PP to + STREAM, separated by newlines (and with a terminating newline). If COUNT + is non-NULL, it points to an integer which is incremented by the number of + characters output. If a fatal error occurs, the error code is returned, + otherwise 0. */ +error_t proc_stat_list_fmt(proc_stat_list_t pp, + ps_fmt_t fmt, FILE *stream, int *count); + +/* Modifies FLAGS to be the subset which can't be set in any proc_stat_t in + PP (and as a side-effect, adds as many bits from FLAGS to each proc_stat_t + as possible). If a fatal error occurs, the error code is returned, + otherwise 0. */ +error_t proc_stat_list_find_bogus_flags(proc_stat_list_t pp, int *flags); + +/* Add thread entries for for every process in PP, located immediately after + the containing process in sequence. Subsequent sorting of PP will leave + the thread entries located after the containing process, although the + order of the thread entries themselves may change. If a fatal error + occurs, the error code is returned, otherwise 0. */ +error_t proc_stat_list_add_threads(proc_stat_list_t pp); + +/* ---------------------------------------------------------------- */ + +#endif /* __PS_H__ */ diff --git a/libps/pshost.h b/libps/pshost.h new file mode 100644 index 00000000..62b74876 --- /dev/null +++ b/libps/pshost.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 1995 Free Software Foundation, Inc. + + 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. */ + +#ifndef __PSHOST_H__ +#define __PSHOST_H__ + +#include <mach/mach_types.h> +#include <mach/host_info.h> + +/* ---------------------------------------------------------------- */ + +/* + The Basic & Sched info types are pretty static, so we cache them, but load + info is dynamic so we don't cache that. + + See <mach/host_info.h> for information on the data types these routines + return. +*/ + +/* Return the current host port. */ +host_t ps_get_host(); + +/* Return a pointer to basic info about the current host in HOST_INFO. Since + this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_basic_info(host_basic_info_t *host_info); + +/* Return a pointer to scheduling info about the current host in HOST_INFO. + Since this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_sched_info(host_sched_info_t *host_info); + +/* Return a pointer to load info about the current host in HOST_INFO. Since + this is global information we just use a static buffer (if someone desires + to keep old load info, they should copy the buffer we return a pointer + to). If a system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_load_info(host_load_info_t *host_info); + +#endif /* __PSHOST_H__ */ diff --git a/libps/spec.c b/libps/spec.c new file mode 100644 index 00000000..c3e68f95 --- /dev/null +++ b/libps/spec.c @@ -0,0 +1,756 @@ +/* Access, formatting, & comparison routines for printing process info. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 "ps.h" +#include "pshost.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ +/* Getter definitions */ + +typedef void (*vf)(); + +static int +ps_get_pid(proc_stat_t ps) +{ + return proc_stat_pid(ps); +} +struct ps_getter ps_pid_getter = +{"pid", PSTAT_PID, (vf) ps_get_pid}; + +static int +ps_get_thread_index(proc_stat_t ps) +{ + return proc_stat_thread_index(ps); +} +struct ps_getter ps_thread_index_getter = +{"thread_index", PSTAT_THREAD, (vf) ps_get_thread_index}; + +static int +ps_get_owner(proc_stat_t ps) +{ + return proc_stat_info(ps)->owner; +} +struct ps_getter ps_owner_getter = +{"owner", PSTAT_INFO, (vf) ps_get_owner}; + +static int +ps_get_ppid(proc_stat_t ps) +{ + return proc_stat_info(ps)->ppid; +} +struct ps_getter ps_ppid_getter = +{"ppid", PSTAT_INFO, (vf) ps_get_ppid}; + +static int +ps_get_pgrp(proc_stat_t ps) +{ + return proc_stat_info(ps)->pgrp; +} +struct ps_getter ps_pgrp_getter = +{"pgrp", PSTAT_INFO, (vf) ps_get_pgrp}; + +static int +ps_get_session(proc_stat_t ps) +{ + return proc_stat_info(ps)->session; +} +struct ps_getter ps_session_getter = +{"session", PSTAT_INFO, (vf) ps_get_session}; + +static int +ps_get_login_col(proc_stat_t ps) +{ + return proc_stat_info(ps)->logincollection; +} +struct ps_getter ps_login_col_getter = +{"login_col", PSTAT_INFO, (vf) ps_get_login_col}; + +static int +ps_get_num_threads(proc_stat_t ps) +{ + return proc_stat_num_threads(ps); +} +struct ps_getter ps_num_threads_getter = +{"num_threads", PSTAT_NUM_THREADS, (vf)ps_get_num_threads}; + +static void +ps_get_args(proc_stat_t ps, char **args_p, int *args_len_p) +{ + *args_p = proc_stat_args(ps); + *args_len_p = proc_stat_args_len(ps); +} +struct ps_getter ps_args_getter = +{"args", PSTAT_ARGS, ps_get_args}; + +static int +ps_get_state(proc_stat_t ps) +{ + return proc_stat_state(ps); +} +struct ps_getter ps_state_getter = +{"state", PSTAT_STATE, (vf) ps_get_state}; + +static int +ps_get_vsize(proc_stat_t ps) +{ + return proc_stat_info(ps)->taskinfo.virtual_size; +} +struct ps_getter ps_vsize_getter = +{"vsize", PSTAT_INFO, (vf) ps_get_vsize}; + +static int +ps_get_rsize(proc_stat_t ps) +{ + return proc_stat_info(ps)->taskinfo.resident_size; +} +struct ps_getter ps_rsize_getter = +{"rsize", PSTAT_INFO, (vf) ps_get_rsize}; + +static int +ps_get_cur_priority(proc_stat_t ps) +{ + return proc_stat_thread_sched_info(ps)->cur_priority; +} +struct ps_getter ps_cur_priority_getter = +{"cur_priority", PSTAT_THREAD_INFO, (vf) ps_get_cur_priority}; + +static int +ps_get_base_priority(proc_stat_t ps) +{ + return proc_stat_thread_sched_info(ps)->base_priority; +} +struct ps_getter ps_base_priority_getter = +{"base_priority", PSTAT_THREAD_INFO, (vf) ps_get_base_priority}; + +static int +ps_get_max_priority(proc_stat_t ps) +{ + return proc_stat_thread_sched_info(ps)->max_priority; +} +struct ps_getter ps_max_priority_getter = +{"max_priority", PSTAT_THREAD_INFO, (vf) ps_get_max_priority}; + +static void +ps_get_usr_time(proc_stat_t ps, time_value_t * tv_out) +{ + *tv_out = proc_stat_thread_basic_info(ps)->user_time; +} +struct ps_getter ps_usr_time_getter = +{"usr_time", PSTAT_THREAD_INFO, ps_get_usr_time}; + +static void +ps_get_sys_time(proc_stat_t ps, time_value_t * tv_out) +{ + *tv_out = proc_stat_thread_basic_info(ps)->system_time; +} +struct ps_getter ps_sys_time_getter = +{"sys_time", PSTAT_THREAD_INFO, ps_get_sys_time}; + +static void +ps_get_tot_time(proc_stat_t ps, time_value_t * tv_out) +{ + *tv_out = proc_stat_thread_basic_info(ps)->user_time; + time_value_add(tv_out, &proc_stat_thread_basic_info(ps)->system_time); +} +struct ps_getter ps_tot_time_getter = +{"tot_time", PSTAT_THREAD_INFO, ps_get_tot_time}; + +static float +ps_get_rmem_frac(proc_stat_t 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_info(ps)->taskinfo.resident_size / (float)mem_size; + else + return 0.0; +} +struct ps_getter ps_rmem_frac_getter = +{"rmem_frac", PSTAT_INFO, (vf) ps_get_rmem_frac}; + +static float +ps_get_cpu_frac(proc_stat_t ps) +{ + return (float) proc_stat_thread_basic_info(ps)->cpu_usage + / (float) TH_USAGE_SCALE; +} +struct ps_getter ps_cpu_frac_getter = +{"cpu_frac", PSTAT_THREAD_INFO, (vf) ps_get_cpu_frac}; + +static int +ps_get_sleep(proc_stat_t ps) +{ + return proc_stat_thread_basic_info(ps)->sleep_time; +} +struct ps_getter ps_sleep_getter = +{"sleep", PSTAT_THREAD_INFO, (vf) ps_get_sleep}; + +static void +ps_get_tty_name(proc_stat_t ps, char **str_p, int *len_p) +{ + *str_p = proc_stat_tty_name(ps); + if (*str_p != NULL) + *len_p = strlen(*str_p) + 1; +} +struct ps_getter ps_tty_name_getter = +{"tty_name", PSTAT_TTY_NAME, ps_get_tty_name}; + +static int +ps_get_page_faults(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->faults; +} +struct ps_getter ps_page_faults_getter = +{"page_faults", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_page_faults}; + +static int +ps_get_cow_faults(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->cow_faults; +} +struct ps_getter ps_cow_faults_getter = +{"cow_faults", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_cow_faults}; + +static int +ps_get_pageins(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->pageins; +} +struct ps_getter ps_pageins_getter = +{"pageins", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_pageins}; + +static int +ps_get_msgs_sent(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->messages_sent; +} +struct ps_getter ps_msgs_sent_getter = +{"msgs_sent", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_msgs_sent}; + +static int +ps_get_msgs_rcvd(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->messages_received; +} +struct ps_getter ps_msgs_rcvd_getter = +{"msgs_rcvd", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_msgs_rcvd}; + +static int +ps_get_zero_fills(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->zero_fills; +} +struct ps_getter ps_zero_fills_getter = +{"zero_fills", PSTAT_TASK_EVENTS_INFO, (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(g,type)((type (*)())ps_getter_function(g)) + +error_t +ps_emit_int(proc_stat_t ps, ps_getter_t getter, int width, FILE *stream, int *count) +{ + return ps_write_int_field(G(getter, int)(ps), width, stream, count); +} + +error_t +ps_emit_num_blocks(proc_stat_t ps, ps_getter_t getter, int width, FILE + *stream, int *count) +{ + char buf[20]; + sprintf(buf, "%ld", G(getter, int)(ps) / 1024); + return ps_write_field(buf, width, stream, count); +} + +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, "%ld.%0*ld", value, frac_len, frac); + else + sprintf(buf, "%ld", value); + + return strlen(buf); +} + +error_t +ps_emit_percent(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char buf[20]; + float perc = G(getter, 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_write_field(buf, width, stream, count); +} + +/* prints its value nicely */ +error_t +ps_emit_nice_int(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char buf[20]; + int value = G(getter, 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(width) - 1), + "%c", *sfx); + + return ps_write_field(buf, width, stream, count); +} + +#define MINUTE 60 +#define HOUR (60*MINUTE) +#define DAY (24*HOUR) +#define WEEK (7*DAY) + +static int +sprint_long_time(char *buf, int seconds, int width) +{ + char *p = buf; + struct tscale + { + int length; + char *sfx; + char *short_sfx; + } + time_scales[] = + { + { WEEK, " week", "wk"} , + { DAY, " day", "dy"} , + { HOUR, " hour", "hr"} , + { MINUTE," min", "m"} , + { 0} + }; + struct tscale *ts = time_scales; + + while (ts->length > 0 && width > 0) + { + if (ts->length < seconds) + { + int len; + int num = seconds / ts->length; + seconds %= ts->length; + sprintf(p, "%d%s", num, ts->sfx); + len = strlen(p); + width -= len; + if (width < 0 && p > buf) + break; + p += len; + } + ts++; + } + + *p = '\0'; + + return p - buf; +} + +error_t +ps_emit_nice_seconds(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char buf[20]; + time_value_t tv; + + G(getter, int)(ps, &tv); + + if (tv.seconds == 0) + { + if (tv.microseconds < 500) + sprintf(buf, "%dus", tv.microseconds); + else + strcpy(buf + + sprint_frac_value(buf, + tv.microseconds / 1000, 1, + tv.microseconds % 1000, 3, + ABS(width) - 2), + "ms"); + } + else if (tv.seconds < MINUTE) + sprint_frac_value(buf, tv.seconds, 1, tv.microseconds, 6, ABS(width)); + else if (tv.seconds < HOUR) + { + /* 0:00.00... */ + int min_len; + sprintf(buf, "%d:", tv.seconds / 60); + min_len = strlen(buf); + sprint_frac_value(buf + min_len, + tv.seconds % 60, 2, + tv.microseconds, 6, + ABS(width) - min_len); + } + else + sprint_long_time(buf, tv.seconds, width); + + return ps_write_field(buf, width, stream, count); +} + +static int +append_fraction(char *buf, int frac, int digits, int width) +{ + int slen = strlen(buf); + int left = width - strlen(buf); + if (left > 1) + { + buf[slen] = '.'; + left--; + while (digits > left) + frac /= 10, digits--; + sprintf(buf + slen + 1, "%0*d", digits, frac); + return slen + 1 + digits; + } + else + return slen; +} + +error_t +ps_emit_seconds(proc_stat_t ps, ps_getter_t getter, int width, FILE *stream, + int *count) +{ + int max = (width == 0 ? 999 : ABS(width)); + char buf[20]; + time_value_t tv; + + G(getter, void)(ps, &tv); + + if (tv.seconds > DAY) + sprint_long_time(buf, tv.seconds, max); + else if (tv.seconds > HOUR) + if (max >= 8) + { + /* 0:00:00.00... */ + sprintf(buf, "%2d:%02d:%02d", + tv.seconds / HOUR, + (tv.seconds % HOUR) / MINUTE, (tv.seconds % MINUTE)); + append_fraction(buf, tv.microseconds, 6, max); + } + else + sprint_long_time(buf, tv.seconds, max); + else if (max >= 5 || tv.seconds > MINUTE) + { + /* 0:00.00... */ + sprintf(buf, "%2d:%02d", tv.seconds / MINUTE, tv.seconds % MINUTE); + append_fraction(buf, tv.microseconds, 6, max); + } + else + sprint_frac_value(buf, tv.seconds, 1, tv.microseconds, 6, max); + + return ps_write_field(buf, width, stream, count); +} + +error_t +ps_emit_user(proc_stat_t ps, ps_getter_t getter, int width, FILE *stream, int *count) +{ + int uid = G(getter, int)(ps); + if (uid < 0) + return ps_write_padding(0, width, stream, count); + else + { + struct passwd *pw = getpwuid(uid); + if (pw == NULL) + return ps_write_int_field(uid, width, stream, count); + else + return ps_write_field(pw->pw_name, width, stream, count); + } +} + +/* prints a string with embedded nuls as spaces */ +error_t +ps_emit_string0(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char *s0, *p, *q; + int s0len; + int fwidth = ABS(width); + char static_buf[200]; + char *buf = static_buf; + + G(getter, void)(ps, &s0, &s0len); + + if (s0 == NULL) + *buf = '\0'; + 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_write_field(buf, width, stream, count); + if (buf != static_buf) + free(buf); + return err; + } +} + +error_t +ps_emit_string(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char *str; + int len; + + G(getter, void)(ps, &str, &len); + + if (str == NULL) + str = ""; + else if (width != 0 && len > ABS(width)) + str[ABS(width)] = '\0'; + + return ps_write_field(str, width, stream, count); +} + +error_t +ps_emit_tty_name(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + struct tty_abbrev + { + char *pfx, *subst; + } + abbrevs[] = + { + { "/tmp/console", "oc" }, /* temp hack */ + { "/dev/console", "co"}, + { "/dev/tty", ""}, + { "/dev/pty", ""}, + { "/dev/", ""}, + { 0 } + }; + char buf[20]; + char *str; + int len; + + G(getter, void)(ps, &str, &len); + + if (str == NULL || len == 0) + str = "-"; + else + { + struct tty_abbrev *abbrev = abbrevs; + while (abbrev->pfx != NULL) + { + int pfx_len = strlen(abbrev->pfx); + if (strncmp(str, abbrev->pfx, pfx_len) == 0) + { + strcpy(buf, abbrev->subst); + strcat(buf, str + pfx_len); + str = buf; + break; + } + else + abbrev++; + } + } + + return ps_write_field(str, width, stream, count); +} + +error_t +ps_emit_state(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char *tags; + int state = G(getter, int)(ps); + char buf[20], *p = buf; + + /* turn off seemingly bogus or annoying flags */ + state &= ~PSTAT_STATE_SWAPPED; + + /* If any thread is running, don't mention sleeping or idle threads -- + presumably *some* work is getting done... */ + if (state & PSTAT_STATE_RUNNING) + state &= ~(PSTAT_STATE_SLEEPING | PSTAT_STATE_IDLE); + if (state & PSTAT_STATE_IDLE) + state &= ~PSTAT_STATE_SLEEPING; + + for (tags = proc_stat_state_tags + ; state != 0 && *tags != '\0' + ; state >>= 1, tags++) + if (state & 1) + *p++ = *tags; + + *p = '\0'; + + return ps_write_field(buf, width, stream, count); +} + +/* ---------------------------------------------------------------- */ +/* comparison functions */ + +int +ps_cmp_ints(proc_stat_t ps1, proc_stat_t ps2, ps_getter_t 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(proc_stat_t ps1, proc_stat_t ps2, ps_getter_t getter) +{ + float (*gf)() = G(getter, float); + float v1 = gf(ps1), v2 = gf(ps2); + return v1 == v2 ? 0 : v1 < v2 ? -1 : 1; +} + +int +ps_cmp_strings(proc_stat_t ps1, proc_stat_t ps2, ps_getter_t getter) +{ + void (*gf)() = G(getter, void); + char *s1, *s2; + int s1len, s2len; + + /* Get both strings */ + gf(ps1, &s1, &s1len); + gf(ps2, &s2, &s2len); + + if (s1 == NULL) + if (s2 == NULL) + return 0; + else + return -1; + else + if (s2 == NULL) + return 1; + else + return strncmp(s1, s2, MIN(s1len, s2len)); +} + +/* ---------------------------------------------------------------- */ + +ps_fmt_spec_t +find_ps_fmt_spec(char *name, ps_fmt_spec_t specs) +{ + while (!ps_fmt_spec_is_end(specs)) + if (strcasecmp(ps_fmt_spec_name(specs), name) == 0) + return specs; + else + specs++; + return NULL; +} + +/* ---------------------------------------------------------------- */ + +struct ps_fmt_spec ps_std_fmt_specs[] = +{ + {"PID", &ps_pid_getter, ps_emit_int, ps_cmp_ints, -5}, + {"TH#", &ps_thread_index_getter,ps_emit_int, ps_cmp_ints, -2}, + {"PPID", &ps_ppid_getter, ps_emit_int, ps_cmp_ints, -5}, + {"UID", &ps_owner_getter, ps_emit_int, ps_cmp_ints, -5}, + {"NTh", &ps_num_threads_getter, ps_emit_int, ps_cmp_ints, -2}, + {"PGrp", &ps_pgrp_getter, ps_emit_int, ps_cmp_ints, -5}, + {"User", &ps_owner_getter, ps_emit_user, ps_cmp_ints, 6}, + {"Sess", &ps_session_getter, ps_emit_int, ps_cmp_ints, -5}, + {"LColl", &ps_login_col_getter, ps_emit_int, ps_cmp_ints, -5}, + {"Args", &ps_args_getter, ps_emit_string0, ps_cmp_strings, 0}, + {"Time", &ps_tot_time_getter, ps_emit_seconds, ps_cmp_ints, -8}, + {"UTime", &ps_usr_time_getter, ps_emit_seconds, ps_cmp_ints, -8}, + {"STime", &ps_sys_time_getter, ps_emit_seconds, ps_cmp_ints, -8}, + {"VSize", &ps_vsize_getter, ps_emit_nice_int, ps_cmp_ints, -5}, + {"RSize", &ps_rsize_getter, ps_emit_nice_int, ps_cmp_ints, -5}, + {"Pri", &ps_cur_priority_getter,ps_emit_int, ps_cmp_ints, -2}, + {"BPri", &ps_base_priority_getter,ps_emit_int, ps_cmp_ints, -2}, + {"MPri", &ps_max_priority_getter,ps_emit_int, ps_cmp_ints, -2}, + {"%Mem", &ps_rmem_frac_getter, ps_emit_percent, ps_cmp_floats, -4}, + {"%CPU", &ps_cpu_frac_getter, ps_emit_percent, ps_cmp_floats, -4}, + {"State", &ps_state_getter, ps_emit_state, NULL, 4}, + {"Sleep", &ps_sleep_getter, ps_emit_int, ps_cmp_ints, -2}, + {"TTY", &ps_tty_name_getter, ps_emit_tty_name, ps_cmp_strings, 2}, + {"PgFlts", &ps_page_faults_getter, ps_emit_int, ps_cmp_ints, -5}, + {"COWFlts",&ps_cow_faults_getter, ps_emit_int, ps_cmp_ints, -5}, + {"PgIns", &ps_pageins_getter, ps_emit_int, ps_cmp_ints, -5}, + {"MsgsIn", &ps_msgs_rcvd_getter, ps_emit_int, ps_cmp_ints, -5}, + {"MsgsOut",&ps_msgs_sent_getter, ps_emit_int, ps_cmp_ints, -5}, + {"ZFills", &ps_zero_fills_getter, ps_emit_int, ps_cmp_ints, -5}, + {0} +}; diff --git a/libps/write.c b/libps/write.c new file mode 100644 index 00000000..8d37056c --- /dev/null +++ b/libps/write.c @@ -0,0 +1,133 @@ +/* Some helper functions for writing output fields. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +/* Write at most MAX_LEN characters of STRING to STREAM (if MAX_LEN > the + length of STRING, then write all of it; if MAX_LEN == -1, then write all + of STRING regardless). If COUNT is non-NULL, the number of characters + written is added to the integer it points to. If an error occurs, the + error code is returned, otherwise 0. */ +error_t +ps_write_string(char *string, int max_len, FILE *stream, int *count) +{ + int len = strlen(string); + + if (max_len > 0 && len > max_len) + len = max_len; + + if (len > 0) + { + int output = fwrite(string, 1, len, stream); + if (output == 0) + return errno; + if (count) + *count += output; + } + + return 0; +} + +/* Write NUM spaces to STREAM. If COUNT is non-NULL, the number of spaces + written is added to the integer it points to. If an error occurs, the + error code is returned, otherwise 0. */ +error_t +ps_write_spaces(int num, FILE *stream, int *count) +{ + static char spaces[] = " "; +#define spaces_len (sizeof(spaces) - 1) + + while (num > spaces_len) + { + error_t err = ps_write_string(spaces, spaces_len, stream, count); + if (err) + return err; + num -= spaces_len; + } + + if (num > 0) + return ps_write_string(spaces, num, stream, count); + else + return 0; +} + +/* Write as many spaces to STREAM as required to make a field of width SOFAR + be at least WIDTH characters wide (the absolute value of WIDTH is used). + If COUNT is non-NULL, the number of spaces written is added to the integer + it points to. If an error occurs, the error code is returned, otherwise + 0. */ +error_t +ps_write_padding(int sofar, int width, FILE *stream, int *count) +{ + width = ABS(width); + if (sofar < width) + return ps_write_spaces(width - sofar, stream, count); + else + return 0; +} + +/* Write the string BUF to STREAM, padded on one side with spaces to be at + least the absolute value of WIDTH long: if WIDTH >= 0, then on the left + side, otherwise on the right side. If COUNT is non-NULL, the number of + characters written is added to the integer it points to. If an error + occurs, the error code is returned, otherwise 0. */ +error_t +ps_write_field(char *buf, int width, FILE *stream, int *count) +{ + error_t err; + int len = strlen(buf); + + if (width > len) + { + err = ps_write_string(buf, -1, stream, count); + if (!err) + err = ps_write_spaces(width - len, stream, count); + } + else if (-width > len) + { + err = ps_write_spaces(-width - len, stream, count); + if (!err) + err = ps_write_string(buf, -1, stream, count); + } + else + err = ps_write_string(buf, -1, stream, count); + + return err; +} + +/* Write the decimal representation of VALUE to STREAM, padded on one side + with spaces to be at least the absolute value of WIDTH long: if WIDTH >= + 0, then on the left side, otherwise on the right side. If COUNT is + non-NULL, the number of characters written is added to the integer it + points to. If an error occurs, the error code is returned, otherwise 0. */ +error_t +ps_write_int_field(int value, int width, FILE *stream, int *count) +{ + char buf[20]; + sprintf(buf, "%ld", value); + return ps_write_field(buf, width, stream, count); +} |