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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * rstat service: built with rstat.x
43 #include <sys/socket.h>
44 #include <sys/cpuvar.h>
45 #include <sys/sysinfo.h>
46 #include <sys/systm.h>
48 #include <sys/stropts.h>
49 #include <sys/tihdr.h>
50 #include <sys/sysmacros.h>
53 #include <inet/mib2.h>
64 * system and cpu stats
66 static kstat_ctl_t
*kc
; /* libkstat cookie */
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
;
73 static struct timeval btm
; /* boottime */
76 * network interface stats
79 typedef struct mib_item_s
{
80 struct mib_item_s
*next_item
;
87 mib_item_t
*netstat_item
;
94 struct diskinfo
*next
;
99 #define NULLDISK ((struct diskinfo *)NULL)
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
;
111 struct netinfo
*next
;
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
;
120 #define NULLNET ((struct netinfo *)NULL)
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
;
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.
133 #define EXIT_WHEN_IDLE 1
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 */
142 /* V2 support for backwards compatibility to pre-5.0 systems */
145 static int stat_is_init
= 0;
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);
159 static void updatestat(int);
161 static mib_item_t
*mibget(int sd
);
162 static int mibopen(void);
163 static char *octetstr(char *buf
, Octet_t
*op
, int code
);
165 static void kstat_copy(kstat_t
*, kstat_t
*, int);
167 static char *cmdname
= "rpc.rstatd";
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 };
176 struct utmpx
*utmpx
, utmpx_id
;
180 if ((kc
= kstat_open()) == NULL
)
181 fail(1, "kstat_open(): can't open /dev/kstat");
184 * Preallocate minimal set of drive entries.
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));
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.
201 utmpx_id
.ut_type
= BOOT_TIME
;
202 if ((utmpx
= getutxid(&utmpx_id
)) != NULL
)
205 btm
.tv_sec
= boot_time_knp
->value
.ul
;
206 btm
.tv_usec
= 0; /* don't bother with usecs for boot time */
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
;
218 signal(SIGALRM
, updatestat
);
219 sleep(2); /* allow for one wake-up */
223 rstatproc_stats_4_svc(argp
, svcrq
)
225 struct svc_req
*svcrq
;
229 #ifdef EXIT_WHEN_IDLE
236 rstatproc_stats_3_svc(argp
, svcrq
)
238 struct svc_req
*svcrq
;
242 #ifdef EXIT_WHEN_IDLE
249 rstatproc_stats_2_svc(argp
, svcrq
)
251 struct svc_req
*svcrq
;
255 #ifdef EXIT_WHEN_IDLE
263 rstatproc_havedisk_4_svc(argp
, svcrq
)
265 struct svc_req
*svcrq
;
267 return (rstatproc_havedisk_3_svc(argp
, svcrq
));
271 rstatproc_havedisk_3_svc(argp
, svcrq
)
273 struct svc_req
*svcrq
;
279 #ifdef EXIT_WHEN_IDLE
282 have
= (ndisks
!= 0);
287 rstatproc_havedisk_2_svc(argp
, svcrq
)
289 struct svc_req
*svcrq
;
291 return (rstatproc_havedisk_3_svc(argp
, svcrq
));
295 updatestat(int ignored
)
297 extern int _rpcpmstart
; /* Started by a port monitor ? */
298 extern int _rpcsvcdirty
; /* Still serving ? */
301 fprintf(stderr
, "entering updatestat\n");
303 #ifdef EXIT_WHEN_IDLE
304 if (_rpcpmstart
&& sincelastreq
>= CLOSEDOWN
&& !_rpcsvcdirty
) {
306 fprintf(stderr
, "about to closedown\n");
311 #endif /* def EXIT_WHEN_IDLE */
315 fprintf(stderr
, "boottime: %d %d\n", stats_s3
.boottime
.tv_sec
,
316 stats_s3
.boottime
.tv_usec
);
318 while (system_stat_load() || diskinfo_load() || netinfo_load()) {
319 (void) kstat_chain_update(kc
);
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");
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
]);
359 gettimeofday((struct timeval
*)&stats_s3
.curtime
, NULL
);
360 stats_s4
.curtime
= stats_s3
.curtime
;
364 stats_s4
.v_pgpgin
= CPU_STAT(&cpu_stats_all
.vm
, "pgpgin");
367 stats_s4
.v_pgpgout
= CPU_STAT(&cpu_stats_all
.vm
, "pgpgout");
370 stats_s4
.v_pswpin
= 0;
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;
378 stats_s4
.v_intr
= stats_s3
.v_intr
;
379 /* swtch not in V1 */
382 stats_s4
.v_swtch
= CPU_STAT(&cpu_stats_all
.sys
, "pswitch");
386 "pgin: %d pgout: %d swpin: %d swpout: %d intr: %d swtch: %d\n",
395 * V2 and V3 of rstat are limited to RSTAT_DK_NDRIVE drives
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));
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]);
409 stats_s2
.if_ipackets
=
410 stats_s3
.if_ipackets
= stats_s4
.if_ipackets
;
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
;
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
;
430 fprintf(stderr
, "avenrun: %d %d %d\n", stats_s3
.avenrun
[0],
431 stats_s3
.avenrun
[1], stats_s3
.avenrun
[2]);
433 signal(SIGALRM
, updatestat
);
437 /* --------------------------------- MIBGET -------------------------------- */
444 struct strbuf ctlbuf
, databuf
;
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
;
450 mib_item_t
*first_item
= NULL
;
451 mib_item_t
*last_item
= NULL
;
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 */
464 ctlbuf
.len
= tor
->OPT_length
+ tor
->OPT_offset
;
466 if (putmsg(sd
, &ctlbuf
, NULL
, flags
) == -1) {
467 perror("mibget: putmsg(ctl) failed");
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.
476 req
= (struct opthdr
*)&toa
[1];
477 ctlbuf
.maxlen
= sizeof (buf
);
481 getcode
= getmsg(sd
, &ctlbuf
, NULL
, &flags
);
484 perror("mibget getmsg(ctl) failed");
485 fprintf(stderr
, "# level name len\n");
487 for (last_item
= first_item
; last_item
;
488 last_item
= last_item
->next_item
)
489 fprintf(stderr
, "%d %4d %5d %d\n", ++i
,
493 #endif /* DEBUG_MIB */
497 (ctlbuf
.len
>= sizeof (struct T_optmgmt_ack
)) &&
498 (toa
->PRIM_type
== T_OPTMGMT_ACK
) &&
499 (toa
->MGMT_flags
== T_SUCCESS
) &&
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 */
509 if (ctlbuf
.len
>= sizeof (struct T_error_ack
) &&
510 (tea
->PRIM_type
== T_ERROR_ACK
)) {
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
;
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
)) {
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
)
531 "T_OPTMGMT_ACK: MGMT_flags = 0x%x, req->len = %d\n",
532 toa
->MGMT_flags
, req
->len
);
533 #endif /* DEBUG_MIB */
538 temp
= malloc(sizeof (mib_item_t
));
540 perror("mibget malloc failed");
544 last_item
->next_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
);
555 "msg %d: group = %4d mib_id = %5d length = %d\n",
556 j
, last_item
->group
, last_item
->mib_id
,
558 #endif /* DEBUG_MIB */
559 databuf
.maxlen
= last_item
->length
;
560 databuf
.buf
= last_item
->valp
;
563 getcode
= getmsg(sd
, NULL
, &databuf
, &flags
);
565 perror("mibget getmsg(data) failed");
567 } else if (getcode
!= 0) {
569 "mibget getmsg(data) returned %d, databuf.maxlen = %d, databuf.len = %d\n",
570 getcode
, databuf
.maxlen
, databuf
.len
);
577 last_item
= first_item
;
578 first_item
= first_item
->next_item
;
579 if (last_item
->valp
) {
580 free(last_item
->valp
);
592 /* gives us ip w/ arp on top */
593 sd
= open("/dev/arp", O_RDWR
);
599 if (ioctl(sd
, I_PUSH
, "tcp") == -1) {
600 perror("tcp I_PUSH");
604 if (ioctl(sd
, I_PUSH
, "udp") == -1) {
605 perror("udp I_PUSH");
613 octetstr(char *buf
, Octet_t
*op
, int code
)
620 for (i
= 0; i
< op
->o_length
; i
++)
623 sprintf(cp
, "%d.", 0xff & op
->o_bytes
[i
]);
624 cp
= strchr(cp
, '\0');
627 *cp
++ = op
->o_bytes
[i
];
631 sprintf(cp
, "%02x:", 0xff & op
->o_bytes
[i
]);
635 if (code
!= 'a' && cp
!= buf
)
642 fail(int do_perror
, char *message
, ...)
646 va_start(args
, message
);
647 fprintf(stderr
, "%s: ", cmdname
);
648 vfprintf(stderr
, message
, args
);
651 fprintf(stderr
, ": %s", strerror(errno
));
652 fprintf(stderr
, "\n");
657 safe_zalloc(void **ptr
, int size
, int free_first
)
661 if ((*ptr
= malloc(size
)) == NULL
)
662 fail(1, "malloc failed");
663 memset(*ptr
, 0, size
);
667 safe_kstat_read(kstat_ctl_t
*kctl
, kstat_t
*ksp
, void *data
)
669 kid_t kstat_chain_id
= kstat_read(kctl
, ksp
, data
);
671 if (kstat_chain_id
== -1)
672 fail(1, "kstat_read(%x, '%s') failed", kctl
, ksp
->ks_name
);
673 return (kstat_chain_id
);
677 safe_kstat_lookup(kstat_ctl_t
*kctl
, char *ks_module
, int ks_instance
,
680 kstat_t
*ksp
= kstat_lookup(kctl
, ks_module
, ks_instance
, ks_name
);
683 fail(0, "kstat_lookup('%s', %d, '%s') failed",
684 ks_module
== NULL
? "" : ks_module
,
686 ks_name
== NULL
? "" : ks_name
);
691 safe_kstat_data_lookup(kstat_t
*ksp
, char *name
)
693 void *fp
= kstat_data_lookup(ksp
, name
);
696 fail(0, "kstat_data_lookup('%s', '%s') failed",
703 * Get various KIDs for subsequent system_stat_load operations.
707 system_stat_init(void)
716 system_misc_ksp
= safe_kstat_lookup(kc
, "unix", 0, "system_misc");
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
,
722 avenrun_5min_knp
= safe_kstat_data_lookup(system_misc_ksp
,
724 avenrun_15min_knp
= safe_kstat_data_lookup(system_misc_ksp
,
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)
737 safe_zalloc((void **)&cpu_stats_list
, ncpus
* sizeof (*cpu_stats_list
),
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
,
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);
751 fail(0, "couldn't find per-CPU VM statistics");
756 fail(0, "couldn't find per-CPU statistics");
760 * load statistics, summing across CPUs where needed
764 system_stat_load(void)
774 safe_kstat_read(kc
, system_misc_ksp
, NULL
);
777 * Per-CPU statistics.
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)
785 kstat_copy(&cpu_stats_list
[0].sys
, &cpu_stats_all
.sys
,
787 kstat_copy(&cpu_stats_list
[0].vm
, &cpu_stats_all
.vm
, 1);
793 * Other CPUs' statistics are accumulated in
794 * cpu_stats_all, initialized at the first iteration of
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
;
811 kscmp(kstat_t
*ks1
, kstat_t
*ks2
)
815 cmp
= strcmp(ks1
->ks_module
, ks2
->ks_module
);
818 cmp
= ks1
->ks_instance
- ks2
->ks_instance
;
821 return (strcmp(ks1
->ks_name
, ks2
->ks_name
));
827 struct diskinfo
*disk
, *prevdisk
, *comp
;
834 * Patch the snip in the diskinfo list (see below)
837 lastdisk
->next
= snip
;
839 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
841 if (ksp
->ks_type
!= KSTAT_TYPE_IO
||
842 strcmp(ksp
->ks_class
, "disk") != 0)
848 safe_zalloc((void **)&disk
->next
,
849 sizeof (struct diskinfo
), 0);
851 disk
->next
= NULLDISK
;
854 memset(&disk
->kios
, 0, sizeof (kstat_io_t
));
855 disk
->kios
.wlastupdate
= disk
->ks
->ks_crtime
;
856 disk
->kios
.rlastupdate
= disk
->ks
->ks_crtime
;
859 * Insertion sort on (ks_module, ks_instance, ks_name)
862 while (kscmp(disk
->ks
, comp
->next
->ks
) > 0)
864 if (prevdisk
!= comp
) {
865 prevdisk
->next
= disk
->next
;
866 disk
->next
= comp
->next
;
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.
881 disk
->next
= NULLDISK
;
883 firstdisk
= zerodisk
.next
;
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);
895 struct diskinfo
*disk
;
898 for (disk
= firstdisk
, i
= 0; disk
; disk
= disk
->next
, i
++) {
899 if (kstat_read(kc
, disk
->ks
, (void *)&disk
->kios
) == -1)
901 stats_s4
.dk_xfer
.dk_xfer_val
[i
] = disk
->kios
.reads
+
912 mib2_ipAddrEntry_t
*ap
;
913 char namebuf
[KSTAT_STRLEN
];
914 struct netinfo
*net
, *prevnet
, *comp
;
920 while (netstat_item
) {
922 netstat_item
= netstat_item
->next_item
;
931 fprintf(stderr
, "mibopen() failed\n");
935 if ((netstat_item
= mibget(sd
)) == NULL
) {
937 fprintf(stderr
, "mibget() failed\n");
944 fprintf(stderr
, "mibget returned item: %x\n", netstat_item
);
951 lastnet
->next
= netsnip
;
953 for (item
= netstat_item
; item
; item
= item
->next_item
) {
955 fprintf(stderr
, "\n--- Item %x ---\n", item
);
957 "Group = %d, mib_id = %d, length = %d, valp = 0x%x\n",
958 item
->group
, item
->mib_id
, item
->length
,
961 if (item
->group
!= MIB2_IP
|| item
->mib_id
!= MIB2_IP_20
)
963 ap
= (mib2_ipAddrEntry_t
*)item
->valp
;
964 for (; (char *)ap
< item
->valp
+ item
->length
; ap
++) {
966 octetstr(namebuf
, &ap
->ipAdEntIfIndex
, 'a');
968 fprintf(stderr
, "%s ", namebuf
);
970 if (strlen(namebuf
) == 0)
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.
979 if (((ksp
= kstat_lookup(kc
, "link", 0, namebuf
))
980 == NULL
) && ((ksp
= kstat_lookup(kc
, NULL
, -1,
981 namebuf
)) == NULL
)) {
984 if (ksp
->ks_type
!= KSTAT_TYPE_NAMED
)
986 if (kstat_read(kc
, ksp
, NULL
) == -1)
992 safe_zalloc((void **)&net
->next
,
993 sizeof (struct netinfo
), 0);
998 net
->ipackets
= kstat_data_lookup(net
->ks
,
1000 net
->opackets
= kstat_data_lookup(net
->ks
,
1002 net
->ierrors
= kstat_data_lookup(net
->ks
,
1004 net
->oerrors
= kstat_data_lookup(net
->ks
,
1006 net
->collisions
= kstat_data_lookup(net
->ks
,
1009 * Insertion sort on the name
1012 while (strcmp(net
->ks
->ks_name
,
1013 comp
->next
->ks
->ks_name
) > 0)
1015 if (prevnet
!= comp
) {
1016 prevnet
->next
= net
->next
;
1017 net
->next
= comp
->next
;
1024 fprintf(stderr
, "\n");
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.
1035 netsnip
= net
->next
;
1036 net
->next
= NULLNET
;
1038 firstnet
= zeronet
.next
;
1044 struct netinfo
*net
;
1046 if (netstat_item
== NULL
) {
1048 fprintf(stderr
, "No net stats\n");
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;
1059 for (net
= firstnet
; net
; net
= net
->next
) {
1060 if (kstat_read(kc
, net
->ks
, NULL
) == -1)
1063 stats_s4
.if_ipackets
+= net
->ipackets
->value
.ul
;
1065 stats_s4
.if_opackets
+= net
->opackets
->value
.ul
;
1067 stats_s4
.if_ierrors
+= net
->ierrors
->value
.ul
;
1069 stats_s4
.if_oerrors
+= net
->oerrors
->value
.ul
;
1070 if (net
->collisions
)
1071 stats_s4
.if_collisions
+= net
->collisions
->value
.ul
;
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
);
1086 kstat_copy(kstat_t
*src
, kstat_t
*dst
, int fr
)
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
);
1095 dst
->ks_data
= NULL
;
1096 dst
->ks_data_size
= 0;