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