xref: /plan9/sys/src/cmd/cwfs/config.c (revision 01a344a29f2ff35133953eaef092a50fc8c3163b)
1 #include "all.h"
2 #include "io.h"
3 
4 static void	dowormcopy(void);
5 static int	dodevcopy(void);
6 
7 struct {
8 	char*	icharp;
9 	char*	charp;
10 	int	error;
11 	int	newconf;	/* clear before start */
12 	int	modconf;	/* write back when done */
13 	int	nextiter;
14 	int	lastiter;
15 	int	diriter;
16 	Device*	lastcw;
17 	Device*	devlist;
18 } f;
19 
20 static Device* confdev;
21 static int copyworm = 0, copydev = 0;
22 static char *src, *dest;
23 
24 static int resetparams;
25 
26 Fspar fspar[] = {
27 	{ "blocksize",	RBUFSIZE, },
28 	{ "daddrbits",	sizeof(Off)*8, },
29 	{ "indirblks",	NIBLOCK, },
30 	{ "dirblks",	NDBLOCK, },
31 	{ "namelen",	NAMELEN, },
32 	{ nil, 0, },
33 };
34 
35 int
devcmpr(Device * d1,Device * d2)36 devcmpr(Device *d1, Device *d2)
37 {
38 	while (d1 != d2) {
39 		if(d1 == nil || d2 == nil || d1->type != d2->type)
40 			return 1;
41 
42 		switch(d1->type) {
43 		default:
44 			print("can't compare dev: %Z\n", d1);
45 			panic("devcmp");
46 			return 1;
47 
48 		case Devmcat:
49 		case Devmlev:
50 		case Devmirr:
51 			d1 = d1->cat.first;
52 			d2 = d2->cat.first;
53 			while(d1 && d2) {
54 				if(devcmpr(d1, d2))
55 					return 1;
56 				d1 = d1->link;
57 				d2 = d2->link;
58 			}
59 			break;
60 
61 		case Devnone:
62 			return 0;
63 
64 		case Devro:
65 			d1 = d1->ro.parent;
66 			d2 = d2->ro.parent;
67 			break;
68 
69 		case Devjuke:
70 		case Devcw:
71 			if(devcmpr(d1->cw.c, d2->cw.c))
72 				return 1;
73 			d1 = d1->cw.w;
74 			d2 = d2->cw.w;
75 			break;
76 
77 		case Devfworm:
78 			d1 = d1->fw.fw;
79 			d2 = d2->fw.fw;
80 			break;
81 
82 		case Devwren:
83 		case Devworm:
84 		case Devlworm:
85 			if(d1->wren.ctrl == d2->wren.ctrl)
86 			if(d1->wren.targ == d2->wren.targ)
87 			if(d1->wren.lun == d2->wren.lun)
88 				return 0;
89 			return 1;
90 
91 		case Devpart:
92 			if(d1->part.base == d2->part.base)
93 			if(d1->part.size == d2->part.size) {
94 				d1 = d1->part.d;
95 				d2 = d2->part.d;
96 				break;
97 			}
98 			return 1;
99 		}
100 	}
101 	return 0;
102 }
103 
104 void
cdiag(char * s,int c1)105 cdiag(char *s, int c1)
106 {
107 
108 	f.charp--;
109 	if(f.error == 0) {
110 		print("config diag: %s -- <%c>\n", s, c1);
111 		f.error = 1;
112 	}
113 }
114 
115 int
cnumb(void)116 cnumb(void)
117 {
118 	int c, n;
119 
120 	c = *f.charp++;
121 	if(c == '<') {
122 		n = f.nextiter;
123 		if(n >= 0) {
124 			f.nextiter = n+f.diriter;
125 			if(n == f.lastiter) {
126 				f.nextiter = -1;
127 				f.lastiter = -1;
128 			}
129 			do {
130 				c = *f.charp++;
131 			} while (c != '>');
132 			return n;
133 		}
134 		n = cnumb();
135 		if(*f.charp++ != '-') {
136 			cdiag("- expected", f.charp[-1]);
137 			return 0;
138 		}
139 		c = cnumb();
140 		if(*f.charp++ != '>') {
141 			cdiag("> expected", f.charp[-1]);
142 			return 0;
143 		}
144 		f.lastiter = c;
145 		f.diriter = 1;
146 		if(n > c)
147 			f.diriter = -1;
148 		f.nextiter = n+f.diriter;
149 		return n;
150 	}
151 	if(!isascii(c) || !isdigit(c)) {
152 		cdiag("number expected", c);
153 		return 0;
154 	}
155 	n = 0;
156 	while(isascii(c) && isdigit(c)) {
157 		n = n*10 + (c-'0');
158 		c = *f.charp++;
159 	}
160 	f.charp--;
161 	return n;
162 }
163 
164 Device*
config1(int c)165 config1(int c)
166 {
167 	Device *d, *t;
168 	int m;
169 
170 	d = malloc(sizeof(Device));
171 	do {
172 		t = config();
173 		if(d->cat.first == 0)
174 			d->cat.first = t;
175 		else
176 			d->cat.last->link = t;
177 		d->cat.last = t;
178 		if(f.error)
179 			return devnone;
180 		m = *f.charp;
181 		if(c == '(' && m == ')')
182 			d->type = Devmcat;
183 		else if(c == '[' && m == ']')
184 			d->type = Devmlev;
185 		else if(c == '{' && m == '}')
186 			d->type = Devmirr;
187 	} while (d->type == 0);
188 	f.charp++;
189 	if(d->cat.first == d->cat.last)
190 		d = d->cat.first;
191 	return d;
192 }
193 
194 static void
map(Device * d)195 map(Device *d)
196 {
197 	Map *map;
198 
199 	if (d->type != Devwren || d->wren.mapped)
200 		return;
201 	for (map = devmap; map != nil; map = map->next)
202 		if (devcmpr(d, map->fdev) == 0)
203 			break;
204 	if (map == nil)
205 		return;
206 	if (access(map->to, AEXIST) >= 0)
207 { print("map: mapped wren %Z to existing file %s\n", d, map->to); // DEBUG
208 		d->wren.file = map->to;		/* wren -> file mapping */
209 }
210 	else if (map->tdev != nil)
211 { print("map: mapped wren %Z to dev %Z\n", d, map->tdev); // DEBUG
212 		*d = *map->tdev;		/* wren -> wren mapping */
213 }
214 	else
215 		print("bad mapping %Z to %s; no such file or device",
216 			d, map->to);
217 	d->wren.mapped = 1;
218 }
219 
220 Device*
config(void)221 config(void)
222 {
223 	int c, m;
224 	Device *d;
225 	char *icp;
226 
227 	if(f.error)
228 		return devnone;
229 	d = malloc(sizeof(Device));
230 
231 	c = *f.charp++;
232 	switch(c) {
233 	default:
234 		cdiag("unknown type", c);
235 		return devnone;
236 
237 	case '(':	/* (d+) one or multiple cat */
238 	case '[':	/* [d+] one or multiple interleave */
239 	case '{':	/* {d+} a mirrored device and optional mirrors */
240 		return config1(c);
241 
242 	case 'f':	/* fd fake worm */
243 		d->type = Devfworm;
244 		d->fw.fw = config();
245 		break;
246 
247 	case 'n':
248 		d->type = Devnone;
249 		break;
250 
251 	case 'w':	/* w[#.]#[.#] wren	[ctrl] unit [lun] */
252 	case 'r':	/* r# worm side */
253 	case 'l':	/* l# labelled-worm side */
254 		icp = f.charp;
255 		d->type = Devwren;
256 		d->wren.ctrl = 0;
257 		d->wren.targ = cnumb();
258 		d->wren.lun = 0;
259 		m = *f.charp;
260 		if(m == '.') {
261 			f.charp++;
262 			d->wren.lun = cnumb();
263 			m = *f.charp;
264 			if(m == '.') {
265 				f.charp++;
266 				d->wren.ctrl = d->wren.targ;
267 				d->wren.targ = d->wren.lun;
268 				d->wren.lun = cnumb();
269 			}
270 		}
271 		if(f.nextiter >= 0)
272 			f.charp = icp-1;
273 		if(c == 'r')		/* worms are virtual and not uniqued */
274 			d->type = Devworm;
275 		else if(c == 'l')
276 			d->type = Devlworm;
277 		else
278 			map(d);		/* subject wrens to optional mapping */
279 		break;
280 
281 	case 'o':	/* o ro part of last cw */
282 		if(f.lastcw == 0) {
283 			cdiag("no cw to match", c);
284 			return devnone;
285 		}
286 		return f.lastcw->cw.ro;
287 
288 	case 'j':	/* DD jukebox */
289 		d->type = Devjuke;
290 		d->j.j = config();
291 		d->j.m = config();
292 		break;
293 
294 	case 'c':	/* cache/worm */
295 		d->type = Devcw;
296 		d->cw.c = config();
297 		d->cw.w = config();
298 		d->cw.ro = malloc(sizeof(Device));
299 		d->cw.ro->type = Devro;
300 		d->cw.ro->ro.parent = d;
301 		f.lastcw = d;
302 		break;
303 
304 	case 'p':	/* pd#.# partition base% size% */
305 		d->type = Devpart;
306 		d->part.d = config();
307 		d->part.base = cnumb();
308 		c = *f.charp++;
309 		if(c != '.')
310 			cdiag("dot expected", c);
311 		d->part.size = cnumb();
312 		break;
313 
314 	case 'x':	/* xD swab a device's metadata */
315 		d->type = Devswab;
316 		d->swab.d = config();
317 		break;
318 	}
319 	d->dlink = f.devlist;
320 	f.devlist = d;
321 	return d;
322 }
323 
324 Device*
iconfig(char * s)325 iconfig(char *s)
326 {
327 	Device *d;
328 
329 	f.nextiter = -1;
330 	f.lastiter = -1;
331 	f.error = 0;
332 	f.icharp = s;
333 	f.charp = f.icharp;
334 	d = config();
335 	if(*f.charp) {
336 		cdiag("junk on end", *f.charp);
337 		f.error = 1;
338 	}
339 	return d;
340 }
341 
342 int
testconfig(char * s)343 testconfig(char *s)
344 {
345 	iconfig(s);
346 	return f.error;
347 }
348 
349 /*
350  * if b is a prefix of a, return 0.
351  */
352 int
astrcmp(char * a,char * b)353 astrcmp(char *a, char *b)
354 {
355 	int n, c;
356 
357 	n = strlen(b);
358 	if(memcmp(a, b, n) != 0)
359 		return 1;
360 	c = a[n];
361 	if(c == '\0')
362 		return 0;
363 	if(a[n+1])
364 		return 1;
365 	if(isascii(c) && isdigit(c))
366 		return 0;
367 	return 1;
368 }
369 
370 static Fspar *
getpar(char * name)371 getpar(char *name)
372 {
373 	Fspar *fsp;
374 
375 	for (fsp = fspar; fsp->name != nil; fsp++)
376 		if (strcmp(name, fsp->name) == 0)
377 			return fsp;
378 	return nil;
379 }
380 
381 /*
382  * continue to parse obsolete keywords so that old configurations can
383  * still work.
384  */
385 void
mergeconf(Iobuf * p)386 mergeconf(Iobuf *p)
387 {
388 	char word[Maxword+1];
389 	char *cp;
390 	Filsys *fs;
391 	Fspar *fsp;
392 
393 	for (cp = p->iobuf; *cp != '\0'; cp++) {
394 		cp = getwrd(word, cp);
395 		if(word[0] == '\0')
396 			break;
397 		else if (word[0] == '#')
398 			while (*cp != '\n' && *cp != '\0')
399 				cp++;
400 		else if(strcmp(word, "service") == 0) {
401 			cp = getwrd(word, cp);
402 			if(service[0] == 0)
403 				strncpy(service, word, sizeof service);
404 		} else if(strcmp(word, "ipauth") == 0)	/* obsolete */
405 			cp = getwrd(word, cp);
406 		else if(astrcmp(word, "ip") == 0)	/* obsolete */
407 			cp = getwrd(word, cp);
408 		else if(astrcmp(word, "ipgw") == 0)	/* obsolete */
409 			cp = getwrd(word, cp);
410 		else if(astrcmp(word, "ipsntp") == 0)	/* obsolete */
411 			cp = getwrd(word, cp);
412 		else if(astrcmp(word, "ipmask") == 0)	/* obsolete */
413 			cp = getwrd(word, cp);
414 		else if(strcmp(word, "filsys") == 0) {
415 			cp = getwrd(word, cp);
416 			for(fs = filsys; fs < filsys + nelem(filsys) - 1 &&
417 			    fs->name; fs++)
418 				if(strcmp(fs->name, word) == 0)
419 					break;
420 			if (fs >= filsys + nelem(filsys) - 1)
421 				panic("out of filsys structures");
422 			if (fs->name && strcmp(fs->name, word) == 0 &&
423 			    fs->flags & FEDIT)
424 				cp = getwrd(word, cp);	/* swallow conf */
425 			else {
426 				fs->name = strdup(word);
427 				cp = getwrd(word, cp);
428 				if (word[0] == '\0')
429 					fs->conf = nil;
430 				else
431 					fs->conf = strdup(word);
432 			}
433 		} else if ((fsp = getpar(word)) != nil) {
434 			cp = getwrd(word, cp);
435 			if (!isascii(word[0]) || !isdigit(word[0]))
436 				print("bad %s value: %s", fsp->name, word);
437 			else
438 				fsp->declared = atol(word);
439 		} else {
440 			putbuf(p);
441 			panic("unknown keyword in config block: %s", word);
442 		}
443 
444 		if(*cp != '\n') {
445 			putbuf(p);
446 			panic("syntax error in config block at `%s'", word);
447 		}
448 	}
449 }
450 
451 void
cmd_printconf(int,char * [])452 cmd_printconf(int, char *[])
453 {
454 	char *p, *s;
455 	Iobuf *iob;
456 
457 	iob = getbuf(confdev, 0, Brd);
458 	if(iob == nil)
459 		return;
460 	if(checktag(iob, Tconfig, 0)){
461 		putbuf(iob);
462 		return;
463 	}
464 
465 	print("config %s\n", nvrgetconfig());
466 	for(s = p = iob->iobuf; *p != 0 && p < iob->iobuf+BUFSIZE; ){
467 		if(*p++ != '\n')
468 			continue;
469 		if (strncmp(s, "ip", 2) != 0)	/* don't print obsolete cmds */
470 			print("%.*s", (int)(p-s), s);
471 		s = p;
472 	}
473 	if(p != s)
474 		print("%.*s", (int)(p-s), s);
475 	print("end\n");
476 
477 	putbuf(iob);
478 }
479 
480 void
sysinit(void)481 sysinit(void)
482 {
483 	int error;
484 	char *cp, *ep;
485 	Device *d;
486 	Filsys *fs;
487 	Fspar *fsp;
488 	Iobuf *p;
489 
490 	cons.chan = fs_chaninit(Devcon, 1, 0);
491 
492 start:
493 	/*
494 	 * part 1 -- read the config file
495 	 */
496 	devnone = iconfig("n");
497 
498 	cp = nvrgetconfig();
499 	print("config %s\n", cp);
500 
501 	confdev = d = iconfig(cp);
502 	devinit(d);
503 	if(f.newconf) {
504 		p = getbuf(d, 0, Bmod);
505 		memset(p->iobuf, 0, RBUFSIZE);
506 		settag(p, Tconfig, 0);
507 	} else
508 		p = getbuf(d, 0, Brd|Bmod);
509 	if(!p || checktag(p, Tconfig, 0))
510 		panic("config io");
511 
512 	mergeconf(p);
513 
514 	if (resetparams) {
515 		for (fsp = fspar; fsp->name != nil; fsp++)
516 			fsp->declared = 0;
517 		resetparams = 0;
518 	}
519 
520 	for (fsp = fspar; fsp->name != nil; fsp++) {
521 		/* supply defaults from this cwfs instance */
522 		if (fsp->declared == 0) {
523 			fsp->declared = fsp->actual;
524 			f.modconf = 1;
525 		}
526 		/* warn if declared value is not our compiled-in value */
527 		if (fsp->declared != fsp->actual)
528 			print("warning: config %s %ld != compiled-in %ld\n",
529 				fsp->name, fsp->declared, fsp->actual);
530 	}
531 
532 	if(f.modconf) {
533 		memset(p->iobuf, 0, BUFSIZE);
534 		p->flags |= Bmod|Bimm;
535 		cp = p->iobuf;
536 		ep = p->iobuf + RBUFSIZE - 1;
537 		if(service[0])
538 			cp = seprint(cp, ep, "service %s\n", service);
539 		for(fs=filsys; fs->name; fs++)
540 			if(fs->conf && fs->conf[0] != '\0')
541 				cp = seprint(cp, ep, "filsys %s %s\n", fs->name,
542 					fs->conf);
543 
544 		for (fsp = fspar; fsp->name != nil; fsp++)
545 			cp = seprint(cp, ep, "%s %ld\n",
546 				fsp->name, fsp->declared);
547 
548 		putbuf(p);
549 		f.modconf = f.newconf = 0;
550 		print("config block written\n");
551 		goto start;
552 	}
553 	putbuf(p);
554 
555 	print("service    %s\n", service);
556 
557 loop:
558 	/*
559 	 * part 2 -- squeeze out the deleted filesystems
560 	 */
561 	for(fs=filsys; fs->name; fs++)
562 		if(fs->conf == nil || fs->conf[0] == '\0') {
563 			for(; fs->name; fs++)
564 				*fs = *(fs+1);
565 			goto loop;
566 		}
567 	if(filsys[0].name == nil)
568 		panic("no filsys");
569 
570 	/*
571 	 * part 3 -- compile the device expression
572 	 */
573 	error = 0;
574 	for(fs=filsys; fs->name; fs++) {
575 		print("filsys %s %s\n", fs->name, fs->conf);
576 		fs->dev = iconfig(fs->conf);
577 		if(f.error) {
578 			error = 1;
579 			continue;
580 		}
581 	}
582 	if(error)
583 		panic("fs config");
584 
585 	/*
586 	 * part 4 -- initialize the devices
587 	 */
588 	for(fs=filsys; fs->name; fs++) {
589 		delay(3000);
590 		print("sysinit: %s\n", fs->name);
591 		if(fs->flags & FREAM)
592 			devream(fs->dev, 1);
593 		if(fs->flags & FRECOVER)
594 			devrecover(fs->dev);
595 		devinit(fs->dev);
596 	}
597 
598 	/*
599 	 * part 5 -- optionally copy devices or worms
600 	 */
601 	if (copyworm) {
602 		dowormcopy();		/* can return if user quits early */
603 		panic("copyworm bailed out!");
604 	}
605 	if (copydev)
606 		if (dodevcopy() < 0)
607 			panic("copydev failed!");
608 		else
609 			panic("copydev done.");
610 }
611 
612 /* an unfinished idea.  a non-blocking rawchar() would help. */
613 static int
userabort(char * msg)614 userabort(char *msg)
615 {
616 	USED(msg);
617 	return 0;
618 }
619 
620 static int
blockok(Device * d,Off a)621 blockok(Device *d, Off a)
622 {
623 	Iobuf *p = getbuf(d, a, Brd);
624 
625 	if (p == 0) {
626 		print("i/o error reading %Z block %lld\n", d, (Wideoff)a);
627 		return 0;
628 	}
629 	putbuf(p);
630 	return 1;
631 }
632 
633 /*
634  * special case for fake worms only:
635  * we need to size the inner cw's worm device.
636  * in particular, we want to avoid copying the fake-worm bitmap
637  * at the end of the device.
638  *
639  * N.B.: for real worms (e.g. cw jukes), we need to compute devsize(cw(juke)),
640  * *NOT* devsize(juke).
641  */
642 static Device *
wormof(Device * dev)643 wormof(Device *dev)
644 {
645 	Device *worm = dev, *cw;
646 
647 	if (dev->type == Devfworm) {
648 		cw = dev->fw.fw;
649 		if (cw != nil && cw->type == Devcw)
650 			worm = cw->cw.w;
651 	}
652 	// print("wormof(%Z)=%Z\n", dev, worm);
653 	return worm;
654 }
655 
656 /*
657  * return the number of the highest-numbered block actually written, plus 1.
658  * 0 indicates an error.
659  */
660 static Devsize
writtensize(Device * worm)661 writtensize(Device *worm)
662 {
663 	Devsize lim = devsize(worm);
664 	Iobuf *p;
665 
666 	print("devsize(%Z) = %lld\n", worm, (Wideoff)lim);
667 	if (!blockok(worm, 0) || !blockok(worm, lim-1))
668 		return 0;
669 	delay(5*1000);
670 	if (userabort("sanity checks"))
671 		return 0;
672 
673 	/* find worm's last valid block in case "worm" is an (f)worm */
674 	while (lim > 0) {
675 		if (userabort("sizing")) {
676 			lim = 0;		/* you lose */
677 			break;
678 		}
679 		--lim;
680 		p = getbuf(worm, lim, Brd);
681 		if (p != 0) {			/* actually read one okay? */
682 			putbuf(p);
683 			break;
684 		}
685 	}
686 	print("limit(%Z) = %lld\n", worm, (Wideoff)lim);
687 	if (lim <= 0)
688 		return 0;
689 	return lim + 1;
690 }
691 
692 /* copy worm fs from "main"'s inner worm to "output" */
693 static void
dowormcopy(void)694 dowormcopy(void)
695 {
696 	Filsys *f1, *f2;
697 	Device *fdev, *from, *to = nil;
698 	Iobuf *p;
699 	Off a;
700 	Devsize lim;
701 
702 	/*
703 	 * convert file system names into Filsyss and Devices.
704 	 */
705 
706 	f1 = fsstr("main");
707 	if(f1 == nil)
708 		panic("main file system missing");
709 	fdev = f1->dev;
710 	from = wormof(fdev);			/* fake worm special */
711 	if (from->type != Devfworm && from->type != Devcw) {
712 		print("main file system is not a worm; copyworm may not do what you want!\n");
713 		print("waiting for 20 seconds...\n");
714 		delay(20000);
715 	}
716 
717 	f2 = fsstr("output");
718 	if(f2 == nil) {
719 		print("no output file system - check only\n\n");
720 		print("reading worm from %Z (worm %Z)\n", fdev, from);
721 	} else {
722 		to = f2->dev;
723 		print("\ncopying worm from %Z (worm %Z) to %Z, starting in 8 seconds\n",
724 			fdev, from, to);
725 		delay(8000);
726 	}
727 	if (userabort("preparing to copy"))
728 		return;
729 
730 	/*
731 	 * initialise devices, size them, more sanity checking.
732 	 */
733 
734 	devinit(from);
735 	if (0 && fdev != from) {
736 		devinit(fdev);
737 		print("debugging, sizing %Z first\n", fdev);
738 		writtensize(fdev);
739 	}
740 	lim = writtensize(from);
741 	if(lim == 0)
742 		panic("no blocks to copy on %Z", from);
743 	if (to) {
744 		print("reaming %Z in 8 seconds\n", to);
745 		delay(8000);
746 		if (userabort("preparing to ream & copy"))
747 			return;
748 		devream(to, 0);
749 		devinit(to);
750 		print("copying worm: %lld blocks from %Z to %Z\n",
751 			(Wideoff)lim, from, to);
752 	}
753 	/* can't read to's blocks in case to is a real WORM device */
754 
755 	/*
756 	 * Copy written fs blocks, a block at a time (or just read
757 	 * if no "output" fs).
758 	 */
759 
760 	for (a = 0; a < lim; a++) {
761 		if (userabort("copy"))
762 			break;
763 		p = getbuf(from, a, Brd);
764 		/*
765 		 * if from is a real WORM device, we'll get errors trying to
766 		 * read unwritten blocks, but the unwritten blocks need not
767 		 * be contiguous.
768 		 */
769 		if (p == 0) {
770 			print("%lld not written yet; can't read\n", (Wideoff)a);
771 			continue;
772 		}
773 		if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
774 			print("out block %lld: write error; bailing",
775 				(Wideoff)a);
776 			break;
777 		}
778 		putbuf(p);
779 		if(a % 20000 == 0)
780 			print("block %lld %T\n", (Wideoff)a, time(nil));
781 	}
782 
783 	/*
784 	 * wrap up: sync target, loop
785 	 */
786 	print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
787 	sync("wormcopy");
788 	delay(2000);
789 	print("looping; reset the machine at any time.\n");
790 	for (; ; )
791 		continue;		/* await reset */
792 }
793 
794 /* copy device from src to dest */
795 static int
dodevcopy(void)796 dodevcopy(void)
797 {
798 	Device *from, *to;
799 	Iobuf *p;
800 	Off a;
801 	Devsize lim, tosize;
802 
803 	/*
804 	 * convert config strings into Devices.
805 	 */
806 	from = iconfig(src);
807 	if(f.error || from == nil) {
808 		print("bad src device %s\n", src);
809 		return -1;
810 	}
811 	to = iconfig(dest);
812 	if(f.error || to == nil) {
813 		print("bad dest device %s\n", dest);
814 		return -1;
815 	}
816 
817 	/*
818 	 * initialise devices, size them, more sanity checking.
819 	 */
820 
821 	devinit(from);
822 	lim = devsize(from);
823 	if(lim == 0)
824 		panic("no blocks to copy on %Z", from);
825 	devinit(to);
826 	tosize = devsize(to);
827 	if(tosize == 0)
828 		panic("no blocks to copy on %Z", to);
829 
830 	/* use smaller of the device sizes */
831 	if (tosize < lim)
832 		lim = tosize;
833 
834 	print("copy %Z to %Z in 8 seconds\n", from, to);
835 	delay(8000);
836 	if (userabort("preparing to copy"))
837 		return -1;
838 	print("copying dev: %lld blocks from %Z to %Z\n", (Wideoff)lim,
839 		from, to);
840 
841 	/*
842 	 * Copy all blocks, a block at a time.
843 	 */
844 
845 	for (a = 0; a < lim; a++) {
846 		if (userabort("copy"))
847 			break;
848 		p = getbuf(from, a, Brd);
849 		/*
850 		 * if from is a real WORM device, we'll get errors trying to
851 		 * read unwritten blocks, but the unwritten blocks need not
852 		 * be contiguous.
853 		 */
854 		if (p == 0) {
855 			print("%lld not written yet; can't read\n", (Wideoff)a);
856 			continue;
857 		}
858 		if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
859 			print("out block %lld: write error; bailing",
860 				(Wideoff)a);
861 			break;
862 		}
863 		putbuf(p);
864 		if(a % 20000 == 0)
865 			print("block %lld %T\n", (Wideoff)a, time(nil));
866 	}
867 
868 	/*
869 	 * wrap up: sync target
870 	 */
871 	print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
872 	sync("devcopy");
873 	return 0;
874 }
875 
876 static void
setconfig(char * dev)877 setconfig(char *dev)
878 {
879 	if (dev != nil && !testconfig(dev))
880 		nvrsetconfig(dev);	/* if it fails, it will complain */
881 }
882 
883 void
arginit(void)884 arginit(void)
885 {
886 	int verb;
887 	char *line;
888 	char word[Maxword+1], *cp;
889 	Filsys *fs;
890 
891 	if(nvrcheck() == 0) {
892 		setconfig(conf.confdev);
893 		if (!conf.configfirst)
894 			return;
895 	}
896 
897 	/* nvr was bad or invoker requested configuration step */
898 	setconfig(conf.confdev);
899 	for (;;) {
900 		print("config: ");
901 		if ((line = Brdline(&bin, '\n')) == nil)
902 			return;
903 		line[Blinelen(&bin)-1] = '\0';
904 
905 		cp = getwrd(word, line);
906 		if (word[0] == '\0' || word[0] == '#')
907 			continue;
908 		if(strcmp(word, "end") == 0)
909 			return;
910 		if(strcmp(word, "halt") == 0)
911 			exit();
912 		if(strcmp(word, "queryjuke") == 0) {
913 			getwrd(word, cp);
914 			if(testconfig(word) == 0)
915 				querychanger(iconfig(word));
916 			continue;
917 		}
918 
919 		if(strcmp(word, "allow") == 0) {
920 			wstatallow = 1;
921 			writeallow = 1;
922 			continue;
923 		}
924 		if(strcmp(word, "copyworm") == 0) {
925 			copyworm = 1;
926 			continue;
927 		}
928 		if(strcmp(word, "copydev") == 0) {
929 			cp = getwrd(word, cp);
930 			if(testconfig(word))
931 				continue;
932 			src = strdup(word);
933 			getwrd(word, cp);
934 			if(testconfig(word))
935 				continue;
936 			dest = strdup(word);
937 			copydev = 1;
938 			continue;
939 		}
940 		if(strcmp(word, "noauth") == 0) {
941 			noauth = !noauth;
942 			continue;
943 		}
944 		if(strcmp(word, "noattach") == 0) {
945 			noattach = !noattach;
946 			continue;
947 		}
948 		if(strcmp(word, "readonly") == 0) {
949 			readonly = 1;
950 			continue;
951 		}
952 
953 		if(strcmp(word, "ream") == 0) {
954 			verb = FREAM;
955 			goto gfsname;
956 		}
957 		if(strcmp(word, "recover") == 0) {
958 			verb = FRECOVER;
959 			goto gfsname;
960 		}
961 		if(strcmp(word, "filsys") == 0) {
962 			verb = FEDIT;
963 			goto gfsname;
964 		}
965 
966 		if(strcmp(word, "nvram") == 0) {
967 			getwrd(word, cp);
968 			if(testconfig(word))
969 				continue;
970 			/* if it fails, it will complain */
971 			nvrsetconfig(word);
972 			continue;
973 		}
974 		if(strcmp(word, "config") == 0) {
975 			getwrd(word, cp);
976 			if(!testconfig(word) && nvrsetconfig(word) == 0)
977 				f.newconf = 1;
978 			continue;
979 		}
980 		if(strcmp(word, "service") == 0) {
981 			getwrd(word, cp);
982 			strncpy(service, word, sizeof service);
983 			f.modconf = 1;
984 			continue;
985 		}
986 		if (strcmp(word, "resetparams") == 0) {
987 			resetparams++;
988 			continue;
989 		}
990 
991 		/*
992 		 * continue to parse obsolete keywords so that old
993 		 * configurations can still work.
994 		 */
995 		if (strcmp(word, "ipauth") != 0 &&
996 		    astrcmp(word, "ip") != 0 &&
997 		    astrcmp(word, "ipgw") != 0 &&
998 		    astrcmp(word, "ipmask") != 0 &&
999 		    astrcmp(word, "ipsntp") != 0) {
1000 			print("unknown config command\n");
1001 			print("\ttype end to get out\n");
1002 			continue;
1003 		}
1004 
1005 		getwrd(word, cp);
1006 		f.modconf = 1;
1007 		continue;
1008 
1009 	gfsname:
1010 		cp = getwrd(word, cp);
1011 		for(fs=filsys; fs->name; fs++)
1012 			if(strcmp(word, fs->name) == 0)
1013 				break;
1014 		if (fs->name == nil) {
1015 			memset(fs, 0, sizeof *fs);
1016 			fs->name = strdup(word);
1017 		}
1018 		switch(verb) {
1019 		case FREAM:
1020 			if(strcmp(fs->name, "main") == 0)
1021 				wstatallow = 1;	/* only set, never reset */
1022 			/* fallthrough */
1023 		case FRECOVER:
1024 			fs->flags |= verb;
1025 			break;
1026 		case FEDIT:
1027 			f.modconf = 1;
1028 			getwrd(word, cp);
1029 			fs->flags |= verb;
1030 			if(word[0] == 0)
1031 				fs->conf = nil;
1032 			else if(!testconfig(word))
1033 				fs->conf = strdup(word);
1034 			break;
1035 		}
1036 	}
1037 }
1038