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