#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "devtab.h" struct Envval { Envval *next; /* for hashing & easy deletion from hash list */ Envval *prev; ulong len; /* length of val that is valid */ int ref; char *val; }; enum{ MAXENV = (BY2PG - sizeof(Envval)), EVHASH = 64, EVFREE = 16, ALIGN = 16, }; struct { Envval *free[EVFREE+1]; char *block; /* the free page we are allocating from */ char *lim; /* end of block */ int npage; /* total pages gotten from newpage() */ }envalloc; QLock evlock; Envval evhash[EVHASH]; char *evscratch; /* for constructing the contents of a file */ Envval *newev(char*, ulong); Envval *evalloc(ulong); void evfree(Envval*); void envreset(void) { evscratch = ialloc(BY2PG, 0); } void envinit(void) { } int envgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp) { Egrp *eg; Env *e; int ans; eg = u->p->egrp; qlock(&eg->ev); if(s >= eg->nenv) ans = -1; else{ e = &eg->etab[s]; if(!e->name) ans = 0; else{ devdir(c, (Qid){s+1, (ulong)e->val}, e->name->val, e->val? e->val->len : 0, eve, 0666, dp); ans = 1; } } qunlock(&eg->ev); return ans; } Chan* envattach(char *spec) { return devattach('e', spec); } Chan* envclone(Chan *c, Chan *nc) { return devclone(c, nc); } int envwalk(Chan *c, char *name) { return devwalk(c, name, 0, 0, envgen); } void envstat(Chan *c, char *db) { devstat(c, db, 0, 0, envgen); } Chan * envopen(Chan *c, int omode) { Egrp *eg; Env *e; int mode; mode = openmode(omode); if(c->qid.path & CHDIR){ if(omode != OREAD) error(Eperm); }else{ eg = u->p->egrp; qlock(&eg->ev); e = &eg->etab[c->qid.path-1]; if(!e->name){ qunlock(&eg->ev); error(Enonexist); } if(omode == (OWRITE|OTRUNC) && e->val){ qlock(&evlock); evfree(e->val); qunlock(&evlock); e->val = 0; } qunlock(&eg->ev); } c->mode = mode; c->flag |= COPEN; c->offset = 0; return c; } void envcreate(Chan *c, char *name, int omode, ulong perm) { Egrp *eg; Env *e, *ne; int i; if(c->qid.path != CHDIR) error(Eperm); omode = openmode(omode); eg = u->p->egrp; qlock(&eg->ev); e = eg->etab; ne = 0; for(i = 0; i < eg->nenv; i++, e++) if(e->name == 0) ne = e; else if(strcmp(e->name->val, name) == 0){ qunlock(&eg->ev); error(Einuse); } if(ne) e = ne; else if(eg->nenv == conf.npgenv){ qunlock(&eg->ev); print("out of egroup envs\n"); error(Enoenv); } i = e - eg->etab + 1; e->val = 0; qlock(&evlock); e->name = newev(name, strlen(name)+1); qunlock(&evlock); if(i > eg->nenv) eg->nenv = i; qunlock(&eg->ev); c->qid = (Qid){i, 0}; c->offset = 0; c->mode = omode; c->flag |= COPEN; } void envremove(Chan *c) { Egrp *eg; Env *e; if(c->qid.path & CHDIR) error(Eperm); eg = u->p->egrp; qlock(&eg->ev); e = &eg->etab[c->qid.path-1]; if(!e->name){ qunlock(&eg->ev); error(Enonexist); } envpgclose(e); qunlock(&eg->ev); } void envwstat(Chan *c, char *db) { USED(c, db); error(Eperm); } void envclose(Chan * c) { USED(c); } void envpgcopy(Env *t, Env *f) { qlock(&evlock); if(t->name = f->name) t->name->ref++; if(t->val = f->val) t->val->ref++; qunlock(&evlock); } void envpgclose(Env *e) { qlock(&evlock); if(e->name) evfree(e->name); if(e->val) evfree(e->val); e->name = e->val = 0; qunlock(&evlock); } long envread(Chan *c, void *a, long n, ulong offset) { Egrp *eg; Env *e; Envval *ev; long vn; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, envgen); eg = u->p->egrp; qlock(&eg->ev); e = &eg->etab[c->qid.path-1]; if(!e->name){ qunlock(&eg->ev); error(Enonexist); } ev = e->val; vn = ev ? ev->len : 0; if(offset + n > vn) n = vn - offset; if(n <= 0) n = 0; else memmove(a, ev->val + offset, n); qunlock(&eg->ev); return n; } long envwrite(Chan *c, void *a, long n, ulong offset) { Egrp *eg; Env *e; Envval *ev; ulong olen; if(n <= 0) return 0; olen = (offset + n + ALIGN - 1) & ~(ALIGN - 1); if(olen > MAXENV) error(Etoobig); eg = u->p->egrp; qlock(&eg->ev); e = &eg->etab[c->qid.path-1]; if(!e->name){ qunlock(&eg->ev); error(Enonexist); } ev = e->val; olen = ev ? ev->len : 0; qlock(&evlock); if(offset == 0 && n >= olen) e->val = newev(a, n); else{ if(olen > offset) olen = offset; if(ev) memmove(evscratch, ev->val, olen); if(olen < offset) memset(evscratch + olen, '\0', offset - olen); memmove(evscratch + offset, a, n); e->val = newev(evscratch, offset + n); } if(ev) evfree(ev); qunlock(&evlock); qunlock(&eg->ev); return n; } /* * called with evlock qlocked */ Envval * newev(char *s, ulong n) { Envval *ev; uchar *t; int h; h = 0; for(t = (uchar*)s; t - (uchar*)s < n; t++) h = (h << 1) ^ *t; h &= EVHASH - 1; for(ev = evhash[h].next; ev; ev = ev->next) if(ev->len == n && memcmp(ev->val, s, n) == 0){ ev->ref++; return ev; } ev = evalloc(n); ev->len = n; memmove(ev->val, s, n); if(ev->next = evhash[h].next) ev->next->prev = ev; evhash[h].next = ev; ev->prev = &evhash[h]; return ev; } /* * called only from newev */ Envval * evalloc(ulong n) { Envval *ev, **p; char *b, *lim; ulong size; size = (n + ALIGN - 1) & ~(ALIGN - 1); n = (size - 1) / ALIGN; p = &envalloc.free[n < EVFREE ? n : EVFREE]; for(ev = *p; ev; ev = *p){ if(ev->len == size){ *p = ev->next; ev->ref = 1; return ev; } p = &ev->next; } /* * make sure we have enough space to allocate the buffer. * if not, use the remaining space for the smallest buffers */ if(size > MAXENV) panic("evalloc"); b = envalloc.block; lim = envalloc.lim; if(!b || lim < b + size + sizeof *ev){ p = &envalloc.free[0]; while(lim >= b + ALIGN + sizeof *ev){ ev = (Envval*)b; ev->len = ALIGN; ev->val = b + sizeof *ev; ev->next = *p; *p = ev; b += ALIGN + sizeof *ev; } b = (char*)VA(kmap(newpage(0, 0, 0))); envalloc.npage++; envalloc.lim = b + BY2PG; } ev = (Envval*)b; ev->val = b + sizeof *ev; ev->ref = 1; envalloc.block = b + size + sizeof *ev; return ev; } /* * called with evlock qlocked */ void evfree(Envval *ev) { int n; if(--ev->ref > 0) return; if(ev->prev) ev->prev->next = ev->next; else panic("evfree"); if(ev->next) ev->next->prev = ev->prev; n = (ev->len + ALIGN - 1) & ~(ALIGN - 1); ev->len = n; n = (n - 1) / ALIGN; if(n > EVFREE) n = EVFREE; ev->next = envalloc.free[n]; ev->prev = 0; envalloc.free[n] = ev; } /* * to let the kernel set environment variables */ void ksetenv(char *ename, char *eval) { Chan *c; char buf[2*NAMELEN]; sprint(buf, "#e/%s", ename); c = namec(buf, Acreate, OWRITE, 0600); (*devtab[c->type].write)(c, eval, strlen(eval), 0); close(c); } void ksetterm(char *f) { char buf[2*NAMELEN]; sprint(buf, f, conffile); ksetenv("terminal", buf); }