2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry
26 * All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. The name of the author may not be used to endorse or promote products
37 * derived from this software without specific prior written permission.
39 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * $FreeBSD: src/usr.sbin/iostat/iostat.c,v 1.22 2001/09/01 07:40:19 kris Exp $
54 * Parts of this program are derived from the original FreeBSD iostat
58 * Copyright (c) 1986, 1991, 1993
59 * The Regents of the University of California. All rights reserved.
61 * Redistribution and use in source and binary forms, with or without
62 * modification, are permitted provided that the following conditions
64 * 1. Redistributions of source code must retain the above copyright
65 * notice, this list of conditions and the following disclaimer.
66 * 2. Redistributions in binary form must reproduce the above copyright
67 * notice, this list of conditions and the following disclaimer in the
68 * documentation and/or other materials provided with the distribution.
69 * 3. All advertising materials mentioning features or use of this software
70 * must display the following acknowledgement:
71 * This product includes software developed by the University of
72 * California, Berkeley and its contributors.
73 * 4. Neither the name of the University nor the names of its contributors
74 * may be used to endorse or promote products derived from this software
75 * without specific prior written permission.
77 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
90 * Ideas for the new iostat statistics output modes taken from the NetBSD
94 * Copyright (c) 1996 John M. Vinopal
95 * All rights reserved.
97 * Redistribution and use in source and binary forms, with or without
98 * modification, are permitted provided that the following conditions
100 * 1. Redistributions of source code must retain the above copyright
101 * notice, this list of conditions and the following disclaimer.
102 * 2. Redistributions in binary form must reproduce the above copyright
103 * notice, this list of conditions and the following disclaimer in the
104 * documentation and/or other materials provided with the distribution.
105 * 3. All advertising materials mentioning features or use of this software
106 * must display the following acknowledgement:
107 * This product includes software developed for the NetBSD Project
108 * by John M. Vinopal.
109 * 4. The name of the author may not be used to endorse or promote products
110 * derived from this software without specific prior written permission.
112 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
113 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
114 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
115 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
116 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
117 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
118 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
119 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
120 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
121 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
125 #define IOKIT 1 /* to get io_name_t in device_types.h */
127 #include <sys/param.h>
128 #include <sys/sysctl.h>
130 #include <CoreFoundation/CoreFoundation.h>
131 #include <IOKit/IOKitLib.h>
132 #include <IOKit/storage/IOBlockStorageDriver.h>
133 #include <IOKit/storage/IOMedia.h>
134 #include <IOKit/IOBSD.h>
135 #include <mach/mach_host.h> /* host_statistics */
143 #define MAXDRIVES 16 /* most drives we will record */
144 #define MAXDRIVENAME 31 /* largest drive name we allow */
147 io_registry_entry_t driver
;
148 char name
[MAXDRIVENAME
+ 1];
150 u_int64_t total_bytes
;
151 u_int64_t total_transfers
;
152 u_int64_t total_time
;
155 static struct drivestats drivestat
[MAXDRIVES
];
157 static struct timeval cur_time
, last_time
;
162 host_cpu_load_info_data_t load
;
165 static struct statinfo cur
, last
;
167 static mach_port_t host_priv_port
;
168 static mach_port_t masterPort
;
170 static int num_devices
;
171 static int maxshowdevs
;
172 static int dflag
= 0, Iflag
= 0, Cflag
= 0, Tflag
= 0, oflag
= 0, Uflag
= 0, Kflag
= 0;
173 static volatile sig_atomic_t phdr_flag
= 0;
174 static IONotificationPortRef notifyPort
;
176 /* local function declarations */
177 static void usage(void);
178 static void phdr(int signo
);
179 static void do_phdr();
180 static void devstats(int perf_select
, long double etime
, int havelast
);
181 static void cpustats(void);
182 static void loadstats(void);
183 static int readvar(const char *name
, void *ptr
, size_t len
);
185 static int record_all_devices(void);
186 static void record_drivelist(void* context
, io_iterator_t drivelist
);
187 static void remove_drivelist(void* context
, io_iterator_t drivelist
);
188 static int record_one_device(char *name
);
189 static int record_device(io_registry_entry_t drive
);
191 static int compare_drivestats(const void* pa
, const void* pb
);
193 static long double compute_etime(struct timeval cur_time
,
194 struct timeval prev_time
);
200 * We also support the following 'traditional' syntax:
201 * iostat [drives] [wait [count]]
202 * This isn't mentioned in the man page, or the usage statement,
203 * but it is supported.
205 fprintf(stderr
, "usage: iostat [-CUdIKoT?] [-c count] [-n devs]\n"
206 "\t [-w wait] [drives]\n");
210 main(int argc
, char **argv
)
213 int hflag
= 0, cflag
= 0, wflag
= 0, nflag
= 0;
214 int count
= 0, waittime
= 0;
216 int num_devices_specified
;
219 CFRunLoopSourceRef rls
;
223 while ((c
= getopt(argc
, argv
, "c:CdIKM:n:oTUw:?")) != -1) {
227 count
= atoi(optarg
);
229 errx(1, "count %d is < 1", count
);
245 maxshowdevs
= atoi(optarg
);
247 errx(1, "number of devices %d is < 0",
261 waittime
= atoi(optarg
);
263 errx(1, "wait time is < 1");
276 * Get the Mach private port.
278 host_priv_port
= mach_host_self();
281 * Get the I/O Kit communication handle.
283 IOMasterPort(bootstrap_port
, &masterPort
);
285 notifyPort
= IONotificationPortCreate(masterPort
);
286 rls
= IONotificationPortGetRunLoopSource(notifyPort
);
287 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
290 * Make sure Tflag, Cflag and Uflag are set if dflag == 0. If dflag is
291 * greater than 0, they may be 0 or non-zero.
300 * TTY statistics are broken, disabling them.
305 * Figure out how many devices we should display if not given
310 if ((dflag
> 0) && (Cflag
== 0) && (Tflag
== 0))
312 else if ((dflag
> 0) && (Tflag
> 0) && (Cflag
== 0))
317 if ((dflag
> 0) && (Cflag
== 0))
325 * If the user specified any devices on the command line, record
326 * them for monitoring.
328 for (num_devices_specified
= 0; *argv
; ++argv
) {
331 if (record_one_device(*argv
))
332 errx(1, "can't record '%s' for monitoring");
333 num_devices_specified
++;
335 if (nflag
== 0 && maxshowdevs
< num_devices_specified
)
336 maxshowdevs
= num_devices_specified
;
338 /* if no devices were specified, pick them ourselves */
339 if ((num_devices_specified
== 0) && record_all_devices())
340 err(1, "can't find any devices to display");
343 * Look for the traditional wait time and count arguments.
346 waittime
= atoi(*argv
);
348 /* Let the user know he goofed, but keep going anyway */
350 warnx("discarding previous wait interval, using"
351 " %d instead", waittime
);
357 warnx("discarding previous count, using %d"
365 * If the user specified a count, but not an interval, we default
366 * to an interval of 1 second.
368 if ((wflag
== 0) && (cflag
> 0))
372 * If the user specified a wait time, but not a count, we want to
373 * go on ad infinitum. This can be redundant if the user uses the
374 * traditional method of specifying the wait, since in that case we
375 * already set count = -1 above. Oh well.
377 if ((wflag
> 0) && (cflag
== 0))
384 * Set the busy time to the system boot time, so the stats are
385 * calculated since system boot.
387 if (readvar("kern.boottime", &cur_time
, sizeof(cur_time
)) != 0)
391 * If the user stops the program (control-Z) and then resumes it,
392 * print out the header again.
394 (void)signal(SIGCONT
, phdr
);
396 for (headercount
= 1;;) {
401 if ((readvar("kern.tty_nin", &cur
.tk_nin
,
402 sizeof(cur
.tk_nin
)) != 0)
403 || (readvar("kern.tty_nout",
404 &cur
.tk_nout
, sizeof(cur
.tk_nout
))!= 0)) {
406 warnx("disabling TTY statistics");
410 if (!--headercount
|| phdr_flag
) {
416 last_time
= cur_time
;
417 gettimeofday(&cur_time
, NULL
);
421 cur
.tk_nin
-= last
.tk_nin
;
424 cur
.tk_nout
-= last
.tk_nout
;
428 etime
= compute_etime(cur_time
, last_time
);
434 printf("%4.0Lf%5.0Lf", cur
.tk_nin
/ etime
,
435 cur
.tk_nout
/ etime
);
437 devstats(hflag
, etime
, havelast
);
448 if (count
>= 0 && --count
<= 0)
452 * Instead of sleep(waittime), wait in
453 * the RunLoop for IONotifications.
455 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, (CFTimeInterval
)waittime
, 1);
476 (void)printf(" tty");
478 for (i
= 0; i
< num_devices
&& i
< maxshowdevs
; i
++){
480 (void)printf("%12.6s ", drivestat
[i
].name
);
482 printf("%15.6s ", drivestat
[i
].name
);
486 (void)printf(" cpu");
489 (void)printf(" load average\n");
494 (void)printf(" tin tout");
496 for (i
=0; i
< num_devices
&& i
< maxshowdevs
; i
++){
499 (void)printf(" sps tps msps ");
501 (void)printf(" blk xfr msps ");
504 printf(" KB/t tps MB/s ");
506 printf(" KB/t xfrs MB ");
511 (void)printf(" us sy id");
514 (void)printf(" 1m 5m 15m\n");
520 devstats(int perf_select
, long double etime
, int havelast
)
523 CFDictionaryRef properties
;
524 CFDictionaryRef statistics
;
525 long double transfers_per_second
;
526 long double kb_per_transfer
, mb_per_second
;
528 u_int64_t total_bytes
, total_transfers
, total_blocks
, total_time
;
529 u_int64_t interval_bytes
, interval_transfers
, interval_blocks
;
530 u_int64_t interval_time
;
531 long double interval_mb
;
532 long double blocks_per_second
, ms_per_transaction
;
533 kern_return_t status
;
536 for (i
= 0; i
< num_devices
&& i
< maxshowdevs
; i
++) {
539 * If the drive goes away, we may not get any properties
540 * for it. So take some defaults.
546 /* get drive properties */
547 status
= IORegistryEntryCreateCFProperties(drivestat
[i
].driver
,
548 (CFMutableDictionaryRef
*)&properties
,
551 if (status
!= KERN_SUCCESS
)
554 /* get statistics from properties */
555 statistics
= (CFDictionaryRef
)CFDictionaryGetValue(properties
,
556 CFSTR(kIOBlockStorageDriverStatisticsKey
));
562 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
563 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey
)))) {
564 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
565 total_bytes
+= value
;
567 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
568 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey
)))) {
569 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
570 total_bytes
+= value
;
576 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
577 CFSTR(kIOBlockStorageDriverStatisticsReadsKey
)))) {
578 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
579 total_transfers
+= value
;
581 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
582 CFSTR(kIOBlockStorageDriverStatisticsWritesKey
)))) {
583 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
584 total_transfers
+= value
;
590 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
591 CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey
)))) {
592 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
595 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
596 CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey
)))) {
597 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
602 CFRelease(properties
);
605 * Compute delta values and stats.
607 interval_bytes
= total_bytes
- drivestat
[i
].total_bytes
;
608 interval_transfers
= total_transfers
609 - drivestat
[i
].total_transfers
;
610 interval_time
= total_time
- drivestat
[i
].total_time
;
612 /* update running totals, only once for -I */
613 if ((Iflag
== 0) || (drivestat
[i
].total_bytes
== 0)) {
614 drivestat
[i
].total_bytes
= total_bytes
;
615 drivestat
[i
].total_transfers
= total_transfers
;
616 drivestat
[i
].total_time
= total_time
;
619 interval_blocks
= interval_bytes
/ drivestat
[i
].blocksize
;
620 total_blocks
= total_bytes
/ drivestat
[i
].blocksize
;
622 blocks_per_second
= interval_blocks
/ etime
;
623 transfers_per_second
= interval_transfers
/ etime
;
624 mb_per_second
= (interval_bytes
/ etime
) / (1024 * 1024);
626 kb_per_transfer
= (interval_transfers
> 0) ?
627 ((long double)interval_bytes
/ interval_transfers
)
630 /* times are in nanoseconds, convert to milliseconds */
631 ms_per_transaction
= (interval_transfers
> 0) ?
632 ((long double)interval_time
/ interval_transfers
)
636 total_blocks
= total_blocks
* drivestat
[i
].blocksize
640 int msdig
= (ms_per_transaction
< 100.0) ? 1 : 0;
643 printf("%4.0Lf%4.0Lf%5.*Lf ",
645 transfers_per_second
,
649 printf("%4.1qu%4.1qu%5.*Lf ",
656 printf(" %7.2Lf %3.0Lf %5.2Lf ",
658 transfers_per_second
,
661 interval_mb
= interval_bytes
;
662 interval_mb
/= 1024 * 1024;
664 printf(" %7.2Lf %3.1qu %5.2Lf ",
676 mach_msg_type_number_t count
;
677 kern_return_t status
;
681 * Get CPU usage counters.
683 count
= HOST_CPU_LOAD_INFO_COUNT
;
684 status
= host_statistics(host_priv_port
, HOST_CPU_LOAD_INFO
,
685 (host_info_t
)&cur
.load
, &count
);
686 if (status
!= KERN_SUCCESS
)
687 errx(1, "couldn't fetch CPU stats");
690 * Make 'cur' fields relative, update 'last' fields to current values,
691 * calculate total elapsed time.
694 cur
.load
.cpu_ticks
[CPU_STATE_USER
]
695 -= last
.load
.cpu_ticks
[CPU_STATE_USER
];
696 last
.load
.cpu_ticks
[CPU_STATE_USER
]
697 += cur
.load
.cpu_ticks
[CPU_STATE_USER
];
698 time
+= cur
.load
.cpu_ticks
[CPU_STATE_USER
];
699 cur
.load
.cpu_ticks
[CPU_STATE_SYSTEM
]
700 -= last
.load
.cpu_ticks
[CPU_STATE_SYSTEM
];
701 last
.load
.cpu_ticks
[CPU_STATE_SYSTEM
]
702 += cur
.load
.cpu_ticks
[CPU_STATE_SYSTEM
];
703 time
+= cur
.load
.cpu_ticks
[CPU_STATE_SYSTEM
];
704 cur
.load
.cpu_ticks
[CPU_STATE_IDLE
]
705 -= last
.load
.cpu_ticks
[CPU_STATE_IDLE
];
706 last
.load
.cpu_ticks
[CPU_STATE_IDLE
]
707 += cur
.load
.cpu_ticks
[CPU_STATE_IDLE
];
708 time
+= cur
.load
.cpu_ticks
[CPU_STATE_IDLE
];
713 #define PTIME(kind) { \
714 double cpu = rint(100. * cur.load.cpu_ticks[kind] / (time ? time : 1));\
715 printf("%*.0f", (100 == cpu) ? 4 : 3, cpu); \
717 PTIME(CPU_STATE_USER
);
718 PTIME(CPU_STATE_SYSTEM
);
719 PTIME(CPU_STATE_IDLE
);
727 if(getloadavg(loadavg
,3)!=3)
728 errx(1, "couldn't fetch load average");
730 printf(" %4.2f %4.2f %4.2f",loadavg
[0],loadavg
[1],loadavg
[2]);
734 readvar(const char *name
, void *ptr
, size_t len
)
741 if (sysctlbyname(name
, ptr
, &nlen
, NULL
, 0) == -1) {
742 if (errno
!= ENOENT
) {
743 warn("sysctl(%s) failed", name
);
747 * XXX fallback code to deal with systems where
748 * sysctlbyname can't find "old" OIDs, should be removed.
750 if (!strcmp(name
, "kern.boottime")) {
752 oid
[1] = KERN_BOOTTIME
;
755 warn("sysctl(%s) failed", name
);
760 if (sysctl(oid
, oidlen
, ptr
, &nlen
, NULL
, 0) == -1) {
761 warn("sysctl(%s) failed", name
);
766 warnx("sysctl(%s): expected %lu, got %lu", name
,
767 (unsigned long)len
, (unsigned long)nlen
);
774 compute_etime(struct timeval cur_time
, struct timeval prev_time
)
776 struct timeval busy_time
;
780 timersub(&cur_time
, &prev_time
, &busy_time
);
782 busy_usec
= busy_time
.tv_sec
;
783 busy_usec
*= 1000000;
784 busy_usec
+= busy_time
.tv_usec
;
792 * Record all "whole" IOMedia objects as being interesting.
795 record_all_devices(void)
797 io_iterator_t drivelist
;
798 CFMutableDictionaryRef match
;
799 kern_return_t status
;
802 * Get an iterator for IOMedia objects.
804 match
= IOServiceMatching("IOMedia");
805 CFDictionaryAddValue(match
, CFSTR(kIOMediaWholeKey
), kCFBooleanTrue
);
808 status
= IOServiceAddMatchingNotification(notifyPort
, kIOFirstMatchNotification
, match
, &record_drivelist
, NULL
, &drivelist
);
810 if (status
!= KERN_SUCCESS
)
811 errx(1, "couldn't match whole IOMedia devices");
814 * Scan all of the IOMedia objects, and for each
815 * object that has a parent IOBlockStorageDriver, save
816 * the object's name and the parent (from which we can
819 * XXX What about RAID devices?
822 record_drivelist(NULL
, drivelist
);
825 status
= IOServiceAddMatchingNotification(notifyPort
, kIOTerminatedNotification
, match
, &remove_drivelist
, NULL
, &drivelist
);
827 if (status
!= KERN_SUCCESS
)
828 errx(1, "couldn't match whole IOMedia device removal");
830 remove_drivelist(NULL
, drivelist
);
835 static void record_drivelist(void* context
, io_iterator_t drivelist
)
837 io_registry_entry_t drive
;
838 while ((drive
= IOIteratorNext(drivelist
))) {
839 if (num_devices
< MAXDRIVES
) {
840 record_device(drive
);
843 IOObjectRelease(drive
);
845 qsort(drivestat
, num_devices
, sizeof(struct drivestats
), &compare_drivestats
);
848 static void remove_drivelist(void* context
, io_iterator_t drivelist
)
850 io_registry_entry_t drive
;
851 while ((drive
= IOIteratorNext(drivelist
))) {
852 kern_return_t status
;
853 char bsdname
[MAXDRIVENAME
];
854 CFDictionaryRef properties
;
857 /* get drive properties */
858 status
= IORegistryEntryCreateCFProperties(drive
,
859 (CFMutableDictionaryRef
*)&properties
,
862 if (status
!= KERN_SUCCESS
) continue;
864 /* get name from properties */
865 name
= (CFStringRef
)CFDictionaryGetValue(properties
,
866 CFSTR(kIOBSDNameKey
));
868 if (name
&& CFStringGetCString(name
, bsdname
, MAXDRIVENAME
, CFStringGetSystemEncoding())) {
870 for (i
= 0; i
< num_devices
; ++i
) {
871 if (strcmp(bsdname
,drivestat
[i
].name
) == 0) {
872 if (i
< MAXDRIVES
-1) {
873 memmove(&drivestat
[i
], &drivestat
[i
+1], sizeof(struct drivestats
)*(MAXDRIVES
-i
));
877 qsort(drivestat
, num_devices
, sizeof(struct drivestats
), &compare_drivestats
);
882 CFRelease(properties
);
883 IOObjectRelease(drive
);
888 * Try to record the named device as interesting. It
889 * must be an IOMedia device.
892 record_one_device(char *name
)
894 io_iterator_t drivelist
;
895 io_registry_entry_t drive
;
896 kern_return_t status
;
901 status
= IOServiceGetMatchingServices(masterPort
,
902 IOBSDNameMatching(masterPort
, kNilOptions
, name
),
904 if (status
!= KERN_SUCCESS
)
905 errx(1, "couldn't match '%s'", name
);
908 * Get the first match (should only be one)
910 if (!(drive
= IOIteratorNext(drivelist
)))
911 errx(1, "'%s' not found", name
);
912 if (!IOObjectConformsTo(drive
, "IOMedia"))
913 errx(1, "'%s' is not a storage device", name
);
918 if (record_device(drive
))
919 errx(1, "could not record '%s' for monitoring", name
);
921 IOObjectRelease(drive
);
922 IOObjectRelease(drivelist
);
928 * Determine whether an IORegistryEntry refers to a valid
929 * I/O device, and if so, record it.
932 record_device(io_registry_entry_t drive
)
934 io_registry_entry_t parent
;
935 CFDictionaryRef properties
;
938 kern_return_t status
;
940 /* get drive's parent */
941 status
= IORegistryEntryGetParentEntry(drive
,
942 kIOServicePlane
, &parent
);
943 if (status
!= KERN_SUCCESS
)
944 errx(1, "device has no parent");
945 if (IOObjectConformsTo(parent
, "IOBlockStorageDriver")) {
946 drivestat
[num_devices
].driver
= parent
;
948 /* get drive properties */
949 status
= IORegistryEntryCreateCFProperties(drive
,
950 (CFMutableDictionaryRef
*)&properties
,
953 if (status
!= KERN_SUCCESS
)
954 errx(1, "device has no properties");
956 /* get name from properties */
957 name
= (CFStringRef
)CFDictionaryGetValue(properties
,
958 CFSTR(kIOBSDNameKey
));
960 CFStringGetCString(name
, drivestat
[num_devices
].name
,
961 MAXDRIVENAME
, CFStringGetSystemEncoding());
963 errx(1, "device does not have a BSD name");
966 /* get blocksize from properties */
967 number
= (CFNumberRef
)CFDictionaryGetValue(properties
,
968 CFSTR(kIOMediaPreferredBlockSizeKey
));
970 CFNumberGetValue(number
, kCFNumberSInt64Type
,
971 &drivestat
[num_devices
].blocksize
);
973 errx(1, "device does not have a preferred block size");
975 /* clean up, return success */
976 CFRelease(properties
);
981 /* failed, don't keep parent */
982 IOObjectRelease(parent
);
987 compare_drivestats(const void* pa
, const void* pb
)
989 struct drivestats
* a
= (struct drivestats
*)pa
;
990 struct drivestats
* b
= (struct drivestats
*)pb
;
991 return strcmp(a
->name
, b
->name
);