xref: /plan9-contrib/sys/src/cmd/fossil/9user.c (revision 81cf874224a4620dc66ce2d894718f8bb735f81d)
15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier 
35e96a66cSDavid du Colombier #include "9.h"
45e96a66cSDavid du Colombier 
55e96a66cSDavid du Colombier enum {
65e96a66cSDavid du Colombier 	NUserHash	= 1009,
75e96a66cSDavid du Colombier };
85e96a66cSDavid du Colombier 
95e96a66cSDavid du Colombier typedef struct Ubox Ubox;
105e96a66cSDavid du Colombier typedef struct User User;
115e96a66cSDavid du Colombier 
125e96a66cSDavid du Colombier typedef struct User {
135e96a66cSDavid du Colombier 	char*	uid;
145e96a66cSDavid du Colombier 	char*	uname;
155e96a66cSDavid du Colombier 	char*	leader;
165e96a66cSDavid du Colombier 	char**	group;
175e96a66cSDavid du Colombier 	int	ngroup;
185e96a66cSDavid du Colombier 
195e96a66cSDavid du Colombier 	User*	next;			/* */
205e96a66cSDavid du Colombier 	User*	ihash;			/* lookup by .uid */
215e96a66cSDavid du Colombier 	User*	nhash;			/* lookup by .uname */
225e96a66cSDavid du Colombier } User;
235e96a66cSDavid du Colombier 
245e96a66cSDavid du Colombier #pragma varargck type "U"   User*
255e96a66cSDavid du Colombier 
265e96a66cSDavid du Colombier typedef struct Ubox {
275e96a66cSDavid du Colombier 	User*	head;
285e96a66cSDavid du Colombier 	User*	tail;
295e96a66cSDavid du Colombier 	int	nuser;
305e96a66cSDavid du Colombier 	int	len;
315e96a66cSDavid du Colombier 
325e96a66cSDavid du Colombier 	User*	ihash[NUserHash];	/* lookup by .uid */
335e96a66cSDavid du Colombier 	User*	nhash[NUserHash];	/* lookup by .uname */
345e96a66cSDavid du Colombier } Ubox;
355e96a66cSDavid du Colombier 
365e96a66cSDavid du Colombier static struct {
375e96a66cSDavid du Colombier 	VtLock*	lock;
385e96a66cSDavid du Colombier 
395e96a66cSDavid du Colombier 	Ubox*	box;
405e96a66cSDavid du Colombier } ubox;
415e96a66cSDavid du Colombier 
425e96a66cSDavid du Colombier static char usersDefault[] = {
435e96a66cSDavid du Colombier 	"adm:adm:adm:sys\n"
445e96a66cSDavid du Colombier 	"none:none::\n"
455e96a66cSDavid du Colombier 	"noworld:noworld::\n"
465e96a66cSDavid du Colombier 	"sys:sys::\n"
475e96a66cSDavid du Colombier };
485e96a66cSDavid du Colombier 
495e96a66cSDavid du Colombier static char* usersMandatory[] = {
505e96a66cSDavid du Colombier 	"adm",
515e96a66cSDavid du Colombier 	"none",
525e96a66cSDavid du Colombier 	"noworld",
535e96a66cSDavid du Colombier 	"sys",
545e96a66cSDavid du Colombier 	nil,
555e96a66cSDavid du Colombier };
565e96a66cSDavid du Colombier 
575e96a66cSDavid du Colombier char* uidadm = "adm";
585e96a66cSDavid du Colombier char* unamenone = "none";
595e96a66cSDavid du Colombier char* uidnoworld = "noworld";
605e96a66cSDavid du Colombier 
615e96a66cSDavid du Colombier static u32int
625e96a66cSDavid du Colombier userHash(char* s)
635e96a66cSDavid du Colombier {
645e96a66cSDavid du Colombier 	uchar *p;
655e96a66cSDavid du Colombier 	u32int hash;
665e96a66cSDavid du Colombier 
675e96a66cSDavid du Colombier 	hash = 0;
685e96a66cSDavid du Colombier 	for(p = (uchar*)s; *p != '\0'; p++)
695e96a66cSDavid du Colombier 		hash = hash*7 + *p;
705e96a66cSDavid du Colombier 
715e96a66cSDavid du Colombier 	return hash % NUserHash;
725e96a66cSDavid du Colombier }
735e96a66cSDavid du Colombier 
745e96a66cSDavid du Colombier static User*
755e96a66cSDavid du Colombier _userByUid(Ubox* box, char* uid)
765e96a66cSDavid du Colombier {
775e96a66cSDavid du Colombier 	User *u;
785e96a66cSDavid du Colombier 
795e96a66cSDavid du Colombier 	if(box != nil){
805e96a66cSDavid du Colombier 		for(u = box->ihash[userHash(uid)]; u != nil; u = u->ihash){
815e96a66cSDavid du Colombier 			if(strcmp(u->uid, uid) == 0)
825e96a66cSDavid du Colombier 				return u;
835e96a66cSDavid du Colombier 		}
845e96a66cSDavid du Colombier 	}
855e96a66cSDavid du Colombier 	vtSetError("uname: uid '%s' not found", uid);
865e96a66cSDavid du Colombier 	return nil;
875e96a66cSDavid du Colombier }
885e96a66cSDavid du Colombier 
895e96a66cSDavid du Colombier char*
905e96a66cSDavid du Colombier unameByUid(char* uid)
915e96a66cSDavid du Colombier {
925e96a66cSDavid du Colombier 	User *u;
935e96a66cSDavid du Colombier 	char *uname;
945e96a66cSDavid du Colombier 
955e96a66cSDavid du Colombier 	vtRLock(ubox.lock);
965e96a66cSDavid du Colombier 	if((u = _userByUid(ubox.box, uid)) == nil){
975e96a66cSDavid du Colombier 		vtRUnlock(ubox.lock);
985e96a66cSDavid du Colombier 		return nil;
995e96a66cSDavid du Colombier 	}
1005e96a66cSDavid du Colombier 	uname = vtStrDup(u->uname);
1015e96a66cSDavid du Colombier 	vtRUnlock(ubox.lock);
1025e96a66cSDavid du Colombier 
1035e96a66cSDavid du Colombier 	return uname;
1045e96a66cSDavid du Colombier }
1055e96a66cSDavid du Colombier 
1065e96a66cSDavid du Colombier static User*
1075e96a66cSDavid du Colombier _userByUname(Ubox* box, char* uname)
1085e96a66cSDavid du Colombier {
1095e96a66cSDavid du Colombier 	User *u;
1105e96a66cSDavid du Colombier 
1115e96a66cSDavid du Colombier 	if(box != nil){
1125e96a66cSDavid du Colombier 		for(u = box->nhash[userHash(uname)]; u != nil; u = u->nhash){
1135e96a66cSDavid du Colombier 			if(strcmp(u->uname, uname) == 0)
1145e96a66cSDavid du Colombier 				return u;
1155e96a66cSDavid du Colombier 		}
1165e96a66cSDavid du Colombier 	}
1175e96a66cSDavid du Colombier 	vtSetError("uname: uname '%s' not found", uname);
1185e96a66cSDavid du Colombier 	return nil;
1195e96a66cSDavid du Colombier }
1205e96a66cSDavid du Colombier 
1215e96a66cSDavid du Colombier char*
1225e96a66cSDavid du Colombier uidByUname(char* uname)
1235e96a66cSDavid du Colombier {
1245e96a66cSDavid du Colombier 	User *u;
1255e96a66cSDavid du Colombier 	char *uid;
1265e96a66cSDavid du Colombier 
1275e96a66cSDavid du Colombier 	vtRLock(ubox.lock);
1285e96a66cSDavid du Colombier 	if((u = _userByUname(ubox.box, uname)) == nil){
1295e96a66cSDavid du Colombier 		vtRUnlock(ubox.lock);
1305e96a66cSDavid du Colombier 		return nil;
1315e96a66cSDavid du Colombier 	}
1325e96a66cSDavid du Colombier 	uid = vtStrDup(u->uid);
1335e96a66cSDavid du Colombier 	vtRUnlock(ubox.lock);
1345e96a66cSDavid du Colombier 
1355e96a66cSDavid du Colombier 	return uid;
1365e96a66cSDavid du Colombier }
1375e96a66cSDavid du Colombier 
1385e96a66cSDavid du Colombier static int
1395e96a66cSDavid du Colombier _groupMember(Ubox* box, char* group, char* member, int whenNoGroup)
1405e96a66cSDavid du Colombier {
1415e96a66cSDavid du Colombier 	int i;
1425e96a66cSDavid du Colombier 	User *g, *m;
1435e96a66cSDavid du Colombier 
1445e96a66cSDavid du Colombier 	/*
1455e96a66cSDavid du Colombier 	 * Is 'member' a member of 'group'?
1465e96a66cSDavid du Colombier 	 * Note that 'group' is a 'uid' and not a 'uname'.
1475e96a66cSDavid du Colombier 	 * A 'member' is automatically in their own group.
1485e96a66cSDavid du Colombier 	 */
1495e96a66cSDavid du Colombier 	if((g = _userByUid(box, group)) == nil)
1505e96a66cSDavid du Colombier 		return whenNoGroup;
1515e96a66cSDavid du Colombier 	if((m = _userByUname(box, member)) == nil)
1525e96a66cSDavid du Colombier 		return 0;
1535e96a66cSDavid du Colombier 	if(m == g)
1545e96a66cSDavid du Colombier 		return 1;
1555e96a66cSDavid du Colombier 	for(i = 0; i < g->ngroup; i++){
1565e96a66cSDavid du Colombier 		if(strcmp(g->group[i], member) == 0)
1575e96a66cSDavid du Colombier 			return 1;
1585e96a66cSDavid du Colombier 	}
1595e96a66cSDavid du Colombier 	return 0;
1605e96a66cSDavid du Colombier }
1615e96a66cSDavid du Colombier 
1625e96a66cSDavid du Colombier int
1635e96a66cSDavid du Colombier groupWriteMember(char* uname)
1645e96a66cSDavid du Colombier {
1655e96a66cSDavid du Colombier 	int ret;
1665e96a66cSDavid du Colombier 
1675e96a66cSDavid du Colombier 	/*
1685e96a66cSDavid du Colombier 	 * If there is a ``write'' group, then only its members can write
1695e96a66cSDavid du Colombier 	 * to the file system, no matter what the permission bits say.
1705e96a66cSDavid du Colombier 	 *
1715e96a66cSDavid du Colombier 	 * To users not in the ``write'' group, the file system appears
1725e96a66cSDavid du Colombier 	 * read only.  This is used to serve sources.cs.bell-labs.com
1735e96a66cSDavid du Colombier 	 * to the world.
1745e96a66cSDavid du Colombier 	 *
1755e96a66cSDavid du Colombier 	 * Note that if there is no ``write'' group, then this routine
1765e96a66cSDavid du Colombier 	 * makes it look like everyone is a member -- the opposite
1775e96a66cSDavid du Colombier 	 * of what groupMember does.
1785e96a66cSDavid du Colombier 	 *
1795e96a66cSDavid du Colombier 	 * We use this for sources.cs.bell-labs.com.
1805e96a66cSDavid du Colombier 	 * If this slows things down too much on systems that don't
1815e96a66cSDavid du Colombier 	 * use this functionality, we could cache the write group lookup.
1825e96a66cSDavid du Colombier 	 */
1835e96a66cSDavid du Colombier 
1845e96a66cSDavid du Colombier 	vtRLock(ubox.lock);
1855e96a66cSDavid du Colombier 	ret = _groupMember(ubox.box, "write", uname, 1);
1865e96a66cSDavid du Colombier 	vtRUnlock(ubox.lock);
1875e96a66cSDavid du Colombier 	return ret;
1885e96a66cSDavid du Colombier }
1895e96a66cSDavid du Colombier 
1905e96a66cSDavid du Colombier static int
1915e96a66cSDavid du Colombier _groupRemMember(Ubox* box, User* g, char* member)
1925e96a66cSDavid du Colombier {
1935e96a66cSDavid du Colombier 	int i;
1945e96a66cSDavid du Colombier 
1955e96a66cSDavid du Colombier 	if(_userByUname(box, member) == nil)
1965e96a66cSDavid du Colombier 		return 0;
1975e96a66cSDavid du Colombier 
1985e96a66cSDavid du Colombier 	for(i = 0; i < g->ngroup; i++){
1995e96a66cSDavid du Colombier 		if(strcmp(g->group[i], member) == 0)
2005e96a66cSDavid du Colombier 			break;
2015e96a66cSDavid du Colombier 	}
2025e96a66cSDavid du Colombier 	if(i >= g->ngroup){
2035e96a66cSDavid du Colombier 		if(strcmp(g->uname, member) == 0)
2045e96a66cSDavid du Colombier 			vtSetError("uname: '%s' always in own group", member);
2055e96a66cSDavid du Colombier 		else
2065e96a66cSDavid du Colombier 			vtSetError("uname: '%s' not in group '%s'",
2075e96a66cSDavid du Colombier 				member, g->uname);
2085e96a66cSDavid du Colombier 		return 0;
2095e96a66cSDavid du Colombier 	}
2105e96a66cSDavid du Colombier 
2115e96a66cSDavid du Colombier 	vtMemFree(g->group[i]);
2125e96a66cSDavid du Colombier 
2135e96a66cSDavid du Colombier 	box->len -= strlen(member);
2145e96a66cSDavid du Colombier 	if(g->ngroup > 1)
2155e96a66cSDavid du Colombier 		box->len--;
2165e96a66cSDavid du Colombier 	g->ngroup--;
2175e96a66cSDavid du Colombier 	switch(g->ngroup){
2185e96a66cSDavid du Colombier 	case 0:
2195e96a66cSDavid du Colombier 		vtMemFree(g->group);
2205e96a66cSDavid du Colombier 		g->group = nil;
2215e96a66cSDavid du Colombier 		break;
2225e96a66cSDavid du Colombier 	default:
2235e96a66cSDavid du Colombier 		while(i < g->ngroup){
2245e96a66cSDavid du Colombier 			g->group[i] = g->group[i+1];
2255e96a66cSDavid du Colombier 			i++;
2265e96a66cSDavid du Colombier 		}
2275e96a66cSDavid du Colombier 		/*FALLTHROUGH*/
2285e96a66cSDavid du Colombier 	case 1:
2295e96a66cSDavid du Colombier 		g->group = vtMemRealloc(g->group, (g->ngroup)*sizeof(char*));
2305e96a66cSDavid du Colombier 		break;
2315e96a66cSDavid du Colombier 	}
2325e96a66cSDavid du Colombier 
2335e96a66cSDavid du Colombier 	return 1;
2345e96a66cSDavid du Colombier }
2355e96a66cSDavid du Colombier 
2365e96a66cSDavid du Colombier static int
2375e96a66cSDavid du Colombier _groupAddMember(Ubox* box, User* g, char* member)
2385e96a66cSDavid du Colombier {
2395e96a66cSDavid du Colombier 	User *u;
2405e96a66cSDavid du Colombier 
2415e96a66cSDavid du Colombier 	if((u = _userByUname(box, member)) == nil)
2425e96a66cSDavid du Colombier 		return 0;
2435e96a66cSDavid du Colombier 	if(_groupMember(box, g->uid, u->uname, 0)){
2445e96a66cSDavid du Colombier 		if(strcmp(g->uname, member) == 0)
2455e96a66cSDavid du Colombier 			vtSetError("uname: '%s' always in own group", member);
2465e96a66cSDavid du Colombier 		else
2475e96a66cSDavid du Colombier 			vtSetError("uname: '%s' already in group '%s'",
2485e96a66cSDavid du Colombier 				member, g->uname);
2495e96a66cSDavid du Colombier 		return 0;
2505e96a66cSDavid du Colombier 	}
2515e96a66cSDavid du Colombier 
2525e96a66cSDavid du Colombier 	g->group = vtMemRealloc(g->group, (g->ngroup+1)*sizeof(char*));
2535e96a66cSDavid du Colombier 	g->group[g->ngroup] = vtStrDup(member);
2545e96a66cSDavid du Colombier 	box->len += strlen(member);
2555e96a66cSDavid du Colombier 	g->ngroup++;
2565e96a66cSDavid du Colombier 	if(g->ngroup > 1)
2575e96a66cSDavid du Colombier 		box->len++;
2585e96a66cSDavid du Colombier 
2595e96a66cSDavid du Colombier 	return 1;
2605e96a66cSDavid du Colombier }
2615e96a66cSDavid du Colombier 
2625e96a66cSDavid du Colombier int
2635e96a66cSDavid du Colombier groupMember(char* group, char* member)
2645e96a66cSDavid du Colombier {
2655e96a66cSDavid du Colombier 	int r;
2665e96a66cSDavid du Colombier 
2675e96a66cSDavid du Colombier 	if(group == nil)
2685e96a66cSDavid du Colombier 		return 0;
2695e96a66cSDavid du Colombier 
2705e96a66cSDavid du Colombier 	vtRLock(ubox.lock);
2715e96a66cSDavid du Colombier 	r = _groupMember(ubox.box, group, member, 0);
2725e96a66cSDavid du Colombier 	vtRUnlock(ubox.lock);
2735e96a66cSDavid du Colombier 
2745e96a66cSDavid du Colombier 	return r;
2755e96a66cSDavid du Colombier }
2765e96a66cSDavid du Colombier 
2775e96a66cSDavid du Colombier int
2785e96a66cSDavid du Colombier groupLeader(char* group, char* member)
2795e96a66cSDavid du Colombier {
2805e96a66cSDavid du Colombier 	int r;
2815e96a66cSDavid du Colombier 	User *g;
2825e96a66cSDavid du Colombier 
2835e96a66cSDavid du Colombier 	/*
2845e96a66cSDavid du Colombier 	 * Is 'member' the leader of 'group'?
2855e96a66cSDavid du Colombier 	 * Note that 'group' is a 'uid' and not a 'uname'.
2865e96a66cSDavid du Colombier 	 * Uname 'none' cannot be a group leader.
2875e96a66cSDavid du Colombier 	 */
2885e96a66cSDavid du Colombier 	if(strcmp(member, unamenone) == 0 || group == nil)
2895e96a66cSDavid du Colombier 		return 0;
2905e96a66cSDavid du Colombier 
2915e96a66cSDavid du Colombier 	vtRLock(ubox.lock);
2925e96a66cSDavid du Colombier 	if((g = _userByUid(ubox.box, group)) == nil){
2935e96a66cSDavid du Colombier 		vtRUnlock(ubox.lock);
2945e96a66cSDavid du Colombier 		return 0;
2955e96a66cSDavid du Colombier 	}
2965e96a66cSDavid du Colombier 	if(g->leader != nil){
2975e96a66cSDavid du Colombier 		if(strcmp(g->leader, member) == 0){
2985e96a66cSDavid du Colombier 			vtRUnlock(ubox.lock);
2995e96a66cSDavid du Colombier 			return 1;
3005e96a66cSDavid du Colombier 		}
3015e96a66cSDavid du Colombier 		r = 0;
3025e96a66cSDavid du Colombier 	}
3035e96a66cSDavid du Colombier 	else
3045e96a66cSDavid du Colombier 		r = _groupMember(ubox.box, group, member, 0);
3055e96a66cSDavid du Colombier 	vtRUnlock(ubox.lock);
3065e96a66cSDavid du Colombier 
3075e96a66cSDavid du Colombier 	return r;
3085e96a66cSDavid du Colombier }
3095e96a66cSDavid du Colombier 
3105e96a66cSDavid du Colombier static void
3115e96a66cSDavid du Colombier userFree(User* u)
3125e96a66cSDavid du Colombier {
3135e96a66cSDavid du Colombier 	int i;
3145e96a66cSDavid du Colombier 
3155e96a66cSDavid du Colombier 	vtMemFree(u->uid);
3165e96a66cSDavid du Colombier 	vtMemFree(u->uname);
3175e96a66cSDavid du Colombier 	if(u->leader != nil)
3185e96a66cSDavid du Colombier 		vtMemFree(u->leader);
3195e96a66cSDavid du Colombier 	if(u->ngroup){
3205e96a66cSDavid du Colombier 		for(i = 0; i < u->ngroup; i++)
3215e96a66cSDavid du Colombier 			vtMemFree(u->group[i]);
3225e96a66cSDavid du Colombier 		vtMemFree(u->group);
3235e96a66cSDavid du Colombier 	}
3245e96a66cSDavid du Colombier 	vtMemFree(u);
3255e96a66cSDavid du Colombier }
3265e96a66cSDavid du Colombier 
3275e96a66cSDavid du Colombier static User*
3285e96a66cSDavid du Colombier userAlloc(char* uid, char* uname)
3295e96a66cSDavid du Colombier {
3305e96a66cSDavid du Colombier 	User *u;
3315e96a66cSDavid du Colombier 
3325e96a66cSDavid du Colombier 	u = vtMemAllocZ(sizeof(User));
3335e96a66cSDavid du Colombier 	u->uid = vtStrDup(uid);
3345e96a66cSDavid du Colombier 	u->uname = vtStrDup(uname);
3355e96a66cSDavid du Colombier 
3365e96a66cSDavid du Colombier 	return u;
3375e96a66cSDavid du Colombier }
3385e96a66cSDavid du Colombier 
3395e96a66cSDavid du Colombier int
3405e96a66cSDavid du Colombier validUserName(char* name)
3415e96a66cSDavid du Colombier {
3425e96a66cSDavid du Colombier 	Rune *r;
3435e96a66cSDavid du Colombier 	static Rune invalid[] = L"#:,()";
3445e96a66cSDavid du Colombier 
3455e96a66cSDavid du Colombier 	for(r = invalid; *r != '\0'; r++){
3465e96a66cSDavid du Colombier 		if(utfrune(name, *r))
3475e96a66cSDavid du Colombier 			return 0;
3485e96a66cSDavid du Colombier 	}
3495e96a66cSDavid du Colombier 	return 1;
3505e96a66cSDavid du Colombier }
3515e96a66cSDavid du Colombier 
3525e96a66cSDavid du Colombier static int
3535e96a66cSDavid du Colombier userFmt(Fmt* fmt)
3545e96a66cSDavid du Colombier {
3555e96a66cSDavid du Colombier 	User *u;
3565e96a66cSDavid du Colombier 	int i, r;
3575e96a66cSDavid du Colombier 
3585e96a66cSDavid du Colombier 	u = va_arg(fmt->args, User*);
3595e96a66cSDavid du Colombier 
3605e96a66cSDavid du Colombier 	r = fmtprint(fmt, "%s:%s:", u->uid, u->uname);
3615e96a66cSDavid du Colombier 	if(u->leader != nil)
3625e96a66cSDavid du Colombier 		r += fmtprint(fmt, u->leader);
3635e96a66cSDavid du Colombier 	r += fmtprint(fmt, ":");
3645e96a66cSDavid du Colombier 	if(u->ngroup){
3655e96a66cSDavid du Colombier 		r += fmtprint(fmt, u->group[0]);
3665e96a66cSDavid du Colombier 		for(i = 1; i < u->ngroup; i++)
3675e96a66cSDavid du Colombier 			r += fmtprint(fmt, ",%s", u->group[i]);
3685e96a66cSDavid du Colombier 	}
3695e96a66cSDavid du Colombier 
3705e96a66cSDavid du Colombier 	return r;
3715e96a66cSDavid du Colombier }
3725e96a66cSDavid du Colombier 
3735e96a66cSDavid du Colombier static int
3745e96a66cSDavid du Colombier usersFileWrite(Ubox* box)
3755e96a66cSDavid du Colombier {
3765e96a66cSDavid du Colombier 	Fs *fs;
3775e96a66cSDavid du Colombier 	User *u;
3785e96a66cSDavid du Colombier 	int i, r;
3795e96a66cSDavid du Colombier 	Fsys *fsys;
3805e96a66cSDavid du Colombier 	char *p, *q, *s;
3815e96a66cSDavid du Colombier 	File *dir, *file;
3825e96a66cSDavid du Colombier 
3835e96a66cSDavid du Colombier 	if((fsys = fsysGet("main")) == nil)
3845e96a66cSDavid du Colombier 		return 0;
3855e96a66cSDavid du Colombier 	fsysFsRlock(fsys);
3865e96a66cSDavid du Colombier 	fs = fsysGetFs(fsys);
3875e96a66cSDavid du Colombier 
3885e96a66cSDavid du Colombier 	/*
3895e96a66cSDavid du Colombier 	 * BUG:
3905e96a66cSDavid du Colombier 	 * 	the owner/group/permissions need to be thought out.
3915e96a66cSDavid du Colombier 	 */
3925e96a66cSDavid du Colombier 	r = 0;
3935e96a66cSDavid du Colombier 	if((dir = fileOpen(fs, "/active")) == nil)
3945e96a66cSDavid du Colombier 		goto tidy0;
3955e96a66cSDavid du Colombier 	if((file = fileWalk(dir, "adm")) == nil)
3965e96a66cSDavid du Colombier 		file = fileCreate(dir, "adm", ModeDir|0775, uidadm);
3975e96a66cSDavid du Colombier 	fileDecRef(dir);
3985e96a66cSDavid du Colombier 	if(file == nil)
3995e96a66cSDavid du Colombier 		goto tidy;
4005e96a66cSDavid du Colombier 	dir = file;
4015e96a66cSDavid du Colombier 	if((file = fileWalk(dir, "users")) == nil)
4025e96a66cSDavid du Colombier 		file = fileCreate(dir, "users", 0664, uidadm);
4035e96a66cSDavid du Colombier 	fileDecRef(dir);
4045e96a66cSDavid du Colombier 	if(file == nil)
4055e96a66cSDavid du Colombier 		goto tidy;
4065e96a66cSDavid du Colombier 	if(!fileTruncate(file, uidadm))
4075e96a66cSDavid du Colombier 		goto tidy;
4085e96a66cSDavid du Colombier 
4095e96a66cSDavid du Colombier 	p = s = vtMemAlloc(box->len+1);
4105e96a66cSDavid du Colombier 	q = p + box->len+1;
4115e96a66cSDavid du Colombier 	for(u = box->head; u != nil; u = u->next){
4125e96a66cSDavid du Colombier 		p += snprint(p, q-p, "%s:%s:", u->uid, u->uname);
4135e96a66cSDavid du Colombier 		if(u->leader != nil)
4145e96a66cSDavid du Colombier 			p+= snprint(p, q-p, u->leader);
4155e96a66cSDavid du Colombier 		p += snprint(p, q-p, ":");
4165e96a66cSDavid du Colombier 		if(u->ngroup){
4175e96a66cSDavid du Colombier 			p += snprint(p, q-p, u->group[0]);
4185e96a66cSDavid du Colombier 			for(i = 1; i < u->ngroup; i++)
4195e96a66cSDavid du Colombier 				p += snprint(p, q-p, ",%s", u->group[i]);
4205e96a66cSDavid du Colombier 		}
4215e96a66cSDavid du Colombier 		p += snprint(p, q-p, "\n");
4225e96a66cSDavid du Colombier 	}
4235e96a66cSDavid du Colombier 	r = fileWrite(file, s, box->len, 0, uidadm);
4245e96a66cSDavid du Colombier 	vtMemFree(s);
4255e96a66cSDavid du Colombier 
4265e96a66cSDavid du Colombier tidy:
4275e96a66cSDavid du Colombier 	if(file != nil)
4285e96a66cSDavid du Colombier 		fileDecRef(file);
4295e96a66cSDavid du Colombier tidy0:
4305e96a66cSDavid du Colombier 	fsysFsRUnlock(fsys);
4315e96a66cSDavid du Colombier 	fsysPut(fsys);
4325e96a66cSDavid du Colombier 
4335e96a66cSDavid du Colombier 	return r;
4345e96a66cSDavid du Colombier }
4355e96a66cSDavid du Colombier 
4365e96a66cSDavid du Colombier static void
4375e96a66cSDavid du Colombier uboxRemUser(Ubox* box, User *u)
4385e96a66cSDavid du Colombier {
4395e96a66cSDavid du Colombier 	User **h, *up;
4405e96a66cSDavid du Colombier 
4415e96a66cSDavid du Colombier 	h = &box->ihash[userHash(u->uid)];
4425e96a66cSDavid du Colombier 	for(up = *h; up != nil && up != u; up = up->ihash)
4435e96a66cSDavid du Colombier 		h = &up->ihash;
4445e96a66cSDavid du Colombier 	assert(up == u);
4455e96a66cSDavid du Colombier 	*h = up->ihash;
4465e96a66cSDavid du Colombier 	box->len -= strlen(u->uid);
4475e96a66cSDavid du Colombier 
4485e96a66cSDavid du Colombier 	h = &box->nhash[userHash(u->uname)];
4495e96a66cSDavid du Colombier 	for(up = *h; up != nil && up != u; up = up->nhash)
4505e96a66cSDavid du Colombier 		h = &up->nhash;
4515e96a66cSDavid du Colombier 	assert(up == u);
4525e96a66cSDavid du Colombier 	*h = up->nhash;
4535e96a66cSDavid du Colombier 	box->len -= strlen(u->uname);
4545e96a66cSDavid du Colombier 
4555e96a66cSDavid du Colombier 	h = &box->head;
4565e96a66cSDavid du Colombier 	for(up = *h; up != nil && strcmp(up->uid, u->uid) != 0; up = up->next)
4575e96a66cSDavid du Colombier 		h = &up->next;
4585e96a66cSDavid du Colombier 	assert(up == u);
4595e96a66cSDavid du Colombier 	*h = u->next;
4605e96a66cSDavid du Colombier 	u->next = nil;
4615e96a66cSDavid du Colombier 
4625e96a66cSDavid du Colombier 	box->len -= 4;
4635e96a66cSDavid du Colombier 	box->nuser--;
4645e96a66cSDavid du Colombier }
4655e96a66cSDavid du Colombier 
4665e96a66cSDavid du Colombier static void
4675e96a66cSDavid du Colombier uboxAddUser(Ubox* box, User* u)
4685e96a66cSDavid du Colombier {
4695e96a66cSDavid du Colombier 	User **h, *up;
4705e96a66cSDavid du Colombier 
4715e96a66cSDavid du Colombier 	h = &box->ihash[userHash(u->uid)];
4725e96a66cSDavid du Colombier 	u->ihash = *h;
4735e96a66cSDavid du Colombier 	*h = u;
4745e96a66cSDavid du Colombier 	box->len += strlen(u->uid);
4755e96a66cSDavid du Colombier 
4765e96a66cSDavid du Colombier 	h = &box->nhash[userHash(u->uname)];
4775e96a66cSDavid du Colombier 	u->nhash = *h;
4785e96a66cSDavid du Colombier 	*h = u;
4795e96a66cSDavid du Colombier 	box->len += strlen(u->uname);
4805e96a66cSDavid du Colombier 
4815e96a66cSDavid du Colombier 	h = &box->head;
4825e96a66cSDavid du Colombier 	for(up = *h; up != nil && strcmp(up->uid, u->uid) < 0; up = up->next)
4835e96a66cSDavid du Colombier 		h = &up->next;
4845e96a66cSDavid du Colombier 	u->next = *h;
4855e96a66cSDavid du Colombier 	*h = u;
4865e96a66cSDavid du Colombier 
4875e96a66cSDavid du Colombier 	box->len += 4;
4885e96a66cSDavid du Colombier 	box->nuser++;
4895e96a66cSDavid du Colombier }
4905e96a66cSDavid du Colombier 
4915e96a66cSDavid du Colombier static void
4925e96a66cSDavid du Colombier uboxDump(Ubox* box)
4935e96a66cSDavid du Colombier {
4945e96a66cSDavid du Colombier 	User* u;
4955e96a66cSDavid du Colombier 
4965e96a66cSDavid du Colombier 	consPrint("nuser %d len = %d\n", box->nuser, box->len);
4975e96a66cSDavid du Colombier 
4985e96a66cSDavid du Colombier 	for(u = box->head; u != nil; u = u->next)
4995e96a66cSDavid du Colombier 		consPrint("%U\n", u);
5005e96a66cSDavid du Colombier }
5015e96a66cSDavid du Colombier 
5025e96a66cSDavid du Colombier static void
5035e96a66cSDavid du Colombier uboxFree(Ubox* box)
5045e96a66cSDavid du Colombier {
5055e96a66cSDavid du Colombier 	User *next, *u;
5065e96a66cSDavid du Colombier 
5075e96a66cSDavid du Colombier 	for(u = box->head; u != nil; u = next){
5085e96a66cSDavid du Colombier 		next = u->next;
5095e96a66cSDavid du Colombier 		userFree(u);
5105e96a66cSDavid du Colombier 	}
5115e96a66cSDavid du Colombier 	vtMemFree(box);
5125e96a66cSDavid du Colombier }
5135e96a66cSDavid du Colombier 
5145e96a66cSDavid du Colombier static int
515*81cf8742SDavid du Colombier uboxInit(char* users, int len)
5165e96a66cSDavid du Colombier {
5175e96a66cSDavid du Colombier 	User *g, *u;
5185e96a66cSDavid du Colombier 	Ubox *box, *obox;
5195e96a66cSDavid du Colombier 	int blank, comment, i, nuser;
5205e96a66cSDavid du Colombier 	char *buf, *f[5], **line, *p, *q, *s;
5215e96a66cSDavid du Colombier 
5225e96a66cSDavid du Colombier 	/*
5235e96a66cSDavid du Colombier 	 * Strip out whitespace and comments.
5245e96a66cSDavid du Colombier 	 * Note that comments are pointless, they disappear
5255e96a66cSDavid du Colombier 	 * when the server writes the database back out.
5265e96a66cSDavid du Colombier 	 */
5275e96a66cSDavid du Colombier 	blank = 1;
5285e96a66cSDavid du Colombier 	comment = nuser = 0;
5295e96a66cSDavid du Colombier 
5305e96a66cSDavid du Colombier 	s = p = buf = vtMemAlloc(len+1);
5315e96a66cSDavid du Colombier 	for(q = users; *q != '\0'; q++){
5325e96a66cSDavid du Colombier 		if(*q == '\r' || *q == '\t' || *q == ' ')
5335e96a66cSDavid du Colombier 			continue;
5345e96a66cSDavid du Colombier 		if(*q == '\n'){
5355e96a66cSDavid du Colombier 			if(!blank){
5365e96a66cSDavid du Colombier 				if(p != s){
5375e96a66cSDavid du Colombier 					*p++ = '\n';
5385e96a66cSDavid du Colombier 					nuser++;
5395e96a66cSDavid du Colombier 					s = p;
5405e96a66cSDavid du Colombier 				}
5415e96a66cSDavid du Colombier 				blank = 1;
5425e96a66cSDavid du Colombier 			}
5435e96a66cSDavid du Colombier 			comment = 0;
5445e96a66cSDavid du Colombier 			continue;
5455e96a66cSDavid du Colombier 		}
5465e96a66cSDavid du Colombier 		if(*q == '#')
5475e96a66cSDavid du Colombier 			comment = 1;
5485e96a66cSDavid du Colombier 		blank = 0;
5495e96a66cSDavid du Colombier 		if(!comment)
5505e96a66cSDavid du Colombier 			*p++ = *q;
5515e96a66cSDavid du Colombier 	}
5525e96a66cSDavid du Colombier 	*p = '\0';
5535e96a66cSDavid du Colombier 
5545e96a66cSDavid du Colombier 	line = vtMemAllocZ((nuser+2)*sizeof(char*));
5555e96a66cSDavid du Colombier 	if((i = gettokens(buf, line, nuser+2, "\n")) != nuser){
5565e96a66cSDavid du Colombier 		fprint(2, "nuser %d (%d) botch\n", nuser, i);
5575e96a66cSDavid du Colombier 		vtMemFree(line);
5585e96a66cSDavid du Colombier 		vtMemFree(buf);
5595e96a66cSDavid du Colombier 		return 0;
5605e96a66cSDavid du Colombier 	}
5615e96a66cSDavid du Colombier 
5625e96a66cSDavid du Colombier 	fprint(2, "nuser %d\n", nuser);
5635e96a66cSDavid du Colombier 
5645e96a66cSDavid du Colombier 	/*
565*81cf8742SDavid du Colombier 	 * Everything is updated in a local Ubox until verified.
5665e96a66cSDavid du Colombier 	 */
5675e96a66cSDavid du Colombier 	box = vtMemAllocZ(sizeof(Ubox));
5685e96a66cSDavid du Colombier 
5695e96a66cSDavid du Colombier 	/*
5705e96a66cSDavid du Colombier 	 * First pass - check format, check for duplicates
5715e96a66cSDavid du Colombier 	 * and enter in hash buckets.
5725e96a66cSDavid du Colombier 	 */
5735e96a66cSDavid du Colombier 	for(i = 0; i < nuser; i++){
5745e96a66cSDavid du Colombier 		s = vtStrDup(line[i]);
5755e96a66cSDavid du Colombier 		if(getfields(s, f, nelem(f), 0, ":") != 4){
5765e96a66cSDavid du Colombier 			fprint(2, "bad line '%s'\n", line[i]);
5775e96a66cSDavid du Colombier 			vtMemFree(s);
5785e96a66cSDavid du Colombier 			continue;
5795e96a66cSDavid du Colombier 		}
5805e96a66cSDavid du Colombier 		if(*f[0] == '\0' || *f[1] == '\0'){
5815e96a66cSDavid du Colombier 			fprint(2, "bad line '%s'\n", line[i]);
5825e96a66cSDavid du Colombier 			vtMemFree(s);
5835e96a66cSDavid du Colombier 			continue;
5845e96a66cSDavid du Colombier 		}
5855e96a66cSDavid du Colombier 		if(!validUserName(f[0])){
5865e96a66cSDavid du Colombier 			fprint(2, "invalid uid '%s'\n", f[0]);
5875e96a66cSDavid du Colombier 			vtMemFree(s);
5885e96a66cSDavid du Colombier 			continue;
5895e96a66cSDavid du Colombier 		}
5905e96a66cSDavid du Colombier 		if(_userByUid(box, f[0]) != nil){
5915e96a66cSDavid du Colombier 			fprint(2, "duplicate uid '%s'\n", f[0]);
5925e96a66cSDavid du Colombier 			vtMemFree(s);
5935e96a66cSDavid du Colombier 			continue;
5945e96a66cSDavid du Colombier 		}
5955e96a66cSDavid du Colombier 		if(!validUserName(f[1])){
5965e96a66cSDavid du Colombier 			fprint(2, "invalid uname '%s'\n", f[0]);
5975e96a66cSDavid du Colombier 			vtMemFree(s);
5985e96a66cSDavid du Colombier 			continue;
5995e96a66cSDavid du Colombier 		}
6005e96a66cSDavid du Colombier 		if(_userByUname(box, f[1]) != nil){
6015e96a66cSDavid du Colombier 			fprint(2, "duplicate uname '%s'\n", f[1]);
6025e96a66cSDavid du Colombier 			vtMemFree(s);
6035e96a66cSDavid du Colombier 			continue;
6045e96a66cSDavid du Colombier 		}
6055e96a66cSDavid du Colombier 
6065e96a66cSDavid du Colombier 		u = userAlloc(f[0], f[1]);
6075e96a66cSDavid du Colombier 		uboxAddUser(box, u);
6085e96a66cSDavid du Colombier 
6095e96a66cSDavid du Colombier 		vtMemFree(s);
6105e96a66cSDavid du Colombier 	}
6115e96a66cSDavid du Colombier 	assert(box->nuser == nuser);
6125e96a66cSDavid du Colombier 
6135e96a66cSDavid du Colombier 	/*
6145e96a66cSDavid du Colombier 	 * Second pass - fill in leader and group information.
6155e96a66cSDavid du Colombier 	 */
6165e96a66cSDavid du Colombier 	for(i = 0; i < nuser; i++){
6175e96a66cSDavid du Colombier 		s = vtStrDup(line[i]);
6185e96a66cSDavid du Colombier 		getfields(s, f, nelem(f), 0, ":");
6195e96a66cSDavid du Colombier 
6205e96a66cSDavid du Colombier 		assert(g = _userByUname(box, f[1]));
6215e96a66cSDavid du Colombier 		if(*f[2] != '\0'){
6225e96a66cSDavid du Colombier 			if((u = _userByUname(box, f[2])) == nil)
6235e96a66cSDavid du Colombier 				g->leader = vtStrDup(g->uname);
6245e96a66cSDavid du Colombier 			else
6255e96a66cSDavid du Colombier 				g->leader = vtStrDup(u->uname);
6265e96a66cSDavid du Colombier 			box->len += strlen(g->leader);
6275e96a66cSDavid du Colombier 		}
6285e96a66cSDavid du Colombier 		for(p = f[3]; p != nil; p = q){
6295e96a66cSDavid du Colombier 			if((q = utfrune(p, L',')) != nil)
6305e96a66cSDavid du Colombier 				*q++ = '\0';
6315e96a66cSDavid du Colombier 			if(!_groupAddMember(box, g, p)){
6325e96a66cSDavid du Colombier 				// print/log error here
6335e96a66cSDavid du Colombier 			}
6345e96a66cSDavid du Colombier 		}
6355e96a66cSDavid du Colombier 
6365e96a66cSDavid du Colombier 		vtMemFree(s);
6375e96a66cSDavid du Colombier 	}
6385e96a66cSDavid du Colombier 
6395e96a66cSDavid du Colombier 	vtMemFree(line);
6405e96a66cSDavid du Colombier 	vtMemFree(buf);
6415e96a66cSDavid du Colombier 
6425e96a66cSDavid du Colombier 	for(i = 0; usersMandatory[i] != nil; i++){
6435e96a66cSDavid du Colombier 		if((u = _userByUid(box, usersMandatory[i])) == nil){
6445e96a66cSDavid du Colombier 			vtSetError("user '%s' is mandatory", usersMandatory[i]);
6455e96a66cSDavid du Colombier 			uboxFree(box);
6465e96a66cSDavid du Colombier 			return 0;
6475e96a66cSDavid du Colombier 		}
6485e96a66cSDavid du Colombier 		if(strcmp(u->uid, u->uname) != 0){
6495e96a66cSDavid du Colombier 			vtSetError("uid/uname for user '%s' must match",
6505e96a66cSDavid du Colombier 				usersMandatory[i]);
6515e96a66cSDavid du Colombier 			uboxFree(box);
6525e96a66cSDavid du Colombier 			return 0;
6535e96a66cSDavid du Colombier 		}
6545e96a66cSDavid du Colombier 	}
6555e96a66cSDavid du Colombier 
6565e96a66cSDavid du Colombier 	vtLock(ubox.lock);
6575e96a66cSDavid du Colombier 	obox = ubox.box;
6585e96a66cSDavid du Colombier 	ubox.box = box;
6595e96a66cSDavid du Colombier 	vtUnlock(ubox.lock);
6605e96a66cSDavid du Colombier 
6615e96a66cSDavid du Colombier 	if(obox != nil)
6625e96a66cSDavid du Colombier 		uboxFree(obox);
6635e96a66cSDavid du Colombier 
6645e96a66cSDavid du Colombier 	return 1;
6655e96a66cSDavid du Colombier }
6665e96a66cSDavid du Colombier 
667*81cf8742SDavid du Colombier int
6685e96a66cSDavid du Colombier usersFileRead(char* path)
6695e96a66cSDavid du Colombier {
6705e96a66cSDavid du Colombier 	char *p;
6715e96a66cSDavid du Colombier 	File *file;
6725e96a66cSDavid du Colombier 	Fsys *fsys;
6735e96a66cSDavid du Colombier 	int len, r;
6745e96a66cSDavid du Colombier 	uvlong size;
6755e96a66cSDavid du Colombier 
6765e96a66cSDavid du Colombier 	if((fsys = fsysGet("main")) == nil)
6775e96a66cSDavid du Colombier 		return 0;
6785e96a66cSDavid du Colombier 	fsysFsRlock(fsys);
6795e96a66cSDavid du Colombier 
680*81cf8742SDavid du Colombier 	if(path == nil)
681*81cf8742SDavid du Colombier 		path = "/active/adm/users";
682*81cf8742SDavid du Colombier 
6835e96a66cSDavid du Colombier 	r = 0;
6845e96a66cSDavid du Colombier 	if((file = fileOpen(fsysGetFs(fsys), path)) != nil){
6855e96a66cSDavid du Colombier 		if(fileGetSize(file, &size)){
6865e96a66cSDavid du Colombier 			len = size;
6875e96a66cSDavid du Colombier 			p = vtMemAlloc(size+1);
6885e96a66cSDavid du Colombier 			if(fileRead(file, p, len, 0) == len){
6895e96a66cSDavid du Colombier 				p[len] = '\0';
690*81cf8742SDavid du Colombier 				r = uboxInit(p, len);
6915e96a66cSDavid du Colombier 			}
6925e96a66cSDavid du Colombier 		}
6935e96a66cSDavid du Colombier 		fileDecRef(file);
6945e96a66cSDavid du Colombier 	}
6955e96a66cSDavid du Colombier 
6965e96a66cSDavid du Colombier 	fsysFsRUnlock(fsys);
6975e96a66cSDavid du Colombier 	fsysPut(fsys);
6985e96a66cSDavid du Colombier 
6995e96a66cSDavid du Colombier 	return r;
7005e96a66cSDavid du Colombier }
7015e96a66cSDavid du Colombier 
7025e96a66cSDavid du Colombier static int
7035e96a66cSDavid du Colombier cmdUname(int argc, char* argv[])
7045e96a66cSDavid du Colombier {
7055e96a66cSDavid du Colombier 	User *u, *up;
7065e96a66cSDavid du Colombier 	int d, dflag, i, r;
7075e96a66cSDavid du Colombier 	char *p, *uid, *uname;
70861201b97SDavid du Colombier 	char *createfmt = "fsys main create /active/usr/%s %s %s d775";
70934e04225SDavid du Colombier 	char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]";
7105e96a66cSDavid du Colombier 
7115e96a66cSDavid du Colombier 	dflag = 0;
7125e96a66cSDavid du Colombier 
7135e96a66cSDavid du Colombier 	ARGBEGIN{
7145e96a66cSDavid du Colombier 	default:
7155e96a66cSDavid du Colombier 		return cliError(usage);
7165e96a66cSDavid du Colombier 	case 'd':
7175e96a66cSDavid du Colombier 		dflag = 1;
7185e96a66cSDavid du Colombier 		break;
7195e96a66cSDavid du Colombier 	}ARGEND
7205e96a66cSDavid du Colombier 
7215e96a66cSDavid du Colombier 	if(argc < 1){
72234e04225SDavid du Colombier 		if(!dflag)
72334e04225SDavid du Colombier 			return cliError(usage);
7245e96a66cSDavid du Colombier 		vtRLock(ubox.lock);
7255e96a66cSDavid du Colombier 		uboxDump(ubox.box);
7265e96a66cSDavid du Colombier 		vtRUnlock(ubox.lock);
7275e96a66cSDavid du Colombier 		return 1;
7285e96a66cSDavid du Colombier 	}
7295e96a66cSDavid du Colombier 
7305e96a66cSDavid du Colombier 	uname = argv[0];
7315e96a66cSDavid du Colombier 	argc--; argv++;
7325e96a66cSDavid du Colombier 
7335e96a66cSDavid du Colombier 	if(argc == 0){
7345e96a66cSDavid du Colombier 		vtRLock(ubox.lock);
7355e96a66cSDavid du Colombier 		if((u = _userByUname(ubox.box, uname)) == nil){
7365e96a66cSDavid du Colombier 			vtRUnlock(ubox.lock);
7375e96a66cSDavid du Colombier 			return 0;
7385e96a66cSDavid du Colombier 		}
7395e96a66cSDavid du Colombier 		consPrint("\t%U\n", u);
7405e96a66cSDavid du Colombier 		vtRUnlock(ubox.lock);
7415e96a66cSDavid du Colombier 		return 1;
7425e96a66cSDavid du Colombier 	}
7435e96a66cSDavid du Colombier 
7445e96a66cSDavid du Colombier 	vtLock(ubox.lock);
7455e96a66cSDavid du Colombier 	u = _userByUname(ubox.box, uname);
7465e96a66cSDavid du Colombier 	while(argc--){
7475e96a66cSDavid du Colombier 		if(argv[0][0] == '%'){
7485e96a66cSDavid du Colombier 			if(u == nil){
7495e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
7505e96a66cSDavid du Colombier 				return 0;
7515e96a66cSDavid du Colombier 			}
7525e96a66cSDavid du Colombier 			p = &argv[0][1];
7535e96a66cSDavid du Colombier 			if((up = _userByUname(ubox.box, p)) != nil){
7545e96a66cSDavid du Colombier 				vtSetError("uname: uname '%s' already exists",
7555e96a66cSDavid du Colombier 					up->uname);
7565e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
7575e96a66cSDavid du Colombier 				return 0;
7585e96a66cSDavid du Colombier 			}
7595e96a66cSDavid du Colombier 			for(i = 0; usersMandatory[i] != nil; i++){
7605e96a66cSDavid du Colombier 				if(strcmp(usersMandatory[i], uname) != 0)
7615e96a66cSDavid du Colombier 					continue;
7625e96a66cSDavid du Colombier 				vtSetError("uname: uname '%s' is mandatory",
7635e96a66cSDavid du Colombier 					uname);
7645e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
7655e96a66cSDavid du Colombier 				return 0;
7665e96a66cSDavid du Colombier 			}
7675e96a66cSDavid du Colombier 
7685e96a66cSDavid du Colombier 			d = strlen(p) - strlen(u->uname);
7695e96a66cSDavid du Colombier 			for(up = ubox.box->head; up != nil; up = up->next){
7705e96a66cSDavid du Colombier 				if(up->leader != nil){
7715e96a66cSDavid du Colombier 					if(strcmp(up->leader, u->uname) == 0){
7725e96a66cSDavid du Colombier 						vtMemFree(up->leader);
7735e96a66cSDavid du Colombier 						up->leader = vtStrDup(p);
7745e96a66cSDavid du Colombier 						ubox.box->len += d;
7755e96a66cSDavid du Colombier 					}
7765e96a66cSDavid du Colombier 				}
7775e96a66cSDavid du Colombier 				for(i = 0; i < up->ngroup; i++){
7785e96a66cSDavid du Colombier 					if(strcmp(up->group[i], u->uname) != 0)
7795e96a66cSDavid du Colombier 						continue;
7805e96a66cSDavid du Colombier 					vtMemFree(up->group[i]);
7815e96a66cSDavid du Colombier 					up->group[i] = vtStrDup(p);
7825e96a66cSDavid du Colombier 					ubox.box->len += d;
7835e96a66cSDavid du Colombier 					break;
7845e96a66cSDavid du Colombier 				}
7855e96a66cSDavid du Colombier 			}
7865e96a66cSDavid du Colombier 
7875e96a66cSDavid du Colombier 			uboxRemUser(ubox.box, u);
7885e96a66cSDavid du Colombier 			vtMemFree(u->uname);
7895e96a66cSDavid du Colombier 			u->uname = vtStrDup(p);
7905e96a66cSDavid du Colombier 			uboxAddUser(ubox.box, u);
7915e96a66cSDavid du Colombier 		}
7925e96a66cSDavid du Colombier 		else if(argv[0][0] == '='){
7935e96a66cSDavid du Colombier 			if(u == nil){
7945e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
7955e96a66cSDavid du Colombier 				return 0;
7965e96a66cSDavid du Colombier 			}
7975e96a66cSDavid du Colombier 			if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
7985e96a66cSDavid du Colombier 				if(argv[0][1] != '\0'){
7995e96a66cSDavid du Colombier 					vtUnlock(ubox.lock);
8005e96a66cSDavid du Colombier 					return 0;
8015e96a66cSDavid du Colombier 				}
8025e96a66cSDavid du Colombier 			}
8035e96a66cSDavid du Colombier 			if(u->leader != nil){
8045e96a66cSDavid du Colombier 				ubox.box->len -= strlen(u->leader);
8055e96a66cSDavid du Colombier 				vtMemFree(u->leader);
8065e96a66cSDavid du Colombier 				u->leader = nil;
8075e96a66cSDavid du Colombier 			}
8085e96a66cSDavid du Colombier 			if(up != nil){
8095e96a66cSDavid du Colombier 				u->leader = vtStrDup(up->uname);
8105e96a66cSDavid du Colombier 				ubox.box->len += strlen(u->leader);
8115e96a66cSDavid du Colombier 			}
8125e96a66cSDavid du Colombier 		}
8135e96a66cSDavid du Colombier 		else if(argv[0][0] == '+'){
8145e96a66cSDavid du Colombier 			if(u == nil){
8155e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8165e96a66cSDavid du Colombier 				return 0;
8175e96a66cSDavid du Colombier 			}
8185e96a66cSDavid du Colombier 			if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
8195e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8205e96a66cSDavid du Colombier 				return 0;
8215e96a66cSDavid du Colombier 			}
8225e96a66cSDavid du Colombier 			if(!_groupAddMember(ubox.box, u, up->uname)){
8235e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8245e96a66cSDavid du Colombier 				return 0;
8255e96a66cSDavid du Colombier 			}
8265e96a66cSDavid du Colombier 		}
8275e96a66cSDavid du Colombier 		else if(argv[0][0] == '-'){
8285e96a66cSDavid du Colombier 			if(u == nil){
8295e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8305e96a66cSDavid du Colombier 				return 0;
8315e96a66cSDavid du Colombier 			}
8325e96a66cSDavid du Colombier 			if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
8335e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8345e96a66cSDavid du Colombier 				return 0;
8355e96a66cSDavid du Colombier 			}
8365e96a66cSDavid du Colombier 			if(!_groupRemMember(ubox.box, u, up->uname)){
8375e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8385e96a66cSDavid du Colombier 				return 0;
8395e96a66cSDavid du Colombier 			}
8405e96a66cSDavid du Colombier 		}
8415e96a66cSDavid du Colombier 		else{
8425e96a66cSDavid du Colombier 			if(u != nil){
8435e96a66cSDavid du Colombier 				vtSetError("uname: uname '%s' already exists",
8445e96a66cSDavid du Colombier 					u->uname);
8455e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8465e96a66cSDavid du Colombier 				return 0;
8475e96a66cSDavid du Colombier 			}
8485e96a66cSDavid du Colombier 
8495e96a66cSDavid du Colombier 			uid = argv[0];
8505e96a66cSDavid du Colombier 			if(*uid == ':')
8515e96a66cSDavid du Colombier 				uid++;
8525e96a66cSDavid du Colombier 			if((u = _userByUid(ubox.box, uid)) != nil){
8535e96a66cSDavid du Colombier 				vtSetError("uname: uid '%s' already exists",
8545e96a66cSDavid du Colombier 					u->uid);
8555e96a66cSDavid du Colombier 				vtUnlock(ubox.lock);
8565e96a66cSDavid du Colombier 				return 0;
8575e96a66cSDavid du Colombier 			}
8585e96a66cSDavid du Colombier 
8595e96a66cSDavid du Colombier 			u = userAlloc(uid, uname);
8605e96a66cSDavid du Colombier 			uboxAddUser(ubox.box, u);
8615e96a66cSDavid du Colombier 			if(argv[0][0] != ':'){
8625e96a66cSDavid du Colombier 				// should have an option for the mode and gid
8635e96a66cSDavid du Colombier 				p = smprint(createfmt, uname, uname, uname);
8645e96a66cSDavid du Colombier 				r = cliExec(p);
8655e96a66cSDavid du Colombier 				vtMemFree(p);
8665e96a66cSDavid du Colombier 				if(r != 0){
8675e96a66cSDavid du Colombier 					vtUnlock(ubox.lock);
8685e96a66cSDavid du Colombier 					return 0;
8695e96a66cSDavid du Colombier 				}
8705e96a66cSDavid du Colombier 			}
8715e96a66cSDavid du Colombier 		}
8725e96a66cSDavid du Colombier 		argv++;
8735e96a66cSDavid du Colombier 	}
8745e96a66cSDavid du Colombier 
8755e96a66cSDavid du Colombier 	if(usersFileWrite(ubox.box) == 0){
8765e96a66cSDavid du Colombier 		vtUnlock(ubox.lock);
8775e96a66cSDavid du Colombier 		return 0;
8785e96a66cSDavid du Colombier 	}
8795e96a66cSDavid du Colombier 	if(dflag)
8805e96a66cSDavid du Colombier 		uboxDump(ubox.box);
8815e96a66cSDavid du Colombier 	vtUnlock(ubox.lock);
8825e96a66cSDavid du Colombier 
8835e96a66cSDavid du Colombier 	return 1;
8845e96a66cSDavid du Colombier }
8855e96a66cSDavid du Colombier 
8865e96a66cSDavid du Colombier static int
8875e96a66cSDavid du Colombier cmdUsers(int argc, char* argv[])
8885e96a66cSDavid du Colombier {
8895e96a66cSDavid du Colombier 	Ubox *box;
8905e96a66cSDavid du Colombier 	int dflag, r, wflag;
891*81cf8742SDavid du Colombier 	char *file;
892*81cf8742SDavid du Colombier 	char *usage = "usage: users [-d | -r file] [-w]";
8935e96a66cSDavid du Colombier 
8945e96a66cSDavid du Colombier 	dflag = wflag = 0;
895*81cf8742SDavid du Colombier 	file = nil;
8965e96a66cSDavid du Colombier 
8975e96a66cSDavid du Colombier 	ARGBEGIN{
8985e96a66cSDavid du Colombier 	default:
8995e96a66cSDavid du Colombier 		return cliError(usage);
9005e96a66cSDavid du Colombier 	case 'd':
9015e96a66cSDavid du Colombier 		dflag = 1;
9025e96a66cSDavid du Colombier 		break;
903*81cf8742SDavid du Colombier 	case 'r':
904*81cf8742SDavid du Colombier 		file = ARGF();
905*81cf8742SDavid du Colombier 		if(file == nil)
906*81cf8742SDavid du Colombier 			return cliError(usage);
907*81cf8742SDavid du Colombier 		break;
9085e96a66cSDavid du Colombier 	case 'w':
9095e96a66cSDavid du Colombier 		wflag = 1;
9105e96a66cSDavid du Colombier 		break;
9115e96a66cSDavid du Colombier 	}ARGEND
9125e96a66cSDavid du Colombier 
913*81cf8742SDavid du Colombier 	if(argc)
9145e96a66cSDavid du Colombier 		return cliError(usage);
915*81cf8742SDavid du Colombier 
916*81cf8742SDavid du Colombier 	if(dflag && file)
917*81cf8742SDavid du Colombier 		return cliError("cannot use -d and -r together");
918*81cf8742SDavid du Colombier 
9195e96a66cSDavid du Colombier 	if(dflag)
920*81cf8742SDavid du Colombier 		uboxInit(usersDefault, sizeof(usersDefault));
921*81cf8742SDavid du Colombier 	else if(file){
922*81cf8742SDavid du Colombier 		if(usersFileRead(file) == 0)
923*81cf8742SDavid du Colombier 			return 0;
924*81cf8742SDavid du Colombier 	}
925*81cf8742SDavid du Colombier 
9265e96a66cSDavid du Colombier 	vtRLock(ubox.lock);
9275e96a66cSDavid du Colombier 	box = ubox.box;
9285e96a66cSDavid du Colombier 	consPrint("\tnuser %d len %d\n", box->nuser, box->len);
9295e96a66cSDavid du Colombier 
930*81cf8742SDavid du Colombier 	r = 1;
931*81cf8742SDavid du Colombier 	if(wflag)
932*81cf8742SDavid du Colombier 		r = usersFileWrite(box);
9335e96a66cSDavid du Colombier 	vtRUnlock(ubox.lock);
9345e96a66cSDavid du Colombier 	return r;
9355e96a66cSDavid du Colombier }
9365e96a66cSDavid du Colombier 
9375e96a66cSDavid du Colombier int
9385e96a66cSDavid du Colombier usersInit(void)
9395e96a66cSDavid du Colombier {
9405e96a66cSDavid du Colombier 	fmtinstall('U', userFmt);
9415e96a66cSDavid du Colombier 
9425e96a66cSDavid du Colombier 	ubox.lock = vtLockAlloc();
943*81cf8742SDavid du Colombier 	uboxInit(usersDefault, sizeof(usersDefault));
9445e96a66cSDavid du Colombier 
9455e96a66cSDavid du Colombier 	cliAddCmd("users", cmdUsers);
9465e96a66cSDavid du Colombier 	cliAddCmd("uname", cmdUname);
9475e96a66cSDavid du Colombier 
9485e96a66cSDavid du Colombier 	return 1;
9495e96a66cSDavid du Colombier }
950