xref: /netbsd-src/usr.bin/systat/ps.c (revision 5858bd6b8cc1dd9a52e0193eb161ef50b857b5b9)
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