/*
* 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 <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include <stdio.h>
#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);
}
|