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