951 lines
17 KiB
C
951 lines
17 KiB
C
#include "stdinc.h"
|
|
|
|
#include "9.h"
|
|
|
|
enum {
|
|
NUserHash = 1009,
|
|
};
|
|
|
|
typedef struct Ubox Ubox;
|
|
typedef struct User User;
|
|
|
|
struct User {
|
|
char* uid;
|
|
char* uname;
|
|
char* leader;
|
|
char** group;
|
|
int ngroup;
|
|
|
|
User* next; /* */
|
|
User* ihash; /* lookup by .uid */
|
|
User* nhash; /* lookup by .uname */
|
|
};
|
|
|
|
#pragma varargck type "U" User*
|
|
|
|
struct Ubox {
|
|
User* head;
|
|
User* tail;
|
|
int nuser;
|
|
int len;
|
|
|
|
User* ihash[NUserHash]; /* lookup by .uid */
|
|
User* nhash[NUserHash]; /* lookup by .uname */
|
|
};
|
|
|
|
static struct {
|
|
RWLock lock;
|
|
|
|
Ubox* box;
|
|
} ubox;
|
|
|
|
static char usersDefault[] = {
|
|
"adm:adm:adm:sys\n"
|
|
"none:none::\n"
|
|
"noworld:noworld::\n"
|
|
"sys:sys::glenda\n"
|
|
"glenda:glenda:glenda:\n"
|
|
};
|
|
|
|
static char* usersMandatory[] = {
|
|
"adm",
|
|
"none",
|
|
"noworld",
|
|
"sys",
|
|
nil,
|
|
};
|
|
|
|
char* uidadm = "adm";
|
|
char* unamenone = "none";
|
|
char* uidnoworld = "noworld";
|
|
|
|
static u32int
|
|
userHash(char* s)
|
|
{
|
|
uchar *p;
|
|
u32int hash;
|
|
|
|
hash = 0;
|
|
for(p = (uchar*)s; *p != '\0'; p++)
|
|
hash = hash*7 + *p;
|
|
|
|
return hash % NUserHash;
|
|
}
|
|
|
|
static User*
|
|
_userByUid(Ubox* box, char* uid)
|
|
{
|
|
User *u;
|
|
|
|
if(box != nil){
|
|
for(u = box->ihash[userHash(uid)]; u != nil; u = u->ihash){
|
|
if(strcmp(u->uid, uid) == 0)
|
|
return u;
|
|
}
|
|
}
|
|
werrstr("uname: uid '%s' not found", uid);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
unameByUid(char* uid)
|
|
{
|
|
User *u;
|
|
char *uname;
|
|
|
|
rlock(&ubox.lock);
|
|
if((u = _userByUid(ubox.box, uid)) == nil){
|
|
runlock(&ubox.lock);
|
|
return nil;
|
|
}
|
|
uname = vtstrdup(u->uname);
|
|
runlock(&ubox.lock);
|
|
|
|
return uname;
|
|
}
|
|
|
|
static User*
|
|
_userByUname(Ubox* box, char* uname)
|
|
{
|
|
User *u;
|
|
|
|
if(box != nil){
|
|
for(u = box->nhash[userHash(uname)]; u != nil; u = u->nhash){
|
|
if(strcmp(u->uname, uname) == 0)
|
|
return u;
|
|
}
|
|
}
|
|
werrstr("uname: uname '%s' not found", uname);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
uidByUname(char* uname)
|
|
{
|
|
User *u;
|
|
char *uid;
|
|
|
|
rlock(&ubox.lock);
|
|
if((u = _userByUname(ubox.box, uname)) == nil){
|
|
runlock(&ubox.lock);
|
|
return nil;
|
|
}
|
|
uid = vtstrdup(u->uid);
|
|
runlock(&ubox.lock);
|
|
|
|
return uid;
|
|
}
|
|
|
|
static int
|
|
_groupMember(Ubox* box, char* group, char* member, int whenNoGroup)
|
|
{
|
|
int i;
|
|
User *g, *m;
|
|
|
|
/*
|
|
* Is 'member' a member of 'group'?
|
|
* Note that 'group' is a 'uid' and not a 'uname'.
|
|
* A 'member' is automatically in their own group.
|
|
*/
|
|
if((g = _userByUid(box, group)) == nil)
|
|
return whenNoGroup;
|
|
if((m = _userByUname(box, member)) == nil)
|
|
return 0;
|
|
if(m == g)
|
|
return 1;
|
|
for(i = 0; i < g->ngroup; i++){
|
|
if(strcmp(g->group[i], member) == 0)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
groupWriteMember(char* uname)
|
|
{
|
|
int ret;
|
|
|
|
/*
|
|
* If there is a ``write'' group, then only its members can write
|
|
* to the file system, no matter what the permission bits say.
|
|
*
|
|
* To users not in the ``write'' group, the file system appears
|
|
* read only. This is used to serve sources.cs.bell-labs.com
|
|
* to the world.
|
|
*
|
|
* Note that if there is no ``write'' group, then this routine
|
|
* makes it look like everyone is a member -- the opposite
|
|
* of what groupMember does.
|
|
*
|
|
* We use this for sources.cs.bell-labs.com.
|
|
* If this slows things down too much on systems that don't
|
|
* use this functionality, we could cache the write group lookup.
|
|
*/
|
|
|
|
rlock(&ubox.lock);
|
|
ret = _groupMember(ubox.box, "write", uname, 1);
|
|
runlock(&ubox.lock);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_groupRemMember(Ubox* box, User* g, char* member)
|
|
{
|
|
int i;
|
|
|
|
if(_userByUname(box, member) == nil)
|
|
return 0;
|
|
|
|
for(i = 0; i < g->ngroup; i++){
|
|
if(strcmp(g->group[i], member) == 0)
|
|
break;
|
|
}
|
|
if(i >= g->ngroup){
|
|
if(strcmp(g->uname, member) == 0)
|
|
werrstr("uname: '%s' always in own group", member);
|
|
else
|
|
werrstr("uname: '%s' not in group '%s'",
|
|
member, g->uname);
|
|
return 0;
|
|
}
|
|
|
|
vtfree(g->group[i]);
|
|
|
|
box->len -= strlen(member);
|
|
if(g->ngroup > 1)
|
|
box->len--;
|
|
g->ngroup--;
|
|
switch(g->ngroup){
|
|
case 0:
|
|
vtfree(g->group);
|
|
g->group = nil;
|
|
break;
|
|
default:
|
|
for(; i < g->ngroup; i++)
|
|
g->group[i] = g->group[i+1];
|
|
g->group[i] = nil; /* prevent accidents */
|
|
g->group = vtrealloc(g->group, g->ngroup * sizeof(char*));
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_groupAddMember(Ubox* box, User* g, char* member)
|
|
{
|
|
User *u;
|
|
|
|
if((u = _userByUname(box, member)) == nil)
|
|
return 0;
|
|
if(_groupMember(box, g->uid, u->uname, 0)){
|
|
if(strcmp(g->uname, member) == 0)
|
|
werrstr("uname: '%s' always in own group", member);
|
|
else
|
|
werrstr("uname: '%s' already in group '%s'",
|
|
member, g->uname);
|
|
return 0;
|
|
}
|
|
|
|
g->group = vtrealloc(g->group, (g->ngroup+1)*sizeof(char*));
|
|
g->group[g->ngroup] = vtstrdup(member);
|
|
box->len += strlen(member);
|
|
g->ngroup++;
|
|
if(g->ngroup > 1)
|
|
box->len++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
groupMember(char* group, char* member)
|
|
{
|
|
int r;
|
|
|
|
if(group == nil)
|
|
return 0;
|
|
|
|
rlock(&ubox.lock);
|
|
r = _groupMember(ubox.box, group, member, 0);
|
|
runlock(&ubox.lock);
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
groupLeader(char* group, char* member)
|
|
{
|
|
int r;
|
|
User *g;
|
|
|
|
/*
|
|
* Is 'member' the leader of 'group'?
|
|
* Note that 'group' is a 'uid' and not a 'uname'.
|
|
* Uname 'none' cannot be a group leader.
|
|
*/
|
|
if(strcmp(member, unamenone) == 0 || group == nil)
|
|
return 0;
|
|
|
|
rlock(&ubox.lock);
|
|
if((g = _userByUid(ubox.box, group)) == nil){
|
|
runlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
if(g->leader != nil){
|
|
if(strcmp(g->leader, member) == 0){
|
|
runlock(&ubox.lock);
|
|
return 1;
|
|
}
|
|
r = 0;
|
|
}
|
|
else
|
|
r = _groupMember(ubox.box, group, member, 0);
|
|
runlock(&ubox.lock);
|
|
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
userFree(User* u)
|
|
{
|
|
int i;
|
|
|
|
vtfree(u->uid);
|
|
vtfree(u->uname);
|
|
if(u->leader != nil)
|
|
vtfree(u->leader);
|
|
if(u->ngroup){
|
|
for(i = 0; i < u->ngroup; i++)
|
|
vtfree(u->group[i]);
|
|
vtfree(u->group);
|
|
}
|
|
vtfree(u);
|
|
}
|
|
|
|
static User*
|
|
userAlloc(char* uid, char* uname)
|
|
{
|
|
User *u;
|
|
|
|
u = vtmallocz(sizeof(User));
|
|
u->uid = vtstrdup(uid);
|
|
u->uname = vtstrdup(uname);
|
|
|
|
return u;
|
|
}
|
|
|
|
int
|
|
validUserName(char* name)
|
|
{
|
|
Rune *r;
|
|
#ifdef PLAN9PORT
|
|
static Rune invalid[] = {'#', ':', ',', '(', ')', '\0'};
|
|
#else
|
|
static Rune invalid[] = L"#:,()";
|
|
#endif
|
|
|
|
for(r = invalid; *r != '\0'; r++){
|
|
if(utfrune(name, *r))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
userFmt(Fmt* fmt)
|
|
{
|
|
User *u;
|
|
int i, r;
|
|
|
|
u = va_arg(fmt->args, User*);
|
|
|
|
r = fmtprint(fmt, "%s:%s:", u->uid, u->uname);
|
|
if(u->leader != nil)
|
|
r += fmtprint(fmt, u->leader);
|
|
r += fmtprint(fmt, ":");
|
|
if(u->ngroup){
|
|
r += fmtprint(fmt, u->group[0]);
|
|
for(i = 1; i < u->ngroup; i++)
|
|
r += fmtprint(fmt, ",%s", u->group[i]);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
usersFileWrite(Ubox* box)
|
|
{
|
|
Fs *fs;
|
|
User *u;
|
|
int i, r;
|
|
Fsys *fsys;
|
|
char *p, *q, *s;
|
|
File *dir, *file;
|
|
|
|
if((fsys = fsysGet("main")) == nil)
|
|
return 0;
|
|
fsysFsRlock(fsys);
|
|
fs = fsysGetFs(fsys);
|
|
|
|
/*
|
|
* BUG:
|
|
* the owner/group/permissions need to be thought out.
|
|
*/
|
|
r = 0;
|
|
if((dir = fileOpen(fs, "/active")) == nil)
|
|
goto tidy0;
|
|
if((file = fileWalk(dir, uidadm)) == nil)
|
|
file = fileCreate(dir, uidadm, ModeDir|0775, uidadm);
|
|
fileDecRef(dir);
|
|
if(file == nil)
|
|
goto tidy;
|
|
dir = file;
|
|
if((file = fileWalk(dir, "users")) == nil)
|
|
file = fileCreate(dir, "users", 0664, uidadm);
|
|
fileDecRef(dir);
|
|
if(file == nil)
|
|
goto tidy;
|
|
if(!fileTruncate(file, uidadm))
|
|
goto tidy;
|
|
|
|
p = s = vtmalloc(box->len+1);
|
|
q = p + box->len+1;
|
|
for(u = box->head; u != nil; u = u->next){
|
|
p += snprint(p, q-p, "%s:%s:", u->uid, u->uname);
|
|
if(u->leader != nil)
|
|
p+= snprint(p, q-p, u->leader);
|
|
p += snprint(p, q-p, ":");
|
|
if(u->ngroup){
|
|
p += snprint(p, q-p, u->group[0]);
|
|
for(i = 1; i < u->ngroup; i++)
|
|
p += snprint(p, q-p, ",%s", u->group[i]);
|
|
}
|
|
p += snprint(p, q-p, "\n");
|
|
}
|
|
r = fileWrite(file, s, box->len, 0, uidadm);
|
|
vtfree(s);
|
|
|
|
tidy:
|
|
if(file != nil)
|
|
fileDecRef(file);
|
|
tidy0:
|
|
fsysFsRUnlock(fsys);
|
|
fsysPut(fsys);
|
|
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
uboxRemUser(Ubox* box, User *u)
|
|
{
|
|
User **h, *up;
|
|
|
|
h = &box->ihash[userHash(u->uid)];
|
|
for(up = *h; up != nil && up != u; up = up->ihash)
|
|
h = &up->ihash;
|
|
assert(up == u);
|
|
*h = up->ihash;
|
|
box->len -= strlen(u->uid);
|
|
|
|
h = &box->nhash[userHash(u->uname)];
|
|
for(up = *h; up != nil && up != u; up = up->nhash)
|
|
h = &up->nhash;
|
|
assert(up == u);
|
|
*h = up->nhash;
|
|
box->len -= strlen(u->uname);
|
|
|
|
h = &box->head;
|
|
for(up = *h; up != nil && strcmp(up->uid, u->uid) != 0; up = up->next)
|
|
h = &up->next;
|
|
assert(up == u);
|
|
*h = u->next;
|
|
u->next = nil;
|
|
|
|
box->len -= 4;
|
|
box->nuser--;
|
|
}
|
|
|
|
static void
|
|
uboxAddUser(Ubox* box, User* u)
|
|
{
|
|
User **h, *up;
|
|
|
|
h = &box->ihash[userHash(u->uid)];
|
|
u->ihash = *h;
|
|
*h = u;
|
|
box->len += strlen(u->uid);
|
|
|
|
h = &box->nhash[userHash(u->uname)];
|
|
u->nhash = *h;
|
|
*h = u;
|
|
box->len += strlen(u->uname);
|
|
|
|
h = &box->head;
|
|
for(up = *h; up != nil && strcmp(up->uid, u->uid) < 0; up = up->next)
|
|
h = &up->next;
|
|
u->next = *h;
|
|
*h = u;
|
|
|
|
box->len += 4;
|
|
box->nuser++;
|
|
}
|
|
|
|
static void
|
|
uboxDump(Ubox* box)
|
|
{
|
|
User* u;
|
|
|
|
consPrint("nuser %d len = %d\n", box->nuser, box->len);
|
|
|
|
for(u = box->head; u != nil; u = u->next)
|
|
consPrint("%U\n", u);
|
|
}
|
|
|
|
static void
|
|
uboxFree(Ubox* box)
|
|
{
|
|
User *next, *u;
|
|
|
|
for(u = box->head; u != nil; u = next){
|
|
next = u->next;
|
|
userFree(u);
|
|
}
|
|
vtfree(box);
|
|
}
|
|
|
|
static int
|
|
uboxInit(char* users, int len)
|
|
{
|
|
User *g, *u;
|
|
Ubox *box, *obox;
|
|
int blank, comment, i, nline, nuser;
|
|
char *buf, *f[5], **line, *p, *q, *s;
|
|
|
|
/*
|
|
* Strip out whitespace and comments.
|
|
* Note that comments are pointless, they disappear
|
|
* when the server writes the database back out.
|
|
*/
|
|
blank = 1;
|
|
comment = nline = 0;
|
|
|
|
s = p = buf = vtmalloc(len+1);
|
|
for(q = users; *q != '\0'; q++){
|
|
if(*q == '\r' || *q == '\t' || *q == ' ')
|
|
continue;
|
|
if(*q == '\n'){
|
|
if(!blank){
|
|
if(p != s){
|
|
*p++ = '\n';
|
|
nline++;
|
|
s = p;
|
|
}
|
|
blank = 1;
|
|
}
|
|
comment = 0;
|
|
continue;
|
|
}
|
|
if(*q == '#')
|
|
comment = 1;
|
|
blank = 0;
|
|
if(!comment)
|
|
*p++ = *q;
|
|
}
|
|
*p = '\0';
|
|
|
|
line = vtmallocz((nline+2)*sizeof(char*));
|
|
if((i = gettokens(buf, line, nline+2, "\n")) != nline){
|
|
fprint(2, "nline %d (%d) botch\n", nline, i);
|
|
vtfree(line);
|
|
vtfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Everything is updated in a local Ubox until verified.
|
|
*/
|
|
box = vtmallocz(sizeof(Ubox));
|
|
|
|
/*
|
|
* First pass - check format, check for duplicates
|
|
* and enter in hash buckets.
|
|
*/
|
|
nuser = 0;
|
|
for(i = 0; i < nline; i++){
|
|
s = vtstrdup(line[i]);
|
|
if(getfields(s, f, nelem(f), 0, ":") != 4){
|
|
fprint(2, "bad line '%s'\n", line[i]);
|
|
vtfree(s);
|
|
continue;
|
|
}
|
|
if(*f[0] == '\0' || *f[1] == '\0'){
|
|
fprint(2, "bad line '%s'\n", line[i]);
|
|
vtfree(s);
|
|
continue;
|
|
}
|
|
if(!validUserName(f[0])){
|
|
fprint(2, "invalid uid '%s'\n", f[0]);
|
|
vtfree(s);
|
|
continue;
|
|
}
|
|
if(_userByUid(box, f[0]) != nil){
|
|
fprint(2, "duplicate uid '%s'\n", f[0]);
|
|
vtfree(s);
|
|
continue;
|
|
}
|
|
if(!validUserName(f[1])){
|
|
fprint(2, "invalid uname '%s'\n", f[0]);
|
|
vtfree(s);
|
|
continue;
|
|
}
|
|
if(_userByUname(box, f[1]) != nil){
|
|
fprint(2, "duplicate uname '%s'\n", f[1]);
|
|
vtfree(s);
|
|
continue;
|
|
}
|
|
|
|
u = userAlloc(f[0], f[1]);
|
|
uboxAddUser(box, u);
|
|
line[nuser] = line[i];
|
|
nuser++;
|
|
|
|
vtfree(s);
|
|
}
|
|
assert(box->nuser == nuser);
|
|
|
|
/*
|
|
* Second pass - fill in leader and group information.
|
|
*/
|
|
for(i = 0; i < nuser; i++){
|
|
s = vtstrdup(line[i]);
|
|
getfields(s, f, nelem(f), 0, ":");
|
|
|
|
assert(g = _userByUname(box, f[1]));
|
|
if(*f[2] != '\0'){
|
|
if((u = _userByUname(box, f[2])) == nil)
|
|
g->leader = vtstrdup(g->uname);
|
|
else
|
|
g->leader = vtstrdup(u->uname);
|
|
box->len += strlen(g->leader);
|
|
}
|
|
for(p = f[3]; p != nil; p = q){
|
|
if((q = utfrune(p, L',')) != nil)
|
|
*q++ = '\0';
|
|
if(!_groupAddMember(box, g, p)){
|
|
// print/log error here
|
|
}
|
|
}
|
|
|
|
vtfree(s);
|
|
}
|
|
|
|
vtfree(line);
|
|
vtfree(buf);
|
|
|
|
for(i = 0; usersMandatory[i] != nil; i++){
|
|
if((u = _userByUid(box, usersMandatory[i])) == nil){
|
|
werrstr("user '%s' is mandatory", usersMandatory[i]);
|
|
uboxFree(box);
|
|
return 0;
|
|
}
|
|
if(strcmp(u->uid, u->uname) != 0){
|
|
werrstr("uid/uname for user '%s' must match",
|
|
usersMandatory[i]);
|
|
uboxFree(box);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
wlock(&ubox.lock);
|
|
obox = ubox.box;
|
|
ubox.box = box;
|
|
wunlock(&ubox.lock);
|
|
|
|
if(obox != nil)
|
|
uboxFree(obox);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
usersFileRead(char* path)
|
|
{
|
|
char *p;
|
|
File *file;
|
|
Fsys *fsys;
|
|
int len, r;
|
|
uvlong size;
|
|
|
|
if((fsys = fsysGet("main")) == nil)
|
|
return 0;
|
|
fsysFsRlock(fsys);
|
|
|
|
if(path == nil)
|
|
path = "/active/adm/users";
|
|
|
|
r = 0;
|
|
if((file = fileOpen(fsysGetFs(fsys), path)) != nil){
|
|
if(fileGetSize(file, &size)){
|
|
len = size;
|
|
p = vtmalloc(size+1);
|
|
if(fileRead(file, p, len, 0) == len){
|
|
p[len] = '\0';
|
|
r = uboxInit(p, len);
|
|
}
|
|
}
|
|
fileDecRef(file);
|
|
}
|
|
|
|
fsysFsRUnlock(fsys);
|
|
fsysPut(fsys);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
cmdUname(int argc, char* argv[])
|
|
{
|
|
User *u, *up;
|
|
int d, dflag, i, r;
|
|
char *p, *uid, *uname;
|
|
char *createfmt = "fsys main create /active/usr/%s %s %s d775";
|
|
char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]";
|
|
|
|
dflag = 0;
|
|
|
|
ARGBEGIN{
|
|
default:
|
|
return cliError(usage);
|
|
case 'd':
|
|
dflag = 1;
|
|
break;
|
|
}ARGEND
|
|
|
|
if(argc < 1){
|
|
if(!dflag)
|
|
return cliError(usage);
|
|
rlock(&ubox.lock);
|
|
uboxDump(ubox.box);
|
|
runlock(&ubox.lock);
|
|
return 1;
|
|
}
|
|
|
|
uname = argv[0];
|
|
argc--; argv++;
|
|
|
|
if(argc == 0){
|
|
rlock(&ubox.lock);
|
|
if((u = _userByUname(ubox.box, uname)) == nil){
|
|
runlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
consPrint("\t%U\n", u);
|
|
runlock(&ubox.lock);
|
|
return 1;
|
|
}
|
|
|
|
wlock(&ubox.lock);
|
|
u = _userByUname(ubox.box, uname);
|
|
while(argc--){
|
|
if(argv[0][0] == '%'){
|
|
if(u == nil){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
p = &argv[0][1];
|
|
if((up = _userByUname(ubox.box, p)) != nil){
|
|
werrstr("uname: uname '%s' already exists",
|
|
up->uname);
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
for(i = 0; usersMandatory[i] != nil; i++){
|
|
if(strcmp(usersMandatory[i], uname) != 0)
|
|
continue;
|
|
werrstr("uname: uname '%s' is mandatory",
|
|
uname);
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
|
|
d = strlen(p) - strlen(u->uname);
|
|
for(up = ubox.box->head; up != nil; up = up->next){
|
|
if(up->leader != nil){
|
|
if(strcmp(up->leader, u->uname) == 0){
|
|
vtfree(up->leader);
|
|
up->leader = vtstrdup(p);
|
|
ubox.box->len += d;
|
|
}
|
|
}
|
|
for(i = 0; i < up->ngroup; i++){
|
|
if(strcmp(up->group[i], u->uname) != 0)
|
|
continue;
|
|
vtfree(up->group[i]);
|
|
up->group[i] = vtstrdup(p);
|
|
ubox.box->len += d;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uboxRemUser(ubox.box, u);
|
|
vtfree(u->uname);
|
|
u->uname = vtstrdup(p);
|
|
uboxAddUser(ubox.box, u);
|
|
}
|
|
else if(argv[0][0] == '='){
|
|
if(u == nil){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
|
|
if(argv[0][1] != '\0'){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
}
|
|
if(u->leader != nil){
|
|
ubox.box->len -= strlen(u->leader);
|
|
vtfree(u->leader);
|
|
u->leader = nil;
|
|
}
|
|
if(up != nil){
|
|
u->leader = vtstrdup(up->uname);
|
|
ubox.box->len += strlen(u->leader);
|
|
}
|
|
}
|
|
else if(argv[0][0] == '+'){
|
|
if(u == nil){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
if(!_groupAddMember(ubox.box, u, up->uname)){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
}
|
|
else if(argv[0][0] == '-'){
|
|
if(u == nil){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
if(!_groupRemMember(ubox.box, u, up->uname)){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
}
|
|
else{
|
|
if(u != nil){
|
|
werrstr("uname: uname '%s' already exists",
|
|
u->uname);
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
|
|
uid = argv[0];
|
|
if(*uid == ':')
|
|
uid++;
|
|
if((u = _userByUid(ubox.box, uid)) != nil){
|
|
werrstr("uname: uid '%s' already exists",
|
|
u->uid);
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
|
|
u = userAlloc(uid, uname);
|
|
uboxAddUser(ubox.box, u);
|
|
if(argv[0][0] != ':'){
|
|
// should have an option for the mode and gid
|
|
p = smprint(createfmt, uname, uname, uname);
|
|
r = cliExec(p);
|
|
vtfree(p);
|
|
if(r == 0){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
argv++;
|
|
}
|
|
|
|
if(usersFileWrite(ubox.box) == 0){
|
|
wunlock(&ubox.lock);
|
|
return 0;
|
|
}
|
|
if(dflag)
|
|
uboxDump(ubox.box);
|
|
wunlock(&ubox.lock);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
cmdUsers(int argc, char* argv[])
|
|
{
|
|
Ubox *box;
|
|
int dflag, r, wflag;
|
|
char *file;
|
|
char *usage = "usage: users [-d | -r file] [-w]";
|
|
|
|
dflag = wflag = 0;
|
|
file = nil;
|
|
|
|
ARGBEGIN{
|
|
default:
|
|
return cliError(usage);
|
|
case 'd':
|
|
dflag = 1;
|
|
break;
|
|
case 'r':
|
|
file = ARGF();
|
|
if(file == nil)
|
|
return cliError(usage);
|
|
break;
|
|
case 'w':
|
|
wflag = 1;
|
|
break;
|
|
}ARGEND
|
|
|
|
if(argc)
|
|
return cliError(usage);
|
|
|
|
if(dflag && file)
|
|
return cliError("cannot use -d and -r together");
|
|
|
|
if(dflag)
|
|
uboxInit(usersDefault, sizeof(usersDefault));
|
|
else if(file){
|
|
if(usersFileRead(file) == 0)
|
|
return 0;
|
|
}
|
|
|
|
rlock(&ubox.lock);
|
|
box = ubox.box;
|
|
consPrint("\tnuser %d len %d\n", box->nuser, box->len);
|
|
|
|
r = 1;
|
|
if(wflag)
|
|
r = usersFileWrite(box);
|
|
runlock(&ubox.lock);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
usersInit(void)
|
|
{
|
|
fmtinstall('U', userFmt);
|
|
|
|
uboxInit(usersDefault, sizeof(usersDefault));
|
|
|
|
cliAddCmd("users", cmdUsers);
|
|
cliAddCmd("uname", cmdUname);
|
|
|
|
return 1;
|
|
}
|