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