plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

9user.c (17154B)


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