summaryrefslogtreecommitdiffstats
path: root/library/pids.c
diff options
context:
space:
mode:
Diffstat (limited to 'library/pids.c')
-rw-r--r--library/pids.c1700
1 files changed, 1700 insertions, 0 deletions
diff --git a/library/pids.c b/library/pids.c
new file mode 100644
index 0000000..6ae94ad
--- /dev/null
+++ b/library/pids.c
@@ -0,0 +1,1700 @@
+/*
+ * pids.c - process related definitions for libproc2
+ *
+ * Copyright © 2015-2023 Jim Warner <james.warner@comcast.net>
+ * Copyright © 2015-2023 Craig Small <csmall@dropbear.xyz>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+//efine _GNU_SOURCE // for qsort_r
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "devname.h"
+#include "misc.h"
+#include "numa.h"
+#include "readproc.h"
+#include "wchan.h"
+
+#include "procps-private.h"
+#include "pids.h"
+
+
+//#define UNREF_RPTHASH // report hash details at uref() time
+
+#define FILL_ID_MAX 255 // upper limit with select of pid/uid
+#define STACKS_INIT 1024 // amount of initial stack allocation
+#define STACKS_GROW 128 // amount reap stack allocations grow
+#define NEWOLD_INIT 1024 // amount for initial hist allocation
+#define NEWOLD_GROW 128 // amt by which hist allocations grow
+
+/* ------------------------------------------------------------------------- +
+ this provision can be used to ensure that our Item_table was synchronized |
+ with those enumerators found in the associated header file. It's intended |
+ to only be used locally (& temporarily) at some point prior to a release! | */
+// #define ITEMTABLE_DEBUG //----------------------------------------------- |
+// ------------------------------------------------------------------------- +
+
+
+struct stacks_extent {
+ int ext_numstacks;
+ struct stacks_extent *next;
+ struct pids_stack **stacks;
+};
+
+struct fetch_support {
+ struct pids_stack **anchor; // reap/select consolidated extents
+ int n_alloc; // number of above pointers allocated
+ int n_inuse; // number of above pointers occupied
+ int n_alloc_save; // last known results.stacks allocation
+ struct pids_fetch results; // counts + stacks for return to caller
+ struct pids_counts counts; // actual counts pointed to by 'results'
+};
+
+typedef void (*SET_t)(struct pids_info *, struct pids_result *, proc_t *);
+
+struct pids_info {
+ int refcount;
+ int maxitems; // includes 'logical_end' delimiter
+ enum pids_item *items; // includes 'logical_end' delimiter
+ struct stacks_extent *extents; // anchor for all resettable extents
+ struct stacks_extent *otherexts; // anchor for invariant extents // <=== currently unused
+ struct fetch_support fetch; // support for procps_pids_reap, select, fatal
+ int history_yes; // need historical data
+ struct history_info *hist; // pointer to historical support data
+ proc_t*(*read_something)(PROCTAB*, proc_t*); // readproc/readeither via which
+ unsigned pgs2k_shift; // to convert some proc vaules
+ unsigned oldflags; // the old library PROC_FILL flagss
+ PROCTAB *fetch_PT; // oldlib interface for 'select' & 'reap'
+ unsigned long hertz; // for the 'TIME' & 'UTILIZATION' calculations
+ unsigned long long boot_tics; // for TIME_ELAPSED & 'UTILIZATION' calculations
+ PROCTAB *get_PT; // oldlib interface for active 'get'
+ struct stacks_extent *get_ext; // for active 'get' (also within 'extents')
+ enum pids_fetch_type get_type; // last known type of 'get' request
+ int seterr; // an ENOMEM encountered during assign
+ proc_t get_proc; // the proc_t used by procps_pids_get
+ proc_t fetch_proc; // the proc_t used by pids_stacks_fetch
+ SET_t *func_array; // extracted Item_table 'setsfunc' pointers
+};
+
+
+// ___ Free Storage Support |||||||||||||||||||||||||||||||||||||||||||||||||||
+
+#define freNAME(t) free_pids_ ## t
+
+static void freNAME(str) (struct pids_result *R) {
+ if (R->result.str) free(R->result.str);
+}
+
+static void freNAME(strv) (struct pids_result *R) {
+ if (R->result.strv && *R->result.strv) free(*R->result.strv);
+}
+
+
+// ___ Results 'Set' Support ||||||||||||||||||||||||||||||||||||||||||||||||||
+
+#define setNAME(e) set_pids_ ## e
+#define setDECL(e) static void setNAME(e) \
+ (struct pids_info *I, struct pids_result *R, proc_t *P)
+
+/* convert pages to kib */
+#define CVT_set(e,t,x) setDECL(e) { \
+ R->result. t = (long)(P-> x) << I -> pgs2k_shift; }
+/* strdup of a static char array */
+#define DUP_set(e,x) setDECL(e) { \
+ freNAME(str)(R); \
+ if (!(R->result.str = strdup(P-> x))) I->seterr = 1; }
+/* regular assignment copy */
+#define REG_set(e,t,x) setDECL(e) { \
+ (void)I; R->result. t = P-> x; }
+/* take ownership of a normal single string if possible, else return
+ some sort of hint that they duplicated this char * item ... */
+#define STR_set(e,x) setDECL(e) { \
+ freNAME(str)(R); \
+ if (NULL != P-> x) { R->result.str = P-> x; P-> x = NULL; } \
+ else { R->result.str = strdup("[ duplicate " STRINGIFY(e) " ]"); \
+ if (!R->result.str) I->seterr = 1; } }
+/* take ownership of true vectorized strings if possible, else return
+ some sort of hint that they duplicated this char ** item ... */
+#define VEC_set(e,x) setDECL(e) { \
+ freNAME(strv)(R); \
+ if (NULL != P-> x) { R->result.strv = P-> x; P-> x = NULL; } \
+ else { R->result.strv = vectorize_this_str("[ duplicate " STRINGIFY(e) " ]"); \
+ if (!R->result.strv) I->seterr = 1; } }
+
+
+setDECL(noop) { (void)I; (void)R; (void)P; }
+setDECL(extra) { (void)I; (void)P; R->result.ull_int = 0; }
+
+REG_set(ADDR_CODE_END, ul_int, end_code)
+REG_set(ADDR_CODE_START, ul_int, start_code)
+REG_set(ADDR_CURR_EIP, ul_int, kstk_eip)
+REG_set(ADDR_CURR_ESP, ul_int, kstk_esp)
+REG_set(ADDR_STACK_START, ul_int, start_stack)
+REG_set(AUTOGRP_ID, s_int, autogrp_id)
+REG_set(AUTOGRP_NICE, s_int, autogrp_nice)
+STR_set(CGNAME, cgname)
+STR_set(CGROUP, cgroup)
+VEC_set(CGROUP_V, cgroup_v)
+STR_set(CMD, cmd)
+STR_set(CMDLINE, cmdline)
+VEC_set(CMDLINE_V, cmdline_v)
+STR_set(ENVIRON, environ)
+VEC_set(ENVIRON_V, environ_v)
+STR_set(EXE, exe)
+REG_set(EXIT_SIGNAL, s_int, exit_signal)
+REG_set(FLAGS, ul_int, flags)
+REG_set(FLT_MAJ, ul_int, maj_flt)
+setDECL(FLT_MAJ_C) { (void)I; R->result.ul_int = P->maj_flt + P->cmaj_flt; }
+REG_set(FLT_MAJ_DELTA, s_int, maj_delta)
+REG_set(FLT_MIN, ul_int, min_flt)
+setDECL(FLT_MIN_C) { (void)I; R->result.ul_int = P->min_flt + P->cmin_flt; }
+REG_set(FLT_MIN_DELTA, s_int, min_delta)
+REG_set(ID_EGID, u_int, egid)
+REG_set(ID_EGROUP, str, egroup)
+REG_set(ID_EUID, u_int, euid)
+REG_set(ID_EUSER, str, euser)
+REG_set(ID_FGID, u_int, fgid)
+REG_set(ID_FGROUP, str, fgroup)
+REG_set(ID_FUID, u_int, fuid)
+REG_set(ID_FUSER, str, fuser)
+REG_set(ID_LOGIN, s_int, luid)
+REG_set(ID_PGRP, s_int, pgrp)
+REG_set(ID_PID, s_int, tid)
+REG_set(ID_PPID, s_int, ppid)
+REG_set(ID_RGID, u_int, rgid)
+REG_set(ID_RGROUP, str, rgroup)
+REG_set(ID_RUID, u_int, ruid)
+REG_set(ID_RUSER, str, ruser)
+REG_set(ID_SESSION, s_int, session)
+REG_set(ID_SGID, u_int, sgid)
+REG_set(ID_SGROUP, str, sgroup)
+REG_set(ID_SUID, u_int, suid)
+REG_set(ID_SUSER, str, suser)
+REG_set(ID_TGID, s_int, tgid)
+REG_set(ID_TID, s_int, tid)
+REG_set(ID_TPGID, s_int, tpgid)
+REG_set(IO_READ_BYTES, ul_int, read_bytes)
+REG_set(IO_READ_CHARS, ul_int, rchar)
+REG_set(IO_READ_OPS, ul_int, syscr)
+REG_set(IO_WRITE_BYTES, ul_int, write_bytes)
+REG_set(IO_WRITE_CBYTES, ul_int, cancelled_write_bytes)
+REG_set(IO_WRITE_CHARS, ul_int, wchar)
+REG_set(IO_WRITE_OPS, ul_int, syscw)
+REG_set(LXCNAME, str, lxcname)
+CVT_set(MEM_CODE, ul_int, trs)
+REG_set(MEM_CODE_PGS, ul_int, trs)
+CVT_set(MEM_DATA, ul_int, drs)
+REG_set(MEM_DATA_PGS, ul_int, drs)
+CVT_set(MEM_RES, ul_int, resident)
+REG_set(MEM_RES_PGS, ul_int, resident)
+CVT_set(MEM_SHR, ul_int, share)
+REG_set(MEM_SHR_PGS, ul_int, share)
+CVT_set(MEM_VIRT, ul_int, size)
+REG_set(MEM_VIRT_PGS, ul_int, size)
+REG_set(NICE, s_int, nice)
+REG_set(NLWP, s_int, nlwp)
+REG_set(NS_CGROUP, ul_int, ns.ns[0])
+REG_set(NS_IPC, ul_int, ns.ns[1])
+REG_set(NS_MNT, ul_int, ns.ns[2])
+REG_set(NS_NET, ul_int, ns.ns[3])
+REG_set(NS_PID, ul_int, ns.ns[4])
+REG_set(NS_TIME, ul_int, ns.ns[5])
+REG_set(NS_USER, ul_int, ns.ns[6])
+REG_set(NS_UTS, ul_int, ns.ns[7])
+REG_set(OOM_ADJ, s_int, oom_adj)
+REG_set(OOM_SCORE, s_int, oom_score)
+REG_set(PRIORITY, s_int, priority)
+REG_set(PRIORITY_RT, s_int, rtprio)
+REG_set(PROCESSOR, s_int, processor)
+setDECL(PROCESSOR_NODE) { (void)I; R->result.s_int = numa_node_of_cpu(P->processor); }
+REG_set(RSS, ul_int, rss)
+REG_set(RSS_RLIM, ul_int, rss_rlim)
+REG_set(SCHED_CLASS, s_int, sched)
+STR_set(SD_MACH, sd_mach)
+STR_set(SD_OUID, sd_ouid)
+STR_set(SD_SEAT, sd_seat)
+STR_set(SD_SESS, sd_sess)
+STR_set(SD_SLICE, sd_slice)
+STR_set(SD_UNIT, sd_unit)
+STR_set(SD_UUNIT, sd_uunit)
+DUP_set(SIGBLOCKED, blocked)
+DUP_set(SIGCATCH, sigcatch)
+DUP_set(SIGIGNORE, sigignore)
+DUP_set(SIGNALS, signal)
+DUP_set(SIGPENDING, _sigpnd)
+REG_set(SMAP_ANONYMOUS, ul_int, smap_Anonymous)
+REG_set(SMAP_HUGE_ANON, ul_int, smap_AnonHugePages)
+REG_set(SMAP_HUGE_FILE, ul_int, smap_FilePmdMapped)
+REG_set(SMAP_HUGE_SHMEM, ul_int, smap_ShmemPmdMapped)
+REG_set(SMAP_HUGE_TLBPRV, ul_int, smap_Private_Hugetlb)
+REG_set(SMAP_HUGE_TLBSHR, ul_int, smap_Shared_Hugetlb)
+REG_set(SMAP_LAZY_FREE, ul_int, smap_LazyFree)
+REG_set(SMAP_LOCKED, ul_int, smap_Locked)
+REG_set(SMAP_PRV_CLEAN, ul_int, smap_Private_Clean)
+REG_set(SMAP_PRV_DIRTY, ul_int, smap_Private_Dirty)
+setDECL(SMAP_PRV_TOTAL) { (void)I; R->result.ul_int = P->smap_Private_Clean + P->smap_Private_Dirty; }
+REG_set(SMAP_PSS, ul_int, smap_Pss)
+REG_set(SMAP_PSS_ANON, ul_int, smap_Pss_Anon)
+REG_set(SMAP_PSS_FILE, ul_int, smap_Pss_File)
+REG_set(SMAP_PSS_SHMEM, ul_int, smap_Pss_Shmem)
+REG_set(SMAP_REFERENCED, ul_int, smap_Referenced)
+REG_set(SMAP_RSS, ul_int, smap_Rss)
+REG_set(SMAP_SHR_CLEAN, ul_int, smap_Shared_Clean)
+REG_set(SMAP_SHR_DIRTY, ul_int, smap_Shared_Dirty)
+REG_set(SMAP_SWAP, ul_int, smap_Swap)
+REG_set(SMAP_SWAP_PSS, ul_int, smap_SwapPss)
+REG_set(STATE, s_ch, state)
+STR_set(SUPGIDS, supgid)
+STR_set(SUPGROUPS, supgrp)
+setDECL(TICS_ALL) { (void)I; R->result.ull_int = P->utime + P->stime; }
+setDECL(TICS_ALL_C) { (void)I; R->result.ull_int = P->utime + P->stime + P->cutime + P->cstime; }
+REG_set(TICS_ALL_DELTA, u_int, pcpu)
+REG_set(TICS_BEGAN, ull_int, start_time)
+REG_set(TICS_BLKIO, ull_int, blkio_tics)
+REG_set(TICS_GUEST, ull_int, gtime)
+setDECL(TICS_GUEST_C) { (void)I; R->result.ull_int = P->gtime + P->cgtime; }
+REG_set(TICS_SYSTEM, ull_int, stime)
+setDECL(TICS_SYSTEM_C) { (void)I; R->result.ull_int = P->stime + P->cstime; }
+REG_set(TICS_USER, ull_int, utime)
+setDECL(TICS_USER_C) { (void)I; R->result.ull_int = P->utime + P->cutime; }
+setDECL(TIME_ALL) { R->result.real = ((double)P->utime + P->stime) / I->hertz; }
+setDECL(TIME_ALL_C) { R->result.real = ((double)P->utime + P->stime + P->cutime + P->cstime) / I->hertz; }
+setDECL(TIME_ELAPSED) { double t = I->boot_tics - P->start_time; if (t > 0) R->result.real = t / I->hertz; }
+setDECL(TIME_START) { R->result.real = (double)P->start_time / I->hertz; }
+REG_set(TTY, s_int, tty)
+setDECL(TTY_NAME) { char buf[64]; freNAME(str)(R); dev_to_tty(buf, sizeof(buf), P->tty, P->tid, ABBREV_DEV); if (!(R->result.str = strdup(buf))) I->seterr = 1; }
+setDECL(TTY_NUMBER) { char buf[64]; freNAME(str)(R); dev_to_tty(buf, sizeof(buf), P->tty, P->tid, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS); if (!(R->result.str = strdup(buf))) I->seterr = 1; }
+setDECL(UTILIZATION) { double t = I->boot_tics - P->start_time; if (t > 0) R->result.real = ((P->utime + P->stime) * 100.0f) / t; }
+setDECL(UTILIZATION_C) { double t = I->boot_tics - P->start_time; if (t > 0) R->result.real = ((P->utime + P->stime + P->cutime + P->cstime) * 100.0f) / t; }
+REG_set(VM_DATA, ul_int, vm_data)
+REG_set(VM_EXE, ul_int, vm_exe)
+REG_set(VM_LIB, ul_int, vm_lib)
+REG_set(VM_RSS, ul_int, vm_rss)
+REG_set(VM_RSS_ANON, ul_int, vm_rss_anon)
+REG_set(VM_RSS_FILE, ul_int, vm_rss_file)
+REG_set(VM_RSS_LOCKED, ul_int, vm_lock)
+REG_set(VM_RSS_SHARED, ul_int, vm_rss_shared)
+REG_set(VM_SIZE, ul_int, vm_size)
+REG_set(VM_STACK, ul_int, vm_stack)
+REG_set(VM_SWAP, ul_int, vm_swap)
+setDECL(VM_USED) { (void)I; R->result.ul_int = P->vm_swap + P->vm_rss; }
+REG_set(VSIZE_BYTES, ul_int, vsize)
+setDECL(WCHAN_NAME) { freNAME(str)(R); if (!(R->result.str = strdup(lookup_wchan(P->tid)))) I->seterr = 1;; }
+
+#undef setDECL
+#undef CVT_set
+#undef DUP_set
+#undef REG_set
+#undef STR_set
+#undef VEC_set
+
+
+// ___ Sorting Support ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+struct sort_parms {
+ int offset;
+ enum pids_sort_order order;
+};
+
+#define srtNAME(t) sort_pids_ ## t
+#define srtDECL(t) static int srtNAME(t) \
+ (const struct pids_stack **A, const struct pids_stack **B, struct sort_parms *P)
+
+#define NUM_srt(T) srtDECL(T) { \
+ const struct pids_result *a = (*A)->head + P->offset; \
+ const struct pids_result *b = (*B)->head + P->offset; \
+ return P->order * (a->result. T - b->result. T); }
+
+#define REG_srt(T) srtDECL(T) { \
+ const struct pids_result *a = (*A)->head + P->offset; \
+ const struct pids_result *b = (*B)->head + P->offset; \
+ if ( a->result. T > b->result. T ) return P->order > 0 ? 1 : -1; \
+ if ( a->result. T < b->result. T ) return P->order > 0 ? -1 : 1; \
+ return 0; }
+
+NUM_srt(s_ch)
+NUM_srt(s_int)
+
+REG_srt(u_int)
+REG_srt(ul_int)
+REG_srt(ull_int)
+
+REG_srt(real)
+
+srtDECL(str) {
+ const struct pids_result *a = (*A)->head + P->offset;
+ const struct pids_result *b = (*B)->head + P->offset;
+ return P->order * strcoll(a->result.str, b->result.str);
+}
+
+srtDECL(strv) {
+ const struct pids_result *a = (*A)->head + P->offset;
+ const struct pids_result *b = (*B)->head + P->offset;
+ if (!a->result.strv || !b->result.strv) return 0;
+ return P->order * strcoll((*a->result.strv), (*b->result.strv));
+}
+
+srtDECL(strvers) {
+ const struct pids_result *a = (*A)->head + P->offset;
+ const struct pids_result *b = (*B)->head + P->offset;
+ return P->order * strverscmp(a->result.str, b->result.str);
+}
+
+srtDECL(noop) {
+ (void)A; (void)B; (void)P;
+ return 0;
+}
+
+#undef srtDECL
+#undef NUM_srt
+#undef REG_srt
+
+
+// ___ Controlling Table ||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+#define f_either PROC_SPARE_1 // either status or stat (favor stat)
+#define f_exe PROC_FILL_EXE
+#define f_grp PROC_FILLGRP
+#define f_io PROC_FILLIO
+#define f_login PROC_FILL_LUID
+#define f_lxc PROC_FILL_LXC
+#define f_ns PROC_FILLNS
+#define f_oom PROC_FILLOOM
+#define f_smaps PROC_FILLSMAPS
+#define f_stat PROC_FILLSTAT
+#define f_statm PROC_FILLMEM
+#define f_status PROC_FILLSTATUS
+#define f_systemd PROC_FILLSYSTEMD
+#define f_usr PROC_FILLUSR
+ // these next three will yield true verctorized strings
+#define v_arg PROC_FILLARG
+#define v_cgroup PROC_FILLCGROUP
+#define v_env PROC_FILLENV
+ // these next three will yield a single string (never vectorized)
+#define x_cgroup PROC_EDITCGRPCVT
+#define x_cmdline PROC_EDITCMDLCVT
+#define x_environ PROC_EDITENVRCVT
+ // these next three will also force PROC_FILLSTATUS
+#define x_ogroup PROC_FILL_OGROUPS
+#define x_ouser PROC_FILL_OUSERS
+#define x_supgrp PROC_FILL_SUPGRP
+ // placed here so an 'f' prefix wouldn't make 'em first
+#define z_autogrp PROC_FILLAUTOGRP
+
+typedef void (*FRE_t)(struct pids_result *);
+typedef int (*QSR_t)(const void *, const void *, void *);
+
+#ifdef ITEMTABLE_DEBUG
+#define RS(e) (SET_t)setNAME(e), PIDS_ ## e, STRINGIFY(PIDS_ ## e)
+#else
+#define RS(e) (SET_t)setNAME(e)
+#endif
+#define FF(t) (FRE_t)freNAME(t)
+#define QS(t) (QSR_t)srtNAME(t)
+#define TS(t) STRINGIFY(t)
+#define TS_noop ""
+
+ /*
+ * Need it be said?
+ * This table must be kept in the exact same order as
+ * those 'enum pids_item' guys ! */
+static struct {
+ SET_t setsfunc; // the actual result setting routine
+#ifdef ITEMTABLE_DEBUG
+ int enumnumb; // enumerator (must match position!)
+ char *enum2str; // enumerator name as a char* string
+#endif
+ unsigned oldflags; // PROC_FILLxxxx flags for this item
+ FRE_t freefunc; // free function for strings storage
+ QSR_t sortfunc; // sort cmp func for a specific type
+ int needhist; // a result requires history support
+ char *type2str; // the result type as a string value
+} Item_table[] = {
+/* setsfunc oldflags freefunc sortfunc needhist type2str
+ --------------------- ---------- --------- ------------- -------- ----------- */
+ { RS(noop), 0, NULL, QS(noop), 0, TS_noop }, // user only, never altered
+ { RS(extra), 0, NULL, QS(ull_int), 0, TS_noop }, // user only, reset to zero
+
+ { RS(ADDR_CODE_END), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(ADDR_CODE_START), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(ADDR_CURR_EIP), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(ADDR_CURR_ESP), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(ADDR_STACK_START), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(AUTOGRP_ID), z_autogrp, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(AUTOGRP_NICE), z_autogrp, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(CGNAME), x_cgroup, FF(str), QS(str), 0, TS(str) },
+ { RS(CGROUP), x_cgroup, FF(str), QS(str), 0, TS(str) },
+ { RS(CGROUP_V), v_cgroup, FF(strv), QS(strv), 0, TS(strv) },
+ { RS(CMD), f_either, FF(str), QS(str), 0, TS(str) },
+ { RS(CMDLINE), x_cmdline, FF(str), QS(str), 0, TS(str) },
+ { RS(CMDLINE_V), v_arg, FF(strv), QS(strv), 0, TS(strv) },
+ { RS(ENVIRON), x_environ, FF(str), QS(str), 0, TS(str) },
+ { RS(ENVIRON_V), v_env, FF(strv), QS(strv), 0, TS(strv) },
+ { RS(EXE), f_exe, FF(str), QS(str), 0, TS(str) },
+ { RS(EXIT_SIGNAL), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(FLAGS), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(FLT_MAJ), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(FLT_MAJ_C), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(FLT_MAJ_DELTA), f_stat, NULL, QS(s_int), +1, TS(s_int) },
+ { RS(FLT_MIN), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(FLT_MIN_C), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(FLT_MIN_DELTA), f_stat, NULL, QS(s_int), +1, TS(s_int) },
+ { RS(ID_EGID), 0, NULL, QS(u_int), 0, TS(u_int) }, // oldflags: free w/ simple_read
+ { RS(ID_EGROUP), f_grp, NULL, QS(str), 0, TS(str) },
+ { RS(ID_EUID), 0, NULL, QS(u_int), 0, TS(u_int) }, // oldflags: free w/ simple_read
+ { RS(ID_EUSER), f_usr, NULL, QS(str), 0, TS(str) }, // freefunc NULL w/ cached string
+ { RS(ID_FGID), f_status, NULL, QS(u_int), 0, TS(u_int) },
+ { RS(ID_FGROUP), x_ogroup, NULL, QS(str), 0, TS(str) },
+ { RS(ID_FUID), f_status, NULL, QS(u_int), 0, TS(u_int) },
+ { RS(ID_FUSER), x_ouser, NULL, QS(str), 0, TS(str) }, // freefunc NULL w/ cached string
+ { RS(ID_LOGIN), f_login, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(ID_PGRP), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(ID_PID), 0, NULL, QS(s_int), 0, TS(s_int) }, // oldflags: free w/ simple_nextpid
+ { RS(ID_PPID), f_either, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(ID_RGID), f_status, NULL, QS(u_int), 0, TS(u_int) },
+ { RS(ID_RGROUP), x_ogroup, NULL, QS(str), 0, TS(str) },
+ { RS(ID_RUID), f_status, NULL, QS(u_int), 0, TS(u_int) },
+ { RS(ID_RUSER), x_ouser, NULL, QS(str), 0, TS(str) }, // freefunc NULL w/ cached string
+ { RS(ID_SESSION), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(ID_SGID), f_status, NULL, QS(u_int), 0, TS(u_int) },
+ { RS(ID_SGROUP), x_ogroup, NULL, QS(str), 0, TS(str) },
+ { RS(ID_SUID), f_status, NULL, QS(u_int), 0, TS(u_int) },
+ { RS(ID_SUSER), x_ouser, NULL, QS(str), 0, TS(str) }, // freefunc NULL w/ cached string
+ { RS(ID_TGID), 0, NULL, QS(s_int), 0, TS(s_int) }, // oldflags: free w/ simple_nextpid
+ { RS(ID_TID), 0, NULL, QS(s_int), 0, TS(s_int) }, // oldflags: free w/ simple_nexttid
+ { RS(ID_TPGID), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(IO_READ_BYTES), f_io, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(IO_READ_CHARS), f_io, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(IO_READ_OPS), f_io, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(IO_WRITE_BYTES), f_io, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(IO_WRITE_CBYTES), f_io, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(IO_WRITE_CHARS), f_io, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(IO_WRITE_OPS), f_io, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(LXCNAME), f_lxc, NULL, QS(str), 0, TS(str) }, // freefunc NULL w/ cached string
+ { RS(MEM_CODE), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_CODE_PGS), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_DATA), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_DATA_PGS), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_RES), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_RES_PGS), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_SHR), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_SHR_PGS), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_VIRT), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(MEM_VIRT_PGS), f_statm, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NICE), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(NLWP), f_either, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(NS_CGROUP), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NS_IPC), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NS_MNT), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NS_NET), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NS_PID), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NS_TIME), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NS_USER), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(NS_UTS), f_ns, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(OOM_ADJ), f_oom, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(OOM_SCORE), f_oom, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(PRIORITY), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(PRIORITY_RT), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(PROCESSOR), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(PROCESSOR_NODE), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(RSS), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(RSS_RLIM), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SCHED_CLASS), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(SD_MACH), f_systemd, FF(str), QS(str), 0, TS(str) },
+ { RS(SD_OUID), f_systemd, FF(str), QS(str), 0, TS(str) },
+ { RS(SD_SEAT), f_systemd, FF(str), QS(str), 0, TS(str) },
+ { RS(SD_SESS), f_systemd, FF(str), QS(str), 0, TS(str) },
+ { RS(SD_SLICE), f_systemd, FF(str), QS(str), 0, TS(str) },
+ { RS(SD_UNIT), f_systemd, FF(str), QS(str), 0, TS(str) },
+ { RS(SD_UUNIT), f_systemd, FF(str), QS(str), 0, TS(str) },
+ { RS(SIGBLOCKED), f_status, FF(str), QS(str), 0, TS(str) },
+ { RS(SIGCATCH), f_status, FF(str), QS(str), 0, TS(str) },
+ { RS(SIGIGNORE), f_status, FF(str), QS(str), 0, TS(str) },
+ { RS(SIGNALS), f_status, FF(str), QS(str), 0, TS(str) },
+ { RS(SIGPENDING), f_status, FF(str), QS(str), 0, TS(str) },
+ { RS(SMAP_ANONYMOUS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_HUGE_ANON), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_HUGE_FILE), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_HUGE_SHMEM), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_HUGE_TLBPRV), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_HUGE_TLBSHR), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_LAZY_FREE), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_LOCKED), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_PRV_CLEAN), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_PRV_DIRTY), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_PRV_TOTAL), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_PSS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_PSS_ANON), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_PSS_FILE), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_PSS_SHMEM), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_REFERENCED), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_RSS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_SHR_CLEAN), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_SHR_DIRTY), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_SWAP), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(SMAP_SWAP_PSS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(STATE), f_either, NULL, QS(s_ch), 0, TS(s_ch) },
+ { RS(SUPGIDS), f_status, FF(str), QS(str), 0, TS(str) },
+ { RS(SUPGROUPS), x_supgrp, FF(str), QS(str), 0, TS(str) },
+ { RS(TICS_ALL), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_ALL_C), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_ALL_DELTA), f_stat, NULL, QS(u_int), +1, TS(u_int) },
+ { RS(TICS_BEGAN), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_BLKIO), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_GUEST), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_GUEST_C), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_SYSTEM), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_SYSTEM_C), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_USER), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TICS_USER_C), f_stat, NULL, QS(ull_int), 0, TS(ull_int) },
+ { RS(TIME_ALL), f_stat, NULL, QS(real), 0, TS(real) },
+ { RS(TIME_ALL_C), f_stat, NULL, QS(real), 0, TS(real) },
+ { RS(TIME_ELAPSED), f_stat, NULL, QS(real), 0, TS(real) },
+ { RS(TIME_START), f_stat, NULL, QS(real), 0, TS(real) },
+ { RS(TTY), f_stat, NULL, QS(s_int), 0, TS(s_int) },
+ { RS(TTY_NAME), f_stat, FF(str), QS(strvers), 0, TS(str) },
+ { RS(TTY_NUMBER), f_stat, FF(str), QS(strvers), 0, TS(str) },
+ { RS(UTILIZATION), f_stat, NULL, QS(real), 0, TS(real) },
+ { RS(UTILIZATION_C), f_stat, NULL, QS(real), 0, TS(real) },
+ { RS(VM_DATA), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_EXE), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_LIB), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_RSS), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_RSS_ANON), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_RSS_FILE), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_RSS_LOCKED), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_RSS_SHARED), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_SIZE), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_STACK), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_SWAP), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VM_USED), f_status, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(VSIZE_BYTES), f_stat, NULL, QS(ul_int), 0, TS(ul_int) },
+ { RS(WCHAN_NAME), 0, FF(str), QS(str), 0, TS(str) }, // oldflags: tid already free
+};
+
+ /* please note,
+ * this enum MUST be 1 greater than the highest value of any enum */
+enum pids_item PIDS_logical_end = MAXTABLE(Item_table);
+
+#undef setNAME
+#undef freNAME
+#undef srtNAME
+#undef RS
+#undef FF
+#undef QS
+
+//#undef f_either // needed later
+#undef f_exe
+#undef f_grp
+#undef f_io
+#undef f_login
+#undef f_lxc
+#undef f_ns
+#undef f_oom
+#undef f_smaps
+//#undef f_stat // needed later
+#undef f_statm
+//#undef f_status // needed later
+#undef f_systemd
+#undef f_usr
+#undef v_arg
+#undef v_cgroup
+#undef v_env
+#undef x_cgroup
+#undef x_cmdline
+#undef x_environ
+#undef x_ogroup
+#undef x_ouser
+#undef x_supgrp
+#undef z_autogrp
+
+
+// ___ History Support Private Functions ||||||||||||||||||||||||||||||||||||||
+// ( stolen from top when he wasn't looking ) -------------------------------
+
+#define HHASH_SIZE 4096
+#define _HASH_PID_(K) (K & (HHASH_SIZE - 1))
+
+#define Hr(x) info->hist->x // 'hist ref', minimize stolen impact
+
+typedef unsigned long long TIC_t;
+
+typedef struct HST_t {
+ TIC_t tics; // last frame's tics count
+ unsigned long maj, min; // last frame's maj/min_flt counts
+ int pid; // record 'key'
+ int lnk; // next on hash chain
+} HST_t;
+
+
+struct history_info {
+ int num_tasks; // used as index (tasks tallied)
+ int HHist_siz; // max number of HST_t structs
+ HST_t *PHist_sav; // alternating 'old/new' HST_t anchors
+ HST_t *PHist_new;
+ int HHash_one [HHASH_SIZE]; // the actual hash tables
+ int HHash_two [HHASH_SIZE]; // (accessed via PHash_sav/PHash_new)
+ int HHash_nul [HHASH_SIZE]; // an 'empty' hash table image
+ int *PHash_sav; // alternating 'old/new' hash tables
+ int *PHash_new; // (aka. the 'one/two' actual tables)
+};
+
+
+static void pids_config_history (
+ struct pids_info *info)
+{
+ int i;
+
+ for (i = 0; i < HHASH_SIZE; i++) // make the 'empty' table image
+ Hr(HHash_nul[i]) = -1;
+ memcpy(Hr(HHash_one), Hr(HHash_nul), sizeof(Hr(HHash_nul)));
+ memcpy(Hr(HHash_two), Hr(HHash_nul), sizeof(Hr(HHash_nul)));
+ Hr(PHash_sav) = Hr(HHash_one); // alternating 'old/new' hash tables
+ Hr(PHash_new) = Hr(HHash_two);
+} // end: pids_config_history
+
+
+static inline HST_t *pids_histget (
+ struct pids_info *info,
+ int pid)
+{
+ int V = Hr(PHash_sav[_HASH_PID_(pid)]);
+
+ while (-1 < V) {
+ if (Hr(PHist_sav[V].pid) == pid)
+ return &Hr(PHist_sav[V]);
+ V = Hr(PHist_sav[V].lnk);
+ }
+ return NULL;
+} // end: pids_histget
+
+
+static inline void pids_histput (
+ struct pids_info *info,
+ unsigned this)
+{
+ int V = _HASH_PID_(Hr(PHist_new[this].pid));
+
+ Hr(PHist_new[this].lnk) = Hr(PHash_new[V]);
+ Hr(PHash_new[V] = this);
+} // end: pids_histput
+
+#undef _HASH_PID_
+
+
+static inline int pids_make_hist (
+ struct pids_info *info,
+ proc_t *p)
+{
+ TIC_t tics;
+ HST_t *h;
+ int slot = info->hist->num_tasks;
+
+ if (slot + 1 >= Hr(HHist_siz)) {
+ Hr(HHist_siz) += NEWOLD_GROW;
+ Hr(PHist_sav) = realloc(Hr(PHist_sav), sizeof(HST_t) * Hr(HHist_siz));
+ Hr(PHist_new) = realloc(Hr(PHist_new), sizeof(HST_t) * Hr(HHist_siz));
+ if (!Hr(PHist_sav) || !Hr(PHist_new))
+ return 0;
+ }
+ Hr(PHist_new[slot].pid) = p->tid;
+ Hr(PHist_new[slot].maj) = p->maj_flt;
+ Hr(PHist_new[slot].min) = p->min_flt;
+ Hr(PHist_new[slot].tics) = tics = (p->utime + p->stime);
+
+ pids_histput(info, slot);
+
+ if ((h = pids_histget(info, p->tid))) {
+ tics -= h->tics;
+ p->maj_delta = p->maj_flt - h->maj;
+ p->min_delta = p->min_flt - h->min;
+ }
+ /* here we're saving elapsed tics, which will include any
+ tasks not previously seen via that pids_histget() guy! */
+ p->pcpu = tics;
+
+ info->hist->num_tasks++;
+ return 1;
+} // end: pids_make_hist
+
+
+static inline void pids_toggle_history (
+ struct pids_info *info)
+{
+ void *v;
+
+ v = Hr(PHist_sav);
+ Hr(PHist_sav) = Hr(PHist_new);
+ Hr(PHist_new) = v;
+
+ v = Hr(PHash_sav);
+ Hr(PHash_sav) = Hr(PHash_new);
+ Hr(PHash_new) = v;
+ memcpy(Hr(PHash_new), Hr(HHash_nul), sizeof(Hr(HHash_nul)));
+
+ info->hist->num_tasks = 0;
+} // end: pids_toggle_history
+
+
+#ifdef UNREF_RPTHASH
+static void pids_unref_rpthash (
+ struct pids_info *info)
+{
+ int i, j, pop, total_occupied, maxdepth, maxdepth_sav, numdepth
+ , cross_foot, sz = HHASH_SIZE * (int)sizeof(int)
+ , hsz = (int)sizeof(HST_t) * Hr(HHist_siz);
+ int depths[HHASH_SIZE];
+
+ for (i = 0, total_occupied = 0, maxdepth = 0; i < HHASH_SIZE; i++) {
+ int V = Hr(PHash_new[i]);
+ j = 0;
+ if (-1 < V) {
+ ++total_occupied;
+ while (-1 < V) {
+ V = Hr(PHist_new[V].lnk);
+ if (-1 < V) j++;
+ }
+ }
+ depths[i] = j;
+ if (maxdepth < j) maxdepth = j;
+ }
+ maxdepth_sav = maxdepth;
+
+ fprintf(stderr,
+ "\n History Memory Costs:"
+ "\n\tHST_t size = %d, total allocated = %d,"
+ "\n\tthus PHist_new & PHist_sav consumed %dk (%d) total bytes."
+ "\n"
+ "\n\tTwo hash tables provide for %d entries each + 1 extra 'empty' image,"
+ "\n\tthus %dk (%d) bytes per table for %dk (%d) total bytes."
+ "\n"
+ "\n\tGrand total = %dk (%d) bytes."
+ "\n"
+ "\n Hash Results Report:"
+ "\n\tTotal hashed = %d"
+ "\n\tLevel-0 hash entries = %d (%d%% occupied)"
+ "\n\tMax Depth = %d"
+ "\n\n"
+ , (int)sizeof(HST_t), Hr(HHist_siz)
+ , hsz / 1024, hsz
+ , HHASH_SIZE
+ , sz / 1024, sz, (sz * 3) / 1024, sz * 3
+ , (hsz + (sz * 3)) / 1024, hsz + (sz * 3)
+ , info->hist->num_tasks
+ , total_occupied, (total_occupied * 100) / HHASH_SIZE
+ , maxdepth);
+
+ if (total_occupied) {
+ for (pop = total_occupied, cross_foot = 0; maxdepth; maxdepth--) {
+ for (i = 0, numdepth = 0; i < HHASH_SIZE; i++)
+ if (depths[i] == maxdepth) ++numdepth;
+ if (numdepth) fprintf(stderr,
+ "\t %5d (%3d%%) hash table entries at depth %d\n"
+ , numdepth, (numdepth * 100) / total_occupied, maxdepth);
+ pop -= numdepth;
+ cross_foot += numdepth;
+ if (0 == pop && cross_foot == total_occupied) break;
+ }
+ if (pop) {
+ fprintf(stderr, "\t %5d (%3d%%) unchained entries (at depth 0)\n"
+ , pop, (pop * 100) / total_occupied);
+ cross_foot += pop;
+ }
+ fprintf(stderr,
+ "\t -----\n"
+ "\t %5d total entries occupied\n", cross_foot);
+
+ if (maxdepth_sav > 1) {
+ fprintf(stderr, "\n PIDs at max depth: ");
+ for (i = 0; i < HHASH_SIZE; i++)
+ if (depths[i] == maxdepth_sav) {
+ j = Hr(PHash_new[i]);
+ fprintf(stderr, "\n\tpos %4d: %05d", i, Hr(PHist_new[j].pid));
+ while (-1 < j) {
+ j = Hr(PHist_new[j].lnk);
+ if (-1 < j) fprintf(stderr, ", %05d", Hr(PHist_new[j].pid));
+ }
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+} // end: pids_unref_rpthash
+#endif // UNREF_RPTHASH
+
+#undef Hr
+#undef HHASH_SIZE
+
+
+// ___ Standard Private Functions |||||||||||||||||||||||||||||||||||||||||||||
+
+static inline int pids_assign_results (
+ struct pids_info *info,
+ struct pids_stack *stack,
+ proc_t *p)
+{
+ struct pids_result *this = stack->head;
+ SET_t *that = &info->func_array[0];
+
+ info->seterr = 0;
+ while (*that) {
+ (*that)(info, this, p);
+ ++this;
+ ++that;
+ }
+ return !info->seterr;
+} // end: pids_assign_results
+
+
+static inline void pids_cleanup_stack (
+ struct pids_result *this)
+{
+ for (;;) {
+ enum pids_item item = this->item;
+ if (item >= PIDS_logical_end)
+ break;
+ if (Item_table[item].freefunc)
+ Item_table[item].freefunc(this);
+ this->result.ull_int = 0;
+ ++this;
+ }
+} // end: pids_cleanup_stack
+
+
+static inline void pids_cleanup_stacks_all (
+ struct pids_info *info)
+{
+ struct stacks_extent *ext = info->extents;
+ int i;
+
+ while (ext) {
+ for (i = 0; ext->stacks[i]; i++)
+ pids_cleanup_stack(ext->stacks[i]->head);
+ ext = ext->next;
+ };
+} // end: pids_cleanup_stacks_all
+
+
+#if 0 // not currently needed after 'fatal_proc_unmounted' was refactored
+ /*
+ * This routine exists in case we ever want to offer something like
+ * 'static' or 'invarient' results stacks. By unsplicing an extent
+ * from the info anchor it will be isolated from future reset/free. */
+static struct stacks_extent *pids_extent_cut (
+ struct pids_info *info,
+ struct stacks_extent *ext)
+{
+ struct stacks_extent *p = info->extents;
+
+ if (ext) {
+ if (ext == p) {
+ info->extents = p->next;
+ return ext;
+ }
+ do {
+ if (ext == p->next) {
+ p->next = p->next->next;
+ return ext;
+ }
+ p = p->next;
+ } while (p);
+ }
+ return NULL;
+} // end: pids_extent_cut
+#endif // ----------------------------------------------------------------
+
+
+static inline struct pids_result *pids_itemize_stack (
+ struct pids_result *p,
+ int depth,
+ enum pids_item *items)
+{
+ struct pids_result *p_sav = p;
+ int i;
+
+ for (i = 0; i < depth; i++) {
+ p->item = items[i];
+ ++p;
+ }
+ return p_sav;
+} // end: pids_itemize_stack
+
+
+static void pids_itemize_stacks_all (
+ struct pids_info *info)
+{
+ struct stacks_extent *ext = info->extents;
+
+ while (ext) {
+ int i;
+ for (i = 0; ext->stacks[i]; i++)
+ pids_itemize_stack(ext->stacks[i]->head, info->maxitems, info->items);
+ ext = ext->next;
+ };
+} // end: pids_itemize_stacks_all
+
+
+static inline int pids_items_check_failed (
+ enum pids_item *items,
+ int numitems)
+{
+ int i;
+
+ /* if an enum is passed instead of an address of one or more enums, ol' gcc
+ * will silently convert it to an address (possibly NULL). only clang will
+ * offer any sort of warning like the following:
+ *
+ * warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'enum pids_item *'
+ * if (procps_pids_new(&info, PIDS_noop, 3) < 0)
+ * ^~~~~~~~~~~~~~~~
+ */
+ if (numitems < 1
+ || (void *)items < (void *)0x8000) // twice as big as our largest enum
+ return 1;
+
+ for (i = 0; i < numitems; i++) {
+ // a pids_item is currently unsigned, but we'll protect our future
+ if (items[i] < 0)
+ return 1;
+ if (items[i] >= PIDS_logical_end) {
+ return 1;
+ }
+ }
+ return 0;
+} // end: pids_items_check_failed
+
+
+static inline void pids_libflags_set (
+ struct pids_info *info)
+{
+ enum pids_item e;
+ int i;
+
+ info->oldflags = info->history_yes = 0;
+ for (i = 0; i < info->maxitems; i++) {
+ if (((e = info->items[i])) >= PIDS_logical_end)
+ break;
+ info->oldflags |= Item_table[e].oldflags;
+ info->history_yes |= Item_table[e].needhist;
+ }
+ if (info->oldflags & f_either) {
+ if (!(info->oldflags & (f_stat | f_status)))
+ info->oldflags |= f_stat;
+ }
+ return;
+} // end: pids_libflags_set
+
+
+static inline void pids_oldproc_close (
+ PROCTAB **this)
+{
+ if (*this != NULL) {
+ int errsav = errno;
+ closeproc(*this);
+ *this = NULL;
+ errno = errsav;
+ }
+} // end: pids_oldproc_close
+
+
+static inline int pids_oldproc_open (
+ PROCTAB **this,
+ unsigned flags,
+ ...)
+{
+ va_list vl;
+ int *ids;
+ int num = 0;
+
+ if (*this == NULL) {
+ va_start(vl, flags);
+ ids = va_arg(vl, int*);
+ if (flags & PROC_UID) num = va_arg(vl, int);
+ va_end(vl);
+ if (NULL == (*this = openproc(flags, ids, num)))
+ return 0;
+ }
+ return 1;
+} // end: pids_oldproc_open
+
+
+static int pids_prep_func_array (
+ struct pids_info *info)
+{
+ int i;
+
+ if (!(info->func_array = realloc(info->func_array, sizeof(SET_t) * info->maxitems)))
+ return 0;
+ for (i = 0; i < info->maxitems -1; i++)
+ info->func_array[i] = Item_table[info->items[i]].setsfunc;
+ info->func_array[i] = NULL;
+ return 1;
+} // end: pids_prep_func_array
+
+
+static inline int pids_proc_tally (
+ struct pids_info *info,
+ struct pids_counts *counts,
+ proc_t *p)
+{
+ switch (p->state) {
+ case 'R':
+ ++counts->running;
+ break;
+ case 'D': // 'D' (disk sleep)
+ case 'S':
+ ++counts->sleeping;
+ break;
+ case 't': // 't' (tracing stop)
+ case 'T':
+ ++counts->stopped;
+ break;
+ case 'Z':
+ ++counts->zombied;
+ break;
+ default:
+ /* currently: 'I' (idle),
+ 'P' (parked),
+ 'X' (dead - actually 'dying' & probably never seen)
+ */
+ ++counts->other;
+ break;
+ }
+ ++counts->total;
+
+ if (info->history_yes)
+ return pids_make_hist(info, p);
+ return 1;
+} // end: pids_proc_tally
+
+
+/*
+ * pids_stacks_alloc():
+ *
+ * Allocate and initialize one or more stacks each of which is anchored in an
+ * associated context structure.
+ *
+ * All such stacks will will have their result structures properly primed with
+ * 'items', while the result itself will be zeroed.
+ *
+ * Returns an array of pointers representing the 'heads' of each new stack.
+ */
+static struct stacks_extent *pids_stacks_alloc (
+ struct pids_info *info,
+ int maxstacks)
+{
+ struct stacks_extent *p_blob;
+ struct pids_stack **p_vect;
+ struct pids_stack *p_head;
+ size_t vect_size, head_size, list_size, blob_size;
+ void *v_head, *v_list;
+ int i;
+
+ vect_size = sizeof(void *) * maxstacks; // size of the addr vectors |
+ vect_size += sizeof(void *); // plus NULL addr delimiter |
+ head_size = sizeof(struct pids_stack); // size of that head struct |
+ list_size = sizeof(struct pids_result) * info->maxitems; // any single results stack |
+ blob_size = sizeof(struct stacks_extent); // the extent anchor itself |
+ blob_size += vect_size; // plus room for addr vects |
+ blob_size += head_size * maxstacks; // plus room for head thing |
+ blob_size += list_size * maxstacks; // plus room for our stacks |
+
+ /* note: all of our memory is allocated in a single blob, facilitating a later free(). |
+ as a minimum, it is important that the result structures themselves always be |
+ contiguous for every stack since they are accessed through relative position. | */
+ if (NULL == (p_blob = calloc(1, blob_size)))
+ return NULL;
+
+ p_blob->next = info->extents; // push this extent onto... |
+ info->extents = p_blob; // ...some existing extents |
+ p_vect = (void *)p_blob + sizeof(struct stacks_extent); // prime our vector pointer |
+ p_blob->stacks = p_vect; // set actual vectors start |
+ v_head = (void *)p_vect + vect_size; // prime head pointer start |
+ v_list = v_head + (head_size * maxstacks); // prime our stacks pointer |
+
+ for (i = 0; i < maxstacks; i++) {
+ p_head = (struct pids_stack *)v_head;
+ p_head->head = pids_itemize_stack((struct pids_result *)v_list, info->maxitems, info->items);
+ p_blob->stacks[i] = p_head;
+ v_list += list_size;
+ v_head += head_size;
+ }
+ p_blob->ext_numstacks = maxstacks;
+ return p_blob;
+} // end: pids_stacks_alloc
+
+
+static int pids_stacks_fetch (
+ struct pids_info *info)
+{
+ #define n_alloc info->fetch.n_alloc
+ #define n_inuse info->fetch.n_inuse
+ #define n_saved info->fetch.n_alloc_save
+ struct stacks_extent *ext;
+
+ // initialize stuff -----------------------------------
+ if (!info->fetch.anchor) {
+ if (!(info->fetch.anchor = calloc(STACKS_INIT, sizeof(void *))))
+ return -1;
+ if (!(ext = pids_stacks_alloc(info, STACKS_INIT)))
+ return -1; // here, errno was set to ENOMEM
+ memcpy(info->fetch.anchor, ext->stacks, sizeof(void *) * STACKS_INIT);
+ n_alloc = STACKS_INIT;
+ }
+ pids_toggle_history(info);
+ memset(&info->fetch.counts, 0, sizeof(struct pids_counts));
+
+ // iterate stuff --------------------------------------
+ n_inuse = 0;
+ while (info->read_something(info->fetch_PT, &info->fetch_proc)) {
+ if (!(n_inuse < n_alloc)) {
+ n_alloc += STACKS_GROW;
+ if (!(info->fetch.anchor = realloc(info->fetch.anchor, sizeof(void *) * n_alloc))
+ || (!(ext = pids_stacks_alloc(info, STACKS_GROW))))
+ return -1; // here, errno was set to ENOMEM
+ memcpy(info->fetch.anchor + n_inuse, ext->stacks, sizeof(void *) * STACKS_GROW);
+ }
+ if (!pids_proc_tally(info, &info->fetch.counts, &info->fetch_proc))
+ return -1; // here, errno was set to ENOMEM
+ if (!pids_assign_results(info, info->fetch.anchor[n_inuse++], &info->fetch_proc))
+ return -1; // here, errno was set to ENOMEM
+ }
+ /* while the possibility is extremely remote, the readproc.c (read_something) |
+ simple_readproc and simple_readtask guys could have encountered this error |
+ in which case they would have returned a NULL, thus ending our while loop. | */
+ if (errno == ENOMEM)
+ return -1;
+
+ // finalize stuff -------------------------------------
+ /* note: we go to this trouble of maintaining a duplicate of the consolidated |
+ extent stacks addresses represented as our 'anchor' since these ptrs |
+ are exposed to a user (um, not that we don't trust 'em or anything). |
+ plus, we can NULL delimit these ptrs which we couldn't do otherwise. | */
+ if (n_saved < n_inuse + 1) {
+ n_saved = n_inuse + 1;
+ if (!(info->fetch.results.stacks = realloc(info->fetch.results.stacks, sizeof(void *) * n_saved)))
+ return -1;
+ }
+ memcpy(info->fetch.results.stacks, info->fetch.anchor, sizeof(void *) * n_inuse);
+ info->fetch.results.stacks[n_inuse] = NULL;
+
+ return n_inuse; // callers beware, this might be zero !
+ #undef n_alloc
+ #undef n_inuse
+ #undef n_saved
+} // end: pids_stacks_fetch
+
+
+// ___ Public Functions |||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+// --- standard required functions --------------------------------------------
+
+/*
+ * procps_pids_new():
+ *
+ * @info: location of returned new structure
+ *
+ * Returns: < 0 on failure, 0 on success along with
+ * a pointer to a new context struct
+ */
+PROCPS_EXPORT int procps_pids_new (
+ struct pids_info **info,
+ enum pids_item *items,
+ int numitems)
+{
+ struct pids_info *p;
+ int pgsz;
+
+#ifdef ITEMTABLE_DEBUG
+ int i, failed = 0;
+ for (i = 0; i < MAXTABLE(Item_table); i++) {
+ if (i != Item_table[i].enumnumb) {
+ fprintf(stderr, "%s: enum/table error: Item_table[%d] was %s, but its value is %d\n"
+ , __FILE__, i, Item_table[i].enum2str, Item_table[i].enumnumb);
+ failed = 1;
+ }
+ }
+ if (PIDS_SELECT_PID != PROC_PID) {
+ fprintf(stderr, "%s: header error: PIDS_SELECT_PID = 0x%04x, PROC_PID = 0x%04x\n"
+ , __FILE__, PIDS_SELECT_PID, PROC_PID);
+ failed = 1;
+ }
+ if (PIDS_SELECT_PID_THREADS != PIDS_SELECT_PID + 1) {
+ fprintf(stderr, "%s: header error: PIDS_SELECT_PID_THREADS = 0x%04x, should be 0x%04x\n"
+ , __FILE__, PIDS_SELECT_PID_THREADS, PIDS_SELECT_PID + 1);
+ failed = 1;
+ }
+ if (PIDS_SELECT_UID != PROC_UID) {
+ fprintf(stderr, "%s: header error: PIDS_SELECT_UID = 0x%04x, PROC_UID = 0x%04x\n"
+ , __FILE__, PIDS_SELECT_UID, PROC_UID);
+ failed = 1;
+ }
+ if (PIDS_SELECT_UID_THREADS != PIDS_SELECT_UID + 1) {
+ fprintf(stderr, "%s: header error: PIDS_SELECT_UID_THREADS = 0x%04x, should be 0x%04x\n"
+ , __FILE__, PIDS_SELECT_UID_THREADS, PIDS_SELECT_UID + 1);
+ failed = 1;
+ }
+ // our select() function & select enumerators assume the following ...
+ if (PIDS_FETCH_THREADS_TOO != 1) {
+ fprintf(stderr, "%s: header error: PIDS_FETCH_THREADS_TOO = %d, should be 1\n"
+ , __FILE__, PIDS_FETCH_THREADS_TOO);
+ failed = 1;
+ }
+ if (failed) _Exit(EXIT_FAILURE);
+#endif
+
+ if (info == NULL || *info != NULL)
+ return -EINVAL;
+ if (!(p = calloc(1, sizeof(struct pids_info))))
+ return -ENOMEM;
+
+ /* if we're without items or numitems, a later call to
+ procps_pids_reset() will become mandatory */
+ if (items && numitems) {
+ if (pids_items_check_failed(items, numitems)) {
+ free(p);
+ return -EINVAL;
+ }
+ // allow for our PIDS_logical_end
+ p->maxitems = numitems + 1;
+ if (!(p->items = calloc(p->maxitems, sizeof(enum pids_item)))) {
+ free(p);
+ return -ENOMEM;
+ }
+ memcpy(p->items, items, sizeof(enum pids_item) * numitems);
+ p->items[numitems] = PIDS_logical_end;
+ pids_libflags_set(p);
+ if (!pids_prep_func_array(p))
+ return -ENOMEM;
+ }
+
+ if (!(p->hist = calloc(1, sizeof(struct history_info)))
+ || (!(p->hist->PHist_new = calloc(NEWOLD_INIT, sizeof(HST_t))))
+ || (!(p->hist->PHist_sav = calloc(NEWOLD_INIT, sizeof(HST_t))))) {
+ free(p->items);
+ if (p->hist) {
+ free(p->hist->PHist_sav); // this & next might be NULL ...
+ free(p->hist->PHist_new);
+ free(p->hist);
+ }
+ free(p);
+ return -ENOMEM;
+ }
+ p->hist->HHist_siz = NEWOLD_INIT;
+ pids_config_history(p);
+
+ pgsz = getpagesize();
+ while (pgsz > 1024) { pgsz >>= 1; p->pgs2k_shift++; }
+ p->hertz = procps_hertz_get();
+
+ numa_init();
+
+ p->fetch.results.counts = &p->fetch.counts;
+
+ p->refcount = 1;
+ *info = p;
+ return 0;
+} // end: procps_pids_new
+
+
+PROCPS_EXPORT int procps_pids_ref (
+ struct pids_info *info)
+{
+ if (info == NULL)
+ return -EINVAL;
+
+ info->refcount++;
+ return info->refcount;
+} // end: procps_pids_ref
+
+
+PROCPS_EXPORT int procps_pids_unref (
+ struct pids_info **info)
+{
+ if (info == NULL || *info == NULL)
+ return -EINVAL;
+
+ (*info)->refcount--;
+
+ if ((*info)->refcount < 1) {
+#ifdef UNREF_RPTHASH
+ pids_unref_rpthash(*info);
+#endif
+ if ((*info)->extents) {
+ pids_cleanup_stacks_all(*info);
+ do {
+ struct stacks_extent *p = (*info)->extents;
+ (*info)->extents = (*info)->extents->next;
+ free(p);
+ } while ((*info)->extents);
+ }
+ if ((*info)->otherexts) {
+ struct stacks_extent *nextext, *ext = (*info)->otherexts;
+ while (ext) {
+ nextext = ext->next;
+ pids_cleanup_stack(ext->stacks[0]->head);
+ free(ext);
+ ext = nextext;
+ };
+ }
+ if ((*info)->fetch.anchor)
+ free((*info)->fetch.anchor);
+ if ((*info)->fetch.results.stacks)
+ free((*info)->fetch.results.stacks);
+
+ if ((*info)->items)
+ free((*info)->items);
+ if ((*info)->hist) {
+ free((*info)->hist->PHist_sav);
+ free((*info)->hist->PHist_new);
+ free((*info)->hist);
+ }
+
+ if ((*info)->get_ext)
+ pids_oldproc_close(&(*info)->get_PT);
+
+ if ((*info)->func_array)
+ free((*info)->func_array);
+
+ numa_uninit();
+
+ free(*info);
+ *info = NULL;
+ return 0;
+ }
+ return (*info)->refcount;
+} // end: procps_pids_unref
+
+
+// --- variable interface functions -------------------------------------------
+
+PROCPS_EXPORT struct pids_stack *fatal_proc_unmounted (
+ struct pids_info *info,
+ int return_self)
+{
+ struct pids_fetch *fetched;
+ unsigned tid;
+
+ /* this is very likely the *only* newlib function where the
+ context (pids_info) of NULL will ever be permitted */
+ if (!look_up_our_self()
+ || (!return_self))
+ return NULL;
+
+ tid = getpid();
+ if (!(fetched = procps_pids_select(info, &tid, 1, PIDS_SELECT_PID)))
+ return NULL;
+ return fetched->stacks[0];
+} // end: fatal_proc_unmounted
+
+
+PROCPS_EXPORT struct pids_stack *procps_pids_get (
+ struct pids_info *info,
+ enum pids_fetch_type which)
+{
+ double up_secs;
+
+ errno = EINVAL;
+ if (info == NULL)
+ return NULL;
+ if (which != PIDS_FETCH_TASKS_ONLY && which != PIDS_FETCH_THREADS_TOO)
+ return NULL;
+ /* with items & numitems technically optional at 'new' time, it's
+ expected 'reset' will have been called -- but just in case ... */
+ if (!info->maxitems)
+ return NULL;
+
+ if (!info->get_ext) {
+ if (!(info->get_ext = pids_stacks_alloc(info, 1)))
+ return NULL; // here, errno was overridden with ENOMEM
+fresh_start:
+ if (!pids_oldproc_open(&info->get_PT, info->oldflags))
+ return NULL; // here, errno was overridden with ENOMEM/others
+ info->get_type = which;
+ info->read_something = which ? readeither : readproc;
+ }
+
+ if (info->get_type != which) {
+ pids_oldproc_close(&info->get_PT);
+ goto fresh_start;
+ }
+ errno = 0;
+
+ /* when in a namespace with proc mounted subset=pid,
+ we will be restricted to process information only */
+ info->boot_tics = 0;
+ if (0 >= procps_uptime(&up_secs, NULL))
+ info->boot_tics = up_secs * info->hertz;
+
+ if (NULL == info->read_something(info->get_PT, &info->get_proc))
+ return NULL;
+ if (!pids_assign_results(info, info->get_ext->stacks[0], &info->get_proc))
+ return NULL;
+ return info->get_ext->stacks[0];
+} // end: procps_pids_get
+
+
+/* procps_pids_reap():
+ *
+ * Harvest all the available tasks/threads and provide the result
+ * stacks along with a summary of the information gathered.
+ *
+ * Returns: pointer to a pids_fetch struct on success, NULL on error.
+ */
+PROCPS_EXPORT struct pids_fetch *procps_pids_reap (
+ struct pids_info *info,
+ enum pids_fetch_type which)
+{
+ double up_secs;
+ int rc;
+
+ errno = EINVAL;
+ if (info == NULL)
+ return NULL;
+ if (which != PIDS_FETCH_TASKS_ONLY && which != PIDS_FETCH_THREADS_TOO)
+ return NULL;
+ /* with items & numitems technically optional at 'new' time, it's
+ expected 'reset' will have been called -- but just in case ... */
+ if (!info->maxitems)
+ return NULL;
+ errno = 0;
+
+ if (!pids_oldproc_open(&info->fetch_PT, info->oldflags))
+ return NULL;
+ info->read_something = which ? readeither : readproc;
+
+ /* when in a namespace with proc mounted subset=pid,
+ we will be restricted to process information only */
+ info->boot_tics = 0;
+ if (0 >= procps_uptime(&up_secs, NULL))
+ info->boot_tics = up_secs * info->hertz;
+
+ rc = pids_stacks_fetch(info);
+
+ pids_oldproc_close(&info->fetch_PT);
+ // we better have found at least 1 pid
+ return (rc > 0) ? &info->fetch.results : NULL;
+} // end: procps_pids_reap
+
+
+PROCPS_EXPORT int procps_pids_reset (
+ struct pids_info *info,
+ enum pids_item *newitems,
+ int newnumitems)
+{
+ if (info == NULL || newitems == NULL)
+ return -EINVAL;
+ if (pids_items_check_failed(newitems, newnumitems))
+ return -EINVAL;
+
+ pids_cleanup_stacks_all(info);
+
+ /* shame on this caller, they didn't change anything. and unless they have
+ altered the depth of the stacks we're not gonna change anything either! */
+ if (info->maxitems == newnumitems + 1
+ && !memcmp(info->items, newitems, sizeof(enum pids_item) * newnumitems))
+ return 0;
+
+ if (info->maxitems < newnumitems + 1) {
+ while (info->extents) {
+ struct stacks_extent *p = info->extents;
+ info->extents = p->next;
+ free(p);
+ };
+ if (info->get_ext) {
+ pids_oldproc_close(&info->get_PT);
+ info->get_ext = NULL;
+ }
+ if (info->fetch.anchor) {
+ free(info->fetch.anchor);
+ info->fetch.anchor = NULL;
+ }
+ // allow for our PIDS_logical_end
+ info->maxitems = newnumitems + 1;
+ if (!(info->items = realloc(info->items, sizeof(enum pids_item) * info->maxitems)))
+ return -ENOMEM;
+ }
+
+ memcpy(info->items, newitems, sizeof(enum pids_item) * newnumitems);
+ info->items[newnumitems] = PIDS_logical_end;
+ // account for above PIDS_logical_end
+ info->maxitems = newnumitems + 1;
+
+ // if extents were freed above, this next guy will have no effect
+ // so we'll rely on pids_stacks_alloc() to itemize ...
+ pids_itemize_stacks_all(info);
+ pids_libflags_set(info);
+ if (!pids_prep_func_array(info))
+ return -ENOMEM;
+
+ return 0;
+} // end: procps_pids_reset
+
+
+/* procps_pids_select():
+ *
+ * Harvest any processes matching the specified PID or UID and provide the
+ * result stacks along with a summary of the information gathered.
+ *
+ * Returns: pointer to a pids_fetch struct on success, NULL on error.
+ */
+PROCPS_EXPORT struct pids_fetch *procps_pids_select (
+ struct pids_info *info,
+ unsigned *these,
+ int numthese,
+ enum pids_select_type which)
+{
+ unsigned ids[FILL_ID_MAX + 1];
+ double up_secs;
+ int rc;
+
+ errno = EINVAL;
+ if (info == NULL || these == NULL)
+ return NULL;
+ if (numthese < 1 || numthese > FILL_ID_MAX)
+ return NULL;
+ if ((which != PIDS_SELECT_PID && which != PIDS_SELECT_UID)
+ && ((which != PIDS_SELECT_PID_THREADS && which != PIDS_SELECT_UID_THREADS)))
+ return NULL;
+ /* with items & numitems technically optional at 'new' time, it's
+ expected 'reset' will have been called -- but just in case ... */
+ if (!info->maxitems)
+ return NULL;
+ errno = 0;
+
+ // this zero delimiter is really only needed with PIDS_SELECT_PID
+ memcpy(ids, these, sizeof(unsigned) * numthese);
+ ids[numthese] = 0;
+
+ if (!pids_oldproc_open(&info->fetch_PT, (info->oldflags | which), ids, numthese))
+ return NULL;
+ info->read_something = (which & PIDS_FETCH_THREADS_TOO) ? readeither : readproc;
+
+ /* when in a namespace with proc mounted subset=pid,
+ we will be restricted to process information only */
+ info->boot_tics = 0;
+ if (0 >= procps_uptime(&up_secs, NULL))
+ info->boot_tics = up_secs * info->hertz;
+
+ rc = pids_stacks_fetch(info);
+
+ pids_oldproc_close(&info->fetch_PT);
+ // no guarantee any pids/uids were found
+ return (rc >= 0) ? &info->fetch.results : NULL;
+} // end: procps_pids_select
+
+
+/*
+ * procps_pids_sort():
+ *
+ * Sort stacks anchored in the passed stack pointers array
+ * based on the designated sort enumerator and specified order.
+ *
+ * Returns those same addresses sorted.
+ *
+ * Note: all of the stacks must be homogeneous (of equal length and content).
+ */
+PROCPS_EXPORT struct pids_stack **procps_pids_sort (
+ struct pids_info *info,
+ struct pids_stack *stacks[],
+ int numstacked,
+ enum pids_item sortitem,
+ enum pids_sort_order order)
+{
+ struct sort_parms parms;
+ struct pids_result *p;
+ int offset;
+
+ errno = EINVAL;
+ if (info == NULL || stacks == NULL)
+ return NULL;
+ // a pids_item is currently unsigned, but we'll protect our future
+ if (sortitem < 0 || sortitem >= PIDS_logical_end)
+ return NULL;
+ if (order != PIDS_SORT_ASCEND && order != PIDS_SORT_DESCEND)
+ return NULL;
+ if (numstacked < 2)
+ return stacks;
+
+ offset = 0;
+ p = stacks[0]->head;
+ for (;;) {
+ if (p->item == sortitem)
+ break;
+ ++offset;
+ if (offset >= info->maxitems)
+ return NULL;
+ if (p->item >= PIDS_logical_end)
+ return NULL;
+ ++p;
+ }
+ errno = 0;
+
+ parms.offset = offset;
+ parms.order = order;
+
+ qsort_r(stacks, numstacked, sizeof(void *), (QSR_t)Item_table[p->item].sortfunc, &parms);
+ return stacks;
+} // end: procps_pids_sort
+
+
+// --- special debugging function(s) ------------------------------------------
+/*
+ * The following isn't part of the normal programming interface. Rather,
+ * it exists to validate result types referenced in application programs.
+ *
+ * It's used only when:
+ * 1) the 'XTRA_PROCPS_DEBUG' has been defined, or
+ * 2) an #include of 'xtra-procps-debug.h' is used
+ */
+
+PROCPS_EXPORT struct pids_result *xtra_pids_val (
+ int relative_enum,
+ const char *typestr,
+ const struct pids_stack *stack,
+ struct pids_info *info,
+ const char *file,
+ int lineno)
+{
+ char *str;
+ int i;
+
+ for (i = 0; stack->head[i].item < PIDS_logical_end; i++)
+ ;
+ if (relative_enum < 0 || relative_enum >= i) {
+ fprintf(stderr, "%s line %d: invalid relative_enum = %d, valid range = 0-%d\n"
+ , file, lineno, relative_enum, i-1);
+ return NULL;
+ }
+ str = Item_table[stack->head[relative_enum].item].type2str;
+ if (str[0]
+ && (strcmp(typestr, str))) {
+ fprintf(stderr, "%s line %d: was %s, expected %s\n", file, lineno, typestr, str);
+ }
+ return &stack->head[relative_enum];
+ (void)info;
+} // end: xtra_pids_val