1 /* $NetBSD: ps.c,v 1.20 2002/05/04 18:44:27 thorpej 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.20 2002/05/04 18:44:27 thorpej 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_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; 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 = 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_proc2 *kp) 157 { 158 int flag; 159 char *cp; 160 char buf[5]; 161 static char statestr[4]; 162 163 flag = kp->p_flag; 164 cp = buf; 165 166 switch (kp->p_stat) { 167 case SSTOP: 168 *cp = 'T'; 169 break; 170 171 case SSLEEP: 172 if (flag & P_SINTR) /* interuptable (long) */ 173 *cp = kp->p_slptime >= maxslp ? 'I' : 'S'; 174 else 175 *cp = 'D'; 176 break; 177 178 case SRUN: 179 case SIDL: 180 case SONPROC: 181 *cp = 'R'; 182 break; 183 184 case SZOMB: 185 #ifdef SDEAD 186 case SDEAD: 187 #endif 188 *cp = 'Z'; 189 break; 190 191 default: 192 *cp = '?'; 193 } 194 cp++; 195 if (flag & P_INMEM) { 196 } else 197 *cp++ = 'W'; 198 if (kp->p_nice < NZERO) 199 *cp++ = '<'; 200 else if (kp->p_nice > NZERO) 201 *cp++ = 'N'; 202 if (flag & P_TRACED) 203 *cp++ = 'X'; 204 if (flag & P_WEXIT && 205 /* XXX - I don't like this */ 206 (kp->p_stat == SZOMB || kp->p_stat == SDEAD) == 0) 207 *cp++ = 'E'; 208 if (flag & P_PPWAIT) 209 *cp++ = 'V'; 210 if ((flag & P_SYSTEM) || kp->p_holdcnt) 211 *cp++ = 'L'; 212 if (kp->p_eflag & EPROC_SLEADER) 213 *cp++ = 's'; 214 if ((flag & P_CONTROLT) && kp->p__pgid == kp->p_tpgid) 215 *cp++ = '+'; 216 *cp = '\0'; 217 snprintf(statestr, sizeof(statestr), "%-s", buf); 218 219 return statestr; 220 } 221 222 char * 223 tty2str(struct kinfo_proc2 *kp) 224 { 225 static char ttystr[4]; 226 char *ttyname; 227 228 if (kp->p_tdev == NODEV || 229 (ttyname = devname(kp->p_tdev, S_IFCHR)) == NULL) 230 strcpy(ttystr, "??"); 231 else { 232 if (strncmp(ttyname, "tty", 3) == 0 || 233 strncmp(ttyname, "dty", 3) == 0) 234 ttyname += 3; 235 snprintf(ttystr, sizeof(ttystr), "%s%c", ttyname, 236 kp->p_eflag & EPROC_CTTY ? ' ' : '-'); 237 } 238 239 return ttystr; 240 } 241 242 #define pgtok(a) (((a)*getpagesize())/1024) 243 244 int 245 vsz2int(struct kinfo_proc2 *kp) 246 { 247 int i; 248 249 i = pgtok(kp->p_vm_dsize + kp->p_vm_ssize + kp->p_vm_tsize); 250 251 return ((i < 0) ? 0 : i); 252 } 253 254 int 255 rss2int(struct kinfo_proc2 *kp) 256 { 257 int i; 258 259 i = pgtok(kp->p_vm_rssize); 260 261 /* XXX don't have info about shared */ 262 return ((i < 0) ? 0 : i); 263 } 264 265 char * 266 comm2str(struct kinfo_proc2 *kp) 267 { 268 char **argv, **pt; 269 static char commstr[41]; 270 271 commstr[0]='\0'; 272 273 argv = kvm_getargv2(kd, kp, 40); 274 if ((pt = argv) != NULL) { 275 while (*pt) { 276 strcat(commstr, *pt); 277 pt++; 278 strcat(commstr, " "); 279 } 280 } else { 281 commstr[0] = '('; 282 commstr[1] = '\0'; 283 strncat(commstr, kp->p_comm, sizeof(commstr) - 1); 284 strcat(commstr, ")"); 285 } 286 287 return commstr; 288 } 289 290 double 291 pmem2float(struct kinfo_proc2 *kp) 292 { 293 double fracmem; 294 int szptudot = 0; 295 296 /* XXX - I don't like this. */ 297 if ((kp->p_flag & P_INMEM) == 0) 298 return (0.0); 299 #ifdef USPACE 300 /* XXX want pmap ptpages, segtab, etc. (per architecture) */ 301 szptudot = USPACE/getpagesize(); 302 #endif 303 /* XXX don't have info about shared */ 304 fracmem = ((double)kp->p_vm_rssize + szptudot)/mempages; 305 return (fracmem >= 0) ? 100.0 * fracmem : 0; 306 } 307 308 char * 309 start2str(struct kinfo_proc2 *kp) 310 { 311 struct timeval u_start; 312 struct tm *tp; 313 time_t startt; 314 static char startstr[10]; 315 316 u_start.tv_sec = kp->p_ustart_sec; 317 u_start.tv_usec = kp->p_ustart_usec; 318 319 startt = u_start.tv_sec; 320 tp = localtime(&startt); 321 if (now == 0) 322 time(&now); 323 if (now - u_start.tv_sec < 24 * SECSPERHOUR) { 324 /* I *hate* SCCS... */ 325 static char fmt[] = "%l:%" "M%p"; 326 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 327 } else if (now - u_start.tv_sec < 7 * SECSPERDAY) { 328 /* I *hate* SCCS... */ 329 static char fmt[] = "%a%" "I%p"; 330 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 331 } else 332 strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp); 333 334 return startstr; 335 } 336 337 char * 338 time2str(struct kinfo_proc2 *kp) 339 { 340 long secs; 341 long psecs; /* "parts" of a second. first micro, then centi */ 342 static char timestr[10]; 343 344 /* XXX - I don't like this. */ 345 if (kp->p_stat == SZOMB || kp->p_stat == SDEAD) { 346 secs = 0; 347 psecs = 0; 348 } else { 349 /* 350 * This counts time spent handling interrupts. We could 351 * fix this, but it is not 100% trivial (and interrupt 352 * time fractions only work on the sparc anyway). XXX 353 */ 354 secs = kp->p_rtime_sec; 355 psecs = kp->p_rtime_usec; 356 #if 0 357 if (sumrusage) { 358 secs += k->ki_u.u_cru.ru_utime.tv_sec + 359 k->ki_u.u_cru.ru_stime.tv_sec; 360 psecs += k->ki_u.u_cru.ru_utime.tv_usec + 361 k->ki_u.u_cru.ru_stime.tv_usec; 362 } 363 #endif 364 /* 365 * round and scale to 100's 366 */ 367 psecs = (psecs + 5000) / 10000; 368 secs += psecs / 100; 369 psecs = psecs % 100; 370 } 371 snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, 372 secs%60, psecs); 373 374 return timestr; 375 } 376 377 void 378 ps_user(char *args) 379 { 380 uid_t uid; 381 382 if (args == NULL) 383 args = ""; 384 if (strcmp(args, "+") == 0) { 385 uid = SHOWUSER_ANY; 386 } else if (uid_from_user(args, &uid) != 0) { 387 error("%s: unknown user", args); 388 return; 389 } 390 391 showuser = uid; 392 display(0); 393 } 394