1 #include "stdinc.h" 2 3 #include "9.h" 4 5 enum { 6 NUserHash = 1009, 7 }; 8 9 typedef struct Ubox Ubox; 10 typedef struct User User; 11 12 typedef struct User { 13 char* uid; 14 char* uname; 15 char* leader; 16 char** group; 17 int ngroup; 18 19 User* next; /* */ 20 User* ihash; /* lookup by .uid */ 21 User* nhash; /* lookup by .uname */ 22 } User; 23 24 #pragma varargck type "U" User* 25 26 typedef struct Ubox { 27 User* head; 28 User* tail; 29 char* name; 30 int nuser; 31 int len; 32 33 User* ihash[NUserHash]; /* lookup by .uid */ 34 User* nhash[NUserHash]; /* lookup by .uname */ 35 } Ubox; 36 37 static struct { 38 VtLock* lock; 39 40 Ubox* box; 41 } ubox; 42 43 static char usersDefault[] = { 44 "adm:adm:adm:sys\n" 45 "none:none::\n" 46 "noworld:noworld::\n" 47 "sys:sys::\n" 48 }; 49 50 static char* usersMandatory[] = { 51 "adm", 52 "none", 53 "noworld", 54 "sys", 55 nil, 56 }; 57 58 char* uidadm = "adm"; 59 char* unamenone = "none"; 60 char* uidnoworld = "noworld"; 61 62 static u32int 63 userHash(char* s) 64 { 65 uchar *p; 66 u32int hash; 67 68 hash = 0; 69 for(p = (uchar*)s; *p != '\0'; p++) 70 hash = hash*7 + *p; 71 72 return hash % NUserHash; 73 } 74 75 static User* 76 _userByUid(Ubox* box, char* uid) 77 { 78 User *u; 79 80 if(box != nil){ 81 for(u = box->ihash[userHash(uid)]; u != nil; u = u->ihash){ 82 if(strcmp(u->uid, uid) == 0) 83 return u; 84 } 85 } 86 vtSetError("uname: uid '%s' not found", uid); 87 return nil; 88 } 89 90 char* 91 unameByUid(char* uid) 92 { 93 User *u; 94 char *uname; 95 96 vtRLock(ubox.lock); 97 if((u = _userByUid(ubox.box, uid)) == nil){ 98 vtRUnlock(ubox.lock); 99 return nil; 100 } 101 uname = vtStrDup(u->uname); 102 vtRUnlock(ubox.lock); 103 104 return uname; 105 } 106 107 static User* 108 _userByUname(Ubox* box, char* uname) 109 { 110 User *u; 111 112 if(box != nil){ 113 for(u = box->nhash[userHash(uname)]; u != nil; u = u->nhash){ 114 if(strcmp(u->uname, uname) == 0) 115 return u; 116 } 117 } 118 vtSetError("uname: uname '%s' not found", uname); 119 return nil; 120 } 121 122 char* 123 uidByUname(char* uname) 124 { 125 User *u; 126 char *uid; 127 128 vtRLock(ubox.lock); 129 if((u = _userByUname(ubox.box, uname)) == nil){ 130 vtRUnlock(ubox.lock); 131 return nil; 132 } 133 uid = vtStrDup(u->uid); 134 vtRUnlock(ubox.lock); 135 136 return uid; 137 } 138 139 static int 140 _groupMember(Ubox* box, char* group, char* member, int whenNoGroup) 141 { 142 int i; 143 User *g, *m; 144 145 /* 146 * Is 'member' a member of 'group'? 147 * Note that 'group' is a 'uid' and not a 'uname'. 148 * A 'member' is automatically in their own group. 149 */ 150 if((g = _userByUid(box, group)) == nil) 151 return whenNoGroup; 152 if((m = _userByUname(box, member)) == nil) 153 return 0; 154 if(m == g) 155 return 1; 156 for(i = 0; i < g->ngroup; i++){ 157 if(strcmp(g->group[i], member) == 0) 158 return 1; 159 } 160 return 0; 161 } 162 163 int 164 groupWriteMember(char* uname) 165 { 166 int ret; 167 168 /* 169 * If there is a ``write'' group, then only its members can write 170 * to the file system, no matter what the permission bits say. 171 * 172 * To users not in the ``write'' group, the file system appears 173 * read only. This is used to serve sources.cs.bell-labs.com 174 * to the world. 175 * 176 * Note that if there is no ``write'' group, then this routine 177 * makes it look like everyone is a member -- the opposite 178 * of what groupMember does. 179 * 180 * We use this for sources.cs.bell-labs.com. 181 * If this slows things down too much on systems that don't 182 * use this functionality, we could cache the write group lookup. 183 */ 184 185 vtRLock(ubox.lock); 186 ret = _groupMember(ubox.box, "write", uname, 1); 187 vtRUnlock(ubox.lock); 188 return ret; 189 } 190 191 static int 192 _groupRemMember(Ubox* box, User* g, char* member) 193 { 194 int i; 195 196 if(_userByUname(box, member) == nil) 197 return 0; 198 199 for(i = 0; i < g->ngroup; i++){ 200 if(strcmp(g->group[i], member) == 0) 201 break; 202 } 203 if(i >= g->ngroup){ 204 if(strcmp(g->uname, member) == 0) 205 vtSetError("uname: '%s' always in own group", member); 206 else 207 vtSetError("uname: '%s' not in group '%s'", 208 member, g->uname); 209 return 0; 210 } 211 212 vtMemFree(g->group[i]); 213 214 box->len -= strlen(member); 215 if(g->ngroup > 1) 216 box->len--; 217 g->ngroup--; 218 switch(g->ngroup){ 219 case 0: 220 vtMemFree(g->group); 221 g->group = nil; 222 break; 223 default: 224 while(i < g->ngroup){ 225 g->group[i] = g->group[i+1]; 226 i++; 227 } 228 /*FALLTHROUGH*/ 229 case 1: 230 g->group = vtMemRealloc(g->group, (g->ngroup)*sizeof(char*)); 231 break; 232 } 233 234 return 1; 235 } 236 237 static int 238 _groupAddMember(Ubox* box, User* g, char* member) 239 { 240 User *u; 241 242 if((u = _userByUname(box, member)) == nil) 243 return 0; 244 if(_groupMember(box, g->uid, u->uname, 0)){ 245 if(strcmp(g->uname, member) == 0) 246 vtSetError("uname: '%s' always in own group", member); 247 else 248 vtSetError("uname: '%s' already in group '%s'", 249 member, g->uname); 250 return 0; 251 } 252 253 g->group = vtMemRealloc(g->group, (g->ngroup+1)*sizeof(char*)); 254 g->group[g->ngroup] = vtStrDup(member); 255 box->len += strlen(member); 256 g->ngroup++; 257 if(g->ngroup > 1) 258 box->len++; 259 260 return 1; 261 } 262 263 int 264 groupMember(char* group, char* member) 265 { 266 int r; 267 268 if(group == nil) 269 return 0; 270 271 vtRLock(ubox.lock); 272 r = _groupMember(ubox.box, group, member, 0); 273 vtRUnlock(ubox.lock); 274 275 return r; 276 } 277 278 int 279 groupLeader(char* group, char* member) 280 { 281 int r; 282 User *g; 283 284 /* 285 * Is 'member' the leader of 'group'? 286 * Note that 'group' is a 'uid' and not a 'uname'. 287 * Uname 'none' cannot be a group leader. 288 */ 289 if(strcmp(member, unamenone) == 0 || group == nil) 290 return 0; 291 292 vtRLock(ubox.lock); 293 if((g = _userByUid(ubox.box, group)) == nil){ 294 vtRUnlock(ubox.lock); 295 return 0; 296 } 297 if(g->leader != nil){ 298 if(strcmp(g->leader, member) == 0){ 299 vtRUnlock(ubox.lock); 300 return 1; 301 } 302 r = 0; 303 } 304 else 305 r = _groupMember(ubox.box, group, member, 0); 306 vtRUnlock(ubox.lock); 307 308 return r; 309 } 310 311 static void 312 userFree(User* u) 313 { 314 int i; 315 316 vtMemFree(u->uid); 317 vtMemFree(u->uname); 318 if(u->leader != nil) 319 vtMemFree(u->leader); 320 if(u->ngroup){ 321 for(i = 0; i < u->ngroup; i++) 322 vtMemFree(u->group[i]); 323 vtMemFree(u->group); 324 } 325 vtMemFree(u); 326 } 327 328 static User* 329 userAlloc(char* uid, char* uname) 330 { 331 User *u; 332 333 u = vtMemAllocZ(sizeof(User)); 334 u->uid = vtStrDup(uid); 335 u->uname = vtStrDup(uname); 336 337 return u; 338 } 339 340 int 341 validUserName(char* name) 342 { 343 Rune *r; 344 static Rune invalid[] = L"#:,()"; 345 346 for(r = invalid; *r != '\0'; r++){ 347 if(utfrune(name, *r)) 348 return 0; 349 } 350 return 1; 351 } 352 353 static int 354 userFmt(Fmt* fmt) 355 { 356 User *u; 357 int i, r; 358 359 u = va_arg(fmt->args, User*); 360 361 r = fmtprint(fmt, "%s:%s:", u->uid, u->uname); 362 if(u->leader != nil) 363 r += fmtprint(fmt, u->leader); 364 r += fmtprint(fmt, ":"); 365 if(u->ngroup){ 366 r += fmtprint(fmt, u->group[0]); 367 for(i = 1; i < u->ngroup; i++) 368 r += fmtprint(fmt, ",%s", u->group[i]); 369 } 370 371 return r; 372 } 373 374 static int 375 usersFileWrite(Ubox* box) 376 { 377 Fs *fs; 378 User *u; 379 int i, r; 380 Fsys *fsys; 381 char *p, *q, *s; 382 File *dir, *file; 383 384 if((fsys = fsysGet("main")) == nil) 385 return 0; 386 fsysFsRlock(fsys); 387 fs = fsysGetFs(fsys); 388 389 /* 390 * BUG: 391 * the owner/group/permissions need to be thought out. 392 */ 393 r = 0; 394 if((dir = fileOpen(fs, "/active")) == nil) 395 goto tidy0; 396 if((file = fileWalk(dir, "adm")) == nil) 397 file = fileCreate(dir, "adm", ModeDir|0775, uidadm); 398 fileDecRef(dir); 399 if(file == nil) 400 goto tidy; 401 dir = file; 402 if((file = fileWalk(dir, "users")) == nil) 403 file = fileCreate(dir, "users", 0664, uidadm); 404 fileDecRef(dir); 405 if(file == nil) 406 goto tidy; 407 if(!fileTruncate(file, uidadm)) 408 goto tidy; 409 410 p = s = vtMemAlloc(box->len+1); 411 q = p + box->len+1; 412 for(u = box->head; u != nil; u = u->next){ 413 p += snprint(p, q-p, "%s:%s:", u->uid, u->uname); 414 if(u->leader != nil) 415 p+= snprint(p, q-p, u->leader); 416 p += snprint(p, q-p, ":"); 417 if(u->ngroup){ 418 p += snprint(p, q-p, u->group[0]); 419 for(i = 1; i < u->ngroup; i++) 420 p += snprint(p, q-p, ",%s", u->group[i]); 421 } 422 p += snprint(p, q-p, "\n"); 423 } 424 r = fileWrite(file, s, box->len, 0, uidadm); 425 vtMemFree(s); 426 427 tidy: 428 if(file != nil) 429 fileDecRef(file); 430 tidy0: 431 fsysFsRUnlock(fsys); 432 fsysPut(fsys); 433 434 return r; 435 } 436 437 static void 438 uboxRemUser(Ubox* box, User *u) 439 { 440 User **h, *up; 441 442 h = &box->ihash[userHash(u->uid)]; 443 for(up = *h; up != nil && up != u; up = up->ihash) 444 h = &up->ihash; 445 assert(up == u); 446 *h = up->ihash; 447 box->len -= strlen(u->uid); 448 449 h = &box->nhash[userHash(u->uname)]; 450 for(up = *h; up != nil && up != u; up = up->nhash) 451 h = &up->nhash; 452 assert(up == u); 453 *h = up->nhash; 454 box->len -= strlen(u->uname); 455 456 h = &box->head; 457 for(up = *h; up != nil && strcmp(up->uid, u->uid) != 0; up = up->next) 458 h = &up->next; 459 assert(up == u); 460 *h = u->next; 461 u->next = nil; 462 463 box->len -= 4; 464 box->nuser--; 465 } 466 467 static void 468 uboxAddUser(Ubox* box, User* u) 469 { 470 User **h, *up; 471 472 h = &box->ihash[userHash(u->uid)]; 473 u->ihash = *h; 474 *h = u; 475 box->len += strlen(u->uid); 476 477 h = &box->nhash[userHash(u->uname)]; 478 u->nhash = *h; 479 *h = u; 480 box->len += strlen(u->uname); 481 482 h = &box->head; 483 for(up = *h; up != nil && strcmp(up->uid, u->uid) < 0; up = up->next) 484 h = &up->next; 485 u->next = *h; 486 *h = u; 487 488 box->len += 4; 489 box->nuser++; 490 } 491 492 static void 493 uboxDump(Ubox* box) 494 { 495 User* u; 496 497 consPrint("nuser %d len = %d\n", box->nuser, box->len); 498 499 for(u = box->head; u != nil; u = u->next) 500 consPrint("%U\n", u); 501 } 502 503 static void 504 uboxFree(Ubox* box) 505 { 506 User *next, *u; 507 508 for(u = box->head; u != nil; u = next){ 509 next = u->next; 510 userFree(u); 511 } 512 if(box->name != nil) 513 vtMemFree(box->name); 514 vtMemFree(box); 515 } 516 517 static int 518 uboxInit(char* name, char* users, int len) 519 { 520 User *g, *u; 521 Ubox *box, *obox; 522 int blank, comment, i, nuser; 523 char *buf, *f[5], **line, *p, *q, *s; 524 525 /* 526 * Strip out whitespace and comments. 527 * Note that comments are pointless, they disappear 528 * when the server writes the database back out. 529 */ 530 blank = 1; 531 comment = nuser = 0; 532 533 s = p = buf = vtMemAlloc(len+1); 534 for(q = users; *q != '\0'; q++){ 535 if(*q == '\r' || *q == '\t' || *q == ' ') 536 continue; 537 if(*q == '\n'){ 538 if(!blank){ 539 if(p != s){ 540 *p++ = '\n'; 541 nuser++; 542 s = p; 543 } 544 blank = 1; 545 } 546 comment = 0; 547 continue; 548 } 549 if(*q == '#') 550 comment = 1; 551 blank = 0; 552 if(!comment) 553 *p++ = *q; 554 } 555 *p = '\0'; 556 557 line = vtMemAllocZ((nuser+2)*sizeof(char*)); 558 if((i = gettokens(buf, line, nuser+2, "\n")) != nuser){ 559 fprint(2, "nuser %d (%d) botch\n", nuser, i); 560 vtMemFree(line); 561 vtMemFree(buf); 562 return 0; 563 } 564 565 fprint(2, "nuser %d\n", nuser); 566 567 /* 568 * Everything us updated in a local Ubox until verified. 569 */ 570 box = vtMemAllocZ(sizeof(Ubox)); 571 if(name != nil) 572 box->name = vtStrDup(name); 573 574 /* 575 * First pass - check format, check for duplicates 576 * and enter in hash buckets. 577 */ 578 for(i = 0; i < nuser; i++){ 579 s = vtStrDup(line[i]); 580 if(getfields(s, f, nelem(f), 0, ":") != 4){ 581 fprint(2, "bad line '%s'\n", line[i]); 582 vtMemFree(s); 583 continue; 584 } 585 if(*f[0] == '\0' || *f[1] == '\0'){ 586 fprint(2, "bad line '%s'\n", line[i]); 587 vtMemFree(s); 588 continue; 589 } 590 if(!validUserName(f[0])){ 591 fprint(2, "invalid uid '%s'\n", f[0]); 592 vtMemFree(s); 593 continue; 594 } 595 if(_userByUid(box, f[0]) != nil){ 596 fprint(2, "duplicate uid '%s'\n", f[0]); 597 vtMemFree(s); 598 continue; 599 } 600 if(!validUserName(f[1])){ 601 fprint(2, "invalid uname '%s'\n", f[0]); 602 vtMemFree(s); 603 continue; 604 } 605 if(_userByUname(box, f[1]) != nil){ 606 fprint(2, "duplicate uname '%s'\n", f[1]); 607 vtMemFree(s); 608 continue; 609 } 610 611 u = userAlloc(f[0], f[1]); 612 uboxAddUser(box, u); 613 614 vtMemFree(s); 615 } 616 assert(box->nuser == nuser); 617 618 /* 619 * Second pass - fill in leader and group information. 620 */ 621 for(i = 0; i < nuser; i++){ 622 s = vtStrDup(line[i]); 623 getfields(s, f, nelem(f), 0, ":"); 624 625 assert(g = _userByUname(box, f[1])); 626 if(*f[2] != '\0'){ 627 if((u = _userByUname(box, f[2])) == nil) 628 g->leader = vtStrDup(g->uname); 629 else 630 g->leader = vtStrDup(u->uname); 631 box->len += strlen(g->leader); 632 } 633 for(p = f[3]; p != nil; p = q){ 634 if((q = utfrune(p, L',')) != nil) 635 *q++ = '\0'; 636 if(!_groupAddMember(box, g, p)){ 637 // print/log error here 638 } 639 } 640 641 vtMemFree(s); 642 } 643 644 vtMemFree(line); 645 vtMemFree(buf); 646 647 for(i = 0; usersMandatory[i] != nil; i++){ 648 if((u = _userByUid(box, usersMandatory[i])) == nil){ 649 vtSetError("user '%s' is mandatory", usersMandatory[i]); 650 uboxFree(box); 651 return 0; 652 } 653 if(strcmp(u->uid, u->uname) != 0){ 654 vtSetError("uid/uname for user '%s' must match", 655 usersMandatory[i]); 656 uboxFree(box); 657 return 0; 658 } 659 } 660 661 vtLock(ubox.lock); 662 if(name != nil && usersFileWrite(box) == 0){ 663 /* 664 * What to do here? How much whining? 665 */ 666 } 667 obox = ubox.box; 668 ubox.box = box; 669 vtUnlock(ubox.lock); 670 671 if(obox != nil) 672 uboxFree(obox); 673 674 return 1; 675 } 676 677 static int 678 usersFileRead(char* path) 679 { 680 char *p; 681 File *file; 682 Fsys *fsys; 683 int len, r; 684 uvlong size; 685 686 if((fsys = fsysGet("main")) == nil) 687 return 0; 688 fsysFsRlock(fsys); 689 690 r = 0; 691 if((file = fileOpen(fsysGetFs(fsys), path)) != nil){ 692 if(fileGetSize(file, &size)){ 693 len = size; 694 p = vtMemAlloc(size+1); 695 if(fileRead(file, p, len, 0) == len){ 696 p[len] = '\0'; 697 r = uboxInit(path, p, len); 698 } 699 } 700 fileDecRef(file); 701 } 702 703 fsysFsRUnlock(fsys); 704 fsysPut(fsys); 705 706 return r; 707 } 708 709 static int 710 cmdUname(int argc, char* argv[]) 711 { 712 User *u, *up; 713 int d, dflag, i, r; 714 char *p, *uid, *uname; 715 char *createfmt = "fsys main create /active/usr/%s %s %s d775"; 716 char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]"; 717 718 dflag = 0; 719 720 ARGBEGIN{ 721 default: 722 return cliError(usage); 723 case 'd': 724 dflag = 1; 725 break; 726 }ARGEND 727 728 if(argc < 1){ 729 if(!dflag) 730 return cliError(usage); 731 vtRLock(ubox.lock); 732 uboxDump(ubox.box); 733 vtRUnlock(ubox.lock); 734 return 1; 735 } 736 737 uname = argv[0]; 738 argc--; argv++; 739 740 if(argc == 0){ 741 vtRLock(ubox.lock); 742 if((u = _userByUname(ubox.box, uname)) == nil){ 743 vtRUnlock(ubox.lock); 744 return 0; 745 } 746 consPrint("\t%U\n", u); 747 vtRUnlock(ubox.lock); 748 return 1; 749 } 750 751 vtLock(ubox.lock); 752 u = _userByUname(ubox.box, uname); 753 while(argc--){ 754 if(argv[0][0] == '%'){ 755 if(u == nil){ 756 vtUnlock(ubox.lock); 757 return 0; 758 } 759 p = &argv[0][1]; 760 if((up = _userByUname(ubox.box, p)) != nil){ 761 vtSetError("uname: uname '%s' already exists", 762 up->uname); 763 vtUnlock(ubox.lock); 764 return 0; 765 } 766 for(i = 0; usersMandatory[i] != nil; i++){ 767 if(strcmp(usersMandatory[i], uname) != 0) 768 continue; 769 vtSetError("uname: uname '%s' is mandatory", 770 uname); 771 vtUnlock(ubox.lock); 772 return 0; 773 } 774 775 d = strlen(p) - strlen(u->uname); 776 for(up = ubox.box->head; up != nil; up = up->next){ 777 if(up->leader != nil){ 778 if(strcmp(up->leader, u->uname) == 0){ 779 vtMemFree(up->leader); 780 up->leader = vtStrDup(p); 781 ubox.box->len += d; 782 } 783 } 784 for(i = 0; i < up->ngroup; i++){ 785 if(strcmp(up->group[i], u->uname) != 0) 786 continue; 787 vtMemFree(up->group[i]); 788 up->group[i] = vtStrDup(p); 789 ubox.box->len += d; 790 break; 791 } 792 } 793 794 uboxRemUser(ubox.box, u); 795 vtMemFree(u->uname); 796 u->uname = vtStrDup(p); 797 uboxAddUser(ubox.box, u); 798 } 799 else if(argv[0][0] == '='){ 800 if(u == nil){ 801 vtUnlock(ubox.lock); 802 return 0; 803 } 804 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 805 if(argv[0][1] != '\0'){ 806 vtUnlock(ubox.lock); 807 return 0; 808 } 809 } 810 if(u->leader != nil){ 811 ubox.box->len -= strlen(u->leader); 812 vtMemFree(u->leader); 813 u->leader = nil; 814 } 815 if(up != nil){ 816 u->leader = vtStrDup(up->uname); 817 ubox.box->len += strlen(u->leader); 818 } 819 } 820 else if(argv[0][0] == '+'){ 821 if(u == nil){ 822 vtUnlock(ubox.lock); 823 return 0; 824 } 825 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 826 vtUnlock(ubox.lock); 827 return 0; 828 } 829 if(!_groupAddMember(ubox.box, u, up->uname)){ 830 vtUnlock(ubox.lock); 831 return 0; 832 } 833 } 834 else if(argv[0][0] == '-'){ 835 if(u == nil){ 836 vtUnlock(ubox.lock); 837 return 0; 838 } 839 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 840 vtUnlock(ubox.lock); 841 return 0; 842 } 843 if(!_groupRemMember(ubox.box, u, up->uname)){ 844 vtUnlock(ubox.lock); 845 return 0; 846 } 847 } 848 else{ 849 if(u != nil){ 850 vtSetError("uname: uname '%s' already exists", 851 u->uname); 852 vtUnlock(ubox.lock); 853 return 0; 854 } 855 856 uid = argv[0]; 857 if(*uid == ':') 858 uid++; 859 if((u = _userByUid(ubox.box, uid)) != nil){ 860 vtSetError("uname: uid '%s' already exists", 861 u->uid); 862 vtUnlock(ubox.lock); 863 return 0; 864 } 865 866 u = userAlloc(uid, uname); 867 uboxAddUser(ubox.box, u); 868 if(argv[0][0] != ':'){ 869 // should have an option for the mode and gid 870 p = smprint(createfmt, uname, uname, uname); 871 r = cliExec(p); 872 vtMemFree(p); 873 if(r != 0){ 874 vtUnlock(ubox.lock); 875 return 0; 876 } 877 } 878 } 879 argv++; 880 } 881 882 if(usersFileWrite(ubox.box) == 0){ 883 vtUnlock(ubox.lock); 884 return 0; 885 } 886 if(dflag) 887 uboxDump(ubox.box); 888 vtUnlock(ubox.lock); 889 890 return 1; 891 } 892 893 static int 894 cmdUsers(int argc, char* argv[]) 895 { 896 Ubox *box; 897 int dflag, r, wflag; 898 char *usage = "usage: users [-dw] [file]"; 899 900 dflag = wflag = 0; 901 902 ARGBEGIN{ 903 default: 904 return cliError(usage); 905 case 'd': 906 dflag = 1; 907 break; 908 case 'w': 909 wflag = 1; 910 break; 911 }ARGEND 912 913 switch(argc){ 914 default: 915 return cliError(usage); 916 case 0: 917 if(dflag) 918 uboxInit(nil, usersDefault, sizeof(usersDefault)); 919 vtRLock(ubox.lock); 920 box = ubox.box; 921 if(box->name != nil) 922 consPrint("\tfile %s\n", box->name); 923 else 924 consPrint("\tno file\n"); 925 consPrint("\tnuser %d len %d\n", box->nuser, box->len); 926 vtRUnlock(ubox.lock); 927 break; 928 case 1: 929 if(dflag) 930 return cliError(usage); 931 if(usersFileRead(argv[0]) == 0) 932 return 0; 933 break; 934 } 935 936 if(wflag){ 937 vtRLock(ubox.lock); 938 r = usersFileWrite(ubox.box); 939 vtRUnlock(ubox.lock); 940 return r; 941 } 942 943 return 1; 944 } 945 946 int 947 usersInit(void) 948 { 949 fmtinstall('U', userFmt); 950 951 ubox.lock = vtLockAlloc(); 952 uboxInit(nil, usersDefault, sizeof(usersDefault)); 953 954 cliAddCmd("users", cmdUsers); 955 cliAddCmd("uname", cmdUname); 956 957 return 1; 958 } 959