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 }