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