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