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