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::\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 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, uidadm)) == nil) 397 file = fileCreate(dir, uidadm, 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 vtMemFree(box); 513 } 514 515 static int 516 uboxInit(char* users, int len) 517 { 518 User *g, *u; 519 Ubox *box, *obox; 520 int blank, comment, i, nline, nuser; 521 char *buf, *f[5], **line, *p, *q, *s; 522 523 /* 524 * Strip out whitespace and comments. 525 * Note that comments are pointless, they disappear 526 * when the server writes the database back out. 527 */ 528 blank = 1; 529 comment = nline = 0; 530 531 s = p = buf = vtMemAlloc(len+1); 532 for(q = users; *q != '\0'; q++){ 533 if(*q == '\r' || *q == '\t' || *q == ' ') 534 continue; 535 if(*q == '\n'){ 536 if(!blank){ 537 if(p != s){ 538 *p++ = '\n'; 539 nline++; 540 s = p; 541 } 542 blank = 1; 543 } 544 comment = 0; 545 continue; 546 } 547 if(*q == '#') 548 comment = 1; 549 blank = 0; 550 if(!comment) 551 *p++ = *q; 552 } 553 *p = '\0'; 554 555 line = vtMemAllocZ((nline+2)*sizeof(char*)); 556 if((i = gettokens(buf, line, nline+2, "\n")) != nline){ 557 fprint(2, "nline %d (%d) botch\n", nline, i); 558 vtMemFree(line); 559 vtMemFree(buf); 560 return 0; 561 } 562 563 /* 564 * Everything is updated in a local Ubox until verified. 565 */ 566 box = vtMemAllocZ(sizeof(Ubox)); 567 568 /* 569 * First pass - check format, check for duplicates 570 * and enter in hash buckets. 571 */ 572 nuser = 0; 573 for(i = 0; i < nline; i++){ 574 s = vtStrDup(line[i]); 575 if(getfields(s, f, nelem(f), 0, ":") != 4){ 576 fprint(2, "bad line '%s'\n", line[i]); 577 vtMemFree(s); 578 continue; 579 } 580 if(*f[0] == '\0' || *f[1] == '\0'){ 581 fprint(2, "bad line '%s'\n", line[i]); 582 vtMemFree(s); 583 continue; 584 } 585 if(!validUserName(f[0])){ 586 fprint(2, "invalid uid '%s'\n", f[0]); 587 vtMemFree(s); 588 continue; 589 } 590 if(_userByUid(box, f[0]) != nil){ 591 fprint(2, "duplicate uid '%s'\n", f[0]); 592 vtMemFree(s); 593 continue; 594 } 595 if(!validUserName(f[1])){ 596 fprint(2, "invalid uname '%s'\n", f[0]); 597 vtMemFree(s); 598 continue; 599 } 600 if(_userByUname(box, f[1]) != nil){ 601 fprint(2, "duplicate uname '%s'\n", f[1]); 602 vtMemFree(s); 603 continue; 604 } 605 606 u = userAlloc(f[0], f[1]); 607 uboxAddUser(box, u); 608 line[nuser] = line[i]; 609 nuser++; 610 611 vtMemFree(s); 612 } 613 assert(box->nuser == nuser); 614 615 /* 616 * Second pass - fill in leader and group information. 617 */ 618 for(i = 0; i < nuser; i++){ 619 s = vtStrDup(line[i]); 620 getfields(s, f, nelem(f), 0, ":"); 621 622 assert(g = _userByUname(box, f[1])); 623 if(*f[2] != '\0'){ 624 if((u = _userByUname(box, f[2])) == nil) 625 g->leader = vtStrDup(g->uname); 626 else 627 g->leader = vtStrDup(u->uname); 628 box->len += strlen(g->leader); 629 } 630 for(p = f[3]; p != nil; p = q){ 631 if((q = utfrune(p, L',')) != nil) 632 *q++ = '\0'; 633 if(!_groupAddMember(box, g, p)){ 634 // print/log error here 635 } 636 } 637 638 vtMemFree(s); 639 } 640 641 vtMemFree(line); 642 vtMemFree(buf); 643 644 for(i = 0; usersMandatory[i] != nil; i++){ 645 if((u = _userByUid(box, usersMandatory[i])) == nil){ 646 vtSetError("user '%s' is mandatory", usersMandatory[i]); 647 uboxFree(box); 648 return 0; 649 } 650 if(strcmp(u->uid, u->uname) != 0){ 651 vtSetError("uid/uname for user '%s' must match", 652 usersMandatory[i]); 653 uboxFree(box); 654 return 0; 655 } 656 } 657 658 vtLock(ubox.lock); 659 obox = ubox.box; 660 ubox.box = box; 661 vtUnlock(ubox.lock); 662 663 if(obox != nil) 664 uboxFree(obox); 665 666 return 1; 667 } 668 669 int 670 usersFileRead(char* path) 671 { 672 char *p; 673 File *file; 674 Fsys *fsys; 675 int len, r; 676 uvlong size; 677 678 if((fsys = fsysGet("main")) == nil) 679 return 0; 680 fsysFsRlock(fsys); 681 682 if(path == nil) 683 path = "/active/adm/users"; 684 685 r = 0; 686 if((file = fileOpen(fsysGetFs(fsys), path)) != nil){ 687 if(fileGetSize(file, &size)){ 688 len = size; 689 p = vtMemAlloc(size+1); 690 if(fileRead(file, p, len, 0) == len){ 691 p[len] = '\0'; 692 r = uboxInit(p, len); 693 } 694 } 695 fileDecRef(file); 696 } 697 698 fsysFsRUnlock(fsys); 699 fsysPut(fsys); 700 701 return r; 702 } 703 704 static int 705 cmdUname(int argc, char* argv[]) 706 { 707 User *u, *up; 708 int d, dflag, i, r; 709 char *p, *uid, *uname; 710 char *createfmt = "fsys main create /active/usr/%s %s %s d775"; 711 char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]"; 712 713 dflag = 0; 714 715 ARGBEGIN{ 716 default: 717 return cliError(usage); 718 case 'd': 719 dflag = 1; 720 break; 721 }ARGEND 722 723 if(argc < 1){ 724 if(!dflag) 725 return cliError(usage); 726 vtRLock(ubox.lock); 727 uboxDump(ubox.box); 728 vtRUnlock(ubox.lock); 729 return 1; 730 } 731 732 uname = argv[0]; 733 argc--; argv++; 734 735 if(argc == 0){ 736 vtRLock(ubox.lock); 737 if((u = _userByUname(ubox.box, uname)) == nil){ 738 vtRUnlock(ubox.lock); 739 return 0; 740 } 741 consPrint("\t%U\n", u); 742 vtRUnlock(ubox.lock); 743 return 1; 744 } 745 746 vtLock(ubox.lock); 747 u = _userByUname(ubox.box, uname); 748 while(argc--){ 749 if(argv[0][0] == '%'){ 750 if(u == nil){ 751 vtUnlock(ubox.lock); 752 return 0; 753 } 754 p = &argv[0][1]; 755 if((up = _userByUname(ubox.box, p)) != nil){ 756 vtSetError("uname: uname '%s' already exists", 757 up->uname); 758 vtUnlock(ubox.lock); 759 return 0; 760 } 761 for(i = 0; usersMandatory[i] != nil; i++){ 762 if(strcmp(usersMandatory[i], uname) != 0) 763 continue; 764 vtSetError("uname: uname '%s' is mandatory", 765 uname); 766 vtUnlock(ubox.lock); 767 return 0; 768 } 769 770 d = strlen(p) - strlen(u->uname); 771 for(up = ubox.box->head; up != nil; up = up->next){ 772 if(up->leader != nil){ 773 if(strcmp(up->leader, u->uname) == 0){ 774 vtMemFree(up->leader); 775 up->leader = vtStrDup(p); 776 ubox.box->len += d; 777 } 778 } 779 for(i = 0; i < up->ngroup; i++){ 780 if(strcmp(up->group[i], u->uname) != 0) 781 continue; 782 vtMemFree(up->group[i]); 783 up->group[i] = vtStrDup(p); 784 ubox.box->len += d; 785 break; 786 } 787 } 788 789 uboxRemUser(ubox.box, u); 790 vtMemFree(u->uname); 791 u->uname = vtStrDup(p); 792 uboxAddUser(ubox.box, u); 793 } 794 else if(argv[0][0] == '='){ 795 if(u == nil){ 796 vtUnlock(ubox.lock); 797 return 0; 798 } 799 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 800 if(argv[0][1] != '\0'){ 801 vtUnlock(ubox.lock); 802 return 0; 803 } 804 } 805 if(u->leader != nil){ 806 ubox.box->len -= strlen(u->leader); 807 vtMemFree(u->leader); 808 u->leader = nil; 809 } 810 if(up != nil){ 811 u->leader = vtStrDup(up->uname); 812 ubox.box->len += strlen(u->leader); 813 } 814 } 815 else if(argv[0][0] == '+'){ 816 if(u == nil){ 817 vtUnlock(ubox.lock); 818 return 0; 819 } 820 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 821 vtUnlock(ubox.lock); 822 return 0; 823 } 824 if(!_groupAddMember(ubox.box, u, up->uname)){ 825 vtUnlock(ubox.lock); 826 return 0; 827 } 828 } 829 else if(argv[0][0] == '-'){ 830 if(u == nil){ 831 vtUnlock(ubox.lock); 832 return 0; 833 } 834 if((up = _userByUname(ubox.box, &argv[0][1])) == nil){ 835 vtUnlock(ubox.lock); 836 return 0; 837 } 838 if(!_groupRemMember(ubox.box, u, up->uname)){ 839 vtUnlock(ubox.lock); 840 return 0; 841 } 842 } 843 else{ 844 if(u != nil){ 845 vtSetError("uname: uname '%s' already exists", 846 u->uname); 847 vtUnlock(ubox.lock); 848 return 0; 849 } 850 851 uid = argv[0]; 852 if(*uid == ':') 853 uid++; 854 if((u = _userByUid(ubox.box, uid)) != nil){ 855 vtSetError("uname: uid '%s' already exists", 856 u->uid); 857 vtUnlock(ubox.lock); 858 return 0; 859 } 860 861 u = userAlloc(uid, uname); 862 uboxAddUser(ubox.box, u); 863 if(argv[0][0] != ':'){ 864 // should have an option for the mode and gid 865 p = smprint(createfmt, uname, uname, uname); 866 r = cliExec(p); 867 vtMemFree(p); 868 if(r == 0){ 869 vtUnlock(ubox.lock); 870 return 0; 871 } 872 } 873 } 874 argv++; 875 } 876 877 if(usersFileWrite(ubox.box) == 0){ 878 vtUnlock(ubox.lock); 879 return 0; 880 } 881 if(dflag) 882 uboxDump(ubox.box); 883 vtUnlock(ubox.lock); 884 885 return 1; 886 } 887 888 static int 889 cmdUsers(int argc, char* argv[]) 890 { 891 Ubox *box; 892 int dflag, r, wflag; 893 char *file; 894 char *usage = "usage: users [-d | -r file] [-w]"; 895 896 dflag = wflag = 0; 897 file = nil; 898 899 ARGBEGIN{ 900 default: 901 return cliError(usage); 902 case 'd': 903 dflag = 1; 904 break; 905 case 'r': 906 file = ARGF(); 907 if(file == nil) 908 return cliError(usage); 909 break; 910 case 'w': 911 wflag = 1; 912 break; 913 }ARGEND 914 915 if(argc) 916 return cliError(usage); 917 918 if(dflag && file) 919 return cliError("cannot use -d and -r together"); 920 921 if(dflag) 922 uboxInit(usersDefault, sizeof(usersDefault)); 923 else if(file){ 924 if(usersFileRead(file) == 0) 925 return 0; 926 } 927 928 vtRLock(ubox.lock); 929 box = ubox.box; 930 consPrint("\tnuser %d len %d\n", box->nuser, box->len); 931 932 r = 1; 933 if(wflag) 934 r = usersFileWrite(box); 935 vtRUnlock(ubox.lock); 936 return r; 937 } 938 939 int 940 usersInit(void) 941 { 942 fmtinstall('U', userFmt); 943 944 ubox.lock = vtLockAlloc(); 945 uboxInit(usersDefault, sizeof(usersDefault)); 946 947 cliAddCmd("users", cmdUsers); 948 cliAddCmd("uname", cmdUname); 949 950 return 1; 951 } 952