Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
}