xref: /openbsd-src/usr.bin/vmstat/dkstats.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: dkstats.c,v 1.35 2010/09/24 00:11:15 deraadt Exp $	*/
2 /*	$NetBSD: dkstats.c,v 1.1 1996/05/10 23:19:27 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1996 John M. Vinopal
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed for the NetBSD Project
19  *      by John M. Vinopal.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/param.h>
37 #include <sys/dkstat.h>
38 #include <sys/time.h>
39 #include <sys/disk.h>
40 #include <sys/sysctl.h>
41 #include <sys/tty.h>
42 
43 #include <err.h>
44 #include <fcntl.h>
45 #include <kvm.h>
46 #include <limits.h>
47 #include <nlist.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include "dkstats.h"
53 
54 #if !defined(NOKVM)
55 static struct nlist namelist[] = {
56 #define	X_TK_NIN	0		/* sysctl */
57 	{ "_tk_nin" },
58 #define	X_TK_NOUT	1		/* sysctl */
59 	{ "_tk_nout" },
60 #define	X_CP_TIME	2		/* sysctl */
61 	{ "_cp_time" },
62 #define	X_HZ		3		/* sysctl */
63 	{ "_hz" },
64 #define	X_STATHZ	4		/* sysctl */
65 	{ "_stathz" },
66 #define X_DISK_COUNT	5		/* sysctl */
67 	{ "_disk_count" },
68 #define X_DISKLIST	6		/* sysctl */
69 	{ "_disklist" },
70 	{ NULL },
71 };
72 #define	KVM_ERROR(_string) {						\
73 	warnx("%s", (_string));						\
74 	errx(1, "%s", kvm_geterr(kd));					\
75 }
76 
77 /*
78  * Dereference the namelist pointer `v' and fill in the local copy
79  * 'p' which is of size 's'.
80  */
81 #define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));
82 static void deref_kptr(void *, void *, size_t);
83 #endif /* !defined(NOKVM) */
84 
85 /* Structures to hold the statistics. */
86 struct _disk	cur, last;
87 
88 /* Kernel pointers: nlistf and memf defined in calling program. */
89 #if !defined(NOKVM)
90 extern kvm_t	*kd;
91 #endif
92 extern char	*nlistf;
93 extern char	*memf;
94 
95 #if !defined(NOKVM)
96 /* Pointer to list of disks. */
97 static struct disk	*dk_drivehead = NULL;
98 #endif
99 
100 /* Backward compatibility references. */
101 int		dk_ndrive = 0;
102 int		*dk_select;
103 char		**dr_name;
104 
105 /* Missing from <sys/time.h> */
106 #define timerset(tvp, uvp) \
107 	((uvp)->tv_sec = (tvp)->tv_sec);		\
108 	((uvp)->tv_usec = (tvp)->tv_usec)
109 
110 #define SWAP(fld)	tmp = cur.fld;				\
111 			cur.fld -= last.fld;			\
112 			last.fld = tmp
113 
114 /*
115  * Take the delta between the present values and the last recorded
116  * values, storing the present values in the 'last' structure, and
117  * the delta values in the 'cur' structure.
118  */
119 void
120 dkswap(void)
121 {
122 	u_int64_t tmp;
123 	int	i;
124 
125 	for (i = 0; i < cur.dk_ndrive; i++) {
126 		struct timeval	tmp_timer;
127 
128 		if (!cur.dk_select[i])
129 			continue;
130 
131 		/* Delta Values. */
132 		SWAP(dk_rxfer[i]);
133 		SWAP(dk_wxfer[i]);
134 		SWAP(dk_seek[i]);
135 		SWAP(dk_rbytes[i]);
136 		SWAP(dk_wbytes[i]);
137 
138 		/* Delta Time. */
139 		timerclear(&tmp_timer);
140 		timerset(&(cur.dk_time[i]), &tmp_timer);
141 		timersub(&tmp_timer, &(last.dk_time[i]), &(cur.dk_time[i]));
142 		timerclear(&(last.dk_time[i]));
143 		timerset(&tmp_timer, &(last.dk_time[i]));
144 	}
145 	for (i = 0; i < CPUSTATES; i++) {
146 		long ltmp;
147 
148 		ltmp = cur.cp_time[i];
149 		cur.cp_time[i] -= last.cp_time[i];
150 		last.cp_time[i] = ltmp;
151 	}
152 	SWAP(tk_nin);
153 	SWAP(tk_nout);
154 
155 #undef SWAP
156 }
157 
158 /*
159  * Read the disk statistics for each disk in the disk list.
160  * Also collect statistics for tty i/o and cpu ticks.
161  */
162 void
163 dkreadstats(void)
164 {
165 #if !defined(NOKVM)
166 	struct disk	cur_disk, *p;
167 #endif
168 	int		i, j, mib[3];
169 	size_t		size;
170 	char		*disknames, *name, *bufpp, **dk_name;
171 	struct diskstats *q;
172 
173 	last.dk_ndrive = cur.dk_ndrive;
174 
175 	if (nlistf == NULL && memf == NULL) {
176 		/* Get the number of attached drives. */
177 		mib[0] = CTL_HW;
178 		mib[1] = HW_DISKCOUNT;
179 		size = sizeof(dk_ndrive);
180 		if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) < 0 ) {
181 			warn("could not read hw.diskcount");
182 			dk_ndrive = 0;
183 		}
184 
185 		if (cur.dk_ndrive != dk_ndrive) {
186 			/* Re-read the disk names. */
187 			dk_name = calloc((size_t)dk_ndrive, sizeof(char *));
188 			if (dk_name == NULL)
189 				err(1, NULL);
190 			mib[0] = CTL_HW;
191 			mib[1] = HW_DISKNAMES;
192 			size = 0;
193 			if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
194 				err(1, "can't get hw.disknames");
195 			disknames = malloc(size);
196 			if (disknames == NULL)
197 				err(1, NULL);
198 			if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
199 				err(1, "can't get hw.disknames");
200 			bufpp = disknames;
201 			for (i = 0; i < dk_ndrive &&
202 			    (name = strsep(&bufpp, ",")) != NULL; i++)
203 				dk_name[i] = name;
204 			for (i = 0; i < dk_ndrive; i++) {
205 				char *p = strchr(dk_name[i], ':');
206 				if (p)
207 					*p = '\0';
208 			}
209 			disknames = cur.dk_name[0];	/* To free old names. */
210 
211 			if (dk_ndrive < cur.dk_ndrive) {
212 				for (i = 0, j = 0; i < dk_ndrive; i++, j++) {
213 					while (j < cur.dk_ndrive &&
214 					    strcmp(cur.dk_name[j], dk_name[i]))
215 						j++;
216 					if (i == j) continue;
217 
218 					if (j >= cur.dk_ndrive) {
219 						cur.dk_select[i] = 1;
220 						last.dk_rxfer[i] = 0;
221 						last.dk_wxfer[i] = 0;
222 						last.dk_seek[i] = 0;
223 						last.dk_rbytes[i] = 0;
224 						last.dk_wbytes[i] = 0;
225 						bzero(&last.dk_time[i],
226 						    sizeof(struct timeval));
227 						continue;
228 					}
229 
230 					cur.dk_select[i] = cur.dk_select[j];
231 					last.dk_rxfer[i] = last.dk_rxfer[j];
232 					last.dk_wxfer[i] = last.dk_wxfer[j];
233 					last.dk_seek[i] = last.dk_seek[j];
234 					last.dk_rbytes[i] = last.dk_rbytes[j];
235 					last.dk_wbytes[i] = last.dk_wbytes[j];
236 					last.dk_time[i] = last.dk_time[j];
237 				}
238 
239 				cur.dk_select = realloc(cur.dk_select,
240 				    dk_ndrive * sizeof(*cur.dk_select));
241 				cur.dk_rxfer = realloc(cur.dk_rxfer,
242 				    dk_ndrive * sizeof(*cur.dk_rxfer));
243 				cur.dk_wxfer = realloc(cur.dk_wxfer,
244 				    dk_ndrive * sizeof(*cur.dk_wxfer));
245 				cur.dk_seek = realloc(cur.dk_seek,
246 				    dk_ndrive * sizeof(*cur.dk_seek));
247 				cur.dk_rbytes = realloc(cur.dk_rbytes,
248 				    dk_ndrive * sizeof(*cur.dk_rbytes));
249 				cur.dk_wbytes = realloc(cur.dk_wbytes,
250 				    dk_ndrive * sizeof(*cur.dk_wbytes));
251 				cur.dk_time = realloc(cur.dk_time,
252 				    dk_ndrive * sizeof(*cur.dk_time));
253 				last.dk_rxfer = realloc(last.dk_rxfer,
254 				    dk_ndrive * sizeof(*last.dk_rxfer));
255 				last.dk_wxfer = realloc(last.dk_wxfer,
256 				    dk_ndrive * sizeof(*last.dk_wxfer));
257 				last.dk_seek = realloc(last.dk_seek,
258 				    dk_ndrive * sizeof(*last.dk_seek));
259 				last.dk_rbytes = realloc(last.dk_rbytes,
260 				    dk_ndrive * sizeof(*last.dk_rbytes));
261 				last.dk_wbytes = realloc(last.dk_wbytes,
262 				    dk_ndrive * sizeof(*last.dk_wbytes));
263 				last.dk_time = realloc(last.dk_time,
264 				    dk_ndrive * sizeof(*last.dk_time));
265 
266 				if (!cur.dk_select || !cur.dk_rxfer ||
267 				    !cur.dk_wxfer || !cur.dk_seek ||
268 				    !cur.dk_rbytes || !cur.dk_wbytes ||
269 				    !cur.dk_time || !last.dk_rxfer ||
270 				    !last.dk_wxfer || !last.dk_seek ||
271 				    !last.dk_rbytes || !last.dk_wbytes ||
272 				    !last.dk_time)
273 					errx(1, "Memory allocation failure.");
274 			} else {
275 				cur.dk_select = realloc(cur.dk_select,
276 				    dk_ndrive * sizeof(*cur.dk_select));
277 				cur.dk_rxfer = realloc(cur.dk_rxfer,
278 				    dk_ndrive * sizeof(*cur.dk_rxfer));
279 				cur.dk_wxfer = realloc(cur.dk_wxfer,
280 				    dk_ndrive * sizeof(*cur.dk_wxfer));
281 				cur.dk_seek = realloc(cur.dk_seek,
282 				    dk_ndrive * sizeof(*cur.dk_seek));
283 				cur.dk_rbytes = realloc(cur.dk_rbytes,
284 				    dk_ndrive * sizeof(*cur.dk_rbytes));
285 				cur.dk_wbytes = realloc(cur.dk_wbytes,
286 				    dk_ndrive * sizeof(*cur.dk_wbytes));
287 				cur.dk_time = realloc(cur.dk_time,
288 				    dk_ndrive * sizeof(*cur.dk_time));
289 				last.dk_rxfer = realloc(last.dk_rxfer,
290 				    dk_ndrive * sizeof(*last.dk_rxfer));
291 				last.dk_wxfer = realloc(last.dk_wxfer,
292 				    dk_ndrive * sizeof(*last.dk_wxfer));
293 				last.dk_seek = realloc(last.dk_seek,
294 				    dk_ndrive * sizeof(*last.dk_seek));
295 				last.dk_rbytes = realloc(last.dk_rbytes,
296 				    dk_ndrive * sizeof(*last.dk_rbytes));
297 				last.dk_wbytes = realloc(last.dk_wbytes,
298 				    dk_ndrive * sizeof(*last.dk_wbytes));
299 				last.dk_time = realloc(last.dk_time,
300 				    dk_ndrive * sizeof(*last.dk_time));
301 
302 				if (!cur.dk_select || !cur.dk_rxfer ||
303 				    !cur.dk_wxfer || !cur.dk_seek ||
304 				    !cur.dk_rbytes || !cur.dk_wbytes ||
305 				    !cur.dk_time || !last.dk_rxfer ||
306 				    !last.dk_wxfer || !last.dk_seek ||
307 				    !last.dk_rbytes || !last.dk_wbytes ||
308 				    !last.dk_time)
309 					errx(1, "Memory allocation failure.");
310 
311 				for (i = dk_ndrive - 1, j = cur.dk_ndrive - 1;
312 				     i >= 0; i--) {
313 
314 					if (j < 0 ||
315 					    strcmp(cur.dk_name[j], dk_name[i]))
316 					{
317 						cur.dk_select[i] = 1;
318 						last.dk_rxfer[i] = 0;
319 						last.dk_wxfer[i] = 0;
320 						last.dk_seek[i] = 0;
321 						last.dk_rbytes[i] = 0;
322 						last.dk_wbytes[i] = 0;
323 						bzero(&last.dk_time[i],
324 						    sizeof(struct timeval));
325 						continue;
326 					}
327 
328 					if (i > j) {
329 						cur.dk_select[i] =
330 						    cur.dk_select[j];
331 						last.dk_rxfer[i] =
332 						    last.dk_rxfer[j];
333 						last.dk_wxfer[i] =
334 						    last.dk_wxfer[j];
335 						last.dk_seek[i] =
336 						    last.dk_seek[j];
337 						last.dk_rbytes[i] =
338 						    last.dk_rbytes[j];
339 						last.dk_wbytes[i] =
340 						    last.dk_wbytes[j];
341 						last.dk_time[i] =
342 						    last.dk_time[j];
343 					}
344 					j--;
345 				}
346 			}
347 
348 			cur.dk_ndrive = dk_ndrive;
349 			free(disknames);
350 			cur.dk_name = dk_name;
351 			dr_name = cur.dk_name;
352 			dk_select = cur.dk_select;
353 		}
354 
355 		size = cur.dk_ndrive * sizeof(struct diskstats);
356 		mib[0] = CTL_HW;
357 		mib[1] = HW_DISKSTATS;
358 		q = malloc(size);
359 		if (q == NULL)
360 			err(1, NULL);
361 		if (sysctl(mib, 2, q, &size, NULL, 0) < 0) {
362 #ifdef	DEBUG
363 			warn("could not read hw.diskstats");
364 #endif	/* DEBUG */
365 			bzero(q, cur.dk_ndrive * sizeof(struct diskstats));
366 		}
367 
368 		for (i = 0; i < cur.dk_ndrive; i++)	{
369 			cur.dk_rxfer[i] = q[i].ds_rxfer;
370 			cur.dk_wxfer[i] = q[i].ds_wxfer;
371 			cur.dk_seek[i] = q[i].ds_seek;
372 			cur.dk_rbytes[i] = q[i].ds_rbytes;
373 			cur.dk_wbytes[i] = q[i].ds_wbytes;
374 			timerset(&(q[i].ds_time), &(cur.dk_time[i]));
375 		}
376 		free(q);
377 
378 		size = sizeof(cur.cp_time);
379 		mib[0] = CTL_KERN;
380 		mib[1] = KERN_CPTIME;
381 		if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) {
382 			warn("could not read kern.cp_time");
383 			bzero(cur.cp_time, sizeof(cur.cp_time));
384 		}
385 		size = sizeof(cur.tk_nin);
386 		mib[0] = CTL_KERN;
387 		mib[1] = KERN_TTY;
388 		mib[2] = KERN_TTY_TKNIN;
389 		if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) {
390 			warn("could not read kern.tty.tk_nin");
391 			cur.tk_nin = 0;
392 		}
393 		size = sizeof(cur.tk_nin);
394 		mib[0] = CTL_KERN;
395 		mib[1] = KERN_TTY;
396 		mib[2] = KERN_TTY_TKNOUT;
397 		if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) {
398 			warn("could not read kern.tty.tk_nout");
399 			cur.tk_nout = 0;
400 		}
401 	} else {
402 #if !defined(NOKVM)
403 		p = dk_drivehead;
404 
405 		for (i = 0; i < cur.dk_ndrive; i++) {
406 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
407 			cur.dk_rxfer[i] = cur_disk.dk_rxfer;
408 			cur.dk_wxfer[i] = cur_disk.dk_wxfer;
409 			cur.dk_seek[i] = cur_disk.dk_seek;
410 			cur.dk_rbytes[i] = cur_disk.dk_rbytes;
411 			cur.dk_wbytes[i] = cur_disk.dk_wbytes;
412 			timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
413 			p = TAILQ_NEXT(&cur_disk, dk_link);
414 		}
415 		deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
416 		deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
417 		deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
418 #endif /* !defined(NOKVM) */
419 	}
420 }
421 
422 /*
423  * Perform all of the initialization and memory allocation needed to
424  * track disk statistics.
425  */
426 int
427 dkinit(int sel)
428 {
429 #if !defined(NOKVM)
430 	struct disklist_head disk_head;
431 	struct disk	cur_disk, *p;
432         char		errbuf[_POSIX2_LINE_MAX];
433 #endif
434 	static int	once = 0;
435 	extern int	hz;
436 	int		i, mib[2];
437 	size_t		size;
438 	struct clockinfo clkinfo;
439 	char		*disknames, *name, *bufpp;
440 
441 	if (once)
442 		return(1);
443 
444 	if (nlistf != NULL || memf != NULL) {
445 #if !defined(NOKVM)
446 		/* Open the kernel. */
447 		if (kd == NULL &&
448 		    (kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
449 		    errbuf)) == NULL)
450 			errx(1, "kvm_openfiles: %s", errbuf);
451 
452 		/* Obtain the namelist symbols from the kernel. */
453 		if (kvm_nlist(kd, namelist))
454 			KVM_ERROR("kvm_nlist failed to read symbols.");
455 
456 		/* Get the number of attached drives. */
457 		deref_nl(X_DISK_COUNT, &cur.dk_ndrive, sizeof(cur.dk_ndrive));
458 
459 		if (cur.dk_ndrive < 0)
460 			errx(1, "invalid _disk_count %d.", cur.dk_ndrive);
461 
462 		/* Get a pointer to the first disk. */
463 		deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
464 		dk_drivehead = TAILQ_FIRST(&disk_head);
465 
466 		/* Get ticks per second. */
467 		deref_nl(X_STATHZ, &hz, sizeof(hz));
468 		if (!hz)
469 		  deref_nl(X_HZ, &hz, sizeof(hz));
470 #endif /* !defined(NOKVM) */
471 	} else {
472 		/* Get the number of attached drives. */
473 		mib[0] = CTL_HW;
474 		mib[1] = HW_DISKCOUNT;
475 		size = sizeof(cur.dk_ndrive);
476 		if (sysctl(mib, 2, &cur.dk_ndrive, &size, NULL, 0) < 0 ) {
477 			warn("could not read hw.diskcount");
478 			cur.dk_ndrive = 0;
479 		}
480 
481 		/* Get ticks per second. */
482 		mib[0] = CTL_KERN;
483 		mib[1] = KERN_CLOCKRATE;
484 		size = sizeof(clkinfo);
485 		if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
486 			warn("could not read kern.clockrate");
487 			hz = 0;
488 		} else
489 			hz = clkinfo.stathz;
490 	}
491 
492 	/* allocate space for the statistics */
493 	cur.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
494 	cur.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
495 	cur.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
496 	cur.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
497 	cur.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
498 	cur.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
499 	cur.dk_select = calloc((size_t)cur.dk_ndrive, sizeof(int));
500 	cur.dk_name = calloc((size_t)cur.dk_ndrive, sizeof(char *));
501 	last.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
502 	last.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
503 	last.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
504 	last.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
505 	last.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
506 	last.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
507 
508 	if (!cur.dk_time || !cur.dk_rxfer || !cur.dk_wxfer || !cur.dk_seek ||
509 	    !cur.dk_rbytes || !cur.dk_wbytes || !cur.dk_select ||
510 	    !cur.dk_name || !last.dk_time || !last.dk_rxfer ||
511 	    !last.dk_wxfer || !last.dk_seek || !last.dk_rbytes ||
512 	    !last.dk_wbytes)
513 		errx(1, "Memory allocation failure.");
514 
515 	/* Set up the compatibility interfaces. */
516 	dk_ndrive = cur.dk_ndrive;
517 	dk_select = cur.dk_select;
518 	dr_name = cur.dk_name;
519 
520 	/* Read the disk names and set initial selection. */
521 	if (nlistf == NULL && memf == NULL) {
522 		mib[0] = CTL_HW;
523 		mib[1] = HW_DISKNAMES;
524 		size = 0;
525 		if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
526 			err(1, "can't get hw.disknames");
527 		disknames = malloc(size);
528 		if (disknames == NULL)
529 			err(1, NULL);
530 		if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
531 			err(1, "can't get hw.disknames");
532 		bufpp = disknames;
533 		for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) {
534 			cur.dk_name[i] = name;
535 			cur.dk_select[i] = sel;
536 		}
537 		for (i = 0; i < dk_ndrive; i++) {
538 			char *p = strchr(cur.dk_name[i], ':');
539 			if (p)
540 				*p = '\0';
541 		}
542 	} else {
543 #if !defined(NOKVM)
544 		p = dk_drivehead;
545 		for (i = 0; i < cur.dk_ndrive; i++) {
546 			char	buf[10];
547 
548 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
549 			deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
550 			cur.dk_name[i] = strdup(buf);
551 			if (!cur.dk_name[i])
552 				errx(1, "Memory allocation failure.");
553 			cur.dk_select[i] = sel;
554 
555 			p = TAILQ_NEXT(&cur_disk, dk_link);
556 		}
557 #endif /* !defined(NOKVM) */
558 	}
559 
560 	/* Never do this initialization again. */
561 	once = 1;
562 	return(1);
563 }
564 
565 #if !defined(NOKVM)
566 /*
567  * Dereference the kernel pointer `kptr' and fill in the local copy
568  * pointed to by `ptr'.  The storage space must be pre-allocated,
569  * and the size of the copy passed in `len'.
570  */
571 static void
572 deref_kptr(void *kptr, void *ptr, size_t len)
573 {
574 	char buf[128];
575 
576 	if (kvm_read(kd, (u_long)kptr, ptr, len) != len) {
577 		bzero(buf, sizeof(buf));
578 		snprintf(buf, (sizeof(buf) - 1),
579 		     "can't dereference kptr 0x%lx", (u_long)kptr);
580 		KVM_ERROR(buf);
581 	}
582 }
583 #endif /* !defined(NOKVM) */
584