xref: /openbsd/usr.bin/vmstat/dkstats.c (revision 23d44855)
1 /*	$OpenBSD: dkstats.c,v 1.42 2024/05/06 16:54:22 cheloha 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 #define SWAP(fld)	tmp = cur.fld;				\
105 			cur.fld -= last.fld;			\
106 			last.fld = tmp
107 
108 /*
109  * Take the delta between the present values and the last recorded
110  * values, storing the present values in the 'last' structure, and
111  * the delta values in the 'cur' structure.
112  */
113 void
dkswap(void)114 dkswap(void)
115 {
116 	u_int64_t tmp;
117 	int	i;
118 
119 	for (i = 0; i < cur.dk_ndrive; i++) {
120 		struct timeval	tmp_timer;
121 
122 		if (!cur.dk_select[i])
123 			continue;
124 
125 		/* Delta Values. */
126 		SWAP(dk_rxfer[i]);
127 		SWAP(dk_wxfer[i]);
128 		SWAP(dk_seek[i]);
129 		SWAP(dk_rbytes[i]);
130 		SWAP(dk_wbytes[i]);
131 
132 		/* Delta Time. */
133 		tmp_timer = cur.dk_time[i];
134 		timersub(&tmp_timer, &last.dk_time[i], &cur.dk_time[i]);
135 		last.dk_time[i] = tmp_timer;
136 	}
137 	for (i = 0; i < CPUSTATES; i++) {
138 		long ltmp;
139 
140 		ltmp = cur.cp_time[i];
141 		cur.cp_time[i] -= last.cp_time[i];
142 		last.cp_time[i] = ltmp;
143 	}
144 	SWAP(tk_nin);
145 	SWAP(tk_nout);
146 
147 #undef SWAP
148 }
149 
150 /*
151  * Read the disk statistics for each disk in the disk list.
152  * Also collect statistics for tty i/o and cpu ticks.
153  */
154 void
dkreadstats(void)155 dkreadstats(void)
156 {
157 #if !defined(NOKVM)
158 	struct disk	cur_disk, *p;
159 #endif
160 	int		i, j, mib[3];
161 	size_t		size;
162 	char		*disknames, *name, *bufpp, **dk_name;
163 	struct diskstats *q;
164 
165 	last.dk_ndrive = cur.dk_ndrive;
166 
167 	if (nlistf == NULL && memf == NULL) {
168 		/* Get the number of attached drives. */
169 		mib[0] = CTL_HW;
170 		mib[1] = HW_DISKCOUNT;
171 		size = sizeof(dk_ndrive);
172 		if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) == -1 ) {
173 			warn("could not read hw.diskcount");
174 			dk_ndrive = 0;
175 		}
176 
177 		if (cur.dk_ndrive != dk_ndrive) {
178 			/* Re-read the disk names. */
179 			dk_name = calloc((size_t)dk_ndrive, sizeof(char *));
180 			if (dk_name == NULL)
181 				err(1, NULL);
182 			mib[0] = CTL_HW;
183 			mib[1] = HW_DISKNAMES;
184 			size = 0;
185 			if (sysctl(mib, 2, NULL, &size, NULL, 0) == -1)
186 				err(1, "can't get hw.disknames");
187 			disknames = malloc(size);
188 			if (disknames == NULL)
189 				err(1, NULL);
190 			if (sysctl(mib, 2, disknames, &size, NULL, 0) == -1)
191 				err(1, "can't get hw.disknames");
192 			bufpp = disknames;
193 			for (i = 0; i < dk_ndrive &&
194 			    (name = strsep(&bufpp, ",")) != NULL; i++)
195 				dk_name[i] = name;
196 			for (i = 0; i < dk_ndrive; i++) {
197 				char *ep = strchr(dk_name[i], ':');
198 				if (ep)
199 					*ep = '\0';
200 			}
201 			disknames = cur.dk_name[0];	/* To free old names. */
202 
203 			if (dk_ndrive < cur.dk_ndrive) {
204 				for (i = 0, j = 0; i < dk_ndrive; i++, j++) {
205 					while (j < cur.dk_ndrive &&
206 					    strcmp(cur.dk_name[j], dk_name[i]))
207 						j++;
208 					if (i == j) continue;
209 
210 					if (j >= cur.dk_ndrive) {
211 						cur.dk_select[i] = 1;
212 						last.dk_rxfer[i] = 0;
213 						last.dk_wxfer[i] = 0;
214 						last.dk_seek[i] = 0;
215 						last.dk_rbytes[i] = 0;
216 						last.dk_wbytes[i] = 0;
217 						memset(&last.dk_time[i], 0,
218 						    sizeof(struct timeval));
219 						continue;
220 					}
221 
222 					cur.dk_select[i] = cur.dk_select[j];
223 					last.dk_rxfer[i] = last.dk_rxfer[j];
224 					last.dk_wxfer[i] = last.dk_wxfer[j];
225 					last.dk_seek[i] = last.dk_seek[j];
226 					last.dk_rbytes[i] = last.dk_rbytes[j];
227 					last.dk_wbytes[i] = last.dk_wbytes[j];
228 					last.dk_time[i] = last.dk_time[j];
229 				}
230 
231 				cur.dk_select = reallocarray(cur.dk_select,
232 				    dk_ndrive, sizeof(*cur.dk_select));
233 				cur.dk_rxfer = reallocarray(cur.dk_rxfer,
234 				    dk_ndrive, sizeof(*cur.dk_rxfer));
235 				cur.dk_wxfer = reallocarray(cur.dk_wxfer,
236 				    dk_ndrive, sizeof(*cur.dk_wxfer));
237 				cur.dk_seek = reallocarray(cur.dk_seek,
238 				    dk_ndrive, sizeof(*cur.dk_seek));
239 				cur.dk_rbytes = reallocarray(cur.dk_rbytes,
240 				    dk_ndrive, sizeof(*cur.dk_rbytes));
241 				cur.dk_wbytes = reallocarray(cur.dk_wbytes,
242 				    dk_ndrive, sizeof(*cur.dk_wbytes));
243 				cur.dk_time = reallocarray(cur.dk_time,
244 				    dk_ndrive, sizeof(*cur.dk_time));
245 				last.dk_rxfer = reallocarray(last.dk_rxfer,
246 				    dk_ndrive, sizeof(*last.dk_rxfer));
247 				last.dk_wxfer = reallocarray(last.dk_wxfer,
248 				    dk_ndrive, sizeof(*last.dk_wxfer));
249 				last.dk_seek = reallocarray(last.dk_seek,
250 				    dk_ndrive, sizeof(*last.dk_seek));
251 				last.dk_rbytes = reallocarray(last.dk_rbytes,
252 				    dk_ndrive, sizeof(*last.dk_rbytes));
253 				last.dk_wbytes = reallocarray(last.dk_wbytes,
254 				    dk_ndrive, sizeof(*last.dk_wbytes));
255 				last.dk_time = reallocarray(last.dk_time,
256 				    dk_ndrive, sizeof(*last.dk_time));
257 
258 				if (!cur.dk_select || !cur.dk_rxfer ||
259 				    !cur.dk_wxfer || !cur.dk_seek ||
260 				    !cur.dk_rbytes || !cur.dk_wbytes ||
261 				    !cur.dk_time || !last.dk_rxfer ||
262 				    !last.dk_wxfer || !last.dk_seek ||
263 				    !last.dk_rbytes || !last.dk_wbytes ||
264 				    !last.dk_time)
265 					errx(1, "Memory allocation failure.");
266 			} else {
267 				cur.dk_select = reallocarray(cur.dk_select,
268 				    dk_ndrive, sizeof(*cur.dk_select));
269 				cur.dk_rxfer = reallocarray(cur.dk_rxfer,
270 				    dk_ndrive, sizeof(*cur.dk_rxfer));
271 				cur.dk_wxfer = reallocarray(cur.dk_wxfer,
272 				    dk_ndrive, sizeof(*cur.dk_wxfer));
273 				cur.dk_seek = reallocarray(cur.dk_seek,
274 				    dk_ndrive, sizeof(*cur.dk_seek));
275 				cur.dk_rbytes = reallocarray(cur.dk_rbytes,
276 				    dk_ndrive, sizeof(*cur.dk_rbytes));
277 				cur.dk_wbytes = reallocarray(cur.dk_wbytes,
278 				    dk_ndrive, sizeof(*cur.dk_wbytes));
279 				cur.dk_time = reallocarray(cur.dk_time,
280 				    dk_ndrive, sizeof(*cur.dk_time));
281 				last.dk_rxfer = reallocarray(last.dk_rxfer,
282 				    dk_ndrive, sizeof(*last.dk_rxfer));
283 				last.dk_wxfer = reallocarray(last.dk_wxfer,
284 				    dk_ndrive, sizeof(*last.dk_wxfer));
285 				last.dk_seek = reallocarray(last.dk_seek,
286 				    dk_ndrive, sizeof(*last.dk_seek));
287 				last.dk_rbytes = reallocarray(last.dk_rbytes,
288 				    dk_ndrive, sizeof(*last.dk_rbytes));
289 				last.dk_wbytes = reallocarray(last.dk_wbytes,
290 				    dk_ndrive, sizeof(*last.dk_wbytes));
291 				last.dk_time = reallocarray(last.dk_time,
292 				    dk_ndrive, sizeof(*last.dk_time));
293 
294 				if (!cur.dk_select || !cur.dk_rxfer ||
295 				    !cur.dk_wxfer || !cur.dk_seek ||
296 				    !cur.dk_rbytes || !cur.dk_wbytes ||
297 				    !cur.dk_time || !last.dk_rxfer ||
298 				    !last.dk_wxfer || !last.dk_seek ||
299 				    !last.dk_rbytes || !last.dk_wbytes ||
300 				    !last.dk_time)
301 					errx(1, "Memory allocation failure.");
302 
303 				for (i = dk_ndrive - 1, j = cur.dk_ndrive - 1;
304 				     i >= 0; i--) {
305 
306 					if (j < 0 ||
307 					    strcmp(cur.dk_name[j], dk_name[i]))
308 					{
309 						cur.dk_select[i] = 1;
310 						last.dk_rxfer[i] = 0;
311 						last.dk_wxfer[i] = 0;
312 						last.dk_seek[i] = 0;
313 						last.dk_rbytes[i] = 0;
314 						last.dk_wbytes[i] = 0;
315 						memset(&last.dk_time[i], 0,
316 						    sizeof(struct timeval));
317 						continue;
318 					}
319 
320 					if (i > j) {
321 						cur.dk_select[i] =
322 						    cur.dk_select[j];
323 						last.dk_rxfer[i] =
324 						    last.dk_rxfer[j];
325 						last.dk_wxfer[i] =
326 						    last.dk_wxfer[j];
327 						last.dk_seek[i] =
328 						    last.dk_seek[j];
329 						last.dk_rbytes[i] =
330 						    last.dk_rbytes[j];
331 						last.dk_wbytes[i] =
332 						    last.dk_wbytes[j];
333 						last.dk_time[i] =
334 						    last.dk_time[j];
335 					}
336 					j--;
337 				}
338 			}
339 
340 			cur.dk_ndrive = dk_ndrive;
341 			free(disknames);
342 			cur.dk_name = dk_name;
343 			dr_name = cur.dk_name;
344 			dk_select = cur.dk_select;
345 		}
346 
347 		size = cur.dk_ndrive * sizeof(struct diskstats);
348 		mib[0] = CTL_HW;
349 		mib[1] = HW_DISKSTATS;
350 		q = malloc(size);
351 		if (q == NULL)
352 			err(1, NULL);
353 		if (sysctl(mib, 2, q, &size, NULL, 0) == -1) {
354 #ifdef	DEBUG
355 			warn("could not read hw.diskstats");
356 #endif	/* DEBUG */
357 			memset(q, 0, cur.dk_ndrive * sizeof(struct diskstats));
358 		}
359 
360 		for (i = 0; i < cur.dk_ndrive; i++)	{
361 			cur.dk_rxfer[i] = q[i].ds_rxfer;
362 			cur.dk_wxfer[i] = q[i].ds_wxfer;
363 			cur.dk_seek[i] = q[i].ds_seek;
364 			cur.dk_rbytes[i] = q[i].ds_rbytes;
365 			cur.dk_wbytes[i] = q[i].ds_wbytes;
366 			cur.dk_time[i] = q[i].ds_time;
367 		}
368 		free(q);
369 
370 		size = sizeof(cur.cp_time);
371 		mib[0] = CTL_KERN;
372 		mib[1] = KERN_CPTIME;
373 		if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) == -1) {
374 			warn("could not read kern.cp_time");
375 			memset(cur.cp_time, 0, sizeof(cur.cp_time));
376 		}
377 		size = sizeof(cur.tk_nin);
378 		mib[0] = CTL_KERN;
379 		mib[1] = KERN_TTY;
380 		mib[2] = KERN_TTY_TKNIN;
381 		if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) == -1) {
382 			warn("could not read kern.tty.tk_nin");
383 			cur.tk_nin = 0;
384 		}
385 		size = sizeof(cur.tk_nin);
386 		mib[0] = CTL_KERN;
387 		mib[1] = KERN_TTY;
388 		mib[2] = KERN_TTY_TKNOUT;
389 		if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) == -1) {
390 			warn("could not read kern.tty.tk_nout");
391 			cur.tk_nout = 0;
392 		}
393 	} else {
394 #if !defined(NOKVM)
395 		p = dk_drivehead;
396 
397 		for (i = 0; i < cur.dk_ndrive; i++) {
398 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
399 			cur.dk_rxfer[i] = cur_disk.dk_rxfer;
400 			cur.dk_wxfer[i] = cur_disk.dk_wxfer;
401 			cur.dk_seek[i] = cur_disk.dk_seek;
402 			cur.dk_rbytes[i] = cur_disk.dk_rbytes;
403 			cur.dk_wbytes[i] = cur_disk.dk_wbytes;
404 			cur.dk_time[i] = cur_disk.dk_time;
405 			p = TAILQ_NEXT(&cur_disk, dk_link);
406 		}
407 		deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
408 		deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
409 		deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
410 #endif /* !defined(NOKVM) */
411 	}
412 }
413 
414 /*
415  * Perform all of the initialization and memory allocation needed to
416  * track disk statistics.
417  */
418 int
dkinit(int sel)419 dkinit(int sel)
420 {
421 #if !defined(NOKVM)
422 	struct disklist_head disk_head;
423 	struct disk	cur_disk, *p;
424         char		errbuf[_POSIX2_LINE_MAX];
425 #endif
426 	static int	once = 0;
427 	extern int	hz;
428 	int		i, mib[2];
429 	size_t		size;
430 	struct clockinfo clkinfo;
431 	char		*disknames, *name, *bufpp;
432 
433 	if (once)
434 		return(1);
435 
436 	if (nlistf != NULL || memf != NULL) {
437 #if !defined(NOKVM)
438 		/* Open the kernel. */
439 		if (kd == NULL &&
440 		    (kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
441 		    errbuf)) == NULL)
442 			errx(1, "kvm_openfiles: %s", errbuf);
443 
444 		/* Obtain the namelist symbols from the kernel. */
445 		if (kvm_nlist(kd, namelist))
446 			KVM_ERROR("kvm_nlist failed to read symbols.");
447 
448 		/* Get the number of attached drives. */
449 		deref_nl(X_DISK_COUNT, &cur.dk_ndrive, sizeof(cur.dk_ndrive));
450 
451 		if (cur.dk_ndrive < 0)
452 			errx(1, "invalid _disk_count %d.", cur.dk_ndrive);
453 
454 		/* Get a pointer to the first disk. */
455 		deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
456 		dk_drivehead = TAILQ_FIRST(&disk_head);
457 
458 		/* Get ticks per second. */
459 		deref_nl(X_STATHZ, &hz, sizeof(hz));
460 		if (!hz)
461 		  deref_nl(X_HZ, &hz, sizeof(hz));
462 #endif /* !defined(NOKVM) */
463 	} else {
464 		/* Get the number of attached drives. */
465 		mib[0] = CTL_HW;
466 		mib[1] = HW_DISKCOUNT;
467 		size = sizeof(cur.dk_ndrive);
468 		if (sysctl(mib, 2, &cur.dk_ndrive, &size, NULL, 0) == -1 ) {
469 			warn("could not read hw.diskcount");
470 			cur.dk_ndrive = 0;
471 		}
472 
473 		/* Get ticks per second. */
474 		mib[0] = CTL_KERN;
475 		mib[1] = KERN_CLOCKRATE;
476 		size = sizeof(clkinfo);
477 		if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) == -1) {
478 			warn("could not read kern.clockrate");
479 			hz = 0;
480 		} else
481 			hz = clkinfo.stathz;
482 	}
483 
484 	/* allocate space for the statistics */
485 	cur.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
486 	cur.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
487 	cur.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
488 	cur.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
489 	cur.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
490 	cur.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
491 	cur.dk_select = calloc((size_t)cur.dk_ndrive, sizeof(int));
492 	cur.dk_name = calloc((size_t)cur.dk_ndrive, sizeof(char *));
493 	last.dk_time = calloc((size_t)cur.dk_ndrive, sizeof(struct timeval));
494 	last.dk_rxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
495 	last.dk_wxfer = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
496 	last.dk_seek = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
497 	last.dk_rbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
498 	last.dk_wbytes = calloc((size_t)cur.dk_ndrive, sizeof(u_int64_t));
499 
500 	if (!cur.dk_time || !cur.dk_rxfer || !cur.dk_wxfer || !cur.dk_seek ||
501 	    !cur.dk_rbytes || !cur.dk_wbytes || !cur.dk_select ||
502 	    !cur.dk_name || !last.dk_time || !last.dk_rxfer ||
503 	    !last.dk_wxfer || !last.dk_seek || !last.dk_rbytes ||
504 	    !last.dk_wbytes)
505 		errx(1, "Memory allocation failure.");
506 
507 	/* Set up the compatibility interfaces. */
508 	dk_ndrive = cur.dk_ndrive;
509 	dk_select = cur.dk_select;
510 	dr_name = cur.dk_name;
511 
512 	/* Read the disk names and set initial selection. */
513 	if (nlistf == NULL && memf == NULL) {
514 		mib[0] = CTL_HW;
515 		mib[1] = HW_DISKNAMES;
516 		size = 0;
517 		if (sysctl(mib, 2, NULL, &size, NULL, 0) == -1)
518 			err(1, "can't get hw.disknames");
519 		disknames = malloc(size);
520 		if (disknames == NULL)
521 			err(1, NULL);
522 		if (sysctl(mib, 2, disknames, &size, NULL, 0) == -1)
523 			err(1, "can't get hw.disknames");
524 		bufpp = disknames;
525 		for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) {
526 			cur.dk_name[i] = name;
527 			cur.dk_select[i] = sel;
528 		}
529 		for (i = 0; i < dk_ndrive; i++) {
530 			char *ep = strchr(cur.dk_name[i], ':');
531 			if (ep)
532 				*ep = '\0';
533 		}
534 	} else {
535 #if !defined(NOKVM)
536 		p = dk_drivehead;
537 		for (i = 0; i < cur.dk_ndrive; i++) {
538 			char	buf[10];
539 
540 			deref_kptr(p, &cur_disk, sizeof(cur_disk));
541 			deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
542 			cur.dk_name[i] = strdup(buf);
543 			if (!cur.dk_name[i])
544 				errx(1, "Memory allocation failure.");
545 			cur.dk_select[i] = sel;
546 
547 			p = TAILQ_NEXT(&cur_disk, dk_link);
548 		}
549 #endif /* !defined(NOKVM) */
550 	}
551 
552 	/* Never do this initialization again. */
553 	once = 1;
554 	return(1);
555 }
556 
557 #if !defined(NOKVM)
558 /*
559  * Dereference the kernel pointer `kptr' and fill in the local copy
560  * pointed to by `ptr'.  The storage space must be pre-allocated,
561  * and the size of the copy passed in `len'.
562  */
563 static void
deref_kptr(void * kptr,void * ptr,size_t len)564 deref_kptr(void *kptr, void *ptr, size_t len)
565 {
566 	char buf[128];
567 
568 	if (kvm_read(kd, (u_long)kptr, ptr, len) != len) {
569 		memset(buf, 0, sizeof(buf));
570 		snprintf(buf, (sizeof(buf) - 1),
571 		     "can't dereference kptr 0x%lx", (u_long)kptr);
572 		KVM_ERROR(buf);
573 	}
574 }
575 #endif /* !defined(NOKVM) */
576