xref: /netbsd-src/sbin/raidctl/raidctl.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*      $NetBSD: raidctl.c,v 1.74 2021/08/02 20:31:15 oster Exp $   */
2 
3 /*-
4  * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Greg Oster
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * This program is a re-write of the original rf_ctrl program
34  * distributed by CMU with RAIDframe 1.1.
35  *
36  * This program is the user-land interface to the RAIDframe kernel
37  * driver in NetBSD.
38  */
39 #include <sys/cdefs.h>
40 
41 #ifndef lint
42 __RCSID("$NetBSD: raidctl.c,v 1.74 2021/08/02 20:31:15 oster Exp $");
43 #endif
44 
45 
46 #include <sys/param.h>
47 #include <sys/ioctl.h>
48 #include <sys/stat.h>
49 #include <sys/disklabel.h>
50 
51 #include <ctype.h>
52 #include <err.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <inttypes.h>
59 #include <unistd.h>
60 #include <util.h>
61 
62 #include <dev/raidframe/raidframevar.h>
63 #include <dev/raidframe/raidframeio.h>
64 #include "rf_configure.h"
65 #include "prog_ops.h"
66 
67 void	do_ioctl(int, u_long, void *, const char *);
68 static  void rf_configure(int, char*, int);
69 static  const char *device_status(RF_DiskStatus_t);
70 static  void rf_get_device_status(int);
71 static	void rf_output_configuration(int, const char *);
72 static  void get_component_number(int, char *, int *, int *);
73 static  void rf_fail_disk(int, char *, int);
74 __dead static  void usage(void);
75 static  void get_component_label(int, char *);
76 static  void set_component_label(int, char *);
77 static  void init_component_labels(int, int);
78 static  void set_autoconfig(int, int, char *);
79 static  void add_hot_spare(int, char *);
80 static  void remove_hot_spare(int, char *);
81 static  void rebuild_in_place(int, char *);
82 static  void check_status(int,int);
83 static  void check_parity(int,int, char *);
84 static  void do_meter(int, u_long);
85 static  void get_bar(char *, double, int);
86 static  void get_time_string(char *, size_t, int);
87 static  void rf_output_pmstat(int, int);
88 static  void rf_pm_configure(int, int, char *, int[]);
89 static  unsigned int xstrtouint(const char *);
90 
91 int verbose;
92 
93 static const char *rootpart[] = { "No", "Force", "Soft", "*invalid*" };
94 
95 static void
96 get_comp(char *buf, char *arg, size_t bufsz)
97 {
98 	if (getfsspecname(buf, bufsz, arg) == NULL)
99 		errx(1,"%s",buf);
100 }
101 
102 int
103 main(int argc,char *argv[])
104 {
105 	int ch, i;
106 	int num_options;
107 	unsigned long action;
108 	char config_filename[PATH_MAX];
109 	char dev_name[PATH_MAX];
110 	char name[PATH_MAX];
111 	char component[PATH_MAX];
112 	char autoconf[10];
113 	char *parityconf = NULL;
114 	int parityparams[3];
115 	int do_output;
116 	int do_recon;
117 	int do_rewrite;
118 	int raidID;
119 	int serial_number;
120 	struct stat st;
121 	int fd;
122 	int force;
123 	int openmode;
124 	int last_unit;
125 
126 	num_options = 0;
127 	action = 0;
128 	do_output = 0;
129 	do_recon = 0;
130 	do_rewrite = 0;
131 	serial_number = 0;
132 	force = 0;
133 	last_unit = 0;
134 	openmode = O_RDWR;	/* default to read/write */
135 
136 	while ((ch = getopt(argc, argv, "a:A:Bc:C:f:F:g:GiI:l:LmM:r:R:sSpPuU:v"))
137 	       != -1)
138 		switch(ch) {
139 		case 'a':
140 			action = RAIDFRAME_ADD_HOT_SPARE;
141 			get_comp(component, optarg, sizeof(component));
142 			num_options++;
143 			break;
144 		case 'A':
145 			action = RAIDFRAME_SET_AUTOCONFIG;
146 			strlcpy(autoconf, optarg, sizeof(autoconf));
147 			num_options++;
148 			break;
149 		case 'B':
150 			action = RAIDFRAME_COPYBACK;
151 			num_options++;
152 			break;
153 		case 'c':
154 			action = RAIDFRAME_CONFIGURE;
155 			strlcpy(config_filename, optarg,
156 			    sizeof(config_filename));
157 			force = 0;
158 			num_options++;
159 			break;
160 		case 'C':
161 			strlcpy(config_filename, optarg,
162 			    sizeof(config_filename));
163 			action = RAIDFRAME_CONFIGURE;
164 			force = 1;
165 			num_options++;
166 			break;
167 		case 'f':
168 			action = RAIDFRAME_FAIL_DISK;
169 			get_comp(component, optarg, sizeof(component));
170 			do_recon = 0;
171 			num_options++;
172 			break;
173 		case 'F':
174 			action = RAIDFRAME_FAIL_DISK;
175 			get_comp(component, optarg, sizeof(component));
176 			do_recon = 1;
177 			num_options++;
178 			break;
179 		case 'g':
180 			action = RAIDFRAME_GET_COMPONENT_LABEL;
181 			get_comp(component, optarg, sizeof(component));
182 			openmode = O_RDONLY;
183 			num_options++;
184 			break;
185 		case 'G':
186 			action = RAIDFRAME_GET_INFO;
187 			openmode = O_RDONLY;
188 			do_output = 1;
189 			num_options++;
190 			break;
191 		case 'i':
192 			action = RAIDFRAME_REWRITEPARITY;
193 			num_options++;
194 			break;
195 		case 'I':
196 			action = RAIDFRAME_INIT_LABELS;
197 			serial_number = xstrtouint(optarg);
198 			num_options++;
199 			break;
200 		case 'l':
201 			action = RAIDFRAME_SET_COMPONENT_LABEL;
202 			get_comp(component, optarg, sizeof(component));
203 			num_options++;
204 			break;
205 		case 'L':
206 			action = RAIDFRAME_RESCAN;
207 			num_options++;
208 			break;
209 		case 'm':
210 			action = RAIDFRAME_PARITYMAP_STATUS;
211 			openmode = O_RDONLY;
212 			num_options++;
213 			break;
214 		case 'M':
215 			action = RAIDFRAME_PARITYMAP_SET_DISABLE;
216 			parityconf = strdup(optarg);
217 			num_options++;
218 			/* XXXjld: should rf_pm_configure do the strtol()s? */
219 			i = 0;
220 			while (i < 3 && optind < argc &&
221 			    isdigit((int)argv[optind][0]))
222 				parityparams[i++] = xstrtouint(argv[optind++]);
223 			while (i < 3)
224 				parityparams[i++] = 0;
225 			break;
226 		case 'r':
227 			action = RAIDFRAME_REMOVE_HOT_SPARE;
228 			get_comp(component, optarg, sizeof(component));
229 			num_options++;
230 			break;
231 		case 'R':
232 			get_comp(component, optarg, sizeof(component));
233 			action = RAIDFRAME_REBUILD_IN_PLACE;
234 			num_options++;
235 			break;
236 		case 's':
237 			action = RAIDFRAME_GET_INFO;
238 			openmode = O_RDONLY;
239 			num_options++;
240 			break;
241 		case 'S':
242 			action = RAIDFRAME_CHECK_RECON_STATUS_EXT;
243 			openmode = O_RDONLY;
244 			num_options++;
245 			break;
246 		case 'p':
247 			action = RAIDFRAME_CHECK_PARITY;
248 			openmode = O_RDONLY;
249 			num_options++;
250 			break;
251 		case 'P':
252 			action = RAIDFRAME_CHECK_PARITY;
253 			do_rewrite = 1;
254 			num_options++;
255 			break;
256 		case 'u':
257 			action = RAIDFRAME_SHUTDOWN;
258 			num_options++;
259 			break;
260 		case 'U':
261 			action = RAIDFRAME_SET_LAST_UNIT;
262 			num_options++;
263 			last_unit = atoi(optarg);
264 			if (last_unit < 0)
265 				errx(1, "Bad last unit %s", optarg);
266 			break;
267 		case 'v':
268 			verbose = 1;
269 			/* Don't bump num_options, as '-v' is not
270 			   an option like the others */
271 			/* num_options++; */
272 			break;
273 		default:
274 			usage();
275 		}
276 	argc -= optind;
277 	argv += optind;
278 
279 	if ((num_options > 1) || (argc == 0))
280 		usage();
281 
282 	if (prog_init && prog_init() == -1)
283 		err(1, "init failed");
284 
285 	strlcpy(name, argv[0], sizeof(name));
286 	fd = opendisk1(name, openmode, dev_name, sizeof(dev_name), 0,
287 	    prog_open);
288 	if (fd == -1)
289 		err(1, "Unable to open device file: %s", name);
290 	if (prog_fstat(fd, &st) == -1)
291 		err(1, "stat failure on: %s", dev_name);
292 	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
293 		err(1, "invalid device: %s", dev_name);
294 
295 	raidID = DISKUNIT(st.st_rdev);
296 
297 	switch(action) {
298 	case RAIDFRAME_ADD_HOT_SPARE:
299 		add_hot_spare(fd, component);
300 		break;
301 	case RAIDFRAME_REMOVE_HOT_SPARE:
302 		remove_hot_spare(fd, component);
303 		break;
304 	case RAIDFRAME_CONFIGURE:
305 		rf_configure(fd, config_filename, force);
306 		break;
307 	case RAIDFRAME_SET_AUTOCONFIG:
308 		set_autoconfig(fd, raidID, autoconf);
309 		break;
310 	case RAIDFRAME_COPYBACK:
311 		printf("Copyback.\n");
312 		do_ioctl(fd, RAIDFRAME_COPYBACK, NULL, "RAIDFRAME_COPYBACK");
313 		if (verbose) {
314 			sleep(3); /* XXX give the copyback a chance to start */
315 			printf("Copyback status:\n");
316 			do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS_EXT);
317 		}
318 		break;
319 	case RAIDFRAME_FAIL_DISK:
320 		rf_fail_disk(fd, component, do_recon);
321 		break;
322 	case RAIDFRAME_SET_COMPONENT_LABEL:
323 		set_component_label(fd, component);
324 		break;
325 	case RAIDFRAME_GET_COMPONENT_LABEL:
326 		get_component_label(fd, component);
327 		break;
328 	case RAIDFRAME_INIT_LABELS:
329 		init_component_labels(fd, serial_number);
330 		break;
331 	case RAIDFRAME_REWRITEPARITY:
332 		printf("Initiating re-write of parity\n");
333 		do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
334 			 "RAIDFRAME_REWRITEPARITY");
335 		if (verbose) {
336 			sleep(3); /* XXX give it time to get started */
337 			printf("Parity Re-write status:\n");
338 			do_meter(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
339 		}
340 		break;
341 	case RAIDFRAME_CHECK_RECON_STATUS_EXT:
342 		check_status(fd,1);
343 		break;
344 	case RAIDFRAME_GET_INFO:
345 		if (do_output)
346 			rf_output_configuration(fd, dev_name);
347 		else
348 			rf_get_device_status(fd);
349 		break;
350 	case RAIDFRAME_PARITYMAP_STATUS:
351 		rf_output_pmstat(fd, raidID);
352 		break;
353 	case RAIDFRAME_PARITYMAP_SET_DISABLE:
354 		rf_pm_configure(fd, raidID, parityconf, parityparams);
355 		break;
356 	case RAIDFRAME_REBUILD_IN_PLACE:
357 		rebuild_in_place(fd, component);
358 		break;
359 	case RAIDFRAME_CHECK_PARITY:
360 		check_parity(fd, do_rewrite, dev_name);
361 		break;
362 	case RAIDFRAME_SHUTDOWN:
363 		do_ioctl(fd, RAIDFRAME_SHUTDOWN, NULL, "RAIDFRAME_SHUTDOWN");
364 		break;
365 	case RAIDFRAME_SET_LAST_UNIT:
366 		do_ioctl(fd, RAIDFRAME_SET_LAST_UNIT, &last_unit,
367 		    "RAIDFRAME_SET_LAST_UNIT");
368 		break;
369 	case RAIDFRAME_RESCAN:
370 		do_ioctl(fd, RAIDFRAME_RESCAN, NULL, "RAIDFRAME_RESCAN");
371 		break;
372 	default:
373 		break;
374 	}
375 
376 	prog_close(fd);
377 	exit(0);
378 }
379 
380 void
381 do_ioctl(int fd, unsigned long command, void *arg, const char *ioctl_name)
382 {
383 	if (prog_ioctl(fd, command, arg) == -1)
384 		err(1, "ioctl (%s) failed", ioctl_name);
385 }
386 
387 
388 static void
389 rf_configure(int fd, char *config_file, int force)
390 {
391 	void *generic;
392 	RF_Config_t cfg;
393 
394 	if (rf_MakeConfig( config_file, &cfg ) != 0)
395 		err(1, "Unable to create RAIDframe configuration structure");
396 
397 	cfg.force = force;
398 
399 	/*
400 	 * Note the extra level of redirection needed here, since
401 	 * what we really want to pass in is a pointer to the pointer to
402 	 * the configuration structure.
403 	 */
404 
405 	generic = &cfg;
406 	do_ioctl(fd, RAIDFRAME_CONFIGURE, &generic, "RAIDFRAME_CONFIGURE");
407 }
408 
409 static const char *
410 device_status(RF_DiskStatus_t status)
411 {
412 
413 	switch (status) {
414 	case rf_ds_optimal:
415 		return ("optimal");
416 		break;
417 	case rf_ds_failed:
418 		return ("failed");
419 		break;
420 	case rf_ds_reconstructing:
421 		return ("reconstructing");
422 		break;
423 	case rf_ds_dist_spared:
424 		return ("dist_spared");
425 		break;
426 	case rf_ds_spared:
427 		return ("spared");
428 		break;
429 	case rf_ds_spare:
430 		return ("spare");
431 		break;
432 	case rf_ds_used_spare:
433 		return ("used_spare");
434 		break;
435 	default:
436 		return ("UNKNOWN");
437 	}
438 	/* NOTREACHED */
439 }
440 
441 static void
442 rf_get_device_status(int fd)
443 {
444 	RF_DeviceConfig_t device_config;
445 	void *cfg_ptr;
446 	int is_clean;
447 	int i, nspares;
448 
449 	cfg_ptr = &device_config;
450 
451 	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr, "RAIDFRAME_GET_INFO");
452 
453 	printf("Components:\n");
454 	for(i=0; i < device_config.ndevs; i++) {
455 		printf("%20s: %s\n", device_config.devs[i].devname,
456 		       device_status(device_config.devs[i].status));
457 	}
458 
459 	nspares = MIN(device_config.nspares,
460 	                __arraycount(device_config.spares));
461 
462 	if (nspares > 0) {
463 		printf("Spares:\n");
464 		for(i=0; i < nspares; i++) {
465 			printf("%20s: %s\n",
466 			       device_config.spares[i].devname,
467 			       device_status(device_config.spares[i].status));
468 		}
469 	} else {
470 		printf("No spares.\n");
471 	}
472 	for(i=0; i < device_config.ndevs; i++) {
473 		if (device_config.devs[i].status == rf_ds_optimal) {
474 			get_component_label(fd, device_config.devs[i].devname);
475 		} else {
476 			printf("%s status is: %s.  Skipping label.\n",
477 			       device_config.devs[i].devname,
478 			       device_status(device_config.devs[i].status));
479 		}
480 	}
481 
482 	if (nspares > 0) {
483 		for(i=0; i < nspares; i++) {
484 			if ((device_config.spares[i].status ==
485 			     rf_ds_optimal) ||
486 			    (device_config.spares[i].status ==
487 			     rf_ds_used_spare)) {
488 				get_component_label(fd,
489 					    device_config.spares[i].devname);
490 			} else {
491 				printf("%s status is: %s.  Skipping label.\n",
492 				       device_config.spares[i].devname,
493 				       device_status(device_config.spares[i].status));
494 			}
495 		}
496 	}
497 
498 	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
499 		 "RAIDFRAME_CHECK_PARITY");
500 	if (is_clean) {
501 		printf("Parity status: clean\n");
502 	} else {
503 		printf("Parity status: DIRTY\n");
504 	}
505 	check_status(fd,0);
506 }
507 
508 static void
509 rf_output_pmstat(int fd, int raidID)
510 {
511 	char srs[7];
512 	unsigned int i, j;
513 	int dis, dr;
514 	struct rf_pmstat st;
515 
516 	if (prog_ioctl(fd, RAIDFRAME_PARITYMAP_STATUS, &st) == -1) {
517 		if (errno == EINVAL) {
518 			printf("raid%d: has no parity; parity map disabled\n",
519 				raidID);
520 			return;
521 		}
522 		err(1, "ioctl (%s) failed", "RAIDFRAME_PARITYMAP_STATUS");
523 	}
524 
525 	if (st.enabled) {
526 		if (0 > humanize_number(srs, 7, st.region_size * DEV_BSIZE,
527 			"B", HN_AUTOSCALE, HN_NOSPACE))
528 			strlcpy(srs, "???", 7);
529 
530 		printf("raid%d: parity map enabled with %u regions of %s\n",
531 		    raidID, st.params.regions, srs);
532 		printf("raid%d: regions marked clean after %d intervals of"
533 		    " %d.%03ds\n", raidID, st.params.cooldown,
534 		    st.params.tickms / 1000, st.params.tickms % 1000);
535 		printf("raid%d: write/sync/clean counters "
536 		    "%"PRIu64"/%"PRIu64"/%"PRIu64"\n", raidID,
537 		    st.ctrs.nwrite, st.ctrs.ncachesync, st.ctrs.nclearing);
538 
539 		dr = 0;
540 		for (i = 0; i < st.params.regions; i++)
541 			if (isset(st.dirty, i))
542 				dr++;
543 		printf("raid%d: %d dirty region%s\n", raidID, dr,
544 		    dr == 1 ? "" : "s");
545 
546 		if (verbose > 0) {
547 			for (i = 0; i < RF_PARITYMAP_NBYTE; i += 32) {
548 				printf("    ");
549 				for (j = i; j < RF_PARITYMAP_NBYTE
550 					 && j < i + 32; j++)
551 					printf("%x%x", st.dirty[j] & 15,
552 					    (st.dirty[j] >> 4) & 15);
553 				printf("\n");
554 			}
555 		}
556 	} else {
557 		printf("raid%d: parity map disabled\n", raidID);
558 	}
559 
560 	do_ioctl(fd, RAIDFRAME_PARITYMAP_GET_DISABLE, &dis,
561 	    "RAIDFRAME_PARITYMAP_GET_DISABLE");
562 	printf("raid%d: parity map will %s %sabled on next configure\n",
563 	    raidID, dis == st.enabled ? "be" : "remain", dis ? "dis" : "en");
564 }
565 
566 static void
567 rf_pm_configure(int fd, int raidID, char *parityconf, int parityparams[])
568 {
569 	int dis;
570 	struct rf_pmparams params;
571 
572 	if (strcasecmp(parityconf, "yes") == 0)
573 		dis = 0;
574 	else if (strcasecmp(parityconf, "no") == 0)
575 		dis = 1;
576 	else if (strcasecmp(parityconf, "set") == 0) {
577 		params.cooldown = parityparams[0];
578 		params.tickms = parityparams[1];
579 		params.regions = parityparams[2];
580 
581 		do_ioctl(fd, RAIDFRAME_PARITYMAP_SET_PARAMS, &params,
582 		    "RAIDFRAME_PARITYMAP_SET_PARAMS");
583 
584 		if (params.cooldown != 0 || params.tickms != 0) {
585 			printf("raid%d: parity cleaned after", raidID);
586 			if (params.cooldown != 0)
587 				printf(" %d", params.cooldown);
588 			printf(" intervals");
589 			if (params.tickms != 0) {
590 				printf(" of %d.%03ds", params.tickms / 1000,
591 				    params.tickms % 1000);
592 			}
593 			printf("\n");
594 		}
595 		if (params.regions != 0)
596 			printf("raid%d: will use %d regions on next"
597 			    " configuration\n", raidID, params.regions);
598 
599 		return;
600 		/* XXX the control flow here could be prettier. */
601 	} else
602 		err(1, "`%s' is not a valid parity map command", parityconf);
603 
604 	do_ioctl(fd, RAIDFRAME_PARITYMAP_SET_DISABLE, &dis,
605 	    "RAIDFRAME_PARITYMAP_SET_DISABLE");
606 	printf("raid%d: parity map will be %sabled on next configure\n",
607 	    raidID, dis ? "dis" : "en");
608 }
609 
610 /* convert "component0" into "absent" */
611 static const char *rf_output_devname(const char *name)
612 {
613 
614 	if (strncmp(name, "component", 9) == 0)
615 		return "absent";
616 	return name;
617 }
618 
619 static void
620 rf_output_configuration(int fd, const char *name)
621 {
622 	RF_DeviceConfig_t device_config;
623 	void *cfg_ptr;
624 	int i, nspares;
625 	RF_ComponentLabel_t component_label;
626 	void *label_ptr;
627 	int component_num;
628 	int num_cols;
629 
630 	cfg_ptr = &device_config;
631 
632 	printf("# raidctl config file for %s\n", name);
633 	printf("\n");
634 	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr, "RAIDFRAME_GET_INFO");
635 
636 	nspares = MIN(device_config.nspares,
637 	                __arraycount(device_config.spares));
638 
639 	/*
640 	 * After NetBSD 9, convert this to not output the numRow's value,
641 	 * which is no longer required or ever used.
642 	 */
643 	printf("START array\n");
644 	printf("# numRow numCol numSpare\n");
645 	printf("%d %d %d\n", 1, device_config.cols,
646 	    device_config.nspares);
647 	printf("\n");
648 
649 	printf("START disks\n");
650 	for(i=0; i < device_config.ndevs; i++)
651 		printf("%s\n",
652 		    rf_output_devname(device_config.devs[i].devname));
653 	printf("\n");
654 
655 	if (nspares > 0) {
656 		printf("START spare\n");
657 		for(i=0; i < nspares; i++)
658 			printf("%s\n", device_config.spares[i].devname);
659 		printf("\n");
660 	}
661 
662 	for(i=0; i < device_config.ndevs; i++) {
663 		if (device_config.devs[i].status == rf_ds_optimal)
664 			break;
665 	}
666 	if (i == device_config.ndevs) {
667 		printf("# WARNING: no optimal components; using %s\n",
668 		    device_config.devs[0].devname);
669 		i = 0;
670 	}
671 	get_component_number(fd, device_config.devs[i].devname,
672 	    &component_num, &num_cols);
673 	memset(&component_label, 0, sizeof(RF_ComponentLabel_t));
674 	component_label.row = component_num / num_cols;
675 	component_label.column = component_num % num_cols;
676 	label_ptr = &component_label;
677 	do_ioctl(fd, RAIDFRAME_GET_COMPONENT_LABEL, label_ptr,
678 		  "RAIDFRAME_GET_COMPONENT_LABEL");
679 
680 	printf("START layout\n");
681 	printf(
682 	    "# sectPerSU SUsPerParityUnit SUsPerReconUnit RAID_level_%c\n",
683 	    (char) component_label.parityConfig);
684 	printf("%d %d %d %c\n",
685 	    component_label.sectPerSU, component_label.SUsPerPU,
686 	    component_label.SUsPerRU, (char) component_label.parityConfig);
687 	printf("\n");
688 
689 	printf("START queue\n");
690 	printf("fifo %d\n", device_config.maxqdepth);
691 }
692 
693 static void
694 get_component_number(int fd, char *component_name, int *component_number,
695 		     int *num_columns)
696 {
697 	RF_DeviceConfig_t device_config;
698 	void *cfg_ptr;
699 	int i, nspares;
700 	int found;
701 
702 	*component_number = -1;
703 
704 	/* Assuming a full path spec... */
705 	cfg_ptr = &device_config;
706 	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr,
707 		 "RAIDFRAME_GET_INFO");
708 
709 	*num_columns = device_config.cols;
710 
711 	nspares = MIN(device_config.nspares,
712 	                __arraycount(device_config.spares));
713 
714 	found = 0;
715 	for(i=0; i < device_config.ndevs; i++) {
716 		if (strncmp(component_name, device_config.devs[i].devname,
717 			    PATH_MAX)==0) {
718 			found = 1;
719 			*component_number = i;
720 		}
721 	}
722 	if (!found) { /* maybe it's a spare? */
723 		for(i=0; i < nspares; i++) {
724 			if (strncmp(component_name,
725 				    device_config.spares[i].devname,
726 				    PATH_MAX)==0) {
727 				found = 1;
728 				*component_number = i + device_config.ndevs;
729 				/* the way spares are done should
730 				   really change... */
731 				*num_columns = device_config.cols +
732 					device_config.nspares;
733 			}
734 		}
735 	}
736 
737 	if (!found)
738 		err(1,"%s is not a component of this device", component_name);
739 }
740 
741 static void
742 rf_fail_disk(int fd, char *component_to_fail, int do_recon)
743 {
744 	struct rf_recon_req recon_request;
745 	int component_num;
746 	int num_cols;
747 
748 	get_component_number(fd, component_to_fail, &component_num, &num_cols);
749 
750 	recon_request.col = component_num % num_cols;
751 	if (do_recon) {
752 		recon_request.flags = RF_FDFLAGS_RECON;
753 	} else {
754 		recon_request.flags = RF_FDFLAGS_NONE;
755 	}
756 	do_ioctl(fd, RAIDFRAME_FAIL_DISK, &recon_request,
757 		 "RAIDFRAME_FAIL_DISK");
758 	if (do_recon && verbose) {
759 		printf("Reconstruction status:\n");
760 		sleep(3); /* XXX give reconstruction a chance to start */
761 		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
762 	}
763 }
764 
765 static void
766 get_component_label(int fd, char *component)
767 {
768 	RF_ComponentLabel_t component_label;
769 	void *label_ptr;
770 	int component_num;
771 	int num_cols;
772 
773 	get_component_number(fd, component, &component_num, &num_cols);
774 
775 	memset( &component_label, 0, sizeof(RF_ComponentLabel_t));
776 	component_label.row = component_num / num_cols;
777 	component_label.column = component_num % num_cols;
778 
779 	label_ptr = &component_label;
780 	do_ioctl( fd, RAIDFRAME_GET_COMPONENT_LABEL, label_ptr,
781 		  "RAIDFRAME_GET_COMPONENT_LABEL");
782 
783 	printf("Component label for %s:\n",component);
784 
785 	printf("   Row: %d, Column: %d, Num Rows: %d, Num Columns: %d\n",
786 	       component_label.row, component_label.column,
787 	       component_label.num_rows, component_label.num_columns);
788 	printf("   Version: %d, Serial Number: %u, Mod Counter: %d\n",
789 	       component_label.version, component_label.serial_number,
790 	       component_label.mod_counter);
791 	printf("   Clean: %s, Status: %d\n",
792 	       component_label.clean ? "Yes" : "No",
793 	       component_label.status );
794 	printf("   sectPerSU: %d, SUsPerPU: %d, SUsPerRU: %d\n",
795 	       component_label.sectPerSU, component_label.SUsPerPU,
796 	       component_label.SUsPerRU);
797 	printf("   Queue size: %d, blocksize: %d, numBlocks: %"PRIu64"\n",
798 	       component_label.maxOutstanding, component_label.blockSize,
799 	       rf_component_label_numblocks(&component_label));
800 	printf("   RAID Level: %c\n", (char) component_label.parityConfig);
801 	printf("   Autoconfig: %s\n",
802 	       component_label.autoconfigure ? "Yes" : "No" );
803 	printf("   Root partition: %s\n",
804 	       rootpart[component_label.root_partition & 3]);
805 	printf("   Last configured as: raid%d\n", component_label.last_unit );
806 }
807 
808 static void
809 set_component_label(int fd, char *component)
810 {
811 	RF_ComponentLabel_t component_label;
812 	int component_num;
813 	int num_cols;
814 
815 	get_component_number(fd, component, &component_num, &num_cols);
816 
817 	/* XXX This is currently here for testing, and future expandability */
818 
819 	component_label.version = 1;
820 	component_label.serial_number = 123456;
821 	component_label.mod_counter = 0;
822 	component_label.row = component_num / num_cols;
823 	component_label.column = component_num % num_cols;
824 	component_label.num_rows = 0;
825 	component_label.num_columns = 5;
826 	component_label.clean = 0;
827 	component_label.status = 1;
828 
829 	do_ioctl( fd, RAIDFRAME_SET_COMPONENT_LABEL, &component_label,
830 		  "RAIDFRAME_SET_COMPONENT_LABEL");
831 }
832 
833 
834 static void
835 init_component_labels(int fd, int serial_number)
836 {
837 	RF_ComponentLabel_t component_label;
838 
839 	component_label.version = 0;
840 	component_label.serial_number = serial_number;
841 	component_label.mod_counter = 0;
842 	component_label.row = 0;
843 	component_label.column = 0;
844 	component_label.num_rows = 0;
845 	component_label.num_columns = 0;
846 	component_label.clean = 0;
847 	component_label.status = 0;
848 
849 	do_ioctl( fd, RAIDFRAME_INIT_LABELS, &component_label,
850 		  "RAIDFRAME_INIT_LABELS");
851 }
852 
853 static void
854 set_autoconfig(int fd, int raidID, char *autoconf)
855 {
856 	int auto_config;
857 	int root_config;
858 
859 	auto_config = 0;
860 	root_config = 0;
861 
862 	if (strncasecmp(autoconf, "root", 4) == 0 ||
863 	    strncasecmp(autoconf, "hard", 4) == 0 ||
864 	    strncasecmp(autoconf, "force", 5) == 0) {
865 		root_config = 1;
866 	} else if (strncasecmp(autoconf, "soft", 4) == 0) {
867 		root_config = 2;
868 	}
869 
870 	if ((strncasecmp(autoconf,"yes", 3) == 0) ||
871 	    root_config > 0) {
872 		auto_config = 1;
873 	}
874 
875 	do_ioctl(fd, RAIDFRAME_SET_AUTOCONFIG, &auto_config,
876 		 "RAIDFRAME_SET_AUTOCONFIG");
877 
878 	do_ioctl(fd, RAIDFRAME_SET_ROOT, &root_config,
879 		 "RAIDFRAME_SET_ROOT");
880 
881 	printf("raid%d: Autoconfigure: %s\n", raidID,
882 	       auto_config ? "Yes" : "No");
883 
884 	if (auto_config == 1) {
885 		printf("raid%d: Root: %s\n", raidID, rootpart[root_config]);
886 	}
887 }
888 
889 static void
890 add_hot_spare(int fd, char *component)
891 {
892 	RF_SingleComponent_t hot_spare;
893 
894 	hot_spare.row = 0;
895 	hot_spare.column = 0;
896 	strncpy(hot_spare.component_name, component,
897 		sizeof(hot_spare.component_name));
898 
899 	do_ioctl( fd, RAIDFRAME_ADD_HOT_SPARE, &hot_spare,
900 		  "RAIDFRAME_ADD_HOT_SPARE");
901 }
902 
903 static void
904 remove_hot_spare(int fd, char *component)
905 {
906 	RF_SingleComponent_t hot_spare;
907 	int component_num;
908 	int num_cols;
909 
910 	get_component_number(fd, component, &component_num, &num_cols);
911 
912 	hot_spare.row = component_num / num_cols;
913 	hot_spare.column = component_num % num_cols;
914 
915 	strncpy(hot_spare.component_name, component,
916 		sizeof(hot_spare.component_name));
917 
918 	do_ioctl( fd, RAIDFRAME_REMOVE_HOT_SPARE, &hot_spare,
919 		  "RAIDFRAME_REMOVE_HOT_SPARE");
920 }
921 
922 static void
923 rebuild_in_place(int fd, char *component)
924 {
925 	RF_SingleComponent_t comp;
926 	int component_num;
927 	int num_cols;
928 
929 	get_component_number(fd, component, &component_num, &num_cols);
930 
931 	comp.row = 0;
932 	comp.column = component_num;
933 	strncpy(comp.component_name, component, sizeof(comp.component_name));
934 
935 	do_ioctl( fd, RAIDFRAME_REBUILD_IN_PLACE, &comp,
936 		  "RAIDFRAME_REBUILD_IN_PLACE");
937 
938 	if (verbose) {
939 		printf("Reconstruction status:\n");
940 		sleep(3); /* XXX give reconstruction a chance to start */
941 		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
942 	}
943 
944 }
945 
946 static void
947 check_parity(int fd, int do_rewrite, char *dev_name)
948 {
949 	int is_clean;
950 	int percent_done;
951 
952 	is_clean = 0;
953 	percent_done = 0;
954 	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
955 		 "RAIDFRAME_CHECK_PARITY");
956 	if (is_clean) {
957 		printf("%s: Parity status: clean\n",dev_name);
958 	} else {
959 		printf("%s: Parity status: DIRTY\n",dev_name);
960 		if (do_rewrite) {
961 			printf("%s: Initiating re-write of parity\n",
962 			       dev_name);
963 			do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
964 				 "RAIDFRAME_REWRITEPARITY");
965 			sleep(3); /* XXX give it time to
966 				     get started. */
967 			if (verbose) {
968 				printf("Parity Re-write status:\n");
969 				do_meter(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
970 			} else {
971 				do_ioctl(fd,
972 					 RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
973 					 &percent_done,
974 					 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS"
975 					 );
976 				while( percent_done < 100 ) {
977 					sleep(3); /* wait a bit... */
978 					do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
979 						 &percent_done, "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
980 				}
981 
982 			}
983 			       printf("%s: Parity Re-write complete\n",
984 				      dev_name);
985 		} else {
986 			/* parity is wrong, and is not being fixed.
987 			   Exit w/ an error. */
988 			exit(1);
989 		}
990 	}
991 }
992 
993 
994 static void
995 check_status(int fd, int meter)
996 {
997 	int recon_percent_done = 0;
998 	int parity_percent_done = 0;
999 	int copyback_percent_done = 0;
1000 
1001 	do_ioctl(fd, RAIDFRAME_CHECK_RECON_STATUS, &recon_percent_done,
1002 		 "RAIDFRAME_CHECK_RECON_STATUS");
1003 	printf("Reconstruction is %d%% complete.\n", recon_percent_done);
1004 	do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
1005 		 &parity_percent_done,
1006 		 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
1007 	printf("Parity Re-write is %d%% complete.\n", parity_percent_done);
1008 	do_ioctl(fd, RAIDFRAME_CHECK_COPYBACK_STATUS, &copyback_percent_done,
1009 		 "RAIDFRAME_CHECK_COPYBACK_STATUS");
1010 	printf("Copyback is %d%% complete.\n", copyback_percent_done);
1011 
1012 	if (meter) {
1013 		/* These 3 should be mutually exclusive at this point */
1014 		if (recon_percent_done < 100) {
1015 			printf("Reconstruction status:\n");
1016 			do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
1017 		} else if (parity_percent_done < 100) {
1018 			printf("Parity Re-write status:\n");
1019 			do_meter(fd,RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
1020 		} else if (copyback_percent_done < 100) {
1021 			printf("Copyback status:\n");
1022 			do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS_EXT);
1023 		}
1024 	}
1025 }
1026 
1027 const char *tbits = "|/-\\";
1028 
1029 static void
1030 do_meter(int fd, u_long option)
1031 {
1032 	int percent_done;
1033 	RF_uint64 start_value;
1034 	RF_ProgressInfo_t progressInfo;
1035 	void *pInfoPtr;
1036 	struct timeval start_time;
1037 	struct timeval current_time;
1038 	double elapsed;
1039 	int elapsed_sec;
1040 	int elapsed_usec;
1041 	int simple_eta,last_eta;
1042 	double rate;
1043 	RF_uint64 amount;
1044 	int tbit_value;
1045 	char bar_buffer[1024];
1046 	char eta_buffer[1024];
1047 
1048 	if (gettimeofday(&start_time,NULL) == -1)
1049 		err(1, "gettimeofday failed!?!?");
1050 	memset(&progressInfo, 0, sizeof(RF_ProgressInfo_t));
1051 	pInfoPtr=&progressInfo;
1052 
1053 	percent_done = 0;
1054 	do_ioctl(fd, option, pInfoPtr, "");
1055 	start_value = progressInfo.completed;
1056 	current_time = start_time;
1057 	simple_eta = 0;
1058 	last_eta = 0;
1059 
1060 	tbit_value = 0;
1061 	while(progressInfo.completed < progressInfo.total) {
1062 
1063 		percent_done = (progressInfo.completed * 100) /
1064 			progressInfo.total;
1065 
1066 		get_bar(bar_buffer, percent_done, 40);
1067 
1068 		elapsed_sec = current_time.tv_sec - start_time.tv_sec;
1069 		elapsed_usec = current_time.tv_usec - start_time.tv_usec;
1070 		if (elapsed_usec < 0) {
1071 			elapsed_usec-=1000000;
1072 			elapsed_sec++;
1073 		}
1074 
1075 		elapsed = (double) elapsed_sec +
1076 			(double) elapsed_usec / 1000000.0;
1077 
1078 		amount = progressInfo.completed - start_value;
1079 
1080 		if (amount <= 0) { /* we don't do negatives (yet?) */
1081 			amount = 0;
1082 		}
1083 
1084 		if (elapsed == 0)
1085 			rate = 0.0;
1086 		else
1087 			rate = amount / elapsed;
1088 
1089 		if (rate > 0.0) {
1090 			simple_eta = (int) (((double)progressInfo.total -
1091 					     (double) progressInfo.completed)
1092 					    / rate);
1093 		} else {
1094 			simple_eta = -1;
1095 		}
1096 
1097 		if (simple_eta <=0) {
1098 			simple_eta = last_eta;
1099 		} else {
1100 			last_eta = simple_eta;
1101 		}
1102 
1103 		get_time_string(eta_buffer, sizeof eta_buffer, simple_eta);
1104 
1105 		fprintf(stdout,"\r%3d%% |%s| ETA: %s %c",
1106 			percent_done,bar_buffer,eta_buffer,tbits[tbit_value]);
1107 		fflush(stdout);
1108 
1109 		if (++tbit_value>3)
1110 			tbit_value = 0;
1111 
1112 		sleep(2);
1113 
1114 		if (gettimeofday(&current_time,NULL) == -1)
1115 			err(1, "gettimeofday failed!?!?");
1116 
1117 		do_ioctl( fd, option, pInfoPtr, "");
1118 
1119 
1120 	}
1121 	printf("\n");
1122 }
1123 /* 40 '*''s per line, then 40 ' ''s line. */
1124 /* If you've got a screen wider than 160 characters, "tough" */
1125 
1126 #define STAR_MIDPOINT 4*40
1127 const char stars[] = "****************************************"
1128                      "****************************************"
1129                      "****************************************"
1130                      "****************************************"
1131                      "                                        "
1132                      "                                        "
1133                      "                                        "
1134                      "                                        "
1135                      "                                        ";
1136 
1137 static void
1138 get_bar(char *string, double percent, int max_strlen)
1139 {
1140 	int offset;
1141 
1142 	if (max_strlen > STAR_MIDPOINT) {
1143 		max_strlen = STAR_MIDPOINT;
1144 	}
1145 	offset = STAR_MIDPOINT -
1146 		(int)((percent * max_strlen)/ 100);
1147 	if (offset < 0)
1148 		offset = 0;
1149 	snprintf(string,max_strlen,"%s",stars+offset);
1150 }
1151 
1152 static void
1153 get_time_string(char *string, size_t len, int simple_time)
1154 {
1155 	int minutes, seconds, hours;
1156 	char hours_buffer[8];
1157 	char minutes_buffer[5];
1158 	char seconds_buffer[5];
1159 
1160 	if (simple_time >= 0) {
1161 
1162 		minutes = simple_time / 60;
1163 		seconds = simple_time - 60*minutes;
1164 		hours = minutes / 60;
1165 		minutes = minutes - 60*hours;
1166 #if defined(__GNUC__)
1167 		/*
1168 		 * snprintf() truncation checker fails to detect that seconds
1169 		 * and minutes will be 0-59 range.
1170 		 */
1171 		if (minutes < 0 || minutes > 60)
1172 			minutes = 60;
1173 		if (seconds < 0 || seconds > 60)
1174 			seconds = 60;
1175 #endif
1176 
1177 		if (hours > 0) {
1178 			snprintf(hours_buffer,sizeof hours_buffer,"%02d:",hours);
1179 		} else {
1180 			snprintf(hours_buffer,sizeof hours_buffer,"   ");
1181 		}
1182 
1183 		snprintf(minutes_buffer,sizeof minutes_buffer,"%02d:",minutes);
1184 		snprintf(seconds_buffer,sizeof seconds_buffer,"%02d",seconds);
1185 		snprintf(string,len,"%s%s%s",
1186 			 hours_buffer, minutes_buffer, seconds_buffer);
1187 	} else {
1188 		snprintf(string,len,"   --:--");
1189 	}
1190 
1191 }
1192 
1193 static void
1194 usage(void)
1195 {
1196 	const char *progname = getprogname();
1197 
1198 	fprintf(stderr, "usage: %s [-v] -A [yes | no | softroot | hardroot] dev\n", progname);
1199 	fprintf(stderr, "       %s [-v] -a component dev\n", progname);
1200 	fprintf(stderr, "       %s [-v] -B dev\n", progname);
1201 	fprintf(stderr, "       %s [-v] -C config_file dev\n", progname);
1202 	fprintf(stderr, "       %s [-v] -c config_file dev\n", progname);
1203 	fprintf(stderr, "       %s [-v] -F component dev\n", progname);
1204 	fprintf(stderr, "       %s [-v] -f component dev\n", progname);
1205 	fprintf(stderr, "       %s [-v] -G dev\n", progname);
1206 	fprintf(stderr, "       %s [-v] -g component dev\n", progname);
1207 	fprintf(stderr, "       %s [-v] -I serial_number dev\n", progname);
1208 	fprintf(stderr, "       %s [-v] -i dev\n", progname);
1209 	fprintf(stderr, "       %s [-v] -M [yes | no | set params] dev\n",
1210 	    progname);
1211 	fprintf(stderr, "       %s [-v] -m dev\n", progname);
1212 	fprintf(stderr, "       %s [-v] -P dev\n", progname);
1213 	fprintf(stderr, "       %s [-v] -p dev\n", progname);
1214 	fprintf(stderr, "       %s [-v] -R component dev\n", progname);
1215 	fprintf(stderr, "       %s [-v] -r component dev\n", progname);
1216 	fprintf(stderr, "       %s [-v] -S dev\n", progname);
1217 	fprintf(stderr, "       %s [-v] -s dev\n", progname);
1218 	fprintf(stderr, "       %s [-v] -U unit dev\n", progname);
1219 	fprintf(stderr, "       %s [-v] -u dev\n", progname);
1220 	exit(1);
1221 	/* NOTREACHED */
1222 }
1223 
1224 static unsigned int
1225 xstrtouint(const char *str)
1226 {
1227 	int e;
1228 	unsigned int num = (unsigned int)strtou(str, NULL, 10, 0, INT_MAX, &e);
1229 	if (e)
1230 		errc(EXIT_FAILURE, e, "Bad number `%s'", str);
1231 	return num;
1232 }
1233