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