1 /* $NetBSD: ps.c,v 1.16 2000/07/05 11:03:23 ad 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.16 2000/07/05 11:03:23 ad 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/sched.h> 57 #include <sys/sysctl.h> 58 #include <sys/user.h> 59 #include <curses.h> 60 #include <math.h> 61 #include <nlist.h> 62 #include <pwd.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <tzfile.h> 66 #include <unistd.h> 67 68 #include "extern.h" 69 #include "systat.h" 70 #include "ps.h" 71 72 int compare_pctcpu_noidle(const void *, const void *); 73 char *state2str(struct kinfo_proc *); 74 char *tty2str(struct kinfo_proc *); 75 int rss2int(struct kinfo_proc *); 76 int vsz2int(struct kinfo_proc *); 77 char *comm2str(struct kinfo_proc *); 78 double pmem2float(struct kinfo_proc *); 79 char *start2str(struct kinfo_proc *); 80 char *time2str(struct kinfo_proc *); 81 82 static time_t now; 83 84 #define SHOWUSER_ANY (uid_t)-1 85 static uid_t showuser = SHOWUSER_ANY; 86 87 #ifndef P_ZOMBIE 88 #define P_ZOMBIE(p) ((p)->p_stat == SZOMB) 89 #endif 90 91 void 92 labelps(void) 93 { 94 mvwaddstr(wnd, 0, 0, "USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND"); 95 } 96 97 void 98 showps(void) 99 { 100 int i, k, y, vsz, rss; 101 const char *user, *comm, *state, *tty, *start, *time; 102 pid_t pid; 103 double pctcpu, pctmem; 104 struct eproc *ep; 105 106 now = 0; /* force start2str to reget current time */ 107 108 qsort(pt, nproc + 1, sizeof (struct p_times), compare_pctcpu_noidle); 109 110 y = 1; 111 i = nproc + 1; 112 if (i > getmaxy(wnd)-2) 113 i = getmaxy(wnd)-1; 114 for (k = 0; i > 0 ; k++) { 115 if (pt[k].pt_kp == NULL) /* We're all the way down to the imaginary idle proc */ 116 break; 117 118 ep = &pt[k].pt_kp->kp_eproc; 119 if (showuser != SHOWUSER_ANY && ep->e_ucred.cr_uid != showuser) 120 continue; 121 user = user_from_uid(ep->e_ucred.cr_uid, 0); 122 pid = pt[k].pt_kp->kp_proc.p_pid; 123 pctcpu = 100.0 * pt[k].pt_pctcpu; 124 pctmem = pmem2float(pt[k].pt_kp); 125 vsz = vsz2int(pt[k].pt_kp); 126 rss = rss2int(pt[k].pt_kp); 127 tty = tty2str(pt[k].pt_kp); 128 state = state2str(pt[k].pt_kp); 129 start = start2str(pt[k].pt_kp); 130 time = time2str(pt[k].pt_kp); 131 comm = comm2str(pt[k].pt_kp); 132 /*comm = pt[k].pt_kp->kp_proc.p_comm; */ 133 134 wmove(wnd, y, 0); 135 wclrtoeol(wnd); 136 mvwprintw(wnd, y++, 0, 137 "%-8.8s%5d %4.1f %4.1f %6d %5d %-3s %-4s %7s %10.10s %s", 138 user, pid, pctcpu, pctmem, vsz, rss, tty, state, start, time, comm); 139 i--; 140 } 141 wmove(wnd, y, 0); 142 wclrtobot(wnd); 143 } 144 145 int 146 compare_pctcpu_noidle(const void *a, const void *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(struct kinfo_proc *kp) 161 { 162 struct proc *p; 163 struct eproc *e; 164 int flag; 165 char *cp; 166 char buf[5]; 167 static char statestr[4]; 168 169 p = &(kp->kp_proc); 170 e = &(kp->kp_eproc); 171 172 flag = p->p_flag; 173 cp = buf; 174 175 switch (p->p_stat) { 176 case SSTOP: 177 *cp = 'T'; 178 break; 179 180 case SSLEEP: 181 if (flag & P_SINTR) /* interuptable (long) */ 182 *cp = p->p_slptime >= MAXSLP ? 'I' : 'S'; 183 else 184 *cp = 'D'; 185 break; 186 187 case SRUN: 188 case SIDL: 189 case SONPROC: 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(struct kinfo_proc *kp) 231 { 232 static char ttystr[4]; 233 char *ttyname; 234 struct eproc *e; 235 236 e = &(kp->kp_eproc); 237 238 if (e->e_tdev == NODEV || (ttyname = devname(e->e_tdev, S_IFCHR)) == NULL) 239 strcpy(ttystr, "??"); 240 else { 241 if (strncmp(ttyname, "tty", 3) == 0 || 242 strncmp(ttyname, "dty", 3) == 0) 243 ttyname += 3; 244 snprintf(ttystr, sizeof(ttystr), "%s%c", ttyname, e->e_flag & EPROC_CTTY ? ' ' : '-'); 245 } 246 247 return ttystr; 248 } 249 250 #define pgtok(a) (((a)*getpagesize())/1024) 251 252 int 253 vsz2int(struct kinfo_proc *kp) 254 { 255 struct eproc *e; 256 int i; 257 258 e = &(kp->kp_eproc); 259 i = pgtok(e->e_vm.vm_dsize + e->e_vm.vm_ssize + e->e_vm.vm_tsize); 260 261 return ((i < 0) ? 0 : i); 262 } 263 264 int 265 rss2int(struct kinfo_proc *kp) 266 { 267 struct eproc *e; 268 int i; 269 270 e = &(kp->kp_eproc); 271 i = pgtok(e->e_vm.vm_rssize); 272 273 /* XXX don't have info about shared */ 274 return ((i < 0) ? 0 : i); 275 } 276 277 char * 278 comm2str(struct kinfo_proc *kp) 279 { 280 char **argv, **pt; 281 static char commstr[41]; 282 struct proc *p; 283 284 p = &(kp->kp_proc); 285 commstr[0]='\0'; 286 287 argv = kvm_getargv(kd, kp, 40); 288 if ((pt = argv) != NULL) { 289 while (*pt) { 290 strcat(commstr, *pt); 291 pt++; 292 strcat(commstr, " "); 293 } 294 } else { 295 commstr[0] = '('; 296 commstr[1] = '\0'; 297 strncat(commstr, p->p_comm, sizeof(commstr) - 1); 298 strcat(commstr, ")"); 299 } 300 301 return commstr; 302 } 303 304 double 305 pmem2float(struct kinfo_proc *kp) 306 { 307 struct proc *p; 308 struct eproc *e; 309 double fracmem; 310 int szptudot; 311 312 p = &(kp->kp_proc); 313 e = &(kp->kp_eproc); 314 315 if ((p->p_flag & P_INMEM) == 0) 316 return (0.0); 317 /* XXX want pmap ptpages, segtab, etc. (per architecture) */ 318 szptudot = USPACE/getpagesize(); 319 /* XXX don't have info about shared */ 320 fracmem = ((double)e->e_vm.vm_rssize + szptudot)/mempages; 321 return (fracmem >= 0) ? 100.0 * fracmem : 0; 322 } 323 324 char * 325 start2str(struct kinfo_proc *kp) 326 { 327 struct proc *p; 328 struct pstats pstats; 329 struct timeval u_start; 330 struct tm *tp; 331 time_t startt; 332 static char startstr[10]; 333 334 p = &(kp->kp_proc); 335 336 kvm_read(kd, (u_long)&(p->p_addr->u_stats), (char *)&pstats, sizeof(pstats)); 337 u_start = pstats.p_start; 338 339 startt = u_start.tv_sec; 340 tp = localtime(&startt); 341 if (now == 0) 342 time(&now); 343 if (now - u_start.tv_sec < 24 * SECSPERHOUR) { 344 /* I *hate* SCCS... */ 345 static char fmt[] = __CONCAT("%l:%", "M%p"); 346 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 347 } else if (now - u_start.tv_sec < 7 * SECSPERDAY) { 348 /* I *hate* SCCS... */ 349 static char fmt[] = __CONCAT("%a%", "I%p"); 350 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 351 } else 352 strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp); 353 354 return startstr; 355 } 356 357 char * 358 time2str(struct kinfo_proc *kp) 359 { 360 long secs; 361 long psecs; /* "parts" of a second. first micro, then centi */ 362 static char timestr[10]; 363 struct proc *p; 364 365 p = &(kp->kp_proc); 366 367 if (P_ZOMBIE(p)) { 368 secs = 0; 369 psecs = 0; 370 } else { 371 /* 372 * This counts time spent handling interrupts. We could 373 * fix this, but it is not 100% trivial (and interrupt 374 * time fractions only work on the sparc anyway). XXX 375 */ 376 secs = p->p_rtime.tv_sec; 377 psecs = p->p_rtime.tv_usec; 378 /* if (sumrusage) { 379 secs += k->ki_u.u_cru.ru_utime.tv_sec + 380 k->ki_u.u_cru.ru_stime.tv_sec; 381 psecs += k->ki_u.u_cru.ru_utime.tv_usec + 382 k->ki_u.u_cru.ru_stime.tv_usec; 383 } */ 384 /* 385 * round and scale to 100's 386 */ 387 psecs = (psecs + 5000) / 10000; 388 secs += psecs / 100; 389 psecs = psecs % 100; 390 } 391 snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, secs%60, psecs); 392 393 return timestr; 394 } 395 396 void 397 ps_user(char *args) 398 { 399 uid_t uid; 400 401 if (args == NULL) 402 args = ""; 403 if (strcmp(args, "+") == 0) { 404 uid = SHOWUSER_ANY; 405 } else if (uid_from_user(args, &uid) != 0) { 406 error("%s: unknown user", args); 407 return; 408 } 409 410 showuser = uid; 411 display(0); 412 } 413