raidctl.c revision 1.10
1/*      $NetBSD: raidctl.c,v 1.10 2000/01/05 03:02:41 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
66extern  char *__progname;
67
68int     main __P((int, char *[]));
69void do_ioctl __P((int, unsigned long, void *, char *));
70static  void rf_configure __P((int, char*, int));
71static  char *device_status __P((RF_DiskStatus_t));
72static  void rf_get_device_status __P((int));
73static  void get_component_number __P((int, char *, int *, int *));
74static  void rf_fail_disk __P((int, char *, int));
75static  void usage __P((void));
76static  void get_component_label __P((int, char *));
77static  void set_component_label __P((int, char *));
78static  void init_component_labels __P((int, int));
79static  void add_hot_spare __P((int, char *));
80static  void remove_hot_spare __P((int, char *));
81static  void rebuild_in_place __P((int, char *));
82static  void check_status __P((int));
83static  void check_parity __P((int,int,char *));
84static  void do_meter __P((int, int));
85static  void get_bar __P((char *, double, int));
86static  void get_time_string __P((char *, int));
87
88int verbose = 0;
89
90int
91main(argc,argv)
92	int argc;
93	char *argv[];
94{
95	extern char *optarg;
96	extern int optind;
97	int ch;
98	int num_options;
99	unsigned long action;
100	char config_filename[PATH_MAX];
101	char dev_name[PATH_MAX];
102	char name[PATH_MAX];
103	char component[PATH_MAX];
104	int do_recon;
105	int do_rewrite;
106	int is_clean;
107	int raidID;
108	int rawpart;
109	int serial_number;
110	struct stat st;
111	int fd;
112	int force;
113
114	num_options = 0;
115	action = 0;
116	do_recon = 0;
117	do_rewrite = 0;
118	is_clean = 0;
119	force = 0;
120
121	while ((ch = getopt(argc, argv, "a:Bc:C:f:F:g:iI:l:r:R:sSpPuv")) != -1)
122		switch(ch) {
123		case 'a':
124			action = RAIDFRAME_ADD_HOT_SPARE;
125			strncpy(component, optarg, PATH_MAX);
126			num_options++;
127			break;
128		case 'B':
129			action = RAIDFRAME_COPYBACK;
130			num_options++;
131			break;
132		case 'c':
133			action = RAIDFRAME_CONFIGURE;
134			strncpy(config_filename,optarg,PATH_MAX);
135			force = 0;
136			num_options++;
137			break;
138		case 'C':
139			strncpy(config_filename,optarg,PATH_MAX);
140			action = RAIDFRAME_CONFIGURE;
141			force = 1;
142			num_options++;
143			break;
144		case 'f':
145			action = RAIDFRAME_FAIL_DISK;
146			strncpy(component, optarg, PATH_MAX);
147			do_recon = 0;
148			num_options++;
149			break;
150		case 'F':
151			action = RAIDFRAME_FAIL_DISK;
152			strncpy(component, optarg, PATH_MAX);
153			do_recon = 1;
154			num_options++;
155			break;
156		case 'g':
157			action = RAIDFRAME_GET_COMPONENT_LABEL;
158			strncpy(component, optarg, PATH_MAX);
159			num_options++;
160			break;
161		case 'i':
162			action = RAIDFRAME_REWRITEPARITY;
163			num_options++;
164			break;
165		case 'I':
166			action = RAIDFRAME_INIT_LABELS;
167			serial_number = atoi(optarg);
168			num_options++;
169			break;
170		case 'l':
171			action = RAIDFRAME_SET_COMPONENT_LABEL;
172			strncpy(component, optarg, PATH_MAX);
173			num_options++;
174			break;
175		case 'r':
176			action = RAIDFRAME_REMOVE_HOT_SPARE;
177			strncpy(component, optarg, PATH_MAX);
178			num_options++;
179			break;
180		case 'R':
181			strncpy(component,optarg,PATH_MAX);
182			action = RAIDFRAME_REBUILD_IN_PLACE;
183			num_options++;
184			break;
185		case 's':
186			action = RAIDFRAME_GET_INFO;
187			num_options++;
188			break;
189		case 'S':
190			action = RAIDFRAME_CHECK_RECON_STATUS;
191			num_options++;
192			break;
193		case 'p':
194			action = RAIDFRAME_CHECK_PARITY;
195			num_options++;
196			break;
197		case 'P':
198			action = RAIDFRAME_CHECK_PARITY;
199			do_rewrite = 1;
200			num_options++;
201			break;
202		case 'u':
203			action = RAIDFRAME_SHUTDOWN;
204			num_options++;
205			break;
206		case 'v':
207			verbose = 1;
208			/* Don't bump num_options, as '-v' is not
209			   an option like the others */
210			/* num_options++; */
211			break;
212		default:
213			usage();
214		}
215	argc -= optind;
216	argv += optind;
217
218	if ((num_options > 1) || (argc == NULL))
219		usage();
220
221	strncpy(name,argv[0],PATH_MAX);
222
223	if ((name[0] == '/') || (name[0] == '.')) {
224		/* they've (apparently) given a full path... */
225		strncpy(dev_name, name, PATH_MAX);
226	} else {
227		if (isdigit(name[strlen(name)-1])) {
228			rawpart = getrawpartition();
229			snprintf(dev_name,PATH_MAX,"/dev/%s%c",name,
230				 'a'+rawpart);
231		} else {
232			snprintf(dev_name,PATH_MAX,"/dev/%s",name);
233		}
234	}
235
236	if (stat(dev_name, &st) != 0) {
237		fprintf(stderr,"%s: stat failure on: %s\n",
238			__progname,dev_name);
239		return (errno);
240	}
241	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) {
242		fprintf(stderr,"%s: invalid device: %s\n",
243			__progname,dev_name);
244		return (EINVAL);
245	}
246
247	raidID = RF_DEV2RAIDID(st.st_rdev);
248
249	if ((fd = open( dev_name, O_RDWR, 0640)) < 0) {
250		fprintf(stderr, "%s: unable to open device file: %s\n",
251			__progname, dev_name);
252		exit(1);
253	}
254
255
256	switch(action) {
257	case RAIDFRAME_ADD_HOT_SPARE:
258		add_hot_spare(fd,component);
259		break;
260	case RAIDFRAME_REMOVE_HOT_SPARE:
261		remove_hot_spare(fd,component);
262		break;
263	case RAIDFRAME_CONFIGURE:
264		rf_configure(fd, config_filename,force);
265		break;
266	case RAIDFRAME_COPYBACK:
267		printf("Copyback.\n");
268		do_ioctl(fd, RAIDFRAME_COPYBACK, NULL, "RAIDFRAME_COPYBACK");
269		if (verbose) {
270			sleep(3); /* XXX give the copyback a chance to start */
271			printf("Copyback status:\n");
272			do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS);
273		}
274		break;
275	case RAIDFRAME_FAIL_DISK:
276		rf_fail_disk(fd,component,do_recon);
277		break;
278	case RAIDFRAME_SET_COMPONENT_LABEL:
279		set_component_label(fd,component);
280		break;
281	case RAIDFRAME_GET_COMPONENT_LABEL:
282		get_component_label(fd,component);
283		break;
284	case RAIDFRAME_INIT_LABELS:
285		init_component_labels(fd,serial_number);
286		break;
287	case RAIDFRAME_REWRITEPARITY:
288		printf("Initiating re-write of parity\n");
289		do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
290			 "RAIDFRAME_REWRITEPARITY");
291		if (verbose) {
292			sleep(3); /* XXX give it time to get started */
293			printf("Parity Re-write status:\n");
294			do_meter(fd,RAIDFRAME_CHECK_PARITYREWRITE_STATUS);
295		}
296		break;
297	case RAIDFRAME_CHECK_RECON_STATUS:
298		check_status(fd);
299		break;
300	case RAIDFRAME_GET_INFO:
301		rf_get_device_status(fd);
302		break;
303	case RAIDFRAME_REBUILD_IN_PLACE:
304		rebuild_in_place(fd,component);
305		break;
306	case RAIDFRAME_CHECK_PARITY:
307		check_parity(fd,do_rewrite,dev_name);
308		break;
309	case RAIDFRAME_SHUTDOWN:
310		do_ioctl(fd, RAIDFRAME_SHUTDOWN, NULL, "RAIDFRAME_SHUTDOWN");
311		break;
312	default:
313		break;
314	}
315
316	close(fd);
317	exit(0);
318}
319
320void
321do_ioctl(fd, command, arg, ioctl_name)
322	int fd;
323	unsigned long command;
324	void *arg;
325	char *ioctl_name;
326{
327	if (ioctl(fd, command, arg) < 0) {
328		warn("ioctl (%s) failed", ioctl_name);
329		printf("ioctl (%s) failed", ioctl_name);
330		exit(1);
331	}
332}
333
334
335static void
336rf_configure(fd,config_file,force)
337	int fd;
338	char *config_file;
339	int force;
340{
341	void *generic;
342	RF_Config_t cfg;
343
344	if (rf_MakeConfig( config_file, &cfg ) != 0) {
345		fprintf(stderr,"%s: unable to create RAIDframe %s\n",
346			__progname, "configuration structure\n");
347		exit(1);
348	}
349
350	cfg.force = force;
351
352	/*
353
354	   Note the extra level of redirection needed here, since
355	   what we really want to pass in is a pointer to the pointer to
356	   the configuration structure.
357
358	 */
359
360	generic = (void *) &cfg;
361	do_ioctl(fd, RAIDFRAME_CONFIGURE, &generic, "RAIDFRAME_CONFIGURE");
362}
363
364static char *
365device_status(status)
366	RF_DiskStatus_t status;
367{
368	static char status_string[256];
369
370	switch (status) {
371	case rf_ds_optimal:
372		strcpy(status_string,"optimal");
373		break;
374	case rf_ds_failed:
375		strcpy(status_string,"failed");
376		break;
377	case rf_ds_reconstructing:
378		strcpy(status_string,"reconstructing");
379		break;
380	case rf_ds_dist_spared:
381		strcpy(status_string,"dist_spared");
382		break;
383	case rf_ds_spared:
384		strcpy(status_string,"spared");
385		break;
386	case rf_ds_spare:
387		strcpy(status_string,"spare");
388		break;
389	case rf_ds_used_spare:
390		strcpy(status_string,"used_spare");
391		break;
392	default:
393		strcpy(status_string,"UNKNOWN");
394		break;
395	}
396	return(status_string);
397}
398
399static void
400rf_get_device_status(fd)
401	int fd;
402{
403	RF_DeviceConfig_t device_config;
404	void *cfg_ptr;
405	int is_clean;
406	int i;
407
408	cfg_ptr = &device_config;
409
410	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr, "RAIDFRAME_GET_INFO");
411
412	printf("Components:\n");
413	for(i=0; i < device_config.ndevs; i++) {
414		printf("%20s: %s\n", device_config.devs[i].devname,
415		       device_status(device_config.devs[i].status));
416	}
417	if (device_config.nspares > 0) {
418		printf("Spares:\n");
419		for(i=0; i < device_config.nspares; i++) {
420			printf("%20s: %s\n",
421			       device_config.spares[i].devname,
422			       device_status(device_config.spares[i].status));
423		}
424	} else {
425		printf("No spares.\n");
426	}
427	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
428		 "RAIDFRAME_CHECK_PARITY");
429	if (is_clean) {
430		printf("Parity status: clean\n");
431	} else {
432		printf("Parity status: DIRTY\n");
433	}
434	check_status(fd);
435}
436
437static void
438get_component_number(fd, component_name, component_number, num_columns)
439	int fd;
440	char *component_name;
441	int *component_number;
442	int *num_columns;
443{
444	RF_DeviceConfig_t device_config;
445	void *cfg_ptr;
446	int i;
447	int found;
448
449	*component_number = -1;
450
451	/* Assuming a full path spec... */
452	cfg_ptr = &device_config;
453	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr,
454		 "RAIDFRAME_GET_INFO");
455
456	*num_columns = device_config.cols;
457
458	found = 0;
459	for(i=0; i < device_config.ndevs; i++) {
460		if (strncmp(component_name, device_config.devs[i].devname,
461			    PATH_MAX)==0) {
462			found = 1;
463			*component_number = i;
464		}
465	}
466	if (!found) {
467		fprintf(stderr,"%s: %s is not a component %s", __progname,
468			component_name, "of this device\n");
469		exit(1);
470	}
471}
472
473static void
474rf_fail_disk(fd, component_to_fail, do_recon)
475	int fd;
476	char *component_to_fail;
477	int do_recon;
478{
479	struct rf_recon_req recon_request;
480	int component_num;
481	int num_cols;
482
483	get_component_number(fd, component_to_fail, &component_num, &num_cols);
484
485	recon_request.row = component_num / num_cols;
486	recon_request.col = component_num % num_cols;
487	if (do_recon) {
488		recon_request.flags = RF_FDFLAGS_RECON;
489	} else {
490		recon_request.flags = RF_FDFLAGS_NONE;
491	}
492	do_ioctl(fd, RAIDFRAME_FAIL_DISK, &recon_request,
493		 "RAIDFRAME_FAIL_DISK");
494	if (do_recon && verbose) {
495		printf("Reconstruction status:\n");
496		sleep(3); /* XXX give reconstruction a chance to start */
497		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS);
498	}
499}
500
501static void
502get_component_label(fd, component)
503	int fd;
504	char *component;
505{
506	RF_ComponentLabel_t component_label;
507	void *label_ptr;
508	int component_num;
509	int num_cols;
510
511	get_component_number(fd, component, &component_num, &num_cols);
512
513	memset( &component_label, 0, sizeof(RF_ComponentLabel_t));
514	component_label.row = component_num / num_cols;
515	component_label.column = component_num % num_cols;
516
517	label_ptr = &component_label;
518	do_ioctl( fd, RAIDFRAME_GET_COMPONENT_LABEL, &label_ptr,
519		  "RAIDFRAME_GET_COMPONENT_LABEL");
520
521	printf("Component label for %s:\n",component);
522	printf("Version: %d\n",component_label.version);
523	printf("Serial Number: %d\n",component_label.serial_number);
524	printf("Mod counter: %d\n",component_label.mod_counter);
525	printf("Row: %d\n", component_label.row);
526	printf("Column: %d\n", component_label.column);
527	printf("Num Rows: %d\n", component_label.num_rows);
528	printf("Num Columns: %d\n", component_label.num_columns);
529	printf("Clean: %d\n", component_label.clean);
530	printf("Status: %s\n", device_status(component_label.status));
531}
532
533static void
534set_component_label(fd, component)
535	int fd;
536	char *component;
537{
538	RF_ComponentLabel_t component_label;
539	int component_num;
540	int num_cols;
541
542	get_component_number(fd, component, &component_num, &num_cols);
543
544	/* XXX This is currently here for testing, and future expandability */
545
546	component_label.version = 1;
547	component_label.serial_number = 123456;
548	component_label.mod_counter = 0;
549	component_label.row = component_num / num_cols;
550	component_label.column = component_num % num_cols;
551	component_label.num_rows = 0;
552	component_label.num_columns = 5;
553	component_label.clean = 0;
554	component_label.status = 1;
555
556	do_ioctl( fd, RAIDFRAME_SET_COMPONENT_LABEL, &component_label,
557		  "RAIDFRAME_SET_COMPONENT_LABEL");
558}
559
560
561static void
562init_component_labels(fd, serial_number)
563	int fd;
564	int serial_number;
565{
566	RF_ComponentLabel_t component_label;
567
568	component_label.version = 0;
569	component_label.serial_number = serial_number;
570	component_label.mod_counter = 0;
571	component_label.row = 0;
572	component_label.column = 0;
573	component_label.num_rows = 0;
574	component_label.num_columns = 0;
575	component_label.clean = 0;
576	component_label.status = 0;
577
578	do_ioctl( fd, RAIDFRAME_INIT_LABELS, &component_label,
579		  "RAIDFRAME_SET_COMPONENT_LABEL");
580}
581
582static void
583add_hot_spare(fd, component)
584	int fd;
585	char *component;
586{
587	RF_SingleComponent_t hot_spare;
588
589	hot_spare.row = 0;
590	hot_spare.column = 0;
591	strncpy(hot_spare.component_name, component,
592		sizeof(hot_spare.component_name));
593
594	do_ioctl( fd, RAIDFRAME_ADD_HOT_SPARE, &hot_spare,
595		  "RAIDFRAME_ADD_HOT_SPARE");
596}
597
598static void
599remove_hot_spare(fd, component)
600	int fd;
601	char *component;
602{
603	RF_SingleComponent_t hot_spare;
604	int component_num;
605	int num_cols;
606
607	get_component_number(fd, component, &component_num, &num_cols);
608
609	hot_spare.row = component_num / num_cols;
610	hot_spare.column = component_num % num_cols;
611
612	strncpy(hot_spare.component_name, component,
613		sizeof(hot_spare.component_name));
614
615	do_ioctl( fd, RAIDFRAME_REMOVE_HOT_SPARE, &hot_spare,
616		  "RAIDFRAME_REMOVE_HOT_SPARE");
617}
618
619static void
620rebuild_in_place( fd, component )
621	int fd;
622	char *component;
623{
624	RF_SingleComponent_t comp;
625	int component_num;
626	int num_cols;
627
628	get_component_number(fd, component, &component_num, &num_cols);
629
630	comp.row = 0;
631	comp.column = component_num;
632	strncpy(comp.component_name, component, sizeof(comp.component_name));
633
634	do_ioctl( fd, RAIDFRAME_REBUILD_IN_PLACE, &comp,
635		  "RAIDFRAME_REBUILD_IN_PLACE");
636
637	if (verbose) {
638		printf("Reconstruction status:\n");
639		sleep(3); /* XXX give reconstruction a chance to start */
640		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS);
641	}
642
643}
644
645static void
646check_parity( fd, do_rewrite, dev_name )
647	int fd;
648	int do_rewrite;
649	char *dev_name;
650{
651	int is_clean;
652	int percent_done;
653
654	is_clean = 0;
655	percent_done = 0;
656	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
657		 "RAIDFRAME_CHECK_PARITY");
658	if (is_clean) {
659		printf("%s: Parity status: clean\n",dev_name);
660	} else {
661		printf("%s: Parity status: DIRTY\n",dev_name);
662		if (do_rewrite) {
663			printf("%s: Initiating re-write of parity\n",
664			       dev_name);
665			do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
666				 "RAIDFRAME_REWRITEPARITY");
667			sleep(3); /* XXX give it time to
668				     get started. */
669			if (verbose) {
670				printf("Parity Re-write status:\n");
671				do_meter(fd,
672					 RAIDFRAME_CHECK_PARITYREWRITE_STATUS);
673			} else {
674				do_ioctl(fd,
675					 RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
676					 &percent_done,
677					 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS"
678					 );
679				while( percent_done < 100 ) {
680					do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
681						 &percent_done, "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
682				}
683
684			}
685			       printf("%s: Parity Re-write complete\n",
686				      dev_name);
687		} else {
688			/* parity is wrong, and is not being fixed.
689			   Exit w/ an error. */
690			exit(1);
691		}
692	}
693}
694
695
696static void
697check_status( fd )
698	int fd;
699{
700	int recon_percent_done = 0;
701	int parity_percent_done = 0;
702	int copyback_percent_done = 0;
703
704	do_ioctl(fd, RAIDFRAME_CHECK_RECON_STATUS, &recon_percent_done,
705		 "RAIDFRAME_CHECK_RECON_STATUS");
706	printf("Reconstruction is %d%% complete.\n", recon_percent_done);
707	do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
708		 &parity_percent_done,
709		 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
710	printf("Parity Re-write is %d%% complete.\n", parity_percent_done);
711	do_ioctl(fd, RAIDFRAME_CHECK_COPYBACK_STATUS, &copyback_percent_done,
712		 "RAIDFRAME_CHECK_COPYBACK_STATUS");
713	printf("Copyback is %d%% complete.\n", copyback_percent_done);
714
715	/* These 3 should be mutually exclusive at this point */
716	if (recon_percent_done < 100) {
717		printf("Reconstruction status:\n");
718		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS);
719	} else if (parity_percent_done < 100) {
720		printf("Parity Re-write status:\n");
721		do_meter(fd,RAIDFRAME_CHECK_PARITYREWRITE_STATUS);
722	} else if (copyback_percent_done < 100) {
723		printf("Copyback status:\n");
724		do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS);
725	}
726}
727
728char *tbits = "|/-\\";
729
730static void
731do_meter( fd, option )
732	int fd;
733	int option;
734{
735	int percent_done;
736	int last_percent;
737	int start_percent;
738	struct timeval start_time;
739	struct timeval last_time;
740	struct timeval current_time;
741	double elapsed;
742	int elapsed_sec;
743	int elapsed_usec;
744	int simple_eta,last_eta;
745	double rate;
746	int amount;
747	int tbit_value;
748	int wait_for_more_data;
749	char buffer[1024];
750	char bar_buffer[1024];
751	char eta_buffer[1024];
752
753	if (gettimeofday(&start_time,NULL)) {
754		fprintf(stderr,"%s: gettimeofday failed!?!?\n",__progname);
755		exit(errno);
756	}
757	percent_done = 0;
758	do_ioctl( fd, option, &percent_done, "");
759	last_percent = percent_done;
760	start_percent = percent_done;
761	last_time = start_time;
762	current_time = start_time;
763
764	wait_for_more_data = 0;
765	tbit_value = 0;
766
767	while(percent_done < 100) {
768
769		get_bar(bar_buffer, percent_done, 40);
770
771		elapsed_sec = current_time.tv_sec - last_time.tv_sec;
772
773		elapsed_usec = current_time.tv_usec - last_time.tv_usec;
774
775		if (elapsed_usec < 0) {
776			elapsed_usec-=1000000;
777			elapsed_sec++;
778		}
779
780		elapsed = (double) elapsed_sec +
781			(double) elapsed_usec / 1000000.0;
782		if (elapsed <= 0.0) {
783			elapsed = 0.0001; /* XXX */
784		}
785
786		amount = percent_done - last_percent;
787		if (amount <= 0) { /* we don't do negatives (yet?) */
788			amount = 0;
789			wait_for_more_data = 1;
790		} else {
791			wait_for_more_data = 0;
792		}
793		rate = amount / elapsed;
794
795
796		if (rate > 0.0) {
797			simple_eta = (int) ((100.0 - (double) last_percent ) / rate);
798		} else {
799			simple_eta = -1;
800		}
801		if (simple_eta <=0) {
802			simple_eta = last_eta;
803		} else {
804			last_eta = simple_eta;
805		}
806
807		get_time_string(eta_buffer, simple_eta);
808
809		snprintf(buffer,1024,"\r%3d%% |%s| ETA: %s %c",
810			 percent_done,bar_buffer,eta_buffer,tbits[tbit_value]);
811
812		write(fileno(stdout),buffer,strlen(buffer));
813		fflush(stdout);
814
815		/* resolution wasn't high enough... wait until we get another
816		   timestamp and perhaps more "work" done. */
817
818		if (!wait_for_more_data) {
819			last_time = current_time;
820			last_percent = percent_done;
821		}
822
823		if (++tbit_value>3)
824			tbit_value = 0;
825
826		sleep(2);
827
828		if (gettimeofday(&current_time,NULL)) {
829			fprintf(stderr,"%s: gettimeofday failed!?!?\n",
830				__progname);
831			exit(errno);
832		}
833
834		do_ioctl( fd, option, &percent_done, "");
835
836
837	}
838	printf("\n");
839}
840/* 40 '*''s per line, then 40 ' ''s line. */
841/* If you've got a screen wider than 160 characters, "tough" */
842
843#define STAR_MIDPOINT 4*40
844const char stars[] = "****************************************"
845                     "****************************************"
846                     "****************************************"
847                     "****************************************"
848                     "                                        "
849                     "                                        "
850                     "                                        "
851                     "                                        "
852                     "                                        ";
853
854static void
855get_bar(string,percent,max_strlen)
856	char *string;
857	double percent;
858	int max_strlen;
859{
860	int offset;
861
862	if (max_strlen > STAR_MIDPOINT) {
863		max_strlen = STAR_MIDPOINT;
864	}
865	offset = STAR_MIDPOINT -
866		(int)((percent * max_strlen)/ 100);
867	if (offset < 0)
868		offset = 0;
869	snprintf(string,max_strlen,"%s",&stars[offset]);
870}
871
872static void
873get_time_string(string,simple_time)
874	char *string;
875	int simple_time;
876{
877	int minutes, seconds, hours;
878	char hours_buffer[5];
879	char minutes_buffer[5];
880	char seconds_buffer[5];
881
882	if (simple_time >= 0) {
883
884		minutes = (int) simple_time / 60;
885		seconds = ((int)simple_time - 60*minutes);
886		hours = minutes / 60;
887		minutes = minutes - 60*hours;
888
889		if (hours > 0) {
890			snprintf(hours_buffer,5,"%02d:",hours);
891		} else {
892			snprintf(hours_buffer,5,"   ");
893		}
894
895		snprintf(minutes_buffer,5,"%02d:",minutes);
896		snprintf(seconds_buffer,5,"%02d",seconds);
897		snprintf(string,1024,"%s%s%s",
898			 hours_buffer, minutes_buffer, seconds_buffer);
899	} else {
900		snprintf(string,1024,"   --:--");
901	}
902
903}
904
905static void
906usage()
907{
908	fprintf(stderr, "usage: %s [-v] -a component dev\n", __progname);
909	fprintf(stderr, "       %s [-v] -B dev\n", __progname);
910	fprintf(stderr, "       %s [-v] -c config_file dev\n", __progname);
911	fprintf(stderr, "       %s [-v] -C config_file dev\n", __progname);
912	fprintf(stderr, "       %s [-v] -f component dev\n", __progname);
913	fprintf(stderr, "       %s [-v] -F component dev\n", __progname);
914	fprintf(stderr, "       %s [-v] -g component dev\n", __progname);
915	fprintf(stderr, "       %s [-v] -i dev\n", __progname);
916	fprintf(stderr, "       %s [-v] -I serial_number dev\n", __progname);
917	fprintf(stderr, "       %s [-v] -r component dev\n", __progname);
918	fprintf(stderr, "       %s [-v] -R component dev\n", __progname);
919	fprintf(stderr, "       %s [-v] -s dev\n", __progname);
920	fprintf(stderr, "       %s [-v] -S dev\n", __progname);
921	fprintf(stderr, "       %s [-v] -u dev\n", __progname);
922#if 0
923	fprintf(stderr, "usage: %s %s\n", __progname,
924		"-a | -f | -F | -g | -r | -R component dev");
925	fprintf(stderr, "       %s -B | -i | -s | -S -u dev\n", __progname);
926	fprintf(stderr, "       %s -c | -C config_file dev\n", __progname);
927	fprintf(stderr, "       %s -I serial_number dev\n", __progname);
928#endif
929	exit(1);
930	/* NOTREACHED */
931}
932