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