]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/main.c
awk: make empty FS unicodely-correct.
[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                 /* fs kernel sets chan in /sys/src/fs/ip/il.c:/^getchan */
457                 cp = mb->chan;
458                 if (cp == nil)
459                         panic("serve: nil mb->chan");
460                 rlock(&cp->reflock);
461                 qunlock(&reflock);
462
463                 rlock(&mainlock);
464
465                 if (mb->data == nil)
466                         panic("serve: nil mb->data");
467                 if(cp->protocol != nil){
468                         /* process the request, generate an answer and reply */
469                         cp->protocol(mb);
470                 } else {
471                         /* do we recognise the protocol in this packet? */
472                         for(i = 0; fsprotocol[i] != nil; i++)
473                                 if(fsprotocol[i](mb) != 0) {
474                                         cp->protocol = fsprotocol[i];
475                                         break;
476                                 }
477                         if(cp->protocol == nil && (chatty > 1)){
478                                 fprint(2, "no protocol for message\n");
479                                 hexdump(mb->data, 12);
480                         }
481                 }
482
483                 mbfree(mb);
484                 runlock(&mainlock);
485                 runlock(&cp->reflock);
486         }
487 }
488
489 void
490 exit(void)
491 {
492         lock(&active);
493         active.exiting = 1;
494         unlock(&active);
495
496         fprint(2, "halted at %T.\n", time(nil));
497         postnote(PNGROUP, getpid(), "die");
498         exits(nil);
499 }
500
501 enum {
502         DUMPTIME = 5,   /* 5 am */
503         WEEKMASK = 0,   /* every day (1=sun, 2=mon, 4=tue, etc.) */
504 };
505
506 /*
507  * calculate the next dump time.
508  * minimum delay is 100 minutes.
509  */
510 Timet
511 nextdump(Timet t)
512 {
513         Timet nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK);
514
515         if(!conf.nodump && chatty)
516                 fprint(2, "next dump at %T\n", nddate);
517         return nddate;
518 }
519
520 /*
521  * process to copy dump blocks from
522  * cache to worm. it runs flat out when
523  * it gets work, but only looks for
524  * work every 10 seconds.
525  */
526 void
527 wormcopy(void *)
528 {
529         int f, dorecalc = 1;
530         Timet dt, t = 0, nddate = 0, ntoytime = 0;
531         Filsys *fs;
532
533         for (;;) {
534                 if (dorecalc) {
535                         dorecalc = 0;
536                         t = time(nil);
537                         nddate = nextdump(t);           /* chatters */
538                         ntoytime = time(nil);
539                 }
540                 dt = time(nil) - t;
541                 if(dt < 0 || dt > MINUTE(100)) {
542                         dorecalc = 1;
543                         continue;
544                 }
545                 t += dt;
546                 f = 0;
547                 if(t > ntoytime)
548                         ntoytime = time(nil) + HOUR(1);
549                 else if(t > nddate) {
550                         if(!conf.nodump) {
551                                 fprint(2, "automatic dump %T\n", t);
552                                 for(fs=filsys; fs->name; fs++)
553                                         if(fs->dev->type == Devcw)
554                                                 cfsdump(fs);
555                         }
556                         dorecalc = 1;
557                 } else {
558                         rlock(&mainlock);
559                         for(fs=filsys; fs->name; fs++)
560                                 if(fs->dev->type == Devcw)
561                                         f |= dumpblock(fs->dev);
562                         runlock(&mainlock);
563                         if(!f)
564                                 delay(10000);
565                         wormprobe();
566                 }
567         }
568 }
569
570 /*
571  * process to synch blocks
572  * it puts out a block/cache-line every second
573  * it waits 10 seconds if caught up.
574  * in both cases, it takes about 10 seconds
575  * to get up-to-date.
576  */
577 void
578 synccopy(void *)
579 {
580         int f;
581
582         for (;;) {
583                 rlock(&mainlock);
584                 f = syncblock();
585                 runlock(&mainlock);
586                 if(!f)
587                         delay(10000);
588                 else
589                         delay(1000);
590         }
591 }
592
593 Devsize
594 inqsize(char *file)
595 {
596         int nf;
597         char *ln, *end, *data;
598         char *fields[4];
599         Devsize rv = -1;
600         Biobuf *bp;
601
602         data = malloc(strlen(file) + 5 + 1);
603         strcpy(data, file);
604         end = strstr(data, "/data");
605         if (end == nil)
606                 strcat(data, "/ctl");
607         else
608                 strcpy(end, "/ctl");
609         bp = Bopen(data, OREAD);
610         if (bp) {
611                 while (rv < 0 && (ln = Brdline(bp, '\n')) != nil) {
612                         ln[Blinelen(bp)-1] = '\0';
613                         nf = tokenize(ln, fields, nelem(fields));
614                         if (nf == 3 && strcmp(fields[0], "geometry") == 0)
615                                 rv = atoi(fields[2]);
616                 }
617                 Bterm(bp);
618         }
619         free(data);
620         return rv;
621 }