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