1 /* $NetBSD: ps.c,v 1.25 2004/01/11 19:15:50 jdolecek 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.25 2004/01/11 19:15:50 jdolecek 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 LSSTOP: 168 *cp = 'T'; 169 break; 170 171 case LSSLEEP: 172 if (flag & L_SINTR) /* interruptable (long) */ 173 *cp = kp->p_slptime >= maxslp ? 'I' : 'S'; 174 else 175 *cp = 'D'; 176 break; 177 178 case LSRUN: 179 case LSIDL: 180 case LSONPROC: 181 *cp = 'R'; 182 break; 183 184 case LSZOMB: 185 #ifdef LSDEAD 186 case LSDEAD: 187 #endif 188 *cp = 'Z'; 189 break; 190 191 default: 192 *cp = '?'; 193 } 194 cp++; 195 if (flag & L_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 == LSZOMB || kp->p_stat == LSDEAD) == 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 strlcpy(ttystr, "??", sizeof(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 strlcat(commstr, *pt, sizeof(commstr)); 277 pt++; 278 strlcat(commstr, " ", sizeof(commstr)); 279 } 280 } else 281 snprintf(commstr, sizeof(commstr), "(%s)", kp->p_comm); 282 283 return commstr; 284 } 285 286 double 287 pmem2float(struct kinfo_proc2 *kp) 288 { 289 double fracmem; 290 int szptudot = 0; 291 292 /* XXX - I don't like this. */ 293 if ((kp->p_flag & L_INMEM) == 0) 294 return (0.0); 295 #ifdef USPACE 296 /* XXX want pmap ptpages, segtab, etc. (per architecture) */ 297 szptudot = USPACE/getpagesize(); 298 #endif 299 /* XXX don't have info about shared */ 300 fracmem = ((double)kp->p_vm_rssize + szptudot)/mempages; 301 return (fracmem >= 0) ? 100.0 * fracmem : 0; 302 } 303 304 char * 305 start2str(struct kinfo_proc2 *kp) 306 { 307 struct timeval u_start; 308 struct tm *tp; 309 time_t startt; 310 static char startstr[10]; 311 312 u_start.tv_sec = kp->p_ustart_sec; 313 u_start.tv_usec = kp->p_ustart_usec; 314 315 startt = u_start.tv_sec; 316 tp = localtime(&startt); 317 if (now == 0) 318 time(&now); 319 if (now - u_start.tv_sec < 24 * SECSPERHOUR) { 320 /* I *hate* SCCS... */ 321 static char fmt[] = "%l:%" "M%p"; 322 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 323 } else if (now - u_start.tv_sec < 7 * SECSPERDAY) { 324 /* I *hate* SCCS... */ 325 static char fmt[] = "%a%" "I%p"; 326 strftime(startstr, sizeof(startstr) - 1, fmt, tp); 327 } else 328 strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp); 329 330 return startstr; 331 } 332 333 char * 334 time2str(struct kinfo_proc2 *kp) 335 { 336 long secs; 337 long psecs; /* "parts" of a second. first micro, then centi */ 338 static char timestr[10]; 339 340 /* XXX - I don't like this. */ 341 if (kp->p_stat == SZOMB) { 342 secs = 0; 343 psecs = 0; 344 } else { 345 /* 346 * This counts time spent handling interrupts. We could 347 * fix this, but it is not 100% trivial (and interrupt 348 * time fractions only work on the sparc anyway). XXX 349 */ 350 secs = kp->p_rtime_sec; 351 psecs = kp->p_rtime_usec; 352 #if 0 353 if (sumrusage) { 354 secs += k->ki_u.u_cru.ru_utime.tv_sec + 355 k->ki_u.u_cru.ru_stime.tv_sec; 356 psecs += k->ki_u.u_cru.ru_utime.tv_usec + 357 k->ki_u.u_cru.ru_stime.tv_usec; 358 } 359 #endif 360 /* 361 * round and scale to 100's 362 */ 363 psecs = (psecs + 5000) / 10000; 364 secs += psecs / 100; 365 psecs = psecs % 100; 366 } 367 snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, 368 secs%60, psecs); 369 370 return timestr; 371 } 372 373 void 374 ps_user(char *args) 375 { 376 uid_t uid; 377 378 if (args == NULL) 379 args = ""; 380 if (strcmp(args, "+") == 0) { 381 uid = SHOWUSER_ANY; 382 } else if (uid_from_user(args, &uid) != 0) { 383 error("%s: unknown user", args); 384 return; 385 } 386 387 showuser = uid; 388 display(0); 389 } 390