Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include "all.h"

struct {
        char*   name;
        Userid  uid;
        Userid  lead;
} minusers[] = {
        "adm",          -1,     -1,
        "none",         0,      -1,
        "tor",          1,      1,
        "sys",          10000,  0,
        "map",          10001,  10001,
        "doc",          10002,  0,
        "upas",         10003,  10003,
        "font",         10004,  0,
        "bootes",       10005,  10005,
        0
};

static char buf[4096];
static Rune ichar[] = L"?=+-/:";

Uid*    chkuid(char *name, int chk);
void    do_newuser(int, char*[]);
char*   getword(char*, Rune, char*, int);
void    pentry(char*, Uid*);
int     readln(char*, int);
void    setminusers(void);
Uid*    uidtop(int);

void
cmd_users(int argc, char *argv[])
{
        Uid *ui;
        int u, g, o, line;
        char *file, *p, *uname, *ulead, *unext;

        file = "/adm/users";
        if(argc > 1)
                file = argv[1];

        if(strcmp(file, "default") == 0) {
                setminusers();
                return;
        }

        uidgc.uidbuf = getbuf(devnone, Cuidbuf, 0);
        if(walkto(file) || con_open(FID2, 0)) {
                print("cmd_users: cannot access %s\n", file);
                putbuf(uidgc.uidbuf);
                return;
        }

        uidgc.flen = 0;
        uidgc.find = 0;
        cons.offset = 0;
        cons.nuid = 0;

        u = 0;
        line = 0;
        while(readln(buf, sizeof buf) != 0) {
                line++;
                p = getword(buf, L':', "no : after number", line);
                if(p == nil)
                        continue;
                ulead = getword(p, L':', "no : after name", line);
                if(ulead == nil)
                        continue;

                if(strlen(p) > NAMELEN-1) {
                        print("%s: name too long\n", p);
                        continue;
                }
                strcpy(uid[u].name, p);
                uid[u].uid = number(buf, 0, 10);
                uid[u].lead = 0;
                uid[u].ngrp = 0;
                u++;
                if(u >= conf.nuid) {
                        print("conf.nuid too small (%ld)\n", conf.nuid);
                        break;
                }
        }

        /* Sorted by uid for use in uidtostr */
        wlock(&uidgc.uidlock);
        qsort(uid, u, sizeof(uid[0]), byuid);
        cons.nuid = u;
        wunlock(&uidgc.uidlock);

        /* Parse group table */
        uidgc.flen = 0;
        uidgc.find = 0;
        cons.offset = 0;
        cons.ngid = 0;

        g = 0;
        line = 0;
        while(readln(buf, sizeof buf) != 0) {
                line++;
                uname = getword(buf, L':', 0, 0);       /* skip number */
                if(uname == nil)
                        continue;

                ulead = getword(uname, L':', 0, 0);     /* skip name */
                if(ulead == nil)
                        continue;

                p = getword(ulead, L':', "no : after leader", line);
                if(p == nil)
                        continue;

                ui = uidpstr(uname);
                if(ui == nil)
                        continue;

                /* set to owner if name not known */
                ui->lead = 0;
                if(ulead[0]) {
                        o = strtouid(ulead);
                        if(o >= 0)
                                ui->lead = o;
                        else
                                ui->lead = ui->uid;
                }
                ui->gtab = &gidspace[g];
                ui->ngrp = 0;
                while (p != nil) {
                        unext = getword(p, L',', 0, 0);
                        o = strtouid(p);
                        if(o >= 0) {
                                gidspace[g++] = o;
                                ui->ngrp++;
                        }
                        p = unext;
                }
        }

        cons.ngid = g;

        putbuf(uidgc.uidbuf);
        print("%d uids read, %d groups used\n", cons.nuid, cons.ngid);
}

void
cmd_newuser(int argc, char *argv[])
{
        if(argc <= 1) {
                print("usage: newuser args\n");
                print("\tname -- create a new user\n");
                print("\tname : -- create a new group\n");
                print("\tname ? -- show entry for user\n");
                print("\tname name -- rename\n");
                print("\tname =[name] -- add/alter/remove leader\n");
                print("\tname +name -- add member\n");
                print("\tname -name -- delete member\n");
                return;
        }
        do_newuser(argc, argv);
}

void
do_newuser(int argc, char *argv[])
{
        int i, l, n, nuid;
        char *p, *md, *q;
        Rune *r;
        Userid *s;
        Uid *ui, *u2;

        nuid = 10000;
        md = 0;
        if(argc == 2) {
                nuid = 1;
                argv[2] = ":";
        }

        for(r = ichar; *r; r++)
                if(utfrune(argv[1], *r)) {
                        print("illegal character in name\n");
                        return;
                }
        if(strlen(argv[1]) > NAMELEN-1) {
                print("name %s too long\n", argv[1]);
                return;
        }

        p = argv[2];
        switch(*p) {
        case '?':
                ui = chkuid(argv[1], 1);
                if(ui == 0)
                        return;
                pentry(buf, ui);
                n = strlen(buf);
                p = buf;
                while(n > PRINTSIZE-5) {
                        q = p;
                        p += PRINTSIZE-5;
                        n -= PRINTSIZE-5;
                        i = *p;
                        *p = 0;
                        print("%s", q);
                        *p = i;
                }
                print("%s\n", p);
                return;

        case ':':
                if(chkuid(argv[1], 0))
                        return;
                while(uidtop(nuid) != 0)
                        nuid++;
                if(cons.nuid >= conf.nuid) {
                        print("conf.nuid too small (%ld)\n", conf.nuid);
                        return;
                }

                wlock(&uidgc.uidlock);
                ui = &uid[cons.nuid++];
                ui->uid = nuid;
                ui->lead = 0;
                if(nuid < 10000) {
                        ui->lead = ui->uid;
                        md = argv[1];
                }
                strcpy(ui->name, argv[1]);
                ui->ngrp = 0;
                qsort(uid, cons.nuid, sizeof(uid[0]), byuid);
                wunlock(&uidgc.uidlock);
                break;

        case '=':
                ui = chkuid(argv[1], 1);
                if(ui == 0)
                        return;
                p++;
                if(*p == '\0') {
                        ui->lead = 0;
                        break;
                }
                u2 = chkuid(p, 1);
                if(u2 == 0)
                        return;
                ui->lead = u2->uid;
                break;

        case '+':
                ui = chkuid(argv[1], 1);
                if(ui == 0)
                        return;
                p++;
                u2 = chkuid(p, 1);
                if(u2 == 0)
                        return;
                if(u2->uid == ui->uid)
                        return;
                if(cons.ngid+ui->ngrp+1 >= conf.gidspace) {
                        print("conf.gidspace too small (%ld)\n", conf.gidspace);
                        return;
                }
                for(i = 0; i < ui->ngrp; i++) {
                        if(ui->gtab[i] == u2->uid) {
                                print("member already in group\n");
                                return;
                        }
                }

                wlock(&uidgc.uidlock);
                s = gidspace+cons.ngid;
                memmove(s, ui->gtab, ui->ngrp*sizeof(*s));
                ui->gtab = s;
                s[ui->ngrp++] = u2->uid;
                cons.ngid += ui->ngrp+1;
                wunlock(&uidgc.uidlock);
                break;

        case '-':
                ui = chkuid(argv[1], 1);
                if(ui == 0)
                        return;
                p++;
                u2 = chkuid(p, 1);
                if(u2 == 0)
                        return;
                for(i = 0; i < ui->ngrp; i++)
                        if(ui->gtab[i] == u2->uid)
                                break;

                if(i == ui->ngrp) {
                        print("%s not in group\n", p);
                        return;
                }

                wlock(&uidgc.uidlock);
                s = ui->gtab+i;
                ui->ngrp--;
                memmove(s, s+1, (ui->ngrp-i)*sizeof(*s));
                wunlock(&uidgc.uidlock);
                break;

        default:
                if(chkuid(argv[2], 0))
                        return;

                for(r = ichar; *r; r++)
                        if(utfrune(argv[2], *r)) {
                                print("illegal character in name\n");
                                return;
                        }

                ui = chkuid(argv[1], 1);
                if(ui == 0)
                        return;

                if(strlen(argv[2]) > NAMELEN-1) {
                        print("name %s too long\n", argv[2]);
                        return;
                }

                wlock(&uidgc.uidlock);
                strcpy(ui->name, argv[2]);
                wunlock(&uidgc.uidlock);
                break;
        }


        if(walkto("/adm/users") || con_open(FID2, OWRITE|OTRUNC)) {
                print("can't open /adm/users for write\n");
                return;
        }

        cons.offset = 0;
        for(i = 0; i < cons.nuid; i++) {
                pentry(buf, &uid[i]);
                l = strlen(buf);
                n = con_write(FID2, buf, cons.offset, l);
                if(l != n)
                        print("short write on /adm/users\n");
                cons.offset += n;
        }

        if(md != 0) {
                sprint(buf, "create /usr/%s %s %s 755 d", md, md, md);
                print("%s\n", buf);
                cmd_exec(buf);
        }
}

Uid*
chkuid(char *name, int chk)
{
        Uid *u;

        u = uidpstr(name);
        if(chk == 1) {
                if(u == 0)
                        print("%s does not exist\n", name);
        }
        else {
                if(u != 0)
                        print("%s already exists\n", name);
        }
        return u;
}

void
pentry(char *buf, Uid *u)
{
        int i, posn;
        Uid *p;

        posn = sprint(buf, "%d:%s:", u->uid, u->name);
        p = uidtop(u->lead);
        if(p && u->lead != 0)
                posn += sprint(buf+posn, "%s", p->name);

        posn += sprint(buf+posn, ":");
        for(i = 0; i < u->ngrp; i++) {
                p = uidtop(u->gtab[i]);
                if(i != 0)
                        posn += sprint(buf+posn, ",");
                if(p != 0)
                        posn += sprint(buf+posn, "%s", p->name);
                else
                        posn += sprint(buf+posn, "%d", u->gtab[i]);
        }
        sprint(buf+posn, "\n");
}

void
setminusers(void)
{
        int u;

        for(u = 0; minusers[u].name; u++) {
                strcpy(uid[u].name, minusers[u].name);
                uid[u].uid = minusers[u].uid;
                uid[u].lead = minusers[u].lead;
        }
        cons.nuid = u;
        qsort(uid, u, sizeof(uid[0]), byuid);
}

Uid*
uidpstr(char *name)
{
        Uid *s, *e;

        s = uid;
        for(e = s+cons.nuid; s < e; s++) {
                if(strcmp(name, s->name) == 0)
                        return s;
        }
        return 0;
}

char*
getword(char *buf, Rune delim, char *error, int line)
{
        char *p;

        p = utfrune(buf, delim);
        if(p == 0) {
                if(error)
                        print("cmd_users: %s line %d\n", error, line);
                return 0;
        }
        *p = '\0';
        return p+1;
}

int
strtouid(char *name)
{
        Uid *u;
        int id;

        rlock(&uidgc.uidlock);

        u = uidpstr(name);
        id = -2;
        if(u != 0)
                id = u->uid;

        runlock(&uidgc.uidlock);

        return id;
}

Uid*
uidtop(int id)
{
        Uid *bot, *top, *new;

        bot = uid;
        top = bot + cons.nuid-1;

        while(bot <= top){
                new = bot + (top - bot)/2;
                if(new->uid == id)
                        return new;
                if(new->uid < id)
                        bot = new + 1;
                else
                        top = new - 1;
        }
        return 0;
}

void
uidtostr(char *name, int id, int dolock)
{
        Uid *p;

        if(dolock)
                rlock(&uidgc.uidlock);

        p = uidtop(id);
        if(p == 0)
                strcpy(name, "none");
        else
                strcpy(name, p->name);

        if(dolock)
                runlock(&uidgc.uidlock);
}

int
ingroup(int u, int g)
{
        Uid *p;
        Userid *s, *e;

        if(u == g)
                return 1;

        rlock(&uidgc.uidlock);
        p = uidtop(g);
        if(p != 0) {
                s = p->gtab;
                for(e = s + p->ngrp; s < e; s++) {
                        if(*s == u) {
                                runlock(&uidgc.uidlock);
                                return 1;
                        }
                }
        }
        runlock(&uidgc.uidlock);
        return 0;
}

int
leadgroup(int ui, int gi)
{
        int i;
        Uid *u;

        /* user 'none' cannot be a group leader */
        if(ui == 0)
                return 0;

        rlock(&uidgc.uidlock);
        u = uidtop(gi);
        if(u == 0) {
                runlock(&uidgc.uidlock);
                return 0;
        }
        i = u->lead;
        runlock(&uidgc.uidlock);
        if(i == ui)
                return 1;
        if(i == 0)
                return ingroup(ui, gi);

        return 0;
}

int
byuid(void *a1, void *a2)
{
        Uid *u1, *u2;

        u1 = a1;
        u2 = a2;
        return u1->uid - u2->uid;
}

int
fchar(void)
{
        int n;

        n = BUFSIZE;
        if(n > MAXDAT)
                n = MAXDAT;
        if(uidgc.find >= uidgc.flen) {
                uidgc.find = 0;
                uidgc.flen = con_read(FID2, uidgc.uidbuf->iobuf, cons.offset, n);
                if(uidgc.flen <= 0)
                        return -1;
                cons.offset += uidgc.flen;
        }
        return (uchar)uidgc.uidbuf->iobuf[uidgc.find++];
}

int
readln(char *p, int len)
{
        int n, c;

        n = 0;
        while(len--) {
                c = fchar();
                if(c == -1 || c == '\n')
                        break;
                n++;
                *p++ = c;
        }
        *p = '\0';
        return n;
}