From 09503dc59792ab0147de604ab48342b8508f2d7b Mon Sep 17 00:00:00 2001 From: Jim Warner Date: Thu, 19 May 2016 00:00:00 -0500 Subject: [PATCH] library: eliminated a final potential NULL, api In that reference below a specific systemd problem was fixed in the commit shown. However lurking deep within the interface was yet one final case where NULL could be returned, involving 'strv' and the following: . a user requested both a single string vector (always returned as a normal string) & the vectorized version, as with PROCPS_PIDS_CMDLINE and PROCPS_PIDS_CMDLINE_V. . a user simply duplicated some vectorized enum items. The root of that NULL problem is the fact those single string vectors shared the same proc_t field with their true vectorized version. So while multiple occurrences for most strings could be satisfied with strdup versus the normal ownership usurpation, those true vectorized fields could not be quite so easily copied/duplicated. Thus newlib chose to return a NULL result.strv pointer under either of the above scenarios (which perhaps was just a user boo-boo in the first place). In any event, the NULL was a potential for true string vectors only. Now, since newlib is the sole caller into the readproc module, separate fields have been created for what are just normal strings (never vectorized) and those which remain the true vectorized versions. And, former flags which only worked if combined, now act as stand alone. Thus, both PROCPS_PIDS_CMDLINE & PROCPS_PIDS_CMDLINE_V can be used simultaneously (as they should have been). Also with this patch, items which a user duplicates in the stack (beyond the first such item) will return the the string "[ duplicate ENUM_ID ]". This practice will apply to both single strings and true vectorized ones. In addition to informing users of their error, it will also mean potential NULLs need now never be a concern. Reference(s); http://www.freelists.org/post/procps/systemd-binary-vs-library commit 0580a7b4c67d0297629d37281b4f690894429626 Signed-off-by: Jim Warner --- proc/pids.c | 339 ++++++++++++++++++++++++------------------------ proc/readproc.c | 181 +++++++++++--------------- proc/readproc.h | 27 ++-- 3 files changed, 266 insertions(+), 281 deletions(-) diff --git a/proc/pids.c b/proc/pids.c index 658f0524..e921b096 100644 --- a/proc/pids.c +++ b/proc/pids.c @@ -49,14 +49,6 @@ #define FILL_ID_MAX 255 // upper limit for pid/uid fills #define MEMORY_INCR 128 // amt by which allocations grow - // these represent the proc_t fields whose storage cannot be managed - // optimally if they are ever referenced more than once in any stack -enum rel_ref { - ref_CGROUP, ref_CMD, ref_CMDLINE, ref_ENVIRON, ref_SD_MACH, - ref_SD_OUID, ref_SD_SEAT, ref_SD_SESS, ref_SD_SLICE, ref_SD_UNIT, - ref_SD_UUNIT, ref_SUPGIDS, ref_SUPGROUPS, - MAXIMUM_ref -}; struct stacks_extent { struct pids_stack **stacks; @@ -89,7 +81,6 @@ struct procps_pidsinfo { PROCTAB *PT; // the old library essential interface unsigned long hertz; // for TIME_ALL & TIME_ELAPSED calculations unsigned long long boot_seconds; // for TIME_ELAPSED calculation - int ref_counts[MAXIMUM_ref]; // ref counts for special string fields PROCTAB *get_PT; // old library interface for active 'get' struct stacks_extent *get_ext; // an extent used for active 'get' enum pids_fetch_type get_type; // last known type of 'get' request @@ -98,37 +89,51 @@ struct procps_pidsinfo { // ___ Results 'Set' Support |||||||||||||||||||||||||||||||||||||||||||||||||| +static char** vectorize_this (const char* src) { + #define pSZ (sizeof(char*)) + char *cpy, **vec; + int adj, tot; + + tot = strlen(src) + 1; // prep for our vectors + adj = (pSZ-1) - ((tot + pSZ-1) & (pSZ-1)); // calc alignment bytes + cpy = calloc(1, tot + adj + (2 * pSZ)); // get new larger buffer + if (!cpy) return NULL; // we no longer use xcalloc + snprintf(cpy, tot, "%s", src); // duplicate their string + vec = (char**)(cpy + tot + adj); // prep pointer to pointers + *vec = cpy; // point 1st vector to string + *(vec+1) = NULL; // null ptr 'list' delimit + return vec; // ==> free(*vec) to dealloc + #undef pSZ +} // end: vectorize_this + + +#define mkSTR(a) xySTR(a) +#define xySTR(z) #z + #define setNAME(e) set_results_ ## e #define setDECL(e) static void setNAME(e) \ (struct procps_pidsinfo *I, struct pids_result *R, proc_t *P) -// convert pages to kib +/* 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 +/* strdup of a static char array */ #define DUP_set(e,x) setDECL(e) { \ (void)I; R->result.str = strdup(P-> x); } -// regular assignment copy +/* regular assignment copy */ #define REG_set(e,t,x) setDECL(e) { \ (void)I; R->result. t = P-> x; } -// take ownership of a regular char* string if possible, else duplicate +/* 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) { \ - if (I->ref_counts[ref_ ## e] > 1) R->result.str = strdup(P-> x); \ - else { R->result.str = P-> x; P-> x = NULL; } } -// take ownership of a vectorized single string if possible, else duplicate -#define STV_set(e,x) setDECL(e) { \ - if (I->ref_counts[ref_ ## e] > 1) R->result.str = strdup(*P-> x); \ - else { R->result.str = *P-> x; P-> x = NULL; } } -/* - take ownership of true vectorized strings if possible, else return NULL - [ if there's a source field ref_count, then those true string vectors ] - [ have already been converted into a single string so we return NULL. ] - [ otherwise, the first result struct now gets ownership of those true ] - [ string vectors and any duplicate structures will then receive NULL. ] -*/ -#define VEC_set(e1,e2,x) setDECL(e1) { \ - if (I->ref_counts[ref_ ## e2]) R->result.strv = NULL; \ - else { R->result.strv = P-> x; P-> x = NULL; } } + (void)I; if (NULL != P-> x) { R->result.str = P-> x; P-> x = NULL; } \ + else R->result.str = strdup("[ duplicate " mkSTR(e) " ]"); } +/* 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) { \ + (void)I; if (NULL != P-> x) { R->result.strv = P-> x; P-> x = NULL; } \ + else R->result.strv = vectorize_this("[ duplicate " mkSTR(e) " ]"); } + setDECL(noop) { (void)I; (void)R; (void)P; return; } setDECL(extra) { (void)I; (void)R; (void)P; return; } @@ -138,14 +143,14 @@ REG_set(ADDR_KSTK_ESP, ul_int, kstk_esp) REG_set(ADDR_START_CODE, ul_int, start_code) REG_set(ADDR_START_STACK, ul_int, start_stack) REG_set(ALARM, sl_int, alarm) -setDECL(CGNAME) { char *name = strstr(*P->cgroup, ":name="); if (name && *(name+6)) name += 6; else name = *P->cgroup; R->result.str = strdup(name); } -STV_set(CGROUP, cgroup) -VEC_set(CGROUP_V, CGROUP, cgroup) +STR_set(CGNAME, cgname) +STR_set(CGROUP, cgroup) +VEC_set(CGROUP_V, cgroup_v) STR_set(CMD, cmd) -STV_set(CMDLINE, cmdline) -VEC_set(CMDLINE_V, CMDLINE, cmdline) -STV_set(ENVIRON, environ) -VEC_set(ENVIRON_V, ENVIRON, environ) +STR_set(CMDLINE, cmdline) +VEC_set(CMDLINE_V, cmdline_v) +STR_set(ENVIRON, environ) +VEC_set(ENVIRON_V, environ_v) REG_set(EXIT_SIGNAL, s_int, exit_signal) REG_set(FLAGS, ul_int, flags) REG_set(FLT_MAJ, sl_int, maj_flt) @@ -249,12 +254,15 @@ REG_set(VSIZE_PGS, ul_int, vsize) REG_set(WCHAN_ADDR, ul_int, wchan) setDECL(WCHAN_NAME) { (void)I; R->result.str = strdup(lookup_wchan(P->tid)); } + +#undef mkSTR +#undef xySTR + #undef setDECL #undef CVT_set #undef DUP_set #undef REG_set #undef STR_set -#undef STV_set #undef VEC_set @@ -347,14 +355,15 @@ static int srtNAME(noop) ( #define f_status PROC_FILLSTATUS #define f_systemd PROC_FILLSYSTEMD #define f_usr PROC_FILLUSR + // 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 yield true verctorized strings #define v_arg PROC_FILLARG #define v_cgroup PROC_FILLCGROUP #define v_env PROC_FILLENV - // remaining are compound flags, yielding a single string (maybe vectorized) -#define x_cgroup PROC_EDITCGRPCVT | PROC_FILLCGROUP // just 1 str -#define x_cmdline PROC_EDITCMDLCVT | PROC_FILLARG // just 1 str -#define x_environ PROC_EDITENVRCVT | PROC_FILLENV // just 1 str + // remaining are compound flags #define x_ogroup PROC_FILLSTATUS | PROC_FILLGRP #define x_ouser PROC_FILLSTATUS | PROC_FILLUSR #define x_supgrp PROC_FILLSTATUS | PROC_FILLSUPGRP @@ -378,130 +387,129 @@ static struct { 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 - int refcount; // the result needs reference counts } Item_table[] = { -/* setsfunc oldflags freefunc sortfunc needhist refcount - --------------------- ---------- --------- ------------ -------- ------------- */ - { RS(noop), 0, NULL, QS(noop), 0, -1 }, // user only, never altered - { RS(extra), 0, NULL, QS(ull_int), 0, -1 }, // user only, reset to zero - { RS(ADDR_END_CODE), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(ADDR_KSTK_EIP), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(ADDR_KSTK_ESP), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(ADDR_START_CODE), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(ADDR_START_STACK), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(ALARM), f_stat, NULL, QS(sl_int), 0, -1 }, - { RS(CGNAME), x_cgroup, FF(str), QS(str), 0, ref_CGROUP }, // refcount: diff result, same source - { RS(CGROUP), x_cgroup, FF(str), QS(str), 0, ref_CGROUP }, // refcount: diff result, same source - { RS(CGROUP_V), v_cgroup, FF(strv), QS(strv), 0, -1 }, - { RS(CMD), f_either, FF(str), QS(str), 0, ref_CMD }, - { RS(CMDLINE), x_cmdline, FF(str), QS(str), 0, ref_CMDLINE }, - { RS(CMDLINE_V), v_arg, FF(strv), QS(strv), 0, -1 }, - { RS(ENVIRON), x_environ, FF(str), QS(str), 0, ref_ENVIRON }, - { RS(ENVIRON_V), v_env, FF(strv), QS(strv), 0, -1 }, - { RS(EXIT_SIGNAL), f_stat, NULL, QS(s_int), 0, -1 }, - { RS(FLAGS), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(FLT_MAJ), f_stat, NULL, QS(sl_int), 0, -1 }, - { RS(FLT_MAJ_C), f_stat, NULL, QS(sl_int), 0, -1 }, - { RS(FLT_MAJ_DELTA), f_stat, NULL, QS(sl_int), +1, -1 }, - { RS(FLT_MIN), f_stat, NULL, QS(sl_int), 0, -1 }, - { RS(FLT_MIN_C), f_stat, NULL, QS(sl_int), 0, -1 }, - { RS(FLT_MIN_DELTA), f_stat, NULL, QS(sl_int), +1, -1 }, - { RS(ID_EGID), 0, NULL, QS(u_int), 0, -1 }, // oldflags: free w/ simple_read... - { RS(ID_EGROUP), f_grp, NULL, QS(str), 0, -1 }, - { RS(ID_EUID), 0, NULL, QS(u_int), 0, -1 }, // oldflags: free w/ simple_read... - { RS(ID_EUSER), f_usr, NULL, QS(str), 0, -1 }, - { RS(ID_FGID), f_status, NULL, QS(u_int), 0, -1 }, - { RS(ID_FGROUP), x_ogroup, NULL, QS(str), 0, -1 }, - { RS(ID_FUID), f_status, NULL, QS(u_int), 0, -1 }, - { RS(ID_FUSER), x_ouser, NULL, QS(str), 0, -1 }, - { RS(ID_PGRP), f_stat, NULL, QS(s_int), 0, -1 }, - { RS(ID_PID), 0, NULL, QS(s_int), 0, -1 }, // oldflags: free w/ simple_nextpid - { RS(ID_PPID), f_either, NULL, QS(s_int), 0, -1 }, - { RS(ID_RGID), f_status, NULL, QS(u_int), 0, -1 }, - { RS(ID_RGROUP), x_ogroup, NULL, QS(str), 0, -1 }, - { RS(ID_RUID), f_status, NULL, QS(u_int), 0, -1 }, - { RS(ID_RUSER), x_ouser, NULL, QS(str), 0, -1 }, - { RS(ID_SESSION), f_stat, NULL, QS(s_int), 0, -1 }, - { RS(ID_SGID), f_status, NULL, QS(u_int), 0, -1 }, - { RS(ID_SGROUP), x_ogroup, NULL, QS(str), 0, -1 }, - { RS(ID_SUID), f_status, NULL, QS(u_int), 0, -1 }, - { RS(ID_SUSER), x_ouser, NULL, QS(str), 0, -1 }, - { RS(ID_TGID), 0, NULL, QS(s_int), 0, -1 }, // oldflags: free w/ simple_nextpid - { RS(ID_TPGID), f_stat, NULL, QS(s_int), 0, -1 }, - { RS(LXCNAME), f_lxc, NULL, QS(str), 0, -1 }, - { RS(MEM_CODE), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_CODE_KIB), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_DATA), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_DATA_KIB), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_DT), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_LRS), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_RES), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_RES_KIB), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_SHR), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_SHR_KIB), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_VIRT), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(MEM_VIRT_KIB), f_statm, NULL, QS(sl_int), 0, -1 }, - { RS(NICE), f_stat, NULL, QS(sl_int), 0, -1 }, - { RS(NLWP), f_either, NULL, QS(s_int), 0, -1 }, - { RS(NS_IPC), f_ns, NULL, QS(ul_int), 0, -1 }, - { RS(NS_MNT), f_ns, NULL, QS(ul_int), 0, -1 }, - { RS(NS_NET), f_ns, NULL, QS(ul_int), 0, -1 }, - { RS(NS_PID), f_ns, NULL, QS(ul_int), 0, -1 }, - { RS(NS_USER), f_ns, NULL, QS(ul_int), 0, -1 }, - { RS(NS_UTS), f_ns, NULL, QS(ul_int), 0, -1 }, - { RS(OOM_ADJ), f_oom, NULL, QS(s_int), 0, -1 }, - { RS(OOM_SCORE), f_oom, NULL, QS(s_int), 0, -1 }, - { RS(PRIORITY), f_stat, NULL, QS(s_int), 0, -1 }, - { RS(PROCESSOR), f_stat, NULL, QS(u_int), 0, -1 }, - { RS(RSS), f_stat, NULL, QS(sl_int), 0, -1 }, - { RS(RSS_RLIM), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(RTPRIO), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(SCHED_CLASS), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(SD_MACH), f_systemd, FF(str), QS(str), 0, ref_SD_MACH }, - { RS(SD_OUID), f_systemd, FF(str), QS(str), 0, ref_SD_OUID }, - { RS(SD_SEAT), f_systemd, FF(str), QS(str), 0, ref_SD_SEAT }, - { RS(SD_SESS), f_systemd, FF(str), QS(str), 0, ref_SD_SESS }, - { RS(SD_SLICE), f_systemd, FF(str), QS(str), 0, ref_SD_SLICE }, - { RS(SD_UNIT), f_systemd, FF(str), QS(str), 0, ref_SD_UNIT }, - { RS(SD_UUNIT), f_systemd, FF(str), QS(str), 0, ref_SD_UUNIT }, - { RS(SIGBLOCKED), f_status, FF(str), QS(str), 0, -1 }, - { RS(SIGCATCH), f_status, FF(str), QS(str), 0, -1 }, - { RS(SIGIGNORE), f_status, FF(str), QS(str), 0, -1 }, - { RS(SIGNALS), f_status, FF(str), QS(str), 0, -1 }, - { RS(SIGPENDING), f_status, FF(str), QS(str), 0, -1 }, - { RS(STATE), f_either, NULL, QS(s_ch), 0, -1 }, - { RS(SUPGIDS), f_status, FF(str), QS(str), 0, ref_SUPGIDS }, - { RS(SUPGROUPS), x_supgrp, FF(str), QS(str), 0, ref_SUPGROUPS }, - { RS(TICS_ALL), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TICS_ALL_C), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TICS_DELTA), f_stat, NULL, QS(sl_int), +1, -1 }, - { RS(TICS_SYSTEM), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TICS_SYSTEM_C), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TICS_USER), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TICS_USER_C), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TIME_ALL), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TIME_ELAPSED), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TIME_START), f_stat, NULL, QS(ull_int), 0, -1 }, - { RS(TTY), f_stat, NULL, QS(s_int), 0, -1 }, - { RS(TTY_NAME), f_stat, FF(str), QS(strvers), 0, -1 }, - { RS(TTY_NUMBER), f_stat, FF(str), QS(strvers), 0, -1 }, - { RS(VM_DATA), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_EXE), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_LIB), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_RSS), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_RSS_ANON), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_RSS_FILE), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_RSS_LOCKED), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_RSS_SHARED), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_SIZE), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_STACK), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_SWAP), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VM_USED), f_status, NULL, QS(sl_int), 0, -1 }, - { RS(VSIZE_PGS), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(WCHAN_ADDR), f_stat, NULL, QS(ul_int), 0, -1 }, - { RS(WCHAN_NAME), 0, FF(str), QS(str), 0, -1 }, // oldflags: tid already free +/* setsfunc oldflags freefunc sortfunc needhist + --------------------- ---------- --------- ------------- -------- */ + { RS(noop), 0, NULL, QS(noop), 0 }, // user only, never altered + { RS(extra), 0, NULL, QS(ull_int), 0 }, // user only, reset to zero + { RS(ADDR_END_CODE), f_stat, NULL, QS(ul_int), 0 }, + { RS(ADDR_KSTK_EIP), f_stat, NULL, QS(ul_int), 0 }, + { RS(ADDR_KSTK_ESP), f_stat, NULL, QS(ul_int), 0 }, + { RS(ADDR_START_CODE), f_stat, NULL, QS(ul_int), 0 }, + { RS(ADDR_START_STACK), f_stat, NULL, QS(ul_int), 0 }, + { RS(ALARM), f_stat, NULL, QS(sl_int), 0 }, + { RS(CGNAME), x_cgroup, FF(str), QS(str), 0 }, + { RS(CGROUP), x_cgroup, FF(str), QS(str), 0 }, + { RS(CGROUP_V), v_cgroup, FF(strv), QS(strv), 0 }, + { RS(CMD), f_either, FF(str), QS(str), 0 }, + { RS(CMDLINE), x_cmdline, FF(str), QS(str), 0 }, + { RS(CMDLINE_V), v_arg, FF(strv), QS(strv), 0 }, + { RS(ENVIRON), x_environ, FF(str), QS(str), 0 }, + { RS(ENVIRON_V), v_env, FF(strv), QS(strv), 0 }, + { RS(EXIT_SIGNAL), f_stat, NULL, QS(s_int), 0 }, + { RS(FLAGS), f_stat, NULL, QS(ul_int), 0 }, + { RS(FLT_MAJ), f_stat, NULL, QS(sl_int), 0 }, + { RS(FLT_MAJ_C), f_stat, NULL, QS(sl_int), 0 }, + { RS(FLT_MAJ_DELTA), f_stat, NULL, QS(sl_int), +1 }, + { RS(FLT_MIN), f_stat, NULL, QS(sl_int), 0 }, + { RS(FLT_MIN_C), f_stat, NULL, QS(sl_int), 0 }, + { RS(FLT_MIN_DELTA), f_stat, NULL, QS(sl_int), +1 }, + { RS(ID_EGID), 0, NULL, QS(u_int), 0 }, // oldflags: free w/ simple_read... + { RS(ID_EGROUP), f_grp, NULL, QS(str), 0 }, + { RS(ID_EUID), 0, NULL, QS(u_int), 0 }, // oldflags: free w/ simple_read... + { RS(ID_EUSER), f_usr, NULL, QS(str), 0 }, + { RS(ID_FGID), f_status, NULL, QS(u_int), 0 }, + { RS(ID_FGROUP), x_ogroup, NULL, QS(str), 0 }, + { RS(ID_FUID), f_status, NULL, QS(u_int), 0 }, + { RS(ID_FUSER), x_ouser, NULL, QS(str), 0 }, + { RS(ID_PGRP), f_stat, NULL, QS(s_int), 0 }, + { RS(ID_PID), 0, NULL, QS(s_int), 0 }, // oldflags: free w/ simple_nextpid + { RS(ID_PPID), f_either, NULL, QS(s_int), 0 }, + { RS(ID_RGID), f_status, NULL, QS(u_int), 0 }, + { RS(ID_RGROUP), x_ogroup, NULL, QS(str), 0 }, + { RS(ID_RUID), f_status, NULL, QS(u_int), 0 }, + { RS(ID_RUSER), x_ouser, NULL, QS(str), 0 }, + { RS(ID_SESSION), f_stat, NULL, QS(s_int), 0 }, + { RS(ID_SGID), f_status, NULL, QS(u_int), 0 }, + { RS(ID_SGROUP), x_ogroup, NULL, QS(str), 0 }, + { RS(ID_SUID), f_status, NULL, QS(u_int), 0 }, + { RS(ID_SUSER), x_ouser, NULL, QS(str), 0 }, + { RS(ID_TGID), 0, NULL, QS(s_int), 0 }, // oldflags: free w/ simple_nextpid + { RS(ID_TPGID), f_stat, NULL, QS(s_int), 0 }, + { RS(LXCNAME), f_lxc, NULL, QS(str), 0 }, + { RS(MEM_CODE), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_CODE_KIB), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_DATA), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_DATA_KIB), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_DT), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_LRS), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_RES), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_RES_KIB), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_SHR), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_SHR_KIB), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_VIRT), f_statm, NULL, QS(sl_int), 0 }, + { RS(MEM_VIRT_KIB), f_statm, NULL, QS(sl_int), 0 }, + { RS(NICE), f_stat, NULL, QS(sl_int), 0 }, + { RS(NLWP), f_either, NULL, QS(s_int), 0 }, + { RS(NS_IPC), f_ns, NULL, QS(ul_int), 0 }, + { RS(NS_MNT), f_ns, NULL, QS(ul_int), 0 }, + { RS(NS_NET), f_ns, NULL, QS(ul_int), 0 }, + { RS(NS_PID), f_ns, NULL, QS(ul_int), 0 }, + { RS(NS_USER), f_ns, NULL, QS(ul_int), 0 }, + { RS(NS_UTS), f_ns, NULL, QS(ul_int), 0 }, + { RS(OOM_ADJ), f_oom, NULL, QS(s_int), 0 }, + { RS(OOM_SCORE), f_oom, NULL, QS(s_int), 0 }, + { RS(PRIORITY), f_stat, NULL, QS(s_int), 0 }, + { RS(PROCESSOR), f_stat, NULL, QS(u_int), 0 }, + { RS(RSS), f_stat, NULL, QS(sl_int), 0 }, + { RS(RSS_RLIM), f_stat, NULL, QS(ul_int), 0 }, + { RS(RTPRIO), f_stat, NULL, QS(ul_int), 0 }, + { RS(SCHED_CLASS), f_stat, NULL, QS(ul_int), 0 }, + { RS(SD_MACH), f_systemd, FF(str), QS(str), 0 }, + { RS(SD_OUID), f_systemd, FF(str), QS(str), 0 }, + { RS(SD_SEAT), f_systemd, FF(str), QS(str), 0 }, + { RS(SD_SESS), f_systemd, FF(str), QS(str), 0 }, + { RS(SD_SLICE), f_systemd, FF(str), QS(str), 0 }, + { RS(SD_UNIT), f_systemd, FF(str), QS(str), 0 }, + { RS(SD_UUNIT), f_systemd, FF(str), QS(str), 0 }, + { RS(SIGBLOCKED), f_status, FF(str), QS(str), 0 }, + { RS(SIGCATCH), f_status, FF(str), QS(str), 0 }, + { RS(SIGIGNORE), f_status, FF(str), QS(str), 0 }, + { RS(SIGNALS), f_status, FF(str), QS(str), 0 }, + { RS(SIGPENDING), f_status, FF(str), QS(str), 0 }, + { RS(STATE), f_either, NULL, QS(s_ch), 0 }, + { RS(SUPGIDS), f_status, FF(str), QS(str), 0 }, + { RS(SUPGROUPS), x_supgrp, FF(str), QS(str), 0 }, + { RS(TICS_ALL), f_stat, NULL, QS(ull_int), 0 }, + { RS(TICS_ALL_C), f_stat, NULL, QS(ull_int), 0 }, + { RS(TICS_DELTA), f_stat, NULL, QS(sl_int), +1 }, + { RS(TICS_SYSTEM), f_stat, NULL, QS(ull_int), 0 }, + { RS(TICS_SYSTEM_C), f_stat, NULL, QS(ull_int), 0 }, + { RS(TICS_USER), f_stat, NULL, QS(ull_int), 0 }, + { RS(TICS_USER_C), f_stat, NULL, QS(ull_int), 0 }, + { RS(TIME_ALL), f_stat, NULL, QS(ull_int), 0 }, + { RS(TIME_ELAPSED), f_stat, NULL, QS(ull_int), 0 }, + { RS(TIME_START), f_stat, NULL, QS(ull_int), 0 }, + { RS(TTY), f_stat, NULL, QS(s_int), 0 }, + { RS(TTY_NAME), f_stat, FF(str), QS(strvers), 0 }, + { RS(TTY_NUMBER), f_stat, FF(str), QS(strvers), 0 }, + { RS(VM_DATA), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_EXE), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_LIB), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_RSS), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_RSS_ANON), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_RSS_FILE), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_RSS_LOCKED), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_RSS_SHARED), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_SIZE), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_STACK), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_SWAP), f_status, NULL, QS(sl_int), 0 }, + { RS(VM_USED), f_status, NULL, QS(sl_int), 0 }, + { RS(VSIZE_PGS), f_stat, NULL, QS(ul_int), 0 }, + { RS(WCHAN_ADDR), f_stat, NULL, QS(ul_int), 0 }, + { RS(WCHAN_NAME), 0, FF(str), QS(str), 0 }, // oldflags: tid already free // dummy entry corresponding to PROCPS_PIDS_logical_end ... - { NULL, 0, NULL, NULL, 0, -1 } + { NULL, 0, NULL, NULL, 0 } }; // next MUST be kept in sync with highest value enum @@ -907,17 +915,14 @@ static inline void libflags_set ( struct procps_pidsinfo *info) { enum pids_item e; - int i, n; + int i; - memset (info->ref_counts, 0, sizeof(info->ref_counts)); info->oldflags = info->history_yes = 0; for (i = 0; i < info->curitems; i++) { if (((e = info->items[i])) >= PROCPS_PIDS_logical_end) break; info->oldflags |= Item_table[e].oldflags; info->history_yes |= Item_table[e].needhist; - n = Item_table[e].refcount; - if (n > -1) ++info->ref_counts[n]; } if (info->oldflags & f_either) { if (!(info->oldflags & f_stat)) diff --git a/proc/readproc.c b/proc/readproc.c index b7591892..d87eeac6 100644 --- a/proc/readproc.c +++ b/proc/readproc.c @@ -91,19 +91,23 @@ static inline void free_acquired (proc_t *p, int reuse) { #ifdef QUICK_THREADS if (!IS_THREAD(p)) { #endif - if (p->environ) free((void*)*p->environ); - if (p->cmdline) free((void*)*p->cmdline); - if (p->cgroup) free((void*)*p->cgroup); - if (p->supgid) free(p->supgid); - if (p->supgrp) free(p->supgrp); - if (p->cmd) free(p->cmd); - if (p->sd_mach) free(p->sd_mach); - if (p->sd_ouid) free(p->sd_ouid); - if (p->sd_seat) free(p->sd_seat); - if (p->sd_sess) free(p->sd_sess); - if (p->sd_slice) free(p->sd_slice); - if (p->sd_unit) free(p->sd_unit); - if (p->sd_uunit) free(p->sd_uunit); + if (p->environ) free((void*)p->environ); + if (p->cmdline) free((void*)p->cmdline); + if (p->cgname) free((void*)p->cgname); + if (p->cgroup) free((void*)p->cgroup); + if (p->environ_v) free((void*)*p->environ_v); + if (p->cmdline_v) free((void*)*p->cmdline_v); + if (p->cgroup_v) free((void*)*p->cgroup_v); + if (p->supgid) free(p->supgid); + if (p->supgrp) free(p->supgrp); + if (p->cmd) free(p->cmd); + if (p->sd_mach) free(p->sd_mach); + if (p->sd_ouid) free(p->sd_ouid); + if (p->sd_seat) free(p->sd_seat); + if (p->sd_sess) free(p->sd_sess); + if (p->sd_slice) free(p->sd_slice); + if (p->sd_unit) free(p->sd_unit); + if (p->sd_uunit) free(p->sd_uunit); #ifdef QUICK_THREADS } #endif @@ -713,28 +717,11 @@ static int read_unvectored(char *restrict const dst, unsigned sz, const char* wh return n; } -static char** vectorize_this_str (const char* src) { - #define pSZ (sizeof(char*)) - char *cpy, **vec; - int adj, tot; - - tot = strlen(src) + 1; // prep for our vectors - adj = (pSZ-1) - ((tot + pSZ-1) & (pSZ-1)); // calc alignment bytes - cpy = xcalloc(tot + adj + (2 * pSZ)); // get new larger buffer - snprintf(cpy, tot, "%s", src); // duplicate their string - vec = (char**)(cpy + tot + adj); // prep pointer to pointers - *vec = cpy; // point 1st vector to string - *(vec+1) = NULL; // null ptr 'list' delimit - return vec; // ==> free(*vec) to dealloc - #undef pSZ -} - - // This routine reads a 'cgroup' for the designated proc_t. - // It is similar to file2strvec except we filter and concatenate - // the data into a single string represented as a single vector. + // This routine reads a 'cgroup' for the designated proc_t and + // guarantees the caller a valid proc_t.cgroup pointer. static void fill_cgroup_cvt (const char* directory, proc_t *restrict p) { #define vMAX ( MAX_BUFSZ - (int)(dst - dst_buffer) ) - char *src, *dst, *grp, *eob; + char *src, *dst, *grp, *eob, *name; int tot, x, whackable_int = MAX_BUFSZ; *(dst = dst_buffer) = '\0'; // empty destination @@ -750,13 +737,17 @@ static void fill_cgroup_cvt (const char* directory, proc_t *restrict p) { dst += snprintf(dst, vMAX, "%s", (dst > dst_buffer) ? "," : ""); dst += escape_str(dst, grp, vMAX, &whackable_int); } - p->cgroup = vectorize_this_str(dst_buffer[0] ? dst_buffer : "-"); + p->cgroup = strdup(dst_buffer[0] ? dst_buffer : "-"); + + name = strstr(p->cgroup, ":name="); + if (name && *(name+6)) name += 6; else name = p->cgroup; + p->cgname = strdup(name); #undef vMAX } // This routine reads a 'cmdline' for the designated proc_t, "escapes" - // the result into a single string represented as a single vector - // and guarantees the caller a valid proc_t.cmdline pointer. + // the result into a single string while guaranteeing the caller a + // valid proc_t.cmdline pointer. static void fill_cmdline_cvt (const char* directory, proc_t *restrict p) { #define uFLG ( ESC_BRACKETS | ESC_DEFUNCT ) int whackable_int = MAX_BUFSZ; @@ -765,7 +756,7 @@ static void fill_cmdline_cvt (const char* directory, proc_t *restrict p) { escape_str(dst_buffer, src_buffer, MAX_BUFSZ, &whackable_int); else escape_command(dst_buffer, p, MAX_BUFSZ, &whackable_int, uFLG); - p->cmdline = vectorize_this_str(dst_buffer); + p->cmdline = strdup(dst_buffer[0] ? dst_buffer : "?"); #undef uFLG } @@ -777,7 +768,7 @@ static void fill_environ_cvt (const char* directory, proc_t *restrict p) { dst_buffer[0] = '\0'; if (read_unvectored(src_buffer, MAX_BUFSZ, directory, "environ", ' ')) escape_str(dst_buffer, src_buffer, MAX_BUFSZ, &whackable_int); - p->environ = vectorize_this_str(dst_buffer[0] ? dst_buffer : "-"); + p->environ = strdup(dst_buffer[0] ? dst_buffer : "-"); } @@ -924,29 +915,20 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons } } - if (flags & PROC_FILLENV) { // read /proc/#/environ - if (flags & PROC_EDITENVRCVT) - fill_environ_cvt(path, p); - else - p->environ = file2strvec(path, "environ"); - } else - p->environ = NULL; + if (flags & PROC_FILLENV) // read /proc/#/environ + p->environ_v = file2strvec(path, "environ"); + if (flags & PROC_EDITENVRCVT) + fill_environ_cvt(path, p); - if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/cmdline - if (flags & PROC_EDITCMDLCVT) - fill_cmdline_cvt(path, p); - else - p->cmdline = file2strvec(path, "cmdline"); - } else - p->cmdline = NULL; + if (flags & PROC_FILLARG) // read /proc/#/cmdline + p->cmdline_v = file2strvec(path, "cmdline"); + if (flags & PROC_EDITCMDLCVT) + fill_cmdline_cvt(path, p); - if ((flags & PROC_FILLCGROUP)) { // read /proc/#/cgroup - if (flags & PROC_EDITCGRPCVT) - fill_cgroup_cvt(path, p); - else - p->cgroup = file2strvec(path, "cgroup"); - } else - p->cgroup = NULL; + if ((flags & PROC_FILLCGROUP)) // read /proc/#/cgroup + p->cgroup_v = file2strvec(path, "cgroup"); + if (flags & PROC_EDITCGRPCVT) + fill_cgroup_cvt(path, p); if (flags & PROC_FILLOOM) { if (file2str(path, "oom_score", &ub) != -1) @@ -1046,29 +1028,20 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric if (flags & PROC_FILLSUPGRP) supgrps_from_supgids(t); #endif - if (flags & PROC_FILLENV) { // read /proc/#/task/#/environ - if (flags & PROC_EDITENVRCVT) - fill_environ_cvt(path, t); - else - t->environ = file2strvec(path, "environ"); - } else - t->environ = NULL; - - if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/task/#/cmdline - if (flags & PROC_EDITCMDLCVT) - fill_cmdline_cvt(path, t); - else - t->cmdline = file2strvec(path, "cmdline"); - } else - t->cmdline = NULL; - - if ((flags & PROC_FILLCGROUP)) { // read /proc/#/task/#/cgroup - if (flags & PROC_EDITCGRPCVT) - fill_cgroup_cvt(path, t); - else - t->cgroup = file2strvec(path, "cgroup"); - } else - t->cgroup = NULL; + if (flags & PROC_FILLENV) // read /proc/#/task/#/environ + t->environ_v = file2strvec(path, "environ"); + if (flags & PROC_EDITENVRCVT) + fill_environ_cvt(path, t); + + if (flags & PROC_FILLARG) // read /proc/#/task/#/cmdline + t->cmdline_v = file2strvec(path, "cmdline"); + if (flags & PROC_EDITCMDLCVT) + fill_cmdline_cvt(path, t); + + if ((flags & PROC_FILLCGROUP)) // read /proc/#/task/#/cgroup + t->cgroup_v = file2strvec(path, "cgroup"); + if (flags & PROC_EDITCGRPCVT) + fill_cgroup_cvt(path, t); if (flags & PROC_FILLSYSTEMD) // get sd-login.h stuff sd2proc(t); @@ -1078,26 +1051,30 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric #ifdef QUICK_THREADS } else { - t->size = p->size; - t->resident = p->resident; - t->share = p->share; - t->trs = p->trs; - t->lrs = p->lrs; - t->drs = p->drs; - t->dt = p->dt; - t->cmdline = p->cmdline; // better not free these until done with all threads! - t->environ = p->environ; - t->cgroup = p->cgroup; - t->supgid = p->supgid; - t->supgrp = p->supgrp; - t->sd_mach = p->sd_mach; - t->sd_ouid = p->sd_ouid; - t->sd_seat = p->sd_seat; - t->sd_sess = p->sd_sess; - t->sd_slice = p->sd_slice; - t->sd_unit = p->sd_unit; - t->sd_uunit = p->sd_uunit; - t->lxcname = p->lxcname; + t->size = p->size; + t->resident = p->resident; + t->share = p->share; + t->trs = p->trs; + t->lrs = p->lrs; + t->drs = p->drs; + t->dt = p->dt; + t->cmdline = p->cmdline; // better not free these until done with all threads! + t->cmdline_v = p->cmdline_v; + t->environ = p->environ; + t->environ_v = p->environ_v; + t->cgname = p->cgname; + t->cgroup = p->cgroup; + t->cgroup_v = p->cgroup_v; + t->supgid = p->supgid; + t->supgrp = p->supgrp; + t->sd_mach = p->sd_mach; + t->sd_ouid = p->sd_ouid; + t->sd_seat = p->sd_seat; + t->sd_sess = p->sd_sess; + t->sd_slice = p->sd_slice; + t->sd_unit = p->sd_unit; + t->sd_uunit = p->sd_uunit; + t->lxcname = p->lxcname; MK_THREAD(t); } #endif diff --git a/proc/readproc.h b/proc/readproc.h index ec51749e..65e771c6 100644 --- a/proc/readproc.h +++ b/proc/readproc.h @@ -124,11 +124,15 @@ typedef struct proc_t { cmin_flt, // stat cumulative min_flt of process and child processes cmaj_flt; // stat cumulative maj_flt of process and child processes char - **environ, // (special) environment string vector (/proc/#/environ) - **cmdline, // (special) command line string vector (/proc/#/cmdline) - **cgroup, // (special) cgroup string vector (/proc/#/cgroup) - *supgid, // status supplementary gids as comma delimited str - *supgrp; // supp grp names as comma delimited str, derived from supgid + *environ, // (special) environment as string (/proc/#/environ) + *cmdline, // (special) command line as string (/proc/#/cmdline) + *cgroup, // (special) cgroup as string (/proc/#/cgroup) + *cgname, // (special) name portion of above (if possible) + *supgid, // status supplementary gids as comma delimited str + *supgrp, // supp grp names as comma delimited str, derived from supgid + **environ_v, // (special) environment string vectors (/proc/#/environ) + **cmdline_v, // (special) command line string vectors (/proc/#/cmdline) + **cgroup_v; // (special) cgroup string vectors (/proc/#/cgroup) char *euser, // stat(),status effective user name *ruser, // status real user name @@ -213,14 +217,13 @@ typedef struct PROCTAB { // id's since uid_t supports no convenient termination sentinel.) #define PROC_FILLMEM 0x0001 // read statm -#define PROC_FILLCOM 0x0002 // alloc and fill in `cmdline' -#define PROC_FILLENV 0x0004 // alloc and fill in `environ' +#define PROC_FILLARG 0x0002 // alloc and fill in `cmdline' vectors +#define PROC_FILLENV 0x0004 // alloc and fill in `environ' vectors #define PROC_FILLUSR 0x0008 // resolve user id number -> user name #define PROC_FILLGRP 0x0010 // resolve group id number -> group name #define PROC_FILLSTATUS 0x0020 // read status #define PROC_FILLSTAT 0x0040 // read stat -#define PROC_FILLARG 0x0100 // alloc and fill in `cmdline' -#define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup` +#define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup` vectors #define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id -> group name #define PROC_FILLOOM 0x0800 // fill in proc_t oom_score and oom_adj #define PROC_FILLNS 0x8000 // fill in proc_t namespace information @@ -233,9 +236,9 @@ typedef struct PROCTAB { #define PROC_PID 0x1000 // process id numbers ( 0 terminated) #define PROC_UID 0x4000 // user id numbers ( length needed ) -#define PROC_EDITCGRPCVT 0x10000 // edit `cgroup' as single vector -#define PROC_EDITCMDLCVT 0x20000 // edit `cmdline' as single vector -#define PROC_EDITENVRCVT 0x40000 // edit `environ' as single vector +#define PROC_EDITCGRPCVT 0x10000 // edit `cgroup' as regular string +#define PROC_EDITCMDLCVT 0x20000 // edit `cmdline' as regular string +#define PROC_EDITENVRCVT 0x40000 // edit `environ' as regular string // it helps to give app code a few spare bits #define PROC_SPARE_1 0x01000000 -- 2.40.0