aboutsummaryrefslogtreecommitdiff
path: root/libps/proclist.c
diff options
context:
space:
mode:
Diffstat (limited to 'libps/proclist.c')
-rw-r--r--libps/proclist.c692
1 files changed, 692 insertions, 0 deletions
diff --git a/libps/proclist.c b/libps/proclist.c
new file mode 100644
index 00000000..2dfcfe7d
--- /dev/null
+++ b/libps/proclist.c
@@ -0,0 +1,692 @@
+/* The type proc_stat_list_t, which holds lists of proc_stats.
+
+ Copyright (C) 1995, 1996 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 <string.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* Creates a new proc_stat_list_t for processes from CONTEXT, which is
+ returned in PP, and returns 0, or else returns ENOMEM if there wasn't
+ enough memory. */
+error_t
+proc_stat_list_create (struct ps_context *context, struct proc_stat_list **pp)
+{
+ *pp = NEW (struct proc_stat_list);
+ if (*pp == NULL)
+ return ENOMEM;
+
+ (*pp)->proc_stats = 0;
+ (*pp)->num_procs = 0;
+ (*pp)->alloced = 0;
+ (*pp)->context = context;
+
+ return 0;
+}
+
+/* Free PP, and any resources it consumes. */
+void
+proc_stat_list_free (struct proc_stat_list *pp)
+{
+ proc_stat_list_remove_threads (pp);
+ FREE (pp->proc_stats);
+ FREE (pp);
+}
+
+/* Returns a copy of PP in COPY, or an error. */
+error_t
+proc_stat_list_clone (struct proc_stat_list *pp, struct proc_stat_list **copy)
+{
+ struct proc_stat_list *new = NEW (struct proc_stat_list);
+ struct proc_stat **procs = NEWVEC (struct proc_stat *, pp->num_procs);
+
+ if (!new || !procs)
+ {
+ if (new)
+ free (new);
+ if (procs)
+ free (procs);
+ return ENOMEM;
+ }
+
+ bcopy (pp->proc_stats, procs, pp->num_procs);
+
+ new->proc_stats = procs;
+ new->num_procs = pp->num_procs;
+ new->alloced = pp->num_procs;
+ new->context = pp->context;
+ *copy = new;
+
+ return 0;
+}
+
+/* Make sure there are at least AMOUNT new locations allocated in PP's
+ proc_stat array (but without changing NUM_PROCS). Returns ENOMEM if a
+ memory allocation error occurred, 0 otherwise. */
+static error_t
+proc_stat_list_grow (struct proc_stat_list *pp, int amount)
+{
+ amount += pp->num_procs;
+
+ if (amount > pp->alloced)
+ {
+ struct proc_stat **new_procs =
+ GROWVEC (pp->proc_stats, struct proc_stat *, amount);
+
+ if (new_procs == NULL)
+ return ENOMEM;
+
+ pp->alloced = amount;
+ pp->proc_stats = new_procs;
+ }
+
+ return 0;
+}
+
+/* Add proc_stat 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. If
+ PROC_STATS is non-NULL, a malloced array NUM_PROCS entries long of the
+ resulting proc_stats is returned in it. */
+error_t
+proc_stat_list_add_pids (struct proc_stat_list *pp,
+ pid_t *pids, unsigned num_procs,
+ struct proc_stat ***proc_stats)
+{
+ error_t err = proc_stat_list_grow (pp, num_procs);
+
+ if (err)
+ return err;
+ else
+ {
+ int i;
+ struct proc_stat **end = pp->proc_stats + pp->num_procs;
+
+ if (proc_stats)
+ *proc_stats = NEWVEC (struct proc_stat *, num_procs);
+
+ for (i = 0; i < num_procs; i++)
+ {
+ int pid = *pids++;
+ struct proc_stat *ps = proc_stat_list_pid_proc_stat (pp, pid);
+
+ if (ps == NULL)
+ {
+ err = ps_context_find_proc_stat (pp->context, pid, end);
+ if (err)
+ {
+ if (proc_stats)
+ free (*proc_stats);
+ return err;
+ }
+ else
+ ps = *end++;
+ }
+
+ if (proc_stats)
+ (*proc_stats)[i] = ps;
+ }
+
+ pp->num_procs = end - pp->proc_stats;
+
+ return 0;
+ }
+}
+
+/* Add a proc_stat for the process designated by PID at PP's proc context to
+ PP. If PID already has an entry in PP, nothing is done. If a memory
+ allocation error occurs, ENOMEM is returned, otherwise 0. If PS is
+ non-NULL, the resulting entry is returned in it. */
+error_t
+proc_stat_list_add_pid (struct proc_stat_list *pp, pid_t pid, struct proc_stat **ps)
+{
+ struct proc_stat *_ps = proc_stat_list_pid_proc_stat (pp, pid);
+
+ if (_ps == NULL)
+ {
+ error_t err;
+
+ if (pp->num_procs == pp->alloced)
+ {
+ err = proc_stat_list_grow (pp, 32);
+ if (err)
+ return err;
+ }
+
+ err = ps_context_find_proc_stat (pp->context, pid, &_ps);
+ if (err)
+ return err;
+
+ pp->proc_stats[pp->num_procs++] = _ps;
+ }
+
+ if (ps)
+ *ps = _ps;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the proc_stat in PP with a process-id of PID, if there's one,
+ otherwise, NULL. */
+struct proc_stat *
+proc_stat_list_pid_proc_stat (struct proc_stat_list *pp, pid_t pid)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ while (nprocs-- > 0)
+ if (proc_stat_pid (*procs) == pid)
+ return *procs;
+ else
+ procs++;
+
+ return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Adds all proc_stats in MERGEE to PP that don't correspond to processes
+ already in PP; the resulting order of proc_stats in PP is undefined.
+ If MERGEE and PP point to different proc contexts, 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 (struct proc_stat_list *pp, struct proc_stat_list *mergee)
+{
+ if (pp->context != mergee->context)
+ 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;
+ struct proc_stat **mprocs = mergee->proc_stats;
+ int nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ /* Transfer over any proc_stats 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 in the collection fetched from the
+ proc server by the function FETCH_FN. If an error occurs, the system
+ error code is returned, otherwise 0. If PROC_STATS and NUM_PROCS are
+ non-NULL, a malloced vector of the resulting entries is returned in them. */
+static error_t
+proc_stat_list_add_fn_pids (struct proc_stat_list *pp,
+ kern_return_t (*fetch_fn)(process_t proc,
+ pid_t **pids,
+ unsigned *num_pids),
+ struct proc_stat ***proc_stats, unsigned *num_procs)
+{
+ error_t err;
+ pid_t pid_array[STATICPIDS], *pids = pid_array;
+ unsigned num_pids = STATICPIDS;
+
+ err = (*fetch_fn)(ps_context_server (pp->context), &pids, &num_pids);
+ if (err)
+ return err;
+
+ err = proc_stat_list_add_pids (pp, pids, num_pids, proc_stats);
+ if (!err && num_procs)
+ *num_procs = num_pids;
+
+ if (pids != pid_array)
+ VMFREE(pids, sizeof (pid_t) * num_pids);
+
+ return err;
+}
+
+/* Add to PP entries for all processes in the collection fetched from the
+ proc server by the function FETCH_FN and ID. If an error occurs, the
+ system error code is returned, otherwise 0. If PROC_STATS and NUM_PROCS
+ are non-NULL, a malloced vector of the resulting entries is returned in
+ them. */
+static error_t
+proc_stat_list_add_id_fn_pids (struct proc_stat_list *pp, unsigned id,
+ kern_return_t (*fetch_fn)(process_t proc,
+ pid_t id,
+ pid_t **pids,
+ unsigned *num_pids),
+ struct proc_stat ***proc_stats, unsigned *num_procs)
+{
+ error_t id_fetch_fn (process_t proc, pid_t **pids, unsigned *num_pids)
+ {
+ return (*fetch_fn)(proc, id, pids, num_pids);
+ }
+ return proc_stat_list_add_fn_pids (pp, id_fetch_fn, proc_stats, num_procs);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Add to PP entries for all processes at its context. If an error occurs,
+ the system error code is returned, otherwise 0. If PROC_STATS and
+ NUM_PROCS are non-NULL, a malloced vector of the resulting entries is
+ returned in them. */
+error_t
+proc_stat_list_add_all (struct proc_stat_list *pp,
+ struct proc_stat ***proc_stats, unsigned *num_procs)
+{
+ return
+ proc_stat_list_add_fn_pids (pp, proc_getallpids, proc_stats, num_procs);
+}
+
+/* Add to PP entries for all processes in the login collection LOGIN_ID at
+ its context. If an error occurs, the system error code is returned,
+ otherwise 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector
+ of the resulting entries is returned in them. */
+error_t
+proc_stat_list_add_login_coll (struct proc_stat_list *pp, pid_t login_id,
+ struct proc_stat ***proc_stats, unsigned *num_procs)
+{
+ return
+ proc_stat_list_add_id_fn_pids (pp, login_id, proc_getloginpids,
+ proc_stats, num_procs);
+}
+
+/* Add to PP entries for all processes in the session SESSION_ID at its
+ context. If an error occurs, the system error code is returned, otherwise
+ 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the
+ resulting entries is returned in them. */
+error_t
+proc_stat_list_add_session (struct proc_stat_list *pp, pid_t session_id,
+ struct proc_stat ***proc_stats, unsigned *num_procs)
+{
+ return
+ proc_stat_list_add_id_fn_pids (pp, session_id, proc_getsessionpids,
+ proc_stats, num_procs);
+}
+
+/* Add to PP entries for all processes in the process group PGRP at its
+ context. If an error occurs, the system error code is returned, otherwise
+ 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the
+ resulting entries is returned in them. */
+error_t
+proc_stat_list_add_pgrp (struct proc_stat_list *pp, pid_t pgrp,
+ struct proc_stat ***proc_stats, unsigned *num_procs)
+{
+ return
+ proc_stat_list_add_id_fn_pids (pp, pgrp, proc_getpgrppids,
+ proc_stats, num_procs);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Try to set FLAGS in each proc_stat 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 (struct proc_stat_list *pp, ps_flags_t flags)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ while (nprocs-- > 0)
+ {
+ struct proc_stat *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_stats for which the
+ function PREDICATE returns true; if INVERT is true, only proc_stats 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_stats 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(struct proc_stat_list *pp,
+ int (*predicate)(struct proc_stat *ps), ps_flags_t flags,
+ int invert)
+{
+ unsigned which = 0;
+ unsigned num_procs = pp->num_procs;
+ struct proc_stat **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. */
+ struct proc_stat **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)
+ {
+ struct proc_stat *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;
+ /* ... otherwise implicitly delete PS from PP by not putting it in the
+ KEPT sequence. */
+ }
+
+ pp->num_procs = kept - procs;
+
+ return 0;
+}
+
+/* Destructively modify PP to only include proc_stats for which the
+ predicate function in FILTER returns true; if INVERT is true, only
+ proc_stats for which the predicate returns false are kept. Regardless
+ of the value of INVERT, all proc_stats 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 (struct proc_stat_list *pp,
+ const struct ps_filter *filter, int 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 (struct proc_stat_list *pp,
+ const struct ps_getter *getter,
+ int (*cmp_fn)(struct proc_stat *ps1,
+ struct proc_stat *ps2,
+ const struct ps_getter *getter),
+ int reverse)
+{
+ int needs = ps_getter_needs (getter);
+ struct proc_stat **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)
+ {
+ struct proc_stat *ps1 = *(struct proc_stat **)p1;
+ struct proc_stat *ps2 = *(struct proc_stat **)p2;
+ int is_th_1 = proc_stat_is_thread (ps1);
+ int 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 (ps1 == ps2 || !proc_stat_has(ps1, needs) || !proc_stat_has (ps2, needs))
+ /* If we're comparing a thread with it's process (and so the thread's
+ been replaced by the process), or we can't call CMP_FN on either
+ proc_stat 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 (struct proc_stat *), 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 (struct proc_stat_list *pp,
+ const struct ps_fmt_spec *key, int 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 a
+ fatal error occurs, the error code is returned, otherwise 0. */
+error_t
+proc_stat_list_fmt (struct proc_stat_list *pp, struct ps_fmt *fmt,
+ struct ps_stream *stream)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+ error_t err = proc_stat_list_set_flags(pp, ps_fmt_needs (fmt));
+
+ while (!err && nprocs-- > 0)
+ {
+ err = ps_fmt_write_proc_stat (fmt, *procs++, stream);
+ if (! err)
+ ps_stream_newline (stream);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Modifies FLAGS to be the subset which can't be set in any proc_stat in
+ PP (and as a side-effect, adds as many bits from FLAGS to each proc_stat
+ as possible). If a fatal error occurs, the error code is returned,
+ otherwise 0. */
+error_t
+proc_stat_list_find_bogus_flags (struct proc_stat_list *pp, ps_flags_t *flags)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **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 (struct proc_stat_list *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;
+ struct proc_stat **procs = pp->proc_stats;
+
+ /* First, count the number of threads that will be added. */
+ while (nprocs-- > 0)
+ {
+ struct proc_stat *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. */
+ {
+ struct proc_stat **end = pp->proc_stats + pp->num_procs + new_entries;
+
+ nprocs = pp->num_procs;
+ procs = pp->proc_stats + nprocs;
+
+ while (nprocs-- > 0)
+ {
+ struct proc_stat *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;
+}
+
+error_t
+proc_stat_list_remove_threads (struct proc_stat_list *pp)
+{
+ int is_thread (struct proc_stat *ps)
+ {
+ return proc_stat_is_thread (ps);
+ }
+ return proc_stat_list_filter1(pp, is_thread, 0, FALSE);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Calls FN in order for each proc_stat in PP. If FN ever returns a non-zero
+ value, then the iteration is stopped, and the value is returned
+ immediately; otherwise, 0 is returned. */
+int
+proc_stat_list_for_each (struct proc_stat_list *pp, int (*fn)(struct proc_stat *ps))
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ while (nprocs-- > 0)
+ {
+ int val = (*fn)(*procs++);
+ if (val)
+ return val;
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns true if SPEC is `nominal' in every entry in PP. */
+int
+proc_stat_list_spec_nominal (struct proc_stat_list *pp,
+ const struct ps_fmt_spec *spec)
+{
+ int (*nominal_fn)(struct proc_stat *ps, const struct ps_getter *getter) =
+ spec->nominal_fn;
+
+ if (nominal_fn == NULL)
+ return FALSE;
+ else
+ {
+ const struct ps_getter *getter = ps_fmt_spec_getter (spec);
+ ps_flags_t needs = ps_getter_needs (getter);
+ int interesting (struct proc_stat *ps)
+ {
+ return proc_stat_has (ps, needs) && !(*nominal_fn)(ps, getter);
+ }
+
+ proc_stat_list_set_flags (pp, needs);
+
+ return !proc_stat_list_for_each (pp, interesting);
+ }
+}