]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/main.c
cwfs: fix race between cmd_exec("users") and serve processes, cleanup portfns.h
[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         installcmds();
377         cmd_exec("cfs");
378         cmd_exec("users");
379         cmd_exec("version");
380
381         /*
382          * post filedescriptors to /srv
383          */
384         postservice();
385
386         /*
387          * Ethernet i/o processes
388          */
389         netstart();
390
391         /*
392          * read ahead processes
393          */
394         newproc(rahead, 0, "rah");
395
396         /*
397          * server processes
398          */
399         for(i=0; i < conf.nserve; i++)
400                 newproc(serve, 0, "srv");
401
402         /*
403          * worm "dump" copy process
404          */
405         newproc(wormcopy, 0, "wcp");
406
407         /*
408          * "sync" copy process
409          */
410         newproc(synccopy, 0, "scp");
411
412         /*
413          * processes to read the console
414          */
415         consserve();
416
417         /* success */
418         exits(nil);
419 }
420
421 /*
422  * read ahead processes.
423  * read message from q and then
424  * read the device.
425  */
426 int
427 rbcmp(void *va, void *vb)
428 {
429         Rabuf *ra, *rb;
430
431         ra = *(Rabuf**)va;
432         rb = *(Rabuf**)vb;
433         if(rb == 0)
434                 return 1;
435         if(ra == 0)
436                 return -1;
437         if(ra->dev > rb->dev)
438                 return 1;
439         if(ra->dev < rb->dev)
440                 return -1;
441         if(ra->addr > rb->addr)
442                 return 1;
443         if(ra->addr < rb->addr)
444                 return -1;
445         return 0;
446 }
447
448 void
449 rahead(void *)
450 {
451         Rabuf *rb[50];
452         Iobuf *p;
453         int i, n;
454
455         for (;;) {
456                 rb[0] = fs_recv(raheadq, 0);
457                 for(n = 1; n < nelem(rb); n++) {
458                         if(raheadq->count <= 0)
459                                 break;
460                         rb[n] = fs_recv(raheadq, 0);
461                 }
462                 qsort(rb, n, sizeof rb[0], rbcmp);
463                 for(i = 0; i < n; i++) {
464                         if(rb[i] == 0)
465                                 continue;
466                         p = getbuf(rb[i]->dev, rb[i]->addr, Brd);
467                         if(p)
468                                 putbuf(p);
469                         lock(&rabuflock);
470                         rb[i]->link = rabuffree;
471                         rabuffree = rb[i];
472                         unlock(&rabuflock);
473                 }
474         }
475 }
476
477 /*
478  * main filesystem server loop.
479  * entered by many processes.
480  * they wait for message buffers and
481  * then process them.
482  */
483 void
484 serve(void *)
485 {
486         int i;
487         Chan *cp;
488         Msgbuf *mb;
489
490         for (;;) {
491                 qlock(&reflock);
492                 /* read 9P request from a network input process */
493                 mb = fs_recv(serveq, 0);
494                 assert(mb->magic == Mbmagic);
495                 /* fs kernel sets chan in /sys/src/fs/ip/il.c:/^getchan */
496                 cp = mb->chan;
497                 if (cp == nil)
498                         panic("serve: nil mb->chan");
499                 rlock(&cp->reflock);
500                 qunlock(&reflock);
501
502                 rlock(&mainlock);
503
504                 if (mb->data == nil)
505                         panic("serve: nil mb->data");
506                 /* better sniffing code in /sys/src/cmd/disk/kfs/9p12.c */
507                 if(cp->protocol == nil){
508                         /* do we recognise the protocol in this packet? */
509                         /* better sniffing code: /sys/src/cmd/disk/kfs/9p12.c */
510                         for(i = 0; fsprotocol[i] != nil; i++)
511                                 if(fsprotocol[i](mb) != 0) {
512                                         cp->protocol = fsprotocol[i];
513                                         break;
514                                 }
515                         if(cp->protocol == nil && (chatty > 1)){
516                                 print("no protocol for message\n");
517                                 hexdump(mb->data, 12);
518                         }
519                 } else
520                         /* process the request, generate an answer and reply */
521                         cp->protocol(mb);
522
523                 mbfree(mb);
524                 runlock(&mainlock);
525                 runlock(&cp->reflock);
526         }
527 }
528
529 void
530 exit(void)
531 {
532         lock(&active);
533         active.exiting = 1;
534         unlock(&active);
535
536         print("halted at %T.\n", time(nil));
537         postnote(PNGROUP, getpid(), "die");
538         exits(nil);
539 }
540
541 enum {
542         DUMPTIME = 5,   /* 5 am */
543         WEEKMASK = 0,   /* every day (1=sun, 2=mon, 4=tue, etc.) */
544 };
545
546 /*
547  * calculate the next dump time.
548  * minimum delay is 100 minutes.
549  */
550 Timet
551 nextdump(Timet t)
552 {
553         Timet nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK);
554
555         if(!conf.nodump && chatty)
556                 print("next dump at %T\n", nddate);
557         return nddate;
558 }
559
560 /*
561  * process to copy dump blocks from
562  * cache to worm. it runs flat out when
563  * it gets work, but only looks for
564  * work every 10 seconds.
565  */
566 void
567 wormcopy(void *)
568 {
569         int f, dorecalc = 1;
570         Timet dt, t = 0, nddate = 0, ntoytime = 0;
571         Filsys *fs;
572
573         for (;;) {
574                 if (dorecalc) {
575                         dorecalc = 0;
576                         t = time(nil);
577                         nddate = nextdump(t);           /* chatters */
578                         ntoytime = time(nil);
579                 }
580                 dt = time(nil) - t;
581                 if(dt < 0 || dt > MINUTE(100)) {
582                         dorecalc = 1;
583                         continue;
584                 }
585                 t += dt;
586                 f = 0;
587                 if(t > ntoytime)
588                         ntoytime = time(nil) + HOUR(1);
589                 else if(t > nddate) {
590                         if(!conf.nodump) {
591                                 print("automatic dump %T\n", t);
592                                 for(fs=filsys; fs->name; fs++)
593                                         if(fs->dev->type == Devcw)
594                                                 cfsdump(fs);
595                         }
596                         dorecalc = 1;
597                 } else {
598                         rlock(&mainlock);
599                         for(fs=filsys; fs->name; fs++)
600                                 if(fs->dev->type == Devcw)
601                                         f |= dumpblock(fs->dev);
602                         runlock(&mainlock);
603                         if(!f)
604                                 delay(10000);
605                         wormprobe();
606                 }
607         }
608 }
609
610 /*
611  * process to synch blocks
612  * it puts out a block/cache-line every second
613  * it waits 10 seconds if caught up.
614  * in both cases, it takes about 10 seconds
615  * to get up-to-date.
616  */
617 void
618 synccopy(void *)
619 {
620         int f;
621
622         for (;;) {
623                 rlock(&mainlock);
624                 f = syncblock();
625                 runlock(&mainlock);
626                 if(!f)
627                         delay(10000);
628                 else
629                         delay(1000);
630         }
631 }
632
633 Devsize
634 inqsize(char *file)
635 {
636         int nf;
637         char *ln, *end, *data = malloc(strlen(file) + 5 + 1);
638         char *fields[4];
639         Devsize rv = -1;
640         Biobuf *bp;
641
642         strcpy(data, file);
643         end = strstr(data, "/data");
644         if (end == nil)
645                 strcat(data, "/ctl");
646         else
647                 strcpy(end, "/ctl");
648         bp = Bopen(data, OREAD);
649         if (bp) {
650                 while (rv < 0 && (ln = Brdline(bp, '\n')) != nil) {
651                         ln[Blinelen(bp)-1] = '\0';
652                         nf = tokenize(ln, fields, nelem(fields));
653                         if (nf == 3 && strcmp(fields[0], "geometry") == 0)
654                                 rv = atoi(fields[2]);
655                 }
656                 Bterm(bp);
657         }
658         free(data);
659         return rv;
660 }
661