diff options
Diffstat (limited to 'libps/proclist.c')
-rw-r--r-- | libps/proclist.c | 561 |
1 files changed, 561 insertions, 0 deletions
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; +} |