1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2013 Gary Mills 23 * 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 * 27 * Portions Copyright 2009 Chad Mynhier 28 * Copyright 2018 Joyent, Inc. All rights reserved. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/param.h> 33 #include <sys/resource.h> 34 #include <sys/priocntl.h> 35 #include <sys/rtpriocntl.h> 36 #include <sys/tspriocntl.h> 37 #include <zone.h> 38 39 #include <libintl.h> 40 #include <limits.h> 41 #include <wchar.h> 42 #include <unistd.h> 43 #include <string.h> 44 #include <stdlib.h> 45 #include <stdarg.h> 46 #include <stdio.h> 47 #include <stdio_ext.h> 48 #include <errno.h> 49 #include <ctype.h> 50 #include <poll.h> 51 #include <project.h> 52 53 #include "prfile.h" 54 #include "prstat.h" 55 #include "prutil.h" 56 57 static char PRG_FMT[] = "%s: "; 58 static char ERR_FMT[] = ": %s\n"; 59 static char *progname; 60 static char projbuf[PROJECT_BUFSZ]; 61 62 #define RLIMIT_NOFILE_MAX 32767 63 64 /*PRINTFLIKE1*/ 65 void 66 Warn(char *format, ...) 67 { 68 int err = errno; 69 va_list alist; 70 71 if (progname != NULL) 72 (void) fprintf(stderr, PRG_FMT, progname); 73 va_start(alist, format); 74 (void) vfprintf(stderr, format, alist); 75 va_end(alist); 76 if (strchr(format, '\n') == NULL) 77 (void) fprintf(stderr, gettext(ERR_FMT), strerror(err)); 78 } 79 80 /*PRINTFLIKE1*/ 81 void 82 Die(char *format, ...) 83 { 84 int err = errno; 85 va_list alist; 86 87 if (progname != NULL) 88 (void) fprintf(stderr, PRG_FMT, progname); 89 va_start(alist, format); 90 (void) vfprintf(stderr, format, alist); 91 va_end(alist); 92 if (strchr(format, '\n') == NULL) 93 (void) fprintf(stderr, gettext(ERR_FMT), strerror(err)); 94 exit(1); 95 } 96 97 void 98 Progname(char *arg0) 99 { 100 char *p = strrchr(arg0, '/'); 101 if (p == NULL) 102 p = arg0; 103 else 104 p++; 105 progname = p; 106 } 107 108 void 109 Usage() 110 { 111 (void) fprintf(stderr, gettext( 112 "Usage:\tprstat [-acHJLmrRtTvWZ] [-u euidlist] [-U uidlist]\n" 113 "\t[-p pidlist] [-P cpulist] [-C psrsetlist] [-h lgrouplist]\n" 114 "\t[-j projidlist] [-k taskidlist] [-z zoneidlist]\n" 115 "\t[-s key | -S key] [-n nprocs[,nusers]] [-d d|u]\n" 116 "\t[interval [counter]]\n")); 117 exit(1); 118 } 119 120 int 121 Atoi(char *p) 122 { 123 int i; 124 char *q; 125 errno = 0; 126 i = (int)strtol(p, &q, 10); 127 if (errno != 0 || q == p || i < 0 || *q != '\0') 128 Die(gettext("illegal argument -- %s\n"), p); 129 /*NOTREACHED*/ 130 else 131 return (i); 132 return (0); /* keep gcc happy */ 133 } 134 135 void 136 Format_size(char *str, size_t size, int length) 137 { 138 char tag = 'K'; 139 if (size >= 10000) { 140 size = (size + 512) / 1024; 141 tag = 'M'; 142 if (size >= 10000) { 143 size = (size + 512) / 1024; 144 tag = 'G'; 145 } 146 } 147 (void) snprintf(str, length, "%4d%c", (int)size, tag); 148 } 149 150 void 151 Format_time(char *str, ulong_t time, int length) 152 { 153 (void) snprintf(str, length, gettext("%3d:%2.2d:%2.2d"), /* hr:mm:ss */ 154 (int)time/3600, (int)(time % 3600)/60, (int)time % 60); 155 } 156 157 void 158 Format_pct(char *str, float val, int length) 159 { 160 if (val > (float)100) 161 val = 100; 162 if (val < 0) 163 val = 0; 164 165 if (val < (float)9.95) 166 (void) snprintf(str, length, "%1.1f", val); 167 else 168 (void) snprintf(str, length, "%.0f", val); 169 } 170 171 void 172 Format_num(char *str, int num, int length) 173 { 174 if (num >= 100000) { 175 (void) snprintf(str, length, ".%1dM", num/100000); 176 } else { 177 if (num >= 1000) 178 (void) snprintf(str, length, "%2dK", num/1000); 179 else 180 (void) snprintf(str, length, "%3d", num); 181 } 182 } 183 184 void 185 Format_state(char *str, char state, processorid_t pr_id, int length) 186 { 187 switch (state) { 188 case 'S': 189 (void) strncpy(str, "sleep", length); 190 break; 191 case 'R': 192 (void) strncpy(str, "run", length); 193 break; 194 case 'Z': 195 (void) strncpy(str, "zombie", length); 196 break; 197 case 'T': 198 (void) strncpy(str, "stop", length); 199 break; 200 case 'I': 201 (void) strncpy(str, "idle", length); 202 break; 203 case 'W': 204 (void) strncpy(str, "wait", length); 205 break; 206 case 'O': 207 (void) snprintf(str, length, "cpu%-3d", (int)pr_id); 208 break; 209 default: 210 (void) strncpy(str, "?", length); 211 break; 212 } 213 } 214 215 void * 216 Realloc(void *ptr, size_t size) 217 { 218 int cnt = 0; 219 void *sav = ptr; 220 221 eagain: if ((ptr = realloc(ptr, size))) 222 return (ptr); 223 224 if ((++cnt <= 3) && (errno == EAGAIN)) { 225 Warn(gettext("realloc() failed, attempt %d"), cnt); 226 (void) poll(NULL, 0, 5000); /* wait for 5 seconds */ 227 ptr = sav; 228 goto eagain; 229 } 230 ptr = sav; 231 Die(gettext("not enough memory")); 232 /*NOTREACHED*/ 233 return (NULL); /* keep gcc happy */ 234 } 235 236 void * 237 Malloc(size_t size) 238 { 239 return (Realloc(NULL, size)); 240 } 241 242 void * 243 Zalloc(size_t size) 244 { 245 return (memset(Realloc(NULL, size), 0, size)); 246 } 247 248 int 249 Setrlimit() 250 { 251 struct rlimit rlim; 252 int fd_limit; 253 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) 254 Die(gettext("getrlimit failed")); 255 fd_limit = rlim.rlim_cur; 256 rlim.rlim_max = MIN(rlim.rlim_max, RLIMIT_NOFILE_MAX); 257 rlim.rlim_cur = rlim.rlim_max; 258 (void) enable_extended_FILE_stdio(-1, -1); 259 if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) 260 return (fd_limit); 261 else 262 return (rlim.rlim_cur); 263 } 264 265 void 266 Priocntl(char *class) 267 { 268 pcinfo_t pcinfo; 269 pcparms_t pcparms; 270 (void) strcpy(pcinfo.pc_clname, class); 271 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) { 272 Warn(gettext("cannot get real time class parameters")); 273 return; 274 } 275 pcparms.pc_cid = pcinfo.pc_cid; 276 ((rtparms_t *)pcparms.pc_clparms)->rt_pri = 0; 277 ((rtparms_t *)pcparms.pc_clparms)->rt_tqsecs = 0; 278 ((rtparms_t *)pcparms.pc_clparms)->rt_tqnsecs = RT_NOCHANGE; 279 if (priocntl(P_PID, getpid(), PC_SETPARMS, (caddr_t)&pcparms) == -1) 280 Warn(gettext("cannot enter the real time class")); 281 } 282 283 void 284 getprojname(projid_t projid, char *str, size_t len, int noresolve, 285 int trunc, size_t width) 286 { 287 struct project proj; 288 size_t n; 289 290 if (noresolve || getprojbyid(projid, &proj, projbuf, PROJECT_BUFSZ) == 291 NULL) { 292 (void) snprintf(str, len, "%-6d", (int)projid); 293 } else { 294 n = mbstowcs(NULL, proj.pj_name, 0); 295 if (n == (size_t)-1) 296 (void) snprintf(str, len, "%-28s", "ERROR"); 297 else if (trunc && n > width) 298 (void) snprintf(str, len, "%.*s%c", width - 1, 299 proj.pj_name, '*'); 300 else 301 (void) snprintf(str, len, "%-28s", proj.pj_name); 302 } 303 } 304 305 void 306 getzonename(zoneid_t zoneid, char *str, size_t len, int trunc, size_t width) 307 { 308 char zone_name[ZONENAME_MAX]; 309 size_t n; 310 311 if (getzonenamebyid(zoneid, zone_name, sizeof (zone_name)) < 0) { 312 (void) snprintf(str, len, "%-6d", (int)zoneid); 313 } else { 314 n = mbstowcs(NULL, zone_name, 0); 315 if (n == (size_t)-1) 316 (void) snprintf(str, len, "%-28s", "ERROR"); 317 else if (trunc && n > width) 318 (void) snprintf(str, len, "%.*s%c", width - 1, 319 zone_name, '*'); 320 else 321 (void) snprintf(str, len, "%-28s", zone_name); 322 } 323 } 324 325 /* 326 * Remove all unprintable characters from process name 327 */ 328 static void 329 stripfname(char *buf, size_t bufsize, const char *pname) 330 { 331 int bytesleft = PRFNSZ; 332 wchar_t wchar; 333 int length; 334 char *cp; 335 336 (void) strlcpy(buf, pname, bufsize); 337 338 buf[bytesleft - 1] = '\0'; 339 340 for (cp = buf; *cp != '\0'; cp += length) { 341 length = mbtowc(&wchar, cp, MB_LEN_MAX); 342 if (length <= 0) { 343 *cp = '\0'; 344 break; 345 } 346 if (!iswprint(wchar)) { 347 if (bytesleft <= length) { 348 *cp = '\0'; 349 break; 350 } 351 (void) memmove(cp, cp + length, bytesleft - length); 352 length = 0; 353 } 354 bytesleft -= length; 355 } 356 } 357 358 359 /* 360 * prstat has always implicitly wanted a terminal width of at least 80 columns 361 * (when a TTY is present). If run in a terminal narrower than 80 columns, 362 * prstat output may wrap. For wider terminals, we allow the last column to use 363 * the additional space. 364 * 365 * We never truncate if using -c, or not outputting to a TTY. 366 */ 367 static int 368 format_namewidth(void) 369 { 370 int prefixlen = 0; 371 372 if (opts.o_cols == 0 || !(opts.o_outpmode & (OPT_TERMCAP | OPT_TRUNC))) 373 return (0); 374 375 if (opts.o_outpmode & OPT_PSINFO) { 376 if (opts.o_outpmode & OPT_LGRP) 377 prefixlen = 64; 378 else 379 prefixlen = 59; 380 } else if (opts.o_outpmode & OPT_MSACCT) { 381 prefixlen = 64; 382 } 383 384 return (opts.o_cols - prefixlen); 385 } 386 387 void 388 format_name(lwp_info_t *lwp, char *buf, size_t buflen) 389 { 390 int pname_width = PRFNSZ; 391 char nr_suffix[20]; 392 char pname[PRFNSZ]; 393 int width; 394 int n; 395 396 stripfname(pname, sizeof (pname), lwp->li_info.pr_fname); 397 398 if (opts.o_outpmode & OPT_LWPS) { 399 n = snprintf(nr_suffix, sizeof (nr_suffix), "%d", 400 lwp->li_info.pr_lwp.pr_lwpid); 401 } else { 402 n = snprintf(nr_suffix, sizeof (nr_suffix), "%d", 403 lwp->li_info.pr_nlwp + lwp->li_info.pr_nzomb); 404 } 405 406 width = format_namewidth(); 407 408 /* If we're over budget, truncate the process name not the LWP part. */ 409 if (strlen(pname) > (width - n - 1)) { 410 pname_width = width - n - 1; 411 pname[pname_width - 1] = '*'; 412 } 413 414 if ((opts.o_outpmode & OPT_LWPS) && lwp->li_lwpname[0] != '\0') { 415 n = snprintf(buf, buflen, "%.*s/%s [%s]", pname_width, 416 pname, nr_suffix, lwp->li_lwpname); 417 } else { 418 n = snprintf(buf, buflen, "%.*s/%s", pname_width, 419 pname, nr_suffix); 420 } 421 422 if (width > 0 && strlen(buf) > width) 423 buf[width] = '\0'; 424 } 425