Home | History | Annotate | Line # | Download | only in dkctl
dkctl.c revision 1.9
      1 /*	$NetBSD: dkctl.c,v 1.9 2004/09/25 03:31:35 thorpej 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.9 2004/09/25 03:31:35 thorpej Exp $");
     45 #endif
     46 
     47 
     48 #include <sys/param.h>
     49 #include <sys/ioctl.h>
     50 #include <sys/dkio.h>
     51 #include <sys/disk.h>
     52 #include <sys/queue.h>
     53 #include <err.h>
     54 #include <errno.h>
     55 #include <fcntl.h>
     56 #include <stdio.h>
     57 #include <stdlib.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 int	main(int, char *[]);
     82 void	usage(void);
     83 
     84 int	fd;				/* file descriptor for device */
     85 const	char *dvname;			/* device name */
     86 char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
     87 const	char *cmdname;			/* command user issued */
     88 const	char *argnames;			/* helpstring; expected arguments */
     89 
     90 int yesno(const char *);
     91 
     92 void	disk_getcache(int, char *[]);
     93 void	disk_setcache(int, char *[]);
     94 void	disk_synccache(int, char *[]);
     95 void	disk_keeplabel(int, char *[]);
     96 void	disk_badsectors(int, char *[]);
     97 
     98 void	disk_addwedge(int, char *[]);
     99 void	disk_delwedge(int, char *[]);
    100 void	disk_getwedgeinfo(int, char *[]);
    101 void	disk_listwedges(int, char *[]);
    102 
    103 struct command commands[] = {
    104 	{ "getcache",
    105 	  "",
    106 	  disk_getcache,
    107 	  O_RDONLY },
    108 
    109 	{ "setcache",
    110 	  "none | r | w | rw [save]",
    111 	  disk_setcache,
    112 	  O_RDWR },
    113 
    114 	{ "synccache",
    115 	  "[force]",
    116 	  disk_synccache,
    117 	  O_RDWR },
    118 
    119 	{ "keeplabel",
    120 	  YESNO_ARG,
    121 	  disk_keeplabel,
    122 	  O_RDWR },
    123 
    124 	{ "badsector",
    125 	  "flush | list | retry",
    126 	   disk_badsectors,
    127 	   O_RDWR },
    128 
    129 	{ "addwedge",
    130 	  "name startblk blkcnt ptype",
    131 	  disk_addwedge,
    132 	  O_RDWR },
    133 
    134 	{ "delwedge",
    135 	  "dk",
    136 	  disk_delwedge,
    137 	  O_RDWR },
    138 
    139 	{ "getwedgeinfo",
    140 	  "",
    141 	  disk_getwedgeinfo,
    142 	  O_RDONLY },
    143 
    144 	{ "listwedges",
    145 	  "",
    146 	  disk_listwedges,
    147 	  O_RDONLY },
    148 
    149 	{ NULL,
    150 	  NULL,
    151 	  NULL,
    152 	  0 },
    153 };
    154 
    155 int
    156 main(int argc, char *argv[])
    157 {
    158 	int i;
    159 
    160 	/* Must have at least: device command */
    161 	if (argc < 3)
    162 		usage();
    163 
    164 	/* Skip program name, get and skip device name and command. */
    165 	dvname = argv[1];
    166 	cmdname = argv[2];
    167 	argv += 3;
    168 	argc -= 3;
    169 
    170 	/* Look up and call the command. */
    171 	for (i = 0; commands[i].cmd_name != NULL; i++)
    172 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
    173 			break;
    174 	if (commands[i].cmd_name == NULL)
    175 		errx(1, "unknown command: %s", cmdname);
    176 
    177 	argnames = commands[i].arg_names;
    178 
    179 	/* Open the device. */
    180 	fd = opendisk(dvname, commands[i].open_flags, dvname_store,
    181 	    sizeof(dvname_store), 0);
    182 	if (fd == -1)
    183 		err(1, "%s", dvname);
    184 
    185 	dvname = dvname_store;
    186 
    187 	(*commands[i].cmd_func)(argc, argv);
    188 	exit(0);
    189 }
    190 
    191 void
    192 usage()
    193 {
    194 	int i;
    195 
    196 	fprintf(stderr, "usage: %s device command [arg [...]]\n",
    197 	    getprogname());
    198 
    199 	fprintf(stderr, "   Available commands:\n");
    200 	for (i = 0; commands[i].cmd_name != NULL; i++)
    201 		fprintf(stderr, "\t%s %s\n", commands[i].cmd_name,
    202 		    commands[i].arg_names);
    203 
    204 	exit(1);
    205 }
    206 
    207 void
    208 disk_getcache(int argc, char *argv[])
    209 {
    210 	int bits;
    211 
    212 	if (ioctl(fd, DIOCGCACHE, &bits) == -1)
    213 		err(1, "%s: getcache", dvname);
    214 
    215 	if ((bits & (DKCACHE_READ|DKCACHE_WRITE)) == 0)
    216 		printf("%s: No caches enabled\n", dvname);
    217 	else {
    218 		if (bits & DKCACHE_READ)
    219 			printf("%s: read cache enabled\n", dvname);
    220 		if (bits & DKCACHE_WRITE)
    221 			printf("%s: write-back cache enabled\n", dvname);
    222 	}
    223 
    224 	printf("%s: read cache enable is %schangeable\n", dvname,
    225 	    (bits & DKCACHE_RCHANGE) ? "" : "not ");
    226 	printf("%s: write cache enable is %schangeable\n", dvname,
    227 	    (bits & DKCACHE_WCHANGE) ? "" : "not ");
    228 
    229 	printf("%s: cache parameters are %ssavable\n", dvname,
    230 	    (bits & DKCACHE_SAVE) ? "" : "not ");
    231 }
    232 
    233 void
    234 disk_setcache(int argc, char *argv[])
    235 {
    236 	int bits;
    237 
    238 	if (argc > 2 || argc == 0)
    239 		usage();
    240 
    241 	if (strcmp(argv[0], "none") == 0)
    242 		bits = 0;
    243 	else if (strcmp(argv[0], "r") == 0)
    244 		bits = DKCACHE_READ;
    245 	else if (strcmp(argv[0], "w") == 0)
    246 		bits = DKCACHE_WRITE;
    247 	else if (strcmp(argv[0], "rw") == 0)
    248 		bits = DKCACHE_READ|DKCACHE_WRITE;
    249 	else
    250 		usage();
    251 
    252 	if (argc == 2) {
    253 		if (strcmp(argv[1], "save") == 0)
    254 			bits |= DKCACHE_SAVE;
    255 		else
    256 			usage();
    257 	}
    258 
    259 	if (ioctl(fd, DIOCSCACHE, &bits) == -1)
    260 		err(1, "%s: setcache", dvname);
    261 }
    262 
    263 void
    264 disk_synccache(int argc, char *argv[])
    265 {
    266 	int force;
    267 
    268 	switch (argc) {
    269 	case 0:
    270 		force = 0;
    271 		break;
    272 
    273 	case 1:
    274 		if (strcmp(argv[0], "force") == 0)
    275 			force = 1;
    276 		else
    277 			usage();
    278 		break;
    279 
    280 	default:
    281 		usage();
    282 	}
    283 
    284 	if (ioctl(fd, DIOCCACHESYNC, &force) == -1)
    285 		err(1, "%s: sync cache", dvname);
    286 }
    287 
    288 void
    289 disk_keeplabel(int argc, char *argv[])
    290 {
    291 	int keep;
    292 	int yn;
    293 
    294 	if (argc != 1)
    295 		usage();
    296 
    297 	yn = yesno(argv[0]);
    298 	if (yn < 0)
    299 		usage();
    300 
    301 	keep = yn == YES;
    302 
    303 	if (ioctl(fd, DIOCKLABEL, &keep) == -1)
    304 		err(1, "%s: keep label", dvname);
    305 }
    306 
    307 
    308 void
    309 disk_badsectors(int argc, char *argv[])
    310 {
    311 	struct disk_badsectors *dbs, *dbs2, buffer[200];
    312 	SLIST_HEAD(, disk_badsectors) dbstop;
    313 	struct disk_badsecinfo dbsi;
    314 	daddr_t blk, totbad, bad;
    315 	u_int32_t count;
    316 	struct stat sb;
    317 	u_char *block;
    318 	time_t tm;
    319 
    320 	if (argc != 1)
    321 		usage();
    322 
    323 	if (strcmp(argv[0], "list") == 0) {
    324 		/*
    325 		 * Copy the list of kernel bad sectors out in chunks that fit
    326 		 * into buffer[].  Updating dbsi_skip means we don't sit here
    327 		 * forever only getting the first chunk that fit in buffer[].
    328 		 */
    329 		dbsi.dbsi_buffer = (caddr_t)buffer;
    330 		dbsi.dbsi_bufsize = sizeof(buffer);
    331 		dbsi.dbsi_skip = 0;
    332 		dbsi.dbsi_copied = 0;
    333 		dbsi.dbsi_left = 0;
    334 
    335 		do {
    336 			if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1)
    337 				err(1, "%s: badsectors list", dvname);
    338 
    339 			dbs = (struct disk_badsectors *)dbsi.dbsi_buffer;
    340 			for (count = dbsi.dbsi_copied; count > 0; count--) {
    341 				tm = dbs->dbs_failedat.tv_sec;
    342 				printf("%s: blocks %" PRIdaddr " - %" PRIdaddr " failed at %s",
    343 					dvname, dbs->dbs_min, dbs->dbs_max,
    344 					ctime(&tm));
    345 				dbs++;
    346 			}
    347 			dbsi.dbsi_skip += dbsi.dbsi_copied;
    348 		} while (dbsi.dbsi_left != 0);
    349 
    350 	} else if (strcmp(argv[0], "flush") == 0) {
    351 		if (ioctl(fd, DIOCBSFLUSH) == -1)
    352 			err(1, "%s: badsectors flush", dvname);
    353 
    354 	} else if (strcmp(argv[0], "retry") == 0) {
    355 		/*
    356 		 * Enforce use of raw device here because the block device
    357 		 * causes access to blocks to be clustered in a larger group,
    358 		 * making it impossible to determine which individual sectors
    359 		 * are the cause of a problem.
    360 		 */
    361 		if (fstat(fd, &sb) == -1)
    362 			err(1, "fstat");
    363 
    364 		if (!S_ISCHR(sb.st_mode)) {
    365 			fprintf(stderr, "'badsector retry' must be used %s\n",
    366 				"with character device");
    367 			exit(1);
    368 		}
    369 
    370 		SLIST_INIT(&dbstop);
    371 
    372 		/*
    373 		 * Build up a copy of the in-kernel list in a number of stages.
    374 		 * That the list we build up here is in the reverse order to
    375 		 * the kernel's is of no concern.
    376 		 */
    377 		dbsi.dbsi_buffer = (caddr_t)buffer;
    378 		dbsi.dbsi_bufsize = sizeof(buffer);
    379 		dbsi.dbsi_skip = 0;
    380 		dbsi.dbsi_copied = 0;
    381 		dbsi.dbsi_left = 0;
    382 
    383 		do {
    384 			if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1)
    385 				err(1, "%s: badsectors list", dvname);
    386 
    387 			dbs = (struct disk_badsectors *)dbsi.dbsi_buffer;
    388 			for (count = dbsi.dbsi_copied; count > 0; count--) {
    389 				dbs2 = malloc(sizeof(*dbs2));
    390 				*dbs2 = *dbs;
    391 				SLIST_INSERT_HEAD(&dbstop, dbs2, dbs_next);
    392 				dbs++;
    393 			}
    394 			dbsi.dbsi_skip += dbsi.dbsi_copied;
    395 		} while (dbsi.dbsi_left != 0);
    396 
    397 		/*
    398 		 * Just calculate and print out something that will hopefully
    399 		 * provide some useful information about what's going to take
    400 		 * place next (if anything.)
    401 		 */
    402 		bad = 0;
    403 		totbad = 0;
    404 		block = calloc(1, DEV_BSIZE);
    405 		SLIST_FOREACH(dbs, &dbstop, dbs_next) {
    406 			bad++;
    407 			totbad += dbs->dbs_max - dbs->dbs_min + 1;
    408 		}
    409 
    410 		printf("%s: bad sector clusters %"PRIdaddr
    411 		    " total sectors %"PRIdaddr"\n", dvname, bad, totbad);
    412 
    413 		/*
    414 		 * Clear out the kernel's list of bad sectors, ready for us
    415 		 * to test all those it thought were bad.
    416 		 */
    417 		if (ioctl(fd, DIOCBSFLUSH) == -1)
    418 			err(1, "%s: badsectors flush", dvname);
    419 
    420 		printf("%s: bad sectors flushed\n", dvname);
    421 
    422 		/*
    423 		 * For each entry we obtained from the kernel, retry each
    424 		 * individual sector recorded as bad by seeking to it and
    425 		 * attempting to read it in.  Print out a line item for each
    426 		 * bad block we verify.
    427 		 *
    428 		 * PRIdaddr is used here because the type of dbs_max is daddr_t
    429 		 * and that may be either a 32bit or 64bit number(!)
    430 		 */
    431 		SLIST_FOREACH(dbs, &dbstop, dbs_next) {
    432 			printf("%s: Retrying %"PRIdaddr" - %"
    433 			    PRIdaddr"\n", dvname, dbs->dbs_min, dbs->dbs_max);
    434 
    435 			for (blk = dbs->dbs_min; blk <= dbs->dbs_max; blk++) {
    436 				if (lseek(fd, (off_t)blk * DEV_BSIZE,
    437 				    SEEK_SET) == -1) {
    438 					warn("%s: lseek block %" PRIdaddr "",
    439 					    dvname, blk);
    440 					continue;
    441 				}
    442 				printf("%s: block %"PRIdaddr" - ", dvname, blk);
    443 				if (read(fd, block, DEV_BSIZE) != DEV_BSIZE)
    444 					printf("failed\n");
    445 				else
    446 					printf("ok\n");
    447 				fflush(stdout);
    448 			}
    449 		}
    450 	}
    451 }
    452 
    453 void
    454 disk_addwedge(int argc, char *argv[])
    455 {
    456 	struct dkwedge_info dkw;
    457 	char *cp;
    458 	daddr_t start;
    459 	uint64_t size;
    460 
    461 	if (argc != 4)
    462 		usage();
    463 
    464 	/* XXX Unicode. */
    465 	if (strlen(argv[0]) > sizeof(dkw.dkw_wname) - 1)
    466 		errx(1, "Wedge name too long; max %zd characters",
    467 		    sizeof(dkw.dkw_wname) - 1);
    468 	strcpy(dkw.dkw_wname, argv[0]);
    469 
    470 	if (strlen(argv[3]) > sizeof(dkw.dkw_ptype) - 1)
    471 		errx(1, "Wedge partition type too long; max %zd characters",
    472 		    sizeof(dkw.dkw_ptype) - 1);
    473 	strcpy(dkw.dkw_ptype, argv[3]);
    474 
    475 	errno = 0;
    476 	start = strtoll(argv[1], &cp, 0);
    477 	if (*cp != '\0')
    478 		errx(1, "Invalid start block: %s", argv[1]);
    479 	if (errno == ERANGE && (start == LLONG_MAX ||
    480 				start == LLONG_MIN))
    481 		errx(1, "Start block out of range.");
    482 	if (start < 0)
    483 		errx(1, "Start block must be >= 0.");
    484 
    485 	errno = 0;
    486 	size = strtoull(argv[2], &cp, 0);
    487 	if (*cp != '\0')
    488 		errx(1, "Invalid block count: %s", argv[2]);
    489 	if (errno == ERANGE && (size == ULLONG_MAX))
    490 		errx(1, "Block count out of range.");
    491 
    492 	dkw.dkw_offset = start;
    493 	dkw.dkw_size = size;
    494 
    495 	if (ioctl(fd, DIOCAWEDGE, &dkw) == -1)
    496 		err(1, "%s: addwedge", dvname);
    497 }
    498 
    499 void
    500 disk_delwedge(int argc, char *argv[])
    501 {
    502 	struct dkwedge_info dkw;
    503 
    504 	if (argc != 1)
    505 		usage();
    506 
    507 	if (strlen(argv[0]) > sizeof(dkw.dkw_devname) - 1)
    508 		errx(1, "Wedge dk name too long; max %zd characters",
    509 		    sizeof(dkw.dkw_devname) - 1);
    510 	strcpy(dkw.dkw_devname, argv[0]);
    511 
    512 	if (ioctl(fd, DIOCDWEDGE, &dkw) == -1)
    513 		err(1, "%s: delwedge", dvname);
    514 }
    515 
    516 void
    517 disk_getwedgeinfo(int argc, char *argv[])
    518 {
    519 	struct dkwedge_info dkw;
    520 
    521 	if (argc != 0)
    522 		usage();
    523 
    524 	if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == -1)
    525 		err(1, "%s: getwedgeinfo", dvname);
    526 
    527 	printf("%s at %s: %s\n", dkw.dkw_devname, dkw.dkw_parent,
    528 	    dkw.dkw_wname);	/* XXX Unicode */
    529 	printf("%s: %llu blocks at %lld, type: %s\n",
    530 	    dkw.dkw_devname, dkw.dkw_size, dkw.dkw_offset, dkw.dkw_ptype);
    531 }
    532 
    533 void
    534 disk_listwedges(int argc, char *argv[])
    535 {
    536 	struct dkwedge_info *dkw;
    537 	struct dkwedge_list dkwl;
    538 	size_t bufsize;
    539 	u_int i;
    540 
    541 	if (argc != 0)
    542 		usage();
    543 
    544 	dkw = NULL;
    545 	dkwl.dkwl_buf = dkw;
    546 	dkwl.dkwl_bufsize = 0;
    547 
    548 	for (;;) {
    549 		if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1)
    550 			err(1, "%s: listwedges", dvname);
    551 		if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied)
    552 			break;
    553 		bufsize = dkwl.dkwl_nwedges * sizeof(*dkw);
    554 		if (dkwl.dkwl_bufsize < bufsize) {
    555 			dkw = realloc(dkwl.dkwl_buf, bufsize);
    556 			if (dkw == NULL)
    557 				errx(1, "%s: listwedges: unable to "
    558 				    "allocate wedge info buffer", dvname);
    559 			dkwl.dkwl_buf = dkw;
    560 			dkwl.dkwl_bufsize = bufsize;
    561 		}
    562 	}
    563 
    564 	if (dkwl.dkwl_nwedges == 0) {
    565 		printf("%s: no wedges configured\n", dvname);
    566 		return;
    567 	}
    568 
    569 	printf("%s: %u wedge%s:\n", dvname, dkwl.dkwl_nwedges,
    570 	    dkwl.dkwl_nwedges == 1 ? "" : "s");
    571 	for (i = 0; i < dkwl.dkwl_nwedges; i++) {
    572 		printf("%s: %s, %llu blocks at %lld, type: %s\n",
    573 		    dkw[i].dkw_devname,
    574 		    dkw[i].dkw_wname,	/* XXX Unicode */
    575 		    dkw[i].dkw_size, dkw[i].dkw_offset, dkw[i].dkw_ptype);
    576 	}
    577 }
    578 
    579 /*
    580  * return YES, NO or -1.
    581  */
    582 int
    583 yesno(const char *p)
    584 {
    585 
    586 	if (!strcmp(p, YES_STR))
    587 		return YES;
    588 	if (!strcmp(p, NO_STR))
    589 		return NO;
    590 	return -1;
    591 }
    592