xref: /openbsd/usr.bin/vmstat/dkstats.c (revision 404b540a)
1 /*	$OpenBSD: dkstats.c,v 1.33 2007/11/26 09:28:34 martynas 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 && (name = strsep(&bufpp, ",")) != NULL; i++)
202 				dk_name[i] = name;
203 			disknames = cur.dk_name[0];	/* To free old names. */
204 
205 			if (dk_ndrive < cur.dk_ndrive) {
206 				for (i = 0, j = 0; i < dk_ndrive; i++, j++) {
207 					while (j < cur.dk_ndrive &&
208 					    strcmp(cur.dk_name[j], dk_name[i]))
209 						j++;
210 					if (i == j) continue;
211 
212 					if (j >= cur.dk_ndrive) {
213 						cur.dk_select[i] = 1;
214 						last.dk_rxfer[i] = 0;
215 						last.dk_wxfer[i] = 0;
216 						last.dk_seek[i] = 0;
217 						last.dk_rbytes[i] = 0;
218 						last.dk_wbytes[i] = 0;
219 						bzero(&last.dk_time[i],
220 						    sizeof(struct timeval));
221 						continue;
222 					}
223 
224 					cur.dk_select[i] = cur.dk_select[j];
225 					last.dk_rxfer[i] = last.dk_rxfer[j];
226 					last.dk_wxfer[i] = last.dk_wxfer[j];
227 					last.dk_seek[i] = last.dk_seek[j];
228 					last.dk_rbytes[i] = last.dk_rbytes[j];
229 					last.dk_wbytes[i] = last.dk_wbytes[j];
230 					last.dk_time[i] = last.dk_time[j];
231 				}
232 
233 				cur.dk_select = realloc(cur.dk_select,
234 				    dk_ndrive * sizeof(*cur.dk_select));
235 				cur.dk_rxfer = realloc(cur.dk_rxfer,
236 				    dk_ndrive * sizeof(*cur.dk_rxfer));
237 				cur.dk_wxfer = realloc(cur.dk_wxfer,
238 				    dk_ndrive * sizeof(*cur.dk_wxfer));
239 				cur.dk_seek = realloc(cur.dk_seek,
240 				    dk_ndrive * sizeof(*cur.dk_seek));
241 				cur.dk_rbytes = realloc(cur.dk_rbytes,
242 				    dk_ndrive * sizeof(*cur.dk_rbytes));
243 				cur.dk_wbytes = realloc(cur.dk_wbytes,
244 				    dk_ndrive * sizeof(*cur.dk_wbytes));
245 				cur.dk_time = realloc(cur.dk_time,
246 				    dk_ndrive * sizeof(*cur.dk_time));
247 				last.dk_rxfer = realloc(last.dk_rxfer,
248 				    dk_ndrive * sizeof(*last.dk_rxfer));
249 				last.dk_wxfer = realloc(last.dk_wxfer,
250 				    dk_ndrive * sizeof(*last.dk_wxfer));
251 				last.dk_seek = realloc(last.dk_seek,
252 				    dk_ndrive * sizeof(*last.dk_seek));
253 				last.dk_rbytes = realloc(last.dk_rbytes,
254 				    dk_ndrive * sizeof(*last.dk_rbytes));
255 				last.dk_wbytes = realloc(last.dk_wbytes,
256 				    dk_ndrive * sizeof(*last.dk_wbytes));
257 				last.dk_time = realloc(last.dk_time,
258 				    dk_ndrive * sizeof(*last.dk_time));
259 
260 				if (!cur.dk_select || !cur.dk_rxfer ||
261 				    !cur.dk_wxfer || !cur.dk_seek ||
262 				    !cur.dk_rbytes || !cur.dk_wbytes ||
263 				    !cur.dk_time || !last.dk_rxfer ||
264 				    !last.dk_wxfer || !last.dk_seek ||
265 				    !last.dk_rbytes || !last.dk_wbytes ||
266 				    !last.dk_time)
267 					errx(1, "Memory allocation failure.");
268 			} else {
269 				cur.dk_select = realloc(cur.dk_select,
270 				    dk_ndrive * sizeof(*cur.dk_select));
271 				cur.dk_rxfer = realloc(cur.dk_rxfer,
272 				    dk_ndrive * sizeof(*cur.dk_rxfer));
273 				cur.dk_wxfer = realloc(cur.dk_wxfer,
274 				    dk_ndrive * sizeof(*cur.dk_wxfer));
275 				cur.dk_seek = realloc(cur.dk_seek,
276 				    dk_ndrive * sizeof(*cur.dk_seek));
277 				cur.dk_rbytes = realloc(cur.dk_rbytes,
278 				    dk_ndrive * sizeof(*cur.dk_rbytes));
279 				cur.dk_wbytes = realloc(cur.dk_wbytes,
280 				    dk_ndrive * sizeof(*cur.dk_wbytes));
281 				cur.dk_time = realloc(cur.dk_time,
282 				    dk_ndrive * sizeof(*cur.dk_time));
283 				last.dk_rxfer = realloc(last.dk_rxfer,
284 				    dk_ndrive * sizeof(*last.dk_rxfer));
285 				last.dk_wxfer = realloc(last.dk_wxfer,
286 				    dk_ndrive * sizeof(*last.dk_wxfer));
287 				last.dk_seek = realloc(last.dk_seek,
288 				    dk_ndrive * sizeof(*last.dk_seek));
289 				last.dk_rbytes = realloc(last.dk_rbytes,
290 				    dk_ndrive * sizeof(*last.dk_rbytes));
291 				last.dk_wbytes = realloc(last.dk_wbytes,
292 				    dk_ndrive * sizeof(*last.dk_wbytes));
293 				last.dk_time = realloc(last.dk_time,
294 				    dk_ndrive * sizeof(*last.dk_time));
295 
296 				if (!cur.dk_select || !cur.dk_rxfer ||
297 				    !cur.dk_wxfer || !cur.dk_seek ||
298 				    !cur.dk_rbytes || !cur.dk_wbytes ||
299 				    !cur.dk_time || !last.dk_rxfer ||
300 				    !last.dk_wxfer || !last.dk_seek ||
301 				    !last.dk_rbytes || !last.dk_wbytes ||
302 				    !last.dk_time)
303 					errx(1, "Memory allocation failure.");
304 
305 				for (i = dk_ndrive - 1, j = cur.dk_ndrive - 1;
306 				     i >= 0; i--) {
307 
308 					if (j < 0 ||
309 					    strcmp(cur.dk_name[j], dk_name[i]))
310 					{
311 						cur.dk_select[i] = 1;
312 						last.dk_rxfer[i] = 0;
313 						last.dk_wxfer[i] = 0;
314 						last.dk_seek[i] = 0;
315 						last.dk_rbytes[i] = 0;
316 						last.dk_wbytes[i] = 0;
317 						bzero(&last.dk_time[i],
318 						    sizeof(struct timeval));
319 						continue;
320 					}
321 
322 					if (i > j) {
323 						cur.dk_select[i] =
324 						    cur.dk_select[j];
325 						last.dk_rxfer[i] =
326 						    last.dk_rxfer[j];
327 						last.dk_wxfer[i] =
328 						    last.dk_wxfer[j];
329 						last.dk_seek[i] =
330 						    last.dk_seek[j];
331 						last.dk_rbytes[i] =
332 						    last.dk_rbytes[j];
333 						last.dk_wbytes[i] =
334 						    last.dk_wbytes[j];
335 						last.dk_time[i] =
336 						    last.dk_time[j];
337 					}
338 					j--;
339 				}
340 			}
341 
342 			cur.dk_ndrive = dk_ndrive;
343 			free(disknames);
344 			cur.dk_name = dk_name;
345 			dr_name = cur.dk_name;
346 			dk_select = cur.dk_select;
347 		}
348 
349 		size = cur.dk_ndrive * sizeof(struct diskstats);
350 		mib[0] = CTL_HW;
351 		mib[1] = HW_DISKSTATS;
352 		q = malloc(size);
353 		if (q == NULL)
354 			err(1, NULL);
355 		if (sysctl(mib, 2, q, &size, NULL, 0) < 0) {
356 #ifdef	DEBUG
357 			warn("could not read hw.diskstats");
358 #endif	/* DEBUG */
359 			bzero(q, cur.dk_ndrive * sizeof(struct diskstats));
360 		}
361 
362 		for (i = 0; i < cur.dk_ndrive; i++)	{
363 			cur.dk_rxfer[i] = q[i].ds_rxfer;
364 			cur.dk_wxfer[i] = q[i].ds_wxfer;
365 			cur.dk_seek[i] = q[i].ds_seek;
366 			cur.dk_rbytes[i] = q[i].ds_rbytes;
367 			cur.dk_wbytes[i] = q[i].ds_wbytes;
368 			timerset(&(q[i].ds_time), &(cur.dk_time[i]));
369 		}
370 		free(q);
371 
372 		size = sizeof(cur.cp_time);
373 		mib[0] = CTL_KERN;
374 		mib[1] = KERN_CPTIME;
375 		if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) {
376 			warn("could not read kern.cp_time");
377 			bzero(cur.cp_time, sizeof(cur.cp_time));
378 		}
379 		size = sizeof(cur.tk_nin);
380 		mib[0] = CTL_KERN;
381 		mib[1] = KERN_TTY;
382 		mib[2] = KERN_TTY_TKNIN;
383 		if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) {
384 			warn("could not read kern.tty.tk_nin");
385 			cur.tk_nin = 0;
386 		}
387 		size = sizeof(cur.tk_nin);
388 		mib[0] = CTL_KERN;
389 		mib[1] = KERN_TTY;
390 		mib[2] = KERN_TTY_TKNOUT;
391 		if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) {
392 			warn("could not read kern.tty.tk_nout");
393 			cur.tk_nout = 0;
394 		}
395 	} else {
396 #if !defined(NOKVM)
397 		p = dk_drivehead;
398 
399 		for (i = 0; i < cur.dk_ndrive; i++) {
400 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
401 			cur.dk_rxfer[i] = cur_disk.dk_rxfer;
402 			cur.dk_wxfer[i] = cur_disk.dk_wxfer;
403 			cur.dk_seek[i] = cur_disk.dk_seek;
404 			cur.dk_rbytes[i] = cur_disk.dk_rbytes;
405 			cur.dk_wbytes[i] = cur_disk.dk_wbytes;
406 			timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
407 			p = TAILQ_NEXT(&cur_disk, dk_link);
408 		}
409 		deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
410 		deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
411 		deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
412 #endif /* !defined(NOKVM) */
413 	}
414 }
415 
416 /*
417  * Perform all of the initialization and memory allocation needed to
418  * track disk statistics.
419  */
420 int
421 dkinit(int sel)
422 {
423 #if !defined(NOKVM)
424 	struct disklist_head disk_head;
425 	struct disk	cur_disk, *p;
426         char		errbuf[_POSIX2_LINE_MAX];
427 #endif
428 	static int	once = 0;
429 	extern int	hz;
430 	int		i, mib[2];
431 	size_t		size;
432 	struct clockinfo clkinfo;
433 	char		*disknames, *name, *bufpp;
434 	gid_t		gid;
435 
436 	if (once)
437 		return(1);
438 
439 	gid = getgid();
440 	if (nlistf != NULL || memf != NULL) {
441 #if !defined(NOKVM)
442 		if (memf != NULL)
443 			if (setresgid(gid, gid, gid) == -1)
444 				err(1, "setresgid");
445 
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 		if (memf == NULL)
453 			if (setresgid(gid, gid, gid) == -1)
454 				err(1, "setresgid");
455 
456 		/* Obtain the namelist symbols from the kernel. */
457 		if (kvm_nlist(kd, namelist))
458 			KVM_ERROR("kvm_nlist failed to read symbols.");
459 
460 		/* Get the number of attached drives. */
461 		deref_nl(X_DISK_COUNT, &cur.dk_ndrive, sizeof(cur.dk_ndrive));
462 
463 		if (cur.dk_ndrive < 0)
464 			errx(1, "invalid _disk_count %d.", cur.dk_ndrive);
465 
466 		/* Get a pointer to the first disk. */
467 		deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
468 		dk_drivehead = TAILQ_FIRST(&disk_head);
469 
470 		/* Get ticks per second. */
471 		deref_nl(X_STATHZ, &hz, sizeof(hz));
472 		if (!hz)
473 		  deref_nl(X_HZ, &hz, sizeof(hz));
474 #endif /* !defined(NOKVM) */
475 	} else {
476 		/* Get the number of attached drives. */
477 		mib[0] = CTL_HW;
478 		mib[1] = HW_DISKCOUNT;
479 		size = sizeof(cur.dk_ndrive);
480 		if (sysctl(mib, 2, &cur.dk_ndrive, &size, NULL, 0) < 0 ) {
481 			warn("could not read hw.diskcount");
482 			cur.dk_ndrive = 0;
483 		}
484 
485 		/* Get ticks per second. */
486 		mib[0] = CTL_KERN;
487 		mib[1] = KERN_CLOCKRATE;
488 		size = sizeof(clkinfo);
489 		if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
490 			warn("could not read kern.clockrate");
491 			hz = 0;
492 		} else
493 			hz = clkinfo.stathz;
494 	}
495 
496 	/* allocate space for the statistics */
497 	cur.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
498 	cur.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
499 	cur.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
500 	cur.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
501 	cur.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
502 	cur.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
503 	cur.dk_select = calloc((size_t)cur.dk_ndrive, sizeof(int));
504 	cur.dk_name = calloc((size_t)cur.dk_ndrive, sizeof(char *));
505 	last.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
506 	last.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
507 	last.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
508 	last.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
509 	last.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
510 	last.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
511 
512 	if (!cur.dk_time || !cur.dk_rxfer || !cur.dk_wxfer || !cur.dk_seek ||
513 	    !cur.dk_rbytes || !cur.dk_wbytes || !cur.dk_select ||
514 	    !cur.dk_name || !last.dk_time || !last.dk_rxfer ||
515 	    !last.dk_wxfer || !last.dk_seek || !last.dk_rbytes ||
516 	    !last.dk_wbytes)
517 		errx(1, "Memory allocation failure.");
518 
519 	/* Set up the compatibility interfaces. */
520 	dk_ndrive = cur.dk_ndrive;
521 	dk_select = cur.dk_select;
522 	dr_name = cur.dk_name;
523 
524 	/* Read the disk names and set initial selection. */
525 	if (nlistf == NULL && memf == NULL) {
526 		mib[0] = CTL_HW;
527 		mib[1] = HW_DISKNAMES;
528 		size = 0;
529 		if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
530 			err(1, "can't get hw.disknames");
531 		disknames = malloc(size);
532 		if (disknames == NULL)
533 			err(1, NULL);
534 		if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
535 			err(1, "can't get hw.disknames");
536 		bufpp = disknames;
537 		for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) {
538 			cur.dk_name[i] = name;
539 			cur.dk_select[i] = sel;
540 		}
541 	} else {
542 #if !defined(NOKVM)
543 		p = dk_drivehead;
544 		for (i = 0; i < cur.dk_ndrive; i++) {
545 			char	buf[10];
546 
547 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
548 			deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
549 			cur.dk_name[i] = strdup(buf);
550 			if (!cur.dk_name[i])
551 				errx(1, "Memory allocation failure.");
552 			cur.dk_select[i] = sel;
553 
554 			p = TAILQ_NEXT(&cur_disk, dk_link);
555 		}
556 #endif /* !defined(NOKVM) */
557 	}
558 
559 	/* Never do this initialization again. */
560 	once = 1;
561 	return(1);
562 }
563 
564 #if !defined(NOKVM)
565 /*
566  * Dereference the kernel pointer `kptr' and fill in the local copy
567  * pointed to by `ptr'.  The storage space must be pre-allocated,
568  * and the size of the copy passed in `len'.
569  */
570 static void
571 deref_kptr(void *kptr, void *ptr, size_t len)
572 {
573 	char buf[128];
574 
575 	if (kvm_read(kd, (u_long)kptr, ptr, len) != len) {
576 		bzero(buf, sizeof(buf));
577 		snprintf(buf, (sizeof(buf) - 1),
578 		     "can't dereference kptr 0x%lx", (u_long)kptr);
579 		KVM_ERROR(buf);
580 	}
581 }
582 #endif /* !defined(NOKVM) */
583