xref: /netbsd-src/sbin/raidctl/raidctl.c (revision 89c5a767f8fc7a4633b2d409966e2becbb98ff92)
1 /*      $NetBSD: raidctl.c,v 1.14 2000/02/25 22:24:11 oster Exp $   */
2 /*-
3  * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Greg Oster
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *        This product includes software developed by the NetBSD
20  *        Foundation, Inc. and its contributors.
21  * 4. Neither the name of The NetBSD Foundation nor the names of its
22  *    contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 /*
39 
40    This program is a re-write of the original rf_ctrl program
41    distributed by CMU with RAIDframe 1.1.
42 
43    This program is the user-land interface to the RAIDframe kernel
44    driver in NetBSD.
45 
46  */
47 
48 #include <sys/param.h>
49 #include <sys/ioctl.h>
50 #include <sys/stat.h>
51 #include <util.h>
52 #include <stdio.h>
53 #include <fcntl.h>
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <sys/types.h>
58 #include <string.h>
59 #include <sys/disklabel.h>
60 #include <machine/disklabel.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 
64 #include "rf_raidframe.h"
65 
66 extern  char *__progname;
67 
68 int     main __P((int, char *[]));
69 void do_ioctl __P((int, unsigned long, void *, char *));
70 static  void rf_configure __P((int, char*, int));
71 static  char *device_status __P((RF_DiskStatus_t));
72 static  void rf_get_device_status __P((int));
73 static  void get_component_number __P((int, char *, int *, int *));
74 static  void rf_fail_disk __P((int, char *, int));
75 static  void usage __P((void));
76 static  void get_component_label __P((int, char *));
77 static  void set_component_label __P((int, char *));
78 static  void init_component_labels __P((int, int));
79 static  void set_autoconfig __P((int, int, char *));
80 static  void add_hot_spare __P((int, char *));
81 static  void remove_hot_spare __P((int, char *));
82 static  void rebuild_in_place __P((int, char *));
83 static  void check_status __P((int,int));
84 static  void check_parity __P((int,int,char *));
85 static  void do_meter __P((int, int));
86 static  void get_bar __P((char *, double, int));
87 static  void get_time_string __P((char *, int));
88 
89 int verbose = 0;
90 
91 int
92 main(argc,argv)
93 	int argc;
94 	char *argv[];
95 {
96 	extern char *optarg;
97 	extern int optind;
98 	int ch;
99 	int num_options;
100 	unsigned long action;
101 	char config_filename[PATH_MAX];
102 	char dev_name[PATH_MAX];
103 	char name[PATH_MAX];
104 	char component[PATH_MAX];
105 	char autoconf[10];
106 	int do_recon;
107 	int do_rewrite;
108 	int is_clean;
109 	int raidID;
110 	int rawpart;
111 	int serial_number;
112 	struct stat st;
113 	int fd;
114 	int force;
115 
116 	num_options = 0;
117 	action = 0;
118 	do_recon = 0;
119 	do_rewrite = 0;
120 	is_clean = 0;
121 	force = 0;
122 
123 	while ((ch = getopt(argc, argv, "a:A:Bc:C:f:F:g:iI:l:r:R:sSpPuv"))
124 	       != -1)
125 		switch(ch) {
126 		case 'a':
127 			action = RAIDFRAME_ADD_HOT_SPARE;
128 			strncpy(component, optarg, PATH_MAX);
129 			num_options++;
130 			break;
131 		case 'A':
132 			action = RAIDFRAME_SET_AUTOCONFIG;
133 			strncpy(autoconf, optarg, 10);
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 			strncpy(config_filename,optarg,PATH_MAX);
143 			force = 0;
144 			num_options++;
145 			break;
146 		case 'C':
147 			strncpy(config_filename,optarg,PATH_MAX);
148 			action = RAIDFRAME_CONFIGURE;
149 			force = 1;
150 			num_options++;
151 			break;
152 		case 'f':
153 			action = RAIDFRAME_FAIL_DISK;
154 			strncpy(component, optarg, PATH_MAX);
155 			do_recon = 0;
156 			num_options++;
157 			break;
158 		case 'F':
159 			action = RAIDFRAME_FAIL_DISK;
160 			strncpy(component, optarg, PATH_MAX);
161 			do_recon = 1;
162 			num_options++;
163 			break;
164 		case 'g':
165 			action = RAIDFRAME_GET_COMPONENT_LABEL;
166 			strncpy(component, optarg, PATH_MAX);
167 			num_options++;
168 			break;
169 		case 'i':
170 			action = RAIDFRAME_REWRITEPARITY;
171 			num_options++;
172 			break;
173 		case 'I':
174 			action = RAIDFRAME_INIT_LABELS;
175 			serial_number = atoi(optarg);
176 			num_options++;
177 			break;
178 		case 'l':
179 			action = RAIDFRAME_SET_COMPONENT_LABEL;
180 			strncpy(component, optarg, PATH_MAX);
181 			num_options++;
182 			break;
183 		case 'r':
184 			action = RAIDFRAME_REMOVE_HOT_SPARE;
185 			strncpy(component, optarg, PATH_MAX);
186 			num_options++;
187 			break;
188 		case 'R':
189 			strncpy(component,optarg,PATH_MAX);
190 			action = RAIDFRAME_REBUILD_IN_PLACE;
191 			num_options++;
192 			break;
193 		case 's':
194 			action = RAIDFRAME_GET_INFO;
195 			num_options++;
196 			break;
197 		case 'S':
198 			action = RAIDFRAME_CHECK_RECON_STATUS;
199 			num_options++;
200 			break;
201 		case 'p':
202 			action = RAIDFRAME_CHECK_PARITY;
203 			num_options++;
204 			break;
205 		case 'P':
206 			action = RAIDFRAME_CHECK_PARITY;
207 			do_rewrite = 1;
208 			num_options++;
209 			break;
210 		case 'u':
211 			action = RAIDFRAME_SHUTDOWN;
212 			num_options++;
213 			break;
214 		case 'v':
215 			verbose = 1;
216 			/* Don't bump num_options, as '-v' is not
217 			   an option like the others */
218 			/* num_options++; */
219 			break;
220 		default:
221 			usage();
222 		}
223 	argc -= optind;
224 	argv += optind;
225 
226 	if ((num_options > 1) || (argc == NULL))
227 		usage();
228 
229 	strncpy(name,argv[0],PATH_MAX);
230 
231 	if ((name[0] == '/') || (name[0] == '.')) {
232 		/* they've (apparently) given a full path... */
233 		strncpy(dev_name, name, PATH_MAX);
234 	} else {
235 		if (isdigit(name[strlen(name)-1])) {
236 			rawpart = getrawpartition();
237 			snprintf(dev_name,PATH_MAX,"/dev/%s%c",name,
238 				 'a'+rawpart);
239 		} else {
240 			snprintf(dev_name,PATH_MAX,"/dev/%s",name);
241 		}
242 	}
243 
244 	if (stat(dev_name, &st) != 0) {
245 		fprintf(stderr,"%s: stat failure on: %s\n",
246 			__progname,dev_name);
247 		return (errno);
248 	}
249 	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) {
250 		fprintf(stderr,"%s: invalid device: %s\n",
251 			__progname,dev_name);
252 		return (EINVAL);
253 	}
254 
255 	raidID = RF_DEV2RAIDID(st.st_rdev);
256 
257 	if ((fd = open( dev_name, O_RDWR, 0640)) < 0) {
258 		fprintf(stderr, "%s: unable to open device file: %s\n",
259 			__progname, dev_name);
260 		exit(1);
261 	}
262 
263 
264 	switch(action) {
265 	case RAIDFRAME_ADD_HOT_SPARE:
266 		add_hot_spare(fd, component);
267 		break;
268 	case RAIDFRAME_REMOVE_HOT_SPARE:
269 		remove_hot_spare(fd, component);
270 		break;
271 	case RAIDFRAME_CONFIGURE:
272 		rf_configure(fd, config_filename, force);
273 		break;
274 	case RAIDFRAME_SET_AUTOCONFIG:
275 		set_autoconfig(fd, raidID, autoconf);
276 		break;
277 	case RAIDFRAME_COPYBACK:
278 		printf("Copyback.\n");
279 		do_ioctl(fd, RAIDFRAME_COPYBACK, NULL, "RAIDFRAME_COPYBACK");
280 		if (verbose) {
281 			sleep(3); /* XXX give the copyback a chance to start */
282 			printf("Copyback status:\n");
283 			do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS);
284 		}
285 		break;
286 	case RAIDFRAME_FAIL_DISK:
287 		rf_fail_disk(fd, component, do_recon);
288 		break;
289 	case RAIDFRAME_SET_COMPONENT_LABEL:
290 		set_component_label(fd, component);
291 		break;
292 	case RAIDFRAME_GET_COMPONENT_LABEL:
293 		get_component_label(fd, component);
294 		break;
295 	case RAIDFRAME_INIT_LABELS:
296 		init_component_labels(fd, serial_number);
297 		break;
298 	case RAIDFRAME_REWRITEPARITY:
299 		printf("Initiating re-write of parity\n");
300 		do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
301 			 "RAIDFRAME_REWRITEPARITY");
302 		if (verbose) {
303 			sleep(3); /* XXX give it time to get started */
304 			printf("Parity Re-write status:\n");
305 			do_meter(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS);
306 		}
307 		break;
308 	case RAIDFRAME_CHECK_RECON_STATUS:
309 		check_status(fd,1);
310 		break;
311 	case RAIDFRAME_GET_INFO:
312 		rf_get_device_status(fd);
313 		break;
314 	case RAIDFRAME_REBUILD_IN_PLACE:
315 		rebuild_in_place(fd, component);
316 		break;
317 	case RAIDFRAME_CHECK_PARITY:
318 		check_parity(fd, do_rewrite, dev_name);
319 		break;
320 	case RAIDFRAME_SHUTDOWN:
321 		do_ioctl(fd, RAIDFRAME_SHUTDOWN, NULL, "RAIDFRAME_SHUTDOWN");
322 		break;
323 	default:
324 		break;
325 	}
326 
327 	close(fd);
328 	exit(0);
329 }
330 
331 void
332 do_ioctl(fd, command, arg, ioctl_name)
333 	int fd;
334 	unsigned long command;
335 	void *arg;
336 	char *ioctl_name;
337 {
338 	if (ioctl(fd, command, arg) < 0) {
339 		warn("ioctl (%s) failed", ioctl_name);
340 		exit(1);
341 	}
342 }
343 
344 
345 static void
346 rf_configure(fd,config_file,force)
347 	int fd;
348 	char *config_file;
349 	int force;
350 {
351 	void *generic;
352 	RF_Config_t cfg;
353 
354 	if (rf_MakeConfig( config_file, &cfg ) != 0) {
355 		fprintf(stderr,"%s: unable to create RAIDframe %s\n",
356 			__progname, "configuration structure\n");
357 		exit(1);
358 	}
359 
360 	cfg.force = force;
361 
362 	/*
363 
364 	   Note the extra level of redirection needed here, since
365 	   what we really want to pass in is a pointer to the pointer to
366 	   the configuration structure.
367 
368 	 */
369 
370 	generic = (void *) &cfg;
371 	do_ioctl(fd, RAIDFRAME_CONFIGURE, &generic, "RAIDFRAME_CONFIGURE");
372 }
373 
374 static char *
375 device_status(status)
376 	RF_DiskStatus_t status;
377 {
378 	static char status_string[256];
379 
380 	switch (status) {
381 	case rf_ds_optimal:
382 		strcpy(status_string,"optimal");
383 		break;
384 	case rf_ds_failed:
385 		strcpy(status_string,"failed");
386 		break;
387 	case rf_ds_reconstructing:
388 		strcpy(status_string,"reconstructing");
389 		break;
390 	case rf_ds_dist_spared:
391 		strcpy(status_string,"dist_spared");
392 		break;
393 	case rf_ds_spared:
394 		strcpy(status_string,"spared");
395 		break;
396 	case rf_ds_spare:
397 		strcpy(status_string,"spare");
398 		break;
399 	case rf_ds_used_spare:
400 		strcpy(status_string,"used_spare");
401 		break;
402 	default:
403 		strcpy(status_string,"UNKNOWN");
404 		break;
405 	}
406 	return(status_string);
407 }
408 
409 static void
410 rf_get_device_status(fd)
411 	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 	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
447 		 "RAIDFRAME_CHECK_PARITY");
448 	if (is_clean) {
449 		printf("Parity status: clean\n");
450 	} else {
451 		printf("Parity status: DIRTY\n");
452 	}
453 	check_status(fd,0);
454 }
455 
456 static void
457 get_component_number(fd, component_name, component_number, num_columns)
458 	int fd;
459 	char *component_name;
460 	int *component_number;
461 	int *num_columns;
462 {
463 	RF_DeviceConfig_t device_config;
464 	void *cfg_ptr;
465 	int i;
466 	int found;
467 
468 	*component_number = -1;
469 
470 	/* Assuming a full path spec... */
471 	cfg_ptr = &device_config;
472 	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr,
473 		 "RAIDFRAME_GET_INFO");
474 
475 	*num_columns = device_config.cols;
476 
477 	found = 0;
478 	for(i=0; i < device_config.ndevs; i++) {
479 		if (strncmp(component_name, device_config.devs[i].devname,
480 			    PATH_MAX)==0) {
481 			found = 1;
482 			*component_number = i;
483 		}
484 	}
485 	if (!found) {
486 		fprintf(stderr,"%s: %s is not a component %s", __progname,
487 			component_name, "of this device\n");
488 		exit(1);
489 	}
490 }
491 
492 static void
493 rf_fail_disk(fd, component_to_fail, do_recon)
494 	int fd;
495 	char *component_to_fail;
496 	int do_recon;
497 {
498 	struct rf_recon_req recon_request;
499 	int component_num;
500 	int num_cols;
501 
502 	get_component_number(fd, component_to_fail, &component_num, &num_cols);
503 
504 	recon_request.row = component_num / num_cols;
505 	recon_request.col = component_num % num_cols;
506 	if (do_recon) {
507 		recon_request.flags = RF_FDFLAGS_RECON;
508 	} else {
509 		recon_request.flags = RF_FDFLAGS_NONE;
510 	}
511 	do_ioctl(fd, RAIDFRAME_FAIL_DISK, &recon_request,
512 		 "RAIDFRAME_FAIL_DISK");
513 	if (do_recon && verbose) {
514 		printf("Reconstruction status:\n");
515 		sleep(3); /* XXX give reconstruction a chance to start */
516 		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS);
517 	}
518 }
519 
520 static void
521 get_component_label(fd, component)
522 	int fd;
523 	char *component;
524 {
525 	RF_ComponentLabel_t component_label;
526 	void *label_ptr;
527 	int component_num;
528 	int num_cols;
529 
530 	get_component_number(fd, component, &component_num, &num_cols);
531 
532 	memset( &component_label, 0, sizeof(RF_ComponentLabel_t));
533 	component_label.row = component_num / num_cols;
534 	component_label.column = component_num % num_cols;
535 
536 	label_ptr = &component_label;
537 	do_ioctl( fd, RAIDFRAME_GET_COMPONENT_LABEL, &label_ptr,
538 		  "RAIDFRAME_GET_COMPONENT_LABEL");
539 
540 	printf("Component label for %s:\n",component);
541 
542 	printf("   Row: %d Column: %d Num Rows: %d Num Columns: %d\n",
543 	       component_label.row, component_label.column,
544 	       component_label.num_rows, component_label.num_columns);
545 	printf("   Version: %d Serial Number: %d Mod Counter: %d\n",
546 	       component_label.version, component_label.serial_number,
547 	       component_label.mod_counter);
548 	printf("   Clean: %s Status: %d\n",
549 	       component_label.clean ? "Yes" : "No",
550 	       component_label.status );
551 	printf("   sectPerSU: %d SUsPerPU: %d SUsPerRU: %d\n",
552 	       component_label.sectPerSU, component_label.SUsPerPU,
553 	       component_label.SUsPerRU);
554 	printf("   RAID Level: %c  blocksize: %d numBlocks: %d\n",
555 	       (char) component_label.parityConfig,
556 	       component_label.blockSize, component_label.numBlocks);
557 	printf("   Autoconfig: %s\n",
558 	       component_label.autoconfigure ? "Yes" : "No" );
559 	printf("   Last configured as: raid%d\n", component_label.last_unit );
560 }
561 
562 static void
563 set_component_label(fd, component)
564 	int fd;
565 	char *component;
566 {
567 	RF_ComponentLabel_t component_label;
568 	int component_num;
569 	int num_cols;
570 
571 	get_component_number(fd, component, &component_num, &num_cols);
572 
573 	/* XXX This is currently here for testing, and future expandability */
574 
575 	component_label.version = 1;
576 	component_label.serial_number = 123456;
577 	component_label.mod_counter = 0;
578 	component_label.row = component_num / num_cols;
579 	component_label.column = component_num % num_cols;
580 	component_label.num_rows = 0;
581 	component_label.num_columns = 5;
582 	component_label.clean = 0;
583 	component_label.status = 1;
584 
585 	do_ioctl( fd, RAIDFRAME_SET_COMPONENT_LABEL, &component_label,
586 		  "RAIDFRAME_SET_COMPONENT_LABEL");
587 }
588 
589 
590 static void
591 init_component_labels(fd, serial_number)
592 	int fd;
593 	int serial_number;
594 {
595 	RF_ComponentLabel_t component_label;
596 
597 	component_label.version = 0;
598 	component_label.serial_number = serial_number;
599 	component_label.mod_counter = 0;
600 	component_label.row = 0;
601 	component_label.column = 0;
602 	component_label.num_rows = 0;
603 	component_label.num_columns = 0;
604 	component_label.clean = 0;
605 	component_label.status = 0;
606 
607 	do_ioctl( fd, RAIDFRAME_INIT_LABELS, &component_label,
608 		  "RAIDFRAME_SET_COMPONENT_LABEL");
609 }
610 
611 static void
612 set_autoconfig(fd, raidID, autoconf)
613 	int fd;
614 	int raidID;
615 	char *autoconf;
616 {
617 	int auto_config;
618 	int root_config;
619 
620 	auto_config = 0;
621 	root_config = 0;
622 
623 	if (strncasecmp(autoconf,"root", 4) == 0) {
624 		root_config = 1;
625 	}
626 
627 	if ((strncasecmp(autoconf,"yes", 3) == 0) ||
628 	    root_config == 1) {
629 		auto_config = 1;
630 	}
631 
632 	do_ioctl(fd, RAIDFRAME_SET_AUTOCONFIG, &auto_config,
633 		 "RAIDFRAME_SET_AUTOCONFIG");
634 
635 	do_ioctl(fd, RAIDFRAME_SET_ROOT, &root_config,
636 		 "RAIDFRAME_SET_ROOT");
637 
638 	printf("raid%d: Autoconfigure: %s\n", raidID,
639 	       auto_config ? "Yes" : "No");
640 
641 	if (root_config == 1) {
642 		printf("raid%d: Root: %s\n", raidID,
643 		       auto_config ? "Yes" : "No");
644 	}
645 }
646 
647 static void
648 add_hot_spare(fd, component)
649 	int fd;
650 	char *component;
651 {
652 	RF_SingleComponent_t hot_spare;
653 
654 	hot_spare.row = 0;
655 	hot_spare.column = 0;
656 	strncpy(hot_spare.component_name, component,
657 		sizeof(hot_spare.component_name));
658 
659 	do_ioctl( fd, RAIDFRAME_ADD_HOT_SPARE, &hot_spare,
660 		  "RAIDFRAME_ADD_HOT_SPARE");
661 }
662 
663 static void
664 remove_hot_spare(fd, component)
665 	int fd;
666 	char *component;
667 {
668 	RF_SingleComponent_t hot_spare;
669 	int component_num;
670 	int num_cols;
671 
672 	get_component_number(fd, component, &component_num, &num_cols);
673 
674 	hot_spare.row = component_num / num_cols;
675 	hot_spare.column = component_num % num_cols;
676 
677 	strncpy(hot_spare.component_name, component,
678 		sizeof(hot_spare.component_name));
679 
680 	do_ioctl( fd, RAIDFRAME_REMOVE_HOT_SPARE, &hot_spare,
681 		  "RAIDFRAME_REMOVE_HOT_SPARE");
682 }
683 
684 static void
685 rebuild_in_place( fd, component )
686 	int fd;
687 	char *component;
688 {
689 	RF_SingleComponent_t comp;
690 	int component_num;
691 	int num_cols;
692 
693 	get_component_number(fd, component, &component_num, &num_cols);
694 
695 	comp.row = 0;
696 	comp.column = component_num;
697 	strncpy(comp.component_name, component, sizeof(comp.component_name));
698 
699 	do_ioctl( fd, RAIDFRAME_REBUILD_IN_PLACE, &comp,
700 		  "RAIDFRAME_REBUILD_IN_PLACE");
701 
702 	if (verbose) {
703 		printf("Reconstruction status:\n");
704 		sleep(3); /* XXX give reconstruction a chance to start */
705 		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS);
706 	}
707 
708 }
709 
710 static void
711 check_parity( fd, do_rewrite, dev_name )
712 	int fd;
713 	int do_rewrite;
714 	char *dev_name;
715 {
716 	int is_clean;
717 	int percent_done;
718 
719 	is_clean = 0;
720 	percent_done = 0;
721 	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
722 		 "RAIDFRAME_CHECK_PARITY");
723 	if (is_clean) {
724 		printf("%s: Parity status: clean\n",dev_name);
725 	} else {
726 		printf("%s: Parity status: DIRTY\n",dev_name);
727 		if (do_rewrite) {
728 			printf("%s: Initiating re-write of parity\n",
729 			       dev_name);
730 			do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
731 				 "RAIDFRAME_REWRITEPARITY");
732 			sleep(3); /* XXX give it time to
733 				     get started. */
734 			if (verbose) {
735 				printf("Parity Re-write status:\n");
736 				do_meter(fd,
737 					 RAIDFRAME_CHECK_PARITYREWRITE_STATUS);
738 			} else {
739 				do_ioctl(fd,
740 					 RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
741 					 &percent_done,
742 					 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS"
743 					 );
744 				while( percent_done < 100 ) {
745 					do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
746 						 &percent_done, "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
747 				}
748 
749 			}
750 			       printf("%s: Parity Re-write complete\n",
751 				      dev_name);
752 		} else {
753 			/* parity is wrong, and is not being fixed.
754 			   Exit w/ an error. */
755 			exit(1);
756 		}
757 	}
758 }
759 
760 
761 static void
762 check_status( fd, meter )
763 	int fd;
764 	int meter;
765 {
766 	int recon_percent_done = 0;
767 	int parity_percent_done = 0;
768 	int copyback_percent_done = 0;
769 
770 	do_ioctl(fd, RAIDFRAME_CHECK_RECON_STATUS, &recon_percent_done,
771 		 "RAIDFRAME_CHECK_RECON_STATUS");
772 	printf("Reconstruction is %d%% complete.\n", recon_percent_done);
773 	do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
774 		 &parity_percent_done,
775 		 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
776 	printf("Parity Re-write is %d%% complete.\n", parity_percent_done);
777 	do_ioctl(fd, RAIDFRAME_CHECK_COPYBACK_STATUS, &copyback_percent_done,
778 		 "RAIDFRAME_CHECK_COPYBACK_STATUS");
779 	printf("Copyback is %d%% complete.\n", copyback_percent_done);
780 
781 	if (meter) {
782 		/* These 3 should be mutually exclusive at this point */
783 		if (recon_percent_done < 100) {
784 			printf("Reconstruction status:\n");
785 			do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS);
786 		} else if (parity_percent_done < 100) {
787 			printf("Parity Re-write status:\n");
788 			do_meter(fd,RAIDFRAME_CHECK_PARITYREWRITE_STATUS);
789 		} else if (copyback_percent_done < 100) {
790 			printf("Copyback status:\n");
791 			do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS);
792 		}
793 	}
794 }
795 
796 char *tbits = "|/-\\";
797 
798 static void
799 do_meter( fd, option )
800 	int fd;
801 	int option;
802 {
803 	int percent_done;
804 	int last_percent;
805 	int start_percent;
806 	struct timeval start_time;
807 	struct timeval last_time;
808 	struct timeval current_time;
809 	double elapsed;
810 	int elapsed_sec;
811 	int elapsed_usec;
812 	int simple_eta,last_eta;
813 	double rate;
814 	int amount;
815 	int tbit_value;
816 	int wait_for_more_data;
817 	char buffer[1024];
818 	char bar_buffer[1024];
819 	char eta_buffer[1024];
820 
821 	if (gettimeofday(&start_time,NULL)) {
822 		fprintf(stderr,"%s: gettimeofday failed!?!?\n",__progname);
823 		exit(errno);
824 	}
825 	percent_done = 0;
826 	do_ioctl( fd, option, &percent_done, "");
827 	last_percent = percent_done;
828 	start_percent = percent_done;
829 	last_time = start_time;
830 	current_time = start_time;
831 
832 	wait_for_more_data = 0;
833 	tbit_value = 0;
834 
835 	while(percent_done < 100) {
836 
837 		get_bar(bar_buffer, percent_done, 40);
838 
839 		elapsed_sec = current_time.tv_sec - last_time.tv_sec;
840 
841 		elapsed_usec = current_time.tv_usec - last_time.tv_usec;
842 
843 		if (elapsed_usec < 0) {
844 			elapsed_usec-=1000000;
845 			elapsed_sec++;
846 		}
847 
848 		elapsed = (double) elapsed_sec +
849 			(double) elapsed_usec / 1000000.0;
850 		if (elapsed <= 0.0) {
851 			elapsed = 0.0001; /* XXX */
852 		}
853 
854 		amount = percent_done - last_percent;
855 		if (amount <= 0) { /* we don't do negatives (yet?) */
856 			amount = 0;
857 			wait_for_more_data = 1;
858 		} else {
859 			wait_for_more_data = 0;
860 		}
861 		rate = amount / elapsed;
862 
863 
864 		if (rate > 0.0) {
865 			simple_eta = (int) ((100.0 - (double) last_percent ) / rate);
866 		} else {
867 			simple_eta = -1;
868 		}
869 		if (simple_eta <=0) {
870 			simple_eta = last_eta;
871 		} else {
872 			last_eta = simple_eta;
873 		}
874 
875 		get_time_string(eta_buffer, simple_eta);
876 
877 		snprintf(buffer,1024,"\r%3d%% |%s| ETA: %s %c",
878 			 percent_done,bar_buffer,eta_buffer,tbits[tbit_value]);
879 
880 		write(fileno(stdout),buffer,strlen(buffer));
881 		fflush(stdout);
882 
883 		/* resolution wasn't high enough... wait until we get another
884 		   timestamp and perhaps more "work" done. */
885 
886 		if (!wait_for_more_data) {
887 			last_time = current_time;
888 			last_percent = percent_done;
889 		}
890 
891 		if (++tbit_value>3)
892 			tbit_value = 0;
893 
894 		sleep(2);
895 
896 		if (gettimeofday(&current_time,NULL)) {
897 			fprintf(stderr,"%s: gettimeofday failed!?!?\n",
898 				__progname);
899 			exit(errno);
900 		}
901 
902 		do_ioctl( fd, option, &percent_done, "");
903 
904 
905 	}
906 	printf("\n");
907 }
908 /* 40 '*''s per line, then 40 ' ''s line. */
909 /* If you've got a screen wider than 160 characters, "tough" */
910 
911 #define STAR_MIDPOINT 4*40
912 const char stars[] = "****************************************"
913                      "****************************************"
914                      "****************************************"
915                      "****************************************"
916                      "                                        "
917                      "                                        "
918                      "                                        "
919                      "                                        "
920                      "                                        ";
921 
922 static void
923 get_bar(string,percent,max_strlen)
924 	char *string;
925 	double percent;
926 	int max_strlen;
927 {
928 	int offset;
929 
930 	if (max_strlen > STAR_MIDPOINT) {
931 		max_strlen = STAR_MIDPOINT;
932 	}
933 	offset = STAR_MIDPOINT -
934 		(int)((percent * max_strlen)/ 100);
935 	if (offset < 0)
936 		offset = 0;
937 	snprintf(string,max_strlen,"%s",&stars[offset]);
938 }
939 
940 static void
941 get_time_string(string,simple_time)
942 	char *string;
943 	int simple_time;
944 {
945 	int minutes, seconds, hours;
946 	char hours_buffer[5];
947 	char minutes_buffer[5];
948 	char seconds_buffer[5];
949 
950 	if (simple_time >= 0) {
951 
952 		minutes = (int) simple_time / 60;
953 		seconds = ((int)simple_time - 60*minutes);
954 		hours = minutes / 60;
955 		minutes = minutes - 60*hours;
956 
957 		if (hours > 0) {
958 			snprintf(hours_buffer,5,"%02d:",hours);
959 		} else {
960 			snprintf(hours_buffer,5,"   ");
961 		}
962 
963 		snprintf(minutes_buffer,5,"%02d:",minutes);
964 		snprintf(seconds_buffer,5,"%02d",seconds);
965 		snprintf(string,1024,"%s%s%s",
966 			 hours_buffer, minutes_buffer, seconds_buffer);
967 	} else {
968 		snprintf(string,1024,"   --:--");
969 	}
970 
971 }
972 
973 static void
974 usage()
975 {
976 	fprintf(stderr, "usage: %s [-v] -a component dev\n", __progname);
977 	fprintf(stderr, "       %s [-v] -A yes | no | root dev\n", __progname);
978 	fprintf(stderr, "       %s [-v] -B dev\n", __progname);
979 	fprintf(stderr, "       %s [-v] -c config_file dev\n", __progname);
980 	fprintf(stderr, "       %s [-v] -C config_file dev\n", __progname);
981 	fprintf(stderr, "       %s [-v] -f component dev\n", __progname);
982 	fprintf(stderr, "       %s [-v] -F component dev\n", __progname);
983 	fprintf(stderr, "       %s [-v] -g component dev\n", __progname);
984 	fprintf(stderr, "       %s [-v] -i dev\n", __progname);
985 	fprintf(stderr, "       %s [-v] -I serial_number dev\n", __progname);
986 	fprintf(stderr, "       %s [-v] -r component dev\n", __progname);
987 	fprintf(stderr, "       %s [-v] -R component dev\n", __progname);
988 	fprintf(stderr, "       %s [-v] -s dev\n", __progname);
989 	fprintf(stderr, "       %s [-v] -S dev\n", __progname);
990 	fprintf(stderr, "       %s [-v] -u dev\n", __progname);
991 #if 0
992 	fprintf(stderr, "usage: %s %s\n", __progname,
993 		"-a | -f | -F | -g | -r | -R component dev");
994 	fprintf(stderr, "       %s -B | -i | -s | -S -u dev\n", __progname);
995 	fprintf(stderr, "       %s -c | -C config_file dev\n", __progname);
996 	fprintf(stderr, "       %s -I serial_number dev\n", __progname);
997 #endif
998 	exit(1);
999 	/* NOTREACHED */
1000 }
1001