6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / lib / libdladm / common / usage.c
bloba74e81ee5951ce214d47c72a23011b597a8d385c
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <exacct.h>
31 #include <net/if.h>
32 #include <sys/ethernet.h>
33 #include <libdladm.h>
35 #define TIMEBUFLEN 20
36 #define GBIT 1000000000
37 #define MBIT 1000000
38 #define KBIT 1000
40 #define NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) { \
41 (step) = 1; \
42 (tbytes) = 0; \
43 (ttime) = 0; \
44 (tibytes) = 0; \
45 (tobytes) = 0; \
48 /* Flow/Link Descriptor */
49 typedef struct net_desc_s {
50 char net_desc_name[LIFNAMSIZ];
51 char net_desc_devname[LIFNAMSIZ];
52 uchar_t net_desc_ehost[ETHERADDRL];
53 uchar_t net_desc_edest[ETHERADDRL];
54 ushort_t net_desc_vlan_tpid;
55 ushort_t net_desc_vlan_tci;
56 ushort_t net_desc_sap;
57 ushort_t net_desc_cpuid;
58 ushort_t net_desc_priority;
59 uint64_t net_desc_bw_limit;
60 in6_addr_t net_desc_saddr;
61 in6_addr_t net_desc_daddr;
62 boolean_t net_desc_isv4;
63 in_port_t net_desc_sport;
64 in_port_t net_desc_dport;
65 uint8_t net_desc_protocol;
66 uint8_t net_desc_dsfield;
67 boolean_t net_desc_newrec;
68 } net_desc_t;
70 /* Time structure: Year, Month, Day, Hour, Min, Sec */
71 typedef struct net_time_s {
72 int net_time_yr;
73 int net_time_mon;
74 int net_time_day;
75 int net_time_hr;
76 int net_time_min;
77 int net_time_sec;
78 } net_time_t;
80 /* Flow/Link Stats */
81 typedef struct net_stat_s {
82 char net_stat_name[LIFNAMSIZ];
83 uint64_t net_stat_ibytes;
84 uint64_t net_stat_obytes;
85 uint64_t net_stat_ipackets;
86 uint64_t net_stat_opackets;
87 uint64_t net_stat_ierrors;
88 uint64_t net_stat_oerrors;
89 uint64_t net_stat_tibytes;
90 uint64_t net_stat_tobytes;
91 uint64_t net_stat_tipackets;
92 uint64_t net_stat_topackets;
93 uint64_t net_stat_tierrors;
94 uint64_t net_stat_toerrors;
95 uint64_t net_stat_ctime;
96 uint64_t net_stat_tdiff;
97 net_time_t net_stat_time;
98 struct net_stat_s *net_stat_next;
99 net_desc_t *net_stat_desc;
100 boolean_t net_stat_isref;
101 } net_stat_t;
103 /* Used to create the [gnu]plot file */
104 typedef struct net_plot_entry_s {
105 char *net_pe_name;
106 uint64_t net_pe_tottime;
107 uint64_t net_pe_totbytes;
108 uint64_t net_pe_totibytes;
109 uint64_t net_pe_totobytes;
110 uint64_t net_pe_lasttime;
111 } net_plot_entry_t;
113 /* Stats entry */
114 typedef struct net_entry_s {
115 net_desc_t *net_entry_desc;
116 net_stat_t *net_entry_shead;
117 net_stat_t *net_entry_stail;
118 int net_entry_scount;
119 net_stat_t *net_entry_sref;
120 net_stat_t *net_entry_tstats;
121 uint64_t net_entry_ttime;
122 struct net_entry_s *net_entry_next;
123 } net_entry_t;
125 /* Time sorted list */
126 typedef struct net_time_entry_s {
127 net_stat_t *my_time_stat;
128 struct net_time_entry_s *net_time_entry_next;
129 struct net_time_entry_s *net_time_entry_prev;
130 } net_time_entry_t;
132 /* The parsed table */
133 typedef struct net_table_s {
134 /* List of stats */
135 net_entry_t *net_table_head;
136 net_entry_t *net_table_tail;
137 int net_entries;
140 * Optimization I : List sorted by time, i.e:
141 * Time Resource ..
142 * -------------------------------
143 * 11.15.10 bge0
144 * 11.15.10 ce0
145 * 11.15.10 vnic1
146 * 11.15.15 bge0
147 * 11.15.15 ce0
148 * 11.15.15 vnic1
150 net_time_entry_t *net_time_head;
151 net_time_entry_t *net_time_tail;
154 * Optimization II : List sorted by resources
155 * Time Resource ..
156 * -------------------------------
157 * 11.15.10 bge0
158 * 11.15.15 bge0
159 * 11.15.10 ce0
160 * 11.15.15 ce0
161 * 11.15.10 vnic1
162 * 11.15.15 vnic1
164 net_time_entry_t *net_ctime_head;
165 net_time_entry_t *net_ctime_tail;
167 /* Common to both the above (sorted) lists. */
168 int net_time_entries;
169 } net_table_t;
171 #define NET_DATE_GREATER 0
172 #define NET_DATE_LESSER 1
173 #define NET_DATE_EQUAL 2
175 #define NET_TIME_GREATER 0
176 #define NET_TIME_LESSER 1
177 #define NET_TIME_EQUAL 2
179 #ifndef _LP64
180 #define FMT_UINT64 "%-15llu"
181 #else
182 #define FMT_UINT64 "%-15lu"
183 #endif
186 * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements.
188 static void
189 dissect_time(char *tbuf, net_time_t *nt)
191 char *d;
192 char *t;
193 char *dd;
194 char *h;
195 char *endp;
197 if (tbuf == NULL || nt == NULL)
198 return;
200 d = strtok(tbuf, ","); /* Date */
201 t = strtok(NULL, ","); /* Time */
203 /* Month */
204 dd = strtok(d, "/");
205 if (dd == NULL)
206 return;
207 nt->net_time_mon = strtol(dd, &endp, 10);
209 /* Day */
210 dd = strtok(NULL, "/");
211 if (dd == NULL)
212 return;
213 nt->net_time_day = strtol(dd, &endp, 10);
215 /* Year */
216 dd = strtok(NULL, "/");
217 if (dd == NULL)
218 return;
219 nt->net_time_yr = strtol(dd, &endp, 10);
220 if (strlen(dd) <= 2)
221 nt->net_time_yr += 2000;
223 if (t == NULL)
224 return;
226 /* Hour */
227 h = strtok(t, ":");
228 if (h == NULL)
229 return;
230 nt->net_time_hr = strtol(h, &endp, 10);
232 /* Min */
233 h = strtok(NULL, ":");
234 if (h == NULL)
235 return;
236 nt->net_time_min = strtol(h, &endp, 10);
238 /* Sec */
239 h = strtok(NULL, ":");
240 if (h == NULL)
241 return;
242 nt->net_time_sec = strtol(h, &endp, 10);
245 /* Get a stat item from an object in the exacct file */
246 static void
247 add_stat_item(ea_object_t *o, net_stat_t *ns)
249 switch (o->eo_catalog & EXT_TYPE_MASK) {
250 case EXT_STRING:
251 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_NAME) {
252 (void) strncpy(ns->net_stat_name, o->eo_item.ei_string,
253 strlen(o->eo_item.ei_string));
255 break;
256 case EXT_UINT64:
257 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_CURTIME) {
258 time_t _time;
259 char timebuf[TIMEBUFLEN];
261 ns->net_stat_ctime = o->eo_item.ei_uint64;
262 _time = ns->net_stat_ctime;
263 (void) strftime(timebuf, sizeof (timebuf),
264 "%m/%d/%Y,%T\n", localtime(&_time));
265 dissect_time(timebuf, &ns->net_stat_time);
266 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
267 EXD_NET_STATS_IBYTES) {
268 ns->net_stat_ibytes = o->eo_item.ei_uint64;
269 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
270 EXD_NET_STATS_OBYTES) {
271 ns->net_stat_obytes = o->eo_item.ei_uint64;
272 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
273 EXD_NET_STATS_IPKTS) {
274 ns->net_stat_ipackets = o->eo_item.ei_uint64;
275 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
276 EXD_NET_STATS_OPKTS) {
277 ns->net_stat_opackets = o->eo_item.ei_uint64;
278 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
279 EXD_NET_STATS_IERRPKTS) {
280 ns->net_stat_ierrors = o->eo_item.ei_uint64;
281 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
282 EXD_NET_STATS_OERRPKTS) {
283 ns->net_stat_oerrors = o->eo_item.ei_uint64;
285 break;
286 default:
287 break;
291 /* Get a description item from an object in the exacct file */
292 static void
293 add_desc_item(ea_object_t *o, net_desc_t *nd)
295 switch (o->eo_catalog & EXT_TYPE_MASK) {
296 case EXT_STRING:
297 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_NAME) {
298 (void) strncpy(nd->net_desc_name, o->eo_item.ei_string,
299 strlen(o->eo_item.ei_string));
300 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
301 EXD_NET_DESC_DEVNAME) {
302 (void) strncpy(nd->net_desc_devname,
303 o->eo_item.ei_string, strlen(o->eo_item.ei_string));
305 break;
306 case EXT_UINT8:
307 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_PROTOCOL) {
308 nd->net_desc_protocol = o->eo_item.ei_uint8;
309 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
310 EXD_NET_DESC_DSFIELD) {
311 nd->net_desc_dsfield = o->eo_item.ei_uint8;
313 break;
314 case EXT_UINT16:
315 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_SPORT) {
316 nd->net_desc_sport = o->eo_item.ei_uint16;
317 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
318 EXD_NET_DESC_DPORT) {
319 nd->net_desc_dport = o->eo_item.ei_uint16;
320 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
321 EXD_NET_DESC_SAP) {
322 nd->net_desc_sap = o->eo_item.ei_uint16;
323 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
324 EXD_NET_DESC_VLAN_TPID) {
325 nd->net_desc_vlan_tpid = o->eo_item.ei_uint16;
326 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
327 EXD_NET_DESC_VLAN_TCI) {
328 nd->net_desc_vlan_tci = o->eo_item.ei_uint16;
329 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
330 EXD_NET_DESC_PRIORITY) {
331 nd->net_desc_priority = o->eo_item.ei_uint16;
333 break;
334 case EXT_UINT32:
335 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4SADDR ||
336 (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4DADDR) {
337 struct in_addr addr;
339 addr.s_addr = htonl(o->eo_item.ei_uint32);
341 if ((o->eo_catalog & EXD_DATA_MASK) ==
342 EXD_NET_DESC_V4SADDR) {
343 IN6_INADDR_TO_V4MAPPED(&addr,
344 &nd->net_desc_saddr);
345 } else {
346 IN6_INADDR_TO_V4MAPPED(&addr,
347 &nd->net_desc_daddr);
350 break;
351 case EXT_UINT64:
352 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_BWLIMIT)
353 nd->net_desc_bw_limit = o->eo_item.ei_uint64;
354 break;
355 case EXT_RAW:
356 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6SADDR ||
357 (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6DADDR) {
358 in6_addr_t addr;
360 addr = *(in6_addr_t *)o->eo_item.ei_raw;
361 if ((o->eo_catalog & EXD_DATA_MASK) ==
362 EXD_NET_DESC_V6SADDR) {
363 nd->net_desc_saddr = addr;
364 } else {
365 nd->net_desc_daddr = addr;
367 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
368 EXD_NET_DESC_EHOST) {
369 bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_ehost,
370 ETHERADDRL);
371 } else if ((o->eo_catalog & EXD_DATA_MASK) ==
372 EXD_NET_DESC_EDEST) {
373 bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_edest,
374 ETHERADDRL);
376 break;
377 default:
378 break;
382 /* Add a description item to the table */
383 static dladm_status_t
384 add_desc_to_tbl(net_table_t *net_table, net_desc_t *nd)
386 net_entry_t *ne;
388 if ((ne = calloc(1, sizeof (net_entry_t))) == NULL)
389 return (DLADM_STATUS_NOMEM);
391 if ((ne->net_entry_tstats = calloc(1, sizeof (net_stat_t))) == NULL) {
392 free(ne);
393 return (DLADM_STATUS_NOMEM);
396 ne->net_entry_desc = nd;
397 ne->net_entry_shead = NULL;
398 ne->net_entry_stail = NULL;
399 ne->net_entry_scount = 0;
401 if (net_table->net_table_head == NULL) {
402 net_table->net_table_head = ne;
403 net_table->net_table_tail = ne;
404 } else {
405 net_table->net_table_tail->net_entry_next = ne;
406 net_table->net_table_tail = ne;
408 net_table->net_entries++;
409 return (DLADM_STATUS_OK);
412 /* Compare dates and return if t1 is equal, greater or lesser than t2 */
413 static int
414 compare_date(net_time_t *t1, net_time_t *t2)
416 if (t1->net_time_yr == t2->net_time_yr &&
417 t1->net_time_mon == t2->net_time_mon &&
418 t1->net_time_day == t2->net_time_day) {
419 return (NET_DATE_EQUAL);
421 if (t1->net_time_yr > t2->net_time_yr ||
422 (t1->net_time_yr == t2->net_time_yr &&
423 t1->net_time_mon > t2->net_time_mon) ||
424 (t1->net_time_yr == t2->net_time_yr &&
425 t1->net_time_mon == t2->net_time_mon &&
426 t1->net_time_day > t2->net_time_day)) {
427 return (NET_DATE_GREATER);
429 return (NET_DATE_LESSER);
432 /* Compare times and return if t1 is equal, greater or lesser than t2 */
433 static int
434 compare_time(net_time_t *t1, net_time_t *t2)
436 int cd;
438 cd = compare_date(t1, t2);
440 if (cd == NET_DATE_GREATER) {
441 return (NET_TIME_GREATER);
442 } else if (cd == NET_DATE_LESSER) {
443 return (NET_TIME_LESSER);
444 } else {
445 if (t1->net_time_hr == t2->net_time_hr &&
446 t1->net_time_min == t2->net_time_min &&
447 t1->net_time_sec == t2->net_time_sec) {
448 return (NET_TIME_EQUAL);
450 if (t1->net_time_hr > t2->net_time_hr ||
451 (t1->net_time_hr == t2->net_time_hr &&
452 t1->net_time_min > t2->net_time_min) ||
453 (t1->net_time_hr == t2->net_time_hr &&
454 t1->net_time_min == t2->net_time_min &&
455 t1->net_time_sec > t2->net_time_sec)) {
456 return (NET_TIME_GREATER);
459 return (NET_TIME_LESSER);
463 * Given a start and end time and start and end entries check if the
464 * times are within the range, and adjust, if needed.
466 static dladm_status_t
467 chk_time_bound(net_time_t *s, net_time_t *e, net_time_t *sns,
468 net_time_t *ens)
470 if (s != NULL && e != NULL) {
471 if (compare_time(s, e) == NET_TIME_GREATER)
472 return (DLADM_STATUS_BADTIMEVAL);
474 if (s != NULL) {
475 if (compare_time(s, sns) == NET_TIME_LESSER) {
476 s->net_time_yr = sns->net_time_yr;
477 s->net_time_mon = sns->net_time_mon;
478 s->net_time_day = sns->net_time_day;
479 s->net_time_hr = sns->net_time_hr;
480 s->net_time_min = sns->net_time_min;
481 s->net_time_sec = sns->net_time_sec;
484 if (e != NULL) {
485 if (compare_time(e, ens) == NET_TIME_GREATER) {
486 e->net_time_yr = ens->net_time_yr;
487 e->net_time_mon = ens->net_time_mon;
488 e->net_time_day = ens->net_time_day;
489 e->net_time_hr = ens->net_time_hr;
490 e->net_time_min = ens->net_time_min;
491 e->net_time_sec = ens->net_time_sec;
494 return (DLADM_STATUS_OK);
498 * Given a start and end time (strings), convert them into net_time_t
499 * and also check for the range given the head and tail of the list.
500 * If stime is lower then head or etime is greated than tail, adjust.
502 static dladm_status_t
503 get_time_range(net_time_entry_t *head, net_time_entry_t *tail,
504 net_time_t *st, net_time_t *et, char *stime, char *etime)
506 bzero(st, sizeof (net_time_t));
507 bzero(et, sizeof (net_time_t));
509 if (stime == NULL && etime == NULL)
510 return (0);
512 if (stime != NULL)
513 dissect_time(stime, st);
514 if (etime != NULL)
515 dissect_time(etime, et);
517 if (stime != NULL || etime != NULL) {
518 return (chk_time_bound(stime == NULL ? NULL : st,
519 etime == NULL ? NULL : et,
520 &head->my_time_stat->net_stat_time,
521 &tail->my_time_stat->net_stat_time));
523 return (0);
527 * Walk the list from a given starting point and return when we find
528 * an entry that is greater or equal to st. lasttime will point to the
529 * previous time entry.
531 static void
532 get_starting_point(net_time_entry_t *head, net_time_entry_t **start,
533 net_time_t *st, char *stime, uint64_t *lasttime)
535 net_time_entry_t *next = head;
537 if (head == NULL) {
538 *start = NULL;
539 return;
541 if (stime == NULL) {
542 *start = head;
543 *lasttime = head->my_time_stat->net_stat_ctime;
544 return;
546 *start = NULL;
547 while (next != NULL) {
548 if (compare_time(st,
549 &next->my_time_stat->net_stat_time) != NET_TIME_LESSER) {
550 *lasttime = next->my_time_stat->net_stat_ctime;
551 next = next->net_time_entry_next;
552 continue;
554 *start = next;
555 break;
560 * Point entry (pe) functions
562 /* Clear all the counters. Done after the contents are written to the file */
563 static void
564 clear_pe(net_plot_entry_t *pe, int entries, int *pentries)
566 int count;
568 for (count = 0; count < entries; count++) {
569 pe[count].net_pe_totbytes = 0;
570 pe[count].net_pe_totibytes = 0;
571 pe[count].net_pe_totobytes = 0;
572 pe[count].net_pe_tottime = 0;
574 *pentries = 0;
577 /* Update an entry in the point entry table */
578 static void
579 update_pe(net_plot_entry_t *pe, net_stat_t *nns, int nentries,
580 int *pentries, uint64_t lasttime)
582 int count;
584 for (count = 0; count < nentries; count++) {
585 if (strcmp(pe[count].net_pe_name, nns->net_stat_name) == 0)
586 break;
588 if (count == nentries)
589 return;
591 if (pe[count].net_pe_totbytes == 0)
592 pe[count].net_pe_lasttime = lasttime;
594 pe[count].net_pe_totbytes += nns->net_stat_ibytes +
595 nns->net_stat_obytes;
596 pe[count].net_pe_tottime += nns->net_stat_tdiff;
597 pe[count].net_pe_totibytes += nns->net_stat_ibytes;
598 pe[count].net_pe_totobytes += nns->net_stat_obytes;
599 (*pentries)++;
602 /* Flush the contents of the point entry table to the file. */
603 static void
604 add_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe,
605 net_stat_t *ns, int entries, void *arg)
607 int count;
608 dladm_usage_t usage;
609 uint64_t tottime;
611 bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime));
612 for (count = 0; count < entries; count++) {
613 bcopy(pe[count].net_pe_name, &usage.du_name,
614 sizeof (usage.du_name));
615 bcopy(&pe[count].net_pe_lasttime, &usage.du_stime,
616 sizeof (usage.du_stime));
617 usage.du_rbytes = pe[count].net_pe_totibytes;
618 usage.du_obytes = pe[count].net_pe_totobytes;
619 tottime = pe[count].net_pe_tottime;
620 usage.du_bandwidth = (tottime > 0) ?
621 ((pe[count].net_pe_totbytes * 8) / tottime) : 0;
622 usage.du_last = (count == entries-1);
623 fn(&usage, arg);
628 * Net entry functions
630 static net_entry_t *
631 get_ne_from_table(net_table_t *net_table, char *name)
633 int count;
634 net_desc_t *nd;
635 net_entry_t *ne = net_table->net_table_head;
637 for (count = 0; count < net_table->net_entries; count++) {
638 nd = ne->net_entry_desc;
639 if (strcmp(name, nd->net_desc_name) == 0)
640 return (ne);
641 ne = ne->net_entry_next;
643 return (NULL);
646 /* Get the entry for the descriptor, if it exists */
647 static net_desc_t *
648 get_ndesc(net_table_t *net_table, net_desc_t *nd)
650 int count;
651 net_desc_t *nd1;
652 net_entry_t *ne = net_table->net_table_head;
654 for (count = 0; count < net_table->net_entries; count++) {
655 nd1 = ne->net_entry_desc;
656 if (strcmp(nd1->net_desc_name, nd->net_desc_name) == 0 &&
657 strcmp(nd1->net_desc_devname, nd->net_desc_devname) == 0 &&
658 bcmp(nd1->net_desc_ehost, nd->net_desc_ehost,
659 ETHERADDRL) == 0 &&
660 bcmp(nd1->net_desc_edest, nd->net_desc_edest,
661 ETHERADDRL) == 0 &&
662 nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid &&
663 nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci &&
664 nd1->net_desc_sap == nd->net_desc_sap &&
665 nd1->net_desc_cpuid == nd->net_desc_cpuid &&
666 nd1->net_desc_priority == nd->net_desc_priority &&
667 nd1->net_desc_bw_limit == nd->net_desc_bw_limit &&
668 nd1->net_desc_sport == nd->net_desc_sport &&
669 nd1->net_desc_dport == nd->net_desc_dport &&
670 nd1->net_desc_protocol == nd->net_desc_protocol &&
671 nd1->net_desc_dsfield == nd->net_desc_dsfield &&
672 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr,
673 &nd->net_desc_saddr) &&
674 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr,
675 &nd->net_desc_daddr)) {
676 return (nd1);
678 ne = ne->net_entry_next;
680 return (NULL);
684 * Update the stat entries. The stats in the file are cumulative, so in order
685 * to have increments, we maintain a reference stat entry, which contains
686 * the stats when the record was first written and a total stat entry, which
687 * maintains the running count. When we want to add a stat entry, if it
688 * the reference stat entry, we don't come here. For subsequent entries,
689 * we get the increment by subtracting the current value from the reference
690 * stat and the total stat.
692 static void
693 update_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref)
696 /* get the increment */
697 ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes);
698 ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes);
699 ns1->net_stat_ipackets -= (ref->net_stat_ipackets +
700 ref->net_stat_tipackets);
701 ns1->net_stat_opackets -= (ref->net_stat_opackets +
702 ref->net_stat_topackets);
703 ns1->net_stat_ierrors -= (ref->net_stat_ierrors +
704 ref->net_stat_tierrors);
705 ns1->net_stat_oerrors -= (ref->net_stat_oerrors +
706 ref->net_stat_toerrors);
708 /* update total bytes */
709 ref->net_stat_tibytes += ns1->net_stat_ibytes;
710 ref->net_stat_tobytes += ns1->net_stat_obytes;
711 ref->net_stat_tipackets += ns1->net_stat_ipackets;
712 ref->net_stat_topackets += ns1->net_stat_opackets;
713 ref->net_stat_tierrors += ns1->net_stat_ierrors;
714 ref->net_stat_toerrors += ns1->net_stat_oerrors;
716 ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes;
717 ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes;
718 ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets;
719 ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets;
720 ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors;
721 ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors;
724 /* Add the stat entry into the table */
725 static dladm_status_t
726 add_stat_to_tbl(net_table_t *net_table, net_stat_t *ns)
728 net_entry_t *ne;
730 ne = get_ne_from_table(net_table, ns->net_stat_name);
731 if (ne == NULL)
732 return (DLADM_STATUS_NOMEM);
734 /* Ptr to flow desc */
735 ns->net_stat_desc = ne->net_entry_desc;
736 if (ns->net_stat_desc->net_desc_newrec) {
737 ns->net_stat_desc->net_desc_newrec = B_FALSE;
738 ns->net_stat_isref = B_TRUE;
739 ne->net_entry_sref = ns;
740 } else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes ||
741 (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) {
742 ns->net_stat_isref = B_TRUE;
743 ne->net_entry_sref = ns;
744 } else {
745 ns->net_stat_isref = B_FALSE;
746 update_stats(ns, ne, ne->net_entry_sref);
748 if (ne->net_entry_shead == NULL) {
749 ne->net_entry_shead = ns;
750 ne->net_entry_stail = ns;
751 } else {
752 if (!ns->net_stat_isref) {
753 ne->net_entry_ttime += (ns->net_stat_ctime -
754 ne->net_entry_stail->net_stat_ctime);
755 ns->net_stat_tdiff = ns->net_stat_ctime -
756 ne->net_entry_stail->net_stat_ctime;
758 ne->net_entry_stail->net_stat_next = ns;
759 ne->net_entry_stail = ns;
762 ne->net_entry_scount++;
763 return (DLADM_STATUS_OK);
766 /* Add a flow/link descriptor record to the table */
767 static dladm_status_t
768 add_desc(net_table_t *net_table, ea_file_t *ef, int nobjs)
770 net_desc_t *nd;
771 net_desc_t *dnd;
772 int count;
773 ea_object_t scratch;
775 if ((nd = calloc(1, sizeof (net_desc_t))) == NULL)
776 return (DLADM_STATUS_NOMEM);
777 nd->net_desc_newrec = B_TRUE;
779 for (count = 0; count < nobjs; count++) {
780 if (ea_get_object(ef, &scratch) == -1) {
781 free(nd);
782 return (DLADM_STATUS_NOMEM);
784 add_desc_item(&scratch, nd);
786 if ((dnd = get_ndesc(net_table, nd)) != NULL) {
787 dnd->net_desc_newrec = B_TRUE;
788 free(nd);
789 return (DLADM_STATUS_OK);
791 if (add_desc_to_tbl(net_table, nd) != 0) {
792 free(nd);
793 return (DLADM_STATUS_NOMEM);
795 return (DLADM_STATUS_OK);
798 /* Make an entry into the time sorted list */
799 static void
800 addto_time_list(net_table_t *net_table, net_time_entry_t *nt,
801 net_time_entry_t *ntc)
803 net_stat_t *ns = nt->my_time_stat;
804 net_stat_t *ns1;
805 net_time_entry_t *end;
806 net_time_t *t1;
807 int count;
809 t1 = &ns->net_stat_time;
811 net_table->net_time_entries++;
813 if (net_table->net_time_head == NULL) {
814 net_table->net_time_head = nt;
815 net_table->net_time_tail = nt;
816 } else {
817 net_table->net_time_tail->net_time_entry_next = nt;
818 nt->net_time_entry_prev = net_table->net_time_tail;
819 net_table->net_time_tail = nt;
822 if (net_table->net_ctime_head == NULL) {
823 net_table->net_ctime_head = ntc;
824 net_table->net_ctime_tail = ntc;
825 } else {
826 end = net_table->net_ctime_tail;
827 count = 0;
828 while (count < net_table->net_time_entries - 1) {
829 ns1 = end->my_time_stat;
830 /* Just add it to the tail */
831 if (compare_date(t1, &ns1->net_stat_time) ==
832 NET_DATE_GREATER) {
833 break;
835 if (strcmp(ns1->net_stat_name, ns->net_stat_name) ==
836 0) {
837 ntc->net_time_entry_next =
838 end->net_time_entry_next;
839 if (end->net_time_entry_next != NULL) {
840 end->net_time_entry_next->
841 net_time_entry_prev = ntc;
842 } else {
843 net_table->net_ctime_tail = ntc;
845 end->net_time_entry_next = ntc;
846 ntc->net_time_entry_prev = end;
847 return;
849 count++;
850 end = end->net_time_entry_prev;
852 net_table->net_ctime_tail->net_time_entry_next = ntc;
853 ntc->net_time_entry_prev = net_table->net_ctime_tail;
854 net_table->net_ctime_tail = ntc;
858 /* Add stat entry into the lists */
859 static dladm_status_t
860 add_stats(net_table_t *net_table, ea_file_t *ef, int nobjs)
862 net_stat_t *ns;
863 int count;
864 ea_object_t scratch;
865 net_time_entry_t *nt;
866 net_time_entry_t *ntc;
868 if ((ns = calloc(1, sizeof (net_stat_t))) == NULL)
869 return (DLADM_STATUS_NOMEM);
871 if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) {
872 free(ns);
873 return (DLADM_STATUS_NOMEM);
875 if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) {
876 free(ns);
877 free(nt);
878 return (DLADM_STATUS_NOMEM);
881 nt->my_time_stat = ns;
882 ntc->my_time_stat = ns;
884 for (count = 0; count < nobjs; count++) {
885 if (ea_get_object(ef, &scratch) == -1) {
886 free(ns);
887 free(nt);
888 free(ntc);
889 return (DLADM_STATUS_NOMEM);
891 add_stat_item(&scratch, ns);
893 if (add_stat_to_tbl(net_table, ns) != 0) {
894 free(ns);
895 free(nt);
896 free(ntc);
897 return (DLADM_STATUS_NOMEM);
899 addto_time_list(net_table, nt, ntc);
900 return (DLADM_STATUS_OK);
903 /* Free the entire table */
904 static void
905 free_logtable(net_table_t *net_table)
907 net_entry_t *head;
908 net_entry_t *next;
909 net_stat_t *ns;
910 net_stat_t *ns1;
911 net_time_entry_t *thead;
912 net_time_entry_t *tnext;
914 thead = net_table->net_time_head;
915 while (thead != NULL) {
916 thead->my_time_stat = NULL;
917 tnext = thead->net_time_entry_next;
918 thead->net_time_entry_next = NULL;
919 thead->net_time_entry_prev = NULL;
920 free(thead);
921 thead = tnext;
923 net_table->net_time_head = NULL;
924 net_table->net_time_tail = NULL;
926 thead = net_table->net_ctime_head;
927 while (thead != NULL) {
928 thead->my_time_stat = NULL;
929 tnext = thead->net_time_entry_next;
930 thead->net_time_entry_next = NULL;
931 thead->net_time_entry_prev = NULL;
932 free(thead);
933 thead = tnext;
935 net_table->net_ctime_head = NULL;
936 net_table->net_ctime_tail = NULL;
938 net_table->net_time_entries = 0;
940 head = net_table->net_table_head;
941 while (head != NULL) {
942 next = head->net_entry_next;
943 head->net_entry_next = NULL;
944 ns = head->net_entry_shead;
945 while (ns != NULL) {
946 ns1 = ns->net_stat_next;
947 free(ns);
948 ns = ns1;
950 head->net_entry_scount = 0;
951 head->net_entry_sref = NULL;
952 free(head->net_entry_desc);
953 free(head->net_entry_tstats);
954 free(head);
955 head = next;
957 net_table->net_table_head = NULL;
958 net_table->net_table_tail = NULL;
959 net_table->net_time_entries = 0;
960 free(net_table);
963 /* Parse the exacct file, and return the parsed table. */
964 static void *
965 parse_logfile(char *file, int logtype, dladm_status_t *status)
967 ea_file_t ef;
968 ea_object_t scratch;
969 net_table_t *net_table;
971 *status = DLADM_STATUS_OK;
972 if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) {
973 *status = DLADM_STATUS_NOMEM;
974 return (NULL);
976 if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) {
977 *status = DLADM_STATUS_BADARG;
978 free(net_table);
979 return (NULL);
981 bzero(&scratch, sizeof (ea_object_t));
982 while (ea_get_object(&ef, &scratch) != -1) {
983 if (scratch.eo_type != EO_GROUP) {
984 (void) ea_free_item(&scratch, EUP_ALLOC);
985 (void) bzero(&scratch, sizeof (ea_object_t));
986 continue;
988 /* Read Link Desc/Stat records */
989 if (logtype == DLADM_LOGTYPE_FLOW) {
990 /* Flow Descriptor */
991 if ((scratch.eo_catalog &
992 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) {
993 (void) add_desc(net_table, &ef,
994 scratch.eo_group.eg_nobjs - 1);
995 /* Flow Stats */
996 } else if ((scratch.eo_catalog &
997 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) {
998 (void) add_stats(net_table, &ef,
999 scratch.eo_group.eg_nobjs - 1);
1001 } else if (logtype == DLADM_LOGTYPE_LINK) {
1002 /* Link Descriptor */
1003 if ((scratch.eo_catalog &
1004 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) {
1005 (void) add_desc(net_table, &ef,
1006 scratch.eo_group.eg_nobjs - 1);
1007 /* Link Stats */
1008 } else if ((scratch.eo_catalog &
1009 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) {
1010 (void) add_stats(net_table, &ef,
1011 scratch.eo_group.eg_nobjs - 1);
1013 } else {
1014 if (((scratch.eo_catalog & EXD_DATA_MASK) ==
1015 EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog &
1016 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) {
1017 (void) add_desc(net_table, &ef,
1018 scratch.eo_group.eg_nobjs - 1);
1019 } else if (((scratch.eo_catalog & EXD_DATA_MASK) ==
1020 EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog &
1021 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) {
1022 (void) add_stats(net_table, &ef,
1023 scratch.eo_group.eg_nobjs - 1);
1026 (void) ea_free_item(&scratch, EUP_ALLOC);
1027 (void) bzero(&scratch, sizeof (ea_object_t));
1030 (void) ea_close(&ef);
1031 return ((void *)net_table);
1035 * Walk the ctime list. This is used when looking for usage records
1036 * based on a "resource" name.
1038 dladm_status_t
1039 dladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype,
1040 char *logfile, char *resource, char *stime, char *etime, void *arg)
1042 net_table_t *net_table;
1043 net_time_t st, et;
1044 net_time_entry_t *start;
1045 net_stat_t *ns = NULL;
1046 net_stat_t *nns;
1047 uint64_t tot_time = 0;
1048 uint64_t last_time;
1049 uint64_t tot_bytes = 0;
1050 uint64_t tot_ibytes = 0;
1051 uint64_t tot_obytes = 0;
1052 boolean_t gotstart = B_FALSE;
1053 dladm_status_t status;
1054 dladm_usage_t usage;
1055 int step = 1;
1057 /* Parse the log file */
1058 net_table = parse_logfile(logfile, logtype, &status);
1059 if (net_table == NULL)
1060 return (status);
1062 if (net_table->net_entries == 0)
1063 return (DLADM_STATUS_OK);
1064 start = net_table->net_ctime_head;
1066 /* Time range */
1067 status = get_time_range(net_table->net_ctime_head,
1068 net_table->net_ctime_tail, &st, &et, stime, etime);
1069 if (status != DLADM_STATUS_OK)
1070 return (status);
1072 while (start != NULL) {
1073 nns = start->my_time_stat;
1075 /* Get to the resource we are interested in */
1076 if (strcmp(resource, nns->net_stat_name) != 0) {
1077 start = start->net_time_entry_next;
1078 continue;
1081 /* Find the first record */
1082 if (!gotstart) {
1083 get_starting_point(start, &start, &st, stime,
1084 &last_time);
1085 if (start == NULL)
1086 break;
1087 nns = start->my_time_stat;
1088 gotstart = B_TRUE;
1091 /* Write one entry and return if we are out of the range */
1092 if (etime != NULL && compare_time(&nns->net_stat_time, &et)
1093 == NET_TIME_GREATER) {
1094 if (tot_bytes != 0) {
1095 bcopy(ns->net_stat_name, &usage.du_name,
1096 sizeof (usage.du_name));
1097 bcopy(&last_time, &usage.du_stime,
1098 sizeof (usage.du_stime));
1099 bcopy(&ns->net_stat_ctime, &usage.du_etime,
1100 sizeof (usage.du_etime));
1101 usage.du_rbytes = tot_ibytes;
1102 usage.du_obytes = tot_obytes;
1103 usage.du_bandwidth = tot_bytes*8/tot_time;
1104 usage.du_last = B_TRUE;
1105 fn(&usage, arg);
1107 return (DLADM_STATUS_OK);
1111 * If this is a reference entry, just print what we have
1112 * and proceed.
1114 if (nns->net_stat_isref) {
1115 if (tot_bytes != 0) {
1116 bcopy(&nns->net_stat_name, &usage.du_name,
1117 sizeof (usage.du_name));
1118 bcopy(&nns->net_stat_ctime, &usage.du_stime,
1119 sizeof (usage.du_stime));
1120 usage.du_rbytes = tot_ibytes;
1121 usage.du_obytes = tot_obytes;
1122 usage.du_bandwidth = tot_bytes*8/tot_time;
1123 usage.du_last = B_TRUE;
1124 fn(&usage, arg);
1125 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
1126 tot_obytes, step);
1128 last_time = nns->net_stat_ctime;
1129 start = start->net_time_entry_next;
1130 continue;
1133 ns = nns;
1134 if (--step == 0) {
1135 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
1136 tot_ibytes += ns->net_stat_ibytes;
1137 tot_obytes += ns->net_stat_obytes;
1138 tot_time += ns->net_stat_tdiff;
1139 bcopy(&ns->net_stat_name, &usage.du_name,
1140 sizeof (usage.du_name));
1141 bcopy(&last_time, &usage.du_stime,
1142 sizeof (usage.du_stime));
1143 bcopy(&ns->net_stat_ctime, &usage.du_etime,
1144 sizeof (usage.du_etime));
1145 usage.du_rbytes = tot_ibytes;
1146 usage.du_obytes = tot_obytes;
1147 usage.du_bandwidth = tot_bytes*8/tot_time;
1148 usage.du_last = B_TRUE;
1149 fn(&usage, arg);
1151 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
1152 tot_obytes, step);
1153 last_time = ns->net_stat_ctime;
1154 } else {
1155 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
1156 tot_ibytes += ns->net_stat_ibytes;
1157 tot_obytes += ns->net_stat_obytes;
1158 tot_time += ns->net_stat_tdiff;
1160 start = start->net_time_entry_next;
1163 if (tot_bytes != 0) {
1164 bcopy(&ns->net_stat_name, &usage.du_name,
1165 sizeof (usage.du_name));
1166 bcopy(&last_time, &usage.du_stime,
1167 sizeof (usage.du_stime));
1168 bcopy(&ns->net_stat_ctime, &usage.du_etime,
1169 sizeof (usage.du_etime));
1170 usage.du_rbytes = tot_ibytes;
1171 usage.du_obytes = tot_obytes;
1172 usage.du_bandwidth = tot_bytes*8/tot_time;
1173 usage.du_last = B_TRUE;
1174 fn(&usage, arg);
1177 free_logtable(net_table);
1178 return (status);
1182 * Walk the time sorted list if a resource is not specified.
1184 dladm_status_t
1185 dladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype,
1186 char *logfile, char *stime, char *etime, void *arg)
1188 net_table_t *net_table;
1189 net_time_entry_t *start;
1190 net_stat_t *ns = NULL, *nns;
1191 net_time_t st, et, *t1;
1192 net_desc_t *nd;
1193 net_entry_t *ne;
1194 net_plot_entry_t *pe;
1195 int count;
1196 int step = 1;
1197 int nentries = 0, pentries = 0;
1198 uint64_t last_time;
1199 dladm_status_t status;
1201 /* Parse the log file */
1202 net_table = parse_logfile(logfile, logtype, &status);
1203 if (net_table == NULL)
1204 return (status);
1206 if (net_table->net_entries == 0)
1207 return (DLADM_STATUS_OK);
1208 start = net_table->net_time_head;
1210 /* Find the first and last records and starting point */
1211 status = get_time_range(net_table->net_time_head,
1212 net_table->net_time_tail, &st, &et, stime, etime);
1213 if (status != DLADM_STATUS_OK)
1214 return (status);
1215 get_starting_point(start, &start, &st, stime, &last_time);
1217 * Could assert to be non-null, since get_time_range()
1218 * would have adjusted.
1220 if (start == NULL)
1221 return (DLADM_STATUS_BADTIMEVAL);
1224 * Collect entries for all resources in a time slot before
1225 * writing to the file.
1227 nentries = net_table->net_entries;
1229 pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1);
1230 if (pe == NULL)
1231 return (DLADM_STATUS_NOMEM);
1233 ne = net_table->net_table_head;
1234 for (count = 0; count < nentries; count++) {
1235 nd = ne->net_entry_desc;
1236 pe[count].net_pe_name = nd->net_desc_name;
1237 ne = ne->net_entry_next;
1240 clear_pe(pe, nentries, &pentries);
1242 /* Write header to file */
1243 /* add_pe_to_file(fn, pe, ns, nentries, arg); */
1245 t1 = &start->my_time_stat->net_stat_time;
1247 while (start != NULL) {
1249 nns = start->my_time_stat;
1251 * We have crossed the time boundary, check if we need to
1252 * print out now.
1254 if (compare_time(&nns->net_stat_time, t1) ==
1255 NET_TIME_GREATER) {
1256 /* return if we are out of the range */
1257 if (etime != NULL &&
1258 compare_time(&nns->net_stat_time, &et) ==
1259 NET_TIME_GREATER) {
1260 if (pentries > 0) {
1261 add_pe_to_file(fn, pe, ns, nentries,
1262 arg);
1263 clear_pe(pe, nentries, &pentries);
1265 free(pe);
1266 return (DLADM_STATUS_OK);
1268 /* update the stats from the ns. */
1269 t1 = &nns->net_stat_time;
1270 last_time = ns->net_stat_ctime;
1271 if (--step == 0) {
1272 if (pentries > 0) {
1273 add_pe_to_file(fn, pe, ns, nentries,
1274 arg);
1275 clear_pe(pe, nentries, &pentries);
1277 step = 1;
1282 * if this is a reference entry, just print what we have
1283 * for this resource and proceed. We will end up writing
1284 * the stats for all the entries when we hit a ref element,
1285 * which means 'steps' for some might not be accurate, but
1286 * that is fine, the alternative is to write only the
1287 * resource for which we hit a reference entry.
1289 if (nns->net_stat_isref) {
1290 if (pentries > 0) {
1291 add_pe_to_file(fn, pe, ns, nentries, arg);
1292 clear_pe(pe, nentries, &pentries);
1294 step = 1;
1295 } else {
1296 update_pe(pe, nns, nentries, &pentries, last_time);
1298 ns = nns;
1299 start = start->net_time_entry_next;
1302 if (pentries > 0)
1303 add_pe_to_file(fn, pe, ns, nentries, arg);
1305 free(pe);
1306 free_logtable(net_table);
1308 return (DLADM_STATUS_OK);
1311 dladm_status_t
1312 dladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype,
1313 char *logfile, void *arg)
1315 net_table_t *net_table;
1316 net_entry_t *ne;
1317 net_desc_t *nd;
1318 net_stat_t *ns;
1319 int count;
1320 dladm_usage_t usage;
1321 dladm_status_t status;
1323 /* Parse the log file */
1324 net_table = parse_logfile(logfile, logtype, &status);
1325 if (net_table == NULL)
1326 return (status);
1328 if (net_table->net_entries == 0)
1329 return (DLADM_STATUS_OK);
1331 ne = net_table->net_table_head;
1332 for (count = 0; count < net_table->net_entries; count++) {
1333 ns = ne->net_entry_tstats;
1334 nd = ne->net_entry_desc;
1336 if (ns->net_stat_ibytes + ns->net_stat_obytes == 0) {
1337 ne = ne->net_entry_next;
1338 continue;
1340 bcopy(&nd->net_desc_name, &usage.du_name,
1341 sizeof (usage.du_name));
1342 usage.du_duration = ne->net_entry_ttime;
1343 usage.du_ipackets = ns->net_stat_ipackets;
1344 usage.du_rbytes = ns->net_stat_ibytes;
1345 usage.du_opackets = ns->net_stat_opackets;
1346 usage.du_obytes = ns->net_stat_obytes;
1347 usage.du_bandwidth =
1348 (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 /
1349 usage.du_duration;
1350 usage.du_last = (count == net_table->net_entries-1);
1351 fn(&usage, arg);
1353 ne = ne->net_entry_next;
1356 free_logtable(net_table);
1357 return (DLADM_STATUS_OK);
1361 * Walk the ctime list and display the dates of the records.
1363 dladm_status_t
1364 dladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype,
1365 char *logfile, char *resource, void *arg)
1367 net_table_t *net_table;
1368 net_time_entry_t *start;
1369 net_stat_t *nns;
1370 net_time_t st;
1371 net_time_t *lasttime = NULL;
1372 uint64_t last_time;
1373 boolean_t gotstart = B_FALSE;
1374 dladm_status_t status;
1375 dladm_usage_t usage;
1377 /* Parse the log file */
1378 net_table = parse_logfile(logfile, logtype, &status);
1379 if (net_table == NULL)
1380 return (status);
1382 if (net_table->net_entries == 0)
1383 return (DLADM_STATUS_OK);
1385 start = net_table->net_ctime_head;
1387 while (start != NULL) {
1388 nns = start->my_time_stat;
1390 /* get to the resource we are interested in */
1391 if (resource != NULL) {
1392 if (strcmp(resource, nns->net_stat_name) != 0) {
1393 start = start->net_time_entry_next;
1394 continue;
1398 /* get the starting point in the logfile */
1399 if (!gotstart) {
1400 get_starting_point(start, &start, &st, NULL,
1401 &last_time);
1402 if (start == NULL)
1403 break;
1404 nns = start->my_time_stat;
1405 gotstart = B_TRUE;
1408 if (lasttime == NULL ||
1409 compare_date(&nns->net_stat_time, lasttime) ==
1410 NET_DATE_GREATER) {
1411 bzero(&usage, sizeof (dladm_usage_t));
1412 (void) strlcpy(usage.du_name, nns->net_stat_name,
1413 sizeof (usage.du_name));
1414 bcopy(&nns->net_stat_ctime, &usage.du_stime,
1415 sizeof (usage.du_stime));
1416 fn(&usage, arg);
1417 lasttime = &nns->net_stat_time;
1420 start = start->net_time_entry_next;
1421 continue;
1424 free_logtable(net_table);
1425 return (status);