xref: /plan9/sys/src/cmd/cwfs/main.c (revision 225077b0bf393489f69f6689df234a9b945497b7)
1 /* cached-worm file server */
2 #include "all.h"
3 #include "io.h"
4 #include "9p1.h"
5 
6 extern int oldcachefmt;
7 
8 Map *devmap;
9 
10 Biobuf bin;
11 
12 void
machinit(void)13 machinit(void)
14 {
15 	active.exiting = 0;
16 }
17 
18 /*
19  * Put a string on the console.
20  */
21 void
puts(char * s,int n)22 puts(char *s, int n)
23 {
24 	print("%.*s", n, s);
25 }
26 
27 void
prflush(void)28 prflush(void)
29 {
30 }
31 
32 /*
33  * Print a string on the console.
34  */
35 void
putstrn(char * str,int n)36 putstrn(char *str, int n)
37 {
38 	puts(str, n);
39 }
40 
41 /*
42  * get a character from the console
43  */
44 int
getc(void)45 getc(void)
46 {
47 	return Bgetrune(&bin);
48 }
49 
50 void
panic(char * fmt,...)51 panic(char *fmt, ...)
52 {
53 	int n;
54 	va_list arg;
55 	char buf[PRINTSIZE];
56 
57 	va_start(arg, fmt);
58 	n = vseprint(buf, buf + sizeof buf, fmt, arg) - buf;
59 	va_end(arg);
60 	buf[n] = '\0';
61 	print("panic: %s\n", buf);
62 	exit();
63 }
64 
65 int
okay(char * quest)66 okay(char *quest)
67 {
68 	char *ln;
69 
70 	print("okay to %s? ", quest);
71 	if ((ln = Brdline(&bin, '\n')) == nil)
72 		return 0;
73 	ln[Blinelen(&bin)-1] = '\0';
74 	if (isascii(*ln) && isupper(*ln))
75 		*ln = tolower(*ln);
76 	return *ln == 'y';
77 }
78 
79 static void
mapinit(char * mapfile)80 mapinit(char *mapfile)
81 {
82 	int nf;
83 	char *ln;
84 	char *fields[2];
85 	Biobuf *bp;
86 	Map *map;
87 
88 	if (mapfile == nil)
89 		return;
90 	bp = Bopen(mapfile, OREAD);
91 	if (bp == nil)
92 		sysfatal("can't read %s", mapfile);
93 	devmap = nil;
94 	while ((ln = Brdline(bp, '\n')) != nil) {
95 		ln[Blinelen(bp)-1] = '\0';
96 		if (*ln == '\0' || *ln == '#')
97 			continue;
98 		nf = tokenize(ln, fields, nelem(fields));
99 		if (nf != 2)
100 			continue;
101 		if(testconfig(fields[0]) != 0) {
102 			print("bad `from' device %s in %s\n",
103 				fields[0], mapfile);
104 			continue;
105 		}
106 		map = malloc(sizeof *map);
107 		map->from = strdup(fields[0]);
108 		map->to =   strdup(fields[1]);
109 		map->fdev = iconfig(fields[0]);
110 		map->tdev = nil;
111 		if (access(map->to, AEXIST) < 0) {
112 			/*
113 			 * map->to isn't an existing file, so it had better be
114 			 * a config string for a device.
115 			 */
116 			if(testconfig(fields[1]) == 0)
117 				map->tdev = iconfig(fields[1]);
118 		}
119 		/* else map->to is the replacement file name */
120 		map->next = devmap;
121 		devmap = map;
122 	}
123 	Bterm(bp);
124 }
125 
126 static void
confinit(void)127 confinit(void)
128 {
129 	conf.nmach = 1;
130 
131 	conf.mem = meminit();
132 
133 	conf.nuid = 1000;
134 	conf.nserve = 15;		/* tunable */
135 	conf.nfile = 30000;
136 	conf.nlgmsg = 100;
137 	conf.nsmmsg = 500;
138 
139 	localconfinit();
140 
141 	conf.nwpath = conf.nfile*8;
142 	conf.nauth =  conf.nfile/10;
143 	conf.gidspace = conf.nuid*3;
144 
145 	cons.flags = 0;
146 
147 	if (conf.devmap)
148 		mapinit(conf.devmap);
149 }
150 
151 /*
152  * compute BUFSIZE*(NDBLOCK+INDPERBUF+INDPERBUF⁲+INDPERBUF⁳+INDPERBUF⁴)
153  * while watching for overflow; in that case, return 0.
154  */
155 
156 static uvlong
adduvlongov(uvlong a,uvlong b)157 adduvlongov(uvlong a, uvlong b)
158 {
159 	uvlong r = a + b;
160 
161 	if (r < a || r < b)
162 		return 0;
163 	return r;
164 }
165 
166 static uvlong
muluvlongov(uvlong a,uvlong b)167 muluvlongov(uvlong a, uvlong b)
168 {
169 	uvlong r = a * b;
170 
171 	if (a != 0 && r/a != b || r < a || r < b)
172 		return 0;
173 	return r;
174 }
175 
176 static uvlong
maxsize(void)177 maxsize(void)
178 {
179 	int i;
180 	uvlong max = NDBLOCK, ind = 1;
181 
182 	for (i = 0; i < NIBLOCK; i++) {
183 		ind = muluvlongov(ind, INDPERBUF);	/* power of INDPERBUF */
184 		if (ind == 0)
185 			return 0;
186 		max = adduvlongov(max, ind);
187 		if (max == 0)
188 			return 0;
189 	}
190 	return muluvlongov(max, BUFSIZE);
191 }
192 
193 enum {
194 	INDPERBUF⁲ = ((uvlong)INDPERBUF *INDPERBUF),
195 	INDPERBUF⁴ = ((uvlong)INDPERBUF⁲*INDPERBUF⁲),
196 };
197 
198 static void
printsizes(void)199 printsizes(void)
200 {
201 	uvlong max = maxsize();
202 
203 	print("\tblock size = %d; ", RBUFSIZE);
204 	if (max == 0)
205 		print("max file size exceeds 2⁶⁴ bytes\n");
206 	else {
207 		uvlong offlim = 1ULL << (sizeof(Off)*8 - 1);
208 
209 		if (max >= offlim)
210 			max = offlim - 1;
211 		print("max file size = %,llud\n", (Wideoff)max);
212 	}
213 	if (INDPERBUF⁲/INDPERBUF != INDPERBUF)
214 		print("overflow computing INDPERBUF⁲\n");
215 	if (INDPERBUF⁴/INDPERBUF⁲ != INDPERBUF⁲)
216 		print("overflow computing INDPERBUF⁴\n");
217 	print("\tINDPERBUF = %d, INDPERBUF^4 = %,lld, ", INDPERBUF,
218 		(Wideoff)INDPERBUF⁴);
219 	print("CEPERBK = %d\n", CEPERBK);
220 	print("\tsizeofs: Dentry = %d, Cache = %d\n",
221 		sizeof(Dentry), sizeof(Cache));
222 }
223 
224 void
usage(void)225 usage(void)
226 {
227 	fprint(2, "usage: %s [-cf][-a ann-str][-m dev-map] config-dev\n",
228 		argv0);
229 	exits("usage");
230 }
231 
232 void
main(int argc,char ** argv)233 main(int argc, char **argv)
234 {
235 	int i, nets = 0;
236 	char *ann;
237 
238 	rfork(RFNOTEG);
239 	formatinit();
240 	machinit();
241 	conf.confdev = "n";		/* Devnone */
242 
243 	ARGBEGIN{
244 	case 'a':			/* announce on this net */
245 		ann = EARGF(usage());
246 		if (nets >= Maxnets) {
247 			fprint(2, "%s: too many networks to announce: %s\n",
248 				argv0, ann);
249 			exits("too many nets");
250 		}
251 		annstrs[nets++] = ann;
252 		break;
253 	case 'c':			/* use new, faster cache layout */
254 		oldcachefmt = 0;
255 		break;
256 	case 'f':			/* enter configuration mode first */
257 		conf.configfirst++;
258 		break;
259 	case 'm':			/* name device-map file */
260 		conf.devmap = EARGF(usage());
261 		break;
262 	default:
263 		usage();
264 		break;
265 	}ARGEND
266 
267 	if (argc != 1)
268 		usage();
269 	conf.confdev = argv[0];	/* config string for dev holding full config */
270 
271 	Binit(&bin, 0, OREAD);
272 	confinit();
273 
274 	print("\nPlan 9 %d-bit cached-worm file server with %d-deep indir blks\n",
275 		sizeof(Off)*8 - 1, NIBLOCK);
276 	printsizes();
277 
278 	qlock(&reflock);
279 	qunlock(&reflock);
280 	serveq = newqueue(1000, "9P service");	/* tunable */
281 	raheadq = newqueue(1000, "readahead");	/* tunable */
282 
283 	mbinit();
284 	netinit();
285 	scsiinit();
286 
287 	files = malloc(conf.nfile * sizeof *files);
288 	for(i=0; i < conf.nfile; i++) {
289 		qlock(&files[i]);
290 		qunlock(&files[i]);
291 	}
292 
293 	wpaths = malloc(conf.nwpath * sizeof(*wpaths));
294 	uid = malloc(conf.nuid * sizeof(*uid));
295 	gidspace = malloc(conf.gidspace * sizeof(*gidspace));
296 	authinit();
297 
298 	print("iobufinit\n");
299 	iobufinit();
300 
301 	arginit();
302 	boottime = time(nil);
303 
304 	print("sysinit\n");
305 	sysinit();
306 
307 	/*
308 	 * Ethernet i/o processes
309 	 */
310 	netstart();
311 
312 	/*
313 	 * read ahead processes
314 	 */
315 	newproc(rahead, 0, "rah");
316 
317 	/*
318 	 * server processes
319 	 */
320 	for(i=0; i < conf.nserve; i++)
321 		newproc(serve, 0, "srv");
322 
323 	/*
324 	 * worm "dump" copy process
325 	 */
326 	newproc(wormcopy, 0, "wcp");
327 
328 	/*
329 	 * processes to read the console
330 	 */
331 	consserve();
332 
333 	/*
334 	 * "sync" copy process
335 	 * this doesn't return.
336 	 */
337 	procsetname("scp");
338 	synccopy();
339 }
340 
341 /*
342  * read ahead processes.
343  * read message from q and then
344  * read the device.
345  */
346 int
rbcmp(void * va,void * vb)347 rbcmp(void *va, void *vb)
348 {
349 	Rabuf *ra, *rb;
350 
351 	ra = *(Rabuf**)va;
352 	rb = *(Rabuf**)vb;
353 	if(rb == 0)
354 		return 1;
355 	if(ra == 0)
356 		return -1;
357 	if(ra->dev > rb->dev)
358 		return 1;
359 	if(ra->dev < rb->dev)
360 		return -1;
361 	if(ra->addr > rb->addr)
362 		return 1;
363 	if(ra->addr < rb->addr)
364 		return -1;
365 	return 0;
366 }
367 
368 void
rahead(void *)369 rahead(void *)
370 {
371 	Rabuf *rb[50];
372 	Iobuf *p;
373 	int i, n;
374 
375 	for (;;) {
376 		rb[0] = fs_recv(raheadq, 0);
377 		for(n = 1; n < nelem(rb); n++) {
378 			if(raheadq->count <= 0)
379 				break;
380 			rb[n] = fs_recv(raheadq, 0);
381 		}
382 		qsort(rb, n, sizeof rb[0], rbcmp);
383 		for(i = 0; i < n; i++) {
384 			if(rb[i] == 0)
385 				continue;
386 			p = getbuf(rb[i]->dev, rb[i]->addr, Brd);
387 			if(p)
388 				putbuf(p);
389 			lock(&rabuflock);
390 			rb[i]->link = rabuffree;
391 			rabuffree = rb[i];
392 			unlock(&rabuflock);
393 		}
394 	}
395 }
396 
397 /*
398  * main filesystem server loop.
399  * entered by many processes.
400  * they wait for message buffers and
401  * then process them.
402  */
403 void
serve(void *)404 serve(void *)
405 {
406 	int i;
407 	Chan *cp;
408 	Msgbuf *mb;
409 
410 	for (;;) {
411 		qlock(&reflock);
412 		/* read 9P request from a network input process */
413 		mb = fs_recv(serveq, 0);
414 		assert(mb->magic == Mbmagic);
415 		/* fs kernel sets chan in /sys/src/fs/ip/il.c:/^getchan */
416 		cp = mb->chan;
417 		if (cp == nil)
418 			panic("serve: nil mb->chan");
419 		rlock(&cp->reflock);
420 		qunlock(&reflock);
421 
422 		rlock(&mainlock);
423 
424 		if (mb->data == nil)
425 			panic("serve: nil mb->data");
426 		/* better sniffing code in /sys/src/cmd/disk/kfs/9p12.c */
427 		if(cp->protocol == nil){
428 			/* do we recognise the protocol in this packet? */
429 			/* better sniffing code: /sys/src/cmd/disk/kfs/9p12.c */
430 			for(i = 0; fsprotocol[i] != nil; i++)
431 				if(fsprotocol[i](mb) != 0) {
432 					cp->protocol = fsprotocol[i];
433 					break;
434 				}
435 			if(cp->protocol == nil){
436 				print("no protocol for message\n");
437 				for(i = 0; i < 12; i++)
438 					print(" %2.2uX", mb->data[i]);
439 				print("\n");
440 			}
441 		} else
442 			/* process the request, generate an answer and reply */
443 			cp->protocol(mb);
444 
445 		mbfree(mb);
446 		runlock(&mainlock);
447 		runlock(&cp->reflock);
448 	}
449 }
450 
451 void
exit(void)452 exit(void)
453 {
454 	lock(&active);
455 	active.exiting = 1;
456 	unlock(&active);
457 
458 	print("halted at %T.\n", time(nil));
459 	postnote(PNGROUP, getpid(), "die");
460 	exits(nil);
461 }
462 
463 enum {
464 	DUMPTIME = 5,	/* 5 am */
465 	WEEKMASK = 0,	/* every day (1=sun, 2=mon, 4=tue, etc.) */
466 };
467 
468 /*
469  * calculate the next dump time.
470  * minimum delay is 100 minutes.
471  */
472 Timet
nextdump(Timet t)473 nextdump(Timet t)
474 {
475 	Timet nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK);
476 
477 	if(!conf.nodump)
478 		print("next dump at %T\n", nddate);
479 	return nddate;
480 }
481 
482 /*
483  * process to copy dump blocks from
484  * cache to worm. it runs flat out when
485  * it gets work, but only looks for
486  * work every 10 seconds.
487  */
488 void
wormcopy(void *)489 wormcopy(void *)
490 {
491 	int f, dorecalc = 1;
492 	Timet dt, t = 0, nddate = 0, ntoytime = 0;
493 	Filsys *fs;
494 
495 	for (;;) {
496 		if (dorecalc) {
497 			dorecalc = 0;
498 			t = time(nil);
499 			nddate = nextdump(t);		/* chatters */
500 			ntoytime = time(nil);
501 		}
502 		dt = time(nil) - t;
503 		if(dt < 0 || dt > MINUTE(100)) {
504 			if(dt < 0)
505 				print("time went back\n");
506 			else
507 				print("time jumped ahead\n");
508 			dorecalc = 1;
509 			continue;
510 		}
511 		t += dt;
512 		f = 0;
513 		if(t > ntoytime)
514 			ntoytime = time(nil) + HOUR(1);
515 		else if(t > nddate) {
516 			if(!conf.nodump) {
517 				print("automatic dump %T\n", t);
518 				for(fs=filsys; fs->name; fs++)
519 					if(fs->dev->type == Devcw)
520 						cfsdump(fs);
521 			}
522 			dorecalc = 1;
523 		} else {
524 			rlock(&mainlock);
525 			for(fs=filsys; fs->name; fs++)
526 				if(fs->dev->type == Devcw)
527 					f |= dumpblock(fs->dev);
528 			runlock(&mainlock);
529 			if(!f)
530 				delay(10000);
531 			wormprobe();
532 		}
533 	}
534 }
535 
536 /*
537  * process to synch blocks
538  * it puts out a block/cache-line every second
539  * it waits 10 seconds if caught up.
540  * in both cases, it takes about 10 seconds
541  * to get up-to-date.
542  */
543 void
synccopy(void)544 synccopy(void)
545 {
546 	int f;
547 
548 	for (;;) {
549 		rlock(&mainlock);
550 		f = syncblock();
551 		runlock(&mainlock);
552 		if(!f)
553 			delay(10000);
554 		else
555 			delay(1000);
556 	}
557 }
558 
559 Devsize
inqsize(char * file)560 inqsize(char *file)
561 {
562 	int nf;
563 	char *ln, *end, *data = malloc(strlen(file) + 5 + 1);
564 	char *fields[4];
565 	Devsize rv = -1;
566 	Biobuf *bp;
567 
568 	strcpy(data, file);
569 	end = strstr(data, "/data");
570 	if (end == nil)
571 		strcat(data, "/ctl");
572 	else
573 		strcpy(end, "/ctl");
574 	bp = Bopen(data, OREAD);
575 	if (bp) {
576 		while (rv < 0 && (ln = Brdline(bp, '\n')) != nil) {
577 			ln[Blinelen(bp)-1] = '\0';
578 			nf = tokenize(ln, fields, nelem(fields));
579 			if (nf == 3 && strcmp(fields[0], "geometry") == 0)
580 				rv = atoi(fields[2]);
581 		}
582 		Bterm(bp);
583 	}
584 	free(data);
585 	return rv;
586 }
587