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