2af1e46eab
__CONCAT("foo","bar"); actually works to concantate strings, it's because the preprocessor expands it into "foo""bar" as separate strings, and then ANSI string concatenation is performed on that. It's more straightforward to just use ANSI string concatenation directly, and newer GCCs complain (rightly) about misuse of token pasting.
409 lines
9.8 KiB
C
409 lines
9.8 KiB
C
/* $NetBSD: ps.c,v 1.18 2000/12/20 01:17:49 cgd Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1999
|
|
* The NetBSD Foundation, Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD Foundation.
|
|
* 4. Neither the name of the Foundation nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* XXX Notes XXX
|
|
* showps -- print data needed at each refresh
|
|
* fetchps -- get data used by mode (done at each refresh)
|
|
* labelps -- print labels (ie info not needing refreshing)
|
|
* initps -- prepare once-only data structures for mode
|
|
* openps -- prepare per-run information for mode, return window
|
|
* closeps -- close mode to prepare to switch modes
|
|
* cmdps -- optional, handle commands
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
__RCSID("$NetBSD: ps.c,v 1.18 2000/12/20 01:17:49 cgd Exp $");
|
|
#endif /* not lint */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/sched.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/user.h>
|
|
|
|
#include <curses.h>
|
|
#include <math.h>
|
|
#include <pwd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <tzfile.h>
|
|
#include <unistd.h>
|
|
|
|
#include "extern.h"
|
|
#include "systat.h"
|
|
#include "ps.h"
|
|
|
|
int compare_pctcpu_noidle(const void *, const void *);
|
|
char *state2str(struct kinfo_proc *);
|
|
char *tty2str(struct kinfo_proc *);
|
|
int rss2int(struct kinfo_proc *);
|
|
int vsz2int(struct kinfo_proc *);
|
|
char *comm2str(struct kinfo_proc *);
|
|
double pmem2float(struct kinfo_proc *);
|
|
char *start2str(struct kinfo_proc *);
|
|
char *time2str(struct kinfo_proc *);
|
|
|
|
static time_t now;
|
|
|
|
#define SHOWUSER_ANY (uid_t)-1
|
|
static uid_t showuser = SHOWUSER_ANY;
|
|
|
|
#ifndef P_ZOMBIE
|
|
#define P_ZOMBIE(p) ((p)->p_stat == SZOMB)
|
|
#endif
|
|
|
|
void
|
|
labelps(void)
|
|
{
|
|
mvwaddstr(wnd, 0, 0, "USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND");
|
|
}
|
|
|
|
void
|
|
showps(void)
|
|
{
|
|
int i, k, y, vsz, rss;
|
|
const char *user, *comm, *state, *tty, *start, *time;
|
|
pid_t pid;
|
|
double pctcpu, pctmem;
|
|
struct eproc *ep;
|
|
|
|
now = 0; /* force start2str to reget current time */
|
|
|
|
qsort(pt, nproc + 1, sizeof (struct p_times), compare_pctcpu_noidle);
|
|
|
|
y = 1;
|
|
i = nproc + 1;
|
|
if (i > getmaxy(wnd)-2)
|
|
i = getmaxy(wnd)-1;
|
|
for (k = 0; i > 0 ; k++) {
|
|
if (pt[k].pt_kp == NULL) /* We're all the way down to the imaginary idle proc */
|
|
break;
|
|
|
|
ep = &pt[k].pt_kp->kp_eproc;
|
|
if (showuser != SHOWUSER_ANY && ep->e_ucred.cr_uid != showuser)
|
|
continue;
|
|
user = user_from_uid(ep->e_ucred.cr_uid, 0);
|
|
pid = pt[k].pt_kp->kp_proc.p_pid;
|
|
pctcpu = 100.0 * pt[k].pt_pctcpu;
|
|
pctmem = pmem2float(pt[k].pt_kp);
|
|
vsz = vsz2int(pt[k].pt_kp);
|
|
rss = rss2int(pt[k].pt_kp);
|
|
tty = tty2str(pt[k].pt_kp);
|
|
state = state2str(pt[k].pt_kp);
|
|
start = start2str(pt[k].pt_kp);
|
|
time = time2str(pt[k].pt_kp);
|
|
comm = comm2str(pt[k].pt_kp);
|
|
/*comm = pt[k].pt_kp->kp_proc.p_comm; */
|
|
|
|
wmove(wnd, y, 0);
|
|
wclrtoeol(wnd);
|
|
mvwprintw(wnd, y++, 0,
|
|
"%-8.8s%5d %4.1f %4.1f %6d %5d %-3s %-4s %7s %10.10s %s",
|
|
user, pid, pctcpu, pctmem, vsz, rss, tty, state, start, time, comm);
|
|
i--;
|
|
}
|
|
wmove(wnd, y, 0);
|
|
wclrtobot(wnd);
|
|
}
|
|
|
|
int
|
|
compare_pctcpu_noidle(const void *a, const void *b)
|
|
{
|
|
if (((struct p_times *) a)->pt_kp == NULL)
|
|
return 1;
|
|
|
|
if (((struct p_times *) b)->pt_kp == NULL)
|
|
return -1;
|
|
|
|
return (((struct p_times *) a)->pt_pctcpu >
|
|
((struct p_times *) b)->pt_pctcpu)? -1: 1;
|
|
}
|
|
|
|
/* from here down adapted from .../src/usr.bin/ps/print.c . Any mistakes are my own, however. */
|
|
char *
|
|
state2str(struct kinfo_proc *kp)
|
|
{
|
|
struct proc *p;
|
|
struct eproc *e;
|
|
int flag;
|
|
char *cp;
|
|
char buf[5];
|
|
static char statestr[4];
|
|
|
|
p = &(kp->kp_proc);
|
|
e = &(kp->kp_eproc);
|
|
|
|
flag = p->p_flag;
|
|
cp = buf;
|
|
|
|
switch (p->p_stat) {
|
|
case SSTOP:
|
|
*cp = 'T';
|
|
break;
|
|
|
|
case SSLEEP:
|
|
if (flag & P_SINTR) /* interuptable (long) */
|
|
*cp = p->p_slptime >= MAXSLP ? 'I' : 'S';
|
|
else
|
|
*cp = 'D';
|
|
break;
|
|
|
|
case SRUN:
|
|
case SIDL:
|
|
case SONPROC:
|
|
*cp = 'R';
|
|
break;
|
|
|
|
case SZOMB:
|
|
#ifdef SDEAD
|
|
case SDEAD:
|
|
#endif
|
|
*cp = 'Z';
|
|
break;
|
|
|
|
default:
|
|
*cp = '?';
|
|
}
|
|
cp++;
|
|
if (flag & P_INMEM) {
|
|
} else
|
|
*cp++ = 'W';
|
|
if (p->p_nice < NZERO)
|
|
*cp++ = '<';
|
|
else if (p->p_nice > NZERO)
|
|
*cp++ = 'N';
|
|
if (flag & P_TRACED)
|
|
*cp++ = 'X';
|
|
if (flag & P_WEXIT && P_ZOMBIE(p) == 0)
|
|
*cp++ = 'E';
|
|
if (flag & P_PPWAIT)
|
|
*cp++ = 'V';
|
|
if ((flag & P_SYSTEM) || p->p_holdcnt)
|
|
*cp++ = 'L';
|
|
if (e->e_flag & EPROC_SLEADER)
|
|
*cp++ = 's';
|
|
if ((flag & P_CONTROLT) && e->e_pgid == e->e_tpgid)
|
|
*cp++ = '+';
|
|
*cp = '\0';
|
|
snprintf(statestr, sizeof(statestr), "%-s", buf);
|
|
|
|
return statestr;
|
|
}
|
|
|
|
char *
|
|
tty2str(struct kinfo_proc *kp)
|
|
{
|
|
static char ttystr[4];
|
|
char *ttyname;
|
|
struct eproc *e;
|
|
|
|
e = &(kp->kp_eproc);
|
|
|
|
if (e->e_tdev == NODEV || (ttyname = devname(e->e_tdev, S_IFCHR)) == NULL)
|
|
strcpy(ttystr, "??");
|
|
else {
|
|
if (strncmp(ttyname, "tty", 3) == 0 ||
|
|
strncmp(ttyname, "dty", 3) == 0)
|
|
ttyname += 3;
|
|
snprintf(ttystr, sizeof(ttystr), "%s%c", ttyname, e->e_flag & EPROC_CTTY ? ' ' : '-');
|
|
}
|
|
|
|
return ttystr;
|
|
}
|
|
|
|
#define pgtok(a) (((a)*getpagesize())/1024)
|
|
|
|
int
|
|
vsz2int(struct kinfo_proc *kp)
|
|
{
|
|
struct eproc *e;
|
|
int i;
|
|
|
|
e = &(kp->kp_eproc);
|
|
i = pgtok(e->e_vm.vm_dsize + e->e_vm.vm_ssize + e->e_vm.vm_tsize);
|
|
|
|
return ((i < 0) ? 0 : i);
|
|
}
|
|
|
|
int
|
|
rss2int(struct kinfo_proc *kp)
|
|
{
|
|
struct eproc *e;
|
|
int i;
|
|
|
|
e = &(kp->kp_eproc);
|
|
i = pgtok(e->e_vm.vm_rssize);
|
|
|
|
/* XXX don't have info about shared */
|
|
return ((i < 0) ? 0 : i);
|
|
}
|
|
|
|
char *
|
|
comm2str(struct kinfo_proc *kp)
|
|
{
|
|
char **argv, **pt;
|
|
static char commstr[41];
|
|
struct proc *p;
|
|
|
|
p = &(kp->kp_proc);
|
|
commstr[0]='\0';
|
|
|
|
argv = kvm_getargv(kd, kp, 40);
|
|
if ((pt = argv) != NULL) {
|
|
while (*pt) {
|
|
strcat(commstr, *pt);
|
|
pt++;
|
|
strcat(commstr, " ");
|
|
}
|
|
} else {
|
|
commstr[0] = '(';
|
|
commstr[1] = '\0';
|
|
strncat(commstr, p->p_comm, sizeof(commstr) - 1);
|
|
strcat(commstr, ")");
|
|
}
|
|
|
|
return commstr;
|
|
}
|
|
|
|
double
|
|
pmem2float(struct kinfo_proc *kp)
|
|
{
|
|
struct proc *p;
|
|
struct eproc *e;
|
|
double fracmem;
|
|
int szptudot;
|
|
|
|
p = &(kp->kp_proc);
|
|
e = &(kp->kp_eproc);
|
|
|
|
if ((p->p_flag & P_INMEM) == 0)
|
|
return (0.0);
|
|
/* XXX want pmap ptpages, segtab, etc. (per architecture) */
|
|
szptudot = USPACE/getpagesize();
|
|
/* XXX don't have info about shared */
|
|
fracmem = ((double)e->e_vm.vm_rssize + szptudot)/mempages;
|
|
return (fracmem >= 0) ? 100.0 * fracmem : 0;
|
|
}
|
|
|
|
char *
|
|
start2str(struct kinfo_proc *kp)
|
|
{
|
|
struct proc *p;
|
|
struct pstats pstats;
|
|
struct timeval u_start;
|
|
struct tm *tp;
|
|
time_t startt;
|
|
static char startstr[10];
|
|
|
|
p = &(kp->kp_proc);
|
|
|
|
kvm_read(kd, (u_long)&(p->p_addr->u_stats), (char *)&pstats, sizeof(pstats));
|
|
u_start = pstats.p_start;
|
|
|
|
startt = u_start.tv_sec;
|
|
tp = localtime(&startt);
|
|
if (now == 0)
|
|
time(&now);
|
|
if (now - u_start.tv_sec < 24 * SECSPERHOUR) {
|
|
/* I *hate* SCCS... */
|
|
static char fmt[] = "%l:%" "M%p";
|
|
strftime(startstr, sizeof(startstr) - 1, fmt, tp);
|
|
} else if (now - u_start.tv_sec < 7 * SECSPERDAY) {
|
|
/* I *hate* SCCS... */
|
|
static char fmt[] = "%a%" "I%p";
|
|
strftime(startstr, sizeof(startstr) - 1, fmt, tp);
|
|
} else
|
|
strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp);
|
|
|
|
return startstr;
|
|
}
|
|
|
|
char *
|
|
time2str(struct kinfo_proc *kp)
|
|
{
|
|
long secs;
|
|
long psecs; /* "parts" of a second. first micro, then centi */
|
|
static char timestr[10];
|
|
struct proc *p;
|
|
|
|
p = &(kp->kp_proc);
|
|
|
|
if (P_ZOMBIE(p)) {
|
|
secs = 0;
|
|
psecs = 0;
|
|
} else {
|
|
/*
|
|
* This counts time spent handling interrupts. We could
|
|
* fix this, but it is not 100% trivial (and interrupt
|
|
* time fractions only work on the sparc anyway). XXX
|
|
*/
|
|
secs = p->p_rtime.tv_sec;
|
|
psecs = p->p_rtime.tv_usec;
|
|
/* if (sumrusage) {
|
|
secs += k->ki_u.u_cru.ru_utime.tv_sec +
|
|
k->ki_u.u_cru.ru_stime.tv_sec;
|
|
psecs += k->ki_u.u_cru.ru_utime.tv_usec +
|
|
k->ki_u.u_cru.ru_stime.tv_usec;
|
|
} */
|
|
/*
|
|
* round and scale to 100's
|
|
*/
|
|
psecs = (psecs + 5000) / 10000;
|
|
secs += psecs / 100;
|
|
psecs = psecs % 100;
|
|
}
|
|
snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, secs%60, psecs);
|
|
|
|
return timestr;
|
|
}
|
|
|
|
void
|
|
ps_user(char *args)
|
|
{
|
|
uid_t uid;
|
|
|
|
if (args == NULL)
|
|
args = "";
|
|
if (strcmp(args, "+") == 0) {
|
|
uid = SHOWUSER_ANY;
|
|
} else if (uid_from_user(args, &uid) != 0) {
|
|
error("%s: unknown user", args);
|
|
return;
|
|
}
|
|
|
|
showuser = uid;
|
|
display(0);
|
|
}
|