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#define IOKIT   1       /* to get io_name_t in device_types.h */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <ctype.h>
27#include <time.h>
28#include <err.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <mach/mach.h>
32#include <mach/mach_error.h>
33#include <sys/param.h>
34
35#include <CoreFoundation/CoreFoundation.h>
36#include <IOKit/IOKitLib.h>
37#include <IOKit/storage/IOBlockStorageDriver.h>
38#include <IOKit/storage/IOMedia.h>
39#include <IOKit/IOBSD.h>
40
41#include <sys/socket.h>
42#include <net/if.h>
43#include <net/if_var.h>
44#include <ifaddrs.h>
45
46#include <sadc.h>
47
48extern int errno;
49
50FILE *data_fp = (FILE *)0;	/* raw data output file pointer */
51
52
53#define REVISION_HISTORY_DATE 20030718
54
55struct record_hdr restart_record = { SAR_RESTART, REVISION_HISTORY_DATE, 0, 0 };
56struct record_hdr timestamp_record = { SAR_TIMESTAMP, 1, 0, 0 };
57struct record_hdr vmstat_record = {SAR_VMSTAT, 1, 1, 0 };
58struct record_hdr cpu_record = {SAR_CPU, 1, 1, 0 };
59struct record_hdr drivestats_record = {SAR_DRIVESTATS, 1, 0, 0 };
60struct record_hdr drivepath_record = {SAR_DRIVEPATH, 1, 1, 0 };
61struct record_hdr netstats_record = {SAR_NETSTATS, 1, 0, 0};
62
63/* Compile for verbose output */
64
65int t_interval = 0; 	/* in seconds */
66int n_samples  = 1;	/* number of sample loops */
67char *ofile = NULL;     /* output file */
68int ofd;		/* output file descriptor  */
69static mach_port_t myHost;
70static mach_port_t masterPort;
71
72/* internal table of drive path mappings */
73struct drivepath *dp_table = NULL;
74
75/* number of entries in the dp_table */
76int dp_count = 0;
77
78/* internal table of network interface statistics */
79struct netstats *ns_table = NULL;
80int ns_count = 0;
81
82static uid_t realuid;
83
84int network_mode = 0;
85
86/* Forward fuction declarations */
87static void exit_usage();
88static void open_datafile(char *);
89static void write_record_hdr(struct record_hdr *);
90static void write_record_data(char *, int);
91static void get_all_stats();
92static void get_vmstat_sample();
93static void get_drivestat_sample();
94static int get_ndrives();
95static int record_device(io_registry_entry_t, struct drivestats *, int ndrives);
96static int check_device_path (char *name, char *path, int ndrives);
97static void get_netstat_sample(int pppflag);
98
99int
100main(argc, argv)
101    int argc;
102    char *argv[];
103{
104
105    char        *p;
106    char	ch;
107
108    /*
109     * Stop being root ASAP.
110     */
111    if (geteuid() != 0)
112    {
113	fprintf(stderr, "sadc: must be setuid root or root");
114	exit(1);
115    }
116
117    realuid = getuid();
118    seteuid(realuid);
119
120    setvbuf(stdout, (char *)NULL, _IONBF, 0);
121
122    while ((ch=getopt(argc, argv, "m:")) != EOF) {
123	switch(ch) {
124	case 'm':
125	    /* Only the PPP mode matters on this collector side   */
126	    /* The reporter side deals with the DEV or EDEV modes */
127	    if (!strncmp(optarg, "PPP", 3))
128		network_mode |= NET_PPP_MODE;
129	    break;
130	default:
131	    exit_usage();
132	    break;
133	}
134    }
135
136    argc -= optind;
137    if (argc > 0)
138    {
139	if (isdigit(*argv[optind]))
140	{
141	    /* we expect to have both an interval and a sample count */
142	    errno=0;
143	    t_interval = strtol(argv[optind], &p, 0);
144	    if (errno || (*p !='\0') || t_interval <= 0)
145	    {
146		exit_usage();
147	    }
148
149	    optind++;
150	    if ((argc < 2) || (!isdigit(*argv[optind]))) {
151		exit_usage();
152	    }
153
154	    errno=0;
155	    n_samples = strtol(argv[optind], &p, 0);
156	    if (errno || (*p != '\0') || n_samples <= 0)
157	    {
158		exit_usage();
159	    }
160
161	    optind++;
162	    if (argc == 3)
163	    {
164		/* we have an output file */
165		ofile = argv[optind];
166	    }
167	}
168	else
169	{
170	    /* all we have is an output file */
171	    ofile = argv[optind];
172	}
173    }
174
175
176    /* open the output file */
177    (void)open_datafile(ofile);
178
179    /*
180     * Get the Mach private port.
181     */
182    myHost = mach_host_self();
183
184    /*
185     * Get the I/O Kit communication handle.
186     */
187    IOMasterPort(bootstrap_port, &masterPort);
188
189
190    restart_record.rec_timestamp = time((time_t *)0);
191    write_record_hdr(&restart_record);
192    get_all_stats();   /* this is the initial stat collection */
193    sleep(t_interval);
194
195    if (n_samples > 0)
196    {
197	/* this init sample is not counted */
198	timestamp_record.rec_data = time((time_t *)0); /* returns time in
199						       * seconds */
200#if 0
201	    struct tm *tm;
202	    tm = gmtime(&(timestamp_record.rec_data));
203	    fprintf(stderr, "timestamp=%ld\n", timestamp_record.rec_data);
204	    fprintf(stderr, "GMTIME offset from UTC in seconds = %ld\n", tm->tm_gmtoff);
205	    fprintf(stderr, "GMTIME secnds=%d, min=%d, hour=%d\n", tm->tm_sec, tm->tm_min, tm->tm_hour);
206	    fprintf(stderr, "asctime = %s\n", asctime(tm));
207
208	    tm=localtime(&(timestamp_record.rec_data));
209	    fprintf(stderr, "LOCTIME offset from UTC in seconds = %ld\n",tm->tm_gmtoff);
210	    fprintf(stderr, "LOCTIME secnds=%d, min=%d, hour=%d\n", tm->tm_sec, tm->tm_min, tm->tm_hour);
211	    fprintf(stderr, "asctime = %s\n", asctime(tm));
212#endif
213
214	write_record_hdr(&timestamp_record);
215	get_all_stats();
216    }
217
218    while (n_samples)
219    {
220	sleep(t_interval);
221	timestamp_record.rec_timestamp = time((time_t *)0); /* returns time in
222							     * seconds */
223	write_record_hdr(&timestamp_record);
224	get_all_stats();
225	n_samples--;
226    }
227    exit(EXIT_SUCCESS);
228}
229
230static void
231exit_usage()
232{
233    fprintf(stderr, "/usr/lib/sa/sadc [-m {PPP}] [t n] [ofile]\n");
234    exit(EXIT_FAILURE);
235}
236
237static void
238open_datafile(char *path)
239{
240    if (path == NULL)
241    {
242	data_fp = stdout;
243	return;
244    }
245    else
246	data_fp = fopen(path, "w+");
247
248    if (!data_fp)
249    {
250	/* failed to open path */
251	fprintf(stderr, "sadc: failed to open data file [%s]\n", path?path:"stdout");
252	exit_usage();
253    }
254}
255
256static void
257write_record_hdr(hdr)
258    struct record_hdr *hdr;
259{
260    errno = 0;
261
262    if (fwrite(hdr, sizeof(struct record_hdr), 1, data_fp) != 1)
263    {
264	fprintf(stderr, "sadc: write_record_hdr failed, errno=%d\n", errno);
265	exit(EXIT_FAILURE);
266    }
267
268    fflush(data_fp);
269    return;
270}
271
272static void
273write_record_data(data, size)
274    char *data;
275    int  size;
276{
277    errno = 0;
278
279    if (fwrite(data, size, 1, data_fp) != 1)
280    {
281	fprintf(stderr, "sadc: write_record_data failed, errno=%d\n", errno);
282	exit(EXIT_FAILURE);
283    }
284
285    fflush(data_fp);
286    return;
287}
288
289
290static void
291get_vmstat_sample()
292{
293    struct vm_statistics    stat;
294    kern_return_t           error;
295    mach_msg_type_number_t  count;
296
297    count = HOST_VM_INFO_COUNT;
298    error = host_statistics(myHost, HOST_VM_INFO, (host_info_t)&stat, &count);
299    if (error != KERN_SUCCESS) {
300	fprintf(stderr, "sadc: Error in vm host_statistics(): %s\n",
301	  mach_error_string(error));
302	exit(2);
303    }
304
305    vmstat_record.rec_count = 1;
306    vmstat_record.rec_size = sizeof(vm_statistics_data_t);
307    write_record_hdr(&vmstat_record);
308    write_record_data((char *)&stat, sizeof(vm_statistics_data_t));
309}
310
311static void
312get_cpu_sample()
313{
314    host_cpu_load_info_data_t    cpuload;
315    kern_return_t           error;
316    mach_msg_type_number_t  count;
317
318    count = HOST_CPU_LOAD_INFO_COUNT;
319    error = host_statistics(myHost, HOST_CPU_LOAD_INFO,(host_info_t)&cpuload, &count);
320    if (error != KERN_SUCCESS) {
321	fprintf(stderr, "sadc: Error in cpu host_statistics(): %s",
322	  mach_error_string(error));
323	exit(2);
324    }
325
326    cpu_record.rec_count = 1;
327    cpu_record.rec_size = sizeof(host_cpu_load_info_data_t);
328    write_record_hdr(&cpu_record);
329    write_record_data((char *)&cpuload, sizeof(host_cpu_load_info_data_t));
330}
331
332static void
333get_drivestat_sample()
334{
335    io_registry_entry_t     drive;
336    io_iterator_t           drivelist;
337    CFMutableDictionaryRef match;
338    int			    ndrives;
339    int			    i = 0;
340    long 		    bufsize = 0;
341    char 	 	    *buf;
342    struct drivestats       *dbuf;
343    kern_return_t           status;
344    int error;
345
346    if ((ndrives = get_ndrives()) <= 0)
347	return;
348
349    /* allocate space to collect stats for all the drives */
350    bufsize = ndrives * sizeof(struct drivestats);
351    buf  = (char *) malloc (bufsize);
352    dbuf = (struct drivestats *)buf;
353    if (buf)
354	bzero((char *)buf, bufsize);
355    else
356	return;
357
358    /*
359     * Get an iterator for IOMedia objects.
360     */
361    match = IOServiceMatching("IOMedia");
362
363    /* Get whole disk info */
364    CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
365
366    status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
367    if (status != KERN_SUCCESS)
368	goto RETURN;
369
370    /*
371     * Scan all of the IOMedia objects, and for each
372     * object that has a parent IOBlockStorageDriver,
373     * record the statistics
374     *
375     * XXX What about RAID devices?
376     */
377    error = 1;
378    i = 0;
379    while ((drive = IOIteratorNext(drivelist)))
380    {
381	if (i < ndrives)
382	{
383	    if (record_device(drive, &dbuf[i], ndrives))
384	    {
385		error = 0;
386		i++;
387	    }
388	}
389	else
390	{
391	    IOObjectRelease(drive);
392	    break;
393	}
394	IOObjectRelease(drive);
395    }
396    IOObjectRelease(drivelist);
397
398    if (! error)
399    {
400	drivestats_record.rec_count = i;
401	drivestats_record.rec_size = sizeof (struct drivestats);
402	write_record_hdr(&drivestats_record);
403	write_record_data((char *)buf, (i * sizeof(struct drivestats)));
404    }
405
406    RETURN:
407    if (buf)
408	free(buf);
409    return;
410}
411
412/*
413 * Determine whether an IORegistryEntry refers to a valid
414 * I/O device, and if so, record it.
415 * Return zero: no device recorded
416 * Return non-zero: device stats recorded
417 */
418static int
419record_device(io_registry_entry_t drive, struct drivestats* drivestat, int ndrives)
420{
421    io_registry_entry_t parent;
422    CFDictionaryRef properties, statistics;
423    CFStringRef name;
424    CFNumberRef number;
425    UInt64                  value;
426    kern_return_t status;
427    int retval = 0;
428    int drive_id;
429    io_string_t path;
430    char        BSDName[MAXDRIVENAME + 1];
431
432    status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent);
433    if (status != KERN_SUCCESS)
434    {
435	/* device has no parent */
436	return(retval);
437    }
438
439    if (IOObjectConformsTo(parent, "IOBlockStorageDriver"))
440    {
441	/*
442	 * Get a unique device path identifier.
443	 * Devices available at boot have an Open Firmware Device Tree path.
444	 * The OF path is short and concise and should be first choice.
445	 * Devices that show up after boot, are guaranteed to have
446	 * a Service Plane, hardware unique path.
447	 */
448
449        bzero(path, sizeof(io_string_t));
450	if (IORegistryEntryGetPath(drive, kIODeviceTreePlane, path) != KERN_SUCCESS)
451	{
452	    if(IORegistryEntryGetPath(drive, kIOServicePlane, path) != KERN_SUCCESS)
453		/* device has no unique path identifier */
454		goto RETURN;
455	}
456	retval++;
457
458	/* get drive properties */
459	status = IORegistryEntryCreateCFProperties(drive,
460	  (CFMutableDictionaryRef *)&properties,
461	  kCFAllocatorDefault,
462	  kNilOptions);
463	if (status != KERN_SUCCESS)
464	{
465            /* device has no properties */
466	    goto RETURN;
467	}
468
469	bzero(BSDName, MAXDRIVENAME+1);
470	/* get name from properties */
471	name = (CFStringRef)CFDictionaryGetValue(properties,
472	  CFSTR(kIOBSDNameKey));
473	if (name) {
474	    CFStringGetCString(name, BSDName,
475	      MAXDRIVENAME, kCFStringEncodingUTF8);
476	    retval++;
477	}
478
479	/* get blocksize from properties */
480	number = (CFNumberRef)CFDictionaryGetValue(properties,
481	  CFSTR(kIOMediaPreferredBlockSizeKey));
482	if (number != 0) {
483	    CFNumberGetValue(number,
484	      kCFNumberSInt64Type, &value);
485	    drivestat->blocksize = value;
486	    retval++;
487	}
488	CFRelease(properties);
489    }
490    else
491	goto RETURN;
492
493    /* we should have a name and blocksize at a minimum */
494    if (retval != 3)
495    {
496	retval = FALSE;
497	goto RETURN;
498    }
499
500    drive_id = check_device_path (BSDName, path, ndrives);
501    if (drive_id == -1)
502    {
503	retval = FALSE;
504	goto RETURN;
505    }
506    else
507	drivestat->drivepath_id = drive_id;
508
509
510    /* get parent drive properties */
511    status = IORegistryEntryCreateCFProperties(parent,
512      (CFMutableDictionaryRef *)&properties,
513      kCFAllocatorDefault,
514      kNilOptions);
515    if (status != KERN_SUCCESS)
516    {
517        /* device has no properties */
518	goto RETURN;
519    }
520
521    /* Obtain the statistics from the parent drive properties. */
522
523    statistics
524      = (CFDictionaryRef)CFDictionaryGetValue(properties,
525      CFSTR(kIOBlockStorageDriverStatisticsKey));
526
527    if (statistics != 0)
528    {
529	/* Get number of reads. */
530	number =
531	  (CFNumberRef)CFDictionaryGetValue(statistics,
532	  CFSTR(kIOBlockStorageDriverStatisticsReadsKey));
533	if (number != 0) {
534	    CFNumberGetValue(number,
535	      kCFNumberSInt64Type, &value);
536	    drivestat->Reads = value;
537	}
538
539	/* Get bytes read. */
540	number =
541	  (CFNumberRef)CFDictionaryGetValue(statistics,
542	  CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey));
543	if (number != 0) {
544	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
545	    drivestat->BytesRead = value;
546	}
547
548	/* Get number of writes. */
549	number =
550	  (CFNumberRef)CFDictionaryGetValue(statistics,
551	  CFSTR(kIOBlockStorageDriverStatisticsWritesKey));
552	if (number != 0) {
553	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
554	    drivestat->Writes = value;
555	}
556
557	/* Get bytes written. */
558	number =
559	  (CFNumberRef)CFDictionaryGetValue(statistics,
560	  CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey));
561	if (number != 0) {
562	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
563	    drivestat->BytesWritten = value;
564	}
565
566	/* Get LatentReadTime. */
567	number =
568	  (CFNumberRef)CFDictionaryGetValue(statistics,
569	  CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey));
570	if (number != 0) {
571	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
572	    drivestat->LatentReadTime = value;
573	}
574
575	/* Get LatentWriteTime. */
576	number =
577	  (CFNumberRef)CFDictionaryGetValue(statistics,
578	  CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey));
579	if (number != 0) {
580	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
581	    drivestat->LatentWriteTime = value;
582	}
583
584	/* Get ReadErrors. */
585	number =
586	  (CFNumberRef)CFDictionaryGetValue(statistics,
587	  CFSTR(kIOBlockStorageDriverStatisticsReadErrorsKey));
588	if (number != 0) {
589	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
590	    drivestat->ReadErrors = value;
591	}
592
593	/* Get WriteErrors. */
594	number =
595	  (CFNumberRef)CFDictionaryGetValue(statistics,
596	  CFSTR(kIOBlockStorageDriverStatisticsWriteErrorsKey));
597	if (number != 0) {
598	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
599	    drivestat->WriteErrors = value;
600	}
601
602	/* Get ReadRetries. */
603	number =
604	  (CFNumberRef)CFDictionaryGetValue(statistics,
605	  CFSTR(kIOBlockStorageDriverStatisticsReadRetriesKey));
606	if (number != 0) {
607	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
608	    drivestat->ReadRetries = value;
609	}
610
611	/* Get WriteRetries. */
612	number =
613	  (CFNumberRef)CFDictionaryGetValue(statistics,
614	  CFSTR(kIOBlockStorageDriverStatisticsWriteRetriesKey));
615	if (number != 0) {
616	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
617	    drivestat->WriteRetries = value;
618	}
619
620	/* Get TotalReadTime. */
621	number =
622	  (CFNumberRef)CFDictionaryGetValue(statistics,
623	  CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey));
624	if (number != 0) {
625	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
626	    drivestat->TotalReadTime = value;
627	}
628
629	/* Get WriteRetries. */
630	number =
631	  (CFNumberRef)CFDictionaryGetValue(statistics,
632	  CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey));
633	if (number != 0) {
634	    CFNumberGetValue(number, kCFNumberSInt64Type, &value);
635	    drivestat->TotalWriteTime = value;
636	}
637
638	CFRelease(properties);
639    } /* end if statistics != 0 */
640
641    RETURN:
642    IOObjectRelease(parent);
643    return(retval);
644}
645
646
647/*
648 * find IOMedia objects
649 * This routine always gives me a lower count on the number
650 * of disks.  I don't know which one to use.
651 */
652static int
653get_ndrives(void)
654{
655    io_iterator_t drivelist;
656    io_registry_entry_t drive;
657    io_registry_entry_t parent;
658    CFMutableDictionaryRef match;
659    int error, ndrives;
660    kern_return_t status;
661
662    /*
663     * Get an iterator for IOMedia objects.
664     */
665    match = IOServiceMatching("IOMedia");
666    CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
667    status = IOServiceGetMatchingServices(masterPort, match, &drivelist);
668    if (status != KERN_SUCCESS)
669	return(0);
670
671    /*
672     * Scan all of the IOMedia objects, and count each
673     * object that has a parent IOBlockStorageDriver
674     *
675     * XXX What about RAID devices?
676     */
677    error = 1;
678    ndrives = 0;
679    while ((drive = IOIteratorNext(drivelist)))
680    {
681	/* get drive's parent */
682	status = IORegistryEntryGetParentEntry(drive,
683	  kIOServicePlane, &parent);
684	if (status != KERN_SUCCESS)
685	{
686	    IOObjectRelease(drive);
687	    continue;
688	}
689
690	if (IOObjectConformsTo(parent, "IOBlockStorageDriver"))
691	{
692	    error = 0;
693	    ndrives++;
694	}
695	IOObjectRelease(parent);
696	IOObjectRelease(drive);
697    }
698
699    IOObjectRelease(drivelist);
700
701    return(ndrives);
702}
703
704
705/*
706 * When getting the stats, do it in the order
707 * of their type.  The types that have the most
708 * data come first in the list if possible.
709 * This makes the sar reporter tool more efficient,
710 * because in some cases, it will allocate a buffer
711 * and keep reusing it as long as the sample data fits.
712 * When a sample data doesn't fit, it reallocates the buffer
713 * to a bigger size etc.
714 */
715void
716get_all_stats()
717{
718
719    get_drivestat_sample();
720    get_netstat_sample(network_mode);
721    get_vmstat_sample();
722    get_cpu_sample();
723}
724
725
726/*
727 * An internal table maps the BSDName to a unique ioregistry path.
728 * The table's index is then used as a unique compressed path, and
729 * helps track disks that come and go during the sampling intervals.
730 * This routine finds an entry that maps both the BSDName and the
731 * IOKit registry path.  If no mapping is discovered, a new entry
732 * is created.  An entry is never removed, this maintaining the
733 * unique index throughout the data collection.
734 * Success returns the map index. Failure returns -1.
735 */
736static int
737check_device_path (char *name, char *path, int ndrives)
738{
739    int i;
740    int index;
741    int n;
742
743    if (dp_table == NULL)
744    {
745	/* First setup of internal drivepath table */
746	dp_table = (struct drivepath *)malloc (ndrives * sizeof(struct drivepath));
747	if (dp_table == NULL)
748	    return(-1);
749	else
750	{
751	    bzero(dp_table, (ndrives * sizeof(struct drivepath)));
752	    dp_count = ndrives;
753	    drivepath_record.rec_size = sizeof(struct drivepath);
754	}
755    }
756
757    for (i=0; i < dp_count; i++)
758    {
759	if (dp_table[i].state == DPSTATE_UNINITIALIZED)
760	{
761	    /* This is a new drive entry that should be recorded */
762	    index = i;
763	    goto NEW_ENTRY;
764	}
765	else if (!strcmp (dp_table[i].ioreg_path, path))
766	{
767	    /* Found a matching hardware path */
768	    if (!strcmp(dp_table[i].BSDName, name))
769	    {
770		/* The BSDName matches the entry in the table
771		 * so there is no need to record this data.
772		 */
773		return(i);
774	    }
775	    else
776	    {
777		/* The BSDName is different ... implies a change,
778		 * like the drive was removed and now is back
779		 */
780		bzero((char *)dp_table[i].BSDName, MAXDRIVENAME+1);
781		dp_table[i].drivepath_id = i;
782		dp_table[i].state = DPSTATE_CHANGED;
783		strcpy(dp_table[i].BSDName, name);
784		write_record_hdr(&drivepath_record);
785		write_record_data((char *)&dp_table[i], sizeof(struct drivepath));
786		return(i);
787	    }
788	}
789    } /* end for loop */
790
791    /*
792     * If we reach this point, then we've run out of
793     * table entries. Double the size of the table.
794     */
795    n = dp_count * 2;
796    dp_table = (struct drivepath *)realloc(dp_table, n * sizeof(struct drivepath));
797    bzero(&dp_table[dp_count], dp_count * sizeof(struct drivepath));
798    index = dp_count;
799    dp_count = n;
800
801    /* This is a new drive entry that should be recorded */
802    NEW_ENTRY:
803    dp_table[index].drivepath_id = index;
804    dp_table[index].state = DPSTATE_NEW;
805    strcpy(dp_table[index].BSDName, name);
806    strcpy(dp_table[index].ioreg_path, path);
807    write_record_hdr(&drivepath_record);
808    write_record_data((char *)&dp_table[index], sizeof(struct drivepath));
809    return(index);
810}
811
812
813
814/*
815 * Thus far, only the networking stats take an optional flag
816 * to modify the collection of data.  The number of ppp
817 * interfaces can be very high, causing the raw data file to
818 * grow very large.  We want this option to include ppp
819 * statistics to be off by default.  When we see the -m PPP
820 * mode passed in, ppp collection will be turned on.
821 */
822static void
823get_netstat_sample(int mode)
824{
825
826    int n;
827    int ns_index     = 0;
828    char                    tname[MAX_TNAME_SIZE + 1];
829    char                    name[MAX_TNAME_UNIT_SIZE + 1];
830    struct ifaddrs *ifa_list, *ifa;
831
832
833	/*
834	 * Set the starting table size to 100 entries
835	 * That should be big enough for most cases,
836	 * even with a lot of ppp connections.
837	 */
838	ns_count = 100;
839	ns_table = (struct netstats *) malloc(ns_count * sizeof (struct netstats));
840	if (ns_table == NULL)
841	{
842	    fprintf(stderr, "sadc: malloc netstat table failed\n");
843	    return;
844	}
845
846    bzero(ns_table, ns_count * sizeof(struct netstats));
847    if (getifaddrs(&ifa_list) == -1)
848   	    return;
849
850	for (ifa = ifa_list; ifa; ifa = ifa->ifa_next)
851	{
852        struct if_data *if_data = (struct if_data *)ifa->ifa_data;
853
854		if (AF_LINK != ifa->ifa_addr->sa_family)
855			continue;
856		if (ifa->ifa_data == 0)
857			continue;
858	    tname[MAX_TNAME_SIZE] = '\0';
859	    if (!(network_mode & NET_PPP_MODE))
860	    {
861		/*
862		 * If the flag is set, include PPP connections.
863		 * By default this collection is turned off
864		 */
865		if(!strncmp(ifa->ifa_name, "ppp", 3))
866		    continue;
867	    }
868	    snprintf(name, MAX_TNAME_UNIT_SIZE, "%s", ifa->ifa_name);
869	    name[MAX_TNAME_UNIT_SIZE] = '\0';
870
871	    if (ns_index == ns_count)
872	    {
873		/* the stat table needs to grow */
874		n = ns_count * 2;
875		ns_table = (struct netstats *)realloc(ns_table, n * sizeof(struct netstats));
876		bzero(&ns_table[ns_count], ns_count * sizeof(struct netstats));
877		ns_count = n;
878	    }
879
880	    /*
881	     * As a means of helping to identify when interface unit numbers
882	     * are reused, a generation counter may eventually be implemented.
883	     * This will be especially helpful with ppp-x connections.
884	     * In anticipation, we will reserve a space for it, but always
885	     * set it to zero for now.
886	     */
887	    ns_table[ns_index].gen_counter = 0;
888
889	    strncpy(ns_table[ns_index].tname_unit, name, MAX_TNAME_UNIT_SIZE);
890	    ns_table[ns_index].tname_unit[MAX_TNAME_UNIT_SIZE] = '\0';
891	    ns_table[ns_index].net_ipackets = if_data->ifi_ipackets;
892	    ns_table[ns_index].net_ierrors  = if_data->ifi_ierrors;
893	    ns_table[ns_index].net_opackets = if_data->ifi_opackets;
894	    ns_table[ns_index].net_oerrors  = if_data->ifi_oerrors;
895	    ns_table[ns_index].net_collisions = if_data->ifi_collisions;
896	    ns_table[ns_index].net_ibytes   = if_data->ifi_ibytes;
897	    ns_table[ns_index].net_obytes   = if_data->ifi_obytes;
898	    ns_table[ns_index].net_imcasts   = if_data->ifi_imcasts;
899	    ns_table[ns_index].net_omcasts   = if_data->ifi_omcasts;
900	    ns_table[ns_index].net_drops      = if_data->ifi_iqdrops;
901	    ns_index++;
902	}  /* end for */
903
904	netstats_record.rec_count = ns_index;
905	netstats_record.rec_size = sizeof(struct netstats);
906	write_record_hdr(&netstats_record);
907	write_record_data((char *)ns_table, (ns_index * sizeof(struct netstats)));
908    return;
909}
910