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 }