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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Portions Copyright 2009 Chad Mynhier
  26  */
  27 
  28 #include <sys/types.h>
  29 #include <sys/param.h>
  30 #include <sys/resource.h>
  31 #include <sys/priocntl.h>
  32 #include <sys/rtpriocntl.h>
  33 #include <sys/tspriocntl.h>
  34 #include <zone.h>
  35 
  36 #include <libintl.h>
  37 #include <limits.h>
  38 #include <wchar.h>
  39 #include <unistd.h>
  40 #include <string.h>
  41 #include <stdlib.h>
  42 #include <stdarg.h>
  43 #include <stdio.h>
  44 #include <stdio_ext.h>
  45 #include <errno.h>
  46 #include <ctype.h>
  47 #include <poll.h>
  48 #include <project.h>
  49 
  50 #include "prfile.h"
  51 #include "prstat.h"
  52 #include "prutil.h"
  53 
  54 static char PRG_FMT[] = "%s: ";
  55 static char ERR_FMT[] = ": %s\n";
  56 static char *progname;
  57 static char projbuf[PROJECT_BUFSZ];
  58 
  59 #define RLIMIT_NOFILE_MAX       32767
  60 
  61 /*PRINTFLIKE1*/
  62 void
  63 Warn(char *format, ...)
  64 {
  65         int err = errno;
  66         va_list alist;
  67 
  68         if (progname != NULL)
  69                 (void) fprintf(stderr, PRG_FMT, progname);
  70         va_start(alist, format);
  71         (void) vfprintf(stderr, format, alist);
  72         va_end(alist);
  73         if (strchr(format, '\n') == NULL)
  74                 (void) fprintf(stderr, gettext(ERR_FMT), strerror(err));
  75 }
  76 
  77 /*PRINTFLIKE1*/
  78 void
  79 Die(char *format, ...)
  80 {
  81         int err = errno;
  82         va_list alist;
  83 
  84         if (progname != NULL)
  85                 (void) fprintf(stderr, PRG_FMT, progname);
  86         va_start(alist, format);
  87         (void) vfprintf(stderr, format, alist);
  88         va_end(alist);
  89         if (strchr(format, '\n') == NULL)
  90                 (void) fprintf(stderr, gettext(ERR_FMT), strerror(err));
  91         exit(1);
  92 }
  93 
  94 void
  95 Progname(char *arg0)
  96 {
  97         char *p = strrchr(arg0, '/');
  98         if (p == NULL)
  99                 p = arg0;
 100         else
 101                 p++;
 102         progname = p;
 103 }
 104 
 105 void
 106 Usage()
 107 {
 108         (void) fprintf(stderr, gettext(
 109             "Usage:\tprstat [-acHJLmrRtTvZ] [-u euidlist] [-U uidlist]\n"
 110             "\t[-p pidlist] [-P cpulist] [-C psrsetlist] [-h lgrouplist]\n"
 111             "\t[-j projidlist] [-k taskidlist] [-z zoneidlist]\n"
 112             "\t[-s key | -S key] [-n nprocs[,nusers]] [-d d|u]\n"
 113             "\t[interval [counter]]\n"));
 114         exit(1);
 115 }
 116 
 117 int
 118 Atoi(char *p)
 119 {
 120         int i;
 121         char *q;
 122         errno = 0;
 123         i = (int)strtol(p, &q, 10);
 124         if (errno != 0 || q == p || i < 0 || *q != '\0')
 125                 Die(gettext("illegal argument -- %s\n"), p);
 126                 /*NOTREACHED*/
 127         else
 128                 return (i);
 129         return (0);     /* keep gcc happy */
 130 }
 131 
 132 void
 133 Format_size(char *str, size_t size, int length)
 134 {
 135         char tag = 'K';
 136         if (size >= 10000) {
 137                 size = (size + 512) / 1024;
 138                 tag = 'M';
 139                 if (size >= 10000) {
 140                         size = (size + 512) / 1024;
 141                         tag = 'G';
 142                 }
 143         }
 144         (void) snprintf(str, length, "%4d%c", (int)size, tag);
 145 }
 146 
 147 void
 148 Format_time(char *str, ulong_t time, int length)
 149 {
 150         (void) snprintf(str, length, gettext("%3d:%2.2d:%2.2d"), /* hr:mm:ss */
 151             (int)time/3600, (int)(time % 3600)/60, (int)time % 60);
 152 }
 153 
 154 void
 155 Format_pct(char *str, float val, int length)
 156 {
 157         if (val > (float)100)
 158                 val = 100;
 159         if (val < 0)
 160                 val = 0;
 161 
 162         if (val < (float)9.95)
 163                 (void) snprintf(str, length, "%1.1f", val);
 164         else
 165                 (void) snprintf(str, length, "%.0f", val);
 166 }
 167 
 168 void
 169 Format_num(char *str, int num, int length)
 170 {
 171         if (num >= 100000) {
 172                 (void) snprintf(str, length, ".%1dM", num/100000);
 173         } else {
 174                 if (num >= 1000)
 175                         (void) snprintf(str, length, "%2dK", num/1000);
 176                 else
 177                         (void) snprintf(str, length, "%3d", num);
 178         }
 179 }
 180 
 181 void
 182 Format_state(char *str, char state, processorid_t pr_id, int length)
 183 {
 184         switch (state) {
 185         case 'S':
 186                 (void) strncpy(str, "sleep", length);
 187                 break;
 188         case 'R':
 189                 (void) strncpy(str, "run", length);
 190                 break;
 191         case 'Z':
 192                 (void) strncpy(str, "zombie", length);
 193                 break;
 194         case 'T':
 195                 (void) strncpy(str, "stop", length);
 196                 break;
 197         case 'I':
 198                 (void) strncpy(str, "idle", length);
 199                 break;
 200         case 'W':
 201                 (void) strncpy(str, "wait", length);
 202                 break;
 203         case 'O':
 204                 (void) snprintf(str, length, "cpu%-3d", (int)pr_id);
 205                 break;
 206         default:
 207                 (void) strncpy(str, "?", length);
 208                 break;
 209         }
 210 }
 211 
 212 void *
 213 Realloc(void *ptr, size_t size)
 214 {
 215         int     cnt = 0;
 216         void    *sav = ptr;
 217 
 218 eagain: if ((ptr = realloc(ptr, size)))
 219                 return (ptr);
 220 
 221         if ((++cnt <= 3) && (errno == EAGAIN)) {
 222                 Warn(gettext("realloc() failed, attempt %d"), cnt);
 223                 (void) poll(NULL, 0, 5000); /* wait for 5 seconds */
 224                 ptr = sav;
 225                 goto eagain;
 226         }
 227         ptr = sav;
 228         Die(gettext("not enough memory"));
 229         /*NOTREACHED*/
 230         return (NULL);  /* keep gcc happy */
 231 }
 232 
 233 void *
 234 Malloc(size_t size)
 235 {
 236         return (Realloc(NULL, size));
 237 }
 238 
 239 void *
 240 Zalloc(size_t size)
 241 {
 242         return (memset(Realloc(NULL, size), 0, size));
 243 }
 244 
 245 int
 246 Setrlimit()
 247 {
 248         struct rlimit rlim;
 249         int fd_limit;
 250         if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
 251                 Die(gettext("getrlimit failed"));
 252         fd_limit = rlim.rlim_cur;
 253         rlim.rlim_max = MIN(rlim.rlim_max, RLIMIT_NOFILE_MAX);
 254         rlim.rlim_cur = rlim.rlim_max;
 255         (void) enable_extended_FILE_stdio(-1, -1);
 256         if (setrlimit(RLIMIT_NOFILE, &rlim) == -1)
 257                 return (fd_limit);
 258         else
 259                 return (rlim.rlim_cur);
 260 }
 261 
 262 void
 263 Priocntl(char *class)
 264 {
 265         pcinfo_t pcinfo;
 266         pcparms_t pcparms;
 267         (void) strcpy(pcinfo.pc_clname, class);
 268         if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) {
 269                 Warn(gettext("cannot get real time class parameters"));
 270                 return;
 271         }
 272         pcparms.pc_cid = pcinfo.pc_cid;
 273         ((rtparms_t *)pcparms.pc_clparms)->rt_pri = 0;
 274         ((rtparms_t *)pcparms.pc_clparms)->rt_tqsecs = 0;
 275         ((rtparms_t *)pcparms.pc_clparms)->rt_tqnsecs = RT_NOCHANGE;
 276         if (priocntl(P_PID, getpid(), PC_SETPARMS, (caddr_t)&pcparms) == -1)
 277                 Warn(gettext("cannot enter the real time class"));
 278 }
 279 
 280 void
 281 getprojname(projid_t projid, char *str, int len, int noresolve)
 282 {
 283         struct project proj;
 284 
 285         if (noresolve || getprojbyid(projid, &proj, projbuf, PROJECT_BUFSZ) ==
 286             NULL)
 287                 (void) snprintf(str, len, "%-6d", (int)projid);
 288         else
 289                 (void) snprintf(str, len, "%-28s", proj.pj_name);
 290 }
 291 
 292 void
 293 getzonename(zoneid_t zoneid, char *str, int len)
 294 {
 295         char zone_name[ZONENAME_MAX];
 296 
 297         if (getzonenamebyid(zoneid, zone_name, sizeof (zone_name)) < 0)
 298                 (void) snprintf(str, len, "%-6d", (int)zoneid);
 299         else
 300                 (void) snprintf(str, len, "%-28s", zone_name);
 301 }
 302 
 303 /*
 304  * Remove all unprintable characters from process name
 305  */
 306 void
 307 stripfname(char *buf)
 308 {
 309         int bytesleft = PRFNSZ;
 310         wchar_t wchar;
 311         int length;
 312         char *cp;
 313 
 314         buf[bytesleft - 1] = '\0';
 315 
 316         for (cp = buf; *cp != '\0'; cp += length) {
 317                 length = mbtowc(&wchar, cp, MB_LEN_MAX);
 318                 if (length <= 0) {
 319                         *cp = '\0';
 320                         break;
 321                 }
 322                 if (!iswprint(wchar)) {
 323                         if (bytesleft <= length) {
 324                                 *cp = '\0';
 325                                 break;
 326                         }
 327                         (void) memmove(cp, cp + length, bytesleft - length);
 328                         length = 0;
 329                 }
 330                 bytesleft -= length;
 331         }
 332 }