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 = CPU_STAT(&cpu_stats_all.vm, "pgswapin"); 371 stats_s2.v_pswpout = 372 stats_s3.v_pswpout = 373 stats_s4.v_pswpout = CPU_STAT(&cpu_stats_all.vm, "pgswapout"); 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 } 1099