1 /* $NetBSD: ps.c,v 1.31 2008/12/29 01:48:19 christos 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.31 2008/12/29 01:48:19 christos 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 "systat.h" 65 #include "extern.h" 66 #include "ps.h" 67 68 int compare_pctcpu_noidle(const void *, const void *); 69 char *state2str(struct kinfo_proc2 *); 70 char *tty2str(struct kinfo_proc2 *); 71 int rss2int(struct kinfo_proc2 *); 72 int vsz2int(struct kinfo_proc2 *); 73 char *comm2str(struct kinfo_proc2 *); 74 double pmem2float(struct kinfo_proc2 *); 75 char *start2str(struct kinfo_proc2 *); 76 char *time2str(struct kinfo_proc2 *); 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_str; 98 pid_t pid; 99 double pctcpu, pctmem; 100 struct kinfo_proc2 *kp; 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 kp = pt[k].pt_kp; 115 if (showuser != SHOWUSER_ANY && kp->p_uid != showuser) 116 continue; 117 user = user_from_uid(kp->p_uid, 0); 118 pid = kp->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_str = 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, 135 time_str, comm); 136 i--; 137 } 138 wmove(wnd, y, 0); 139 wclrtobot(wnd); 140 } 141 142 int 143 compare_pctcpu_noidle(const void *a, const void *b) 144 { 145 if (((const struct p_times *) a)->pt_kp == NULL) 146 return 1; 147 148 if (((const struct p_times *) b)->pt_kp == NULL) 149 return -1; 150 151 return (((const struct p_times *) a)->pt_pctcpu > 152 ((const struct p_times *) b)->pt_pctcpu)? -1: 1; 153 } 154 155 /* from here down adapted from .../src/usr.bin/ps/print.c . Any mistakes are my own, however. */ 156 char * 157 state2str(struct kinfo_proc2 *kp) 158 { 159 int flag; 160 char *cp; 161 char buf[5]; 162 static char statestr[4]; 163 164 flag = kp->p_flag; 165 cp = buf; 166 167 switch (kp->p_stat) { 168 case LSSTOP: 169 *cp = 'T'; 170 break; 171 172 case LSSLEEP: 173 if (flag & L_SINTR) /* interruptable (long) */ 174 *cp = kp->p_slptime >= maxslp ? 'I' : 'S'; 175 else 176 *cp = 'D'; 177 break; 178 179 case LSRUN: 180 case LSIDL: 181 case LSONPROC: 182 *cp = 'R'; 183 break; 184 185 case LSZOMB: 186 #ifdef LSDEAD 187 case LSDEAD: 188 #endif 189 *cp = 'Z'; 190 break; 191 192 default: 193 *cp = '?'; 194 } 195 cp++; 196 if (flag & L_INMEM) { 197 } else 198 *cp++ = 'W'; 199 if (kp->p_nice < NZERO) 200 *cp++ = '<'; 201 else if (kp->p_nice > NZERO) 202 *cp++ = 'N'; 203 if (flag & P_TRACED) 204 *cp++ = 'X'; 205 if (flag & P_WEXIT && 206 /* XXX - I don't like this */ 207 (kp->p_stat != LSZOMB)) 208 *cp++ = 'E'; 209 if (flag & P_PPWAIT) 210 *cp++ = 'V'; 211 if ((flag & P_SYSTEM) || kp->p_holdcnt) 212 *cp++ = 'L'; 213 if (kp->p_eflag & EPROC_SLEADER) 214 *cp++ = 's'; 215 if ((flag & P_CONTROLT) && kp->p__pgid == kp->p_tpgid) 216 *cp++ = '+'; 217 *cp = '\0'; 218 snprintf(statestr, sizeof(statestr), "%-s", buf); 219 220 return statestr; 221 } 222 223 char * 224 tty2str(struct kinfo_proc2 *kp) 225 { 226 static char ttystr[4]; 227 char *tty_name; 228 229 if (kp->p_tdev == (uint32_t)NODEV || 230 (tty_name = devname(kp->p_tdev, S_IFCHR)) == NULL) 231 strlcpy(ttystr, "??", sizeof(ttystr)); 232 else { 233 if (strncmp(tty_name, "tty", 3) == 0 || 234 strncmp(tty_name, "dty", 3) == 0) 235 tty_name += 3; 236 snprintf(ttystr, sizeof(ttystr), "%s%c", tty_name, 237 kp->p_eflag & EPROC_CTTY ? ' ' : '-'); 238 } 239 240 return ttystr; 241 } 242 243 #define pgtok(a) (((a)*getpagesize())/1024) 244 245 int 246 vsz2int(struct kinfo_proc2 *kp) 247 { 248 int i; 249 250 i = pgtok(kp->p_vm_dsize + kp->p_vm_ssize + kp->p_vm_tsize); 251 252 return ((i < 0) ? 0 : i); 253 } 254 255 int 256 rss2int(struct kinfo_proc2 *kp) 257 { 258 int i; 259 260 i = pgtok(kp->p_vm_rssize); 261 262 /* XXX don't have info about shared */ 263 return ((i < 0) ? 0 : i); 264 } 265 266 char * 267 comm2str(struct kinfo_proc2 *kp) 268 { 269 char **argv; 270 static char commstr[41]; 271 272 commstr[0]='\0'; 273 274 argv = kvm_getargv2(kd, kp, 40); 275 if (argv != NULL) { 276 while (*argv) { 277 strlcat(commstr, *argv, sizeof(commstr)); 278 argv++; 279 strlcat(commstr, " ", sizeof(commstr)); 280 } 281 } else { 282 const char *fmt; 283 284 /* 285 * Commands that don't set an argv vector are printed with 286 * square brackets if they are system commands. Otherwise 287 * they are printed within parentheses. 288 */ 289 if (kp->p_flag & P_SYSTEM) 290 fmt = "[%s]"; 291 else 292 fmt = "(%s)"; 293 294 snprintf(commstr, sizeof(commstr), fmt, kp->p_comm); 295 } 296 297 return commstr; 298 } 299 300 double 301 pmem2float(struct kinfo_proc2 *kp) 302 { 303 double fracmem; 304 int szptudot = 0; 305 306 /* XXX - I don't like this. */ 307 if ((kp->p_flag & L_INMEM) == 0) 308 return (0.0); 309 #ifdef USPACE 310 /* XXX want pmap ptpages, segtab, etc. (per architecture) */ 311 szptudot = USPACE/getpagesize(); 312 #endif 313 /* XXX don't have info about shared */ 314 fracmem = ((double)kp->p_vm_rssize + szptudot)/mempages; 315 return (fracmem >= 0) ? 100.0 * fracmem : 0; 316 } 317 318 char * 319 start2str(struct kinfo_proc2 *kp) 320 { 321 struct timeval u_start; 322 struct tm *tp; 323 time_t startt; 324 static char startstr[10]; 325 326 u_start.tv_sec = kp->p_ustart_sec; 327 u_start.tv_usec = kp->p_ustart_usec; 328 329 startt = u_start.tv_sec; 330 tp = localtime(&startt); 331 if (now == 0) 332 time(&now); 333 if (now - u_start.tv_sec < 24 * SECSPERHOUR) { 334 /* I *hate* SCCS... */ 335 static char fmt[] = "%l:%" "M%p"; 336 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 337 } else if (now - u_start.tv_sec < 7 * SECSPERDAY) { 338 /* I *hate* SCCS... */ 339 static char fmt[] = "%a%" "I%p"; 340 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 341 } else 342 strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp); 343 344 return startstr; 345 } 346 347 char * 348 time2str(struct kinfo_proc2 *kp) 349 { 350 long secs; 351 long psecs; /* "parts" of a second. first micro, then centi */ 352 static char timestr[10]; 353 354 /* XXX - I don't like this. */ 355 if (kp->p_stat == SZOMB) { 356 secs = 0; 357 psecs = 0; 358 } else { 359 /* 360 * This counts time spent handling interrupts. We could 361 * fix this, but it is not 100% trivial (and interrupt 362 * time fractions only work on the sparc anyway). XXX 363 */ 364 secs = kp->p_rtime_sec; 365 psecs = kp->p_rtime_usec; 366 #if 0 367 if (sumrusage) { 368 secs += k->ki_u.u_cru.ru_utime.tv_sec + 369 k->ki_u.u_cru.ru_stime.tv_sec; 370 psecs += k->ki_u.u_cru.ru_utime.tv_usec + 371 k->ki_u.u_cru.ru_stime.tv_usec; 372 } 373 #endif 374 /* 375 * round and scale to 100's 376 */ 377 psecs = (psecs + 5000) / 10000; 378 secs += psecs / 100; 379 psecs = psecs % 100; 380 } 381 snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, 382 secs%60, psecs); 383 384 return timestr; 385 } 386 387 void 388 ps_user(char *args) 389 { 390 uid_t uid; 391 392 if (args == NULL || *args == 0 || strcmp(args, "+") == 0) { 393 uid = SHOWUSER_ANY; 394 } else if (uid_from_user(args, &uid) != 0) { 395 error("%s: unknown user", args); 396 return; 397 } 398 399 showuser = uid; 400 display(0); 401 } 402