2 * Copyright (c) 1999-2019 Apple 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 * SPDX-License-Identifier: BSD-3-Clause
27 * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry
28 * All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. The name of the author may not be used to endorse or promote products
39 * derived from this software without specific prior written permission.
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * Parts of this program are derived from the original FreeBSD iostat
60 * Copyright (c) 1986, 1991, 1993
61 * The Regents of the University of California. All rights reserved.
63 * Redistribution and use in source and binary forms, with or without
64 * modification, are permitted provided that the following conditions
66 * 1. Redistributions of source code must retain the above copyright
67 * notice, this list of conditions and the following disclaimer.
68 * 2. Redistributions in binary form must reproduce the above copyright
69 * notice, this list of conditions and the following disclaimer in the
70 * documentation and/or other materials provided with the distribution.
71 * 4. Neither the name of the University nor the names of its contributors
72 * may be used to endorse or promote products derived from this software
73 * without specific prior written permission.
75 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
76 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
77 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
78 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
79 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
80 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
81 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
82 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
83 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
84 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
88 * Ideas for the new iostat statistics output modes taken from the NetBSD
92 * Copyright (c) 1996 John M. Vinopal
93 * All rights reserved.
95 * Redistribution and use in source and binary forms, with or without
96 * modification, are permitted provided that the following conditions
98 * 1. Redistributions of source code must retain the above copyright
99 * notice, this list of conditions and the following disclaimer.
100 * 2. Redistributions in binary form must reproduce the above copyright
101 * notice, this list of conditions and the following disclaimer in the
102 * documentation and/or other materials provided with the distribution.
103 * 3. All advertising materials mentioning features or use of this software
104 * must display the following acknowledgement:
105 * This product includes software developed for the NetBSD Project
106 * by John M. Vinopal.
107 * 4. The name of the author may not be used to endorse or promote products
108 * derived from this software without specific prior written permission.
110 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
111 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
112 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
113 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
114 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
115 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
116 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
117 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
118 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
119 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
123 #define IOKIT 1 /* to get io_name_t in device_types.h */
125 #include <sys/param.h>
126 #include <sys/sysctl.h>
128 #include <CoreFoundation/CoreFoundation.h>
129 #include <IOKit/IOKitLib.h>
130 #include <IOKit/storage/IOBlockStorageDriver.h>
131 #include <IOKit/storage/IOMedia.h>
132 #include <IOKit/IOBSD.h>
133 #include <mach/mach_host.h> /* host_statistics */
141 #define MAXDRIVES 16 /* most drives we will record */
142 #define MAXDRIVENAME 31 /* largest drive name we allow */
145 io_registry_entry_t driver
;
146 char name
[MAXDRIVENAME
+ 1];
148 u_int64_t total_bytes
;
149 u_int64_t total_transfers
;
150 u_int64_t total_time
;
153 static struct drivestats drivestat
[MAXDRIVES
];
155 static struct timeval cur_time
, last_time
;
160 host_cpu_load_info_data_t load
;
163 static struct statinfo cur
, last
;
165 static mach_port_t host_priv_port
;
166 static mach_port_t masterPort
;
168 static int num_devices
;
169 static int maxshowdevs
;
170 static int dflag
= 0, Iflag
= 0, Cflag
= 0, Tflag
= 0, oflag
= 0, Uflag
= 0, Kflag
= 0;
171 static volatile sig_atomic_t phdr_flag
= 0;
172 static IONotificationPortRef notifyPort
;
174 /* local function declarations */
175 static void usage(void);
176 static void phdr(int signo
);
177 static void do_phdr(void);
178 static void devstats(int perf_select
, long double etime
, int havelast
);
179 static void cpustats(void);
180 static void loadstats(void);
181 static int readvar(const char *name
, void *ptr
, size_t len
);
183 static int record_all_devices(void);
184 static void record_drivelist(void* context
, io_iterator_t drivelist
);
185 static void remove_drivelist(void* context
, io_iterator_t drivelist
);
186 static int record_one_device(char *name
);
187 static int record_device(io_registry_entry_t drive
);
189 static int compare_drivestats(const void* pa
, const void* pb
);
191 static long double compute_etime(struct timeval cur_time
,
192 struct timeval prev_time
);
198 * We also support the following 'traditional' syntax:
199 * iostat [drives] [wait [count]]
200 * This isn't mentioned in the man page, or the usage statement,
201 * but it is supported.
203 fprintf(stderr
, "usage: iostat [-CUdIKoT?] [-c count] [-n devs]\n"
204 "\t [-w wait] [drives]\n");
208 main(int argc
, char **argv
)
211 int hflag
= 0, cflag
= 0, wflag
= 0, nflag
= 0;
212 int count
= 0, waittime
= 0;
214 int num_devices_specified
;
217 CFRunLoopSourceRef rls
;
221 while ((c
= getopt(argc
, argv
, "c:CdIKM:n:oTUw:?")) != -1) {
225 count
= atoi(optarg
);
227 errx(1, "count %d is < 1", count
);
243 maxshowdevs
= atoi(optarg
);
245 errx(1, "number of devices %d is < 0",
259 waittime
= atoi(optarg
);
261 errx(1, "wait time is < 1");
274 * Get the Mach private port.
276 host_priv_port
= mach_host_self();
279 * Get the I/O Kit communication handle.
281 IOMasterPort(bootstrap_port
, &masterPort
);
283 notifyPort
= IONotificationPortCreate(masterPort
);
284 rls
= IONotificationPortGetRunLoopSource(notifyPort
);
285 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
288 * Make sure Tflag, Cflag and Uflag are set if dflag == 0. If dflag is
289 * greater than 0, they may be 0 or non-zero.
298 * TTY statistics are broken, disabling them.
303 * Figure out how many devices we should display if not given
308 if ((dflag
> 0) && (Cflag
== 0) && (Tflag
== 0))
310 else if ((dflag
> 0) && (Tflag
> 0) && (Cflag
== 0))
315 if ((dflag
> 0) && (Cflag
== 0))
323 * If the user specified any devices on the command line, record
324 * them for monitoring.
326 for (num_devices_specified
= 0; *argv
; ++argv
) {
329 if (record_one_device(*argv
))
330 errx(1, "can't record '%s' for monitoring", *argv
);
331 num_devices_specified
++;
333 if (nflag
== 0 && maxshowdevs
< num_devices_specified
)
334 maxshowdevs
= num_devices_specified
;
336 /* if no devices were specified, pick them ourselves */
337 if ((num_devices_specified
== 0) && record_all_devices())
338 err(1, "can't find any devices to display");
341 * Look for the traditional wait time and count arguments.
344 waittime
= atoi(*argv
);
346 /* Let the user know he goofed, but keep going anyway */
348 warnx("discarding previous wait interval, using"
349 " %d instead", waittime
);
355 warnx("discarding previous count, using %d"
363 * If the user specified a count, but not an interval, we default
364 * to an interval of 1 second.
366 if ((wflag
== 0) && (cflag
> 0))
370 * If the user specified a wait time, but not a count, we want to
371 * go on ad infinitum. This can be redundant if the user uses the
372 * traditional method of specifying the wait, since in that case we
373 * already set count = -1 above. Oh well.
375 if ((wflag
> 0) && (cflag
== 0))
382 * Set the busy time to the system boot time, so the stats are
383 * calculated since system boot.
385 if (readvar("kern.boottime", &cur_time
, sizeof(cur_time
)) != 0)
389 * If the user stops the program (control-Z) and then resumes it,
390 * print out the header again.
392 (void)signal(SIGCONT
, phdr
);
394 for (headercount
= 1;;) {
399 if ((readvar("kern.tty_nin", &cur
.tk_nin
,
400 sizeof(cur
.tk_nin
)) != 0)
401 || (readvar("kern.tty_nout",
402 &cur
.tk_nout
, sizeof(cur
.tk_nout
))!= 0)) {
404 warnx("disabling TTY statistics");
408 if (!--headercount
|| phdr_flag
) {
414 last_time
= cur_time
;
415 gettimeofday(&cur_time
, NULL
);
419 cur
.tk_nin
-= last
.tk_nin
;
422 cur
.tk_nout
-= last
.tk_nout
;
426 etime
= compute_etime(cur_time
, last_time
);
432 printf("%4.0Lf%5.0Lf", cur
.tk_nin
/ etime
,
433 cur
.tk_nout
/ etime
);
435 devstats(hflag
, etime
, havelast
);
446 if (count
>= 0 && --count
<= 0)
450 * Instead of sleep(waittime), wait in
451 * the RunLoop for IONotifications.
453 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, (CFTimeInterval
)waittime
, 1);
474 (void)printf(" tty");
476 for (i
= 0; i
< num_devices
&& i
< maxshowdevs
; i
++){
478 (void)printf("%12.6s ", drivestat
[i
].name
);
480 printf("%19.6s ", drivestat
[i
].name
);
484 (void)printf(" cpu");
487 (void)printf(" load average\n");
492 (void)printf(" tin tout");
494 for (i
=0; i
< num_devices
&& i
< maxshowdevs
; i
++){
497 (void)printf(" sps tps msps ");
499 (void)printf(" blk xfr msps ");
502 printf(" KB/t tps MB/s ");
504 printf(" KB/t xfrs MB ");
509 (void)printf(" us sy id");
512 (void)printf(" 1m 5m 15m\n");
518 devstats(int perf_select
, long double etime
, int havelast
)
521 CFDictionaryRef properties
;
522 CFDictionaryRef statistics
;
523 long double transfers_per_second
;
524 long double kb_per_transfer
, mb_per_second
;
526 u_int64_t total_bytes
, total_transfers
, total_blocks
, total_time
;
527 u_int64_t interval_bytes
, interval_transfers
, interval_blocks
;
528 u_int64_t interval_time
;
529 long double interval_mb
;
530 long double blocks_per_second
, ms_per_transaction
;
531 kern_return_t status
;
534 for (i
= 0; i
< num_devices
&& i
< maxshowdevs
; i
++) {
537 * If the drive goes away, we may not get any properties
538 * for it. So take some defaults.
544 /* get drive properties */
545 status
= IORegistryEntryCreateCFProperties(drivestat
[i
].driver
,
546 (CFMutableDictionaryRef
*)&properties
,
549 if (status
!= KERN_SUCCESS
)
552 /* get statistics from properties */
553 statistics
= (CFDictionaryRef
)CFDictionaryGetValue(properties
,
554 CFSTR(kIOBlockStorageDriverStatisticsKey
));
560 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
561 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey
)))) {
562 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
563 total_bytes
+= value
;
565 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
566 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey
)))) {
567 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
568 total_bytes
+= value
;
574 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
575 CFSTR(kIOBlockStorageDriverStatisticsReadsKey
)))) {
576 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
577 total_transfers
+= value
;
579 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
580 CFSTR(kIOBlockStorageDriverStatisticsWritesKey
)))) {
581 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
582 total_transfers
+= value
;
588 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
589 CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey
)))) {
590 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
593 if ((number
= (CFNumberRef
)CFDictionaryGetValue(statistics
,
594 CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey
)))) {
595 CFNumberGetValue(number
, kCFNumberSInt64Type
, &value
);
600 CFRelease(properties
);
603 * Compute delta values and stats.
605 interval_bytes
= total_bytes
- drivestat
[i
].total_bytes
;
606 interval_transfers
= total_transfers
607 - drivestat
[i
].total_transfers
;
608 interval_time
= total_time
- drivestat
[i
].total_time
;
610 /* update running totals, only once for -I */
611 if ((Iflag
== 0) || (drivestat
[i
].total_bytes
== 0)) {
612 drivestat
[i
].total_bytes
= total_bytes
;
613 drivestat
[i
].total_transfers
= total_transfers
;
614 drivestat
[i
].total_time
= total_time
;
617 interval_blocks
= interval_bytes
/ drivestat
[i
].blocksize
;
618 total_blocks
= total_bytes
/ drivestat
[i
].blocksize
;
620 blocks_per_second
= interval_blocks
/ etime
;
621 transfers_per_second
= interval_transfers
/ etime
;
622 mb_per_second
= (interval_bytes
/ etime
) / (1024 * 1024);
624 kb_per_transfer
= (interval_transfers
> 0) ?
625 ((long double)interval_bytes
/ interval_transfers
)
628 /* times are in nanoseconds, convert to milliseconds */
629 ms_per_transaction
= (interval_transfers
> 0) ?
630 ((long double)interval_time
/ interval_transfers
)
634 total_blocks
= total_blocks
* drivestat
[i
].blocksize
638 int msdig
= (ms_per_transaction
< 100.0) ? 1 : 0;
641 printf("%4.0Lf%4.0Lf%5.*Lf ",
643 transfers_per_second
,
647 printf("%4.1qu%4.1qu%5.*Lf ",
654 printf(" %7.2Lf %4.0Lf %5.2Lf ",
656 transfers_per_second
,
659 interval_mb
= interval_bytes
;
660 interval_mb
/= 1024 * 1024;
662 printf(" %7.2Lf %3.1qu %5.2Lf ",
674 mach_msg_type_number_t count
;
675 kern_return_t status
;
679 * Get CPU usage counters.
681 count
= HOST_CPU_LOAD_INFO_COUNT
;
682 status
= host_statistics(host_priv_port
, HOST_CPU_LOAD_INFO
,
683 (host_info_t
)&cur
.load
, &count
);
684 if (status
!= KERN_SUCCESS
)
685 errx(1, "couldn't fetch CPU stats");
688 * Make 'cur' fields relative, update 'last' fields to current values,
689 * calculate total elapsed time.
692 cur
.load
.cpu_ticks
[CPU_STATE_USER
]
693 -= last
.load
.cpu_ticks
[CPU_STATE_USER
];
694 last
.load
.cpu_ticks
[CPU_STATE_USER
]
695 += cur
.load
.cpu_ticks
[CPU_STATE_USER
];
696 time
+= cur
.load
.cpu_ticks
[CPU_STATE_USER
];
697 cur
.load
.cpu_ticks
[CPU_STATE_SYSTEM
]
698 -= last
.load
.cpu_ticks
[CPU_STATE_SYSTEM
];
699 last
.load
.cpu_ticks
[CPU_STATE_SYSTEM
]
700 += cur
.load
.cpu_ticks
[CPU_STATE_SYSTEM
];
701 time
+= cur
.load
.cpu_ticks
[CPU_STATE_SYSTEM
];
702 cur
.load
.cpu_ticks
[CPU_STATE_IDLE
]
703 -= last
.load
.cpu_ticks
[CPU_STATE_IDLE
];
704 last
.load
.cpu_ticks
[CPU_STATE_IDLE
]
705 += cur
.load
.cpu_ticks
[CPU_STATE_IDLE
];
706 time
+= cur
.load
.cpu_ticks
[CPU_STATE_IDLE
];
711 #define PTIME(kind) { \
712 double cpu = rint(100. * cur.load.cpu_ticks[kind] / (time ? time : 1));\
713 printf("%*.0f", (100 == cpu) ? 4 : 3, cpu); \
715 PTIME(CPU_STATE_USER
);
716 PTIME(CPU_STATE_SYSTEM
);
717 PTIME(CPU_STATE_IDLE
);
725 if(getloadavg(loadavg
,3)!=3)
726 errx(1, "couldn't fetch load average");
728 printf(" %4.2f %4.2f %4.2f",loadavg
[0],loadavg
[1],loadavg
[2]);
732 readvar(const char *name
, void *ptr
, size_t len
)
739 if (sysctlbyname(name
, ptr
, &nlen
, NULL
, 0) == -1) {
740 if (errno
!= ENOENT
) {
741 warn("sysctl(%s) failed", name
);
745 * XXX fallback code to deal with systems where
746 * sysctlbyname can't find "old" OIDs, should be removed.
748 if (!strcmp(name
, "kern.boottime")) {
750 oid
[1] = KERN_BOOTTIME
;
753 warn("sysctl(%s) failed", name
);
758 if (sysctl(oid
, oidlen
, ptr
, &nlen
, NULL
, 0) == -1) {
759 warn("sysctl(%s) failed", name
);
764 warnx("sysctl(%s): expected %lu, got %lu", name
,
765 (unsigned long)len
, (unsigned long)nlen
);
772 compute_etime(struct timeval cur_time
, struct timeval prev_time
)
774 struct timeval busy_time
;
778 timersub(&cur_time
, &prev_time
, &busy_time
);
780 busy_usec
= busy_time
.tv_sec
;
781 busy_usec
*= 1000000;
782 busy_usec
+= busy_time
.tv_usec
;
790 * Record all "whole" IOMedia objects as being interesting.
793 record_all_devices(void)
795 io_iterator_t drivelist
;
796 CFMutableDictionaryRef match
;
797 kern_return_t status
;
800 * Get an iterator for IOMedia objects.
802 match
= IOServiceMatching("IOMedia");
803 CFDictionaryAddValue(match
, CFSTR(kIOMediaWholeKey
), kCFBooleanTrue
);
806 status
= IOServiceAddMatchingNotification(notifyPort
, kIOFirstMatchNotification
, match
, &record_drivelist
, NULL
, &drivelist
);
808 if (status
!= KERN_SUCCESS
)
809 errx(1, "couldn't match whole IOMedia devices");
812 * Scan all of the IOMedia objects, and for each
813 * object that has a parent IOBlockStorageDriver, save
814 * the object's name and the parent (from which we can
817 * XXX What about RAID devices?
820 record_drivelist(NULL
, drivelist
);
823 status
= IOServiceAddMatchingNotification(notifyPort
, kIOTerminatedNotification
, match
, &remove_drivelist
, NULL
, &drivelist
);
825 if (status
!= KERN_SUCCESS
)
826 errx(1, "couldn't match whole IOMedia device removal");
828 remove_drivelist(NULL
, drivelist
);
834 record_drivelist(void* context
, io_iterator_t drivelist
)
836 io_registry_entry_t drive
;
837 while ((drive
= IOIteratorNext(drivelist
))) {
838 if (num_devices
< MAXDRIVES
) {
839 record_device(drive
);
842 IOObjectRelease(drive
);
844 qsort(drivestat
, num_devices
, sizeof(struct drivestats
), &compare_drivestats
);
848 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
, kCFStringEncodingUTF8
)) {
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
, kCFStringEncodingUTF8
);
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");
976 if (drivestat
[num_devices
].blocksize
== 0) {
977 warnx("%s claims a blocksize of 0; defaulting to 512. Its statistics may be inaccurate.", drivestat
[num_devices
].name
);
978 drivestat
[num_devices
].blocksize
= 512;
981 /* clean up, return success */
982 CFRelease(properties
);
987 /* failed, don't keep parent */
988 IOObjectRelease(parent
);
993 compare_drivestats(const void* pa
, const void* pb
)
995 struct drivestats
* a
= (struct drivestats
*)pa
;
996 struct drivestats
* b
= (struct drivestats
*)pb
;
997 return strcmp(a
->name
, b
->name
);