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