1 /* $NetBSD: ps.c,v 1.13 2000/01/08 23:12:37 itojun Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 5 * The NetBSD Foundation, Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the NetBSD Foundation. 18 * 4. Neither the name of the Foundation nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * XXX Notes XXX 37 * showps -- print data needed at each refresh 38 * fetchps -- get data used by mode (done at each refresh) 39 * labelps -- print labels (ie info not needing refreshing) 40 * initps -- prepare once-only data structures for mode 41 * openps -- prepare per-run information for mode, return window 42 * closeps -- close mode to prepare to switch modes 43 * cmdps -- optional, handle commands 44 */ 45 46 #include <sys/cdefs.h> 47 #ifndef lint 48 __RCSID("$NetBSD: ps.c,v 1.13 2000/01/08 23:12:37 itojun Exp $"); 49 #endif /* not lint */ 50 51 #include <sys/param.h> 52 #include <sys/dkstat.h> 53 #include <sys/dir.h> 54 #include <sys/time.h> 55 #include <sys/proc.h> 56 #include <sys/sysctl.h> 57 #include <sys/user.h> 58 #include <curses.h> 59 #include <math.h> 60 #include <nlist.h> 61 #include <pwd.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <tzfile.h> 65 #include <unistd.h> 66 67 #include "extern.h" 68 #include "systat.h" 69 #include "ps.h" 70 71 int compare_pctcpu_noidle __P((const void *, const void *)); 72 char *state2str __P((struct kinfo_proc *)); 73 char *tty2str __P((struct kinfo_proc *)); 74 int rss2int __P((struct kinfo_proc *)); 75 int vsz2int __P((struct kinfo_proc *)); 76 char *comm2str __P((struct kinfo_proc *)); 77 double pmem2float __P((struct kinfo_proc *)); 78 char *start2str __P((struct kinfo_proc *)); 79 char *time2str __P((struct kinfo_proc *)); 80 81 static time_t now; 82 83 #define SHOWUSER_ANY (uid_t)-1 84 static uid_t showuser = SHOWUSER_ANY; 85 86 #ifndef P_ZOMBIE 87 #define P_ZOMBIE(p) ((p)->p_stat == SZOMB) 88 #endif 89 90 void 91 labelps () 92 { 93 mvwaddstr(wnd, 0, 0, "USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND"); 94 } 95 96 void 97 showps () 98 { 99 int i, k, y, vsz, rss; 100 const char *user, *comm, *state, *tty, *start, *time; 101 pid_t pid; 102 double pctcpu, pctmem; 103 struct eproc *ep; 104 105 now = 0; /* force start2str to reget current time */ 106 107 qsort(pt, nproc + 1, sizeof (struct p_times), compare_pctcpu_noidle); 108 109 y = 1; 110 i = nproc + 1; 111 if (i > getmaxy(wnd)-2) 112 i = getmaxy(wnd)-1; 113 for (k = 0; i > 0 ; k++) { 114 if (pt[k].pt_kp == NULL) /* We're all the way down to the imaginary idle proc */ 115 break; 116 117 ep = &pt[k].pt_kp->kp_eproc; 118 if (showuser != SHOWUSER_ANY && ep->e_ucred.cr_uid != showuser) 119 continue; 120 user = user_from_uid(ep->e_ucred.cr_uid, 0); 121 pid = pt[k].pt_kp->kp_proc.p_pid; 122 pctcpu = 100.0 * pt[k].pt_pctcpu; 123 pctmem = pmem2float(pt[k].pt_kp); 124 vsz = vsz2int(pt[k].pt_kp); 125 rss = rss2int(pt[k].pt_kp); 126 tty = tty2str(pt[k].pt_kp); 127 state = state2str(pt[k].pt_kp); 128 start = start2str(pt[k].pt_kp); 129 time = time2str(pt[k].pt_kp); 130 comm = comm2str(pt[k].pt_kp); 131 /*comm = pt[k].pt_kp->kp_proc.p_comm; */ 132 133 wmove(wnd, y, 0); 134 wclrtoeol(wnd); 135 mvwprintw(wnd, y++, 0, 136 "%-8.8s%5d %4.1f %4.1f %6d %5d %-3s %-4s %7s %10.10s %s", 137 user, pid, pctcpu, pctmem, vsz, rss, tty, state, start, time, comm); 138 i--; 139 } 140 wmove(wnd, y, 0); 141 wclrtobot(wnd); 142 } 143 144 int 145 compare_pctcpu_noidle (a, b) 146 const void *a, *b; 147 { 148 if (((struct p_times *) a)->pt_kp == NULL) 149 return 1; 150 151 if (((struct p_times *) b)->pt_kp == NULL) 152 return -1; 153 154 return (((struct p_times *) a)->pt_pctcpu > 155 ((struct p_times *) b)->pt_pctcpu)? -1: 1; 156 } 157 158 /* from here down adapted from .../src/usr.bin/ps/print.c . Any mistakes are my own, however. */ 159 char * 160 state2str(kp) 161 struct kinfo_proc *kp; 162 { 163 struct proc *p; 164 struct eproc *e; 165 int flag; 166 char *cp; 167 char buf[5]; 168 static char statestr[4]; 169 170 p = &(kp->kp_proc); 171 e = &(kp->kp_eproc); 172 173 flag = p->p_flag; 174 cp = buf; 175 176 switch (p->p_stat) { 177 case SSTOP: 178 *cp = 'T'; 179 break; 180 181 case SSLEEP: 182 if (flag & P_SINTR) /* interuptable (long) */ 183 *cp = p->p_slptime >= MAXSLP ? 'I' : 'S'; 184 else 185 *cp = 'D'; 186 break; 187 188 case SRUN: 189 case SIDL: 190 *cp = 'R'; 191 break; 192 193 case SZOMB: 194 #ifdef SDEAD 195 case SDEAD: 196 #endif 197 *cp = 'Z'; 198 break; 199 200 default: 201 *cp = '?'; 202 } 203 cp++; 204 if (flag & P_INMEM) { 205 } else 206 *cp++ = 'W'; 207 if (p->p_nice < NZERO) 208 *cp++ = '<'; 209 else if (p->p_nice > NZERO) 210 *cp++ = 'N'; 211 if (flag & P_TRACED) 212 *cp++ = 'X'; 213 if (flag & P_WEXIT && P_ZOMBIE(p) == 0) 214 *cp++ = 'E'; 215 if (flag & P_PPWAIT) 216 *cp++ = 'V'; 217 if ((flag & P_SYSTEM) || p->p_holdcnt) 218 *cp++ = 'L'; 219 if (e->e_flag & EPROC_SLEADER) 220 *cp++ = 's'; 221 if ((flag & P_CONTROLT) && e->e_pgid == e->e_tpgid) 222 *cp++ = '+'; 223 *cp = '\0'; 224 snprintf(statestr, sizeof(statestr), "%-s", buf); 225 226 return statestr; 227 } 228 229 char * 230 tty2str(kp) 231 struct kinfo_proc *kp; 232 { 233 static char ttystr[4]; 234 char *ttyname; 235 struct eproc *e; 236 237 e = &(kp->kp_eproc); 238 239 if (e->e_tdev == NODEV || (ttyname = devname(e->e_tdev, S_IFCHR)) == NULL) 240 strcpy(ttystr, "??"); 241 else { 242 if (strncmp(ttyname, "tty", 3) == 0 || 243 strncmp(ttyname, "dty", 3) == 0) 244 ttyname += 3; 245 snprintf(ttystr, sizeof(ttystr), "%s%c", ttyname, e->e_flag & EPROC_CTTY ? ' ' : '-'); 246 } 247 248 return ttystr; 249 } 250 251 #define pgtok(a) (((a)*getpagesize())/1024) 252 253 int 254 vsz2int(kp) 255 struct kinfo_proc *kp; 256 { 257 struct eproc *e; 258 int i; 259 260 e = &(kp->kp_eproc); 261 i = pgtok(e->e_vm.vm_dsize + e->e_vm.vm_ssize + e->e_vm.vm_tsize); 262 263 return ((i < 0) ? 0 : i); 264 } 265 266 int 267 rss2int(kp) 268 struct kinfo_proc *kp; 269 { 270 struct eproc *e; 271 int i; 272 273 e = &(kp->kp_eproc); 274 i = pgtok(e->e_vm.vm_rssize); 275 276 /* XXX don't have info about shared */ 277 return ((i < 0) ? 0 : i); 278 } 279 280 char * 281 comm2str(kp) 282 struct kinfo_proc *kp; 283 { 284 char **argv, **pt; 285 static char commstr[41]; 286 struct proc *p; 287 288 p = &(kp->kp_proc); 289 commstr[0]='\0'; 290 291 argv = kvm_getargv(kd, kp, 40); 292 if ((pt = argv) != NULL) { 293 while (*pt) { 294 strcat(commstr, *pt); 295 pt++; 296 strcat(commstr, " "); 297 } 298 } else { 299 commstr[0] = '('; 300 commstr[1] = '\0'; 301 strncat(commstr, p->p_comm, sizeof(commstr) - 1); 302 strcat(commstr, ")"); 303 } 304 305 return commstr; 306 } 307 308 double 309 pmem2float(kp) 310 struct kinfo_proc *kp; 311 { 312 struct proc *p; 313 struct eproc *e; 314 double fracmem; 315 int szptudot; 316 317 p = &(kp->kp_proc); 318 e = &(kp->kp_eproc); 319 320 if ((p->p_flag & P_INMEM) == 0) 321 return (0.0); 322 /* XXX want pmap ptpages, segtab, etc. (per architecture) */ 323 szptudot = USPACE/getpagesize(); 324 /* XXX don't have info about shared */ 325 fracmem = ((double)e->e_vm.vm_rssize + szptudot)/mempages; 326 return (fracmem >= 0) ? 100.0 * fracmem : 0; 327 } 328 329 char * 330 start2str(kp) 331 struct kinfo_proc *kp; 332 { 333 struct proc *p; 334 struct pstats pstats; 335 struct timeval u_start; 336 struct tm *tp; 337 time_t startt; 338 static char startstr[10]; 339 340 p = &(kp->kp_proc); 341 342 kvm_read(kd, (u_long)&(p->p_addr->u_stats), (char *)&pstats, sizeof(pstats)); 343 u_start = pstats.p_start; 344 345 startt = u_start.tv_sec; 346 tp = localtime(&startt); 347 if (now == 0) 348 time(&now); 349 if (now - u_start.tv_sec < 24 * SECSPERHOUR) { 350 /* I *hate* SCCS... */ 351 static char fmt[] = __CONCAT("%l:%", "M%p"); 352 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 353 } else if (now - u_start.tv_sec < 7 * SECSPERDAY) { 354 /* I *hate* SCCS... */ 355 static char fmt[] = __CONCAT("%a%", "I%p"); 356 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 357 } else 358 strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp); 359 360 return startstr; 361 } 362 363 char * 364 time2str(kp) 365 struct kinfo_proc *kp; 366 { 367 long secs; 368 long psecs; /* "parts" of a second. first micro, then centi */ 369 static char timestr[10]; 370 struct proc *p; 371 372 p = &(kp->kp_proc); 373 374 if (P_ZOMBIE(p)) { 375 secs = 0; 376 psecs = 0; 377 } else { 378 /* 379 * This counts time spent handling interrupts. We could 380 * fix this, but it is not 100% trivial (and interrupt 381 * time fractions only work on the sparc anyway). XXX 382 */ 383 secs = p->p_rtime.tv_sec; 384 psecs = p->p_rtime.tv_usec; 385 /* if (sumrusage) { 386 secs += k->ki_u.u_cru.ru_utime.tv_sec + 387 k->ki_u.u_cru.ru_stime.tv_sec; 388 psecs += k->ki_u.u_cru.ru_utime.tv_usec + 389 k->ki_u.u_cru.ru_stime.tv_usec; 390 } */ 391 /* 392 * round and scale to 100's 393 */ 394 psecs = (psecs + 5000) / 10000; 395 secs += psecs / 100; 396 psecs = psecs % 100; 397 } 398 snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, secs%60, psecs); 399 400 return timestr; 401 } 402 403 void 404 ps_user(args) 405 char *args; 406 { 407 uid_t uid; 408 409 if (args == NULL) 410 args = ""; 411 if (strcmp(args, "+") == 0) { 412 uid = SHOWUSER_ANY; 413 } else if (uid_from_user(args, &uid) != 0) { 414 error("%s: unknown user", args); 415 return; 416 } 417 418 showuser = uid; 419 display(0); 420 } 421