diff options
Diffstat (limited to 'src/libps')
-rw-r--r-- | src/libps/deps-lib/s6ps | 7 | ||||
-rw-r--r-- | src/libps/s6ps_grcache.c | 67 | ||||
-rw-r--r-- | src/libps/s6ps_otree.c | 100 | ||||
-rw-r--r-- | src/libps/s6ps_pfield.c | 561 | ||||
-rw-r--r-- | src/libps/s6ps_pwcache.c | 67 | ||||
-rw-r--r-- | src/libps/s6ps_statparse.c | 186 | ||||
-rw-r--r-- | src/libps/s6ps_ttycache.c | 138 | ||||
-rw-r--r-- | src/libps/s6ps_wchan.c | 96 |
8 files changed, 1222 insertions, 0 deletions
diff --git a/src/libps/deps-lib/s6ps b/src/libps/deps-lib/s6ps new file mode 100644 index 0000000..0f078a6 --- /dev/null +++ b/src/libps/deps-lib/s6ps @@ -0,0 +1,7 @@ +s6ps_grcache.o +s6ps_otree.o +s6ps_pfield.o +s6ps_pwcache.o +s6ps_statparse.o +s6ps_ttycache.o +s6ps_wchan.o diff --git a/src/libps/s6ps_grcache.c b/src/libps/s6ps_grcache.c new file mode 100644 index 0000000..189f5ae --- /dev/null +++ b/src/libps/s6ps_grcache.c @@ -0,0 +1,67 @@ +/* ISC license. */ + +#include <stdint.h> + +#include <grp.h> +#include <errno.h> +#include <skalibs/types.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/skamisc.h> +#include <skalibs/avltree.h> + +#include "s6-ps.h" + +static avltree grcache_tree = AVLTREE_ZERO ; +static genalloc grcache_index = GENALLOC_ZERO ; + +int s6ps_grcache_init (void) +{ + avltree_init(&grcache_tree, 5, 3, 8, &left_dtok, &uint32_cmp, &grcache_index) ; + return 1 ; +} + +void s6ps_grcache_finish (void) +{ + avltree_free(&grcache_tree) ; + genalloc_free(dius_t, &grcache_index) ; +} + +int s6ps_grcache_lookup (stralloc *sa, gid_t gid) +{ + int wasnull = !satmp.s ; + dius_t d = { .left = (uint32_t)gid, .right = satmp.len } ; + uint32_t i ; + if (!avltree_search(&grcache_tree, &d.left, &i)) + { + struct group *gr ; + unsigned int n = genalloc_len(dius_t, &grcache_index) ; + errno = 0 ; + gr = getgrgid(gid) ; + if (!gr) + { + if (errno) return 0 ; + if (!stralloc_readyplus(&satmp, UINT_FMT + 2)) return 0 ; + stralloc_catb(&satmp, "(", 1) ; + satmp.len += uint_fmt(satmp.s + satmp.len, gid) ; + stralloc_catb(&satmp, ")", 2) ; + } + else if (!stralloc_cats(&satmp, gr->gr_name) || !stralloc_0(&satmp)) return 0 ; + if (!genalloc_append(dius_t, &grcache_index, &d)) goto err ; + if (!avltree_insert(&grcache_tree, n)) + { + genalloc_setlen(dius_t, &grcache_index, n) ; + goto err ; + } + i = n ; + } + return stralloc_cats(sa, satmp.s + genalloc_s(dius_t, &grcache_index)[i].right) ; + err: + { + int e = errno ; + if (wasnull) stralloc_free(&satmp) ; + else satmp.len = d.right ; + errno = e ; + } + return 0 ; +} diff --git a/src/libps/s6ps_otree.c b/src/libps/s6ps_otree.c new file mode 100644 index 0000000..6ba8589 --- /dev/null +++ b/src/libps/s6ps_otree.c @@ -0,0 +1,100 @@ +/* ISC license. */ + +#include <errno.h> + +#include <skalibs/avltreen.h> + +#include "s6-ps.h" + +typedef struct ptreeiter_s ptreeiter_t, *ptreeiter_t_ref ; +struct ptreeiter_s +{ + unsigned int *childlist ; + unsigned int const *childindex ; + unsigned int const *ppindex ; + unsigned int *cpos ; +} ; + +typedef struct pstuff_s pstuff_t, *pstuff_t_ref ; +struct pstuff_s +{ + unsigned int *orderedlist ; + pscan_t *p ; + unsigned int const *childlist ; + unsigned int const *childindex ; + unsigned int const *nchild ; +} ; + +static int fillchildlist (unsigned int i, unsigned int h, void *x) +{ + ptreeiter_t *pt = x ; + unsigned int j = pt->ppindex[i] ; + pt->childlist[pt->childindex[j] + pt->cpos[j]++] = i ; + (void)h ; + return 1 ; +} + +static void fillo_tree_rec (pstuff_t *blah, unsigned int root, signed int h) +{ + static unsigned int j = 0 ; + unsigned int i = !blah->p[root].pid ; + if (blah->p[root].pid == 1) h = -1 ; + blah->p[root].height = (h > 0) ? h : 0 ; + blah->orderedlist[j++] = root ; + for (; i < blah->nchild[root] ; i++) + fillo_tree_rec(blah, blah->childlist[blah->childindex[root] + i], h+1) ; +} + + /* + Fills up orderedlist with the right indices to print a process tree. + O(n log n) time, O(n) space, all in the stack. + */ + +void s6ps_otree (pscan_t *p, unsigned int n, avltreen *pidtree, unsigned int *orderedlist) +{ + unsigned int childlist[n] ; + unsigned int childindex[n] ; + unsigned int nchild[n] ; + unsigned int i = 0 ; + for (; i < n ; i++) nchild[i] = 0 ; + + /* Compute the ppid tree */ + for (i = 0 ; i < n ; i++) + { + uint32_t k ; + if (!avltreen_search(pidtree, &p[i].ppid, &k)) k = n-1 ; + orderedlist[i] = k ; /* using orderedlist as ppindex */ + nchild[k]++ ; + } + { + unsigned int j = 0 ; + for (i = 0 ; i < n ; i++) + { + childindex[i] = j ; + j += nchild[i] ; + } + } + + /* Fill the childlist by increasing pids so it is sorted */ + { + unsigned int cpos[n] ; + ptreeiter_t blah = { .childlist = childlist, .childindex = childindex, .ppindex = orderedlist, .cpos = cpos } ; + for (i = 0 ; i < n ; i++) cpos[i] = 0 ; + avltreen_iter_nocancel(pidtree, avltreen_totalsize(pidtree), &fillchildlist, &blah) ; + } + + /* If we have init, make it the last in the orphan list */ + if (n > 1 && p[childlist[childindex[n-1]+1]].pid == 1) + { + unsigned int pos1 = childlist[childindex[n-1] + 1] ; + for (i = 2 ; i < nchild[n-1] ; i++) + childlist[childindex[n-1]+i-1] = childlist[childindex[n-1]+i] ; + childlist[childindex[n-1]+nchild[n-1]-1] = pos1 ; + } + + /* Finally, fill orderedlist by walking the childindex tree. */ + { + pstuff_t blah = { .orderedlist = orderedlist, .p = p, .childlist = childlist, .childindex = childindex, .nchild = nchild } ; + fillo_tree_rec(&blah, n-1, -1) ; + } +} diff --git a/src/libps/s6ps_pfield.c b/src/libps/s6ps_pfield.c new file mode 100644 index 0000000..9e53e50 --- /dev/null +++ b/src/libps/s6ps_pfield.c @@ -0,0 +1,561 @@ +/* ISC license. */ + +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <time.h> +#include <sys/sysinfo.h> + +#include <skalibs/uint64.h> +#include <skalibs/types.h> +#include <skalibs/strerr.h> +#include <skalibs/tai.h> +#include <skalibs/djbtime.h> +#include <skalibs/stralloc.h> + +#include "s6-ps.h" + +static char const *const fieldheaders[PFIELD_PHAIL] = +{ + "PID", + "COMM", + "STAT", + "PPID", + "PGRP", + "SESSION", + "TTY", + "TPGID", + "UTIME", + "STIME", + "CUTIME", + "CSTIME", + "PRIO", + "NICE", + "THREADS", + "START", + "VSZ", + "RSS", + "RSSLIM", + "CPU", + "RTPRIO", + "RTPOLICY", + "USER", + "GROUP", + "%MEM", + "WCHAN", + "COMMAND", + "ENVIRONMENT", + "%CPU", + "TTIME", + "CTTIME", + "TSTART", + "C%CPU" +} ; + +char const *const *s6ps_fieldheaders = fieldheaders ; + +static char const *const opttable[PFIELD_PHAIL] = +{ + "pid", + "comm", + "s", + "ppid", + "pgrp", + "sess", + "tty", + "tpgid", + "utime", + "stime", + "cutime", + "cstime", + "prio", + "nice", + "thcount", + "start", + "vsize", + "rss", + "rsslimit", + "psr", + "rtprio", + "policy", + "user", + "group", + "pmem", + "wchan", + "args", + "env", + "pcpu", + "ttime", + "cttime", + "tstart", + "cpcpu" +} ; + +char const *const *s6ps_opttable = opttable ; + +static tain boottime = TAIN_EPOCH ; + +static int fmt_64 (pscan_t *p, size_t *pos, size_t *len, uint64_t u) +{ + if (!stralloc_readyplus(&p->data, UINT64_FMT)) return 0 ; + *pos = p->data.len ; + *len = uint64_fmt(p->data.s + *pos, u) ; + p->data.len += *len ; + return 1 ; +} + +static int fmt_i (pscan_t *p, size_t *pos, size_t *len, int d) +{ + if (!stralloc_readyplus(&p->data, UINT32_FMT+1)) return 0 ; + *pos = p->data.len ; + *len = int_fmt(p->data.s + *pos, d) ; + p->data.len += *len ; + return 1 ; +} + +static int fmt_pid (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->pid) ; +} + +static int fmt_comm (pscan_t *p, size_t *pos, size_t *len) +{ + *pos = p->statlen ; + *len = p->commlen ; + return 1 ; +} + +static int fmt_s (pscan_t *p, size_t *pos, size_t *len) +{ + if (!stralloc_readyplus(&p->data, 4)) return 0 ; + *pos = p->data.len ; + p->data.s[p->data.len++] = p->data.s[p->state] ; + if (p->pid == p->session) p->data.s[p->data.len++] = 's' ; + if (p->threads > 1) p->data.s[p->data.len++] = 'l' ; + if ((p->tpgid > 0) && ((unsigned int)p->tpgid == p->pgrp)) + p->data.s[p->data.len++] = '+' ; + if (p->nice) p->data.s[p->data.len++] = (p->nice < 0) ? '<' : 'N' ; + + *len = p->data.len - *pos ; + return 1 ; +} + +static int fmt_ppid (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->ppid) ; +} + +static int fmt_pgrp (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->pgrp) ; +} + +static int fmt_session (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->session) ; +} + +static int fmt_ttynr(pscan_t *p, size_t *pos, size_t *len) +{ + if (p->ttynr) + { + size_t tmppos = p->data.len ; + if (!s6ps_ttycache_lookup(&p->data, p->ttynr)) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + } + else + { + if (!stralloc_catb(&p->data, "-", 1)) return 0 ; + *pos = p->data.len - 1 ; + *len = 1 ; + } + return 1 ; +} + +static int fmt_tpgid (pscan_t *p, size_t *pos, size_t *len) +{ + return p->tpgid < 0 ? fmt_i(p, pos, len, -1) : fmt_64(p, pos, len, p->tpgid) ; +} + +static unsigned int gethz (void) +{ + static unsigned int hz = 0 ; + if (!hz) + { + long jiffies = sysconf(_SC_CLK_TCK) ; + if (jiffies < 1) + { + char fmt[ULONG_FMT + 1] ; + fmt[long_fmt(fmt, jiffies)] = 0 ; + strerr_warnw3x("invalid _SC_CLK_TCK value (", fmt, "), using 100") ; + hz = 100 ; + } + else hz = (unsigned int)jiffies ; + } + return hz ; +} + +int s6ps_compute_boottime (pscan_t *p, unsigned int mypos) +{ + if (!mypos--) + { + strerr_warnwu1x("compute boot time - using epoch") ; + return 0 ; + } + else + { + unsigned int hz = gethz() ; + tain offset = { .sec = { .x = p[mypos].start / hz }, .nano = (p[mypos].start % hz) * (1000000000 / hz) } ; + tain_sub(&boottime, &STAMP, &offset) ; + return 1 ; + } +} + +static int fmt_jiffies (pscan_t *p, size_t *pos, size_t *len, uint64_t j) +{ + unsigned int hz = gethz() ; + uint32_t hrs, mins, secs, hfrac ; + if (!stralloc_readyplus(&p->data, UINT64_FMT + 13)) return 0 ; + hfrac = (j % hz) * 100 / hz ; + *pos = p->data.len ; + j /= hz ; + secs = j % 60 ; j /= 60 ; + mins = j % 60 ; j /= 60 ; + hrs = j % 24 ; j /= 24 ; + if (j) + { + p->data.len += uint64_fmt(p->data.s + p->data.len, j) ; + p->data.s[p->data.len++] = 'd' ; + } + if (j || hrs) + { + uint320_fmt(p->data.s + p->data.len, hrs, 2) ; + p->data.len += 2 ; + p->data.s[p->data.len++] = 'h' ; + } + if (j || hrs || mins) + { + uint320_fmt(p->data.s + p->data.len, mins, 2) ; + p->data.len += 2 ; + p->data.s[p->data.len++] = 'm' ; + } + uint320_fmt(p->data.s + p->data.len, secs, 2) ; + p->data.len += 2 ; + if (!j && !hrs && !mins) + { + p->data.s[p->data.len++] = '.' ; + uint320_fmt(p->data.s + p->data.len, hfrac, 2) ; + p->data.len += 2 ; + } + p->data.s[p->data.len++] = 's' ; + *len = p->data.len - *pos ; + return 1 ; +} + +static int fmt_utime (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_jiffies(p, pos, len, p->utime) ; +} + +static int fmt_stime (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_jiffies(p, pos, len, p->stime) ; +} + +static int fmt_cutime (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_jiffies(p, pos, len, p->utime + p->cutime) ; +} + +static int fmt_cstime (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_jiffies(p, pos, len, p->stime + p->cstime) ; +} + +static int fmt_prio (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_i(p, pos, len, p->prio) ; +} + +static int fmt_nice (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_i(p, pos, len, p->nice) ; +} + +static int fmt_threads (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->threads) ; +} + +static int fmt_timedate (pscan_t *p, size_t *pos, size_t *len, struct tm const *tm) +{ + static struct tm nowtm = { .tm_year = 0 } ; + size_t tmplen ; + char *tmpstrf = "%F" ; + if (!nowtm.tm_year && !localtm_from_tai(&nowtm, tain_secp(&STAMP), 1)) return 0 ; + if (!stralloc_readyplus(&p->data, 20)) return 0 ; + if (tm->tm_year == nowtm.tm_year && tm->tm_yday == nowtm.tm_yday) + tmpstrf = "%T" ; + else if (tm->tm_year == nowtm.tm_year || (tm->tm_year+1 == nowtm.tm_year && (nowtm.tm_mon + 12 - tm->tm_mon) % 12 < 9)) + tmpstrf = "%b%d %R" ; + tmplen = strftime(p->data.s + p->data.len, 20, tmpstrf, tm) ; + if (!tmplen) return 0 ; + *len = tmplen ; + *pos = p->data.len ; + p->data.len += tmplen ; + return 1 ; +} + +static int fmt_start (pscan_t *p, size_t *pos, size_t *len) +{ + struct tm starttm ; + unsigned int hz = gethz() ; + tain blah = { .sec = { .x = p->start / hz }, .nano = (p->start % hz) * (1000000000 / hz) } ; + tain_add(&blah, &boottime, &blah) ; + if (!localtm_from_tai(&starttm, tain_secp(&blah), 1)) return 0 ; + return fmt_timedate(p, pos, len, &starttm) ; +} + +static unsigned int getpgsz (void) +{ + static unsigned int pgsz = 0 ; + if (!pgsz) + { + long sz = sysconf(_SC_PAGESIZE) ; + if (sz < 1) + { + char fmt[ULONG_FMT + 1] ; + fmt[long_fmt(fmt, sz)] = 0 ; + strerr_warnw3x("invalid _SC_PAGESIZE value (", fmt, "), using 4096") ; + pgsz = 4096 ; + } + else pgsz = sz ; + } + return pgsz ; +} + +static int fmt_vsize (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->vsize / 1024) ; +} + +static int fmt_rss (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->rss * (getpgsz() / 1024)) ; +} + +static int fmt_rsslim (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->rsslim / 1024) ; +} + +static int fmt_cpuno (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->cpuno) ; +} + +static int fmt_rtprio (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_64(p, pos, len, p->rtprio) ; +} + +static int fmt_policy (pscan_t *p, size_t *pos, size_t *len) +{ + static char const *const policies[8] = { "NORMAL", "FIFO", "RR", "BATCH", "ISO", "IDLE", "UNKNOWN", "UNKNOWN" } ; + size_t tmppos = p->data.len ; + if (!stralloc_cats(&p->data, policies[p->policy & 7])) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + return 1 ; +} + +static int fmt_user (pscan_t *p, size_t *pos, size_t *len) +{ + size_t tmppos = p->data.len ; + if (!s6ps_pwcache_lookup(&p->data, p->uid)) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + return 1 ; +} + +static int fmt_group (pscan_t *p, size_t *pos, size_t *len) +{ + size_t tmppos = p->data.len ; + if (!s6ps_grcache_lookup(&p->data, p->gid)) return 0 ; + *pos = tmppos ; + *len = p->data.len - tmppos ; + return 1 ; +} + +static struct sysinfo si = { .totalram = 0, .loads = { 0, 0, 0 } } ; + +static uint64_t gettotalmem (void) +{ + uint64_t totalmem = 0 ; + if (!si.totalram && (sysinfo(&si) < 0)) return 0 ; + totalmem = si.totalram ; + totalmem *= si.mem_unit ; + return totalmem ; +} + +static int percent (stralloc *sa, unsigned int n, size_t *pos, size_t *len) +{ + if (!stralloc_readyplus(sa, UINT64_FMT+1)) return 0 ; + *pos = sa->len ; + sa->len += uint_fmt(sa->s + sa->len, n / 100) ; + sa->s[sa->len++] = '.' ; + uint0_fmt(sa->s + sa->len, n % 100, 2) ; + sa->len += 2 ; + *len = sa->len - *pos ; + return 1 ; +} + +static int fmt_pmem (pscan_t *p, size_t *pos, size_t *len) +{ + uint64 l = gettotalmem() ; + return l ? percent(&p->data, p->rss * getpgsz() * 10000 / l, pos, len) : 0 ; +} + +static int fmt_wchan (pscan_t *p, size_t *pos, size_t *len) +{ + size_t tmppos = p->data.len ; + if (!s6ps_wchan_lookup(&p->data, p->wchan)) return 0 ; + *len = p->data.len - tmppos ; + *pos = tmppos ; + return 1 ; +} + +static int fmt_args (pscan_t *p, size_t *pos, size_t *len) +{ + if (!stralloc_readyplus(&p->data, (p->height << 2) + (p->cmdlen ? p->cmdlen : p->commlen + (p->data.s[p->state] == 'Z' ? 11 : 3)))) + return 0 ; + *pos = p->data.len ; + if (p->height) + { + unsigned int i = 0 ; + for (; i < 4 * (unsigned int)p->height - 3 ; i++) + p->data.s[p->data.len + i] = ' ' ; + memcpy(p->data.s + p->data.len + 4 * p->height - 3, "\\_ ", 3) ; + p->data.len += p->height << 2 ; + } + if (p->cmdlen) + { + char const *r = p->data.s + p->statlen + p->commlen ; + char *w = p->data.s + p->data.len ; + size_t i = p->cmdlen ; + while (i--) + { + char c = *r++ ; + *w++ = c ? c : ' ' ; + } + p->data.len += p->cmdlen ; + } + else if (p->data.s[p->state] == 'Z') + { + stralloc_catb(&p->data, p->data.s + uint32_fmt(0, p->pid) + 2, p->commlen) ; + stralloc_catb(&p->data, " <defunct>", 10) ; + } + else + stralloc_catb(&p->data, p->data.s + uint32_fmt(0, p->pid) + 1, p->commlen+2) ; + *len = p->data.len - *pos ; + return 1 ; +} + +static int fmt_env (pscan_t *p, size_t *pos, size_t *len) +{ + size_t i = 0 ; + if (!p->envlen) + { + if (!stralloc_catb(&p->data, "*", 1)) return 0 ; + *pos = p->data.len - 1 ; + *len = 1 ; + return 1 ; + } + *pos = p->statlen + p->commlen + p->cmdlen ; + *len = p->envlen ; + for (; i < *len ; i++) + if (!p->data.s[*pos + i]) p->data.s[*pos + i] = ' ' ; + return 1 ; +} + +static uint64_t gettotalj (uint64_t j) +{ + tain totaltime ; + unsigned int hz = gethz() ; + tain_sub(&totaltime, &STAMP, &boottime) ; + j = totaltime.sec.x * hz + totaltime.nano / (1000000000 / hz) - j ; + if (!j) j = 1 ; + return j ; +} + +static int fmt_pcpu (pscan_t *p, size_t *pos, size_t *len) +{ + return percent(&p->data, 10000 * (p->utime + p->stime) / gettotalj(p->start), pos, len) ; +} + +static int fmt_ttime (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_jiffies(p, pos, len, p->utime + p->stime) ; +} + +static int fmt_cttime (pscan_t *p, size_t *pos, size_t *len) +{ + return fmt_jiffies(p, pos, len, p->utime + p->stime + p->cutime + p->cstime) ; +} + +static int fmt_tstart (pscan_t *p, size_t *pos, size_t *len) +{ + unsigned int hz = gethz() ; + tain blah = { .sec = { .x = p->start / hz }, .nano = (p->start % hz) * (1000000000 / hz) } ; + if (!stralloc_readyplus(&p->data, TIMESTAMP)) return 0 ; + tain_add(&blah, &boottime, &blah) ; + *pos = p->data.len ; + *len = timestamp_fmt(p->data.s + p->data.len, &blah) ; + p->data.len += *len ; + return 1 ; +} + +static int fmt_cpcpu (pscan_t *p, size_t *pos, size_t *len) +{ + return percent(&p->data, 10000 * (p->utime + p->stime + p->cutime + p->cstime) / gettotalj(p->start), pos, len) ; +} + +static pfieldfmt_func_ref pfieldfmt_table[PFIELD_PHAIL] = +{ + &fmt_pid, + &fmt_comm, + &fmt_s, + &fmt_ppid, + &fmt_pgrp, + &fmt_session, + &fmt_ttynr, + &fmt_tpgid, + &fmt_utime, + &fmt_stime, + &fmt_cutime, + &fmt_cstime, + &fmt_prio, + &fmt_nice, + &fmt_threads, + &fmt_start, + &fmt_vsize, + &fmt_rss, + &fmt_rsslim, + &fmt_cpuno, + &fmt_rtprio, + &fmt_policy, + &fmt_user, + &fmt_group, + &fmt_pmem, + &fmt_wchan, + &fmt_args, + &fmt_env, + &fmt_pcpu, + &fmt_ttime, + &fmt_cttime, + &fmt_tstart, + &fmt_cpcpu +} ; + +pfieldfmt_func_ref *s6ps_pfield_fmt = pfieldfmt_table ; diff --git a/src/libps/s6ps_pwcache.c b/src/libps/s6ps_pwcache.c new file mode 100644 index 0000000..ce4caa8 --- /dev/null +++ b/src/libps/s6ps_pwcache.c @@ -0,0 +1,67 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <stdint.h> +#include <pwd.h> +#include <errno.h> + +#include <skalibs/types.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/skamisc.h> +#include <skalibs/avltree.h> +#include "s6-ps.h" + +static avltree pwcache_tree = AVLTREE_ZERO ; +static genalloc pwcache_index = GENALLOC_ZERO ; + +int s6ps_pwcache_init (void) +{ + avltree_init(&pwcache_tree, 5, 3, 8, &left_dtok, &uint32_cmp, &pwcache_index) ; + return 1 ; +} + +void s6ps_pwcache_finish (void) +{ + avltree_free(&pwcache_tree) ; + genalloc_free(dius_t, &pwcache_index) ; +} + +int s6ps_pwcache_lookup (stralloc *sa, uid_t uid) +{ + int wasnull = !satmp.s ; + dius_t d = { .left = (uint32_t)uid, .right = satmp.len } ; + uint32_t i ; + if (!avltree_search(&pwcache_tree, &d.left, &i)) + { + struct passwd *pw ; + unsigned int n = genalloc_len(dius_t, &pwcache_index) ; + errno = 0 ; + pw = getpwuid(uid) ; + if (!pw) + { + if (errno) return 0 ; + if (!stralloc_readyplus(&satmp, UINT_FMT + 2)) return 0 ; + stralloc_catb(&satmp, "(", 1) ; + satmp.len += uint_fmt(satmp.s + satmp.len, uid) ; + stralloc_catb(&satmp, ")", 2) ; + } + else if (!stralloc_cats(&satmp, pw->pw_name) || !stralloc_0(&satmp)) return 0 ; + if (!genalloc_append(dius_t, &pwcache_index, &d)) goto err ; + if (!avltree_insert(&pwcache_tree, n)) + { + genalloc_setlen(dius_t, &pwcache_index, n) ; + goto err ; + } + i = n ; + } + return stralloc_cats(sa, satmp.s + genalloc_s(dius_t, &pwcache_index)[i].right) ; + err: + { + int e = errno ; + if (wasnull) stralloc_free(&satmp) ; + else satmp.len = d.right ; + errno = e ; + } + return 0 ; +} diff --git a/src/libps/s6ps_statparse.c b/src/libps/s6ps_statparse.c new file mode 100644 index 0000000..ea46d70 --- /dev/null +++ b/src/libps/s6ps_statparse.c @@ -0,0 +1,186 @@ +/* ISC license. */ + +#include <stdint.h> +#include <sys/types.h> +#include <errno.h> + +#include <skalibs/uint64.h> +#include <skalibs/types.h> +#include <skalibs/stralloc.h> +#include <skalibs/tai.h> + +#include "s6-ps.h" + + + /* + going to great lengths to avoid scanf(), but all this code + is still smaller than scanf (no floating point parsing etc.) + */ + +#define STATVARS 49 + +typedef size_t pscan_func (char const *, void *) ; +typedef pscan_func *pscan_func_ref ; + +static size_t f64 (char const *s, void *u64) +{ + uint64_t *u = u64 ; + return uint64_scan(s, u) ; +} + +#define DEFUNU(name, type) \ +static size_t name (char const *s, void *p) \ +{ \ + uint64_t u ; \ + size_t len = uint64_scan(s, &u) ; \ + *(type *)p = u ; \ + return len ; \ +} \ + +#define DEFUNS(name, type) \ +static size_t name (char const *s, void *p) \ +{ \ + int64_t d ; \ + size_t len = int64_scan(s, &d) ; \ + *(type *)p = d ; \ + return len ; \ +} \ + +DEFUNS(fint, int) +DEFUNS(fpid, pid_t) +DEFUNU(fdev, dev_t) + +static pscan_func_ref scanfuncs[STATVARS] = +{ + &fpid, /* ppid */ + &fpid, /* pgrp */ + &fpid, /* session */ + &fdev, /* tty_nr */ + &fpid, /* tpgid */ + &f64, /* flags */ + &f64, /* minflt */ + &f64, /* cminflt */ + &f64, /* majflt */ + &f64, /* cmajflt */ + &f64, /* utime */ + &f64, /* stime */ + &f64, /* cutime */ + &f64, /* cstime */ + &fint, /* priority */ + &fint, /* nice */ + &f64, /* num_threads */ + &f64, /* itrealvalue */ + &f64, /* starttime */ + &f64, /* vsize */ + &f64, /* rss */ + &f64, /* rsslim */ + &f64, /* startcode */ + &f64, /* endcode */ + &f64, /* startstack */ + &f64, /* kstkesp */ + &f64, /* kstkeip */ + &f64, /* signal */ + &f64, /* blocked */ + &f64, /* sigignore */ + &f64, /* sigcatch */ + &f64, /* wchan */ + &f64, /* nswap */ + &f64, /* cnswap */ + &fint, /* exit_signal */ + &f64, /* processor */ + &f64, /* rt_priority */ + &f64, /* policy */ + &f64, /* delayacct_blkio_ticks */ + &f64, /* guest_time */ + &f64, /* cguest_time */ + &f64, /* start_data */ + &f64, /* end_data */ + &f64, /* start_brk */ + &f64, /* arg_start */ + &f64, /* arg_end */ + &f64, /* env_start */ + &f64, /* env_end */ + &fint /* exit_code */ +} ; + +int s6ps_statparse (pscan_t *p) +{ + uint64_t dummy64 ; + int dummyint ; + size_t pos = 0 ; + void *scanresults[STATVARS] = + { + &p->ppid, + &p->pgrp, + &p->session, + &p->ttynr, + &p->tpgid, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &p->utime, + &p->stime, + &p->cutime, + &p->cstime, + &p->prio, + &p->nice, + &p->threads, + &dummy64, + &p->start, + &p->vsize, + &p->rss, + &p->rsslim, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &p->wchan, + &dummy64, + &dummy64, + &dummy64, + &p->cpuno, + &p->rtprio, + &p->policy, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummy64, + &dummyint + } ; + unsigned int i = 0 ; + + if (!p->statlen) return 0 ; + pos = uint64_scan(p->data.s, &dummy64) ; + if (!pos) return 0 ; + if (dummy64 != p->pid) return 0 ; + if (pos + 5 + p->commlen > p->statlen) return 0 ; + if (p->data.s[pos++] != ' ') return 0 ; + if (p->data.s[pos++] != '(') return 0 ; + pos += p->commlen ; + if (p->data.s[pos++] != ')') return 0 ; + if (p->data.s[pos++] != ' ') return 0 ; + p->state = pos++ ; + for (; i < STATVARS ; i++) + { + size_t w ; + if (pos + 1 > p->statlen) return 0 ; + if (p->data.s[pos++] != ' ') return 0 ; + w = (*scanfuncs[i])(p->data.s + pos, scanresults[i]) ; + if (!w) return 0 ; + pos += w ; + } + return 1 ; +} diff --git a/src/libps/s6ps_ttycache.c b/src/libps/s6ps_ttycache.c new file mode 100644 index 0000000..27282e7 --- /dev/null +++ b/src/libps/s6ps_ttycache.c @@ -0,0 +1,138 @@ +/* ISC license. */ + +#include <string.h> +#include <stdint.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <errno.h> + +#include <skalibs/types.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/buffer.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/skamisc.h> +#include <skalibs/avltree.h> + +#include "s6-ps.h" + +static avltree ttycache_tree = AVLTREE_ZERO ; +static genalloc ttycache_index = GENALLOC_ZERO ; + +int s6ps_ttycache_init (void) +{ + avltree_init(&ttycache_tree, 5, 3, 8, &left_dtok, &uint32_cmp, &ttycache_index) ; + return 1 ; +} + +void s6ps_ttycache_finish (void) +{ + avltree_free(&ttycache_tree) ; + genalloc_free(dius_t, &ttycache_index) ; +} + +static int check (char const *s, dev_t ttynr) +{ + struct stat st ; + if (stat(s, &st) < 0) return 0 ; + return S_ISCHR(st.st_mode) && (st.st_rdev == ttynr) ; +} + + + /* No blind scanning of all /dev or /sys/devices, kthx */ + +static int ttyguess (stralloc *sa, dev_t ttynr) +{ + unsigned int maj = major(ttynr), min = minor(ttynr) ; + + /* Try /dev/tty? and /dev/pts/? */ + if (maj == 4 && min < 64) + { + char tmp[11] = "/dev/tty" ; + tmp[uint_fmt(tmp+8, min)] = 0 ; + if (check(tmp, ttynr)) return stralloc_cats(sa, tmp+5) && stralloc_0(sa) ; + } + else if (maj >= 136 && maj < 144) + { + unsigned int n = ((maj - 136) << 20) | min ; + char tmp[9 + UINT_FMT] = "/dev/pts/" ; + tmp[9 + uint_fmt(tmp+9, n)] = 0 ; + if (check(tmp, ttynr)) return stralloc_cats(sa, tmp+5) && stralloc_0(sa) ; + } + + /* Use /sys/dev/char/maj:min if it exists */ + { + int fd ; + size_t pos = 14 ; + char path[23 + 2 * UINT_FMT] = "/sys/dev/char/" ; + pos += uint_fmt(path + pos, maj) ; + path[pos++] = ':' ; + pos += uint_fmt(path + pos, min) ; + memcpy(path + pos, "/uevent", 8) ; + fd = open_read(path) ; + if (fd >= 0) + { + char buf[4097] ; + buffer b = BUFFER_INIT(&fd_readv, fd, buf, 4097) ; + size_t start = satmp.len ; + int r ; + for (;;) + { + satmp.len = start ; + r = skagetln(&b, &satmp, '\n') ; + if (r <= 0) break ; + if ((satmp.len - start) > 8 && !memcmp(satmp.s + start, "DEVNAME=", 8)) break ; + } + fd_close(fd) ; + if (r > 0) + { + satmp.s[satmp.len - 1] = 0 ; + satmp.len = start ; + memcpy(satmp.s + start + 3, "/dev/", 5) ; + if (check(satmp.s + start + 3, ttynr)) + return stralloc_cats(sa, satmp.s + start + 8) && stralloc_0(sa) ; + } + } + } + + /* Fallback: print explicit maj:min */ + { + size_t pos = 1 ; + char tmp[3 + 2 * UINT_FMT] = "(" ; + pos += uint_fmt(tmp + pos, maj) ; + tmp[pos++] = ':' ; + pos += uint_fmt(tmp + pos, min) ; + tmp[pos++] = ')' ; + tmp[pos++] = 0 ; + return stralloc_catb(sa, tmp, pos) ; + } +} + +int s6ps_ttycache_lookup (stralloc *sa, dev_t ttynr) +{ + int wasnull = !satmp.s ; + dius_t d = { .left = (uint32_t)ttynr, .right = satmp.len } ; + uint32_t i ; + if (!avltree_search(&ttycache_tree, &d.left, &i)) + { + unsigned int n = genalloc_len(dius_t, &ttycache_index) ; + if (!ttyguess(&satmp, ttynr)) return 0 ; + if (!genalloc_append(dius_t, &ttycache_index, &d)) goto err ; + if (!avltree_insert(&ttycache_tree, n)) + { + genalloc_setlen(dius_t, &ttycache_index, n) ; + goto err ; + } + i = n ; + } + return stralloc_cats(sa, satmp.s + genalloc_s(dius_t, &ttycache_index)[i].right) ; + err: + { + int e = errno ; + if (wasnull) stralloc_free(&satmp) ; + else satmp.len = d.right ; + errno = e ; + } + return 0 ; +} diff --git a/src/libps/s6ps_wchan.c b/src/libps/s6ps_wchan.c new file mode 100644 index 0000000..9261693 --- /dev/null +++ b/src/libps/s6ps_wchan.c @@ -0,0 +1,96 @@ +/* ISC license. */ + +#include <string.h> +#include <sys/utsname.h> + +#include <skalibs/uint64.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/djbunix.h> + +#include "s6-ps.h" + +static stralloc sysmap = STRALLOC_ZERO ; +static genalloc ind = GENALLOC_ZERO ; + +int s6ps_wchan_init (char const *file) +{ + if (file) + { + if (!openslurpclose(&sysmap, file)) return 0 ; + } + else + { + char *files[3] = { "/proc/kallsyms", 0, "/boot/System.map" } ; + struct utsname uts ; + size_t n ; + if (uname(&uts) < 0) return 0 ; + n = strlen(uts.release) ; + { + char buf[18 + n] ; + unsigned int i = 0 ; + memcpy(buf, "/boot/System.map", 16) ; + buf[16] = '-' ; + memcpy(buf + 17, uts.release, n + 1) ; + files[1] = buf ; + for (; i < 3 ; i++) + if (openslurpclose(&sysmap, files[i])) break ; + if (i >= 3) return 0 ; + } + } + { + size_t i = 0 ; + if (!genalloc_append(size_t, &ind, &i)) goto err2 ; + for (i = 1 ; i <= sysmap.len ; i++) + if (sysmap.s[i-1] == '\n') + if (!genalloc_append(size_t, &ind, &i)) goto err ; + } + return 1 ; + err: + genalloc_free(size_t, &ind) ; + err2: + stralloc_free(&sysmap) ; + return 0 ; +} + +void s6ps_wchan_finish (void) +{ + genalloc_free(size_t, &ind) ; + stralloc_free(&sysmap) ; +} + +static inline size_t lookup (uint64_t addr, size_t *i) +{ + size_t low = 0, mid, high = genalloc_len(size_t, &ind), len ; + for (;;) + { + uint64_t cur ; + mid = (low + high) >> 1 ; + len = uint64_xscan(sysmap.s + genalloc_s(size_t, &ind)[mid], &cur) ; + if (!len) return 0 ; + if (cur == addr) break ; + if (mid == low) return 0 ; + if (addr < cur) high = mid ; else low = mid ; + } + *i = mid ; + return len ; +} + +int s6ps_wchan_lookup (stralloc *sa, uint64_t addr) +{ + if (addr == (sizeof(void *) == 8 ? 0xffffffffffffffffULL : 0xffffffffUL)) + return stralloc_catb(sa, "*", 1) ; + if (!addr) return stralloc_catb(sa, "-", 1) ; + if (sysmap.len) + { + size_t i, pos, len = lookup(addr, &i) ; + if (!len) return stralloc_catb(sa, "?", 1) ; + pos = genalloc_s(size_t, &ind)[i] + len + 3 ; + return stralloc_catb(sa, sysmap.s + pos, genalloc_s(size_t, &ind)[i+1] - 1 - pos) ; + } + if (!stralloc_readyplus(sa, UINT64_FMT + 3)) return 0 ; + stralloc_catb(sa, "(0x", 3) ; + sa->len += uint64_fmt(sa->s + sa->len, addr) ; + stralloc_catb(sa, ")", 1) ; + return 1 ; +} |