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