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