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
stat_init(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 *
rstatproc_stats_4_svc(argp,svcrq)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 *
rstatproc_stats_3_svc(argp,svcrq)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 *
rstatproc_stats_2_svc(argp,svcrq)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 *
rstatproc_havedisk_4_svc(argp,svcrq)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 *
rstatproc_havedisk_3_svc(argp,svcrq)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 *
rstatproc_havedisk_2_svc(argp,svcrq)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
updatestat(int ignored)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 *
mibget(int sd)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
mibopen(void)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 *
octetstr(char * buf,Octet_t * op,int code)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
fail(int do_perror,char * message,...)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
safe_zalloc(void ** ptr,int size,int free_first)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
safe_kstat_read(kstat_ctl_t * kctl,kstat_t * ksp,void * data)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 *
safe_kstat_lookup(kstat_ctl_t * kctl,char * ks_module,int ks_instance,char * ks_name)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 *
safe_kstat_data_lookup(kstat_t * ksp,char * name)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
system_stat_init(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
system_stat_load(void)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
kscmp(kstat_t * ks1,kstat_t * ks2)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
init_disks(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
diskinfo_load(void)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
init_net(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
netinfo_load(void)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
kstat_copy(kstat_t * src,kstat_t * dst,int fr)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 }
1101