1 #include "all.h" 2 #include "9p1.h" 3 4 static char elem[NAMELEN]; 5 static Filsys* cur_fs; 6 static char conline[100]; 7 8 void 9 consserve(void) 10 { 11 con_session(); 12 cmd_exec("cfs"); 13 cmd_exec("user"); 14 } 15 16 int 17 cmd_exec(char *arg) 18 { 19 char *s, *c; 20 int i; 21 22 for(i=0; s = command[i].string; i++) { 23 for(c=arg; *s; c++) 24 if(*c != *s++) 25 goto brk; 26 if(*c == '\0' || *c == ' ' || *c == '\t'){ 27 cons.arg = c; 28 (*command[i].func)(); 29 return 1; 30 } 31 brk:; 32 } 33 return 0; 34 } 35 36 void 37 cmd_check(void) 38 { 39 char *s; 40 int flags; 41 42 flags = 0; 43 for(s = cons.arg; *s; s++){ 44 while(*s == ' ' || *s == '\t') 45 s++; 46 if(*s == '\0') 47 break; 48 switch(*s){ 49 /* rebuild the free list */ 50 case 'f': flags |= Cfree; break; 51 /* fix bad tags */ 52 case 't': flags |= Ctag; break; 53 /* fix bad tags and clear the contents of the block */ 54 case 'c': flags |= Cream; break; 55 /* delete all redundant references to a block */ 56 case 'd': flags |= Cbad; break; 57 /* read and check tags on all blocks */ 58 case 'r': flags |= Crdall; break; 59 /* write all of the blocks you touch */ 60 case 'w': flags |= Ctouch; break; 61 /* print all directories as they are read */ 62 case 'p': flags |= Cpdir; break; 63 /* print all files as they are read */ 64 case 'P': flags |= Cpfile; break; 65 /* quiet, just report really nasty stuff */ 66 case 'q': flags |= Cquiet; break; 67 } 68 } 69 check(cur_fs, flags); 70 } 71 72 enum 73 { 74 Sset = (1<<0), 75 Setc = (1<<1), 76 }; 77 void 78 cmd_stats(void) 79 { 80 cprint("work stats\n"); 81 cprint(" work = %A rps\n", (Filta){&cons.work, 1}); 82 cprint(" rate = %A tBps\n", (Filta){&cons.rate, 1000}); 83 cprint(" hits = %A iops\n", (Filta){&cons.bhit, 1}); 84 cprint(" read = %A iops\n", (Filta){&cons.bread, 1}); 85 cprint(" init = %A iops\n", (Filta){&cons.binit, 1}); 86 /* for(i = 0; i < MAXTAG; i++) 87 cprint(" tag %G = %A iops\n", i, (Filta){&cons.tags[i], 1}); 88 */ 89 } 90 91 void 92 cmd_sync(void) 93 { 94 rlock(&mainlock); 95 syncall(); 96 runlock(&mainlock); 97 } 98 99 void 100 cmd_halt(void) 101 { 102 wlock(&mainlock); 103 syncall(); 104 superok(cur_fs->dev, superaddr(cur_fs->dev), 1); 105 print("kfs: file system halted\n"); 106 } 107 108 void 109 cmd_start(void) 110 { 111 superok(cur_fs->dev, superaddr(cur_fs->dev), 0); 112 wunlock(&mainlock); 113 print("kfs: file system started\n"); 114 } 115 116 void 117 cmd_help(void) 118 { 119 int i; 120 121 for(i=0; command[i].string; i++) 122 cprint(" %s %s\n", command[i].string, command[i].args); 123 cprint("check options:\n" 124 " r read all blocks\n" 125 " f rebuild the free list\n" 126 " t fix all bad tags\n" 127 " c fix bad tags and zero the blocks\n" 128 " d delete all redundant references to blocks\n" 129 " p print directories as they are checked\n" 130 " P print all files as they are checked\n" 131 " w write all blocks that are read\n"); 132 } 133 134 void 135 cmd_create(void) 136 { 137 int uid, gid, err; 138 long perm; 139 char oelem[NAMELEN]; 140 char name[NAMELEN]; 141 142 if(err = con_clone(FID1, FID2)){ 143 cprint("clone failed: %s\n", errstring[err]); 144 return; 145 } 146 if(skipbl(1)){ 147 cprint("skipbl\n"); 148 return; 149 } 150 oelem[0] = 0; 151 while(nextelem()) { 152 if(oelem[0]) 153 if(err = con_walk(FID2, oelem)){ 154 cprint("walk failed: %s\n", errstring[err]); 155 return; 156 } 157 memmove(oelem, elem, NAMELEN); 158 } 159 if(skipbl(1)) 160 return; 161 uid = strtouid(cname(name)); 162 if(uid == 0){ 163 cprint("unknown user %s\n", name); 164 return; 165 } 166 gid = strtouid(cname(name)); 167 if(gid == 0){ 168 cprint("unknown group %s\n", name); 169 return; 170 } 171 perm = number(0777, 8); 172 skipbl(0); 173 for(; *cons.arg; cons.arg++){ 174 if(*cons.arg == 'l') 175 perm |= PLOCK; 176 else 177 if(*cons.arg == 'a') 178 perm |= PAPND; 179 else 180 if(*cons.arg == 'd') 181 perm |= PDIR; 182 else 183 break; 184 } 185 err = con_create(FID2, elem, uid, gid, perm, 0); 186 if(err) 187 cprint("can't create %s: %s\n", elem, errstring[err]); 188 } 189 190 void 191 cmd_clri(void) 192 { 193 if(con_clone(FID1, FID2)) 194 return; 195 if(skipbl(1)) 196 return; 197 while(nextelem()) 198 if(con_walk(FID2, elem)){ 199 cprint("can't walk %s\n", elem); 200 return; 201 } 202 con_clri(FID2); 203 } 204 205 void 206 cmd_rename(void) 207 { 208 ulong perm; 209 Dentry d; 210 char stat[DIRREC]; 211 char oelem[NAMELEN], noelem[NAMELEN], nxelem[NAMELEN]; 212 int err; 213 214 if(con_clone(FID1, FID2)) 215 return; 216 if(skipbl(1)) 217 return; 218 oelem[0] = 0; 219 while(nextelem()) { 220 if(oelem[0]) 221 if(con_walk(FID2, oelem)){ 222 cprint("file does not exits"); 223 return; 224 } 225 memmove(oelem, elem, NAMELEN); 226 } 227 if(skipbl(1)) 228 return; 229 if(cons.arg[0]=='/'){ 230 if(con_clone(FID1, FID3)) 231 return; 232 noelem[0] = 0; 233 while(nextelem()){ 234 if(noelem[0]) 235 if(con_walk(FID3, noelem)){ 236 cprint("target path %s does not exist", noelem); 237 return; 238 } 239 memmove(noelem, elem, NAMELEN); 240 } 241 if(!con_walk(FID3, elem)){ 242 cprint("target %s already exists\n", elem); 243 return; 244 } 245 if(con_walk(FID2, oelem)){ 246 cprint("src %s does not exist\n", oelem); 247 return; 248 } 249 /* 250 * we know the target does not exist, 251 * the source does exist. 252 * to do the rename, create the target and then 253 * copy the directory entry directly. then remove the source. 254 */ 255 if(err = con_stat(FID2, stat)){ 256 cprint("can't stat file: %s\n", errstring[err]); 257 return; 258 } 259 convM2D9p1(stat, &d); 260 perm = (d.mode&0777)|((d.mode&0x7000)<<17); 261 if(err = con_create(FID3, elem, d.uid, d.gid, perm, (d.mode&DDIR)?OREAD:ORDWR)){ 262 cprint("can't create %s: %s\n", elem, errstring[err]); 263 return; 264 } 265 if(err = con_swap(FID2, FID3)){ 266 cprint("can't swap data: %s\n", errstring[err]); 267 return; 268 } 269 if(err = con_remove(FID2)){ 270 cprint("can't remove file: %s\n", errstring[err]); 271 return; 272 } 273 }else{ 274 cname(nxelem); 275 if(strchr(nxelem, '/')){ 276 cprint("bad rename target: not full path, but contains slashes\n"); 277 return; 278 } 279 if(!con_walk(FID2, nxelem)) 280 cprint("file %s already exists\n", nxelem); 281 else if(con_walk(FID2, oelem)) 282 cprint("file does not already exist\n"); 283 else if(err = con_stat(FID2, stat)) 284 cprint("can't stat file: %s\n", errstring[err]); 285 else{ 286 convM2D9p1(stat, &d); 287 strncpy(d.name, nxelem, NAMELEN); 288 convD2M9p1(&d, stat); 289 if(err = con_wstat(FID2, stat)) 290 cprint("can't move file: %s\n", errstring[err]); 291 } 292 } 293 } 294 295 void 296 cmd_remove(void) 297 { 298 if(con_clone(FID1, FID2)) 299 return; 300 if(skipbl(1)) 301 return; 302 while(nextelem()) 303 if(con_walk(FID2, elem)){ 304 cprint("can't walk %s\n", elem); 305 return; 306 } 307 con_remove(FID2); 308 } 309 310 void 311 cmd_cfs(void) 312 { 313 Filsys *fs; 314 315 if(*cons.arg != ' ') { 316 fs = &filesys[0]; /* default */ 317 } else { 318 if(skipbl(1)){ 319 cprint("skipbl\n"); 320 return; 321 } 322 if(!nextelem()) 323 fs = &filesys[0]; /* default */ 324 else 325 fs = fsstr(elem); 326 } 327 if(fs == 0) { 328 cprint("unknown file system %s\n", elem); 329 return; 330 } 331 if(con_attach(FID1, "adm", fs->name)) 332 panic("FID1 attach to root"); 333 cur_fs = fs; 334 } 335 336 /* 337 * find out the length of a file 338 * given the mesg version of a stat buffer 339 * we call this because convM2D is different 340 * for the file system than in the os 341 */ 342 static uvlong 343 statlen(char *ap) 344 { 345 uchar *p; 346 ulong ll, hl; 347 348 p = (uchar*)ap; 349 p += 3*28+5*4; 350 ll = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); 351 hl = p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24); 352 return ll | ((uvlong) hl << 32); 353 } 354 355 int 356 adduser(char *user) 357 { 358 char stat[DIRREC]; 359 char msg[100]; 360 Uid *u; 361 int i, c, nu; 362 363 /* 364 * check uniq of name 365 * and get next uid 366 */ 367 cmd_exec("cfs"); 368 cmd_exec("user"); 369 nu = 0; 370 for(i=0, u=uid; i<conf.nuid; i++,u++) { 371 c = u->uid; 372 if(c == 0) 373 break; 374 if(strcmp(uidspace+u->offset, user) == 0) 375 return 1; 376 if(c >= 9000) 377 continue; 378 if(c > nu) 379 nu = c; 380 } 381 nu++; 382 if(nu >= 9000) { 383 cprint("out of user ids\n"); 384 return 0; 385 } 386 387 /* 388 * write onto adm/users 389 */ 390 if(con_clone(FID1, FID2) 391 || con_path(FID2, "/adm/users") 392 || con_open(FID2, 1)) { 393 cprint("can't open /adm/users\n"); 394 return 0; 395 } 396 397 sprint(msg, "%d:%s:%s:\n", nu, user, user); 398 cprint("add user '%s'", msg); 399 c = strlen(msg); 400 i = con_stat(FID2, stat); 401 if(i){ 402 cprint("can't stat /adm/users: %s\n", errstring[i]); 403 return 0; 404 } 405 i = con_write(FID2, msg, statlen(stat), c); 406 if(i != c){ 407 cprint("short write on /adm/users: %d %d\n", c, i); 408 return 0; 409 } 410 return 1; 411 } 412 413 void 414 cmd_newuser(void) 415 { 416 char user[NAMELEN], msg[100]; 417 int i, c; 418 419 /* 420 * get uid 421 */ 422 cname(user); 423 for(i=0; i<NAMELEN; i++) { 424 c = user[i]; 425 if(c == 0) 426 break; 427 if(c >= '0' && c <= '9' 428 || c >= 'a' && c <= 'z' 429 || c >= 'A' && c <= 'Z') 430 continue; 431 cprint("bad character in name: 0x%x\n", c); 432 return; 433 } 434 if(i < 2) { 435 cprint("name too short: %s\n", user); 436 return; 437 } 438 if(i >= NAMELEN) { 439 cprint("name too long: %s\n", user); 440 return; 441 } 442 443 /* 444 * install and create directory 445 */ 446 if(!adduser(user)) 447 return; 448 449 cmd_exec("user"); 450 sprint(msg, "create /usr/%s %s %s 775 d", user, user, user); 451 cmd_exec(msg); 452 sprint(msg, "create /usr/%s/tmp %s %s 775 d", user, user, user); 453 cmd_exec(msg); 454 sprint(msg, "create /usr/%s/lib %s %s 775 d", user, user, user); 455 cmd_exec(msg); 456 sprint(msg, "create /usr/%s/bin %s %s 775 d", user, user, user); 457 cmd_exec(msg); 458 sprint(msg, "create /usr/%s/bin/rc %s %s 775 d", user, user, user); 459 cmd_exec(msg); 460 sprint(msg, "create /usr/%s/bin/mips %s %s 775 d", user, user, user); 461 cmd_exec(msg); 462 sprint(msg, "create /usr/%s/bin/386 %s %s 775 d", user, user, user); 463 cmd_exec(msg); 464 sprint(msg, "create /usr/%s/bin/power %s %s 775 d", user, user, user); 465 cmd_exec(msg); 466 sprint(msg, "create /usr/%s/bin/alpha %s %s 775 d", user, user, user); 467 cmd_exec(msg); 468 } 469 470 void 471 cmd_checkuser(void) 472 { 473 uchar buf[DIRREC], *p; 474 static char utime[4]; 475 476 if(con_clone(FID1, FID2) 477 || con_path(FID2, "/adm/users") 478 || con_open(FID2, 0) 479 || con_stat(FID2, (char*)buf)) 480 return; 481 p = buf + 3*NAMELEN + 4*4; 482 if(memcmp(utime, p, 4) == 0) 483 return; 484 memmove(utime, p, 4); 485 cmd_user(); 486 } 487 488 void 489 cmd_allow(void) 490 { 491 wstatallow = 1; 492 } 493 494 void 495 cmd_disallow(void) 496 { 497 wstatallow = 0; 498 } 499 500 void 501 cmd_chat(void) 502 { 503 chat = 1 - chat; 504 } 505 506 void 507 cmd_atime(void) 508 { 509 noatime = !noatime; 510 if(noatime) 511 cprint("atimes will not be updated\n"); 512 else 513 cprint("atimes will be updated\n"); 514 } 515 516 void 517 cmd_noneattach(void) 518 { 519 allownone = !allownone; 520 if(allownone) 521 cprint("none can attach to new connections\n"); 522 else 523 cprint("none can only attach on authenticated connections\n"); 524 } 525 526 void 527 cmd_listen(void) 528 { 529 char addr[NAMELEN]; 530 531 if(skipbl(0)) 532 strcpy(addr, "il!*!17008"); 533 else 534 cname(addr); 535 536 if(netserve(addr)) 537 cprint("announce %s failed\n", addr); 538 else 539 cprint("announce %s\n", addr); 540 } 541 542 Command command[] = 543 { 544 "allow", cmd_allow, "", 545 "allowoff", cmd_disallow, "", 546 "atime", cmd_atime, "", 547 "cfs", cmd_cfs, "[filesys]", 548 "chat", cmd_chat, "", 549 "check", cmd_check, "[rftRdPpw]", 550 "clri", cmd_clri, "filename", 551 "create", cmd_create, "filename user group perm [ald]", 552 "disallow", cmd_disallow, "", 553 "halt", cmd_halt, "", 554 "help", cmd_help, "", 555 "listen", cmd_listen, "[address]", 556 "newuser", cmd_newuser, "username", 557 "noneattach", cmd_noneattach, "", 558 "remove", cmd_remove, "filename", 559 "rename", cmd_rename, "file newname", 560 "start", cmd_start, "", 561 "stats", cmd_stats, "[fw]", 562 "sync", cmd_sync, "", 563 "user", cmd_user, "", 564 0 565 }; 566 567 int 568 skipbl(int err) 569 { 570 if(*cons.arg != ' ') { 571 if(err) 572 cprint("syntax error\n"); 573 return 1; 574 } 575 while(*cons.arg == ' ') 576 cons.arg++; 577 return 0; 578 } 579 580 char* 581 _cname(char *name) 582 { 583 int i, c; 584 585 memset(name, 0, NAMELEN); 586 for(i=0;; i++) { 587 c = *cons.arg; 588 switch(c) { 589 case ' ': 590 case '\t': 591 case '\n': 592 case '\0': 593 return name; 594 } 595 if(i < NAMELEN-1) 596 name[i] = c; 597 cons.arg++; 598 } 599 return 0; 600 } 601 602 char* 603 cname(char *name) 604 { 605 skipbl(0); 606 return _cname(name); 607 } 608 609 int 610 nextelem(void) 611 { 612 char *e; 613 int i, c; 614 615 e = elem; 616 while(*cons.arg == '/') 617 cons.arg++; 618 c = *cons.arg; 619 if(c == 0 || c == ' ') 620 return 0; 621 for(i = 0; c = *cons.arg; i++) { 622 if(c == ' ' || c == '/') 623 break; 624 if(i == NAMELEN) { 625 cprint("path name component too long\n"); 626 return 0; 627 } 628 *e++ = c; 629 cons.arg++; 630 } 631 *e = 0; 632 return 1; 633 } 634 635 long 636 number(int d, int base) 637 { 638 int c, sign, any; 639 long n; 640 641 sign = 0; 642 any = 0; 643 n = 0; 644 645 c = *cons.arg; 646 while(c == ' ') { 647 cons.arg++; 648 c = *cons.arg; 649 } 650 if(c == '-') { 651 sign = 1; 652 cons.arg++; 653 c = *cons.arg; 654 } 655 while((c >= '0' && c <= '9') || 656 (base == 16 && c >= 'a' && c <= 'f') || 657 (base == 16 && c >= 'A' && c <= 'F')) { 658 n *= base; 659 if(c >= 'a' && c <= 'f') 660 n += c - 'a' + 10; 661 else 662 if(c >= 'A' && c <= 'F') 663 n += c - 'A' + 10; 664 else 665 n += c - '0'; 666 cons.arg++; 667 c = *cons.arg; 668 any = 1; 669 } 670 if(!any) 671 return d; 672 if(sign) 673 n = -n; 674 return n; 675 } 676