/* * Copyright (c) 2013, Coraid, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Coraid nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL CORAID BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include <9p.h> #include #include "dat.h" enum { CMallow, CMblockuse, CMcheckalloc, CMcstat, CMdisallow, CMdumpusers, CMfixfamilies, CMfixpaths, CMhalt, CMhelp, CMhstat, CMinituid, CMlcreate, CMlls, CMlmeta, CMlrm, CMmpred, CMmprint, CMmstat, CMnewroot, CMnfsdebug, CMp2q, CMp9debug, CMphash, CMpmeta, CMq2m, CMqmeta, CMrecovermeta, CMrevert, CMrmp, CMrootallow, CMrootdisallow, CMsetmeta, CMsetmstruct, CMsetqhash, CMsnap, CMsuper, CMsync, }; enum { SecPerDay = 24 * 60 * 60, }; static void θconsread(Req *); static void θconswrite(Req *); static Srv θconssrv = { .start = θstart, .read = θconsread, .write = θconswrite, }; static int snapid; static Channel *snaptrigger; static Ioproc *consio; static int pfd[2]; static Cmdtab ctab[] = { {CMallow, "allow", 1}, {CMblockuse, "blockuse", 2}, {CMcheckalloc, "checkalloc", 1}, {CMcstat, "cstat", 1}, {CMdisallow, "disallow", 1}, {CMdumpusers, "dumpusers", 1}, {CMfixfamilies, "fixfamilies", 1}, {CMfixpaths, "fixpaths", 1}, {CMhalt, "halt", 1}, {CMhelp, "help", 1}, {CMhstat, "hstat", 1}, {CMinituid, "inituid", 1}, {CMlcreate, "lcreate", 3}, {CMlls, "lls", 1}, {CMlmeta, "lmeta", 2}, {CMlrm, "lrm", 2}, {CMmpred, "mpred", 2}, {CMmprint, "mprint", 2}, {CMmstat, "mstat", 1}, {CMnewroot, "newroot", 2}, {CMnfsdebug, "nfsdebug", 0}, {CMp2q, "p2q", 2}, {CMp9debug, "p9debug", 2}, {CMphash, "phash", 2}, {CMpmeta, "pmeta", 2}, {CMq2m, "q2m", 2}, {CMqmeta, "qmeta", 2}, {CMrecovermeta, "recovermeta", 1}, {CMrevert, "revert", 2}, {CMrmp, "rmp", 2}, {CMrootallow, "rootallow", 1}, {CMrootdisallow, "rootdisallow", 1}, {CMsetmeta, "setmeta", 5}, {CMsetmstruct, "setmstruct", 6}, {CMsetqhash, "setqhash", 3}, {CMsnap, "snap", 1}, {CMsuper, "super", 1}, {CMsync, "sync", 1}, }; extern int chatty9p; int allow; int rootallow; static void showhelp(void) { int i; for(i = 0; i < nelem(ctab); ++i) fprint(pfd[1], "%-15s %d\n", ctab[i].cmd, ctab[i].narg); } static void lcreate(char *aoeid, uvlong size) { Qid nqid; uvlong meta, dirblk, now, nblk, pperb; int sperb; int aoemajor, aoeminor; sperb = BlkSize / 512; pperb = BlkSize / 8; nblk = (size + sperb - 1) / sperb; if(nblk + 3 >= super.nfree) { fprint(pfd[1], "Not enough space\n"); return; } sscanf(aoeid, "%d.%d", &aoemajor, &aoeminor); nqid.path = ((uvlong)TLun << 60) | (aoemajor << 8) | aoeminor; nqid.vers = 0; nqid.type = QTFILE; meta = q2m(-1, nqid.path, 1); if(meta == 0) { fprint(pfd[1], "Creation failure\n"); return; } setmetaint(meta, "aoemajor", nil, aoemajor); setmetaint(meta, "aoeminor", nil, aoeminor); setmetaint(meta, "qpath", nil, nqid.path); setmetaint(meta, "qvers", nil, nqid.vers); setmetaint(meta, "qtype", nil, nqid.type); now = nsec(); setmetaint(meta, "ctime", nil, now); setmetaint(meta, "length", nil, size << 9); dirblk = allocblock(); if(dirblk != 0) { cbclean(dirblk); cbwrite(dirblk); brelease(dirblk); } if(nblk <= pperb) setmetaint(meta, "index", nil, dirblk); else if(nblk <= pperb * pperb) setmetaint(meta, "indirect", nil, dirblk); else setmetaint(meta, "dblindir", nil, dirblk); setmetaint(meta, "nextlun", nil, super.firstlun); setqhash(nqid.path, meta); super.firstlun = nqid.path; savesuper(); starttarget(aoemajor, aoeminor, size); resetmeta(); csync(); fprint(pfd[1], "Created %d.%d with qid %ulld\n", aoemajor, aoeminor, nqid.path); } static char llsbuf[1024]; static char * lls(void) { char *p, *e; uvlong x; uvlong qpath, meta, length; int aoemajor, aoeminor; p = llsbuf; e = llsbuf + nelem(llsbuf); p = seprint(p, e, "Luns:\n"); for(qpath = super.firstlun; qpath; ) { meta = q2m(-1, qpath, 0); if(meta == 0) { seprint(p, e, "no metadata for %ulld\n", qpath); return llsbuf; } getmetaint(-1, meta, "aoemajor", &x); aoemajor = x; getmetaint(-1, meta, "aoeminor", &x); aoeminor = x; getmetaint(-1, meta, "length", &x); length = x; p = seprint(p, e, "%d.%d %ulld\n", aoemajor, aoeminor, length); getmetaint(-1, meta, "nextlun", &qpath); } return llsbuf; } static void lmeta(char *aoeid) { uvlong qpath; int aoemajor, aoeminor; sscanf(aoeid, "%d.%d", &aoemajor, &aoeminor); qpath = ((uvlong)TLun << 60) | (aoemajor << 8) | aoeminor; fprint(pfd[1], "metadata for %d.%d:\n", aoemajor, aoeminor); prmeta(pfd[1], qpath); } static void lrm(char *aoeid) { uvlong qpath, meta, nextlun, qt, mt; int aoemajor, aoeminor; sscanf(aoeid, "%d.%d", &aoemajor, &aoeminor); qpath = ((uvlong)TLun << 60) | (aoemajor << 8) | aoeminor; meta = q2m(-1, qpath, 0); if(meta == 0) { fprint(pfd[1], "Not found\n"); return; } freedata(meta); getmetaint(-1, meta, "nextlun", &nextlun); if(super.firstlun == qpath) { super.firstlun = nextlun; savesuper(); } else { qt = super.firstlun; while(1) { mt = q2m(-1, qt, 0); if(mt == 0) { fprint(pfd[1], "Missing metadata in LUN set\n"); goto bail; } getmetaint(-1, mt, "nextlun", &qt); if(qt == qpath) break; } setmetaint(mt, "nextlun", nil, nextlun); } bail: rmq(qpath, meta); freeblock(meta); rmtarget(aoemajor, aoeminor); resetmeta(); csync(); } static void newroot(char *name) { Qid rootqid; char *me, *path; uvlong meta; vlong now; path = smprint("/%s", name); rootqid.path = p2q(-1, path, 1); meta = q2m(-1, rootqid.path, 1); setmetastr(meta, "name", nil, path, 0); rootqid.vers = 0; rootqid.type = QTDIR; setmetaint(meta, "qpath", nil, rootqid.path); setmetaint(meta, "qvers", nil, rootqid.vers); setmetaint(meta, "qtype", nil, rootqid.type); setmetaint(meta, "mode", nil, DMDIR | 0775); now = nsec(); setmetaint(meta, "atime", nil, now); setmetaint(meta, "mtime", nil, now); setmetaint(meta, "length", nil, 0); me = getuser(); setmetastr(meta, "uid", nil, me, 0); setmetastr(meta, "gid", nil, me, 0); setmetastr(meta, "muid", nil, me, 0); setmetaint(meta, "child", nil, 0); setqhash(rootqid.path, meta); savesuper(); free(path); } static char * dosnap(void) { Qid qid; Tm *today; char *me; uvlong meta, now, dqid, dmeta, yqid, ymeta, x; int fd, seq, n; char path[128], sname[32]; dqid = p2q(-1, "/dump", 0); if(dqid == 0) return "no dump"; dmeta = q2m(-1, dqid, 0); snprint(path, 127, "%s/ctl", ddir); fd = open(path, ORDWR); if(fd < 0) return "no snap"; today = localtime(time(0)); snprint(path, 127, "/dump/%04d", today->year + 1900); seq = 0; yqid = p2q(-1, path, 0); if(yqid == 0) { qid.path = p2q(-1, path, 1); yqid = qid.path; qid.vers = 0; qid.type = QTDIR; ymeta = q2m(-1, qid.path, 1); snprint(path, 127, "%04d", today->year + 1900); setmetastr(ymeta, "name", nil, path, 0); setmetaint(ymeta, "qpath", nil, qid.path); setmetaint(ymeta, "qvers", nil, qid.vers); setmetaint(ymeta, "qtype", nil, qid.type); setmetaint(ymeta, "mode", nil, DMDIR | 0775); setmetaint(ymeta, "parent", nil, dqid); now = nsec(); setmetaint(ymeta, "atime", nil, now); setmetaint(ymeta, "mtime", nil, now); setmetaint(ymeta, "length", nil, 0); me = getuser(); setmetastr(ymeta, "uid", nil, me, 0); setmetastr(ymeta, "gid", nil, me, 0); setmetastr(ymeta, "muid", nil, me, 0); getmetaint(-1, dmeta, "child", &x); setmetaint(ymeta, "sib", nil, x); setmetaint(dmeta, "child", nil, yqid); setmetaint(ymeta, "child", nil, 0); setqhash(qid.path, ymeta); savesuper(); snprint(path, 127, "/dump/%04d/%02d%02d", today->year + 1900, today->mon+1, today->mday); } else { snprint(path, 127, "/dump/%04d/%02d%02d", today->year + 1900, today->mon+1, today->mday); if(p2q(-1, path, 0) != 0) { for(seq = 1; seq < 10; ++seq) { snprint(path, 127, "/dump/%04d/%02d%02d%d", today->year + 1900, today->mon+1, today->mday, seq); if(p2q(-1, path, 0) == 0) break; } if(seq >= 10) { close(fd); return "too many snaps"; } } ymeta = q2m(-1, yqid, 0); } qid.path = p2q(-1, path, 1); qid.vers = 0; qid.type = QTDIR; meta = q2m(-1, qid.path, 1); if(seq == 0) { snprint(path, 127, "%02d%02d", today->mon+1, today->mday); snprint(sname, 31, "%s.%04d%02d%02d", dname, today->year + 1900, today->mon+1, today->mday); } else { snprint(path, 127, "%02d%02d%d", today->mon+1, today->mday, seq); snprint(sname, 31, "%s.%04d%02d%02d%d", dname, today->year + 1900, today->mon+1, today->mday, seq); } resetmeta(); csync(); n = fprint(fd, "snap %s %s", dname, sname); close(fd); if(n < 0) return (char *)(~0); setmetastr(meta, "name", nil, path, 0); setmetaint(meta, "qpath", nil, qid.path); setmetaint(meta, "qvers", nil, qid.vers); setmetaint(meta, "qtype", nil, qid.type); setmetaint(meta, "mode", nil, DMDIR | 0775); setmetaint(meta, "parent", nil, yqid); now = nsec(); setmetaint(meta, "atime", nil, now); setmetaint(meta, "mtime", nil, now); setmetaint(meta, "length", nil, 0); me = getuser(); setmetastr(meta, "uid", nil, me, 0); setmetastr(meta, "gid", nil, me, 0); setmetastr(meta, "muid", nil, me, 0); getmetaint(-1, ymeta, "child", &x); setmetaint(meta, "sib", nil, x); setmetaint(ymeta, "child", nil, qid.path); setmetastr(meta, "snap", nil, sname, 0); setqhash(qid.path, meta); savesuper(); return nil; } static char * revert(char *snap) { char *path, *p; int fd, n; path = smprint("%s/ctl", ddir); fd = open(path, ORDWR); free(path); if(fd < 0) return (char *)(~0); p = strchr(snap, '/'); if(p) path = smprint("%s.%.*s%s", dname, (int)(p - snap), snap, p + 1); else path = smprint("%s.%s", dname, snap); n = fprint(fd, "revert %s %s", dname, path); free(path); close(fd); resetmeta(); resetcache(); if(n < 0) return (char *)(~0); return nil; } static void doshutdown(void) { shutdown = 1; threadkill(snapid); haltaoe(); haltnfs(); halt9p(); haltfree(); haltcache(); threadkillgrp(threadgetgrp()); } void docons(void *x) { Cmdbuf *cb; Cmdtab *ct; char *s; char buf[256]; uvlong vl; int n; USED(x); while(1) { fprint(pfd[1], "> "); n = ioread(consio, pfd[1], buf, 255); if(n <= 0) return; buf[n] = 0; cb = parsecmd(buf, n); if(cb == nil) { fprint(pfd[1], "Unparsable command %s\n", buf); continue; } if(cb->nf == 0) continue; ct = lookupcmd(cb, ctab, nelem(ctab)); if(ct == nil) { fprint(pfd[1], "%s: %r\n", buf); continue; } switch(ct->index) { case CMallow: allow = 1; break; case CMblockuse: blockuse(pfd[1], strtoull(cb->f[1], nil, 0)); break; case CMcheckalloc: checkalloc(pfd[1]); break; case CMcstat: fprint(pfd[1], "%s", prcstat()); break; case CMdisallow: allow = 0; break; case CMdumpusers: dumpusers(pfd[1]); break; case CMfixfamilies: fixfamilies(pfd[1]); break; case CMfixpaths: fixpaths(pfd[1]); break; case CMhalt: doshutdown(); return; case CMhelp: showhelp(); break; case CMhstat: fprint(pfd[1], "%s", prhstat()); break; case CMinituid: inituid(); break; case CMlcreate: lcreate(cb->f[1], strtoull(cb->f[2], nil, 10)); break; case CMlls: fprint(pfd[1], "%s", lls()); break; case CMlmeta: lmeta(cb->f[1]); break; case CMlrm: lrm(cb->f[1]); break; case CMmpred: mpred(pfd[1], strtoull(cb->f[1], nil, 0)); break; case CMmprint: mprint(pfd[1], strtoull(cb->f[1], nil, 0)); break; case CMmstat: fprint(pfd[1], "%s", prmstat()); break; case CMnewroot: newroot(cb->f[1]); break; case CMnfsdebug: if(cb->nf < 2) fprint(pfd[1], "%d\n", debugnfs); else debugnfs = atoi(cb->f[1]); break; case CMp2q: vl = p2q(-1, cb->f[1], 0); fprint(pfd[1], "%ulld\n", vl); break; case CMp9debug: chatty9p = atoi(cb->f[1]); break; case CMphash: showphash(pfd[1], cb->f[1]); break; case CMpmeta: fprint(pfd[1], "metadata for %s\n", cb->f[1]); prmeta(pfd[1], p2q(-1, cb->f[1], 0)); break; case CMq2m: vl = q2m(-1, strtoull(cb->f[1], nil, 10), 0); fprint(pfd[1], "%ulld\n", vl); break; case CMqmeta: fprint(pfd[1], "metadata for %s\n", cb->f[1]); prmeta(pfd[1], strtoull(cb->f[1], nil, 10)); break; case CMrecovermeta: recovermeta(pfd[1]); break; case CMrevert: s = revert(cb->f[1]); if(s == (char *)(~0)) fprint(pfd[1], "%r\n"); else if(s) fprint(pfd[1], "%s\n", s); break; case CMrmp: rmp(cb->f[1]); break; case CMrootallow: rootallow = 1; break; case CMrootdisallow: rootallow = 0; break; case CMsetmeta: vl = q2m(-1, strtoull(cb->f[1], nil, 10), 0); if(cb->f[2][0] == 's') setmetastr(vl, cb->f[3], nil, cb->f[4], 0); else setmetaint(vl, cb->f[3], nil, strtoull(cb->f[4], nil, 0)); break; case CMsetmstruct: vl = strtoull(cb->f[1], nil, 0); setmstruct(vl, strtoull(cb->f[2], nil, 0), cb->f[3], atoi(cb->f[4]), strtoull(cb->f[5], nil, 0)); break; case CMsetqhash: setqhash(strtoull(cb->f[1], nil, 0), strtoull(cb->f[2], nil, 0)); break; case CMsnap: s = dosnap(); if(s == (char *)(~0)) fprint(pfd[1], "%r\n"); else if(s) fprint(pfd[1], "%s\n", s); break; case CMsuper: fprint(pfd[1], "%s", prsuper()); break; case CMsync: resetmeta(); csync(); break; } } } static void θconsread(Req *r) { char *s; s = smprint("%s\n%s\n%s\n%s\n%s", prsuper(), prcstat(), prmstat(), prhstat(), lls()); readstr(r, s); free(s); respond(r, nil); } static void θconswrite(Req *r) { Cmdbuf *cb; Cmdtab *ct; char *s; s = nil; cb = parsecmd(r->ifcall.data, r->ifcall.count); if(cb == nil) { respond(r, "unparsable command"); return; } if(cb->nf == 0) { respond(r, nil); return; } ct = lookupcmd(cb, ctab, nelem(ctab)); if(ct == nil) { respond(r, r->error); return; } switch(ct->index) { case CMallow: allow = 1; break; case CMcheckalloc: checkalloc(pfd[1]); break; case CMdisallow: allow = 0; break; case CMinituid: inituid(); break; case CMlcreate: lcreate(cb->f[1], strtoull(cb->f[2], nil, 10)); break; case CMlrm: lrm(cb->f[1]); break; case CMnewroot: newroot(cb->f[1]); break; case CMp9debug: chatty9p = atoi(cb->f[1]); break; case CMrevert: s = revert(cb->f[1]); break; case CMrmp: rmp(cb->f[1]); break; case CMrootallow: rootallow = 1; break; case CMrootdisallow: rootallow = 0; break; case CMsetmstruct: setmstruct(strtoull(cb->f[1], nil, 0), strtoull(cb->f[2], nil, 0), cb->f[3], atoi(cb->f[4]), strtoull(cb->f[5], nil, 0)); break; case CMsetqhash: setqhash(strtoull(cb->f[1], nil, 0), strtoull(cb->f[2], nil, 0)); break; case CMsnap: s = dosnap(); break; case CMsync: resetmeta(); csync(); break; default: s = "unsupported ctl command"; break; } if(s == (char *)(~0)) respond(r, r->error); else respond(r, s); } static void mysrvproc(void *a) { Srv *s; int data; s = a; data = s->infd; srv(s); close(data); threadexits(nil); } static void snapthread(void *) { while(1) { recvul(snaptrigger); if(shutdown) break; dosnap(); } threadexits(nil); } static void snapproc(void *) { // Tm *now; ulong cursec, waitsec; sleep(300*1000); /* Give sometime to get the clock set before looking at tod */ while(1) { /* * We'd like to get the time zone correction here, but * it's doesn't play nice with the threading. I'll come * back to this later. */ // now = localtime(time(nil)); // cursec = (now->hour * 60 + now->min) * 60 + now->sec; cursec = time(nil) % SecPerDay; waitsec = (super.snaptime + SecPerDay - cursec) % SecPerDay; if(waitsec < 60) waitsec = SecPerDay; sleep(waitsec*1000); sendul(snaptrigger, 1); if(shutdown) break; } threadexits(nil); } void initcons(int postcons) { char *me; int cfd[2]; if(postcons) { consio = ioproc(); me = getuser(); θconssrv.tree = alloctree(me, me, 0555, nil); createfile(θconssrv.tree->root, "θfsctl", me, 0664, nil); if(pipe(cfd) < 0) sysfatal("pipe: %r"); θconssrv.infd = θconssrv.outfd = cfd[1]; conspost(cfd, pfd); threadcreate(mysrvproc, &θconssrv, 32 * 1024); } snaptrigger = chancreate(sizeof(ulong), 2); threadcreate(snapthread, nil, 8192); snapid = proccreate(snapproc, nil, 1024); }