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