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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 /*
  29  * rstat service:  built with rstat.x
  30  */
  31 
  32 #include <stdio.h>
  33 #include <stdlib.h>
  34 #include <stdarg.h>
  35 #include <string.h>
  36 #include <signal.h>
  37 #include <utmpx.h>
  38 #include <nlist.h>
  39 #include <fcntl.h>
  40 #include <syslog.h>
  41 #include <kstat.h>
  42 
  43 #include <rpc/rpc.h>
  44 
  45 #include <sys/socket.h>
  46 #include <sys/cpuvar.h>
  47 #include <sys/sysinfo.h>
  48 #include <sys/systm.h>
  49 #include <errno.h>
  50 #include <sys/stropts.h>
  51 #include <sys/tihdr.h>
  52 #include <sys/sysmacros.h>
  53 
  54 #include <net/if.h>
  55 #include <inet/mib2.h>
  56 
  57 #include "rstat.h"
  58 #include "rstat_v2.h"
  59 
  60 typedef struct {
  61         kstat_t sys;
  62         kstat_t vm;
  63 } _cpu_stats_t;
  64 
  65 /*
  66  *      system and cpu stats
  67  */
  68 static  kstat_ctl_t     *kc;            /* libkstat cookie */
  69 static  int     ncpus;
  70 static  _cpu_stats_t    *cpu_stats_list = NULL;
  71 static  kstat_t *system_misc_ksp;
  72 static  kstat_named_t *boot_time_knp;
  73 static  kstat_named_t *avenrun_1min_knp, *avenrun_5min_knp, *avenrun_15min_knp;
  74 static  int     hz;
  75 static  struct  timeval btm;            /* boottime */
  76 
  77 /*
  78  *      network interface stats
  79  */
  80 
  81 typedef struct mib_item_s {
  82         struct mib_item_s       *next_item;
  83         long                    group;
  84         long                    mib_id;
  85         long                    length;
  86         char                    *valp;
  87 } mib_item_t;
  88 
  89 mib_item_t      *netstat_item;
  90 
  91 /*
  92  * disk stats
  93  */
  94 
  95 struct diskinfo {
  96         struct diskinfo *next;
  97         kstat_t *ks;
  98         kstat_io_t kios;
  99 };
 100 
 101 #define NULLDISK (struct diskinfo *)0
 102 static  struct diskinfo zerodisk = { NULL, NULL };
 103 static  struct diskinfo *firstdisk = NULLDISK;
 104 static  struct diskinfo *lastdisk = NULLDISK;
 105 static  struct diskinfo *snip = NULLDISK;
 106 static  int ndisks;
 107 
 108 /*
 109  * net stats
 110  */
 111 
 112 struct netinfo {
 113         struct netinfo *next;
 114         kstat_t *ks;
 115         kstat_named_t *ipackets;
 116         kstat_named_t *opackets;
 117         kstat_named_t *ierrors;
 118         kstat_named_t *oerrors;
 119         kstat_named_t *collisions;
 120 };
 121 
 122 #define NULLNET (struct netinfo *)0
 123 static  struct netinfo zeronet = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
 124 static  struct netinfo *firstnet = NULLNET;
 125 static  struct netinfo *lastnet = NULLNET;
 126 static  struct netinfo *netsnip = NULLNET;
 127 static  int nnets;
 128 
 129 /*
 130  *  Define EXIT_WHEN_IDLE if you are able to have this program invoked
 131  *  automatically on demand (as from inetd).  When defined, the service
 132  *  will terminated after being idle for 120 seconds.
 133  */
 134 
 135 #define EXIT_WHEN_IDLE  1
 136 
 137 int sincelastreq = 0;           /* number of alarms since last request */
 138 #ifdef EXIT_WHEN_IDLE
 139 #define CLOSEDOWN 120           /* how long to wait before exiting */
 140 #endif /* def EXIT_WHEN_IDLE */
 141 
 142 statstime stats_s3;
 143 statsvar stats_s4;
 144 /* V2 support for backwards compatibility to pre-5.0 systems */
 145 statsswtch stats_s2;
 146 
 147 static int stat_is_init = 0;
 148 
 149 static  void    fail(int, char *, ...);
 150 static  void    safe_zalloc(void **, int, int);
 151 static  kid_t   safe_kstat_read(kstat_ctl_t *, kstat_t *, void *);
 152 static  kstat_t *safe_kstat_lookup(kstat_ctl_t *, char *, int, char *);
 153 static  void    *safe_kstat_data_lookup(kstat_t *, char *);
 154 static  void    system_stat_init(void);
 155 static  int     system_stat_load(void);
 156 static  void    init_disks(void);
 157 static  int     diskinfo_load(void);
 158 static  void    init_net(void);
 159 static  int     netinfo_load(void);
 160 
 161 static  void    updatestat(int);
 162 
 163 static  mib_item_t      *mibget(int sd);
 164 static  int     mibopen(void);
 165 static  char    *octetstr(char *buf, Octet_t *op, int code);
 166 
 167 static  void    kstat_copy(kstat_t *, kstat_t *, int);
 168 
 169 static  char    *cmdname = "rpc.rstatd";
 170 
 171 #define CPU_STAT(ksp, name)     (((kstat_named_t *)safe_kstat_data_lookup( \
 172                                     (ksp), (name)))->value.ui64)
 173 static  _cpu_stats_t    cpu_stats_all = { 0 };
 174 
 175 static void
 176 stat_init(void)
 177 {
 178         struct utmpx *utmpx, utmpx_id;
 179 
 180         stat_is_init = 1;
 181 
 182         if ((kc = kstat_open()) == NULL)
 183                 fail(1, "kstat_open(): can't open /dev/kstat");
 184 
 185         /*
 186          * Preallocate minimal set of drive entries.
 187          */
 188 
 189         if (stats_s4.dk_xfer.dk_xfer_val == NULL) {
 190                 stats_s4.dk_xfer.dk_xfer_len = RSTAT_DK_NDRIVE;
 191                 stats_s4.dk_xfer.dk_xfer_val =
 192                     (int *)calloc(RSTAT_DK_NDRIVE, sizeof (int));
 193         }
 194 
 195         system_stat_init();
 196         init_disks();
 197         init_net();
 198 
 199         /*
 200          * To get the boot time, use utmpx, which is per-zone, but fall back
 201          * to the system-wide kstat if utmpx is hosed for any reason.
 202          */
 203         utmpx_id.ut_type = BOOT_TIME;
 204         if ((utmpx = getutxid(&utmpx_id)) != NULL)
 205                 btm = utmpx->ut_tv;
 206         else {
 207                 btm.tv_sec = boot_time_knp->value.ul;
 208                 btm.tv_usec = 0; /* don't bother with usecs for boot time */
 209         }
 210         endutxent();
 211         stats_s4.boottime.tv_sec =
 212                 stats_s2.boottime.tv_sec =
 213                 stats_s3.boottime.tv_sec = btm.tv_sec;
 214         stats_s4.boottime.tv_usec =
 215                 stats_s2.boottime.tv_usec =
 216                 stats_s3.boottime.tv_usec = btm.tv_usec;
 217 
 218         updatestat(0);
 219         alarm(1);
 220         signal(SIGALRM, updatestat);
 221         sleep(2);               /* allow for one wake-up */
 222 }
 223 
 224 statsvar *
 225 rstatproc_stats_4_svc(argp, svcrq)
 226 void *argp;
 227 struct svc_req *svcrq;
 228 {
 229         if (! stat_is_init)
 230                 stat_init();
 231 #ifdef EXIT_WHEN_IDLE
 232         sincelastreq = 0;
 233 #endif
 234         return (&stats_s4);
 235 }
 236 
 237 statstime *
 238 rstatproc_stats_3_svc(argp, svcrq)
 239 void *argp;
 240 struct svc_req *svcrq;
 241 {
 242         if (! stat_is_init)
 243                 stat_init();
 244 #ifdef EXIT_WHEN_IDLE
 245         sincelastreq = 0;
 246 #endif
 247         return (&stats_s3);
 248 }
 249 
 250 statsswtch *
 251 rstatproc_stats_2_svc(argp, svcrq)
 252 void *argp;
 253 struct svc_req *svcrq;
 254 {
 255         if (! stat_is_init)
 256                 stat_init();
 257 #ifdef EXIT_WHEN_IDLE
 258         sincelastreq = 0;
 259 #endif
 260         return (&stats_s2);
 261 }
 262 
 263 
 264 uint_t *
 265 rstatproc_havedisk_4_svc(argp, svcrq)
 266 void *argp;
 267 struct svc_req *svcrq;
 268 {
 269         return (rstatproc_havedisk_3_svc(argp, svcrq));
 270 }
 271 
 272 uint_t *
 273 rstatproc_havedisk_3_svc(argp, svcrq)
 274 void *argp;
 275 struct svc_req *svcrq;
 276 {
 277         static uint_t have;
 278 
 279         if (! stat_is_init)
 280                 stat_init();
 281 #ifdef EXIT_WHEN_IDLE
 282         sincelastreq = 0;
 283 #endif
 284         have = (ndisks != 0);
 285         return (&have);
 286 }
 287 
 288 uint_t *
 289 rstatproc_havedisk_2_svc(argp, svcrq)
 290 void *argp;
 291 struct svc_req *svcrq;
 292 {
 293         return (rstatproc_havedisk_3_svc(argp, svcrq));
 294 }
 295 
 296 void
 297 updatestat(int ignored)
 298 {
 299 extern int _rpcpmstart;          /* Started by a port monitor ? */
 300 extern int _rpcsvcdirty;         /* Still serving ? */
 301 
 302 #ifdef DEBUG
 303         fprintf(stderr, "entering updatestat\n");
 304 #endif
 305 #ifdef EXIT_WHEN_IDLE
 306         if (_rpcpmstart && sincelastreq >= CLOSEDOWN && !_rpcsvcdirty) {
 307 #ifdef DEBUG
 308                 fprintf(stderr, "about to closedown\n");
 309 #endif
 310                 exit(0);
 311         }
 312         sincelastreq++;
 313 #endif /* def EXIT_WHEN_IDLE */
 314 
 315         (void) alarm(0);
 316 #ifdef DEBUG
 317         fprintf(stderr, "boottime: %d %d\n", stats_s3.boottime.tv_sec,
 318                 stats_s3.boottime.tv_usec);
 319 #endif
 320         while (system_stat_load() || diskinfo_load() || netinfo_load()) {
 321                 (void) kstat_chain_update(kc);
 322                 system_stat_init();
 323                 init_disks();
 324                 init_net();
 325         }
 326         stats_s4.cp_time.cp_time_len = CPU_STATES;
 327         if (stats_s4.cp_time.cp_time_val == NULL)
 328                 stats_s4.cp_time.cp_time_val =
 329                 malloc(stats_s4.cp_time.cp_time_len * sizeof (int));
 330         stats_s2.cp_time[RSTAT_CPU_USER] =
 331         stats_s3.cp_time[RSTAT_CPU_USER] =
 332         stats_s4.cp_time.cp_time_val[RSTAT_CPU_USER] =
 333                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_user");
 334         stats_s2.cp_time[RSTAT_CPU_NICE] =
 335         stats_s3.cp_time[RSTAT_CPU_NICE] =
 336         stats_s4.cp_time.cp_time_val[RSTAT_CPU_NICE] =
 337                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_wait");
 338         stats_s2.cp_time[RSTAT_CPU_SYS] =
 339         stats_s3.cp_time[RSTAT_CPU_SYS] =
 340         stats_s4.cp_time.cp_time_val[RSTAT_CPU_SYS] =
 341                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_kernel");
 342         stats_s2.cp_time[RSTAT_CPU_IDLE] =
 343         stats_s3.cp_time[RSTAT_CPU_IDLE] =
 344         stats_s4.cp_time.cp_time_val[RSTAT_CPU_IDLE] =
 345                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_idle");
 346 
 347 #ifdef DEBUG
 348         fprintf(stderr, "cpu: %d %d %d %d\n",
 349                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_user"),
 350                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_wait"),
 351                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_kernel"),
 352                 CPU_STAT(&cpu_stats_all.sys, "cpu_ticks_idle"));
 353         fprintf(stderr, "cp_time: %d %d %d %d\n",
 354                 stats_s3.cp_time[RSTAT_CPU_USER],
 355                 stats_s3.cp_time[RSTAT_CPU_NICE],
 356                 stats_s3.cp_time[RSTAT_CPU_SYS],
 357                 stats_s3.cp_time[RSTAT_CPU_IDLE]);
 358 #endif
 359 
 360         /* current time */
 361         gettimeofday((struct timeval *)&stats_s3.curtime, NULL);
 362         stats_s4.curtime = stats_s3.curtime;
 363 
 364         stats_s2.v_pgpgin =
 365         stats_s3.v_pgpgin =
 366         stats_s4.v_pgpgin = CPU_STAT(&cpu_stats_all.vm, "pgpgin");
 367         stats_s2.v_pgpgout =
 368         stats_s3.v_pgpgout =
 369         stats_s4.v_pgpgout = CPU_STAT(&cpu_stats_all.vm, "pgpgout");
 370         stats_s2.v_pswpin =
 371         stats_s3.v_pswpin =
 372         stats_s4.v_pswpin = CPU_STAT(&cpu_stats_all.vm, "pgswapin");
 373         stats_s2.v_pswpout =
 374         stats_s3.v_pswpout =
 375         stats_s4.v_pswpout = CPU_STAT(&cpu_stats_all.vm, "pgswapout");
 376         stats_s3.v_intr = CPU_STAT(&cpu_stats_all.sys, "intr");
 377         stats_s3.v_intr -= hz*(stats_s3.curtime.tv_sec - btm.tv_sec) +
 378                 hz*(stats_s3.curtime.tv_usec - btm.tv_usec)/1000000;
 379         stats_s2.v_intr =
 380         stats_s4.v_intr = stats_s3.v_intr;
 381         /* swtch not in V1 */
 382         stats_s2.v_swtch =
 383         stats_s3.v_swtch =
 384         stats_s4.v_swtch = CPU_STAT(&cpu_stats_all.sys, "pswitch");
 385 
 386 #ifdef DEBUG
 387         fprintf(stderr,
 388                 "pgin: %d pgout: %d swpin: %d swpout: %d intr: %d swtch: %d\n",
 389                 stats_s3.v_pgpgin,
 390                 stats_s3.v_pgpgout,
 391                 stats_s3.v_pswpin,
 392                 stats_s3.v_pswpout,
 393                 stats_s3.v_intr,
 394                 stats_s3.v_swtch);
 395 #endif
 396         /*
 397          * V2 and V3 of rstat are limited to RSTAT_DK_NDRIVE drives
 398          */
 399         memcpy(stats_s3.dk_xfer, stats_s4.dk_xfer.dk_xfer_val,
 400                 RSTAT_DK_NDRIVE * sizeof (int));
 401         memcpy(stats_s2.dk_xfer, stats_s4.dk_xfer.dk_xfer_val,
 402                 RSTAT_DK_NDRIVE * sizeof (int));
 403 #ifdef DEBUG
 404         fprintf(stderr, "dk_xfer: %d %d %d %d\n",
 405                 stats_s4.dk_xfer.dk_xfer_val[0],
 406                 stats_s4.dk_xfer.dk_xfer_val[1],
 407                 stats_s4.dk_xfer.dk_xfer_val[2],
 408                 stats_s4.dk_xfer.dk_xfer_val[3]);
 409 #endif
 410 
 411         stats_s2.if_ipackets =
 412         stats_s3.if_ipackets = stats_s4.if_ipackets;
 413         /* no s2 opackets */
 414         stats_s3.if_opackets = stats_s4.if_opackets;
 415         stats_s2.if_ierrors =
 416         stats_s3.if_ierrors = stats_s4.if_ierrors;
 417         stats_s2.if_oerrors =
 418         stats_s3.if_oerrors = stats_s4.if_oerrors;
 419         stats_s2.if_collisions =
 420         stats_s3.if_collisions = stats_s4.if_collisions;
 421 
 422         stats_s2.avenrun[0] =
 423         stats_s3.avenrun[0] =
 424         stats_s4.avenrun[0] = avenrun_1min_knp->value.ul;
 425         stats_s2.avenrun[1] =
 426         stats_s3.avenrun[1] =
 427         stats_s4.avenrun[1] = avenrun_5min_knp->value.ul;
 428         stats_s2.avenrun[2] =
 429         stats_s3.avenrun[2] =
 430         stats_s4.avenrun[2] = avenrun_15min_knp->value.ul;
 431 #ifdef DEBUG
 432         fprintf(stderr, "avenrun: %d %d %d\n", stats_s3.avenrun[0],
 433                 stats_s3.avenrun[1], stats_s3.avenrun[2]);
 434 #endif
 435         signal(SIGALRM, updatestat);
 436         alarm(1);
 437 }
 438 
 439 /* --------------------------------- MIBGET -------------------------------- */
 440 
 441 static mib_item_t *
 442 mibget(int sd)
 443 {
 444         int                     flags;
 445         int                     j, getcode;
 446         struct strbuf           ctlbuf, databuf;
 447         char                    buf[512];
 448         struct T_optmgmt_req    *tor = (struct T_optmgmt_req *)buf;
 449         struct T_optmgmt_ack    *toa = (struct T_optmgmt_ack *)buf;
 450         struct T_error_ack      *tea = (struct T_error_ack *)buf;
 451         struct opthdr           *req;
 452         mib_item_t              *first_item = NULL;
 453         mib_item_t              *last_item  = NULL;
 454         mib_item_t              *temp;
 455 
 456         tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
 457         tor->OPT_offset = sizeof (struct T_optmgmt_req);
 458         tor->OPT_length = sizeof (struct opthdr);
 459         tor->MGMT_flags = T_CURRENT;
 460         req = (struct opthdr *)&tor[1];
 461         req->level = MIB2_IP;                /* any MIB2_xxx value ok here */
 462         req->name  = 0;
 463         req->len   = 0;
 464 
 465         ctlbuf.buf = buf;
 466         ctlbuf.len = tor->OPT_length + tor->OPT_offset;
 467         flags = 0;
 468         if (putmsg(sd, &ctlbuf, NULL, flags) == -1) {
 469                 perror("mibget: putmsg(ctl) failed");
 470                 goto error_exit;
 471         }
 472         /*
 473          * each reply consists of a ctl part for one fixed structure
 474          * or table, as defined in mib2.h.  The format is a T_OPTMGMT_ACK,
 475          * containing an opthdr structure.  level/name identify the entry,
 476          * len is the size of the data part of the message.
 477          */
 478         req = (struct opthdr *)&toa[1];
 479         ctlbuf.maxlen = sizeof (buf);
 480         /*CSTYLED*/
 481         for (j = 1; ; j++) {
 482                 flags = 0;
 483                 getcode = getmsg(sd, &ctlbuf, NULL, &flags);
 484                 if (getcode == -1) {
 485 #ifdef DEBUG_MIB
 486                         perror("mibget getmsg(ctl) failed");
 487                         fprintf(stderr, "#   level   name    len\n");
 488                         i = 0;
 489                         for (last_item = first_item; last_item;
 490                                 last_item = last_item->next_item)
 491                                 fprintf(stderr, "%d  %4d   %5d   %d\n", ++i,
 492                                         last_item->group,
 493                                         last_item->mib_id,
 494                                         last_item->length);
 495 #endif /* DEBUG_MIB */
 496                         goto error_exit;
 497                 }
 498                 if (getcode == 0 &&
 499                         (ctlbuf.len >= sizeof (struct T_optmgmt_ack)) &&
 500                         (toa->PRIM_type == T_OPTMGMT_ACK) &&
 501                         (toa->MGMT_flags == T_SUCCESS) &&
 502                         req->len == 0) {
 503 #ifdef DEBUG_MIB
 504                         fprintf(stderr,
 505                 "mibget getmsg() %d returned EOD (level %d, name %d)\n",
 506                                 j, req->level, req->name);
 507 #endif /* DEBUG_MIB */
 508                         return (first_item);            /* this is EOD msg */
 509                 }
 510 
 511                 if (ctlbuf.len >= sizeof (struct T_error_ack) &&
 512                         (tea->PRIM_type == T_ERROR_ACK)) {
 513 #ifdef DEBUG_MIB
 514                         fprintf(stderr,
 515         "mibget %d gives T_ERROR_ACK: TLI_error = 0x%x, UNIX_error = 0x%x\n",
 516                                 j, getcode, tea->TLI_error, tea->UNIX_error);
 517 #endif /* DEBUG_MIB */
 518                         errno = (tea->TLI_error == TSYSERR)
 519                                 ? tea->UNIX_error : EPROTO;
 520                         goto error_exit;
 521                 }
 522 
 523                 if (getcode != MOREDATA ||
 524                         (ctlbuf.len < sizeof (struct T_optmgmt_ack)) ||
 525                         (toa->PRIM_type != T_OPTMGMT_ACK) ||
 526                         (toa->MGMT_flags != T_SUCCESS)) {
 527 #ifdef DEBUG_MIB
 528                         fprintf(stderr,
 529         "mibget getmsg(ctl) %d returned %d, ctlbuf.len = %d, PRIM_type = %d\n",
 530                                 j, getcode, ctlbuf.len, toa->PRIM_type);
 531                         if (toa->PRIM_type == T_OPTMGMT_ACK)
 532                                 fprintf(stderr,
 533         "T_OPTMGMT_ACK: MGMT_flags = 0x%x, req->len = %d\n",
 534                                         toa->MGMT_flags, req->len);
 535 #endif /* DEBUG_MIB */
 536                         errno = ENOMSG;
 537                         goto error_exit;
 538                 }
 539 
 540                 temp = malloc(sizeof (mib_item_t));
 541                 if (!temp) {
 542                         perror("mibget malloc failed");
 543                         goto error_exit;
 544                 }
 545                 if (last_item)
 546                         last_item->next_item = temp;
 547                 else
 548                         first_item = temp;
 549                 last_item = temp;
 550                 last_item->next_item = NULL;
 551                 last_item->group = req->level;
 552                 last_item->mib_id = req->name;
 553                 last_item->length = req->len;
 554                 last_item->valp = malloc(req->len);
 555 #ifdef DEBUG_MIB
 556                 fprintf(stderr,
 557                         "msg %d:  group = %4d   mib_id = %5d   length = %d\n",
 558                         j, last_item->group, last_item->mib_id,
 559                         last_item->length);
 560 #endif /* DEBUG_MIB */
 561                 databuf.maxlen = last_item->length;
 562                 databuf.buf    = last_item->valp;
 563                 databuf.len    = 0;
 564                 flags = 0;
 565                 getcode = getmsg(sd, NULL, &databuf, &flags);
 566                 if (getcode == -1) {
 567                         perror("mibget getmsg(data) failed");
 568                         goto error_exit;
 569                 } else if (getcode != 0) {
 570                         fprintf(stderr,
 571 "mibget getmsg(data) returned %d, databuf.maxlen = %d, databuf.len = %d\n",
 572                                 getcode, databuf.maxlen, databuf.len);
 573                         goto error_exit;
 574                 }
 575         }
 576 
 577 error_exit:
 578         while (first_item) {
 579                 last_item = first_item;
 580                 first_item = first_item->next_item;
 581                 if (last_item->valp) {
 582                         free(last_item->valp);
 583                 }
 584                 free(last_item);
 585         }
 586         return (first_item);
 587 }
 588 
 589 static int
 590 mibopen(void)
 591 {
 592         int     sd;
 593 
 594         /* gives us ip w/ arp on top */
 595         sd = open("/dev/arp", O_RDWR);
 596         if (sd == -1) {
 597                 perror("arp open");
 598                 close(sd);
 599                 return (-1);
 600         }
 601         if (ioctl(sd, I_PUSH, "tcp") == -1) {
 602                 perror("tcp I_PUSH");
 603                 close(sd);
 604                 return (-1);
 605         }
 606         if (ioctl(sd, I_PUSH, "udp") == -1) {
 607                 perror("udp I_PUSH");
 608                 close(sd);
 609                 return (-1);
 610         }
 611         return (sd);
 612 }
 613 
 614 static char *
 615 octetstr(char *buf, Octet_t *op, int code)
 616 {
 617         int     i;
 618         char    *cp;
 619 
 620         cp = buf;
 621         if (op)
 622                 for (i = 0; i < op->o_length; i++)
 623                         switch (code) {
 624                         case 'd':
 625                                 sprintf(cp, "%d.", 0xff & op->o_bytes[i]);
 626                                 cp = strchr(cp, '\0');
 627                                 break;
 628                         case 'a':
 629                                 *cp++ = op->o_bytes[i];
 630                                 break;
 631                         case 'h':
 632                         default:
 633                                 sprintf(cp, "%02x:", 0xff & op->o_bytes[i]);
 634                                 cp += 3;
 635                                 break;
 636                         }
 637         if (code != 'a' && cp != buf)
 638                 cp--;
 639         *cp = '\0';
 640         return (buf);
 641 }
 642 
 643 static void
 644 fail(int do_perror, char *message, ...)
 645 {
 646         va_list args;
 647 
 648         va_start(args, message);
 649         fprintf(stderr, "%s: ", cmdname);
 650         vfprintf(stderr, message, args);
 651         va_end(args);
 652         if (do_perror)
 653                 fprintf(stderr, ": %s", strerror(errno));
 654         fprintf(stderr, "\n");
 655         exit(2);
 656 }
 657 
 658 static void
 659 safe_zalloc(void **ptr, int size, int free_first)
 660 {
 661         if (free_first && *ptr != NULL)
 662                 free(*ptr);
 663         if ((*ptr = malloc(size)) == NULL)
 664                 fail(1, "malloc failed");
 665         memset(*ptr, 0, size);
 666 }
 667 
 668 kid_t
 669 safe_kstat_read(kstat_ctl_t *kctl, kstat_t *ksp, void *data)
 670 {
 671         kid_t kstat_chain_id = kstat_read(kctl, ksp, data);
 672 
 673         if (kstat_chain_id == -1)
 674                 fail(1, "kstat_read(%x, '%s') failed", kctl, ksp->ks_name);
 675         return (kstat_chain_id);
 676 }
 677 
 678 kstat_t *
 679 safe_kstat_lookup(kstat_ctl_t *kctl, char *ks_module, int ks_instance,
 680         char *ks_name)
 681 {
 682         kstat_t *ksp = kstat_lookup(kctl, ks_module, ks_instance, ks_name);
 683 
 684         if (ksp == NULL)
 685                 fail(0, "kstat_lookup('%s', %d, '%s') failed",
 686                         ks_module == NULL ? "" : ks_module,
 687                         ks_instance,
 688                         ks_name == NULL ? "" : ks_name);
 689         return (ksp);
 690 }
 691 
 692 void *
 693 safe_kstat_data_lookup(kstat_t *ksp, char *name)
 694 {
 695         void *fp = kstat_data_lookup(ksp, name);
 696 
 697         if (fp == NULL) {
 698                 fail(0, "kstat_data_lookup('%s', '%s') failed",
 699                         ksp->ks_name, name);
 700         }
 701         return (fp);
 702 }
 703 
 704 /*
 705  * Get various KIDs for subsequent system_stat_load operations.
 706  */
 707 
 708 static void
 709 system_stat_init(void)
 710 {
 711         kstat_t *ksp;
 712         int i, nvmks;
 713 
 714         /*
 715          * Global statistics
 716          */
 717 
 718         system_misc_ksp = safe_kstat_lookup(kc, "unix", 0, "system_misc");
 719 
 720         safe_kstat_read(kc, system_misc_ksp, NULL);
 721         boot_time_knp = safe_kstat_data_lookup(system_misc_ksp, "boot_time");
 722         avenrun_1min_knp = safe_kstat_data_lookup(system_misc_ksp,
 723                 "avenrun_1min");
 724         avenrun_5min_knp = safe_kstat_data_lookup(system_misc_ksp,
 725                 "avenrun_5min");
 726         avenrun_15min_knp = safe_kstat_data_lookup(system_misc_ksp,
 727                 "avenrun_15min");
 728 
 729         /*
 730          * Per-CPU statistics
 731          */
 732 
 733         ncpus = 0;
 734         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next)
 735                 if (strcmp(ksp->ks_module, "cpu") == 0 &&
 736                     strcmp(ksp->ks_name, "sys") == 0)
 737                         ncpus++;
 738 
 739         safe_zalloc((void **)&cpu_stats_list, ncpus * sizeof (*cpu_stats_list),
 740             1);
 741 
 742         ncpus = 0;
 743         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next)
 744                 if (strcmp(ksp->ks_module, "cpu") == 0 &&
 745                     strcmp(ksp->ks_name, "sys") == 0 &&
 746                     kstat_read(kc, ksp, NULL) != -1) {
 747                         kstat_copy(ksp, &cpu_stats_list[ncpus].sys,
 748                             1);
 749                         if ((ksp = kstat_lookup(kc, "cpu", ksp->ks_instance,
 750                             "vm")) != NULL && kstat_read(kc, ksp, NULL) != -1)
 751                                 kstat_copy(ksp, &cpu_stats_list[ncpus].vm, 1);
 752                         else
 753                                 fail(0, "couldn't find per-CPU VM statistics");
 754                         ncpus++;
 755                     }
 756 
 757         if (ncpus == 0)
 758                 fail(0, "couldn't find per-CPU statistics");
 759 }
 760 
 761 /*
 762  * load statistics, summing across CPUs where needed
 763  */
 764 
 765 static int
 766 system_stat_load(void)
 767 {
 768         int i, j;
 769         _cpu_stats_t cs;
 770         ulong_t *np, *tp;
 771 
 772         /*
 773          * Global statistics
 774          */
 775 
 776         safe_kstat_read(kc, system_misc_ksp, NULL);
 777 
 778         /*
 779          * Per-CPU statistics.
 780          */
 781 
 782         for (i = 0; i < ncpus; i++) {
 783                 if (kstat_read(kc, &cpu_stats_list[i].sys, NULL) == -1 ||
 784                     kstat_read(kc, &cpu_stats_list[i].vm, NULL) == -1)
 785                         return (1);
 786                 if (i == 0) {
 787                         kstat_copy(&cpu_stats_list[0].sys, &cpu_stats_all.sys,
 788                             1);
 789                         kstat_copy(&cpu_stats_list[0].vm, &cpu_stats_all.vm, 1);
 790                 } else {
 791                         kstat_named_t *nkp;
 792                         kstat_named_t *tkp;
 793 
 794                         /*
 795                          * Other CPUs' statistics are accumulated in
 796                          * cpu_stats_all, initialized at the first iteration of
 797                          * the loop.
 798                          */
 799                         nkp = (kstat_named_t *)cpu_stats_all.sys.ks_data;
 800                         tkp = (kstat_named_t *)cpu_stats_list[i].sys.ks_data;
 801                         for (j = 0; j < cpu_stats_list[i].sys.ks_ndata; j++)
 802                                 (nkp++)->value.ui64 += (tkp++)->value.ui64;
 803                         nkp = (kstat_named_t *)cpu_stats_all.vm.ks_data;
 804                         tkp = (kstat_named_t *)cpu_stats_list[i].vm.ks_data;
 805                         for (j = 0; j < cpu_stats_list[i].vm.ks_ndata; j++)
 806                                 (nkp++)->value.ui64 += (tkp++)->value.ui64;
 807                 }
 808         }
 809         return (0);
 810 }
 811 
 812 static int
 813 kscmp(kstat_t *ks1, kstat_t *ks2)
 814 {
 815         int cmp;
 816 
 817         cmp = strcmp(ks1->ks_module, ks2->ks_module);
 818         if (cmp != 0)
 819                 return (cmp);
 820         cmp = ks1->ks_instance - ks2->ks_instance;
 821         if (cmp != 0)
 822                 return (cmp);
 823         return (strcmp(ks1->ks_name, ks2->ks_name));
 824 }
 825 
 826 static void
 827 init_disks(void)
 828 {
 829         struct diskinfo *disk, *prevdisk, *comp;
 830         kstat_t *ksp;
 831 
 832         ndisks = 0;
 833         disk = &zerodisk;
 834 
 835         /*
 836          * Patch the snip in the diskinfo list (see below)
 837          */
 838         if (snip)
 839                 lastdisk->next = snip;
 840 
 841         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
 842 
 843                 if (ksp->ks_type != KSTAT_TYPE_IO ||
 844                     strcmp(ksp->ks_class, "disk") != 0)
 845                         continue;
 846                 prevdisk = disk;
 847                 if (disk->next)
 848                         disk = disk->next;
 849                 else {
 850                         safe_zalloc((void **)&disk->next,
 851                             sizeof (struct diskinfo), 0);
 852                         disk = disk->next;
 853                         disk->next = NULLDISK;
 854                 }
 855                 disk->ks = ksp;
 856                 memset((void *)&disk->kios, 0, sizeof (kstat_io_t));
 857                 disk->kios.wlastupdate = disk->ks->ks_crtime;
 858                 disk->kios.rlastupdate = disk->ks->ks_crtime;
 859 
 860                 /*
 861                  * Insertion sort on (ks_module, ks_instance, ks_name)
 862                  */
 863                 comp = &zerodisk;
 864                 while (kscmp(disk->ks, comp->next->ks) > 0)
 865                         comp = comp->next;
 866                 if (prevdisk != comp) {
 867                         prevdisk->next = disk->next;
 868                         disk->next = comp->next;
 869                         comp->next = disk;
 870                         disk = prevdisk;
 871                 }
 872                 ndisks++;
 873         }
 874         /*
 875          * Put a snip in the linked list of diskinfos.  The idea:
 876          * If there was a state change such that now there are fewer
 877          * disks, we snip the list and retain the tail, rather than
 878          * freeing it.  At the next state change, we clip the tail back on.
 879          * This prevents a lot of malloc/free activity, and it's simpler.
 880          */
 881         lastdisk = disk;
 882         snip = disk->next;
 883         disk->next = NULLDISK;
 884 
 885         firstdisk = zerodisk.next;
 886 
 887         if (ndisks > stats_s4.dk_xfer.dk_xfer_len) {
 888                 stats_s4.dk_xfer.dk_xfer_len = ndisks;
 889                 safe_zalloc((void **)&stats_s4.dk_xfer.dk_xfer_val,
 890                         ndisks * sizeof (int), 1);
 891         }
 892 }
 893 
 894 static int
 895 diskinfo_load(void)
 896 {
 897         struct diskinfo *disk;
 898         int i;
 899 
 900         for (disk = firstdisk, i = 0; disk; disk = disk->next, i++) {
 901                 if (kstat_read(kc, disk->ks, (void *)&disk->kios) == -1)
 902                         return (1);
 903                 stats_s4.dk_xfer.dk_xfer_val[i] = disk->kios.reads +
 904                         disk->kios.writes;
 905         }
 906         return (0);
 907 }
 908 
 909 static void
 910 init_net(void)
 911 {
 912         static int sd;
 913         mib_item_t *item;
 914         mib2_ipAddrEntry_t *ap;
 915         char namebuf[KSTAT_STRLEN];
 916         struct netinfo *net, *prevnet, *comp;
 917         kstat_t *ksp;
 918 
 919         if (sd) {
 920                 close(sd);
 921         }
 922         while (netstat_item) {
 923                 item = netstat_item;
 924                 netstat_item = netstat_item->next_item;
 925                 if (item->valp) {
 926                         free(item->valp);
 927                 }
 928                 free(item);
 929         }
 930         sd = mibopen();
 931         if (sd == -1) {
 932 #ifdef DEBUG
 933                 fprintf(stderr, "mibopen() failed\n");
 934 #endif
 935                 sd = 0;
 936         } else {
 937                 if ((netstat_item = mibget(sd)) == NULL) {
 938 #ifdef DEBUG
 939                         fprintf(stderr, "mibget() failed\n");
 940 #endif
 941                         close(sd);
 942                         sd = 0;
 943                 }
 944         }
 945 #ifdef DEBUG
 946         fprintf(stderr, "mibget returned item: %x\n", netstat_item);
 947 #endif
 948 
 949         nnets = 0;
 950         net = &zeronet;
 951 
 952         if (netsnip)
 953                 lastnet->next = netsnip;
 954 
 955         for (item = netstat_item; item; item = item->next_item) {
 956 #ifdef DEBUG_MIB
 957                 fprintf(stderr, "\n--- Item %x ---\n", item);
 958                 fprintf(stderr,
 959                 "Group = %d, mib_id = %d, length = %d, valp = 0x%x\n",
 960                 item->group, item->mib_id, item->length,
 961                 item->valp);
 962 #endif
 963                 if (item->group != MIB2_IP || item->mib_id != MIB2_IP_20)
 964                         continue;
 965                 ap = (mib2_ipAddrEntry_t *)item->valp;
 966                 for (; (char *)ap < item->valp + item->length; ap++) {
 967 
 968                         octetstr(namebuf, &ap->ipAdEntIfIndex, 'a');
 969 #ifdef DEBUG
 970                         fprintf(stderr, "%s ", namebuf);
 971 #endif
 972                         if (strlen(namebuf) == 0)
 973                                 continue;
 974                         /*
 975                          * We found a device of interest.
 976                          * Now, let's see if there's a kstat for it.
 977                          * First we try to query the "link" kstats in case
 978                          * the link is renamed. If that fails, fallback
 979                          * to legacy ktats for those non-GLDv3 links.
 980                          */
 981                         if (((ksp = kstat_lookup(kc, "link", 0, namebuf))
 982                             == NULL) && ((ksp = kstat_lookup(kc, NULL, -1,
 983                             namebuf)) == NULL)) {
 984                                 continue;
 985                         }
 986                         if (ksp->ks_type != KSTAT_TYPE_NAMED)
 987                                 continue;
 988                         if (kstat_read(kc, ksp, NULL) == -1)
 989                                 continue;
 990                         prevnet = net;
 991                         if (net->next)
 992                                 net = net->next;
 993                         else {
 994                                 safe_zalloc((void **)&net->next,
 995                                         sizeof (struct netinfo), 0);
 996                                 net = net->next;
 997                                 net->next = NULLNET;
 998                         }
 999                         net->ks = ksp;
1000                         net->ipackets        = kstat_data_lookup(net->ks,
1001                                 "ipackets");
1002                         net->opackets        = kstat_data_lookup(net->ks,
1003                                 "opackets");
1004                         net->ierrors = kstat_data_lookup(net->ks,
1005                                 "ierrors");
1006                         net->oerrors = kstat_data_lookup(net->ks,
1007                                 "oerrors");
1008                         net->collisions      = kstat_data_lookup(net->ks,
1009                                 "collisions");
1010                         /*
1011                          * Insertion sort on the name
1012                          */
1013                         comp = &zeronet;
1014                         while (strcmp(net->ks->ks_name,
1015                             comp->next->ks->ks_name) > 0)
1016                                 comp = comp->next;
1017                         if (prevnet != comp) {
1018                                 prevnet->next = net->next;
1019                                 net->next = comp->next;
1020                                 comp->next = net;
1021                                 net = prevnet;
1022                         }
1023                         nnets++;
1024                 }
1025 #ifdef DEBUG
1026                 fprintf(stderr, "\n");
1027 #endif
1028         }
1029         /*
1030          * Put a snip in the linked list of netinfos.  The idea:
1031          * If there was a state change such that now there are fewer
1032          * nets, we snip the list and retain the tail, rather than
1033          * freeing it.  At the next state change, we clip the tail back on.
1034          * This prevents a lot of malloc/free activity, and it's simpler.
1035          */
1036         lastnet = net;
1037         netsnip = net->next;
1038         net->next = NULLNET;
1039 
1040         firstnet = zeronet.next;
1041 }
1042 
1043 static int
1044 netinfo_load(void)
1045 {
1046         struct netinfo *net;
1047 
1048         if (netstat_item == NULL) {
1049 #ifdef DEBUG
1050                 fprintf(stderr, "No net stats\n");
1051 #endif
1052                 return (0);
1053         }
1054 
1055         stats_s4.if_ipackets =
1056         stats_s4.if_opackets =
1057         stats_s4.if_ierrors =
1058         stats_s4.if_oerrors =
1059         stats_s4.if_collisions = 0;
1060 
1061         for (net = firstnet; net; net = net->next) {
1062                 if (kstat_read(kc, net->ks, NULL) == -1)
1063                         return (1);
1064                 if (net->ipackets)
1065                         stats_s4.if_ipackets    += net->ipackets->value.ul;
1066                 if (net->opackets)
1067                         stats_s4.if_opackets    += net->opackets->value.ul;
1068                 if (net->ierrors)
1069                         stats_s4.if_ierrors     += net->ierrors->value.ul;
1070                 if (net->oerrors)
1071                         stats_s4.if_oerrors     += net->oerrors->value.ul;
1072                 if (net->collisions)
1073                         stats_s4.if_collisions  += net->collisions->value.ul;
1074         }
1075 #ifdef DEBUG
1076         fprintf(stderr,
1077             "ipackets: %d opackets: %d ierrors: %d oerrors: %d colls: %d\n",
1078                 stats_s4.if_ipackets,
1079                 stats_s4.if_opackets,
1080                 stats_s4.if_ierrors,
1081                 stats_s4.if_oerrors,
1082                 stats_s4.if_collisions);
1083 #endif
1084         return (0);
1085 }
1086 
1087 static void
1088 kstat_copy(kstat_t *src, kstat_t *dst, int fr)
1089 {
1090         if (fr)
1091                 free(dst->ks_data);
1092         *dst = *src;
1093         if (src->ks_data != NULL) {
1094                 safe_zalloc(&dst->ks_data, src->ks_data_size, 0);
1095                 (void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size);
1096         } else {
1097                 dst->ks_data = NULL;
1098                 dst->ks_data_size = 0;
1099         }
1100 }