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