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(×tamp_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(×tamp_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