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 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 };
23
24 #pragma varargck type "U" User*
25
26 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 };
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
userHash(char * s)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*
_userByUid(Ubox * box,char * uid)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*
unameByUid(char * uid)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*
_userByUname(Ubox * box,char * uname)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*
uidByUname(char * uname)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
_groupMember(Ubox * box,char * group,char * member,int whenNoGroup)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
groupWriteMember(char * uname)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
_groupRemMember(Ubox * box,User * g,char * member)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
_groupAddMember(Ubox * box,User * g,char * member)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
groupMember(char * group,char * member)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
groupLeader(char * group,char * member)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
userFree(User * u)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*
userAlloc(char * uid,char * uname)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
validUserName(char * name)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
userFmt(Fmt * fmt)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
usersFileWrite(Ubox * box)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
uboxRemUser(Ubox * box,User * u)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
uboxAddUser(Ubox * box,User * u)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
uboxDump(Ubox * box)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
uboxFree(Ubox * box)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
uboxInit(char * users,int len)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
usersFileRead(char * path)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
cmdUname(int argc,char * argv[])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
cmdUsers(int argc,char * argv[])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
usersInit(void)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