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 int nuser; 30 int len; 31 32 User* ihash[NUserHash]; /* lookup by .uid */ 33 User* nhash[NUserHash]; /* lookup by .uname */ 34 } Ubox; 35 36 static struct { 37 VtLock* lock; 38 39 Ubox* box; 40 } ubox; 41 42 static char usersDefault[] = { 43 "adm:adm:adm:sys\n" 44 "none:none::\n" 45 "noworld:noworld::\n" 46 "sys:sys::glenda\n" 47 "glenda:glenda:glenda:\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 for(; i < g->ngroup; i++) 225 g->group[i] = g->group[i+1]; 226 g->group[i] = nil; /* prevent accidents */ 227 g->group = vtMemRealloc(g->group, g->ngroup * sizeof(char*)); 228 break; 229 } 230 231 return 1; 232 } 233 234 static int 235 _groupAddMember(Ubox* box, User* g, char* member) 236 { 237 User *u; 238 239 if((u = _userByUname(box, member)) == nil) 240 return 0; 241 if(_groupMember(box, g->uid, u->uname, 0)){ 242 if(strcmp(g->uname, member) == 0) 243 vtSetError("uname: '%s' always in own group", member); 244 else 245 vtSetError("uname: '%s' already in group '%s'", 246 member, g->uname); 247 return 0; 248 } 249 250 g->group = vtMemRealloc(g->group, (g->ngroup+1)*sizeof(char*)); 251 g->group[g->ngroup] = vtStrDup(member); 252 box->len += strlen(member); 253 g->ngroup++; 254 if(g->ngroup > 1) 255 box->len++; 256 257 return 1; 258 } 259 260 int 261 groupMember(char* group, char* member) 262 { 263 int r; 264 265 if(group == nil) 266 return 0; 267 268 vtRLock(ubox.lock); 269 r = _groupMember(ubox.box, group, member, 0); 270 vtRUnlock(ubox.lock); 271 272 return r; 273 } 274 275 int 276 groupLeader(char* group, char* member) 277 { 278 int r; 279 User *g; 280 281 /* 282 * Is 'member' the leader of 'group'? 283 * Note that 'group' is a 'uid' and not a 'uname'. 284 * Uname 'none' cannot be a group leader. 285 */ 286 if(strcmp(member, unamenone) == 0 || group == nil) 287 return 0; 288 289 vtRLock(ubox.lock); 290 if((g = _userByUid(ubox.box, group)) == nil){ 291 vtRUnlock(ubox.lock); 292 return 0; 293 } 294 if(g->leader != nil){ 295 if(strcmp(g->leader, member) == 0){ 296 vtRUnlock(ubox.lock); 297 return 1; 298 } 299 r = 0; 300 } 301 else 302 r = _groupMember(ubox.box, group, member, 0); 303 vtRUnlock(ubox.lock); 304 305 return r; 306 } 307 308 static void 309 userFree(User* u) 310 { 311 int i; 312 313 vtMemFree(u->uid); 314 vtMemFree(u->uname); 315 if(u->leader != nil) 316 vtMemFree(u->leader); 317 if(u->ngroup){ 318 for(i = 0; i < u->ngroup; i++) 319 vtMemFree(u->group[i]); 320 vtMemFree(u->group); 321 } 322 vtMemFree(u); 323 } 324 325 static User* 326 userAlloc(char* uid, char* uname) 327 { 328 User *u; 329 330 u = vtMemAllocZ(sizeof(User)); 331 u->uid = vtStrDup(uid); 332 u->uname = vtStrDup(uname); 333 334 return u; 335 } 336 337 int 338 validUserName(char* name) 339 { 340 Rune *r; 341 static Rune invalid[] = L"#:,()"; 342 343 for(r = invalid; *r != '\0'; r++){ 344 if(utfrune(name, *r)) 345 return 0; 346 } 347 return 1; 348 } 349 350 static int 351 userFmt(Fmt* fmt) 352 { 353 User *u; 354 int i, r; 355 356 u = va_arg(fmt->args, User*); 357 358 r = fmtprint(fmt, "%s:%s:", u->uid, u->uname); 359 if(u->leader != nil) 360 r += fmtprint(fmt, u->leader); 361 r += fmtprint(fmt, ":"); 362 if(u->ngroup){ 363 r += fmtprint(fmt, u->group[0]); 364 for(i = 1; i < u->ngroup; i++) 365 r += fmtprint(fmt, ",%s", u->group[i]); 366 } 367 368 return r; 369 } 370 371 static int 372 usersFileWrite(Ubox* box) 373 { 374 Fs *fs; 375 User *u; 376 int i, r; 377 Fsys *fsys; 378 char *p, *q, *s; 379 File *dir, *file; 380 381 if((fsys = fsysGet("main")) == nil) 382 return 0; 383 fsysFsRlock(fsys); 384 fs = fsysGetFs(fsys); 385 386 /* 387 * BUG: 388 * the owner/group/permissions need to be thought out. 389 */ 390 r = 0; 391 if((dir = fileOpen(fs, "/active")) == nil) 392 goto tidy0; 393 if((file = fileWalk(dir, uidadm)) == nil) 394 file = fileCreate(dir, uidadm, ModeDir|0775, uidadm); 395 fileDecRef(dir); 396 if(file == nil) 397 goto tidy; 398 dir = file; 399 if((file = fileWalk(dir, "users")) == nil) 400 file = fileCreate(dir, "users", 0664, uidadm); 401 fileDecRef(dir); 402 if(file == nil) 403 goto tidy; 404 if(!fileTruncate(file, uidadm)) 405 goto tidy; 406 407 p = s = vtMemAlloc(box->len+1); 408 q = p + box->len+1; 409 for(u = box->head; u != nil; u = u->next){ 410 p += snprint(p, q-p, "%s:%s:", u->uid, u->uname); 411 if(u->leader != nil) 412 p+= snprint(p, q-p, u->leader); 413 p += snprint(p, q-p, ":"); 414 if(u->ngroup){ 415 p += snprint(p, q-p, u->group[0]); 416 for(i = 1; i < u->ngroup; i++) 417 p += snprint(p, q-p, ",%s", u->group[i]); 418 } 419 p += snprint(p, q-p, "\n"); 420 } 421 r = fileWrite(file, s, box->len, 0, uidadm); 422 vtMemFree(s); 423 424 tidy: 425 if(file != nil) 426 fileDecRef(file); 427 tidy0: 428 fsysFsRUnlock(fsys); 429 fsysPut(fsys); 430 431 return r; 432 } 433 434 static void 435 uboxRemUser(Ubox* box, User *u) 436 { 437 User **h, *up; 438 439 h = &box->ihash[userHash(u->uid)]; 440 for(up = *h; up != nil && up != u; up = up->ihash) 441 h = &up->ihash; 442 assert(up == u); 443 *h = up->ihash; 444 box->len -= strlen(u->uid); 445 446 h = &box->nhash[userHash(u->uname)]; 447 for(up = *h; up != nil && up != u; up = up->nhash) 448 h = &up->nhash; 449 assert(up == u); 450 *h = up->nhash; 451 box->len -= strlen(u->uname); 452 453 h = &box->head; 454 for(up = *h; up != nil && strcmp(up->uid, u->uid) != 0; up = up->next) 455 h = &up->next; 456 assert(up == u); 457 *h = u->next; 458 u->next = nil; 459 460 box->len -= 4; 461 box->nuser--; 462 } 463 464 static void 465 uboxAddUser(Ubox* box, User* u) 466 { 467 User **h, *up; 468 469 h = &box->ihash[userHash(u->uid)]; 470 u->ihash = *h; 471 *h = u; 472 box->len += strlen(u->uid); 473 474 h = &box->nhash[userHash(u->uname)]; 475 u->nhash = *h; 476 *h = u; 477 box->len += strlen(u->uname); 478 479 h = &box->head; 480 for(up = *h; up != nil && strcmp(up->uid, u->uid) < 0; up = up->next) 481 h = &up->next; 482 u->next = *h; 483 *h = u; 484 485 box->len += 4; 486 box->nuser++; 487 } 488 489 static void 490 uboxDump(Ubox* box) 491 { 492 User* u; 493 494 consPrint("nuser %d len = %d\n", box->nuser, box->len); 495 496 for(u = box->head; u != nil; u = u->next) 497 consPrint("%U\n", u); 498 } 499 500 static void 501 uboxFree(Ubox* box) 502 { 503 User *next, *u; 504 505 for(u = box->head; u != nil; u = next){ 506 next = u->next; 507 userFree(u); 508 } 509 vtMemFree(box); 510 } 511 512 static int 513 uboxInit(char* users, int len) 514 { 515 User *g, *u; 516 Ubox *box, *obox; 517 int blank, comment, i, nline, nuser; 518 char *buf, *f[5], **line, *p, *q, *s; 519 520 /* 521 * Strip out whitespace and comments. 522 * Note that comments are pointless, they disappear 523 * when the server writes the database back out. 524 */ 525 blank = 1; 526 comment = nline = 0; 527 528 s = p = buf = vtMemAlloc(len+1); 529 for(q = users; *q != '\0'; q++){ 530 if(*q == '\r' || *q == '\t' || *q == ' ') 531 continue; 532 if(*q == '\n'){ 533 if(!blank){ 534 if(p != s){ 535 *p++ = '\n'; 536 nline++; 537 s = p; 538 } 539 blank = 1; 540 } 541 comment = 0; 542 continue; 543 } 544 if(*q == '#') 545 comment = 1; 546 blank = 0; 547 if(!comment) 548 *p++ = *q; 549 } 550 *p = '\0'; 551 552 line = vtMemAllocZ((nline+2)*sizeof(char*)); 553 if((i = gettokens(buf, line, nline+2, "\n")) != nline){ 554 fprint(2, "nline %d (%d) botch\n", nline, i); 555 vtMemFree(line); 556 vtMemFree(buf); 557 return 0; 558 } 559 560 /* 561 * Everything is updated in a local Ubox until verified. 562 */ 563 box = vtMemAllocZ(sizeof(Ubox)); 564 565 /* 566 * First pass - check format, check for duplicates 567 * and enter in hash buckets. 568 */ 569 nuser = 0; 570 for(i = 0; i < nline; i++){ 571 s = vtStrDup(line[i]); 572 if(getfields(s, f, nelem(f), 0, ":") != 4){ 573 fprint(2, "bad line '%s'\n", line[i]); 574 vtMemFree(s); 575 continue; 576 } 577 if(*f[0] == '\0' || *f[1] == '\0'){ 578 fprint(2, "bad line '%s'\n", line[i]); 579 vtMemFree(s); 580 continue; 581 } 582 if(!validUserName(f[0])){ 583 fprint(2, "invalid uid '%s'\n", f[0]); 584 vtMemFree(s); 585 continue; 586 } 587 if(_userByUid(box, f[0]) != nil){ 588 fprint(2, "duplicate uid '%s'\n", f[0]); 589 vtMemFree(s); 590 continue; 591 } 592 if(!validUserName(f[1])){ 593 fprint(2, "invalid uname '%s'\n", f[0]); 594 vtMemFree(s); 595 continue; 596 } 597 if(_userByUname(box, f[1]) != nil){ 598 fprint(2, "duplicate uname '%s'\n", f[1]); 599 vtMemFree(s); 600 continue; 601 } 602 603 u = userAlloc(f[0], f[1]); 604 uboxAddUser(box, u); 605 line[nuser] = line[i]; 606 nuser++; 607 608 vtMemFree(s); 609 } 610 assert(box->nuser == nuser); 611 612 /* 613 * Second pass - fill in leader and group information. 614 */ 615 for(i = 0; i < nuser; i++){ 616 s = vtStrDup(line[i]); 617 getfields(s, f, nelem(f), 0, ":"); 618 619 assert(g = _userByUname(box, f[1])); 620 if(*f[2] != '\0'){ 621 if((u = _userByUname(box, f[2])) == nil) 622 g->leader = vtStrDup(g->uname); 623 else 624 g->leader = vtStrDup(u->uname); 625 box->len += strlen(g->leader); 626 } 627 for(p = f[3]; p != nil; p = q){ 628 if((q = utfrune(p, L',')) != nil) 629 *q++ = '\0'; 630 if(!_groupAddMember(box, g, p)){ 631 // print/log error here 632 } 633 } 634 635 vtMemFree(s); 636 } 637 638 vtMemFree(line); 639 vtMemFree(buf); 640 641 for(i = 0; usersMandatory[i] != nil; i++){ 642 if((u = _userByUid(box, usersMandatory[i])) == nil){ 643 vtSetError("user '%s' is mandatory", usersMandatory[i]); 644 uboxFree(box); 645 return 0; 646 } 647 if(strcmp(u->uid, u->uname) != 0){ 648 vtSetError("uid/uname for user '%s' must match", 649 usersMandatory[i]); 650 uboxFree(box); 651 return 0; 652 } 653 } 654 655 vtLock(ubox.lock); 656 obox = ubox.box; 657 ubox.box = box; 658 vtUnlock(ubox.lock); 659 660 if(obox != nil) 661 uboxFree(obox); 662 663 return 1; 664 } 665 666 int 667 usersFileRead(char* path) 668 { 669 char *p; 670 File *file; 671 Fsys *fsys; 672 int len, r; 673 uvlong size; 674 675 if((fsys = fsysGet("main")) == nil) 676 return 0; 677 fsysFsRlock(fsys); 678 679 if(path == nil) 680 path = "/active/adm/users"; 681 682 r = 0; 683 if((file = fileOpen(fsysGetFs(fsys), path)) != nil){ 684 if(fileGetSize(file, &size)){ 685 len = size; 686 p = vtMemAlloc(size+1); 687 if(fileRead(file, p, len, 0) == len){ 688 p[len] = '\0'; 689 r = uboxInit(p, len); 690 } 691 } 692 fileDecRef(file); 693 } 694 695 fsysFsRUnlock(fsys); 696 fsysPut(fsys); 697 698 return r; 699 } 700 701 static int 702 cmdUname(int argc, char* argv[]) 703 { 704 User *u, *up; 705 int d, dflag, i, r; 706 char *p, *uid, *uname; 707 char *createfmt = "fsys main create /active/usr/%s %s %s d775"; 708 char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]"; 709 710 dflag = 0; 711 712 ARGBEGIN{ 713 default: 714 return cliError(usage); 715 case 'd': 716 dflag = 1; 717 break; 718 }ARGEND 719 720 if(argc < 1){ 721 if(!dflag) 722 return cliError(usage); 723 vtRLock(ubox.lock); 724 uboxDump(ubox.box); 725 vtRUnlock(ubox.lock); 726 return 1; 727 } 728 729 uname = argv[0]; 730 argc--; argv++; 731 732 if(argc == 0){ 733 vtRLock(ubox.lock); 734 if((u = _userByUname(ubox.box, uname)) == nil){ 735 vtRUnlock(ubox.lock); 736 return 0; 737 } 738 consPrint("\t%U\n", u); 739 vtRUnlock(ubox.lock); 740 return 1; 741 } 742 743 vtLock(ubox.lock); 744 u = _userByUname(ubox.box, uname); 745 while(argc--){ 746 if(argv[0][0] == '%'){ 747 if(u == nil){ 748 vtUnlock(ubox.lock); 749 return 0; 750 } 751 p = &argv[0][1]; 752 if((up = _userByUname(ubox.box, p)) != nil){ 753 vtSetError("uname: uname '%s' already exists", 754 up->uname); 755 vtUnlock(ubox.lock); 756 return 0; 757 } 758 for(i = 0; usersMandatory[i] != nil; i++){ 759 if(strcmp(usersMandatory[i], uname) != 0) 760 continue; 761 vtSetError("uname: uname '%s' is mandatory", 762 uname); 763 vtUnlock(ubox.lock); 764 return 0; 765 } 766 767 d = strlen(p) - strlen(u->uname); 768 for(up = ubox.box->head; up != nil; up = up->next){ 769 if(up->leader != nil){ 770 if(strcmp(up->leader, u->uname) == 0){ 771 vtMemFree(up->leader); 772 up->leader = vtStrDup(p); 773 ubox.box->len += d; 774 } 775 } 776 for(i = 0; i < up->ngroup; i++){ 777 if(strcmp(up->group[i], u->uname) != 0) 778 continue; 779 vtMemFree(up->group[i]); 780 up->group[i] = vtStrDup(p); 781 ubox.box->len += d; 782 break; 783 } 784 } 785 786 uboxRemUser(ubox.box, u); 787 vtMemFree(u->uname); 788 u->uname = vtStrDup(p); 789 uboxAddUser(ubox.box, u); 790 } 791 else if(argv[0][0] == '='){ 792 if(u == nil){ 793 vtUnlock(ubox.lock); 794 return 0; 795 } 796 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 797 if(argv[0][1] != '\0'){ 798 vtUnlock(ubox.lock); 799 return 0; 800 } 801 } 802 if(u->leader != nil){ 803 ubox.box->len -= strlen(u->leader); 804 vtMemFree(u->leader); 805 u->leader = nil; 806 } 807 if(up != nil){ 808 u->leader = vtStrDup(up->uname); 809 ubox.box->len += strlen(u->leader); 810 } 811 } 812 else if(argv[0][0] == '+'){ 813 if(u == nil){ 814 vtUnlock(ubox.lock); 815 return 0; 816 } 817 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 818 vtUnlock(ubox.lock); 819 return 0; 820 } 821 if(!_groupAddMember(ubox.box, u, up->uname)){ 822 vtUnlock(ubox.lock); 823 return 0; 824 } 825 } 826 else if(argv[0][0] == '-'){ 827 if(u == nil){ 828 vtUnlock(ubox.lock); 829 return 0; 830 } 831 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 832 vtUnlock(ubox.lock); 833 return 0; 834 } 835 if(!_groupRemMember(ubox.box, u, up->uname)){ 836 vtUnlock(ubox.lock); 837 return 0; 838 } 839 } 840 else{ 841 if(u != nil){ 842 vtSetError("uname: uname '%s' already exists", 843 u->uname); 844 vtUnlock(ubox.lock); 845 return 0; 846 } 847 848 uid = argv[0]; 849 if(*uid == ':') 850 uid++; 851 if((u = _userByUid(ubox.box, uid)) != nil){ 852 vtSetError("uname: uid '%s' already exists", 853 u->uid); 854 vtUnlock(ubox.lock); 855 return 0; 856 } 857 858 u = userAlloc(uid, uname); 859 uboxAddUser(ubox.box, u); 860 if(argv[0][0] != ':'){ 861 // should have an option for the mode and gid 862 p = smprint(createfmt, uname, uname, uname); 863 r = cliExec(p); 864 vtMemFree(p); 865 if(r == 0){ 866 vtUnlock(ubox.lock); 867 return 0; 868 } 869 } 870 } 871 argv++; 872 } 873 874 if(usersFileWrite(ubox.box) == 0){ 875 vtUnlock(ubox.lock); 876 return 0; 877 } 878 if(dflag) 879 uboxDump(ubox.box); 880 vtUnlock(ubox.lock); 881 882 return 1; 883 } 884 885 static int 886 cmdUsers(int argc, char* argv[]) 887 { 888 Ubox *box; 889 int dflag, r, wflag; 890 char *file; 891 char *usage = "usage: users [-d | -r file] [-w]"; 892 893 dflag = wflag = 0; 894 file = nil; 895 896 ARGBEGIN{ 897 default: 898 return cliError(usage); 899 case 'd': 900 dflag = 1; 901 break; 902 case 'r': 903 file = ARGF(); 904 if(file == nil) 905 return cliError(usage); 906 break; 907 case 'w': 908 wflag = 1; 909 break; 910 }ARGEND 911 912 if(argc) 913 return cliError(usage); 914 915 if(dflag && file) 916 return cliError("cannot use -d and -r together"); 917 918 if(dflag) 919 uboxInit(usersDefault, sizeof(usersDefault)); 920 else if(file){ 921 if(usersFileRead(file) == 0) 922 return 0; 923 } 924 925 vtRLock(ubox.lock); 926 box = ubox.box; 927 consPrint("\tnuser %d len %d\n", box->nuser, box->len); 928 929 r = 1; 930 if(wflag) 931 r = usersFileWrite(box); 932 vtRUnlock(ubox.lock); 933 return r; 934 } 935 936 int 937 usersInit(void) 938 { 939 fmtinstall('U', userFmt); 940 941 ubox.lock = vtLockAlloc(); 942 uboxInit(usersDefault, sizeof(usersDefault)); 943 944 cliAddCmd("users", cmdUsers); 945 cliAddCmd("uname", cmdUname); 946 947 return 1; 948 } 949