summaryrefslogtreecommitdiff
path: root/libps
diff options
context:
space:
mode:
Diffstat (limited to 'libps')
-rw-r--r--libps/filters.c74
-rw-r--r--libps/fmt.c385
-rw-r--r--libps/host.c110
-rw-r--r--libps/proclist.c561
-rw-r--r--libps/procstat.c419
-rw-r--r--libps/ps.h605
-rw-r--r--libps/pshost.h53
-rw-r--r--libps/spec.c756
-rw-r--r--libps/write.c133
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);
+}