1*5858bd6bSmaya /* $NetBSD: ps.c,v 1.40 2021/04/17 08:34:27 maya Exp $ */
2cfb46b6fSjwise
3cfb46b6fSjwise /*-
4cfb46b6fSjwise * Copyright (c) 1999
5cfb46b6fSjwise * The NetBSD Foundation, Inc. All rights reserved.
6cfb46b6fSjwise *
7cfb46b6fSjwise * Redistribution and use in source and binary forms, with or without
8cfb46b6fSjwise * modification, are permitted provided that the following conditions
9cfb46b6fSjwise * are met:
10cfb46b6fSjwise * 1. Redistributions of source code must retain the above copyright
11cfb46b6fSjwise * notice, this list of conditions and the following disclaimer.
12cfb46b6fSjwise * 2. Redistributions in binary form must reproduce the above copyright
13cfb46b6fSjwise * notice, this list of conditions and the following disclaimer in the
14cfb46b6fSjwise * documentation and/or other materials provided with the distribution.
15b5f0c03bSjwise * 3. All advertising materials mentioning features or use of this software
16b5f0c03bSjwise * must display the following acknowledgement:
17299c3534Sjwise * This product includes software developed by the NetBSD Foundation.
18299c3534Sjwise * 4. Neither the name of the Foundation nor the names of its contributors
19cfb46b6fSjwise * may be used to endorse or promote products derived from this software
20cfb46b6fSjwise * without specific prior written permission.
21cfb46b6fSjwise *
22cfb46b6fSjwise * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23cfb46b6fSjwise * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24cfb46b6fSjwise * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25cfb46b6fSjwise * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26cfb46b6fSjwise * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27cfb46b6fSjwise * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28cfb46b6fSjwise * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29cfb46b6fSjwise * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30cfb46b6fSjwise * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31cfb46b6fSjwise * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32cfb46b6fSjwise * SUCH DAMAGE.
33cfb46b6fSjwise */
34cfb46b6fSjwise
35cfb46b6fSjwise /*
36cfb46b6fSjwise * XXX Notes XXX
37cfb46b6fSjwise * showps -- print data needed at each refresh
38cfb46b6fSjwise * fetchps -- get data used by mode (done at each refresh)
39cfb46b6fSjwise * labelps -- print labels (ie info not needing refreshing)
40cfb46b6fSjwise * initps -- prepare once-only data structures for mode
41cfb46b6fSjwise * openps -- prepare per-run information for mode, return window
42cfb46b6fSjwise * closeps -- close mode to prepare to switch modes
43cfb46b6fSjwise * cmdps -- optional, handle commands
44cfb46b6fSjwise */
45cfb46b6fSjwise
468ac21971Ssimonb #include <sys/cdefs.h>
478ac21971Ssimonb #ifndef lint
48*5858bd6bSmaya __RCSID("$NetBSD: ps.c,v 1.40 2021/04/17 08:34:27 maya Exp $");
498ac21971Ssimonb #endif /* not lint */
508ac21971Ssimonb
51cfb46b6fSjwise #include <sys/param.h>
52bd70b198Sperry #include <sys/sched.h>
53cfb46b6fSjwise #include <sys/sysctl.h>
5432377daaSchristos #include <sys/stat.h>
55828483a7Ssimonb
56cfb46b6fSjwise #include <curses.h>
57cfb46b6fSjwise #include <math.h>
58cfb46b6fSjwise #include <pwd.h>
59cfb46b6fSjwise #include <stdlib.h>
60cfb46b6fSjwise #include <string.h>
61cfb46b6fSjwise #include <tzfile.h>
62cfb46b6fSjwise #include <unistd.h>
63cfb46b6fSjwise
64cfb46b6fSjwise #include "systat.h"
65d5eba984Smatt #include "extern.h"
66cfb46b6fSjwise #include "ps.h"
67cfb46b6fSjwise
68fc391547Sad int compare_pctcpu_noidle(const void *, const void *);
69477d91c6Sthorpej char *state2str(struct kinfo_proc2 *);
70477d91c6Sthorpej char *tty2str(struct kinfo_proc2 *);
71477d91c6Sthorpej int rss2int(struct kinfo_proc2 *);
72477d91c6Sthorpej int vsz2int(struct kinfo_proc2 *);
73477d91c6Sthorpej char *comm2str(struct kinfo_proc2 *);
74477d91c6Sthorpej double pmem2float(struct kinfo_proc2 *);
75477d91c6Sthorpej char *start2str(struct kinfo_proc2 *);
76477d91c6Sthorpej char *time2str(struct kinfo_proc2 *);
77cfb46b6fSjwise
78cfb46b6fSjwise static time_t now;
79cfb46b6fSjwise
80c23c312aSkleink #define SHOWUSER_ANY (uid_t)-1
81c23c312aSkleink static uid_t showuser = SHOWUSER_ANY;
82c23c312aSkleink
83d7b4ec92Sitojun #ifndef P_ZOMBIE
84d7b4ec92Sitojun #define P_ZOMBIE(p) ((p)->p_stat == SZOMB)
85d7b4ec92Sitojun #endif
86d7b4ec92Sitojun
87cfb46b6fSjwise void
labelps(void)88fc391547Sad labelps(void)
89cfb46b6fSjwise {
90cfb46b6fSjwise mvwaddstr(wnd, 0, 0, "USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND");
91cfb46b6fSjwise }
92cfb46b6fSjwise
93cfb46b6fSjwise void
showps(void)94fc391547Sad showps(void)
95cfb46b6fSjwise {
96cfb46b6fSjwise int i, k, y, vsz, rss;
97019733f6Sdsl const char *user, *comm, *state, *tty, *start, *time_str;
98cfb46b6fSjwise pid_t pid;
995bebc476Sitohy double pctcpu, pctmem;
100477d91c6Sthorpej struct kinfo_proc2 *kp;
101cfb46b6fSjwise
102cfb46b6fSjwise now = 0; /* force start2str to reget current time */
103cfb46b6fSjwise
104cfb46b6fSjwise qsort(pt, nproc + 1, sizeof (struct p_times), compare_pctcpu_noidle);
105cfb46b6fSjwise
106cfb46b6fSjwise y = 1;
107cfb46b6fSjwise i = nproc + 1;
108cfb46b6fSjwise if (i > getmaxy(wnd)-2)
109cfb46b6fSjwise i = getmaxy(wnd)-1;
110c23c312aSkleink for (k = 0; i > 0 ; k++) {
111cfb46b6fSjwise if (pt[k].pt_kp == NULL) /* We're all the way down to the imaginary idle proc */
112c23c312aSkleink break;
113cfb46b6fSjwise
114477d91c6Sthorpej kp = pt[k].pt_kp;
115477d91c6Sthorpej if (showuser != SHOWUSER_ANY && kp->p_uid != showuser)
116c23c312aSkleink continue;
117477d91c6Sthorpej user = user_from_uid(kp->p_uid, 0);
118477d91c6Sthorpej pid = kp->p_pid;
1195bebc476Sitohy pctcpu = 100.0 * pt[k].pt_pctcpu;
120cfb46b6fSjwise pctmem = pmem2float(pt[k].pt_kp);
121cfb46b6fSjwise vsz = vsz2int(pt[k].pt_kp);
122cfb46b6fSjwise rss = rss2int(pt[k].pt_kp);
123cfb46b6fSjwise tty = tty2str(pt[k].pt_kp);
124cfb46b6fSjwise state = state2str(pt[k].pt_kp);
125cfb46b6fSjwise start = start2str(pt[k].pt_kp);
126019733f6Sdsl time_str = time2str(pt[k].pt_kp);
127cfb46b6fSjwise comm = comm2str(pt[k].pt_kp);
128cfb46b6fSjwise /*comm = pt[k].pt_kp->kp_proc.p_comm; */
129cfb46b6fSjwise
130cfb46b6fSjwise wmove(wnd, y, 0);
131cfb46b6fSjwise wclrtoeol(wnd);
132c23c312aSkleink mvwprintw(wnd, y++, 0,
133c23c312aSkleink "%-8.8s%5d %4.1f %4.1f %6d %5d %-3s %-4s %7s %10.10s %s",
134019733f6Sdsl user, pid, pctcpu, pctmem, vsz, rss, tty, state, start,
135019733f6Sdsl time_str, comm);
136c23c312aSkleink i--;
137cfb46b6fSjwise }
138c23c312aSkleink wmove(wnd, y, 0);
139c23c312aSkleink wclrtobot(wnd);
140cfb46b6fSjwise }
141cfb46b6fSjwise
142cfb46b6fSjwise int
compare_pctcpu_noidle(const void * a,const void * b)143fc391547Sad compare_pctcpu_noidle(const void *a, const void *b)
144cfb46b6fSjwise {
145019733f6Sdsl if (((const struct p_times *) a)->pt_kp == NULL)
146cfb46b6fSjwise return 1;
147cfb46b6fSjwise
148019733f6Sdsl if (((const struct p_times *) b)->pt_kp == NULL)
149cfb46b6fSjwise return -1;
150cfb46b6fSjwise
151019733f6Sdsl return (((const struct p_times *) a)->pt_pctcpu >
152019733f6Sdsl ((const struct p_times *) b)->pt_pctcpu)? -1: 1;
153cfb46b6fSjwise }
154cfb46b6fSjwise
155cfb46b6fSjwise /* from here down adapted from .../src/usr.bin/ps/print.c . Any mistakes are my own, however. */
156cfb46b6fSjwise char *
state2str(struct kinfo_proc2 * kp)157477d91c6Sthorpej state2str(struct kinfo_proc2 *kp)
158cfb46b6fSjwise {
159cfb46b6fSjwise int flag;
160cfb46b6fSjwise char *cp;
161cfb46b6fSjwise char buf[5];
162cfb46b6fSjwise static char statestr[4];
163cfb46b6fSjwise
164477d91c6Sthorpej flag = kp->p_flag;
165cfb46b6fSjwise cp = buf;
166cfb46b6fSjwise
167477d91c6Sthorpej switch (kp->p_stat) {
1683fdac2b8Sthorpej case LSSTOP:
169cfb46b6fSjwise *cp = 'T';
170cfb46b6fSjwise break;
171cfb46b6fSjwise
1723fdac2b8Sthorpej case LSSLEEP:
1733fdac2b8Sthorpej if (flag & L_SINTR) /* interruptable (long) */
174e7a39c4fSlukem *cp = kp->p_slptime >= (uint32_t)maxslp ? 'I' : 'S';
175cfb46b6fSjwise else
176cfb46b6fSjwise *cp = 'D';
177cfb46b6fSjwise break;
178cfb46b6fSjwise
1793fdac2b8Sthorpej case LSRUN:
1803fdac2b8Sthorpej case LSIDL:
1813fdac2b8Sthorpej case LSONPROC:
182cfb46b6fSjwise *cp = 'R';
183cfb46b6fSjwise break;
184cfb46b6fSjwise
1853fdac2b8Sthorpej case LSZOMB:
1863fdac2b8Sthorpej #ifdef LSDEAD
1873fdac2b8Sthorpej case LSDEAD:
188d7b4ec92Sitojun #endif
189cfb46b6fSjwise *cp = 'Z';
190cfb46b6fSjwise break;
191cfb46b6fSjwise
192cfb46b6fSjwise default:
193cfb46b6fSjwise *cp = '?';
194cfb46b6fSjwise }
195cfb46b6fSjwise cp++;
196477d91c6Sthorpej if (kp->p_nice < NZERO)
197cfb46b6fSjwise *cp++ = '<';
198477d91c6Sthorpej else if (kp->p_nice > NZERO)
199cfb46b6fSjwise *cp++ = 'N';
200f5e7ca24Spavel if (flag & P_TRACED)
201cfb46b6fSjwise *cp++ = 'X';
202f5e7ca24Spavel if (flag & P_WEXIT &&
203477d91c6Sthorpej /* XXX - I don't like this */
204401499f3Sad (kp->p_stat != LSZOMB))
205cfb46b6fSjwise *cp++ = 'E';
206f5e7ca24Spavel if (flag & P_PPWAIT)
207cfb46b6fSjwise *cp++ = 'V';
208477d91c6Sthorpej if (kp->p_eflag & EPROC_SLEADER)
209cfb46b6fSjwise *cp++ = 's';
210f5e7ca24Spavel if ((flag & P_CONTROLT) && kp->p__pgid == kp->p_tpgid)
211cfb46b6fSjwise *cp++ = '+';
212cfb46b6fSjwise *cp = '\0';
21381ed25dbSjwise snprintf(statestr, sizeof(statestr), "%-s", buf);
214cfb46b6fSjwise
215cfb46b6fSjwise return statestr;
216cfb46b6fSjwise }
217cfb46b6fSjwise
218cfb46b6fSjwise char *
tty2str(struct kinfo_proc2 * kp)219477d91c6Sthorpej tty2str(struct kinfo_proc2 *kp)
220cfb46b6fSjwise {
221cfb46b6fSjwise static char ttystr[4];
222019733f6Sdsl char *tty_name;
223cfb46b6fSjwise
224e12ce13bSchristos if (kp->p_tdev == (uint32_t)NODEV ||
225019733f6Sdsl (tty_name = devname(kp->p_tdev, S_IFCHR)) == NULL)
2265bce78caSitojun strlcpy(ttystr, "??", sizeof(ttystr));
227cfb46b6fSjwise else {
228019733f6Sdsl if (strncmp(tty_name, "tty", 3) == 0 ||
229019733f6Sdsl strncmp(tty_name, "dty", 3) == 0)
230019733f6Sdsl tty_name += 3;
231019733f6Sdsl snprintf(ttystr, sizeof(ttystr), "%s%c", tty_name,
232477d91c6Sthorpej kp->p_eflag & EPROC_CTTY ? ' ' : '-');
233cfb46b6fSjwise }
234cfb46b6fSjwise
235cfb46b6fSjwise return ttystr;
236cfb46b6fSjwise }
237cfb46b6fSjwise
238cfb46b6fSjwise #define pgtok(a) (((a)*getpagesize())/1024)
239cfb46b6fSjwise
240cfb46b6fSjwise int
vsz2int(struct kinfo_proc2 * kp)241477d91c6Sthorpej vsz2int(struct kinfo_proc2 *kp)
242cfb46b6fSjwise {
243cfb46b6fSjwise int i;
244cfb46b6fSjwise
245fcc02354Smrg i = pgtok(kp->p_vm_msize);
246cfb46b6fSjwise
247cfb46b6fSjwise return ((i < 0) ? 0 : i);
248cfb46b6fSjwise }
249cfb46b6fSjwise
250cfb46b6fSjwise int
rss2int(struct kinfo_proc2 * kp)251477d91c6Sthorpej rss2int(struct kinfo_proc2 *kp)
252cfb46b6fSjwise {
253cfb46b6fSjwise int i;
254cfb46b6fSjwise
255477d91c6Sthorpej i = pgtok(kp->p_vm_rssize);
256cfb46b6fSjwise
257cfb46b6fSjwise /* XXX don't have info about shared */
258cfb46b6fSjwise return ((i < 0) ? 0 : i);
259cfb46b6fSjwise }
260cfb46b6fSjwise
261cfb46b6fSjwise char *
comm2str(struct kinfo_proc2 * kp)262477d91c6Sthorpej comm2str(struct kinfo_proc2 *kp)
263cfb46b6fSjwise {
264019733f6Sdsl char **argv;
265cfb46b6fSjwise static char commstr[41];
266cfb46b6fSjwise
267cfb46b6fSjwise commstr[0]='\0';
268cfb46b6fSjwise
269477d91c6Sthorpej argv = kvm_getargv2(kd, kp, 40);
270019733f6Sdsl if (argv != NULL) {
271019733f6Sdsl while (*argv) {
272019733f6Sdsl strlcat(commstr, *argv, sizeof(commstr));
273019733f6Sdsl argv++;
2740e1bb4cdSitojun strlcat(commstr, " ", sizeof(commstr));
275cfb46b6fSjwise }
2762d52a8c9Skleink } else {
2772d52a8c9Skleink const char *fmt;
2782d52a8c9Skleink
2792d52a8c9Skleink /*
2802d52a8c9Skleink * Commands that don't set an argv vector are printed with
2812d52a8c9Skleink * square brackets if they are system commands. Otherwise
2822d52a8c9Skleink * they are printed within parentheses.
2832d52a8c9Skleink */
284f5e7ca24Spavel if (kp->p_flag & P_SYSTEM)
28508c8290bSchristos fmt = "[]";
2862d52a8c9Skleink else
28708c8290bSchristos fmt = "()";
2882d52a8c9Skleink
28908c8290bSchristos snprintf(commstr, sizeof(commstr), "%c%s%c", fmt[0],
29008c8290bSchristos kp->p_comm, fmt[1]);
2912d52a8c9Skleink }
292cfb46b6fSjwise
293cfb46b6fSjwise return commstr;
294cfb46b6fSjwise }
295cfb46b6fSjwise
2965bebc476Sitohy double
pmem2float(struct kinfo_proc2 * kp)297477d91c6Sthorpej pmem2float(struct kinfo_proc2 *kp)
298cfb46b6fSjwise {
299cfb46b6fSjwise double fracmem;
300f34bcfadSsimonb static int szptudot = -1;
301cfb46b6fSjwise
302f34bcfadSsimonb /*
303f34bcfadSsimonb * XXX want pmap ptpages, segtab, etc. (per architecture),
304f34bcfadSsimonb * not just the uarea.
305f34bcfadSsimonb */
306f34bcfadSsimonb if (szptudot < 0) {
307f34bcfadSsimonb int mib[2];
308f34bcfadSsimonb size_t size;
309f34bcfadSsimonb int uspace;
310f34bcfadSsimonb
311f34bcfadSsimonb mib[0] = CTL_VM;
312f34bcfadSsimonb mib[1] = VM_USPACE;
313f34bcfadSsimonb size = sizeof(uspace);
314f34bcfadSsimonb if (sysctl(mib, 2, &uspace, &size, NULL, 0) == 0) {
315f34bcfadSsimonb szptudot = uspace / getpagesize();
316f34bcfadSsimonb } else {
317f34bcfadSsimonb /* pick a vaguely useful default */
318f34bcfadSsimonb szptudot = getpagesize();
319f34bcfadSsimonb }
320f34bcfadSsimonb }
321f34bcfadSsimonb
322cfb46b6fSjwise /* XXX don't have info about shared */
323477d91c6Sthorpej fracmem = ((double)kp->p_vm_rssize + szptudot) / mempages;
324cfb46b6fSjwise return (fracmem >= 0) ? 100.0 * fracmem : 0;
325cfb46b6fSjwise }
326cfb46b6fSjwise
327cfb46b6fSjwise char *
start2str(struct kinfo_proc2 * kp)328477d91c6Sthorpej start2str(struct kinfo_proc2 *kp)
329cfb46b6fSjwise {
330cfb46b6fSjwise struct timeval u_start;
331cfb46b6fSjwise struct tm *tp;
332cfb46b6fSjwise time_t startt;
333cfb46b6fSjwise static char startstr[10];
334cfb46b6fSjwise
335477d91c6Sthorpej u_start.tv_sec = kp->p_ustart_sec;
336477d91c6Sthorpej u_start.tv_usec = kp->p_ustart_usec;
337cfb46b6fSjwise
338cfb46b6fSjwise startt = u_start.tv_sec;
339cfb46b6fSjwise tp = localtime(&startt);
340cfb46b6fSjwise if (now == 0)
341cfb46b6fSjwise time(&now);
342cfb46b6fSjwise if (now - u_start.tv_sec < 24 * SECSPERHOUR) {
343*5858bd6bSmaya strftime(startstr, sizeof(startstr) - 1, "%l:%M%p", tp);
344cfb46b6fSjwise } else if (now - u_start.tv_sec < 7 * SECSPERDAY) {
345*5858bd6bSmaya strftime(startstr, sizeof(startstr) - 1, "%a%I%p", tp);
346cfb46b6fSjwise } else
347cfb46b6fSjwise strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp);
348cfb46b6fSjwise
349cfb46b6fSjwise return startstr;
350cfb46b6fSjwise }
351cfb46b6fSjwise
352cfb46b6fSjwise char *
time2str(struct kinfo_proc2 * kp)353477d91c6Sthorpej time2str(struct kinfo_proc2 *kp)
354cfb46b6fSjwise {
355cfb46b6fSjwise long secs;
356cfb46b6fSjwise long psecs; /* "parts" of a second. first micro, then centi */
35737649e40Smrg static char timestr[18];
358cfb46b6fSjwise
359477d91c6Sthorpej /* XXX - I don't like this. */
360bd4dbe81Sjdolecek if (kp->p_stat == SZOMB) {
361cfb46b6fSjwise secs = 0;
362cfb46b6fSjwise psecs = 0;
363cfb46b6fSjwise } else {
364cfb46b6fSjwise /*
365cfb46b6fSjwise * This counts time spent handling interrupts. We could
366cfb46b6fSjwise * fix this, but it is not 100% trivial (and interrupt
367cfb46b6fSjwise * time fractions only work on the sparc anyway). XXX
368cfb46b6fSjwise */
369477d91c6Sthorpej secs = kp->p_rtime_sec;
370477d91c6Sthorpej psecs = kp->p_rtime_usec;
371477d91c6Sthorpej #if 0
372477d91c6Sthorpej if (sumrusage) {
373cfb46b6fSjwise secs += k->ki_u.u_cru.ru_utime.tv_sec +
374cfb46b6fSjwise k->ki_u.u_cru.ru_stime.tv_sec;
375cfb46b6fSjwise psecs += k->ki_u.u_cru.ru_utime.tv_usec +
376cfb46b6fSjwise k->ki_u.u_cru.ru_stime.tv_usec;
377477d91c6Sthorpej }
378477d91c6Sthorpej #endif
379cfb46b6fSjwise /*
380cfb46b6fSjwise * round and scale to 100's
381cfb46b6fSjwise */
382cfb46b6fSjwise psecs = (psecs + 5000) / 10000;
383cfb46b6fSjwise secs += psecs / 100;
384cfb46b6fSjwise psecs = psecs % 100;
385cfb46b6fSjwise }
386477d91c6Sthorpej snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60,
387477d91c6Sthorpej secs%60, psecs);
388cfb46b6fSjwise
389cfb46b6fSjwise return timestr;
390cfb46b6fSjwise }
391c23c312aSkleink
392c23c312aSkleink void
ps_user(char * args)393fc391547Sad ps_user(char *args)
394c23c312aSkleink {
395c23c312aSkleink uid_t uid;
396c23c312aSkleink
397019733f6Sdsl if (args == NULL || *args == 0 || strcmp(args, "+") == 0) {
398c23c312aSkleink uid = SHOWUSER_ANY;
399c23c312aSkleink } else if (uid_from_user(args, &uid) != 0) {
400c23c312aSkleink error("%s: unknown user", args);
401c23c312aSkleink return;
402c23c312aSkleink }
403c23c312aSkleink
404c23c312aSkleink showuser = uid;
405c23c312aSkleink display(0);
406c23c312aSkleink }
407