1 /* $NetBSD: ps.c,v 1.39 2020/08/26 10:56:01 simonb 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.39 2020/08/26 10:56:01 simonb Exp $"); 49 #endif /* not lint */ 50 51 #include <sys/param.h> 52 #include <sys/sched.h> 53 #include <sys/sysctl.h> 54 #include <sys/stat.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 >= (uint32_t)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 (kp->p_nice < NZERO) 197 *cp++ = '<'; 198 else if (kp->p_nice > NZERO) 199 *cp++ = 'N'; 200 if (flag & P_TRACED) 201 *cp++ = 'X'; 202 if (flag & P_WEXIT && 203 /* XXX - I don't like this */ 204 (kp->p_stat != LSZOMB)) 205 *cp++ = 'E'; 206 if (flag & P_PPWAIT) 207 *cp++ = 'V'; 208 if (kp->p_eflag & EPROC_SLEADER) 209 *cp++ = 's'; 210 if ((flag & P_CONTROLT) && kp->p__pgid == kp->p_tpgid) 211 *cp++ = '+'; 212 *cp = '\0'; 213 snprintf(statestr, sizeof(statestr), "%-s", buf); 214 215 return statestr; 216 } 217 218 char * 219 tty2str(struct kinfo_proc2 *kp) 220 { 221 static char ttystr[4]; 222 char *tty_name; 223 224 if (kp->p_tdev == (uint32_t)NODEV || 225 (tty_name = devname(kp->p_tdev, S_IFCHR)) == NULL) 226 strlcpy(ttystr, "??", sizeof(ttystr)); 227 else { 228 if (strncmp(tty_name, "tty", 3) == 0 || 229 strncmp(tty_name, "dty", 3) == 0) 230 tty_name += 3; 231 snprintf(ttystr, sizeof(ttystr), "%s%c", tty_name, 232 kp->p_eflag & EPROC_CTTY ? ' ' : '-'); 233 } 234 235 return ttystr; 236 } 237 238 #define pgtok(a) (((a)*getpagesize())/1024) 239 240 int 241 vsz2int(struct kinfo_proc2 *kp) 242 { 243 int i; 244 245 i = pgtok(kp->p_vm_msize); 246 247 return ((i < 0) ? 0 : i); 248 } 249 250 int 251 rss2int(struct kinfo_proc2 *kp) 252 { 253 int i; 254 255 i = pgtok(kp->p_vm_rssize); 256 257 /* XXX don't have info about shared */ 258 return ((i < 0) ? 0 : i); 259 } 260 261 char * 262 comm2str(struct kinfo_proc2 *kp) 263 { 264 char **argv; 265 static char commstr[41]; 266 267 commstr[0]='\0'; 268 269 argv = kvm_getargv2(kd, kp, 40); 270 if (argv != NULL) { 271 while (*argv) { 272 strlcat(commstr, *argv, sizeof(commstr)); 273 argv++; 274 strlcat(commstr, " ", sizeof(commstr)); 275 } 276 } else { 277 const char *fmt; 278 279 /* 280 * Commands that don't set an argv vector are printed with 281 * square brackets if they are system commands. Otherwise 282 * they are printed within parentheses. 283 */ 284 if (kp->p_flag & P_SYSTEM) 285 fmt = "[]"; 286 else 287 fmt = "()"; 288 289 snprintf(commstr, sizeof(commstr), "%c%s%c", fmt[0], 290 kp->p_comm, fmt[1]); 291 } 292 293 return commstr; 294 } 295 296 double 297 pmem2float(struct kinfo_proc2 *kp) 298 { 299 double fracmem; 300 static int szptudot = -1; 301 302 /* 303 * XXX want pmap ptpages, segtab, etc. (per architecture), 304 * not just the uarea. 305 */ 306 if (szptudot < 0) { 307 int mib[2]; 308 size_t size; 309 int uspace; 310 311 mib[0] = CTL_VM; 312 mib[1] = VM_USPACE; 313 size = sizeof(uspace); 314 if (sysctl(mib, 2, &uspace, &size, NULL, 0) == 0) { 315 szptudot = uspace / getpagesize(); 316 } else { 317 /* pick a vaguely useful default */ 318 szptudot = getpagesize(); 319 } 320 } 321 322 /* XXX don't have info about shared */ 323 fracmem = ((double)kp->p_vm_rssize + szptudot) / mempages; 324 return (fracmem >= 0) ? 100.0 * fracmem : 0; 325 } 326 327 char * 328 start2str(struct kinfo_proc2 *kp) 329 { 330 struct timeval u_start; 331 struct tm *tp; 332 time_t startt; 333 static char startstr[10]; 334 335 u_start.tv_sec = kp->p_ustart_sec; 336 u_start.tv_usec = kp->p_ustart_usec; 337 338 startt = u_start.tv_sec; 339 tp = localtime(&startt); 340 if (now == 0) 341 time(&now); 342 if (now - u_start.tv_sec < 24 * SECSPERHOUR) { 343 /* I *hate* SCCS... */ 344 strftime(startstr, sizeof(startstr) - 1, "%l:%" "M%p", tp); 345 } else if (now - u_start.tv_sec < 7 * SECSPERDAY) { 346 /* I *hate* SCCS... */ 347 strftime(startstr, sizeof(startstr) - 1, "%a%" "I%p", tp); 348 } else 349 strftime(startstr, sizeof(startstr) - 1, "%e%b%y", tp); 350 351 return startstr; 352 } 353 354 char * 355 time2str(struct kinfo_proc2 *kp) 356 { 357 long secs; 358 long psecs; /* "parts" of a second. first micro, then centi */ 359 static char timestr[18]; 360 361 /* XXX - I don't like this. */ 362 if (kp->p_stat == SZOMB) { 363 secs = 0; 364 psecs = 0; 365 } else { 366 /* 367 * This counts time spent handling interrupts. We could 368 * fix this, but it is not 100% trivial (and interrupt 369 * time fractions only work on the sparc anyway). XXX 370 */ 371 secs = kp->p_rtime_sec; 372 psecs = kp->p_rtime_usec; 373 #if 0 374 if (sumrusage) { 375 secs += k->ki_u.u_cru.ru_utime.tv_sec + 376 k->ki_u.u_cru.ru_stime.tv_sec; 377 psecs += k->ki_u.u_cru.ru_utime.tv_usec + 378 k->ki_u.u_cru.ru_stime.tv_usec; 379 } 380 #endif 381 /* 382 * round and scale to 100's 383 */ 384 psecs = (psecs + 5000) / 10000; 385 secs += psecs / 100; 386 psecs = psecs % 100; 387 } 388 snprintf(timestr, sizeof(timestr), "%3ld:%02ld.%02ld", secs/60, 389 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 || *args == 0 || strcmp(args, "+") == 0) { 400 uid = SHOWUSER_ANY; 401 } else if (uid_from_user(args, &uid) != 0) { 402 error("%s: unknown user", args); 403 return; 404 } 405 406 showuser = uid; 407 display(0); 408 } 409