URI:
       t9user.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       t9user.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 }