1/*
2 * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
3 *  Reserved.
4 *
5 *  This file contains Original Code and/or Modifications of Original Code
6 *  as defined in and that are subject to the Apple Public Source License
7 *  Version 2.0 (the 'License'). You may not use this file except in
8 *  compliance with the License. Please obtain a copy of the License at
9 *  http://www.opensource.apple.com/apsl/ and read it before using this
10 *  file.
11 *
12 * The Original Code and all software distributed under the License are
13 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
17 * Please see the License for the specific language governing rights and
18 * limitations under the License.
19*/
20
21/*
22  cc -Wall -I. -I ../sadc.tproj -O -o  sar sar.c
23*/
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <ctype.h>
29#include <time.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <signal.h>
33#include <mach/mach.h>
34#include <sys/param.h>
35#include <sys/sysctl.h>
36
37#include <sadc.h>
38#include <sar.h>
39
40
41#define IFNET_32_BIT_COUNTERS 1
42
43/* Options used only for launching sadc */
44int t_interval = 5; 	        /* in seconds              */
45char * t_intervalp = "5";
46int n_samples  = 1;	        /* number of sample loops  */
47char * n_samplesp = "1";
48
49/* Used only for storing the binary output after launching sadc */
50char *outfile  = NULL;          /* output file             */
51int ofd        = 0;		/* output file descriptor  */
52
53/*
54 * When launching sadc, this file descriptor reads sadc's stdout
55 *    via pipe.
56 * When not launching sadc, this file descriptor will be either
57 *    the input file passed in with the -f flag
58 *    or the standard input file /var/log/sa/saXX
59 */
60int ifd        = 0;		/* input file descriptor   */
61char *infile   = NULL;          /* input file              */
62
63
64
65/* Used when we have to luanch sadc */
66pid_t pid;
67int fd[2];  /* read from fd[0], write to fd[1] */
68
69char *optionstring1 =  "Adgn:puo:";
70char *optionstring1_usage = "/usr/bin/sar [-Adgpu] [-n { DEV | EDEV | PPP } ] [-o filename] t [n]";
71char *optionstring2 = "Adgn:pue:f:i:s:";
72char *optionstring2_usage = "/usr/bin/sar [-Adgpu] [-n { DEV | EDEV | PPP }] [-e time] [-f filename] [-i sec] [-s time]";
73
74
75/* option flags */
76int aflag = 0;
77int Aflag = 0;
78int bflag = 0;
79int cflag = 0;
80int dflag = 0;  /* drive statistics */
81int gflag = 0;  /* page-out activity */
82int kflag = 0;
83int mflag = 0;
84
85int nflag = 0;  /* network statistics */
86int network_mode = 0;
87char *sadc_mflagp = "-m";
88char *sadc_ppp_modep = "PPP";
89
90int pflag = 0;  /* page-in activity */
91int qflag = 0;
92int rflag = 0;
93int uflag = 0;   /* cpu utilization - this is the only default */
94int vflag = 0;
95int wflag = 0;
96int yflag = 0;
97int set_default_flag = 1;
98int flag_count = 0;
99
100/*
101 *  To get the current time of day in seconds
102 *  based on a 24 hour clock, pass in the time_t from time()
103 *  the remainder is the current time in seconds
104*/
105#define HOURS_PER_DAY 24
106#define MINS_PER_HOUR 60
107#define SECS_PER_MIN 60
108#define SECS_PER_DAY (SECS_PER_MIN * MINS_PER_HOUR * HOURS_PER_DAY)
109
110/* end time delimiter -- converted from hh:mm:ss to seconds */
111time_t end_time = 0;
112
113int iflag = 0;
114int iseconds = 0;  /* interval seconds, default = 0 implies all samples are
115		    * printed */
116
117/* start time delimiter -- converted from hh:mm:ss to seconds */
118time_t start_time = 0;
119
120int oflag = 0;
121int fflag = 0;
122
123/* stat records average and previous */
124struct vm_statistics       prev_vmstat,  avg_vmstat, cur_vmstat;
125host_cpu_load_info_data_t  prev_cpuload, avg_cpuload, cur_cpuload;
126struct drivestats_report   *dr_head = NULL;
127
128/* internal table of drive path mappings */
129struct drivepath *dp_table = NULL;
130int dp_count = 0;
131
132/* internal table of network interface statistics */
133struct netstats_report *nr_table = NULL;
134int nr_count;
135struct netstats *netstat_readbuf = NULL;
136size_t netstat_readbuf_size = 0;
137
138int avg_counter = 0;
139int avg_interval = 0;
140
141extern int errno;
142
143/* Forward function declarations */
144static void exit_usage();
145static void open_output_file(char *path);
146static void open_input_file(char *path);
147static void read_record_hdr(struct record_hdr *hdr, int writeflag);
148static void read_record_data(char *buf, size_t size, int writeflag);
149static void write_record_hdr(struct record_hdr *hdr);
150static void write_record_data(char *buf, size_t size);
151static time_t convert_hms(char *string);
152static char *get_hms_string(time_t, char *);
153static int find_restart_header(struct record_hdr *);
154static void print_all_column_headings (time_t timestamp);
155static void print_column_heading (int type, char *timebufptr, int mode);
156static void read_sample_set(int, time_t, struct record_hdr *);
157static void do_main_workloop();
158static int bypass_sample_set(struct record_hdr *, time_t);
159static void skip_data(int);
160static int get_cpu_sample(int flag, struct record_hdr *hdr);
161static void print_cpu_sample(char *timebufptr);
162static int get_vmstat_sample(int flag, struct record_hdr *hdr);
163static void print_vmstat_sample(char *timebufptr);
164
165static int get_drivestats_sample(int flag, struct record_hdr *hdr);
166static void init_drivestats(struct drivestats_report *dr);
167static void print_drivestats_sample(char *timebufptr);
168static int get_drivepath_sample(int flag, struct record_hdr *hdr);
169
170static void set_cur_netstats(struct netstats_report *nr, struct netstats *ns);
171static void init_prev_netstats(struct netstats_report *nr);
172static int get_netstats_sample(int flag, struct record_hdr *hdr);
173static void print_netstats_sample(char *timebufptr);
174
175static void exit_average();
176
177int
178main(argc, argv)
179    int argc;
180    char *argv[];
181{
182
183    char    ch;
184
185    time_t curr_time;		/* current time in seconds */
186    char timebuf[26];
187    char filenamebuf[20];
188    char *optstring = NULL;
189    int optstringval;
190    int i;
191
192    /*
193     * Detirmine which option string to use
194     */
195
196    optreset=0;
197    optstringval=0;
198
199    while((ch=getopt(argc, argv, "aAbcdgkmn:pqruvwyo:e:f:i:s:")) != EOF) {
200	switch(ch) {
201	case 'o':
202	    if (optstringval == 2)
203		exit_usage();
204	    optstring=optionstring1;
205	    optstringval=1;
206	    break;
207	case 'e':
208	case 'f':
209	case 'i':
210	case 's':
211	    if (optstringval == 1)
212		exit_usage();
213	    optstring=optionstring2;
214	    optstringval=2;
215	    break;
216	default:
217	    /* ignore for now */
218	    break;
219	}
220    }
221
222    if (!optstring)
223    {
224	/* still trying to determine which option string to use */
225	if (argc - optind > 0)
226	{
227	    optstring=optionstring1;  /* we should have a t_second value */
228	    optstringval=1;
229	}
230	else
231	{
232	    optstring=optionstring2;
233	    optstringval=2;
234	}
235    }
236
237    optreset = optind = 1;
238    while ((ch=getopt(argc, argv, optstring)) != EOF) {
239	switch (ch) {
240	case 'a':
241	    aflag = 1;
242	    set_default_flag = 0;
243	    flag_count++;
244	    break;
245	case 'A':
246	    Aflag = 1;
247	    set_default_flag = 0;
248	    flag_count++;
249	    break;
250	case 'b':
251	    bflag = 1;
252	    set_default_flag = 0;
253	    flag_count++;
254	    break;
255	case 'c':
256	    cflag = 1;
257	    set_default_flag = 0;
258	    flag_count++;
259	    break;
260	case 'd':
261	    dflag = 1;
262	    set_default_flag = 0;
263	    flag_count++;
264	    break;
265	case 'g':
266	    gflag = 1;
267	    set_default_flag = 0;
268	    flag_count++;
269	    break;
270	case 'k':
271	    kflag = 1;
272	    set_default_flag = 0;
273	    flag_count++;
274	    break;
275	case 'm':
276	    mflag = 1;
277	    set_default_flag = 0;
278	    flag_count++;
279	    break;
280	case 'n':
281	    nflag= 1;
282	    if (!strncmp(optarg, "PPP", 3))
283		network_mode |= NET_PPP_MODE;
284	    else if (!strncmp(optarg, "DEV", 3))
285		network_mode |= NET_DEV_MODE;
286	    else if (!strncmp(optarg, "EDEV", 4))
287		network_mode |= NET_EDEV_MODE;
288	    else
289		exit_usage();
290	    set_default_flag = 0;
291	    flag_count++;
292	    break;
293	case 'p':
294	    pflag = 1;
295	    set_default_flag = 0;
296	    flag_count++;
297	    break;
298	case 'q':
299	    qflag = 1;
300	    set_default_flag = 0;
301	    flag_count++;
302	    break;
303	case 'r':
304	    rflag = 1;
305	    set_default_flag = 0;
306	    flag_count++;
307	    break;
308	case 'u':
309	    uflag= 1;
310	    set_default_flag = 0;
311	    flag_count++;
312	    break;
313	case 'v':
314	    vflag = 1;
315	    set_default_flag = 0;
316	    flag_count++;
317	    break;
318	case 'w':
319	    wflag = 1;
320	    set_default_flag = 0;
321	    flag_count++;
322	    break;
323	case 'y':
324	    yflag = 1;
325	    set_default_flag = 0;
326	    flag_count++;
327	    break;
328	case 'o':
329	    /* open the output file */
330	    oflag = 1;
331	    outfile=optarg;
332	    (void)open_output_file(outfile);
333	    break;
334	case 'e':  /* eflag */
335	    end_time = convert_hms(optarg);
336	    break;
337	case 'f':
338	    fflag = 1;
339	    infile=optarg;
340	    break;
341	case 'i':
342	    iflag = 1;
343	    iseconds=atoi(optarg);
344	    break;
345	case 's':
346	    start_time = convert_hms(optarg);
347	    break;
348	default:
349	    exit_usage();
350	    break;
351	}
352    }
353
354    /* setup default uflag option */
355    if (Aflag)
356    {
357	dflag = gflag = pflag = uflag = 1;
358	if (!nflag)
359	{
360	    /*
361	     * Add network stats to the load
362	     * but avoid PPP data by default.
363	     */
364	    nflag = 1;
365	    network_mode = NET_DEV_MODE | NET_EDEV_MODE;;
366	}
367	flag_count = 2;	  /* triggers column headings */
368    }
369    else if (set_default_flag)
370    {
371	uflag=1;
372	flag_count++;
373    }
374
375    if (nflag)
376    {
377	if (network_mode & NET_PPP_MODE)
378	{
379	    if (!(network_mode & NET_DEV_MODE) &&
380	      !(network_mode & NET_EDEV_MODE))
381	    {
382		/* set defaults */
383		network_mode |= NET_DEV_MODE;
384		network_mode |= NET_EDEV_MODE;
385		flag_count++;
386	    }
387	}
388    }
389
390    argc -= optind;
391    argv += optind;
392
393    /* set up signal handlers */
394    signal(SIGINT,  exit_average);
395    signal(SIGQUIT, exit_average);
396    signal(SIGHUP,  exit_average);
397    signal(SIGTERM, exit_average);
398
399    if (optstringval == 1)
400    {
401	/* expecting a time interval */
402
403	char *p;
404
405	if (argc >= 1)
406	{
407	    errno = 0;
408	    t_interval = strtol(argv[0], &p, 0);
409	    t_intervalp = argv[0];
410	    if (errno || (*p != '\0') || t_interval <= 0 )
411		exit_usage();
412	    if (argc >= 2)
413	    {
414		errno=0;
415		n_samples = strtol(argv[1], &p, 0);
416		n_samplesp = argv[1];
417		if (errno || (*p != '\0') || n_samples <= 0)
418		    exit_usage();
419	    }
420	}
421    }
422
423    /* where does the input come from */
424    if (fflag)
425    {
426	(void)open_input_file(infile);
427    }
428    else if (optstringval == 2)
429    {
430	/*
431	 * Create a filename of the form /var/log/sa/sadd
432	 * where "dd" is the date of the month
433	 */
434	curr_time = time((time_t *)0);        /* returns time in seconds */
435
436	/*
437	  timebuf will be a 26-character string of the form:
438	  Thu Nov 24 18:22:48 1986\n\0
439	*/
440
441	ctime_r(&curr_time, timebuf);
442	strncpy(filenamebuf, "/var/log/sa/sa", 14);
443	strncpy(&filenamebuf[14], &timebuf[8], 2);
444	if (filenamebuf[14] == ' ')
445	    filenamebuf[14] = '0';
446	filenamebuf[16]='\0';
447	infile = filenamebuf;
448	(void)open_input_file(infile);
449    }
450    else if (optstringval == 1)
451    {
452	/* launch sadc */
453	if (pipe(fd) == -1)
454	{
455	    fprintf(stderr, "sar: pipe(2) failed, errno = (%d)\n",errno);
456	    exit(1);
457	}
458
459	if ((pid=fork()) == 0)
460	{
461#if 0
462	    int efd;
463#endif
464	    int fdlimit = getdtablesize();
465
466            /* This is the child */
467	    /* Close all file descriptors except the one we need */
468
469	    for (i=0; i < fdlimit; i++) {
470		if ((i != fd[0]) && (i != fd[1]))
471		    (void)close(i);
472	    }
473#if 0
474	    efd = open("/tmp/errlog", O_CREAT|O_APPEND|O_RDWR, 0666);
475	    if (dup2(efd,2) == -1) {
476		exit(1);
477	    }
478#endif
479	    /* Dup the two file descriptors to stdin and stdout */
480	    if (dup2(fd[0],0) == -1) {
481		exit(1);
482	    }
483	    if (dup2(fd[1],1) == -1) {
484		exit(1);
485	    }
486	    /* Exec the child process */
487	    if (network_mode & NET_PPP_MODE)
488		execl("/usr/lib/sa/sadc", "sadc", sadc_mflagp, sadc_ppp_modep, t_intervalp, n_samplesp, NULL);
489	    else
490		execl("/usr/lib/sa/sadc", "sadc", t_intervalp, n_samplesp, NULL);
491
492	    perror("execlp sadc");
493	    exit(2); /* This call of exit(2) should never be reached... */
494	}
495	else
496	{	 /* This is the parent */
497	    if (pid == -1) {
498		fprintf(stderr, "sar: fork(2) failed, errno = (%d)\n",errno);
499		exit(1);
500	    }
501	    close (fd[1]);  /* parent does not write to the pipe */
502	    ifd = fd[0];    /* parent will read from the pipe */
503	}
504    }
505    else
506    {
507	/* we're confused about source of input data - bail out */
508	fprintf(stderr, "sar: no input file recognized\n");
509	exit_usage();
510    }
511
512    /* start reading input data and format the output */
513    (void)do_main_workloop();
514    (void)exit_average();
515    exit(0);
516}
517
518static void
519exit_usage()
520{
521    fprintf(stderr, "\n%s\n\n", optionstring1_usage);
522    fprintf(stderr, "%s\n",   optionstring2_usage);
523    exit(EXIT_FAILURE);
524}
525
526static void
527open_output_file(char *path)
528{
529    if ((ofd = open(path, O_CREAT|O_APPEND|O_TRUNC|O_WRONLY, 0664)) == -1 )
530    {
531	/* failed to open path */
532	fprintf(stderr, "sar: failed to open output file [%s]\n", path);
533	exit_usage();
534    }
535}
536
537
538static void
539open_input_file(char *path)
540{
541    if ((ifd = open(path, O_RDONLY, 0)) == -1)
542    {
543	/* failed to open path */
544	fprintf(stderr, "sar: failed to open input file [%d][%s]\n", ifd, path);
545	exit_usage();
546    }
547}
548
549static void
550read_record_hdr(hdr, writeflag)
551    struct record_hdr *hdr;
552    int writeflag;
553{
554    errno = 0;
555    int num = 0;
556    int n = 0;
557    size_t size = 0;
558
559    size = sizeof(struct record_hdr);
560
561    while (size)
562    {
563	num = read(ifd, &hdr[n], size);
564	if (num > 0)
565	{
566	    n += num;
567	    size -= num;
568	}
569	else if (num == 0)
570	    exit_average();
571	else
572	{
573	    fprintf(stderr, "sar: read_record_data failed, errno=%d num=%d, size=%d\n", (int)errno, (int)num, (int)size);
574	    exit(EXIT_FAILURE);
575	}
576    }
577
578    if (oflag && writeflag)
579	write_record_hdr(hdr);
580
581    return;
582}
583
584static void
585read_record_data(buf, size, writeflag)
586    char *  buf;
587    size_t  size;
588    int     writeflag;
589{
590    errno = 0;
591    size_t num = 0;
592    size_t n = 0;
593
594    while (size)
595    {
596	num = read(ifd, &buf[n], size);
597	if (num > 0)
598	{
599	    n += num;
600	    size -= num;
601	}
602	else if (num == 0)   /* EOF */
603	    exit_average();
604	else
605	{
606	    fprintf(stderr, "sar: read_record_data failed, errno=%d num=%d, size=%d\n", (int)errno, (int)num, (int)size);
607	    exit(EXIT_FAILURE);
608	}
609    }
610
611    if (oflag && writeflag)
612	write_record_data(buf, n);
613
614    return;
615}
616
617static void
618write_record_hdr(hdr)
619    struct record_hdr *hdr;
620{
621    errno = 0;
622    int num;
623
624    if ((num = write(ofd, hdr, sizeof(struct record_hdr))) == -1)
625    {
626	fprintf(stderr, "sar: write_record_hdr failed, errno=%d\n", errno);
627	exit(EXIT_FAILURE);
628    }
629    return;
630}
631
632static void
633write_record_data(char *buf, size_t nbytes)
634{
635    errno = 0;
636    int num;
637    if ((num = write(ofd, buf, nbytes)) == -1)
638    {
639	fprintf(stderr, "sar: write_record_data failed, errno=%d\n", errno);
640	exit(EXIT_FAILURE);
641    }
642    return;
643}
644
645/*
646 * Convert a string of one of the forms
647 *      hh   hh:mm     hh:mm:ss
648 * into the number of seconds.
649 * exit on error
650*/
651
652static time_t
653convert_hms(string)
654    char *string;
655{
656    int hh = 0;   /* hours */
657    int mm = 0;   /* minutes */
658    int ss = 0;   /* seconds */
659    time_t seconds;
660    time_t timestamp;
661    struct tm *tm;
662    int i;
663
664    if (string == NULL || *string == '\0')
665	goto convert_err;
666
667    for (i=0; string[i] != '\0'; i++)
668    {
669	if ((!isdigit(string[i])) && (string[i] != ':'))
670	{
671	    goto convert_err;
672	}
673    }
674
675    if (sscanf(string, "%d:%d:%d", &hh, &mm, &ss) != 3)
676    {
677	if (sscanf(string, "%d:%d", &hh, &mm) != 2)
678	{
679	    if (sscanf(string, "%d", &hh) != 1)
680	    {
681		goto convert_err;
682	    }
683	}
684    }
685
686    if (hh < 0 || hh >= HOURS_PER_DAY ||
687      mm < 0 || mm >= MINS_PER_HOUR ||
688      ss < 0 || ss > SECS_PER_MIN)
689    {
690	goto convert_err;
691    }
692
693    seconds = ((((hh * MINS_PER_HOUR) + mm) * SECS_PER_MIN) + ss);
694    timestamp = time((time_t *)0);
695    tm=localtime(&timestamp);
696    seconds -= tm->tm_gmtoff;
697
698    return(seconds);
699
700    convert_err:
701    fprintf(stderr, "sar: time format usage is hh[:mm[:ss]]\n");
702    exit_usage();
703    return(0);
704}
705
706
707/*
708 * Use ctime_r to convert a time value into
709 * a 26-character string of the form:
710 *
711 * Thu Nov 24 18:22:48 1986\n\0
712 */
713
714static char *
715get_hms_string(tdata, tbuf)
716    time_t tdata;
717    char *tbuf;
718{
719    time_t t;
720    char *p;
721
722    t = tdata;
723    ctime_r(&t, tbuf);
724    p=&tbuf[11];
725    tbuf[19] = 0;
726
727    return(p);
728}
729
730
731/* sample set flags */
732#define INIT_SET   0
733#define PRINT_SET  1
734#define PRINT_AVG  2
735
736static void
737do_main_workloop()
738{
739    struct record_hdr hdr;
740    time_t cur_timestamp = 0;	/* seconds - Coordinated Universal Time */
741    time_t next_timestamp = 0;  /* seconds - Coordinated Universal Time */
742
743    if (!find_restart_header(&hdr))
744	exit(1);
745
746    cur_timestamp = hdr.rec_timestamp;
747
748    /* convert sflag's start_time from 24 hour clock time to UTC seconds */
749    if (start_time  < (cur_timestamp % SECS_PER_DAY))
750	start_time = cur_timestamp;
751    else
752	start_time += cur_timestamp - (cur_timestamp % SECS_PER_DAY);
753
754    /* convert end_time, from 24 hour clock time to UTC seconds */
755    if (end_time != 0)
756	end_time += cur_timestamp - (cur_timestamp % SECS_PER_DAY);
757
758#if 0
759	fprintf(stderr, "start = %ld, end = %ld, cur=%ld, [24hour - %ld]\n",
760	  start_time, end_time, cur_timestamp,(cur_timestamp % SECS_PER_DAY));
761#endif
762
763    while (cur_timestamp < start_time)
764    {
765	bypass_sample_set(&hdr, cur_timestamp);
766	cur_timestamp = hdr.rec_timestamp;
767    }
768
769    next_timestamp = cur_timestamp + iseconds;
770    print_all_column_headings(cur_timestamp);
771    read_sample_set(INIT_SET, cur_timestamp, &hdr);
772    cur_timestamp = hdr.rec_timestamp;
773
774    while ((end_time == 0) || (next_timestamp < end_time))
775    {
776	if (cur_timestamp < next_timestamp)
777	{
778	    bypass_sample_set (&hdr, cur_timestamp);
779	    cur_timestamp = hdr.rec_timestamp;
780	}
781	else
782	{
783	    /* need to know the seconds interval when printing averages */
784	    if (avg_interval == 0)
785	    {
786		if (iseconds)
787		    avg_interval = iseconds;
788		else
789		    avg_interval = cur_timestamp - next_timestamp;
790	    }
791	    next_timestamp = cur_timestamp + iseconds;
792	    read_sample_set(PRINT_SET, cur_timestamp, &hdr);
793	    cur_timestamp = hdr.rec_timestamp;
794	}
795    }
796    exit_average();
797}
798
799
800/*
801 * Find and fill in a restart header.  We don't write
802 * the binary data when looking for SAR_RESTART.
803 * Return:  1 on success
804 *          0 on failure
805 */
806static int
807find_restart_header (ret_hdr)
808    struct record_hdr *ret_hdr;
809{
810    struct record_hdr hdr;
811    int bufsize = 0;
812    char *buf = NULL;
813
814    errno = 0;
815
816    restart_loop:
817    read_record_hdr(&hdr, FALSE);   /* exits on error */
818
819    if (hdr.rec_type == SAR_RESTART)
820    {
821	*ret_hdr = hdr;
822	if (oflag)
823	    write_record_hdr(&hdr);   /* writes the RESTART record */
824	if (buf)
825	    free(buf);
826	return(1);
827    }
828
829    /*
830     * not the record we want...
831     * read past data and try again
832     */
833    if (hdr.rec_count)
834    {
835	if (fflag)
836	{ /* seek past data in the file */
837	    if ((lseek(ifd, (hdr.rec_count * hdr.rec_size), SEEK_CUR)) == -1)
838	    {
839		/*exit on error */
840		fprintf(stderr, "sar: lseek failed, errno=%d\n", errno);
841		exit(EXIT_FAILURE);
842	    }
843
844	}
845	/* compute data size - malloc a new buf if it's not big enough */
846	else
847	{
848	    /* have to read from the pipe */
849	    if (bufsize < (hdr.rec_count * hdr.rec_size))
850	    {
851		if (buf)
852		    free(buf);
853		bufsize = hdr.rec_count * hdr.rec_size;
854		if((buf = (char *)malloc(bufsize)) == NULL)
855		{
856		    fprintf(stderr, "sar: malloc failed\n");
857		    return(0);
858		}
859	    }
860	    /* exits on error */
861	    read_record_data(buf, (hdr.rec_count * hdr.rec_size), FALSE);
862	}
863    }
864    goto restart_loop;
865}
866
867static void
868print_all_column_headings(timestamp)
869    time_t timestamp;
870{
871    char timebuf[26];
872    char *timebufp;
873
874    timebufp = get_hms_string (timestamp, timebuf);
875
876    if (uflag) /* print cpu headers */
877	print_column_heading(SAR_CPU, timebufp, 0);
878
879    if (gflag) 	/* print page-out activity */
880	print_column_heading(SAR_VMSTAT, timebufp, 0);
881
882    if (pflag ) /* print page-in activity */
883	print_column_heading(SAR_VMSTAT, timebufp, 1);
884
885    if (dflag) /* print drive stats */
886	print_column_heading(SAR_DRIVESTATS, timebufp, 0);
887
888    if (nflag) /* print network stats */
889    {
890	if (network_mode & NET_DEV_MODE)
891	    print_column_heading(SAR_NETSTATS, timebufp, NET_DEV_MODE);
892
893	if (network_mode & NET_EDEV_MODE)
894	    print_column_heading(SAR_NETSTATS, timebufp, NET_EDEV_MODE);
895    }
896}
897
898
899/*
900 * Find and fill in a timestamp header.
901 * Write the binary data when looking for SAR_TIMESTAMP
902 * Don't do anything with the data, just read past it.
903 * Return:  1 on success
904 *          0 on failure
905 */
906static int
907bypass_sample_set (ret_hdr, timestamp)
908    struct record_hdr *ret_hdr;
909    time_t timestamp;
910{
911    struct record_hdr hdr;
912    int bufsize = 0;
913    char *buf = NULL;
914
915    bypass_loop:
916    read_record_hdr(&hdr, TRUE);   /* exits on error */
917
918    if (hdr.rec_type == SAR_TIMESTAMP)
919    {
920	*ret_hdr = hdr;
921	if (buf)
922	    free(buf);
923	return(1);
924    }
925
926    /*
927     * not the record we want...
928     * read past data and try again
929     */
930    if (hdr.rec_count)
931    {
932	if (fflag && !oflag)
933	{
934	    /*
935	     * we're reading from a file and we don't have to write the
936	     * binary data so seek past data in the file
937	     */
938	    errno = 0;
939	    if ((lseek(ifd, (hdr.rec_count * hdr.rec_size), SEEK_CUR)) == -1)
940	    {
941		/*exit on error */
942		fprintf(stderr, "sar: lseek failed, errno=%d\n", errno);
943		exit(EXIT_FAILURE);
944	    }
945	}
946	else
947	{
948	    /*
949	     * We end up here when reading from pipe.
950	     * malloc a new buffer if current is not big enough
951	     */
952	    if (bufsize < (hdr.rec_count * hdr.rec_size))
953	    {
954		if (buf)
955		    free(buf);
956		bufsize = hdr.rec_count * hdr.rec_size;
957		if((buf = (char *)malloc(bufsize)) == NULL)
958		{
959		    fprintf(stderr, "sar: malloc failed\n");
960		    exit(EXIT_FAILURE);
961		}
962	    }
963
964	    /* exits on error */
965	    read_record_data(buf, (hdr.rec_count * hdr.rec_size), TRUE);
966	}
967    } /* end if hdr.rec_count */
968    goto bypass_loop;
969}
970
971
972/*
973 * INIT_SET: This initializes the first sample for each type.
974 * PRINT_SET: This read, compute and print out sample data.
975 */
976static void
977read_sample_set(flag, timestamp, ret_hdr)
978    int flag;
979    time_t timestamp;
980    struct record_hdr *ret_hdr;
981{
982    struct record_hdr hdr;
983    char timebuf[26];
984    char *timebufp;
985    char *indent_string;
986    char *indent_string_wide;
987    char *indent_string_narrow;
988    int sar_cpu = 0;
989    int sar_vmstat=0;
990    int sar_drivestats=0;
991    int sar_drivepath=0;
992    int sar_netstats = 0;
993
994    indent_string_wide = "          ";
995    indent_string_narrow = "  ";
996    indent_string = indent_string_narrow;
997
998    read_record_hdr(&hdr, TRUE);
999
1000    while (hdr.rec_type != SAR_TIMESTAMP)
1001    {
1002	switch (hdr.rec_type)
1003	{
1004	case SAR_CPU:
1005	    sar_cpu = get_cpu_sample(flag, &hdr);
1006	    break;
1007	case SAR_VMSTAT:
1008	    sar_vmstat=get_vmstat_sample(flag, &hdr);
1009	    break;
1010	case SAR_DRIVEPATH:
1011	  sar_drivepath = get_drivepath_sample(flag, &hdr);
1012	  if (sar_drivepath < 0)
1013	      fprintf(stderr, "sar: drivepath sync code error %d\n", sar_drivepath);
1014	  break;
1015	case SAR_DRIVESTATS:
1016	    sar_drivestats = get_drivestats_sample(flag, &hdr);
1017	    break;
1018	case SAR_NETSTATS:
1019	    sar_netstats = get_netstats_sample(flag, &hdr);
1020	    break;
1021	default:
1022	    break;
1023	}
1024
1025	read_record_hdr(&hdr, TRUE);
1026    }
1027
1028    /* return the timestamp header */
1029    *ret_hdr = hdr;
1030
1031    if (flag == PRINT_SET)
1032    {
1033	avg_counter++;
1034	timebufp = get_hms_string(timestamp, timebuf);
1035
1036	if (uflag && sar_cpu)
1037	    print_cpu_sample(timebufp);
1038
1039	if((gflag || pflag) && sar_vmstat)
1040	    print_vmstat_sample(timebufp);
1041
1042	if (dflag && sar_drivestats)
1043	    print_drivestats_sample(timebufp);
1044
1045	if (nflag && sar_netstats)
1046	    print_netstats_sample(timebufp);
1047    }
1048}
1049
1050static void
1051skip_data(bufsize)
1052    int bufsize;
1053{
1054    char *buf = NULL;
1055
1056    if (fflag)
1057    {
1058        /* seek past data in the file */
1059	if ((lseek(ifd, bufsize, SEEK_CUR) == -1))
1060	{
1061	    /*exit on error */
1062	    fprintf(stderr, "sar: lseek failed, errno=%d\n", errno);
1063	    exit(EXIT_FAILURE);
1064	}
1065    }
1066    else
1067    {
1068	/* have to read from the pipe */
1069	if((buf = (char *)malloc(bufsize)) == NULL)
1070	{
1071	    fprintf(stderr, "sar: malloc failed\n");
1072	    exit(EXIT_FAILURE);
1073	}
1074	/* even though we skip this data, we still write it if necessary */
1075	read_record_data(buf, bufsize, TRUE);
1076    }
1077    if (buf)
1078	free(buf);
1079
1080    return;
1081}
1082
1083static int
1084get_cpu_sample(flag, hdr)
1085    int flag;
1086    struct record_hdr *hdr;
1087{
1088    int  datasize;
1089
1090    datasize = hdr->rec_count * hdr->rec_size;
1091
1092    if (datasize != sizeof(host_cpu_load_info_data_t))
1093    {
1094	/* read past the data but don't do anything with it */
1095	skip_data(datasize);
1096	return(0);
1097    }
1098
1099    read_record_data ((char *)&cur_cpuload, (int)sizeof(host_cpu_load_info_data_t), TRUE );
1100
1101    if (flag == INIT_SET)
1102    {
1103	prev_cpuload = cur_cpuload;
1104	bzero(&avg_cpuload, sizeof(avg_cpuload));
1105    }
1106    return(1);
1107}
1108
1109static void
1110print_cpu_sample(timebufptr)
1111    char * timebufptr;
1112{
1113
1114    double time;
1115
1116    time = 0.0;
1117    cur_cpuload.cpu_ticks[CPU_STATE_USER]
1118      -= prev_cpuload.cpu_ticks[CPU_STATE_USER];
1119
1120    prev_cpuload.cpu_ticks[CPU_STATE_USER]
1121      += cur_cpuload.cpu_ticks[CPU_STATE_USER];
1122
1123    time += cur_cpuload.cpu_ticks[CPU_STATE_USER];
1124
1125    cur_cpuload.cpu_ticks[CPU_STATE_NICE]
1126      -= prev_cpuload.cpu_ticks[CPU_STATE_NICE];
1127
1128    prev_cpuload.cpu_ticks[CPU_STATE_NICE]
1129      += cur_cpuload.cpu_ticks[CPU_STATE_NICE];
1130
1131    time += cur_cpuload.cpu_ticks[CPU_STATE_NICE];
1132
1133    cur_cpuload.cpu_ticks[CPU_STATE_SYSTEM]
1134      -= prev_cpuload.cpu_ticks[CPU_STATE_SYSTEM];
1135
1136    prev_cpuload.cpu_ticks[CPU_STATE_SYSTEM]
1137      += cur_cpuload.cpu_ticks[CPU_STATE_SYSTEM];
1138
1139    time += cur_cpuload.cpu_ticks[CPU_STATE_SYSTEM];
1140
1141    cur_cpuload.cpu_ticks[CPU_STATE_IDLE]
1142      -= prev_cpuload.cpu_ticks[CPU_STATE_IDLE];
1143
1144    prev_cpuload.cpu_ticks[CPU_STATE_IDLE]
1145      += cur_cpuload.cpu_ticks[CPU_STATE_IDLE];
1146
1147    time += cur_cpuload.cpu_ticks[CPU_STATE_IDLE];
1148
1149    avg_cpuload.cpu_ticks[CPU_STATE_USER] += rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_USER]
1150      / (time ? time : 1));
1151
1152    avg_cpuload.cpu_ticks[CPU_STATE_NICE] += rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_NICE]
1153      / (time ? time : 1));
1154
1155    avg_cpuload.cpu_ticks[CPU_STATE_SYSTEM] += rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_SYSTEM]
1156      / (time ? time : 1));
1157
1158    avg_cpuload.cpu_ticks[CPU_STATE_IDLE] += rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_IDLE]
1159      / (time ? time : 1));
1160
1161    if(flag_count > 1)
1162	print_column_heading(SAR_CPU, timebufptr, 0);
1163
1164    fprintf(stdout, "%s%5.0f   ", timebufptr,
1165      rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_USER]
1166      / (time ? time : 1)));
1167
1168    fprintf(stdout, "%4.0f   ",
1169      rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_NICE]
1170      / (time ? time : 1)));
1171
1172    fprintf(stdout, "%4.0f   ",
1173      rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_SYSTEM]
1174      / (time ? time : 1)));
1175
1176    fprintf(stdout, "%4.0f\n",
1177      rint(100. * cur_cpuload.cpu_ticks[CPU_STATE_IDLE]
1178      / (time ? time : 1)));
1179}
1180
1181static int
1182get_vmstat_sample(flag, hdr)
1183    int flag;
1184    struct record_hdr *hdr;
1185{
1186    int  datasize;
1187
1188    datasize = hdr->rec_count * hdr->rec_size;
1189
1190    if (datasize != sizeof(struct vm_statistics))
1191    {
1192	/* read past the data but don't do anything with it */
1193	skip_data(datasize);
1194	return(0);
1195    }
1196
1197    read_record_data ((char *)&cur_vmstat, (int)sizeof(struct vm_statistics), TRUE );
1198
1199    if (flag == INIT_SET)
1200    {
1201	prev_vmstat = cur_vmstat;
1202	bzero(&avg_vmstat, sizeof(avg_vmstat));
1203    }
1204    return(1);
1205}
1206
1207
1208static void
1209print_vmstat_sample(char *timebufptr)
1210{
1211
1212    cur_vmstat.faults -= prev_vmstat.faults;
1213    prev_vmstat.faults += cur_vmstat.faults;
1214    avg_vmstat.faults += cur_vmstat.faults;
1215
1216    cur_vmstat.cow_faults -= prev_vmstat.cow_faults;
1217    prev_vmstat.cow_faults += cur_vmstat.cow_faults;
1218    avg_vmstat.cow_faults += cur_vmstat.cow_faults;
1219
1220    cur_vmstat.zero_fill_count -= prev_vmstat.zero_fill_count;
1221    prev_vmstat.zero_fill_count += cur_vmstat.zero_fill_count;
1222    avg_vmstat.zero_fill_count += cur_vmstat.zero_fill_count;
1223
1224    cur_vmstat.reactivations -= prev_vmstat.reactivations;
1225    prev_vmstat.reactivations += cur_vmstat.reactivations;
1226    avg_vmstat.reactivations += cur_vmstat.reactivations;
1227
1228    cur_vmstat.pageins -= prev_vmstat.pageins;
1229    prev_vmstat.pageins += cur_vmstat.pageins;
1230    avg_vmstat.pageins += cur_vmstat.pageins;
1231
1232    cur_vmstat.pageouts -= prev_vmstat.pageouts;
1233    prev_vmstat.pageouts += cur_vmstat.pageouts;
1234    avg_vmstat.pageouts += cur_vmstat.pageouts;
1235
1236
1237    if (gflag)
1238    {
1239	if (flag_count > 1)
1240	    print_column_heading(SAR_VMSTAT, timebufptr, 0);
1241	fprintf(stdout, "%s   %8.1f   \n", timebufptr, (float)((float)cur_vmstat.pageouts/avg_interval));
1242    }
1243
1244    if (pflag)
1245    {
1246	if (flag_count > 1)
1247	    print_column_heading(SAR_VMSTAT, timebufptr, 1);
1248	fprintf(stdout, "%s   %8.1f      %8.1f      %8.1f\n", timebufptr,
1249	  (float)((float)cur_vmstat.pageins / avg_interval),
1250	  (float)((float)cur_vmstat.cow_faults/avg_interval),
1251	  (float)((float)cur_vmstat.faults/avg_interval));
1252    }
1253    fflush(stdout);
1254}
1255
1256static int
1257get_drivestats_sample(flag, hdr)
1258    int flag;
1259    struct record_hdr *hdr;
1260{
1261    struct drivestats *databuf;
1262    struct drivestats_report *dr;
1263    size_t  datasize;
1264    int     datacount;
1265    int     index;
1266    int     i;
1267
1268    datasize = hdr->rec_count * hdr->rec_size;
1269    datacount = hdr->rec_count;
1270
1271    if (hdr->rec_size != sizeof(struct drivestats))
1272    {
1273	/* something isn't right... read past the data but don't analyze it */
1274	skip_data(datasize);
1275	return(0);
1276    }
1277
1278    /* malloc read buffer */
1279    if ((databuf = (struct drivestats *)malloc(datasize)) == NULL)
1280    {
1281	fprintf(stderr, "sar: malloc failed\n");
1282	exit (EXIT_FAILURE);
1283    }
1284
1285    bzero(databuf, datasize);
1286
1287    read_record_data ((char *)databuf, datasize, TRUE );
1288
1289    /* clear all global current fields */
1290    for(dr = dr_head; dr; dr=(struct drivestats_report *)dr->next)
1291    {
1292	dr->present = 0;
1293	dr->cur_Reads = 0;
1294	dr->cur_BytesRead = 0;
1295	dr->cur_Writes = 0;
1296	dr->cur_BytesWritten = 0;
1297	dr->cur_LatentReadTime = 0;
1298	dr->cur_LatentWriteTime = 0;
1299	dr->cur_ReadErrors = 0;
1300	dr->cur_WriteErrors = 0;
1301	dr->cur_ReadRetries = 0;
1302	dr->cur_WriteRetries = 0;
1303	dr->cur_TotalReadTime = 0;
1304	dr->cur_TotalWriteTime=0;
1305    }
1306
1307    /* By this point, we have read in a complete set of diskstats from the sadc
1308     * data collector.
1309     * The order of the drives in not guaranteed.
1310     * The global report structure is a linked list, but may need initialization
1311     * We need to traverse this list  and transfer the current
1312     * read data.  If a disk entry isn't found, then we need to allocate one
1313     * initilize it.
1314    */
1315    for (i=0; i< datacount; i++)
1316    {
1317	struct drivestats_report *dr_last = NULL;
1318
1319	index = databuf[i].drivepath_id;   /* use this as index into dp_table */
1320
1321	/* find disk entry or allocate new one*/
1322	for(dr = dr_head; dr; dr=(struct drivestats_report *)dr->next)
1323	{
1324	    dr_last = dr;
1325	    if(index == dr->drivepath_id)
1326		break;
1327	}
1328
1329	if (dr == NULL)
1330	{
1331	    /* allocate new entry */
1332	    if((dr = (struct drivestats_report *)malloc(sizeof(struct drivestats_report))) == NULL)
1333	    {
1334		fprintf(stderr, "sar: malloc error\n");
1335		exit(EXIT_FAILURE);
1336	    }
1337	    bzero((char *)dr, sizeof(struct drivestats_report));
1338	    dr->blocksize = databuf[i].blocksize;
1339	    dr->drivepath_id = index;
1340	    dr->next = NULL;
1341	    dr->avg_count = 0;
1342
1343	    /* get the BSDName which should be in the table by now */
1344	    if ((index < dp_count) && (dp_table[index].state != DPSTATE_UNINITIALIZED))
1345		strncpy(dr->name, dp_table[index].BSDName, MAXDRIVENAME+1);
1346	    else
1347		strcpy(dr->name, "disk??");
1348
1349	    if (dr_head == NULL)
1350	    {
1351		dr_head = dr;
1352		dr_head->next = NULL;
1353	    }
1354	    else
1355	    {
1356		dr_last->next = (char *)dr;
1357	    }
1358	} /* end if dr == NULL */
1359
1360	dr->present = TRUE;
1361	dr->cur_Reads = databuf[i].Reads;
1362	dr->cur_BytesRead = databuf[i].BytesRead;
1363	dr->cur_Writes = databuf[i].Writes;
1364	dr->cur_BytesWritten = databuf[i].BytesWritten;
1365	dr->cur_LatentReadTime = databuf[i].LatentReadTime;
1366	dr->cur_LatentWriteTime = databuf[i].LatentWriteTime;
1367	dr->cur_ReadErrors = databuf[i].ReadErrors;
1368	dr->cur_WriteErrors = databuf[i].WriteErrors;
1369	dr->cur_ReadRetries = databuf[i].ReadRetries;
1370	dr->cur_WriteRetries = databuf[i].WriteRetries;
1371	dr->cur_TotalReadTime = databuf[i].TotalReadTime;
1372	dr->cur_TotalWriteTime=databuf[i].TotalWriteTime;
1373    } /* end for loop */
1374
1375    /* Reinitialize the prev and avg fields when
1376     * This is a new disk
1377     * This is a changed disk - name change implies disk swapping
1378     * This disk is not present in this sample
1379     */
1380    for(dr = dr_head; dr; dr=(struct drivestats_report *)dr->next)
1381    {
1382	if (dr->drivepath_id >= dp_count)
1383	{
1384	    /* something is amiss */
1385	    continue;
1386	}
1387	else
1388	{
1389	    index = dr->drivepath_id;   /* use this as index into dp_table */
1390	}
1391
1392	if ((flag == INIT_SET) ||
1393	  (dp_table[index].state == DPSTATE_NEW) ||
1394	  (dp_table[index].state == DPSTATE_CHANGED) ||
1395	  (!dr->present))
1396	{
1397	    /*
1398	     * prev will be set to cur
1399	     * activate the state in dp_table
1400	     */
1401	    if (dr->present)
1402		dp_table[index].state = DPSTATE_ACTIVE;
1403
1404	    init_drivestats(dr);
1405	}
1406    }
1407    return(1);
1408}
1409
1410static void
1411init_drivestats(struct drivestats_report *dr)
1412{
1413    dr->avg_count = 0;
1414    dr->prev_Reads = dr->cur_Reads;
1415    dr->avg_Reads = 0;
1416    dr->prev_BytesRead = dr->cur_BytesRead;
1417    dr->avg_BytesRead = 0;
1418    dr->prev_Writes = dr->cur_Writes;
1419    dr->avg_Writes = 0;
1420    dr->prev_BytesWritten = dr->cur_BytesWritten;
1421    dr->avg_BytesWritten = 0;
1422    dr->prev_LatentReadTime = dr->cur_LatentReadTime;
1423    dr->avg_LatentReadTime = 0;
1424    dr->prev_LatentWriteTime = dr->cur_LatentWriteTime ;
1425    dr->avg_LatentWriteTime = 0;
1426    dr->prev_ReadErrors = dr->cur_ReadErrors ;
1427    dr->avg_ReadErrors = 0;
1428    dr->prev_WriteErrors = dr->cur_WriteErrors ;
1429    dr->avg_WriteErrors = 0;
1430    dr->prev_ReadRetries = dr->cur_ReadRetries ;
1431    dr->avg_ReadRetries = 0;
1432    dr->prev_WriteRetries = dr->cur_WriteRetries ;
1433    dr->avg_WriteRetries = 0;
1434    dr->prev_TotalReadTime = dr->cur_TotalReadTime ;
1435    dr->avg_TotalReadTime = 0;
1436    dr->prev_TotalWriteTime = dr->cur_TotalWriteTime ;
1437    dr->avg_TotalWriteTime = 0;
1438}
1439
1440
1441static void
1442print_drivestats_sample(char *timebufptr)
1443{
1444    struct drivestats_report *dr;
1445    long double transfers_per_second;
1446    long double kb_per_transfer, mb_per_second;
1447    u_int64_t interval_bytes, interval_transfers, interval_blocks;
1448    u_int64_t interval_time;
1449    long double blocks_per_second, ms_per_transaction;
1450
1451    if (flag_count > 1)
1452	print_column_heading(SAR_DRIVESTATS, timebufptr, 0);
1453
1454    for (dr=dr_head; dr; dr=(struct drivestats_report *)dr->next)
1455    {
1456	if(!dr->present)
1457	    continue;
1458
1459	/*
1460	 * This sanity check is for drives that get removed and then
1461	 * returned during the sampling sleep interval.  If anything
1462	 * looks out of sync, reinit and skip this entry.  There is
1463	 * no way to guard against this entirely.
1464	 */
1465	if ((dr->cur_Reads < dr->prev_Reads) ||
1466	  (dr->cur_BytesRead < dr->prev_BytesRead) ||
1467	  (dr->cur_Writes < dr->prev_Writes) ||
1468	  (dr->cur_BytesWritten < dr->prev_BytesWritten))
1469	{
1470	    init_drivestats(dr);
1471	    continue;
1472	}
1473
1474	dr->avg_count++;
1475
1476	dr->cur_Reads -= dr->prev_Reads;
1477	dr->prev_Reads += dr->cur_Reads;
1478	dr->avg_Reads += dr->cur_Reads;
1479
1480        dr->cur_BytesRead -= dr->prev_BytesRead;
1481	dr->prev_BytesRead += dr->cur_BytesRead;
1482	dr->avg_BytesRead += dr->cur_BytesRead;
1483
1484	dr->cur_Writes -= dr->prev_Writes ;
1485	dr->prev_Writes += dr->cur_Writes ;
1486	dr->avg_Writes += dr->cur_Writes ;
1487
1488	dr->cur_BytesWritten -= dr->prev_BytesWritten ;
1489	dr->prev_BytesWritten += dr->cur_BytesWritten ;
1490	dr->avg_BytesWritten += dr->cur_BytesWritten ;
1491
1492	dr->cur_LatentReadTime -= dr->prev_LatentReadTime ;
1493	dr->prev_LatentReadTime += dr->cur_LatentReadTime ;
1494	dr->avg_LatentReadTime += dr->cur_LatentReadTime ;
1495
1496	dr->cur_LatentWriteTime -= dr->prev_LatentWriteTime ;
1497	dr->prev_LatentWriteTime += dr->cur_LatentWriteTime ;
1498	dr->avg_LatentWriteTime += dr->cur_LatentWriteTime ;
1499
1500	dr->cur_ReadErrors -= dr->prev_ReadErrors ;
1501	dr->prev_ReadErrors += dr->cur_ReadErrors ;
1502	dr->avg_ReadErrors += dr->cur_ReadErrors ;
1503
1504	dr->cur_WriteErrors -= dr->prev_WriteErrors ;
1505	dr->prev_WriteErrors += dr->cur_WriteErrors ;
1506	dr->avg_WriteErrors += dr->cur_WriteErrors ;
1507
1508	dr->cur_ReadRetries -= dr->prev_ReadRetries ;
1509	dr->prev_ReadRetries += dr->cur_ReadRetries ;
1510	dr->avg_ReadRetries += dr->cur_ReadRetries ;
1511
1512	dr->cur_WriteRetries -= dr->prev_WriteRetries ;
1513	dr->prev_WriteRetries += dr->cur_WriteRetries;
1514	dr->avg_WriteRetries += dr->cur_WriteRetries;
1515
1516	dr->cur_TotalReadTime -= dr->prev_TotalReadTime ;
1517	dr->prev_TotalReadTime += dr->cur_TotalReadTime ;
1518	dr->avg_TotalReadTime += dr->cur_TotalReadTime ;
1519
1520	dr->cur_TotalWriteTime -= dr->prev_TotalWriteTime ;
1521	dr->prev_TotalWriteTime += dr->cur_TotalWriteTime ;
1522	dr->avg_TotalWriteTime += dr->cur_TotalWriteTime ;
1523
1524	/* I/O volume */
1525	interval_bytes = dr->cur_BytesRead + dr->cur_BytesWritten;
1526
1527	/* I/O counts */
1528	interval_transfers = dr->cur_Reads + dr->cur_Writes;
1529
1530	/* I/O time */
1531	interval_time = dr->cur_LatentReadTime + dr->cur_LatentWriteTime;
1532
1533	interval_blocks = interval_bytes / dr->blocksize;
1534	blocks_per_second = interval_blocks / avg_interval;
1535	transfers_per_second = interval_transfers / avg_interval;
1536	mb_per_second = (interval_bytes / avg_interval) / (1024 *1024);
1537
1538	kb_per_transfer = (interval_transfers > 0) ?
1539	  ((long double)interval_bytes / interval_transfers)
1540	  / 1024 : 0;
1541
1542	/* times are in nanoseconds, convert to milliseconds */
1543	ms_per_transaction = (interval_transfers > 0) ?
1544	  ((long double)interval_time / interval_transfers)
1545	  / 1000 : 0;
1546
1547	/* print device name */
1548	fprintf(stdout, "%s   %-10s", timebufptr, dr->name);
1549
1550	/* print transfers per second */
1551	fprintf(stdout, "%4.0Lf       ", transfers_per_second);
1552
1553	/* print blocks per second - in device blocksize */
1554	fprintf(stdout, "%4.0Lf\n", blocks_per_second);
1555    }
1556}
1557
1558/*
1559 * Print averages before exiting.
1560 */
1561static void
1562exit_average()
1563{
1564    int i;
1565
1566    if (avg_counter <= 0 )
1567	exit(0);
1568
1569    if (oflag)
1570      {
1571	if (ofd)
1572	  close (ofd);
1573	ofd = 0;
1574      }
1575
1576    if (uflag) /* print cpu averages */
1577    {
1578	if(flag_count > 1)
1579	    print_column_heading(SAR_CPU, 0, 0);
1580
1581        fprintf(stdout, "Average:  %5d   ",
1582	   (int)avg_cpuload.cpu_ticks[CPU_STATE_USER]
1583	  / (avg_counter ? avg_counter : 1));
1584
1585	fprintf(stdout, "%4d   ",
1586	   (int)avg_cpuload.cpu_ticks[CPU_STATE_NICE]
1587	  / (avg_counter ? avg_counter : 1));
1588
1589	fprintf(stdout, "%4d   ",
1590	   (int)avg_cpuload.cpu_ticks[CPU_STATE_SYSTEM]
1591	  / (avg_counter ? avg_counter : 1));
1592
1593	fprintf(stdout, "%4d   \n",
1594	   (int)avg_cpuload.cpu_ticks[CPU_STATE_IDLE]
1595	  / (avg_counter ? avg_counter : 1));
1596
1597	fflush(stdout);
1598    }
1599
1600
1601    if (gflag) /* print page-out averages */
1602    {
1603	if (flag_count > 1)
1604	    print_column_heading(SAR_VMSTAT, 0, 0);
1605
1606	fprintf(stdout, "Average:   %8.1f\n",
1607	(float)((avg_vmstat.pageouts / (avg_counter ? avg_counter : 1)) / avg_interval));
1608	fflush(stdout);
1609    }
1610
1611    if (pflag) /* print page-in averages */
1612    {
1613	if (flag_count > 1)
1614	    print_column_heading(SAR_VMSTAT, 0, 1);
1615
1616	fprintf(stdout, "Average:   %8.1f      %8.1f      %8.1f\n",
1617	  (float)(((float)avg_vmstat.pageins / (avg_counter ? avg_counter : 1)) / avg_interval),
1618	  (float)(((float)avg_vmstat.cow_faults / (avg_counter ? avg_counter : 1)) / avg_interval),
1619	  (float)(((float)avg_vmstat.faults / (avg_counter ? avg_counter : 1)) / avg_interval));
1620	fflush(stdout);
1621    }
1622
1623    if (dflag) /* print drivestats averages */
1624    {
1625	struct drivestats_report *dr;
1626	long double transfers_per_second;
1627	long double kb_per_transfer, mb_per_second;
1628	u_int64_t total_bytes, total_transfers, total_blocks;
1629	u_int64_t total_time;
1630	long double blocks_per_second, ms_per_transaction;
1631	int msdig;
1632
1633	if (flag_count > 1)
1634	    print_column_heading(SAR_DRIVESTATS, 0, 0);
1635
1636	for (dr=dr_head; dr; dr=(struct drivestats_report *)dr->next)
1637	{
1638	    /* don't bother to print out averages for disks that were removed */
1639	    if (!dr->present)
1640		continue;
1641
1642	    fprintf(stdout, "           %s    %s\n",
1643	      dp_table[dr->drivepath_id].BSDName, dp_table[dr->drivepath_id].ioreg_path);
1644
1645	    /* I/O volume */
1646	    total_bytes = dr->avg_BytesRead + dr->avg_BytesWritten;
1647
1648	    /* I/O counts */
1649	    total_transfers = dr->avg_Reads + dr->avg_Writes;
1650
1651	    /* I/O time */
1652	    total_time = dr->avg_LatentReadTime + dr->avg_LatentWriteTime;
1653
1654	    total_blocks = total_bytes / dr->blocksize;
1655	    blocks_per_second = total_blocks / avg_interval;
1656	    transfers_per_second = total_transfers / avg_interval;
1657	    mb_per_second = (total_bytes / avg_interval) / (1024 *1024);
1658
1659	    kb_per_transfer = (total_transfers > 0) ?
1660	      ((long double)total_bytes / total_transfers)
1661	      / 1024 : 0;
1662
1663	    /* times are in nanoseconds, convert to milliseconds */
1664	    ms_per_transaction = (total_transfers > 0) ?
1665	      ((long double)total_time / total_transfers)
1666	      / 1000 : 0;
1667	    msdig = (ms_per_transaction < 100.0) ? 1 : 0;
1668	    fprintf(stdout, "Average:   %-10s %4.0Lf      %4.0Lf\n",
1669	      dr->name,
1670	      (transfers_per_second / dr->avg_count),
1671	      (blocks_per_second / dr->avg_count));
1672
1673	    fflush(stdout);
1674	}
1675    } /* end if dflag */
1676
1677    if (nflag)
1678    {
1679	int avg_count;
1680
1681	if (network_mode & NET_DEV_MODE)
1682	{
1683	    if (flag_count > 1)
1684		print_column_heading(SAR_NETSTATS, 0, NET_DEV_MODE);
1685	    for (i = 0; i < nr_count; i++)
1686	    {
1687		if (!nr_table[i].valid)
1688		    continue;
1689
1690		if(nr_table[i].avg_count == 0)
1691		    avg_count = 1;
1692		else
1693		    avg_count = nr_table[i].avg_count;
1694
1695		fprintf(stdout, "Average:   %-8.8s", nr_table[i].tname_unit);
1696
1697		fprintf (stdout, "%8llu    ",
1698		  ((nr_table[i].avg_ipackets / avg_count) / avg_interval));
1699
1700		fprintf (stdout, "%10llu    ",
1701		  ((nr_table[i].avg_ibytes / avg_count) / avg_interval));
1702
1703		fprintf (stdout, "%8llu    ",
1704		  ((nr_table[i].avg_opackets / avg_count) / avg_interval));
1705
1706		fprintf (stdout, "%10llu\n",
1707		  ((nr_table[i].avg_obytes / avg_count) / avg_interval));
1708
1709		fflush(stdout);
1710	    }
1711	}
1712
1713	if (network_mode & NET_EDEV_MODE)
1714	{
1715
1716	    if(flag_count > 1)
1717		print_column_heading(SAR_NETSTATS, 0, NET_EDEV_MODE);
1718
1719	    for (i = 0; i < nr_count; i++)
1720	    {
1721		if (!nr_table[i].valid)
1722		    continue;
1723
1724		if(nr_table[i].avg_count == 0)
1725		    avg_count = 1;
1726		else
1727		    avg_count = nr_table[i].avg_count;
1728
1729		fprintf(stdout, "Average:   %-8.8s  ", nr_table[i].tname_unit);
1730
1731		fprintf (stdout, "%7llu    ",
1732		  ((nr_table[i].avg_ierrors / avg_count) / avg_interval));
1733
1734		fprintf (stdout, "%7llu    ",
1735		  ((nr_table[i].avg_oerrors / avg_count) / avg_interval));
1736
1737		fprintf (stdout, "%5llu    ",
1738		  ((nr_table[i].avg_collisions / avg_count) / avg_interval));
1739
1740		fprintf (stdout, "   %5llu\n",
1741		  ((nr_table[i].avg_drops / avg_count) / avg_interval));
1742
1743		fflush(stdout);
1744	    }
1745	}
1746
1747    } /* end if nflag */
1748    exit(0);
1749}
1750
1751
1752/*
1753 * Return < 0 failure, debugging purposes only
1754 * Return = 0 data skipped
1755 * Return > 0 success
1756 */
1757
1758static int
1759get_drivepath_sample(flag, hdr)
1760    int flag;
1761    struct record_hdr *hdr;
1762{
1763    size_t datasize;
1764    struct drivepath dp;
1765    struct drivestats_report *dr;
1766    int i, n;
1767
1768    datasize = hdr->rec_count * hdr->rec_size;
1769
1770    if (datasize != sizeof(struct drivepath))
1771    {
1772	/* read past the data but don't do anything with it */
1773	skip_data(datasize);
1774	return(0);
1775    }
1776
1777    read_record_data ((char *)&dp, (int)sizeof(struct drivepath), TRUE );
1778
1779    /*
1780     * If state is new -- put a new entry in the dp_table.
1781     * If state is changed -- traverse the drivestats_report table
1782     * and copy new name.
1783     */
1784    if (dp.state == DPSTATE_NEW)
1785    {
1786
1787	if (dp_table == NULL)
1788	{
1789	    if (dp.drivepath_id != 0)
1790		return(-1);
1791	    /* First setup of internal drivepath table */
1792	    dp_table = (struct drivepath *)malloc(sizeof(struct drivepath));
1793	    if (dp_table == NULL)
1794		return(-2);
1795	    dp_count = 1;
1796	}
1797
1798	if (dflag)
1799	    fprintf(stdout, "New Disk: [%s] %s\n", dp.BSDName, dp.ioreg_path);
1800
1801	/* traverse table and find next uninitialized entry */
1802	for (i = 0; i< dp_count; i++)
1803	{
1804	    if (dp_table[i].state == DPSTATE_UNINITIALIZED)
1805	    {
1806		if (dp.drivepath_id != i)
1807		{
1808		    /* the table is out of sync - this should not happen */
1809		    return (-3);
1810		}
1811		dp_table[i] = dp;
1812		return(1);
1813	    }
1814	}
1815	/*
1816	 * If we get here, we've run out of table entries.
1817	 * Double the size of the table, then assign the next entry.
1818	 */
1819	if (dp.drivepath_id != i)
1820	{
1821	    /* the table is out of sync - this should not happen */
1822	    return (-4);
1823	}
1824	n = dp_count * 2;
1825	dp_table = (struct drivepath *)realloc(dp_table, n * sizeof(struct drivepath));
1826	bzero(&dp_table[dp_count], dp_count * sizeof(struct drivepath));
1827	dp_table[dp_count] = dp;
1828	dp_count = n;
1829	return(1);
1830
1831    }
1832    else if (dp.state == DPSTATE_CHANGED)
1833    {
1834
1835	  /* Update the name in the table */
1836	if ((dp.drivepath_id < dp_count) && (dp_table[dp.drivepath_id].state != DPSTATE_UNINITIALIZED))
1837	{
1838	    if (strcmp(dp_table[dp.drivepath_id].ioreg_path, dp.ioreg_path) != 0)
1839	    {
1840		/* something is amiss */
1841		return (-5);
1842	    }
1843	    else
1844	    {
1845		if (dflag)
1846		{
1847		    fprintf(stdout, "Change: [%s] %s\n", dp.BSDName,
1848		      dp_table[dp.drivepath_id].ioreg_path);
1849		}
1850		strcpy(dp_table[dp.drivepath_id].BSDName, dp.BSDName);
1851
1852		for(dr = dr_head; dr; dr=(struct drivestats_report *)dr->next)
1853		  {
1854		    if (dr->drivepath_id == dp.drivepath_id)
1855		      strcpy(dr->name, dp.BSDName);
1856		  }
1857		return(1);
1858	    }
1859	}
1860	else
1861	    return(-6);
1862    }
1863    return(-7);
1864}
1865
1866/*
1867 * Bytes and packet counts are used to track
1868 * counter wraps.  So, don't enforce the
1869 * NET_DEV_MODE or NET_EDEV_MODE  in here.
1870 * Maintain all the stats.
1871 */
1872static void
1873set_cur_netstats(struct netstats_report *nr, struct netstats *ns)
1874{
1875
1876    nr->cur_ipackets   = ns->net_ipackets;
1877    nr->cur_ibytes     = ns->net_ibytes;
1878    nr->cur_opackets   = ns->net_opackets;
1879    nr->cur_obytes     = ns->net_obytes;
1880
1881    nr->cur_ierrors    = ns->net_ierrors;
1882    nr->cur_oerrors    = ns->net_oerrors;
1883    nr->cur_collisions = ns->net_collisions;
1884    nr->cur_drops      = ns->net_drops;
1885
1886    nr->cur_imcasts    = ns->net_imcasts;
1887    nr->cur_omcasts    = ns->net_omcasts;
1888
1889}
1890
1891static void
1892init_prev_netstats(struct netstats_report *nr)
1893{
1894    nr->avg_count = 0;
1895    nr->valid = 1;
1896    nr->present = 1;
1897
1898    nr->prev_ipackets = nr->cur_ipackets;
1899    nr->avg_ipackets  = 0;
1900    nr->prev_ibytes   = nr->cur_ibytes;
1901    nr->avg_ibytes    = 0;
1902    nr->prev_opackets = nr->cur_opackets;
1903    nr->avg_opackets  = 0;
1904    nr->prev_obytes   = nr->cur_obytes;
1905    nr->avg_obytes    = 0;
1906
1907    nr->prev_ierrors  = nr->cur_ierrors;
1908    nr->avg_ierrors   = 0;
1909    nr->prev_oerrors  = nr->cur_oerrors ;
1910    nr->avg_oerrors   = 0;
1911    nr->prev_collisions = nr->cur_collisions ;
1912    nr->avg_collisions  = 0;
1913    nr->prev_drops  = nr->cur_drops ;
1914    nr->avg_drops   = 0;
1915
1916    /* track these, but never displayed */
1917    nr->prev_imcasts  = nr->cur_imcasts;
1918    nr->avg_imcasts = 0;
1919    nr->prev_omcasts  = nr->cur_omcasts;
1920    nr->avg_omcasts = 0;
1921}
1922
1923/*
1924 * Success : 1
1925 * Failure : 0
1926 */
1927static int
1928get_netstats_sample(flag, hdr)
1929    int flag;
1930    struct record_hdr *hdr;
1931{
1932    struct netstats *databuf = NULL;
1933    size_t datasize;
1934    int    datacount;
1935    int    i, j;
1936
1937    datasize = hdr->rec_count * hdr->rec_size;
1938    datacount = hdr->rec_count;
1939
1940    if (hdr->rec_size != sizeof(struct netstats))
1941    {
1942	/* something isn't right... read past the data but don't analyze it */
1943	skip_data(datasize);
1944	return(0);
1945    }
1946
1947    /* malloc new or bigger read buffer */
1948    if((netstat_readbuf == NULL) || (netstat_readbuf_size < datasize))
1949    {
1950	if (netstat_readbuf)
1951	    free (netstat_readbuf);
1952
1953	if ((netstat_readbuf = (struct netstats *)malloc(datasize)) == NULL)
1954	{
1955	    fprintf(stderr, "sar: malloc failed\n");
1956	    exit (EXIT_FAILURE);
1957	}
1958	netstat_readbuf_size = datasize;
1959    }
1960
1961    bzero(netstat_readbuf, netstat_readbuf_size);
1962    databuf = netstat_readbuf;
1963
1964    read_record_data ((char *)databuf, datasize, TRUE );
1965
1966    if (nr_table == NULL)
1967    {
1968	/* initial internal table setup */
1969	nr_table = (struct netstats_report *)malloc(datacount * sizeof(struct netstats_report));
1970	nr_count = datacount;
1971	bzero(nr_table, (datacount * sizeof(struct netstats_report)));
1972
1973	/* on first init, this is faster than finding our way to NEW_ENTRY */
1974	for (i = 0; i < datacount; i++)
1975	{
1976	    if (!(network_mode & NET_PPP_MODE))
1977	    {
1978		if (!strncmp(databuf[i].tname_unit, "ppp", 3))
1979		    continue;   /*
1980				 * Skip ppp interfaces.
1981				 * ie don't even put them in this internal table.
1982				 */
1983	    }
1984	    strncpy(nr_table[i].tname_unit, databuf[i].tname_unit, MAX_TNAME_UNIT_SIZE);
1985	    nr_table[i].tname_unit[MAX_TNAME_UNIT_SIZE] = '\0';
1986	    set_cur_netstats(&nr_table[i], &databuf[i]);
1987	    init_prev_netstats(&nr_table[i]);
1988	}
1989	return(1);
1990    }
1991
1992    /*
1993     * clear all the present flags.
1994     * As we traverse the current sample set
1995     * and update the internal table, the flag
1996     * is reset.
1997     */
1998    for (i = 0; i < nr_count; i++)
1999    {
2000	nr_table[i].present = 0;
2001    }
2002
2003    /*
2004     * Find and update table entries.
2005     * Init new entries.
2006     */
2007    for (i=0; i<datacount; i++)
2008    {
2009	int found;
2010	char *name;
2011	int nr_index;
2012	int n;
2013
2014	name = databuf[i].tname_unit;
2015	found = 0;
2016
2017	if (!(network_mode & NET_PPP_MODE))
2018	{
2019	    if (!strncmp(name, "ppp", 3))
2020		continue;   /* skip ppp interfaces */
2021	}
2022
2023	/* Find the matching entry using the interface name */
2024	for (j=0; j < nr_count && !found; j++)
2025	{
2026	    if (nr_table[j].valid)
2027	    {
2028		if(!strcmp(nr_table[j].tname_unit, name))
2029		{
2030		    found = 1;
2031		    nr_table[j].present = 1;
2032		    set_cur_netstats(&nr_table[j], &databuf[i]);
2033		}
2034	    }
2035	} /* end for */
2036
2037	if (!found)  /* this is a new entry */
2038	{
2039	    /* Find an invalid entry in the table and init it */
2040	    for (j=0; j < nr_count; j++)
2041	    {
2042		if (!nr_table[j].valid)
2043		{
2044		    nr_index = j;
2045		    goto NEW_ENTRY;
2046		}
2047	    }
2048
2049	    /* we ran out of entries... grow the table */
2050	    n = nr_count * 2;
2051	    nr_table = (struct netstats_report *)realloc(nr_table, n * sizeof(struct netstats_report));
2052	    bzero(&nr_table[nr_count], nr_count * sizeof (struct netstats_report));
2053	    nr_index = nr_count;
2054	    nr_count = n;
2055
2056	    NEW_ENTRY:
2057	    strncpy(nr_table[nr_index].tname_unit, databuf[i].tname_unit, MAX_TNAME_UNIT_SIZE);
2058	    nr_table[nr_index].tname_unit[MAX_TNAME_UNIT_SIZE] = '\0';
2059	    set_cur_netstats(&nr_table[nr_index], &databuf[i]);
2060	    init_prev_netstats(&nr_table[nr_index]);
2061	}
2062
2063    } /* end for */
2064
2065    /*
2066     * Traverse the internal table.  Any valid entry that wasn't
2067     * present in this sample is cleared for reuse.
2068     */
2069    for (i = 0; i < nr_count; i++)
2070    {
2071	if (nr_table[i].valid)
2072	{
2073	    if (nr_table[i].present == 0)
2074		bzero(&nr_table[i], sizeof(struct netstats_report));
2075	}
2076    }
2077    return (1);
2078}
2079
2080static void
2081print_netstats_sample(char *timebufptr)
2082{
2083    int i;
2084
2085    for (i=0; i < nr_count; i++)
2086    {
2087	if (!nr_table[i].valid)
2088	    continue;
2089
2090	/*
2091	 * This is where we attempt to handle counters that
2092	 * might wrap ... the kernel netstats are only 32 bits.
2093	 *
2094	 * Interfaces may go away and then return within the
2095	 * sampling period.  This can't be detected and it
2096	 * may look like a counter wrap.  An interface generation
2097	 * counter will help... but isn't implemented at this time.
2098	 */
2099
2100	/*
2101	 * The ppp interfaces are very likely to come and go during
2102	 * a sampling period.  During the normal life of a ppp interface,
2103	 * it's less likely that the packet counter will wrap, so if
2104	 * it appears to have done so, is probably because the
2105	 * interface unit number has been reused.
2106	 * We reinitialize that interface in that case.
2107	 */
2108	if (network_mode & NET_PPP_MODE)
2109	{
2110	    /*
2111	     * ppp interfaces won't even make it into this table
2112	     * when NET_PPP_MODE isn't set
2113	    */
2114	    if (!strncmp(nr_table[i].tname_unit, "ppp", 3))
2115	    {
2116		/*
2117		 * Both ipackets and opackets have to be less
2118		 * than the previous counter to cause us to reinit.
2119		 */
2120
2121		if ((nr_table[i].cur_ipackets < nr_table[i].prev_ipackets)
2122		  && (nr_table[i].cur_opackets < nr_table[i].prev_opackets))
2123		{
2124		    init_prev_netstats(&nr_table[i]);
2125		    continue;
2126		}
2127	    }
2128	}
2129
2130	nr_table[i].avg_count ++;
2131
2132#ifdef IFNET_32_BIT_COUNTERS
2133	while (nr_table[i].cur_ipackets < nr_table[i].prev_ipackets)
2134	    nr_table[i].cur_ipackets += 0x100000000LL;
2135#endif /* IFNET_32_BIT_COUNTERS */
2136	nr_table[i].cur_ipackets -= nr_table[i].prev_ipackets;
2137	nr_table[i].prev_ipackets += nr_table[i].cur_ipackets;
2138	nr_table[i].avg_ipackets += nr_table[i].cur_ipackets;
2139
2140
2141#ifdef IFNET_32_BIT_COUNTERS
2142	while (nr_table[i].cur_ibytes < nr_table[i].prev_ibytes)
2143	    nr_table[i].cur_ibytes += 0x100000000LL;
2144#endif /* IFNET_32_BIT_COUNTERS */
2145	nr_table[i].cur_ibytes -= nr_table[i].prev_ibytes;
2146	nr_table[i].prev_ibytes += nr_table[i].cur_ibytes;
2147	nr_table[i].avg_ibytes += nr_table[i].cur_ibytes;
2148
2149
2150#ifdef IFNET_32_BIT_COUNTERS
2151	while (nr_table[i].cur_opackets < nr_table[i].prev_opackets)
2152	    nr_table[i].cur_opackets += 0x100000000LL;
2153#endif /* IFNET_32_BIT_COUNTERS */
2154	nr_table[i].cur_opackets -= nr_table[i].prev_opackets;
2155	nr_table[i].prev_opackets += nr_table[i].cur_opackets;
2156	nr_table[i].avg_opackets += nr_table[i].cur_opackets;
2157
2158#ifdef IFNET_32_BIT_COUNTERS
2159	while (nr_table[i].cur_obytes < nr_table[i].prev_obytes)
2160	    nr_table[i].cur_obytes += 0x100000000LL;
2161#endif /* IFNET_32_BIT_COUNTERS */
2162	nr_table[i].cur_obytes -= nr_table[i].prev_obytes;
2163	nr_table[i].prev_obytes += nr_table[i].cur_obytes;
2164	nr_table[i].avg_obytes += nr_table[i].cur_obytes;
2165
2166
2167#ifdef IFNET_32_BIT_COUNTERS
2168	while (nr_table[i].cur_ierrors < nr_table[i].prev_ierrors)
2169	    nr_table[i].cur_ierrors += 0x100000000LL;
2170#endif /* IFNET_32_BIT_COUNTERS */
2171	nr_table[i].cur_ierrors -= nr_table[i].prev_ierrors;
2172	nr_table[i].prev_ierrors += nr_table[i].cur_ierrors;
2173	nr_table[i].avg_ierrors += nr_table[i].cur_ierrors;
2174
2175#ifdef IFNET_32_BIT_COUNTERS
2176	while (nr_table[i].cur_oerrors < nr_table[i].prev_oerrors)
2177	    nr_table[i].cur_oerrors += 0x100000000LL;
2178#endif /* IFNET_32_BIT_COUNTERS */
2179	nr_table[i].cur_oerrors -= nr_table[i].prev_oerrors;
2180	nr_table[i].prev_oerrors += nr_table[i].cur_oerrors;
2181	nr_table[i].avg_oerrors += nr_table[i].cur_oerrors;
2182
2183#ifdef IFNET_32_BIT_COUNTERS
2184	while (nr_table[i].cur_collisions < nr_table[i].prev_collisions)
2185	    nr_table[i].cur_collisions += 0x100000000LL;
2186#endif /* IFNET_32_BIT_COUNTERS */
2187	nr_table[i].cur_collisions -= nr_table[i].prev_collisions;
2188	nr_table[i].prev_collisions += nr_table[i].cur_collisions;
2189	nr_table[i].avg_collisions += nr_table[i].cur_collisions;
2190
2191#ifdef IFNET_32_BIT_COUNTERS
2192	while (nr_table[i].cur_drops < nr_table[i].prev_drops)
2193	    nr_table[i].cur_drops += 0x100000000LL;
2194#endif /* IFNET_32_BIT_COUNTERS */
2195	nr_table[i].cur_drops -= nr_table[i].prev_drops;
2196	nr_table[i].prev_drops += nr_table[i].cur_drops;
2197	nr_table[i].avg_drops += nr_table[i].cur_drops;
2198
2199
2200#ifdef IFNET_32_BIT_COUNTERS
2201	while (nr_table[i].cur_imcasts < nr_table[i].prev_imcasts)
2202	    nr_table[i].cur_imcasts += 0x100000000LL;
2203#endif /* IFNET_32_BIT_COUNTERS */
2204	nr_table[i].cur_imcasts -= nr_table[i].prev_imcasts;
2205	nr_table[i].prev_imcasts += nr_table[i].cur_imcasts;
2206	nr_table[i].avg_imcasts += nr_table[i].cur_imcasts;
2207
2208#ifdef IFNET_32_BIT_COUNTERS
2209	while (nr_table[i].cur_omcasts < nr_table[i].prev_omcasts)
2210	    nr_table[i].cur_omcasts += 0x100000000LL;
2211#endif /* IFNET_32_BIT_COUNTERS */
2212	nr_table[i].cur_omcasts -= nr_table[i].prev_omcasts;
2213	nr_table[i].prev_omcasts += nr_table[i].cur_omcasts;
2214	nr_table[i].avg_omcasts += nr_table[i].cur_omcasts;
2215    }
2216
2217
2218    if (!(flag_count > 1))
2219	fprintf(stdout, "\n");
2220
2221    if (network_mode & NET_DEV_MODE)
2222    {
2223	if (flag_count > 1)
2224	    print_column_heading(SAR_NETSTATS, timebufptr, NET_DEV_MODE);
2225
2226	for (i=0; i < nr_count; i++)
2227	{
2228	    if (!nr_table[i].valid)
2229		continue;
2230
2231	    if (!(network_mode & NET_PPP_MODE))
2232	    {
2233		if (!strncmp(nr_table[i].tname_unit, "ppp", 3))
2234		{
2235		    continue;  /* skip any ppp interfaces */
2236		}
2237	    }
2238
2239	    /* print the interface name */
2240	    fprintf(stdout, "%s    %-8.8s", timebufptr, nr_table[i].tname_unit);
2241
2242	    fprintf (stdout, "%8llu    ",
2243	      (nr_table[i].cur_ipackets / avg_interval));
2244
2245	    fprintf (stdout, "%10llu    ",
2246	      (nr_table[i].cur_ibytes / avg_interval));
2247
2248	    fprintf (stdout, "%8llu    ",
2249	      (nr_table[i].cur_opackets / avg_interval));
2250
2251	    fprintf (stdout, "%10llu\n",
2252	      (nr_table[i].cur_obytes / avg_interval));
2253	}
2254    }
2255
2256
2257    if (network_mode & NET_EDEV_MODE)
2258    {
2259	if(flag_count > 1)
2260	{
2261	    print_column_heading(SAR_NETSTATS, timebufptr, NET_EDEV_MODE);
2262	}
2263
2264	for (i=0; i < nr_count; i++)
2265	{
2266	    if (!nr_table[i].valid)
2267		continue;
2268
2269	    if (!(network_mode & NET_PPP_MODE))
2270	    {
2271		if (!strncmp(nr_table[i].tname_unit, "ppp", 3))
2272		{
2273		    continue;  /* skip any ppp interfaces */
2274		}
2275	    }
2276
2277	    /* print the interface name */
2278	    fprintf(stdout, "%s    %-8.8s  ", timebufptr, nr_table[i].tname_unit);
2279
2280	    fprintf (stdout, "%7llu    ",
2281	      (nr_table[i].cur_ierrors / avg_interval));
2282
2283	    fprintf (stdout, "%7llu    ",
2284	      (nr_table[i].cur_oerrors / avg_interval));
2285
2286	    fprintf (stdout, "%5llu    ",
2287	      (nr_table[i].cur_collisions / avg_interval));
2288
2289	    fprintf (stdout, "   %5llu\n",
2290	      (nr_table[i].cur_drops / avg_interval));
2291	}
2292	fflush(stdout);
2293    }
2294}
2295
2296static void
2297print_column_heading(int type, char *timebufptr, int mode)
2298{
2299    char *p;
2300
2301    p = timebufptr;
2302
2303    if (p == NULL)
2304	p = "Average:";
2305
2306    if (!(flag_count > 1))
2307      fprintf(stdout, "\n");
2308
2309    switch (type)
2310    {
2311    case SAR_CPU:
2312    	fprintf (stdout, "\n%s  %%usr  %%nice   %%sys   %%idle\n", p);
2313	break;
2314
2315    case SAR_VMSTAT:
2316	if (mode == 0)  /* gflag */
2317	    fprintf(stdout, "\n%s    pgout/s\n", p);
2318	else if (mode == 1)  /* pflag */
2319	    fprintf(stdout, "\n%s     pgin/s        pflt/s        vflt/s\n", p);
2320	break;
2321    case SAR_DRIVESTATS:
2322	fprintf(stdout, "\n%s   device    r+w/s    blks/s\n", p);
2323	break;
2324    case SAR_NETSTATS:
2325	if (mode == NET_DEV_MODE)
2326	{
2327	    fprintf(stdout, "\n%s %-8.8s   %8.8s    %10.10s    %8.8s    %10.10s\n", p,
2328	      "   IFACE", "Ipkts/s", "Ibytes/s", "Opkts/s", "Obytes/s");
2329	}
2330	else if (mode == NET_EDEV_MODE)
2331	{
2332	    fprintf(stdout, "\n%s %-8.8s     %7.7s     %7.7s    %5s      %s\n", p,
2333	      "   IFACE", "Ierrs/s", "Oerrs/s", "Coll/s", "Drop/s");
2334	}
2335	break;
2336    default:
2337	break;
2338    }
2339}
2340
2341