1/*
2 * Copyright (c) 1999 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 * Copyright (c) 1997, 1998, 2000, 2001  Kenneth D. Merry
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
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.
38 *
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
49 * SUCH DAMAGE.
50 *
51 * $FreeBSD: src/usr.sbin/iostat/iostat.c,v 1.22 2001/09/01 07:40:19 kris Exp $
52 */
53/*
54 * Parts of this program are derived from the original FreeBSD iostat
55 * program:
56 */
57/*-
58 * Copyright (c) 1986, 1991, 1993
59 *	The Regents of the University of California.  All rights reserved.
60 *
61 * Redistribution and use in source and binary forms, with or without
62 * modification, are permitted provided that the following conditions
63 * are met:
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.
76 *
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
87 * SUCH DAMAGE.
88 */
89/*
90 * Ideas for the new iostat statistics output modes taken from the NetBSD
91 * version of iostat:
92 */
93/*
94 * Copyright (c) 1996 John M. Vinopal
95 * All rights reserved.
96 *
97 * Redistribution and use in source and binary forms, with or without
98 * modification, are permitted provided that the following conditions
99 * are met:
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.
111 *
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
122 * SUCH DAMAGE.
123 */
124
125#define IOKIT	1	/* to get io_name_t in device_types.h */
126
127#include <sys/param.h>
128#include <sys/sysctl.h>
129
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 */
136#include <err.h>
137#include <signal.h>
138#include <stdio.h>
139#include <stdlib.h>
140#include <string.h>
141#include <unistd.h>
142
143#define MAXDRIVES	16	/* most drives we will record */
144#define MAXDRIVENAME	31	/* largest drive name we allow */
145
146struct drivestats {
147	io_registry_entry_t	driver;
148	char			name[MAXDRIVENAME + 1];
149	u_int64_t		blocksize;
150	u_int64_t		total_bytes;
151	u_int64_t		total_transfers;
152	u_int64_t		total_time;
153};
154
155static struct drivestats drivestat[MAXDRIVES];
156
157static struct timeval	cur_time, last_time;
158
159struct statinfo {
160	long		tk_nin;
161	long		tk_nout;
162	host_cpu_load_info_data_t load;
163};
164
165static struct statinfo cur, last;
166
167static mach_port_t host_priv_port;
168static mach_port_t masterPort;
169
170static int num_devices;
171static int maxshowdevs;
172static int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Uflag = 0, Kflag = 0;
173static volatile sig_atomic_t phdr_flag = 0;
174static IONotificationPortRef notifyPort;
175
176/* local function declarations */
177static void usage(void);
178static void phdr(int signo);
179static void do_phdr();
180static void devstats(int perf_select, long double etime, int havelast);
181static void cpustats(void);
182static void loadstats(void);
183static int readvar(const char *name, void *ptr, size_t len);
184
185static int record_all_devices(void);
186static void record_drivelist(void* context, io_iterator_t drivelist);
187static void remove_drivelist(void* context, io_iterator_t drivelist);
188static int record_one_device(char *name);
189static int record_device(io_registry_entry_t drive);
190
191static int compare_drivestats(const void* pa, const void* pb);
192
193static long double compute_etime(struct timeval cur_time,
194				 struct timeval prev_time);
195
196static void
197usage(void)
198{
199	/*
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.
204	 */
205	fprintf(stderr, "usage: iostat [-CUdIKoT?] [-c count] [-n devs]\n"
206		"\t      [-w wait] [drives]\n");
207}
208
209int
210main(int argc, char **argv)
211{
212	int c;
213	int hflag = 0, cflag = 0, wflag = 0, nflag = 0;
214	int count = 0, waittime = 0;
215	int headercount;
216	int num_devices_specified;
217	int havelast = 0;
218
219	CFRunLoopSourceRef rls;
220
221	maxshowdevs = 3;
222
223	while ((c = getopt(argc, argv, "c:CdIKM:n:oTUw:?")) != -1) {
224		switch(c) {
225			case 'c':
226				cflag++;
227				count = atoi(optarg);
228				if (count < 1)
229					errx(1, "count %d is < 1", count);
230				break;
231			case 'C':
232				Cflag++;
233				break;
234			case 'd':
235				dflag++;
236				break;
237			case 'I':
238				Iflag++;
239				break;
240			case 'K':
241				Kflag++;
242				break;
243			case 'n':
244				nflag++;
245				maxshowdevs = atoi(optarg);
246				if (maxshowdevs < 0)
247					errx(1, "number of devices %d is < 0",
248					     maxshowdevs);
249				break;
250			case 'o':
251				oflag++;
252				break;
253			case 'T':
254				Tflag++;
255				break;
256			case 'U':
257				Uflag++;
258				break;
259			case 'w':
260				wflag++;
261				waittime = atoi(optarg);
262				if (waittime < 1)
263					errx(1, "wait time is < 1");
264				break;
265			default:
266				usage();
267				exit(1);
268				break;
269		}
270	}
271
272	argc -= optind;
273	argv += optind;
274
275	/*
276	 * Get the Mach private port.
277	 */
278	host_priv_port = mach_host_self();
279
280	/*
281	 * Get the I/O Kit communication handle.
282	 */
283	IOMasterPort(bootstrap_port, &masterPort);
284
285	notifyPort = IONotificationPortCreate(masterPort);
286	rls = IONotificationPortGetRunLoopSource(notifyPort);
287	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
288
289	/*
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.
292	 */
293	if (dflag == 0) {
294		Cflag = 1;
295		Tflag = 1;
296		Uflag = 1;
297	}
298
299	/*
300	 * TTY statistics are broken, disabling them.
301	 */
302	Tflag = 0;
303
304	/*
305	 * Figure out how many devices we should display if not given
306	 * an explicit value.
307	 */
308	if (nflag == 0) {
309		if (oflag > 0) {
310			if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
311				maxshowdevs = 5;
312			else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
313				maxshowdevs = 5;
314			else
315				maxshowdevs = 4;
316		} else {
317			if ((dflag > 0) && (Cflag == 0))
318				maxshowdevs = 4;
319			else
320				maxshowdevs = 3;
321		}
322	}
323
324	/*
325	 * If the user specified any devices on the command line, record
326	 * them for monitoring.
327	 */
328	for (num_devices_specified = 0; *argv; ++argv) {
329		if (isdigit(**argv))
330			break;
331		if (record_one_device(*argv))
332			errx(1, "can't record '%s' for monitoring", *argv);
333		num_devices_specified++;
334	}
335	if (nflag == 0 && maxshowdevs < num_devices_specified)
336		maxshowdevs = num_devices_specified;
337
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");
341
342	/*
343	 * Look for the traditional wait time and count arguments.
344	 */
345	if (*argv) {
346		waittime = atoi(*argv);
347
348		/* Let the user know he goofed, but keep going anyway */
349		if (wflag != 0)
350			warnx("discarding previous wait interval, using"
351			      " %d instead", waittime);
352		wflag++;
353
354		if (*++argv) {
355			count = atoi(*argv);
356			if (cflag != 0)
357				warnx("discarding previous count, using %d"
358				      " instead", count);
359			cflag++;
360		} else
361			count = -1;
362	}
363
364	/*
365	 * If the user specified a count, but not an interval, we default
366	 * to an interval of 1 second.
367	 */
368	if ((wflag == 0) && (cflag > 0))
369		waittime = 1;
370
371	/*
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.
376	 */
377	if ((wflag > 0) && (cflag == 0))
378		count = -1;
379
380	cur.tk_nout = 0;
381	cur.tk_nin = 0;
382
383	/*
384	 * Set the busy time to the system boot time, so the stats are
385	 * calculated since system boot.
386	 */
387	if (readvar("kern.boottime", &cur_time,	sizeof(cur_time)) != 0)
388		exit(1);
389
390	/*
391	 * If the user stops the program (control-Z) and then resumes it,
392	 * print out the header again.
393	 */
394	(void)signal(SIGCONT, phdr);
395
396	for (headercount = 1;;) {
397		long tmp;
398		long double etime;
399
400		if (Tflag > 0) {
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)) {
405				Tflag = 0;
406				warnx("disabling TTY statistics");
407			}
408		 }
409
410		if (!--headercount || phdr_flag) {
411			phdr_flag = 0;
412			headercount = 20;
413			do_phdr();
414		}
415
416		last_time = cur_time;
417		gettimeofday(&cur_time, NULL);
418
419		if (Tflag > 0) {
420			tmp = cur.tk_nin;
421			cur.tk_nin -= last.tk_nin;
422			last.tk_nin = tmp;
423			tmp = cur.tk_nout;
424			cur.tk_nout -= last.tk_nout;
425			last.tk_nout = tmp;
426		}
427
428		etime = compute_etime(cur_time, last_time);
429
430		if (etime == 0.0)
431			etime = 1.0;
432
433		if (Tflag > 0)
434			printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
435				cur.tk_nout / etime);
436
437		devstats(hflag, etime, havelast);
438
439		if (Cflag > 0)
440			cpustats();
441
442		if (Uflag > 0)
443			loadstats();
444
445		printf("\n");
446		fflush(stdout);
447
448		if (count >= 0 && --count <= 0)
449			break;
450
451		/*
452		 * Instead of sleep(waittime), wait in
453		 * the RunLoop for IONotifications.
454		 */
455		CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)waittime, 1);
456
457		havelast = 1;
458	}
459
460	exit(0);
461}
462
463static void
464phdr(int signo)
465{
466
467	phdr_flag = 1;
468}
469
470static void
471do_phdr()
472{
473	register int i;
474
475	if (Tflag > 0)
476		(void)printf("      tty");
477
478	for (i = 0; i < num_devices && i < maxshowdevs; i++){
479		if (oflag > 0)
480			(void)printf("%12.6s ", drivestat[i].name);
481		else
482			printf("%15.6s ", drivestat[i].name);
483	}
484
485	if (Cflag > 0)
486		(void)printf("      cpu");
487
488	if (Uflag > 0)
489		(void)printf("     load average\n");
490	else
491		(void)printf("\n");
492
493	if (Tflag > 0)
494		(void)printf(" tin tout");
495
496	for (i=0; i < num_devices && i < maxshowdevs; i++){
497		if (oflag > 0) {
498			if (Iflag == 0)
499				(void)printf(" sps tps msps ");
500			else
501				(void)printf(" blk xfr msps ");
502		} else {
503			if (Iflag == 0)
504				printf("    KB/t tps  MB/s ");
505			else
506				printf("    KB/t xfrs   MB ");
507		}
508	}
509
510	if (Cflag > 0)
511		(void)printf(" us sy id");
512
513	if (Uflag > 0)
514		(void)printf("   1m   5m   15m\n");
515	else
516		printf("\n");
517}
518
519static void
520devstats(int perf_select, long double etime, int havelast)
521{
522	CFNumberRef number;
523	CFDictionaryRef properties;
524	CFDictionaryRef statistics;
525	long double transfers_per_second;
526	long double kb_per_transfer, mb_per_second;
527	u_int64_t value;
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;
534	int i;
535
536	for (i = 0; i < num_devices && i < maxshowdevs; i++) {
537
538		/*
539		 * If the drive goes away, we may not get any properties
540		 * for it.  So take some defaults.
541		 */
542		total_bytes = 0;
543		total_transfers = 0;
544		total_time = 0;
545
546		/* get drive properties */
547		status = IORegistryEntryCreateCFProperties(drivestat[i].driver,
548			(CFMutableDictionaryRef *)&properties,
549			kCFAllocatorDefault,
550			kNilOptions);
551		if (status != KERN_SUCCESS)
552			continue;
553
554		/* get statistics from properties */
555		statistics = (CFDictionaryRef)CFDictionaryGetValue(properties,
556			CFSTR(kIOBlockStorageDriverStatisticsKey));
557		if (statistics) {
558
559			/*
560			 * Get I/O volume.
561			 */
562			if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
563				CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
564				CFNumberGetValue(number, kCFNumberSInt64Type, &value);
565				total_bytes += value;
566			}
567			if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
568				CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
569				CFNumberGetValue(number, kCFNumberSInt64Type, &value);
570				total_bytes += value;
571			}
572
573			/*
574			 * Get I/O counts.
575			 */
576			if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
577				CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
578				CFNumberGetValue(number, kCFNumberSInt64Type, &value);
579				total_transfers += value;
580			}
581			if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
582				CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
583				CFNumberGetValue(number, kCFNumberSInt64Type, &value);
584				total_transfers += value;
585			}
586
587			/*
588			 * Get I/O time.
589			 */
590			if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
591				CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
592				CFNumberGetValue(number, kCFNumberSInt64Type, &value);
593				total_time += value;
594			}
595			if ((number = (CFNumberRef)CFDictionaryGetValue(statistics,
596				CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
597				CFNumberGetValue(number, kCFNumberSInt64Type, &value);
598				total_time += value;
599			}
600
601		}
602		CFRelease(properties);
603
604		/*
605		 * Compute delta values and stats.
606		 */
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;
611
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;
617		}
618
619		interval_blocks = interval_bytes / drivestat[i].blocksize;
620		total_blocks = total_bytes / drivestat[i].blocksize;
621
622		blocks_per_second = interval_blocks / etime;
623		transfers_per_second = interval_transfers / etime;
624		mb_per_second = (interval_bytes / etime) / (1024 * 1024);
625
626		kb_per_transfer = (interval_transfers > 0) ?
627			((long double)interval_bytes / interval_transfers)
628			/ 1024 : 0;
629
630		/* times are in nanoseconds, convert to milliseconds */
631		ms_per_transaction = (interval_transfers > 0) ?
632			((long double)interval_time / interval_transfers)
633			/ 1000 : 0;
634
635		if (Kflag)
636			total_blocks = total_blocks * drivestat[i].blocksize
637				/ 1024;
638
639		if (oflag > 0) {
640			int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
641
642			if (Iflag == 0)
643				printf("%4.0Lf%4.0Lf%5.*Lf ",
644				       blocks_per_second,
645				       transfers_per_second,
646				       msdig,
647				       ms_per_transaction);
648			else
649				printf("%4.1qu%4.1qu%5.*Lf ",
650				       interval_blocks,
651				       interval_transfers,
652				       msdig,
653				       ms_per_transaction);
654		} else {
655			if (Iflag == 0)
656				printf(" %7.2Lf %3.0Lf %5.2Lf ",
657				       kb_per_transfer,
658				       transfers_per_second,
659				       mb_per_second);
660			else {
661				interval_mb = interval_bytes;
662				interval_mb /= 1024 * 1024;
663
664				printf(" %7.2Lf %3.1qu %5.2Lf ",
665				       kb_per_transfer,
666				       interval_transfers,
667				       interval_mb);
668			}
669		}
670	}
671}
672
673static void
674cpustats(void)
675{
676	mach_msg_type_number_t count;
677	kern_return_t status;
678	double time;
679
680	/*
681	 * Get CPU usage counters.
682	 */
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");
688
689	/*
690	 * Make 'cur' fields relative, update 'last' fields to current values,
691	 * calculate total elapsed time.
692	 */
693	time = 0.0;
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];
709
710	/*
711	 * Print times.
712	 */
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); \
716}
717	PTIME(CPU_STATE_USER);
718	PTIME(CPU_STATE_SYSTEM);
719	PTIME(CPU_STATE_IDLE);
720}
721
722static void
723loadstats(void)
724{
725	double loadavg[3];
726
727	if(getloadavg(loadavg,3)!=3)
728		errx(1, "couldn't fetch load average");
729
730	printf("  %4.2f %4.2f %4.2f",loadavg[0],loadavg[1],loadavg[2]);
731}
732
733static int
734readvar(const char *name, void *ptr, size_t len)
735{
736	int	oid[4];
737	int	oidlen;
738
739	size_t nlen = len;
740
741	if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
742		if (errno != ENOENT) {
743			warn("sysctl(%s) failed", name);
744			return (1);
745		}
746		/*
747		 * XXX fallback code to deal with systems where
748		 * sysctlbyname can't find "old" OIDs, should be removed.
749		 */
750		if (!strcmp(name, "kern.boottime")) {
751			oid[0] = CTL_KERN;
752			oid[1] = KERN_BOOTTIME;
753			oidlen = 2;
754		} else {
755			warn("sysctl(%s) failed", name);
756			return (1);
757		}
758
759		nlen = len;
760		if (sysctl(oid, oidlen, ptr, &nlen, NULL, 0) == -1) {
761			warn("sysctl(%s) failed", name);
762			return (1);
763		}
764	}
765	if (nlen != len) {
766		warnx("sysctl(%s): expected %lu, got %lu", name,
767		      (unsigned long)len, (unsigned long)nlen);
768		return (1);
769	}
770	return (0);
771}
772
773static long double
774compute_etime(struct timeval cur_time, struct timeval prev_time)
775{
776	struct timeval busy_time;
777	u_int64_t busy_usec;
778	long double etime;
779
780	timersub(&cur_time, &prev_time, &busy_time);
781
782	busy_usec = busy_time.tv_sec;
783	busy_usec *= 1000000;
784	busy_usec += busy_time.tv_usec;
785	etime = busy_usec;
786	etime /= 1000000;
787
788	return(etime);
789}
790
791/*
792 * Record all "whole" IOMedia objects as being interesting.
793 */
794static int
795record_all_devices(void)
796{
797	io_iterator_t drivelist;
798	CFMutableDictionaryRef match;
799	kern_return_t status;
800
801	/*
802	 * Get an iterator for IOMedia objects.
803	 */
804	match = IOServiceMatching("IOMedia");
805	CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
806
807	CFRetain(match);
808	status = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, match, &record_drivelist, NULL, &drivelist);
809
810	if (status != KERN_SUCCESS)
811		errx(1, "couldn't match whole IOMedia devices");
812
813	/*
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
817	 * fetch statistics).
818	 *
819	 * XXX What about RAID devices?
820	 */
821
822	record_drivelist(NULL, drivelist);
823
824
825	status = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, match, &remove_drivelist, NULL, &drivelist);
826
827	if (status != KERN_SUCCESS)
828		errx(1, "couldn't match whole IOMedia device removal");
829
830	remove_drivelist(NULL, drivelist);
831
832	return(0);
833}
834
835static void record_drivelist(void* context, io_iterator_t drivelist)
836{
837	io_registry_entry_t drive;
838	while ((drive = IOIteratorNext(drivelist))) {
839		if (num_devices < MAXDRIVES) {
840			record_device(drive);
841			phdr_flag = 1;
842		}
843		IOObjectRelease(drive);
844	}
845	qsort(drivestat, num_devices, sizeof(struct drivestats), &compare_drivestats);
846}
847
848static void remove_drivelist(void* context, io_iterator_t drivelist)
849{
850	io_registry_entry_t drive;
851	while ((drive = IOIteratorNext(drivelist))) {
852		kern_return_t status;
853		char bsdname[MAXDRIVENAME];
854		CFDictionaryRef properties;
855		CFStringRef name;
856
857		/* get drive properties */
858		status = IORegistryEntryCreateCFProperties(drive,
859			(CFMutableDictionaryRef *)&properties,
860			kCFAllocatorDefault,
861			kNilOptions);
862		if (status != KERN_SUCCESS) continue;
863
864		/* get name from properties */
865		name = (CFStringRef)CFDictionaryGetValue(properties,
866			CFSTR(kIOBSDNameKey));
867
868		if (name && CFStringGetCString(name, bsdname, MAXDRIVENAME, kCFStringEncodingUTF8)) {
869			int i;
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));
874					}
875					--num_devices;
876					phdr_flag = 1;
877					qsort(drivestat, num_devices, sizeof(struct drivestats), &compare_drivestats);
878					break;
879				}
880			}
881		}
882		CFRelease(properties);
883		IOObjectRelease(drive);
884	}
885}
886
887/*
888 * Try to record the named device as interesting.  It
889 * must be an IOMedia device.
890 */
891static int
892record_one_device(char *name)
893{
894	io_iterator_t drivelist;
895	io_registry_entry_t drive;
896	kern_return_t status;
897
898	/*
899	 * Find the device.
900	 */
901	status = IOServiceGetMatchingServices(masterPort,
902		IOBSDNameMatching(masterPort, kNilOptions, name),
903		&drivelist);
904	if (status != KERN_SUCCESS)
905		errx(1, "couldn't match '%s'", name);
906
907	/*
908	 * Get the first match (should only be one)
909	 */
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);
914
915	/*
916	 * Record the device.
917	 */
918	if (record_device(drive))
919		errx(1, "could not record '%s' for monitoring", name);
920
921	IOObjectRelease(drive);
922	IOObjectRelease(drivelist);
923
924	return(0);
925}
926
927/*
928 * Determine whether an IORegistryEntry refers to a valid
929 * I/O device, and if so, record it.
930 */
931static int
932record_device(io_registry_entry_t drive)
933{
934	io_registry_entry_t parent;
935	CFDictionaryRef properties;
936	CFStringRef name;
937	CFNumberRef number;
938	kern_return_t status;
939
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;
947
948		/* get drive properties */
949		status = IORegistryEntryCreateCFProperties(drive,
950			(CFMutableDictionaryRef *)&properties,
951			kCFAllocatorDefault,
952			kNilOptions);
953		if (status != KERN_SUCCESS)
954			errx(1, "device has no properties");
955
956		/* get name from properties */
957		name = (CFStringRef)CFDictionaryGetValue(properties,
958			CFSTR(kIOBSDNameKey));
959		if (name)
960			CFStringGetCString(name, drivestat[num_devices].name,
961					   MAXDRIVENAME, kCFStringEncodingUTF8);
962		else {
963			errx(1, "device does not have a BSD name");
964		}
965
966		/* get blocksize from properties */
967		number = (CFNumberRef)CFDictionaryGetValue(properties,
968			CFSTR(kIOMediaPreferredBlockSizeKey));
969		if (number)
970			CFNumberGetValue(number, kCFNumberSInt64Type,
971					 &drivestat[num_devices].blocksize);
972		else
973			errx(1, "device does not have a preferred block size");
974
975		/* clean up, return success */
976		CFRelease(properties);
977		num_devices++;
978		return(0);
979	}
980
981	/* failed, don't keep parent */
982	IOObjectRelease(parent);
983	return(1);
984}
985
986static int
987compare_drivestats(const void* pa, const void* pb)
988{
989	struct drivestats* a = (struct drivestats*)pa;
990	struct drivestats* b = (struct drivestats*)pb;
991	return strcmp(a->name, b->name);
992}
993