Home | History | Annotate | Line # | Download | only in dkctl
dkctl.c revision 1.22
      1 /*	$NetBSD: dkctl.c,v 1.22 2014/11/23 15:43:49 christos Exp $	*/
      2 
      3 /*
      4  * Copyright 2001 Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *	This product includes software developed for the NetBSD Project by
     20  *	Wasabi Systems, Inc.
     21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     22  *    or promote products derived from this software without specific prior
     23  *    written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  * POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 
     38 /*
     39  * dkctl(8) -- a program to manipulate disks.
     40  */
     41 #include <sys/cdefs.h>
     42 
     43 #ifndef lint
     44 __RCSID("$NetBSD: dkctl.c,v 1.22 2014/11/23 15:43:49 christos Exp $");
     45 #endif
     46 
     47 #include <sys/param.h>
     48 #include <sys/ioctl.h>
     49 #include <sys/dkio.h>
     50 #include <sys/disk.h>
     51 #include <sys/queue.h>
     52 #include <err.h>
     53 #include <errno.h>
     54 #include <fcntl.h>
     55 #include <stdio.h>
     56 #include <stdlib.h>
     57 #include <stdbool.h>
     58 #include <string.h>
     59 #include <unistd.h>
     60 #include <util.h>
     61 
     62 #define	YES	1
     63 #define	NO	0
     64 
     65 /* I don't think nl_langinfo is suitable in this case */
     66 #define	YES_STR	"yes"
     67 #define	NO_STR	"no"
     68 #define YESNO_ARG	YES_STR " | " NO_STR
     69 
     70 #ifndef PRIdaddr
     71 #define PRIdaddr PRId64
     72 #endif
     73 
     74 struct command {
     75 	const char *cmd_name;
     76 	const char *arg_names;
     77 	void (*cmd_func)(int, char *[]);
     78 	int open_flags;
     79 };
     80 
     81 static struct command *lookup(const char *);
     82 __dead static void	usage(void);
     83 static void	run(int, char *[]);
     84 static void	showall(void);
     85 
     86 static int	fd;				/* file descriptor for device */
     87 static const	char *dvname;			/* device name */
     88 static char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
     89 
     90 static int dkw_sort(const void *, const void *);
     91 static int yesno(const char *);
     92 
     93 static void	disk_getcache(int, char *[]);
     94 static void	disk_setcache(int, char *[]);
     95 static void	disk_synccache(int, char *[]);
     96 static void	disk_keeplabel(int, char *[]);
     97 static void	disk_badsectors(int, char *[]);
     98 
     99 static void	disk_addwedge(int, char *[]);
    100 static void	disk_delwedge(int, char *[]);
    101 static void	disk_getwedgeinfo(int, char *[]);
    102 static void	disk_listwedges(int, char *[]);
    103 static void	disk_makewedges(int, char *[]);
    104 static void	disk_strategy(int, char *[]);
    105 
    106 static struct command commands[] = {
    107 	{ "getcache",
    108 	  "",
    109 	  disk_getcache,
    110 	  O_RDONLY },
    111 
    112 	{ "setcache",
    113 	  "none | r | w | rw [save]",
    114 	  disk_setcache,
    115 	  O_RDWR },
    116 
    117 	{ "synccache",
    118 	  "[force]",
    119 	  disk_synccache,
    120 	  O_RDWR },
    121 
    122 	{ "keeplabel",
    123 	  YESNO_ARG,
    124 	  disk_keeplabel,
    125 	  O_RDWR },
    126 
    127 	{ "badsector",
    128 	  "flush | list | retry",
    129 	   disk_badsectors,
    130 	   O_RDWR },
    131 
    132 	{ "addwedge",
    133 	  "name startblk blkcnt ptype",
    134 	  disk_addwedge,
    135 	  O_RDWR },
    136 
    137 	{ "delwedge",
    138 	  "dk",
    139 	  disk_delwedge,
    140 	  O_RDWR },
    141 
    142 	{ "getwedgeinfo",
    143 	  "",
    144 	  disk_getwedgeinfo,
    145 	  O_RDONLY },
    146 
    147 	{ "listwedges",
    148 	  "",
    149 	  disk_listwedges,
    150 	  O_RDONLY },
    151 
    152 	{ "makewedges",
    153 	  "",
    154 	  disk_makewedges,
    155 	  O_RDWR },
    156 
    157 	{ "strategy",
    158 	  "[name]",
    159 	  disk_strategy,
    160 	  O_RDWR },
    161 
    162 	{ NULL,
    163 	  NULL,
    164 	  NULL,
    165 	  0 },
    166 };
    167 
    168 int
    169 main(int argc, char *argv[])
    170 {
    171 
    172 	/* Must have at least: device command */
    173 	if (argc < 2)
    174 		usage();
    175 
    176 	dvname = argv[1];
    177 	if (argc == 2)
    178 		showall();
    179 	else
    180 		run(argc - 2, argv + 2);
    181 
    182 	return EXIT_SUCCESS;
    183 }
    184 
    185 static void
    186 run(int argc, char *argv[])
    187 {
    188 	struct command *command;
    189 
    190 	command = lookup(argv[0]);
    191 
    192 	/* Open the device. */
    193 	fd = opendisk(dvname, command->open_flags, dvname_store,
    194 	    sizeof(dvname_store), 0);
    195 	if (fd == -1)
    196 		err(EXIT_FAILURE, "%s", dvname);
    197 	dvname = dvname_store;
    198 
    199 	(*command->cmd_func)(argc, argv);
    200 
    201 	/* Close the device. */
    202 	(void)close(fd);
    203 }
    204 
    205 static struct command *
    206 lookup(const char *name)
    207 {
    208 	int i;
    209 
    210 	/* Look up the command. */
    211 	for (i = 0; commands[i].cmd_name != NULL; i++)
    212 		if (strcmp(name, commands[i].cmd_name) == 0)
    213 			break;
    214 	if (commands[i].cmd_name == NULL)
    215 		errx(EXIT_FAILURE, "unknown command: %s", name);
    216 
    217 	return &commands[i];
    218 }
    219 
    220 static void
    221 usage(void)
    222 {
    223 	int i;
    224 
    225 	fprintf(stderr,
    226 	    "Usage: %s device\n"
    227 	    "       %s device command [arg [...]]\n",
    228 	    getprogname(), getprogname());
    229 
    230 	fprintf(stderr, "   Available commands:\n");
    231 	for (i = 0; commands[i].cmd_name != NULL; i++)
    232 		fprintf(stderr, "\t%s %s\n", commands[i].cmd_name,
    233 		    commands[i].arg_names);
    234 
    235 	exit(EXIT_FAILURE);
    236 }
    237 
    238 static void
    239 showall(void)
    240 {
    241 	static const char *cmds[] = { "strategy", "getcache", "listwedges" };
    242 	size_t i;
    243 	char *args[2];
    244 
    245 	args[1] = NULL;
    246 	for (i = 0; i < __arraycount(cmds); i++) {
    247 		printf("%s:\n", cmds[i]);
    248 		args[0] = __UNCONST(cmds[i]);
    249 		run(1, args);
    250 		putchar('\n');
    251 	}
    252 }
    253 
    254 static void
    255 disk_strategy(int argc, char *argv[])
    256 {
    257 	struct disk_strategy odks;
    258 	struct disk_strategy dks;
    259 
    260 	memset(&dks, 0, sizeof(dks));
    261 	if (ioctl(fd, DIOCGSTRATEGY, &odks) == -1) {
    262 		err(EXIT_FAILURE, "%s: DIOCGSTRATEGY", dvname);
    263 	}
    264 
    265 	memset(&dks, 0, sizeof(dks));
    266 	switch (argc) {
    267 	case 1:
    268 		/* show the buffer queue strategy used */
    269 		printf("%s: %s\n", dvname, odks.dks_name);
    270 		return;
    271 	case 2:
    272 		/* set the buffer queue strategy */
    273 		strlcpy(dks.dks_name, argv[1], sizeof(dks.dks_name));
    274 		if (ioctl(fd, DIOCSSTRATEGY, &dks) == -1) {
    275 			err(EXIT_FAILURE, "%s: DIOCSSTRATEGY", dvname);
    276 		}
    277 		printf("%s: %s -> %s\n", dvname, odks.dks_name, argv[1]);
    278 		break;
    279 	default:
    280 		usage();
    281 		/* NOTREACHED */
    282 	}
    283 }
    284 
    285 static void
    286 disk_getcache(int argc, char *argv[])
    287 {
    288 	int bits;
    289 
    290 	if (ioctl(fd, DIOCGCACHE, &bits) == -1)
    291 		err(EXIT_FAILURE, "%s: getcache", dvname);
    292 
    293 	if ((bits & (DKCACHE_READ|DKCACHE_WRITE)) == 0)
    294 		printf("%s: No caches enabled\n", dvname);
    295 	else {
    296 		if (bits & DKCACHE_READ)
    297 			printf("%s: read cache enabled\n", dvname);
    298 		if (bits & DKCACHE_WRITE)
    299 			printf("%s: write-back cache enabled\n", dvname);
    300 	}
    301 
    302 	printf("%s: read cache enable is %schangeable\n", dvname,
    303 	    (bits & DKCACHE_RCHANGE) ? "" : "not ");
    304 	printf("%s: write cache enable is %schangeable\n", dvname,
    305 	    (bits & DKCACHE_WCHANGE) ? "" : "not ");
    306 
    307 	printf("%s: cache parameters are %ssavable\n", dvname,
    308 	    (bits & DKCACHE_SAVE) ? "" : "not ");
    309 }
    310 
    311 static void
    312 disk_setcache(int argc, char *argv[])
    313 {
    314 	int bits;
    315 
    316 	if (argc > 3 || argc == 1)
    317 		usage();
    318 
    319 	if (strcmp(argv[1], "none") == 0)
    320 		bits = 0;
    321 	else if (strcmp(argv[1], "r") == 0)
    322 		bits = DKCACHE_READ;
    323 	else if (strcmp(argv[1], "w") == 0)
    324 		bits = DKCACHE_WRITE;
    325 	else if (strcmp(argv[1], "rw") == 0)
    326 		bits = DKCACHE_READ|DKCACHE_WRITE;
    327 	else
    328 		usage();
    329 
    330 	if (argc == 3) {
    331 		if (strcmp(argv[2], "save") == 0)
    332 			bits |= DKCACHE_SAVE;
    333 		else
    334 			usage();
    335 	}
    336 
    337 	if (ioctl(fd, DIOCSCACHE, &bits) == -1)
    338 		err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
    339 }
    340 
    341 static void
    342 disk_synccache(int argc, char *argv[])
    343 {
    344 	int force;
    345 
    346 	switch (argc) {
    347 	case 1:
    348 		force = 0;
    349 		break;
    350 
    351 	case 2:
    352 		if (strcmp(argv[1], "force") == 0)
    353 			force = 1;
    354 		else
    355 			usage();
    356 		break;
    357 
    358 	default:
    359 		usage();
    360 	}
    361 
    362 	if (ioctl(fd, DIOCCACHESYNC, &force) == -1)
    363 		err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
    364 }
    365 
    366 static void
    367 disk_keeplabel(int argc, char *argv[])
    368 {
    369 	int keep;
    370 	int yn;
    371 
    372 	if (argc != 2)
    373 		usage();
    374 
    375 	yn = yesno(argv[1]);
    376 	if (yn < 0)
    377 		usage();
    378 
    379 	keep = yn == YES;
    380 
    381 	if (ioctl(fd, DIOCKLABEL, &keep) == -1)
    382 		err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
    383 }
    384 
    385 
    386 static void
    387 disk_badsectors(int argc, char *argv[])
    388 {
    389 	struct disk_badsectors *dbs, *dbs2, buffer[200];
    390 	SLIST_HEAD(, disk_badsectors) dbstop;
    391 	struct disk_badsecinfo dbsi;
    392 	daddr_t blk, totbad, bad;
    393 	u_int32_t count;
    394 	struct stat sb;
    395 	u_char *block;
    396 	time_t tm;
    397 
    398 	if (argc != 2)
    399 		usage();
    400 
    401 	if (strcmp(argv[1], "list") == 0) {
    402 		/*
    403 		 * Copy the list of kernel bad sectors out in chunks that fit
    404 		 * into buffer[].  Updating dbsi_skip means we don't sit here
    405 		 * forever only getting the first chunk that fit in buffer[].
    406 		 */
    407 		dbsi.dbsi_buffer = (caddr_t)buffer;
    408 		dbsi.dbsi_bufsize = sizeof(buffer);
    409 		dbsi.dbsi_skip = 0;
    410 		dbsi.dbsi_copied = 0;
    411 		dbsi.dbsi_left = 0;
    412 
    413 		do {
    414 			if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1)
    415 				err(EXIT_FAILURE, "%s: badsectors list", dvname);
    416 
    417 			dbs = (struct disk_badsectors *)dbsi.dbsi_buffer;
    418 			for (count = dbsi.dbsi_copied; count > 0; count--) {
    419 				tm = dbs->dbs_failedat.tv_sec;
    420 				printf("%s: blocks %" PRIdaddr " - %" PRIdaddr " failed at %s",
    421 					dvname, dbs->dbs_min, dbs->dbs_max,
    422 					ctime(&tm));
    423 				dbs++;
    424 			}
    425 			dbsi.dbsi_skip += dbsi.dbsi_copied;
    426 		} while (dbsi.dbsi_left != 0);
    427 
    428 	} else if (strcmp(argv[1], "flush") == 0) {
    429 		if (ioctl(fd, DIOCBSFLUSH) == -1)
    430 			err(EXIT_FAILURE, "%s: badsectors flush", dvname);
    431 
    432 	} else if (strcmp(argv[1], "retry") == 0) {
    433 		/*
    434 		 * Enforce use of raw device here because the block device
    435 		 * causes access to blocks to be clustered in a larger group,
    436 		 * making it impossible to determine which individual sectors
    437 		 * are the cause of a problem.
    438 		 */
    439 		if (fstat(fd, &sb) == -1)
    440 			err(EXIT_FAILURE, "fstat");
    441 
    442 		if (!S_ISCHR(sb.st_mode)) {
    443 			fprintf(stderr, "'badsector retry' must be used %s\n",
    444 				"with character device");
    445 			exit(1);
    446 		}
    447 
    448 		SLIST_INIT(&dbstop);
    449 
    450 		/*
    451 		 * Build up a copy of the in-kernel list in a number of stages.
    452 		 * That the list we build up here is in the reverse order to
    453 		 * the kernel's is of no concern.
    454 		 */
    455 		dbsi.dbsi_buffer = (caddr_t)buffer;
    456 		dbsi.dbsi_bufsize = sizeof(buffer);
    457 		dbsi.dbsi_skip = 0;
    458 		dbsi.dbsi_copied = 0;
    459 		dbsi.dbsi_left = 0;
    460 
    461 		do {
    462 			if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1)
    463 				err(EXIT_FAILURE, "%s: badsectors list", dvname);
    464 
    465 			dbs = (struct disk_badsectors *)dbsi.dbsi_buffer;
    466 			for (count = dbsi.dbsi_copied; count > 0; count--) {
    467 				dbs2 = malloc(sizeof *dbs2);
    468 				if (dbs2 == NULL)
    469 					err(EXIT_FAILURE, NULL);
    470 				*dbs2 = *dbs;
    471 				SLIST_INSERT_HEAD(&dbstop, dbs2, dbs_next);
    472 				dbs++;
    473 			}
    474 			dbsi.dbsi_skip += dbsi.dbsi_copied;
    475 		} while (dbsi.dbsi_left != 0);
    476 
    477 		/*
    478 		 * Just calculate and print out something that will hopefully
    479 		 * provide some useful information about what's going to take
    480 		 * place next (if anything.)
    481 		 */
    482 		bad = 0;
    483 		totbad = 0;
    484 		if ((block = calloc(1, DEV_BSIZE)) == NULL)
    485 			err(EXIT_FAILURE, NULL);
    486 		SLIST_FOREACH(dbs, &dbstop, dbs_next) {
    487 			bad++;
    488 			totbad += dbs->dbs_max - dbs->dbs_min + 1;
    489 		}
    490 
    491 		printf("%s: bad sector clusters %"PRIdaddr
    492 		    " total sectors %"PRIdaddr"\n", dvname, bad, totbad);
    493 
    494 		/*
    495 		 * Clear out the kernel's list of bad sectors, ready for us
    496 		 * to test all those it thought were bad.
    497 		 */
    498 		if (ioctl(fd, DIOCBSFLUSH) == -1)
    499 			err(EXIT_FAILURE, "%s: badsectors flush", dvname);
    500 
    501 		printf("%s: bad sectors flushed\n", dvname);
    502 
    503 		/*
    504 		 * For each entry we obtained from the kernel, retry each
    505 		 * individual sector recorded as bad by seeking to it and
    506 		 * attempting to read it in.  Print out a line item for each
    507 		 * bad block we verify.
    508 		 *
    509 		 * PRIdaddr is used here because the type of dbs_max is daddr_t
    510 		 * and that may be either a 32bit or 64bit number(!)
    511 		 */
    512 		SLIST_FOREACH(dbs, &dbstop, dbs_next) {
    513 			printf("%s: Retrying %"PRIdaddr" - %"
    514 			    PRIdaddr"\n", dvname, dbs->dbs_min, dbs->dbs_max);
    515 
    516 			for (blk = dbs->dbs_min; blk <= dbs->dbs_max; blk++) {
    517 				if (lseek(fd, (off_t)blk * DEV_BSIZE,
    518 				    SEEK_SET) == -1) {
    519 					warn("%s: lseek block %" PRIdaddr "",
    520 					    dvname, blk);
    521 					continue;
    522 				}
    523 				printf("%s: block %"PRIdaddr" - ", dvname, blk);
    524 				if (read(fd, block, DEV_BSIZE) != DEV_BSIZE)
    525 					printf("failed\n");
    526 				else
    527 					printf("ok\n");
    528 				fflush(stdout);
    529 			}
    530 		}
    531 	}
    532 }
    533 
    534 static void
    535 disk_addwedge(int argc, char *argv[])
    536 {
    537 	struct dkwedge_info dkw;
    538 	char *cp;
    539 	daddr_t start;
    540 	uint64_t size;
    541 
    542 	if (argc != 5)
    543 		usage();
    544 
    545 	/* XXX Unicode: dkw_wname is supposed to be utf-8 */
    546 	if (strlcpy((char *)dkw.dkw_wname, argv[1], sizeof(dkw.dkw_wname)) >=
    547 	    sizeof(dkw.dkw_wname))
    548 		errx(EXIT_FAILURE, "Wedge name too long; max %zd characters",
    549 		    sizeof(dkw.dkw_wname) - 1);
    550 
    551 	if (strlcpy(dkw.dkw_ptype, argv[4], sizeof(dkw.dkw_ptype)) >=
    552 	    sizeof(dkw.dkw_ptype))
    553 		errx(EXIT_FAILURE, "Wedge partition type too long; max %zd characters",
    554 		    sizeof(dkw.dkw_ptype) - 1);
    555 
    556 	errno = 0;
    557 	start = strtoll(argv[2], &cp, 0);
    558 	if (*cp != '\0')
    559 		errx(EXIT_FAILURE, "Invalid start block: %s", argv[2]);
    560 	if (errno == ERANGE && (start == LLONG_MAX ||
    561 				start == LLONG_MIN))
    562 		errx(EXIT_FAILURE, "Start block out of range.");
    563 	if (start < 0)
    564 		errx(EXIT_FAILURE, "Start block must be >= 0.");
    565 
    566 	errno = 0;
    567 	size = strtoull(argv[3], &cp, 0);
    568 	if (*cp != '\0')
    569 		errx(EXIT_FAILURE, "Invalid block count: %s", argv[3]);
    570 	if (errno == ERANGE && (size == ULLONG_MAX))
    571 		errx(EXIT_FAILURE, "Block count out of range.");
    572 
    573 	dkw.dkw_offset = start;
    574 	dkw.dkw_size = size;
    575 
    576 	if (ioctl(fd, DIOCAWEDGE, &dkw) == -1)
    577 		err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
    578 	else
    579 		printf("%s created successfully.\n", dkw.dkw_devname);
    580 
    581 }
    582 
    583 static void
    584 disk_delwedge(int argc, char *argv[])
    585 {
    586 	struct dkwedge_info dkw;
    587 
    588 	if (argc != 2)
    589 		usage();
    590 
    591 	if (strlcpy(dkw.dkw_devname, argv[1], sizeof(dkw.dkw_devname)) >=
    592 	    sizeof(dkw.dkw_devname))
    593 		errx(EXIT_FAILURE, "Wedge dk name too long; max %zd characters",
    594 		    sizeof(dkw.dkw_devname) - 1);
    595 
    596 	if (ioctl(fd, DIOCDWEDGE, &dkw) == -1)
    597 		err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
    598 }
    599 
    600 static void
    601 disk_getwedgeinfo(int argc, char *argv[])
    602 {
    603 	struct dkwedge_info dkw;
    604 
    605 	if (argc != 1)
    606 		usage();
    607 
    608 	if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == -1)
    609 		err(EXIT_FAILURE, "%s: getwedgeinfo", dvname);
    610 
    611 	printf("%s at %s: %s\n", dkw.dkw_devname, dkw.dkw_parent,
    612 	    dkw.dkw_wname);	/* XXX Unicode */
    613 	printf("%s: %"PRIu64" blocks at %"PRId64", type: %s\n",
    614 	    dkw.dkw_devname, dkw.dkw_size, dkw.dkw_offset, dkw.dkw_ptype);
    615 }
    616 
    617 static void
    618 disk_listwedges(int argc, char *argv[])
    619 {
    620 	struct dkwedge_info *dkw;
    621 	struct dkwedge_list dkwl;
    622 	size_t bufsize;
    623 	u_int i;
    624 	int c;
    625 	bool error, quiet;
    626 
    627 	optreset = 1;
    628 	optind = 1;
    629 	quiet = error = false;
    630 	while ((c = getopt(argc, argv, "qe")) != -1)
    631 		switch (c) {
    632 		case 'e':
    633 			error = true;
    634 			break;
    635 		case 'q':
    636 			quiet = true;
    637 			break;
    638 		default:
    639 			usage();
    640 		}
    641 
    642 	argc -= optind;
    643 	argv += optind;
    644 
    645 	if (argc != 0)
    646 		usage();
    647 
    648 	dkw = NULL;
    649 	dkwl.dkwl_buf = dkw;
    650 	dkwl.dkwl_bufsize = 0;
    651 
    652 	for (;;) {
    653 		if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1)
    654 			err(EXIT_FAILURE, "%s: listwedges", dvname);
    655 		if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied)
    656 			break;
    657 		bufsize = dkwl.dkwl_nwedges * sizeof(*dkw);
    658 		if (dkwl.dkwl_bufsize < bufsize) {
    659 			dkw = realloc(dkwl.dkwl_buf, bufsize);
    660 			if (dkw == NULL)
    661 				errx(EXIT_FAILURE, "%s: listwedges: unable to "
    662 				    "allocate wedge info buffer", dvname);
    663 			dkwl.dkwl_buf = dkw;
    664 			dkwl.dkwl_bufsize = bufsize;
    665 		}
    666 	}
    667 
    668 	if (dkwl.dkwl_nwedges == 0) {
    669 		if (!quiet)
    670 			printf("%s: no wedges configured\n", dvname);
    671 		if (error)
    672 			exit(EXIT_FAILURE);
    673 		return;
    674 	}
    675 
    676 	qsort(dkw, dkwl.dkwl_nwedges, sizeof(*dkw), dkw_sort);
    677 
    678 	printf("%s: %u wedge%s:\n", dvname, dkwl.dkwl_nwedges,
    679 	    dkwl.dkwl_nwedges == 1 ? "" : "s");
    680 	for (i = 0; i < dkwl.dkwl_nwedges; i++) {
    681 		printf("%s: %s, %"PRIu64" blocks at %"PRId64", type: %s\n",
    682 		    dkw[i].dkw_devname,
    683 		    dkw[i].dkw_wname,	/* XXX Unicode */
    684 		    dkw[i].dkw_size, dkw[i].dkw_offset, dkw[i].dkw_ptype);
    685 	}
    686 }
    687 
    688 static void
    689 disk_makewedges(int argc, char *argv[])
    690 {
    691 	int bits;
    692 
    693 	if (argc != 1)
    694 		usage();
    695 
    696 	if (ioctl(fd, DIOCMWEDGES, &bits) == -1)
    697 		err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
    698 	else
    699 		printf("successfully scanned %s.\n", dvname);
    700 }
    701 
    702 static int
    703 dkw_sort(const void *a, const void *b)
    704 {
    705 	const struct dkwedge_info *dkwa = a, *dkwb = b;
    706 	const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset;
    707 
    708 	return (oa < ob) ? -1 : (oa > ob) ? 1 : 0;
    709 }
    710 
    711 /*
    712  * return YES, NO or -1.
    713  */
    714 static int
    715 yesno(const char *p)
    716 {
    717 
    718 	if (!strcmp(p, YES_STR))
    719 		return YES;
    720 	if (!strcmp(p, NO_STR))
    721 		return NO;
    722 	return -1;
    723 }
    724