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