]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/main.c
d1422a1f9f865244b36b78fc6b48c303da50765b
[plan9front.git] / sys / src / cmd / cwfs / main.c
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
13 machinit(void)
14 {
15         active.exiting = 0;
16 }
17
18 /*
19  * Put a string on the console.
20  */
21 void
22 puts(char *s, int n)
23 {
24         print("%.*s", n, s);
25 }
26
27 void
28 prflush(void)
29 {
30 }
31
32 /*
33  * Print a string on the console.
34  */
35 void
36 putstrn(char *str, int n)
37 {
38         puts(str, n);
39 }
40
41 /*
42  * get a character from the console
43  */
44 int
45 getc(void)
46 {
47         return Bgetrune(&bin);
48 }
49
50 void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }