xref: /openbsd/usr.bin/vmstat/dkstats.c (revision 6f40fd34)
1 /*	$OpenBSD: dkstats.c,v 1.40 2017/05/30 05:57:46 tedu 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/time.h>
37 #include <sys/disk.h>
38 #include <sys/sched.h>
39 #include <sys/sysctl.h>
40 #include <sys/tty.h>
41 
42 #include <err.h>
43 #include <fcntl.h>
44 #include <kvm.h>
45 #include <limits.h>
46 #include <nlist.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include "dkstats.h"
52 
53 #if !defined(NOKVM)
54 static struct nlist namelist[] = {
55 #define	X_TK_NIN	0		/* sysctl */
56 	{ "_tk_nin" },
57 #define	X_TK_NOUT	1		/* sysctl */
58 	{ "_tk_nout" },
59 #define	X_CP_TIME	2		/* sysctl */
60 	{ "_cp_time" },
61 #define	X_HZ		3		/* sysctl */
62 	{ "_hz" },
63 #define	X_STATHZ	4		/* sysctl */
64 	{ "_stathz" },
65 #define X_DISK_COUNT	5		/* sysctl */
66 	{ "_disk_count" },
67 #define X_DISKLIST	6		/* sysctl */
68 	{ "_disklist" },
69 	{ NULL },
70 };
71 #define	KVM_ERROR(_string) {						\
72 	warnx("%s", (_string));						\
73 	errx(1, "%s", kvm_geterr(kd));					\
74 }
75 
76 /*
77  * Dereference the namelist pointer `v' and fill in the local copy
78  * 'p' which is of size 's'.
79  */
80 #define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));
81 static void deref_kptr(void *, void *, size_t);
82 #endif /* !defined(NOKVM) */
83 
84 /* Structures to hold the statistics. */
85 struct _disk	cur, last;
86 
87 /* Kernel pointers: nlistf and memf defined in calling program. */
88 #if !defined(NOKVM)
89 extern kvm_t	*kd;
90 #endif
91 extern char	*nlistf;
92 extern char	*memf;
93 
94 #if !defined(NOKVM)
95 /* Pointer to list of disks. */
96 static struct disk	*dk_drivehead = NULL;
97 #endif
98 
99 /* Backward compatibility references. */
100 int		dk_ndrive = 0;
101 int		*dk_select;
102 char		**dr_name;
103 
104 /* Missing from <sys/time.h> */
105 #define timerset(tvp, uvp) \
106 	((uvp)->tv_sec = (tvp)->tv_sec);		\
107 	((uvp)->tv_usec = (tvp)->tv_usec)
108 
109 #define SWAP(fld)	tmp = cur.fld;				\
110 			cur.fld -= last.fld;			\
111 			last.fld = tmp
112 
113 /*
114  * Take the delta between the present values and the last recorded
115  * values, storing the present values in the 'last' structure, and
116  * the delta values in the 'cur' structure.
117  */
118 void
119 dkswap(void)
120 {
121 	u_int64_t tmp;
122 	int	i;
123 
124 	for (i = 0; i < cur.dk_ndrive; i++) {
125 		struct timeval	tmp_timer;
126 
127 		if (!cur.dk_select[i])
128 			continue;
129 
130 		/* Delta Values. */
131 		SWAP(dk_rxfer[i]);
132 		SWAP(dk_wxfer[i]);
133 		SWAP(dk_seek[i]);
134 		SWAP(dk_rbytes[i]);
135 		SWAP(dk_wbytes[i]);
136 
137 		/* Delta Time. */
138 		timerclear(&tmp_timer);
139 		timerset(&(cur.dk_time[i]), &tmp_timer);
140 		timersub(&tmp_timer, &(last.dk_time[i]), &(cur.dk_time[i]));
141 		timerclear(&(last.dk_time[i]));
142 		timerset(&tmp_timer, &(last.dk_time[i]));
143 	}
144 	for (i = 0; i < CPUSTATES; i++) {
145 		long ltmp;
146 
147 		ltmp = cur.cp_time[i];
148 		cur.cp_time[i] -= last.cp_time[i];
149 		last.cp_time[i] = ltmp;
150 	}
151 	SWAP(tk_nin);
152 	SWAP(tk_nout);
153 
154 #undef SWAP
155 }
156 
157 /*
158  * Read the disk statistics for each disk in the disk list.
159  * Also collect statistics for tty i/o and cpu ticks.
160  */
161 void
162 dkreadstats(void)
163 {
164 #if !defined(NOKVM)
165 	struct disk	cur_disk, *p;
166 #endif
167 	int		i, j, mib[3];
168 	size_t		size;
169 	char		*disknames, *name, *bufpp, **dk_name;
170 	struct diskstats *q;
171 
172 	last.dk_ndrive = cur.dk_ndrive;
173 
174 	if (nlistf == NULL && memf == NULL) {
175 		/* Get the number of attached drives. */
176 		mib[0] = CTL_HW;
177 		mib[1] = HW_DISKCOUNT;
178 		size = sizeof(dk_ndrive);
179 		if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) < 0 ) {
180 			warn("could not read hw.diskcount");
181 			dk_ndrive = 0;
182 		}
183 
184 		if (cur.dk_ndrive != dk_ndrive) {
185 			/* Re-read the disk names. */
186 			dk_name = calloc((size_t)dk_ndrive, sizeof(char *));
187 			if (dk_name == NULL)
188 				err(1, NULL);
189 			mib[0] = CTL_HW;
190 			mib[1] = HW_DISKNAMES;
191 			size = 0;
192 			if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
193 				err(1, "can't get hw.disknames");
194 			disknames = malloc(size);
195 			if (disknames == NULL)
196 				err(1, NULL);
197 			if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
198 				err(1, "can't get hw.disknames");
199 			bufpp = disknames;
200 			for (i = 0; i < dk_ndrive &&
201 			    (name = strsep(&bufpp, ",")) != NULL; i++)
202 				dk_name[i] = name;
203 			for (i = 0; i < dk_ndrive; i++) {
204 				char *ep = strchr(dk_name[i], ':');
205 				if (ep)
206 					*ep = '\0';
207 			}
208 			disknames = cur.dk_name[0];	/* To free old names. */
209 
210 			if (dk_ndrive < cur.dk_ndrive) {
211 				for (i = 0, j = 0; i < dk_ndrive; i++, j++) {
212 					while (j < cur.dk_ndrive &&
213 					    strcmp(cur.dk_name[j], dk_name[i]))
214 						j++;
215 					if (i == j) continue;
216 
217 					if (j >= cur.dk_ndrive) {
218 						cur.dk_select[i] = 1;
219 						last.dk_rxfer[i] = 0;
220 						last.dk_wxfer[i] = 0;
221 						last.dk_seek[i] = 0;
222 						last.dk_rbytes[i] = 0;
223 						last.dk_wbytes[i] = 0;
224 						memset(&last.dk_time[i], 0,
225 						    sizeof(struct timeval));
226 						continue;
227 					}
228 
229 					cur.dk_select[i] = cur.dk_select[j];
230 					last.dk_rxfer[i] = last.dk_rxfer[j];
231 					last.dk_wxfer[i] = last.dk_wxfer[j];
232 					last.dk_seek[i] = last.dk_seek[j];
233 					last.dk_rbytes[i] = last.dk_rbytes[j];
234 					last.dk_wbytes[i] = last.dk_wbytes[j];
235 					last.dk_time[i] = last.dk_time[j];
236 				}
237 
238 				cur.dk_select = reallocarray(cur.dk_select,
239 				    dk_ndrive, sizeof(*cur.dk_select));
240 				cur.dk_rxfer = reallocarray(cur.dk_rxfer,
241 				    dk_ndrive, sizeof(*cur.dk_rxfer));
242 				cur.dk_wxfer = reallocarray(cur.dk_wxfer,
243 				    dk_ndrive, sizeof(*cur.dk_wxfer));
244 				cur.dk_seek = reallocarray(cur.dk_seek,
245 				    dk_ndrive, sizeof(*cur.dk_seek));
246 				cur.dk_rbytes = reallocarray(cur.dk_rbytes,
247 				    dk_ndrive, sizeof(*cur.dk_rbytes));
248 				cur.dk_wbytes = reallocarray(cur.dk_wbytes,
249 				    dk_ndrive, sizeof(*cur.dk_wbytes));
250 				cur.dk_time = reallocarray(cur.dk_time,
251 				    dk_ndrive, sizeof(*cur.dk_time));
252 				last.dk_rxfer = reallocarray(last.dk_rxfer,
253 				    dk_ndrive, sizeof(*last.dk_rxfer));
254 				last.dk_wxfer = reallocarray(last.dk_wxfer,
255 				    dk_ndrive, sizeof(*last.dk_wxfer));
256 				last.dk_seek = reallocarray(last.dk_seek,
257 				    dk_ndrive, sizeof(*last.dk_seek));
258 				last.dk_rbytes = reallocarray(last.dk_rbytes,
259 				    dk_ndrive, sizeof(*last.dk_rbytes));
260 				last.dk_wbytes = reallocarray(last.dk_wbytes,
261 				    dk_ndrive, sizeof(*last.dk_wbytes));
262 				last.dk_time = reallocarray(last.dk_time,
263 				    dk_ndrive, sizeof(*last.dk_time));
264 
265 				if (!cur.dk_select || !cur.dk_rxfer ||
266 				    !cur.dk_wxfer || !cur.dk_seek ||
267 				    !cur.dk_rbytes || !cur.dk_wbytes ||
268 				    !cur.dk_time || !last.dk_rxfer ||
269 				    !last.dk_wxfer || !last.dk_seek ||
270 				    !last.dk_rbytes || !last.dk_wbytes ||
271 				    !last.dk_time)
272 					errx(1, "Memory allocation failure.");
273 			} else {
274 				cur.dk_select = reallocarray(cur.dk_select,
275 				    dk_ndrive, sizeof(*cur.dk_select));
276 				cur.dk_rxfer = reallocarray(cur.dk_rxfer,
277 				    dk_ndrive, sizeof(*cur.dk_rxfer));
278 				cur.dk_wxfer = reallocarray(cur.dk_wxfer,
279 				    dk_ndrive, sizeof(*cur.dk_wxfer));
280 				cur.dk_seek = reallocarray(cur.dk_seek,
281 				    dk_ndrive, sizeof(*cur.dk_seek));
282 				cur.dk_rbytes = reallocarray(cur.dk_rbytes,
283 				    dk_ndrive, sizeof(*cur.dk_rbytes));
284 				cur.dk_wbytes = reallocarray(cur.dk_wbytes,
285 				    dk_ndrive, sizeof(*cur.dk_wbytes));
286 				cur.dk_time = reallocarray(cur.dk_time,
287 				    dk_ndrive, sizeof(*cur.dk_time));
288 				last.dk_rxfer = reallocarray(last.dk_rxfer,
289 				    dk_ndrive, sizeof(*last.dk_rxfer));
290 				last.dk_wxfer = reallocarray(last.dk_wxfer,
291 				    dk_ndrive, sizeof(*last.dk_wxfer));
292 				last.dk_seek = reallocarray(last.dk_seek,
293 				    dk_ndrive, sizeof(*last.dk_seek));
294 				last.dk_rbytes = reallocarray(last.dk_rbytes,
295 				    dk_ndrive, sizeof(*last.dk_rbytes));
296 				last.dk_wbytes = reallocarray(last.dk_wbytes,
297 				    dk_ndrive, sizeof(*last.dk_wbytes));
298 				last.dk_time = reallocarray(last.dk_time,
299 				    dk_ndrive, sizeof(*last.dk_time));
300 
301 				if (!cur.dk_select || !cur.dk_rxfer ||
302 				    !cur.dk_wxfer || !cur.dk_seek ||
303 				    !cur.dk_rbytes || !cur.dk_wbytes ||
304 				    !cur.dk_time || !last.dk_rxfer ||
305 				    !last.dk_wxfer || !last.dk_seek ||
306 				    !last.dk_rbytes || !last.dk_wbytes ||
307 				    !last.dk_time)
308 					errx(1, "Memory allocation failure.");
309 
310 				for (i = dk_ndrive - 1, j = cur.dk_ndrive - 1;
311 				     i >= 0; i--) {
312 
313 					if (j < 0 ||
314 					    strcmp(cur.dk_name[j], dk_name[i]))
315 					{
316 						cur.dk_select[i] = 1;
317 						last.dk_rxfer[i] = 0;
318 						last.dk_wxfer[i] = 0;
319 						last.dk_seek[i] = 0;
320 						last.dk_rbytes[i] = 0;
321 						last.dk_wbytes[i] = 0;
322 						memset(&last.dk_time[i], 0,
323 						    sizeof(struct timeval));
324 						continue;
325 					}
326 
327 					if (i > j) {
328 						cur.dk_select[i] =
329 						    cur.dk_select[j];
330 						last.dk_rxfer[i] =
331 						    last.dk_rxfer[j];
332 						last.dk_wxfer[i] =
333 						    last.dk_wxfer[j];
334 						last.dk_seek[i] =
335 						    last.dk_seek[j];
336 						last.dk_rbytes[i] =
337 						    last.dk_rbytes[j];
338 						last.dk_wbytes[i] =
339 						    last.dk_wbytes[j];
340 						last.dk_time[i] =
341 						    last.dk_time[j];
342 					}
343 					j--;
344 				}
345 			}
346 
347 			cur.dk_ndrive = dk_ndrive;
348 			free(disknames);
349 			cur.dk_name = dk_name;
350 			dr_name = cur.dk_name;
351 			dk_select = cur.dk_select;
352 		}
353 
354 		size = cur.dk_ndrive * sizeof(struct diskstats);
355 		mib[0] = CTL_HW;
356 		mib[1] = HW_DISKSTATS;
357 		q = malloc(size);
358 		if (q == NULL)
359 			err(1, NULL);
360 		if (sysctl(mib, 2, q, &size, NULL, 0) < 0) {
361 #ifdef	DEBUG
362 			warn("could not read hw.diskstats");
363 #endif	/* DEBUG */
364 			memset(q, 0, cur.dk_ndrive * sizeof(struct diskstats));
365 		}
366 
367 		for (i = 0; i < cur.dk_ndrive; i++)	{
368 			cur.dk_rxfer[i] = q[i].ds_rxfer;
369 			cur.dk_wxfer[i] = q[i].ds_wxfer;
370 			cur.dk_seek[i] = q[i].ds_seek;
371 			cur.dk_rbytes[i] = q[i].ds_rbytes;
372 			cur.dk_wbytes[i] = q[i].ds_wbytes;
373 			timerset(&(q[i].ds_time), &(cur.dk_time[i]));
374 		}
375 		free(q);
376 
377 		size = sizeof(cur.cp_time);
378 		mib[0] = CTL_KERN;
379 		mib[1] = KERN_CPTIME;
380 		if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) {
381 			warn("could not read kern.cp_time");
382 			memset(cur.cp_time, 0, sizeof(cur.cp_time));
383 		}
384 		size = sizeof(cur.tk_nin);
385 		mib[0] = CTL_KERN;
386 		mib[1] = KERN_TTY;
387 		mib[2] = KERN_TTY_TKNIN;
388 		if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) {
389 			warn("could not read kern.tty.tk_nin");
390 			cur.tk_nin = 0;
391 		}
392 		size = sizeof(cur.tk_nin);
393 		mib[0] = CTL_KERN;
394 		mib[1] = KERN_TTY;
395 		mib[2] = KERN_TTY_TKNOUT;
396 		if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) {
397 			warn("could not read kern.tty.tk_nout");
398 			cur.tk_nout = 0;
399 		}
400 	} else {
401 #if !defined(NOKVM)
402 		p = dk_drivehead;
403 
404 		for (i = 0; i < cur.dk_ndrive; i++) {
405 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
406 			cur.dk_rxfer[i] = cur_disk.dk_rxfer;
407 			cur.dk_wxfer[i] = cur_disk.dk_wxfer;
408 			cur.dk_seek[i] = cur_disk.dk_seek;
409 			cur.dk_rbytes[i] = cur_disk.dk_rbytes;
410 			cur.dk_wbytes[i] = cur_disk.dk_wbytes;
411 			timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
412 			p = TAILQ_NEXT(&cur_disk, dk_link);
413 		}
414 		deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
415 		deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
416 		deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
417 #endif /* !defined(NOKVM) */
418 	}
419 }
420 
421 /*
422  * Perform all of the initialization and memory allocation needed to
423  * track disk statistics.
424  */
425 int
426 dkinit(int sel)
427 {
428 #if !defined(NOKVM)
429 	struct disklist_head disk_head;
430 	struct disk	cur_disk, *p;
431         char		errbuf[_POSIX2_LINE_MAX];
432 #endif
433 	static int	once = 0;
434 	extern int	hz;
435 	int		i, mib[2];
436 	size_t		size;
437 	struct clockinfo clkinfo;
438 	char		*disknames, *name, *bufpp;
439 
440 	if (once)
441 		return(1);
442 
443 	if (nlistf != NULL || memf != NULL) {
444 #if !defined(NOKVM)
445 		/* Open the kernel. */
446 		if (kd == NULL &&
447 		    (kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
448 		    errbuf)) == NULL)
449 			errx(1, "kvm_openfiles: %s", errbuf);
450 
451 		/* Obtain the namelist symbols from the kernel. */
452 		if (kvm_nlist(kd, namelist))
453 			KVM_ERROR("kvm_nlist failed to read symbols.");
454 
455 		/* Get the number of attached drives. */
456 		deref_nl(X_DISK_COUNT, &cur.dk_ndrive, sizeof(cur.dk_ndrive));
457 
458 		if (cur.dk_ndrive < 0)
459 			errx(1, "invalid _disk_count %d.", cur.dk_ndrive);
460 
461 		/* Get a pointer to the first disk. */
462 		deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
463 		dk_drivehead = TAILQ_FIRST(&disk_head);
464 
465 		/* Get ticks per second. */
466 		deref_nl(X_STATHZ, &hz, sizeof(hz));
467 		if (!hz)
468 		  deref_nl(X_HZ, &hz, sizeof(hz));
469 #endif /* !defined(NOKVM) */
470 	} else {
471 		/* Get the number of attached drives. */
472 		mib[0] = CTL_HW;
473 		mib[1] = HW_DISKCOUNT;
474 		size = sizeof(cur.dk_ndrive);
475 		if (sysctl(mib, 2, &cur.dk_ndrive, &size, NULL, 0) < 0 ) {
476 			warn("could not read hw.diskcount");
477 			cur.dk_ndrive = 0;
478 		}
479 
480 		/* Get ticks per second. */
481 		mib[0] = CTL_KERN;
482 		mib[1] = KERN_CLOCKRATE;
483 		size = sizeof(clkinfo);
484 		if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
485 			warn("could not read kern.clockrate");
486 			hz = 0;
487 		} else
488 			hz = clkinfo.stathz;
489 	}
490 
491 	/* allocate space for the statistics */
492 	cur.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
493 	cur.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
494 	cur.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
495 	cur.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
496 	cur.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
497 	cur.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
498 	cur.dk_select = calloc((size_t)cur.dk_ndrive, sizeof(int));
499 	cur.dk_name = calloc((size_t)cur.dk_ndrive, sizeof(char *));
500 	last.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
501 	last.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
502 	last.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
503 	last.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
504 	last.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
505 	last.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
506 
507 	if (!cur.dk_time || !cur.dk_rxfer || !cur.dk_wxfer || !cur.dk_seek ||
508 	    !cur.dk_rbytes || !cur.dk_wbytes || !cur.dk_select ||
509 	    !cur.dk_name || !last.dk_time || !last.dk_rxfer ||
510 	    !last.dk_wxfer || !last.dk_seek || !last.dk_rbytes ||
511 	    !last.dk_wbytes)
512 		errx(1, "Memory allocation failure.");
513 
514 	/* Set up the compatibility interfaces. */
515 	dk_ndrive = cur.dk_ndrive;
516 	dk_select = cur.dk_select;
517 	dr_name = cur.dk_name;
518 
519 	/* Read the disk names and set initial selection. */
520 	if (nlistf == NULL && memf == NULL) {
521 		mib[0] = CTL_HW;
522 		mib[1] = HW_DISKNAMES;
523 		size = 0;
524 		if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
525 			err(1, "can't get hw.disknames");
526 		disknames = malloc(size);
527 		if (disknames == NULL)
528 			err(1, NULL);
529 		if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
530 			err(1, "can't get hw.disknames");
531 		bufpp = disknames;
532 		for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) {
533 			cur.dk_name[i] = name;
534 			cur.dk_select[i] = sel;
535 		}
536 		for (i = 0; i < dk_ndrive; i++) {
537 			char *ep = strchr(cur.dk_name[i], ':');
538 			if (ep)
539 				*ep = '\0';
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 		memset(buf, 0, 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