#include #include #include #include #include #include /* * Error semantics: * 1. Replicas are removed on I/O errors. * 2. I/O fails when all the replicas failed. * 3. Errors are ignored on closes and removes * * Locking: * - glock held while changing list of panels * - g->Lock held while changing g->repl * - Events keep a ref to a panel. A panel is * removed when the user asks for it. But the data * structure is released only when all pending * events pointing to the panel have been processed. */ static Panel* panels; static QLock glock; // for all panels. #define ddump if(omerodebug>1)paneldump #define dprint if(omerodebug)fprint void paneldump(int fd) { Panel* g; Repl* r; qlock(&glock); for(g = panels; g; g = g->next){ qlock(g); fprint(fd, "%s %ld ref:\n", g->name, g->ref); for (r = g->repl; r; r = r->next) fprint(fd, "\t%s dfd=%d cfd=%d\n", r->path, r->fd[Fdata], r->fd[Fctl]); qunlock(g); } qunlock(&glock); } static int prefix(char* pref, int prefl, char* path) { if (!strncmp(pref, path, prefl)) if (path[prefl] == '/' || path[prefl] == 0) return 1; return 0; } static Panel* panelalloc(char* name) { Panel* g; g = emalloc(sizeof(Panel)); memset(g, 0, sizeof(*g)); g->next = panels; g->name = estrdup(name); assert(g->name); panels = g; return g; } /* Used by newpanel and by unpackevent. * This adds a new Ref for Panel. */ Panel* findpanel(char* n, int mkit) { Panel* g; qlock(&glock); for(g = panels; g; g = g->next) if (!strcmp(g->name, n)) break; if (g && g->gone){ qunlock(&glock); return nil; } if (mkit && !g) g = panelalloc(n); if (g != nil) incref(g); qunlock(&glock); return g; } Repl* findrepl(Panel* g, char* path, int mkit) { Repl* r; qlock(g); for (r = g->repl; r; r = r->next) if (!strcmp(r->path, path)) break; if (!r && mkit){ r = emalloc(sizeof(Repl)); r->path= strdup(path); r->fd[0] = r->fd[1] =-1; r->next = g->repl; g->repl = r; g->nrepl++; } qunlock(g); return r; } /* Creates a new panel or returns a * ref to one created by an event. * This means one Ref into Panel. */ Panel* newpanel(char* path, int mkit) { Panel* g; char* name; name = strrchr(path, '/'); if (!name){ werrstr("newpanel: path must be absolute"); return nil; } name++; if (g = findpanel(name, mkit)) findrepl(g, path, 1); return g; } Panel* mkpanel(char* path) { int fd; Panel* g; fd = create(path, OREAD, 0775|DMDIR); if (fd < 0) return nil; close(fd); g = newpanel(path, 1); dprint(2, "mkpanel: %s\n", path); return g; } static void rmrepl(Repl* r) { char* path; if (r->fd[0] >= 0) close(r->fd[0]); if (r->fd[1] >= 0) close(r->fd[1]); r->fd[0] = r->fd[1] = -1; path = smprint("%s/ctl", r->path); assert(path); remove(path); free(path); path = smprint("%s/data", r->path); assert(path); remove(path); free(path); remove(r->path); } Panel* createsubpanel(Panel* g, char* name) { Repl* r; char* path; Panel* ng; ulong rnd; Panel* rg; Repl** rl; char e[100]; rnd = truerand()%10000; rg = nil; qlock(g); e[0] = 0; for (rl = &g->repl; r = *rl; ){ path = smprint("%s/%s.%uld", r->path, name, rnd); assert(path); ng = mkpanel(path); dprint(2, "createsubpanel %s %p\n", path, ng); free(path); if (ng == nil){ dprint(2, "createsubpanel: rmrepl: %s\n", r->path); rerrstr(e, sizeof(e)); rmrepl(r); *rl = r->next; free(r->path); free(r); g->nrepl--; } else { ng->evc = g->evc; if (rg == nil) rg = ng; rl = &r->next; } } qunlock(g); if (rg == nil) werrstr("createsubpanel: %s", e); return rg; } char* panelpath(Panel* g) { char* p; qlock(g); if (g->repl != nil && g->repl->path != nil) p = strdup(g->repl->path); else p = nil; qunlock(g); return p; } static void panelfree(Panel* g) { Repl* gr; Repl* nr; if (g == nil) return; free(g->name); for(gr = g->repl; gr; gr = nr){ nr = gr->next; assert(gr->fd[Fctl] < 0); assert(gr->fd[Fdata] < 0); free(gr->path); free(gr); } free(g); } static void unlinkpanel(Panel* g) { Panel** gl; for(gl = &panels; *gl; gl = &(*gl)->next){ if (*gl == g){ *gl = g->next; break; } } } void removepanel(Panel* g) { Repl* r; int wasgone; qlock(g); wasgone = g->gone; g->gone = 1; for (r = g->repl; r; r = r->next){ dprint(2, "removepanel: rmrepl: %s\n", r->path); rmrepl(r); } qunlock(g); if (!wasgone && decref(g) > 0) // must have events pending. return; qlock(&glock); unlinkpanel(g); panelfree(g); qunlock(&glock); } static int _openpanel(Panel* g, int omode, int what) { Repl* r; Repl** rl; char* path; char e[100]; qlock(g); seprint(e, e+sizeof(e), "%s: no replicas", g->name); if (g->repl == nil) dprint(2, "openpanel: %s: no replicas\n", g->name); for (rl = &g->repl; r = *rl; ){ if (r->fd[what] < 0){ if (what == Fctl) path = smprint("%s/ctl", r->path); else path = smprint("%s/data", r->path); r->fd[what] = open(path, omode); free(path); if (r->fd[what] < 0){ rerrstr(e, sizeof(e)); dprint(2, "openpanel: rmrepl: %s: %r\n", r->path); rmrepl(r); *rl = r->next; free(r->path); free(r); g->nrepl--; continue; } } rl = &r->next; } qunlock(g); if (g->repl == nil){ werrstr("openpanel: %s", e); return -1; } return 1; } static void _closepanel(Panel* g, int what) { Repl* r; qlock(g); for (r = g->repl; r; r = r->next){ if (r->fd[what] >= 0){ close(r->fd[what]); r->fd[what] = -1; } } qunlock(g); } static long _readpanel(Panel* g, int what, void* buf, long len) { Repl* r; Repl** rl; long rc, nr; char e[100]; qlock(g); rc = -1; seprint(e, e+sizeof(e), "%s: not open", g->name); for (rl = &g->repl; r = *rl; ) if (r->fd[what] >= 0){ nr = read(r->fd[what], buf, len); if (nr >= 0){ rc = nr; break; } rerrstr(e, sizeof(e)); dprint(2, "_readpanel: rmrepl: %s: %r\n", r->path); rmrepl(r); *rl = r->next; free(r->path); free(r); g->nrepl--; } else rl = &r->next; qunlock(g); if (rc < 0) werrstr("_readpanel: %s", e); return rc; } Dir* dirstatpanel(Panel* g) { Repl* r; Repl** rl; Dir* d; char e[100]; char* path; qlock(g); seprint(e, e+sizeof(e), "%s: no replicas", g->name); d = nil; for (rl = &g->repl; r = *rl; ){ if (r->fd[Fdata] >= 0) d = dirfstat(r->fd[Fdata]); else { path = smprint("%s/data", r->path); d = dirstat(path); free(path); } if (d != nil) break; rerrstr(e, sizeof(e)); dprint(2, "dirstatpanel: rmrepl: %s: %r\n", r->path); rmrepl(r); *rl = r->next; free(r->path); free(r); g->nrepl--; } qunlock(g); if (d == nil) werrstr("_readpanel: %s", e); return d; } void* readallpanel(Panel* g, long* l) { Dir* d; char* buf; d = dirstatpanel(g); if (d == nil){ *l = -1; return nil; } buf = malloc(d->length+1); *l = d->length; free(d); if (buf == nil){ *l = -1; return nil; } if (openpanel(g, OREAD) < 0){ free(buf); return nil; } *l = readpanel(g, buf, *l); closepanel(g); if (*l < 0){ free(buf); buf = nil; }else buf[*l] = 0; return buf; } /* A ctl write error is ignored. Otherwise, a * bad ctl would remove the replica thinking that * it has been an error accesing the replica files. */ static long _writepanel(Panel* g, int what, void* buf, long len) { Repl* r; Repl** rl; long wc; long ec; char e[100]; if (len == 0) return 0; qlock(g); seprint(e, e+sizeof(e), "%s: no replicas", g->name); ec = -1; for (rl = &g->repl; r = *rl; ){ if (r->fd[what] >= 0){ wc = write(r->fd[what], buf, len); if (wc != len){ if (wc < 0) rerrstr(e, sizeof(e)); else strcpy(e, "couldn't write all"); if (what == Fdata){ rmrepl(r); *rl = r->next; free(r->path); free(r); g->nrepl--; werrstr("writepanel: %s", e); continue; } } else ec = wc; } rl = &r->next; } qunlock(g); return ec; } int openpanel(Panel* g, int omode) { return _openpanel(g, omode, Fdata); } int openpanelctl(Panel* g) { return _openpanel(g, ORDWR, Fctl); } void closepanel(Panel* g) { _closepanel(g, Fdata); } void closepanelctl(Panel* g) { _closepanel(g, Fctl); } long writepanel(Panel* g, void* buf, long len) { return _writepanel(g, Fdata, buf, len); } long writepanelctl(Panel* g, void* buf, long len) { return _writepanel(g, Fctl, buf, len); } long readpanel(Panel* g, void* buf, long len) { return _readpanel(g, Fdata, buf, len); } long readpanelctl(Panel* g, void* buf, long len) { return _readpanel(g, Fctl, buf, len); } vlong seekpanel(Panel*g, vlong pos, int type) { Repl* r; vlong rc; int some; rc = -1; some = 0; qlock(g); for (r = g->repl; r; r = r->next) if (r->fd[Fdata] >= 0){ some++; rc=seek(r->fd[Fdata], pos, type); } if (!some) werrstr("%s: no replicas", g->name); qunlock(g); return rc; } int panelctl(Panel* g, char* fmt, ...) { char* ctl; va_list arg; long rc; va_start(arg, fmt); ctl = vsmprint(fmt, arg); va_end(arg); rc = writepanelctl(g, ctl, strlen(ctl)); free(ctl); return rc; } void wpanelexcl(Panel* g, char* what, void* buf, long len, Repl* excl) { Repl* r; char* path; qlock(g); for(r = g->repl; r; r = r->next){ if (r != excl){ path = smprint("%s/%s", r->path, what); if (path) writef(path, buf, len); free(path); } } qunlock(g); } void rpaneldata(Panel* g, Repl* r) { char* f; void* data; long len; if (g == nil || r == nil) return; qlock(g); if (g->repl && g->nrepl >= 2){ f = smprint("%s/data", r->path); data = readf(f, nil, 0, &len); free(f); if (data != nil){ qunlock(g); wpanelexcl(g, "data", data, len, r); free(data); return; } } qunlock(g); } void movepanel(char* from, char* to) { Panel* g; Repl* r; qlock(&glock); for(g = panels; g; g = g->next){ qlock(g); for (r = g->repl; r; r = r->next) if (!strcmp(from, r->path)){ free(r->path); r->path = strdup(to); qunlock(g); qunlock(&glock); return; } qunlock(g); } qunlock(&glock); /* To debug problems related to races on panel moves */ fprint(2, "movepanel: no panel: move from %s to %s\npanels:\n", from, to); qlock(&glock); for(g = panels; g; g = g->next){ fprint(2, "%s:\n", g->name); qlock(g); for (r = g->repl; r; r = r->next) fprint(2, "\t%s\n", r->path); qunlock(g); } qunlock(&glock); /* No source replica found. Create the target anyway. */ if (g = newpanel(from, 0)) decref(g); // we don't keep the ref. It's > 1 also. }