1 /* $NetBSD: dkctl.c,v 1.17 2009/02/07 17:13:32 uebayasi Exp $ */
4 * Copyright 2001 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
39 * dkctl(8) -- a program to manipulate disks.
41 #include <sys/cdefs.h>
44 __RCSID("$NetBSD: dkctl.c,v 1.17 2009/02/07 17:13:32 uebayasi Exp $");
48 #include <sys/param.h>
49 #include <sys/ioctl.h>
52 #include <sys/queue.h>
65 /* I don't think nl_langinfo is suitable in this case */
68 #define YESNO_ARG YES_STR " | " NO_STR
71 #define PRIdaddr PRId64
76 const char *arg_names
;
77 void (*cmd_func
)(int, char *[]);
81 struct command
*lookup(const char *);
83 void run(int, char *[]);
86 int fd
; /* file descriptor for device */
87 const char *dvname
; /* device name */
88 char dvname_store
[MAXPATHLEN
]; /* for opendisk(3) */
89 const char *cmdname
; /* command user issued */
91 int dkw_sort(const void *, const void *);
92 int yesno(const char *);
94 void disk_getcache(int, char *[]);
95 void disk_setcache(int, char *[]);
96 void disk_synccache(int, char *[]);
97 void disk_keeplabel(int, char *[]);
98 void disk_badsectors(int, char *[]);
100 void disk_addwedge(int, char *[]);
101 void disk_delwedge(int, char *[]);
102 void disk_getwedgeinfo(int, char *[]);
103 void disk_listwedges(int, char *[]);
104 void disk_strategy(int, char *[]);
106 void disk_foreachwedges(int, char *[], void (*)(struct dkwedge_list
*));
107 void disk_listwedges_cb(struct dkwedge_list
*);
108 void disk_getwedgeinfo_cb(struct dkwedge_info
*);
110 struct command commands
[] = {
117 "none | r | w | rw [save]",
132 "flush | list | retry",
137 "name startblk blkcnt ptype",
168 main(int argc
, char *argv
[])
171 /* Must have at least: device command */
179 /* Skip program name, get and skip device name and command. */
190 run(int argc
, char *argv
[])
192 struct command
*command
;
194 command
= lookup(cmdname
);
196 /* Open the device. */
197 fd
= opendisk(dvname
, command
->open_flags
, dvname_store
,
198 sizeof(dvname_store
), 0);
200 err(1, "%s", dvname
);
201 dvname
= dvname_store
;
203 (*command
->cmd_func
)(argc
, argv
);
205 /* Close the device. */
210 lookup(const char *name
)
214 /* Look up the command. */
215 for (i
= 0; commands
[i
].cmd_name
!= NULL
; i
++)
216 if (strcmp(name
, commands
[i
].cmd_name
) == 0)
218 if (commands
[i
].cmd_name
== NULL
)
219 errx(1, "unknown command: %s", name
);
231 " %s device command [arg [...]]\n",
232 getprogname(), getprogname());
234 fprintf(stderr
, " Available commands:\n");
235 for (i
= 0; commands
[i
].cmd_name
!= NULL
; i
++)
236 fprintf(stderr
, "\t%s %s\n", commands
[i
].cmd_name
,
237 commands
[i
].arg_names
);
245 printf("strategy:\n");
246 cmdname
= "strategy";
252 cmdname
= "getcache";
258 cmdname
= "listwedges";
263 disk_strategy(int argc
, char *argv
[])
265 struct disk_strategy odks
;
266 struct disk_strategy dks
;
268 memset(&dks
, 0, sizeof(dks
));
269 if (ioctl(fd
, DIOCGSTRATEGY
, &odks
) == -1) {
270 err(EXIT_FAILURE
, "%s: DIOCGSTRATEGY", dvname
);
273 memset(&dks
, 0, sizeof(dks
));
276 /* show the buffer queue strategy used */
277 printf("%s: %s\n", dvname
, odks
.dks_name
);
280 /* set the buffer queue strategy */
281 strlcpy(dks
.dks_name
, argv
[0], sizeof(dks
.dks_name
));
282 if (ioctl(fd
, DIOCSSTRATEGY
, &dks
) == -1) {
283 err(EXIT_FAILURE
, "%s: DIOCSSTRATEGY", dvname
);
285 printf("%s: %s -> %s\n", dvname
, odks
.dks_name
, argv
[0]);
294 disk_getcache(int argc
, char *argv
[])
298 if (ioctl(fd
, DIOCGCACHE
, &bits
) == -1)
299 err(1, "%s: getcache", dvname
);
301 if ((bits
& (DKCACHE_READ
|DKCACHE_WRITE
)) == 0)
302 printf("%s: No caches enabled\n", dvname
);
304 if (bits
& DKCACHE_READ
)
305 printf("%s: read cache enabled\n", dvname
);
306 if (bits
& DKCACHE_WRITE
)
307 printf("%s: write-back cache enabled\n", dvname
);
310 printf("%s: read cache enable is %schangeable\n", dvname
,
311 (bits
& DKCACHE_RCHANGE
) ? "" : "not ");
312 printf("%s: write cache enable is %schangeable\n", dvname
,
313 (bits
& DKCACHE_WCHANGE
) ? "" : "not ");
315 printf("%s: cache parameters are %ssavable\n", dvname
,
316 (bits
& DKCACHE_SAVE
) ? "" : "not ");
320 disk_setcache(int argc
, char *argv
[])
324 if (argc
> 2 || argc
== 0)
327 if (strcmp(argv
[0], "none") == 0)
329 else if (strcmp(argv
[0], "r") == 0)
331 else if (strcmp(argv
[0], "w") == 0)
332 bits
= DKCACHE_WRITE
;
333 else if (strcmp(argv
[0], "rw") == 0)
334 bits
= DKCACHE_READ
|DKCACHE_WRITE
;
339 if (strcmp(argv
[1], "save") == 0)
340 bits
|= DKCACHE_SAVE
;
345 if (ioctl(fd
, DIOCSCACHE
, &bits
) == -1)
346 err(1, "%s: setcache", dvname
);
350 disk_synccache(int argc
, char *argv
[])
360 if (strcmp(argv
[0], "force") == 0)
370 if (ioctl(fd
, DIOCCACHESYNC
, &force
) == -1)
371 err(1, "%s: sync cache", dvname
);
375 disk_keeplabel(int argc
, char *argv
[])
389 if (ioctl(fd
, DIOCKLABEL
, &keep
) == -1)
390 err(1, "%s: keep label", dvname
);
395 disk_badsectors(int argc
, char *argv
[])
397 struct disk_badsectors
*dbs
, *dbs2
, buffer
[200];
398 SLIST_HEAD(, disk_badsectors
) dbstop
;
399 struct disk_badsecinfo dbsi
;
400 daddr_t blk
, totbad
, bad
;
409 if (strcmp(argv
[0], "list") == 0) {
411 * Copy the list of kernel bad sectors out in chunks that fit
412 * into buffer[]. Updating dbsi_skip means we don't sit here
413 * forever only getting the first chunk that fit in buffer[].
415 dbsi
.dbsi_buffer
= (caddr_t
)buffer
;
416 dbsi
.dbsi_bufsize
= sizeof(buffer
);
418 dbsi
.dbsi_copied
= 0;
422 if (ioctl(fd
, DIOCBSLIST
, (caddr_t
)&dbsi
) == -1)
423 err(1, "%s: badsectors list", dvname
);
425 dbs
= (struct disk_badsectors
*)dbsi
.dbsi_buffer
;
426 for (count
= dbsi
.dbsi_copied
; count
> 0; count
--) {
427 tm
= dbs
->dbs_failedat
.tv_sec
;
428 printf("%s: blocks %" PRIdaddr
" - %" PRIdaddr
" failed at %s",
429 dvname
, dbs
->dbs_min
, dbs
->dbs_max
,
433 dbsi
.dbsi_skip
+= dbsi
.dbsi_copied
;
434 } while (dbsi
.dbsi_left
!= 0);
436 } else if (strcmp(argv
[0], "flush") == 0) {
437 if (ioctl(fd
, DIOCBSFLUSH
) == -1)
438 err(1, "%s: badsectors flush", dvname
);
440 } else if (strcmp(argv
[0], "retry") == 0) {
442 * Enforce use of raw device here because the block device
443 * causes access to blocks to be clustered in a larger group,
444 * making it impossible to determine which individual sectors
445 * are the cause of a problem.
447 if (fstat(fd
, &sb
) == -1)
450 if (!S_ISCHR(sb
.st_mode
)) {
451 fprintf(stderr
, "'badsector retry' must be used %s\n",
452 "with character device");
459 * Build up a copy of the in-kernel list in a number of stages.
460 * That the list we build up here is in the reverse order to
461 * the kernel's is of no concern.
463 dbsi
.dbsi_buffer
= (caddr_t
)buffer
;
464 dbsi
.dbsi_bufsize
= sizeof(buffer
);
466 dbsi
.dbsi_copied
= 0;
470 if (ioctl(fd
, DIOCBSLIST
, (caddr_t
)&dbsi
) == -1)
471 err(1, "%s: badsectors list", dvname
);
473 dbs
= (struct disk_badsectors
*)dbsi
.dbsi_buffer
;
474 for (count
= dbsi
.dbsi_copied
; count
> 0; count
--) {
475 dbs2
= malloc(sizeof *dbs2
);
479 SLIST_INSERT_HEAD(&dbstop
, dbs2
, dbs_next
);
482 dbsi
.dbsi_skip
+= dbsi
.dbsi_copied
;
483 } while (dbsi
.dbsi_left
!= 0);
486 * Just calculate and print out something that will hopefully
487 * provide some useful information about what's going to take
488 * place next (if anything.)
492 if ((block
= calloc(1, DEV_BSIZE
)) == NULL
)
494 SLIST_FOREACH(dbs
, &dbstop
, dbs_next
) {
496 totbad
+= dbs
->dbs_max
- dbs
->dbs_min
+ 1;
499 printf("%s: bad sector clusters %"PRIdaddr
500 " total sectors %"PRIdaddr
"\n", dvname
, bad
, totbad
);
503 * Clear out the kernel's list of bad sectors, ready for us
504 * to test all those it thought were bad.
506 if (ioctl(fd
, DIOCBSFLUSH
) == -1)
507 err(1, "%s: badsectors flush", dvname
);
509 printf("%s: bad sectors flushed\n", dvname
);
512 * For each entry we obtained from the kernel, retry each
513 * individual sector recorded as bad by seeking to it and
514 * attempting to read it in. Print out a line item for each
515 * bad block we verify.
517 * PRIdaddr is used here because the type of dbs_max is daddr_t
518 * and that may be either a 32bit or 64bit number(!)
520 SLIST_FOREACH(dbs
, &dbstop
, dbs_next
) {
521 printf("%s: Retrying %"PRIdaddr
" - %"
522 PRIdaddr
"\n", dvname
, dbs
->dbs_min
, dbs
->dbs_max
);
524 for (blk
= dbs
->dbs_min
; blk
<= dbs
->dbs_max
; blk
++) {
525 if (lseek(fd
, (off_t
)blk
* DEV_BSIZE
,
527 warn("%s: lseek block %" PRIdaddr
"",
531 printf("%s: block %"PRIdaddr
" - ", dvname
, blk
);
532 if (read(fd
, block
, DEV_BSIZE
) != DEV_BSIZE
)
543 disk_addwedge(int argc
, char *argv
[])
545 struct dkwedge_info dkw
;
554 if (strlcpy(dkw
.dkw_wname
, argv
[0], sizeof(dkw
.dkw_wname
)) >=
555 sizeof(dkw
.dkw_wname
))
556 errx(1, "Wedge name too long; max %zd characters",
557 sizeof(dkw
.dkw_wname
) - 1);
559 if (strlcpy(dkw
.dkw_ptype
, argv
[3], sizeof(dkw
.dkw_ptype
)) >=
560 sizeof(dkw
.dkw_ptype
))
561 errx(1, "Wedge partition type too long; max %zd characters",
562 sizeof(dkw
.dkw_ptype
) - 1);
565 start
= strtoll(argv
[1], &cp
, 0);
567 errx(1, "Invalid start block: %s", argv
[1]);
568 if (errno
== ERANGE
&& (start
== LLONG_MAX
||
570 errx(1, "Start block out of range.");
572 errx(1, "Start block must be >= 0.");
575 size
= strtoull(argv
[2], &cp
, 0);
577 errx(1, "Invalid block count: %s", argv
[2]);
578 if (errno
== ERANGE
&& (size
== ULLONG_MAX
))
579 errx(1, "Block count out of range.");
581 dkw
.dkw_offset
= start
;
584 if (ioctl(fd
, DIOCAWEDGE
, &dkw
) == -1)
585 err(1, "%s: addwedge", dvname
);
587 printf("%s created successfully.\n", dkw
.dkw_devname
);
592 disk_delwedge(int argc
, char *argv
[])
594 struct dkwedge_info dkw
;
599 if (strlcpy(dkw
.dkw_devname
, argv
[0], sizeof(dkw
.dkw_devname
)) >=
600 sizeof(dkw
.dkw_devname
))
601 errx(1, "Wedge dk name too long; max %zd characters",
602 sizeof(dkw
.dkw_devname
) - 1);
604 if (ioctl(fd
, DIOCDWEDGE
, &dkw
) == -1)
605 err(1, "%s: delwedge", dvname
);
609 disk_getwedgeinfo(int argc
, char *argv
[])
611 struct dkwedge_info dkw
;
616 if (ioctl(fd
, DIOCGWEDGEINFO
, &dkw
) == -1)
617 err(1, "%s: getwedgeinfo", dvname
);
619 printf("%s at %s: %s\n", dkw
.dkw_devname
, dkw
.dkw_parent
,
620 dkw
.dkw_wname
); /* XXX Unicode */
621 printf("%s: %"PRIu64
" blocks at %"PRId64
", type: %s\n",
622 dkw
.dkw_devname
, dkw
.dkw_size
, dkw
.dkw_offset
, dkw
.dkw_ptype
);
626 disk_listwedges(int argc
, char *argv
[])
628 struct dkwedge_info
*dkw
;
629 struct dkwedge_list dkwl
;
638 dkwl
.dkwl_bufsize
= 0;
641 if (ioctl(fd
, DIOCLWEDGES
, &dkwl
) == -1)
642 err(1, "%s: listwedges", dvname
);
643 if (dkwl
.dkwl_nwedges
== dkwl
.dkwl_ncopied
)
645 bufsize
= dkwl
.dkwl_nwedges
* sizeof(*dkw
);
646 if (dkwl
.dkwl_bufsize
< bufsize
) {
647 dkw
= realloc(dkwl
.dkwl_buf
, bufsize
);
649 errx(1, "%s: listwedges: unable to "
650 "allocate wedge info buffer", dvname
);
652 dkwl
.dkwl_bufsize
= bufsize
;
656 if (dkwl
.dkwl_nwedges
== 0) {
657 printf("%s: no wedges configured\n", dvname
);
661 qsort(dkw
, dkwl
.dkwl_nwedges
, sizeof(*dkw
), dkw_sort
);
663 printf("%s: %u wedge%s:\n", dvname
, dkwl
.dkwl_nwedges
,
664 dkwl
.dkwl_nwedges
== 1 ? "" : "s");
665 for (i
= 0; i
< dkwl
.dkwl_nwedges
; i
++) {
666 printf("%s: %s, %"PRIu64
" blocks at %"PRId64
", type: %s\n",
668 dkw
[i
].dkw_wname
, /* XXX Unicode */
669 dkw
[i
].dkw_size
, dkw
[i
].dkw_offset
, dkw
[i
].dkw_ptype
);
674 dkw_sort(const void *a
, const void *b
)
676 const struct dkwedge_info
*dkwa
= a
, *dkwb
= b
;
677 const daddr_t oa
= dkwa
->dkw_offset
, ob
= dkwb
->dkw_offset
;
679 return (oa
< ob
) ? -1 : (oa
> ob
) ? 1 : 0;
683 * return YES, NO or -1.
689 if (!strcmp(p
, YES_STR
))
691 if (!strcmp(p
, NO_STR
))