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