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