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