]> git.saurik.com Git - apple/system_cmds.git/blob - system_cmds-597.1.1/sadc.tproj/sadc.c
70ead05adb74445e22bb536534fc364cacc68da9
[apple/system_cmds.git] / system_cmds-597.1.1 / sadc.tproj / sadc.c
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
48 extern int errno;
49
50 FILE *data_fp = (FILE *)0; /* raw data output file pointer */
51
52
53 #define REVISION_HISTORY_DATE 20030718
54
55 struct record_hdr restart_record = { SAR_RESTART, REVISION_HISTORY_DATE, 0, 0 };
56 struct record_hdr timestamp_record = { SAR_TIMESTAMP, 1, 0, 0 };
57 struct record_hdr vmstat_record = {SAR_VMSTAT, 1, 1, 0 };
58 struct record_hdr cpu_record = {SAR_CPU, 1, 1, 0 };
59 struct record_hdr drivestats_record = {SAR_DRIVESTATS, 1, 0, 0 };
60 struct record_hdr drivepath_record = {SAR_DRIVEPATH, 1, 1, 0 };
61 struct record_hdr netstats_record = {SAR_NETSTATS, 1, 0, 0};
62
63 /* Compile for verbose output */
64
65 int t_interval = 0; /* in seconds */
66 int n_samples = 1; /* number of sample loops */
67 char *ofile = NULL; /* output file */
68 int ofd; /* output file descriptor */
69 static mach_port_t myHost;
70 static mach_port_t masterPort;
71
72 /* internal table of drive path mappings */
73 struct drivepath *dp_table = NULL;
74
75 /* number of entries in the dp_table */
76 int dp_count = 0;
77
78 /* internal table of network interface statistics */
79 struct netstats *ns_table = NULL;
80 int ns_count = 0;
81
82 static uid_t realuid;
83
84 int network_mode = 0;
85
86 /* Forward fuction declarations */
87 static void exit_usage();
88 static void open_datafile(char *);
89 static void write_record_hdr(struct record_hdr *);
90 static void write_record_data(char *, int);
91 static void get_all_stats();
92 static void get_vmstat_sample();
93 static void get_drivestat_sample();
94 static int get_ndrives();
95 static int record_device(io_registry_entry_t, struct drivestats *, int ndrives);
96 static int check_device_path (char *name, char *path, int ndrives);
97 static void get_netstat_sample(int pppflag);
98
99 int
100 main(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
230 static void
231 exit_usage()
232 {
233 fprintf(stderr, "/usr/lib/sa/sadc [-m {PPP}] [t n] [ofile]\n");
234 exit(EXIT_FAILURE);
235 }
236
237 static void
238 open_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
256 static void
257 write_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
272 static void
273 write_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
290 static void
291 get_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
311 static void
312 get_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
332 static void
333 get_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 */
418 static int
419 record_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 */
652 static int
653 get_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 */
715 void
716 get_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 */
736 static int
737 check_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 */
822 static void
823 get_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 }