#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <auth.h>
#include <draw.h>
#include <mouse.h>
#include <frame.h>
#include <9p.h>
#include <pool.h>
#include <b.h>
#include "gui.h"
int mainstacksize = 32 * 1024;
File* slash;
char* sname;
char* saddr;
int smallomero;
char* remoteox;
static Channel* fsreqc;
static Channel* writeallc;
static Channel* pidc;
Channel* startc;
Channel* panelhupc;
Channel* destroyc;
static char* vname;
static char* addr;
static ulong oxpids[4];
static void
fsattach(Req* r)
{
if (r->srv->auth != nil && authattach(r) < 0)
return;
respond(r, nil);
}
int
fileclass(File* f)
{
return ((f->qid.path&0xff000000)>>24);
}
void
setfileclass(File* f, int id)
{
f->qid.path |= (((id)&0xff)<<24);
}
File*
newfspanel(File* d, char* name, char* uid)
{
File* f;
Panel* p;
int id;
Panel* dp;
cleanpolicy(d);
dp = d->aux;
p = newpanel(name, dp);
if (p == nil)
return nil;
id = p->type;
f = createfile(d, name, uid, 0775|DMDIR, nil);
if (f == nil){
closepanel(p);
fprint(2, "omero: newfspanel: %s: %r\n", name);
return nil;
}
setfileclass(f, id*2);
p->path = filepath(f);
p->file = f;
p->dfile = createfile(f, "data", uid, 0660, p);
incref(p);
setfileclass(p->dfile, id*2);
closefile(p->dfile);
p->cfile = createfile(f, "ctl", uid, 0660, p);
incref(p);
setfileclass(p->cfile, id*2+1);
closefile(p->cfile);
setctlfilelen(p);
f->aux = p;
p->flags |= Predraw;
dp->flags|= Predraw;
return f;
}
static void
fsopen(Req* r)
{
File* f;
int type;
Panel* p;
int mode;
if (r->fid->qid.type&QTAUTH)
sysfatal("fsopen for an AUTH file. fixme: add respond/return\n");
mode = (r->ifcall.mode&3);
if (mode == OREAD){
respond(r, nil);
return;
}
f = r->fid->file;
type = fileclass(f);
p = f->aux;
panelok(p);
if ((type&1) == 0 && (r->ifcall.mode&OTRUNC) && panels[p->type]->truncate){
wlock(p->dfile);
panels[p->type]->truncate(p);
wunlock(p->dfile);
}
if ((f->qid.type&QTDIR) || (type&1) || panels[p->type]->writeall == nil){
respond(r, nil);
return;
}
/*
* Trying to open a panel data file that needs to process all
* the writes at onces (e.g., images: created once copied).
* Make sure the file behaves as OEXCL regarding writes.
*/
wlock(f);
if (p->writebuf != nil){
wunlock(f);
respond(r, "exclusive use for write");
} else {
p->writebuf = emalloc9p(64*1024);
p->nawrite = 64*1024;
p->nwrite = 0;
wunlock(f);
respond(r, nil);
}
}
static int
isheld(File* f)
{
Panel* p;
for(;;){
p = f->aux;
if (p->flags&Ptop)
break;
if (p->flags&Playout)
break;
if (f == f->parent)
break;
if (p->flags&Phold)
return 1;
f = f->parent;
}
return 0;
}
static void
fscreate(Req* r)
{
File* f;
File* d;
char* name;
char* uid;
int mode;
Panel* p;
d = r->fid->file;
name = r->ifcall.name;
uid = r->fid->uid;
mode = d->mode & 0x777 & r->ifcall.perm;
mode |= (r->ifcall.perm & ~0x777);
if (!(mode&DMDIR)){
werrstr("%s must be a directory", name);
responderror(r);
return;
}
p = d->aux;
if (p->type != Qcol && p->type != Qrow){
werrstr("not a column or a row: type %d", p->type);
responderror(r);
return;
}
if (f = newfspanel(d, name, uid)){
closefile(r->fid->file);
r->fid->file = f;
r->ofcall.qid = f->qid;
p->flags |= Predraw;
if (!isheld(f))
resize();
respond(r, nil);
} else
responderror(r);
}
static void
fsread(Req* r)
{
File* f;
Panel* cp;
int type;
char buf[512];
long n;
void* data;
long count;
vlong off;
if (r->fid->qid.type&QTAUTH){
authread(r);
return;
}
f = r->fid->file;
type = fileclass(f);
cp = f->aux;
assert(cp);
if (cp->dfile == nil || cp->cfile == nil || cp->file == nil || (cp->flags&Pdead)){
respond(r, "panel is deleted");
return;
}
panelok(cp);
if (type&1){
rlock(cp->dfile);
n = panels[cp->type]->attrs(cp, buf, sizeof(buf)-1);
runlock(cp->dfile);
if (n < 0){
responderror(r);
return;
}
buf[n] = 0;
readstr(r, buf);
} else {
data = r->ofcall.data;
off = r->ifcall.offset;
count = r->ifcall.count;
rlock(cp->dfile);
n = panels[cp->type]->read(cp, data, count, off);
runlock(cp->dfile);
if (n < 0){
responderror(r);
return;
}
r->ofcall.count = n;
}
respond(r, nil);
}
static int
multilinectl(char* c)
{
if(!strncmp(c, "ins ", 4) || !strncmp(c, "search ", 7))
return 1;
return 0;
}
static void
fswrite(Req* r)
{
File* f;
Panel* cp;
int type, n;
void* data;
long count;
vlong off;
char* buf;
char* s;
char* e;
int mustdraw;
if (r->fid->qid.type&QTAUTH){
authwrite(r);
return;
}
if (r->ifcall.count == 0){
r->ofcall.count = 0;
respond(r, nil);
return;
}
f = r->fid->file;
type = fileclass(f);
cp = f->aux;
assert(cp);
if ((cp->flags&Pdead) ||
cp->dfile == nil || cp->cfile == nil || cp->file == nil){
respond(r, "panel is deleted");
return;
}
panelok(cp);
if (type&1){
mustdraw = 0;
buf = emalloc9p(r->ifcall.count + 1);
memmove(buf, r->ifcall.data, r->ifcall.count);
buf[r->ifcall.count] = 0;
r->ofcall.count = r->ifcall.count;
for (s = buf; s && *s; s = e){
if (multilinectl(s)){ // consume all buf
e = nil;
if (buf[r->ifcall.count-1] == '\n')
buf[r->ifcall.count-1] = 0;
} else {
e = strchr(s, '\n');
if (e)
*e++ = 0;
}
if (*s){
wlock(cp->dfile);
if (!strcmp(s, "hold")){
cp->holdfid = r->fid->fid;
cp->flags |= Phold;
} else {
n = panels[cp->type]->ctl(cp, s);
if (n < 0){
wunlock(cp->dfile);
free(buf);
responderror(r);
return;
} else
mustdraw |= n > 0;
}
wunlock(cp->dfile);
}
}
free(buf);
if (mustdraw)
resize();
else
flushimage(display, 1);
} else {
data = r->ifcall.data;
off = r->ifcall.offset;
count = r->ifcall.count;
wlock(cp->dfile);
if (panels[cp->type]->writeall != nil){
if (off + count > cp->nawrite){
cp->writebuf = erealloc9p(cp->writebuf, off+count);
cp->nawrite = off+count;
}
memmove(cp->writebuf+off, data, count);
n = count;
cp->nwrite = off+count;
} else
n = panels[cp->type]->write(cp, data, count, off);
wunlock(cp->dfile);
if (n < 0){
responderror(r);
return;
}
r->ofcall.count = n;
}
respond(r, nil);
}
static void
fsclunk(Fid* fid)
{
File* f;
Panel* cp;
int type;
if (fid->qid.type&QTAUTH){
authdestroy(fid);
return;
}
f = fid->file;
if (f == nil)
return;
type = fileclass(f);
cp = f->aux;
assert(cp);
if (fid->fid == cp->holdfid){
cp->holdfid = 0;
cp->flags &= ~Phold;
resize();
edprint("hold clear\n");
}
if (!(fid->qid.type&QTDIR) && !(type&1) && panels[cp->type]->writeall)
if (cp->dfile != nil && cp->cfile != nil && cp->file != nil)
sendp(writeallc, cp);
}
static void
fsremove(Req* r)
{
File* fp;
Panel* p;
/* Be sure the parent is redrawn later.
* freefile() will actually remove the file when it's safe.
*/
fp = r->fid->file;
p = fp->aux;
assert(p);
p->flags |= Pdead;
if (fp != nil && fp->parent != nil){
p = fp->parent->aux;
p->flags |= Predraw;
}
respond(r, nil);
}
static void
freefile(File *f)
{
Panel* p;
p = f->aux;
f->aux = nil;
p->flags |= Pdead;
if (p->dfile == f)
p->dfile = nil;
else if (p->cfile == f)
p->cfile = nil;
else if (p->file == f)
p->file = nil;
if ( decref(p)<= 0)
sendp(destroyc, p);
}
static void
fsinit(Srv* s)
{
assert(!s->aux);
s->aux = chancreate(sizeof(ulong), 0);
}
static void
fsend(Srv* s)
{
assert(s->aux);
chanfree(s->aux);
}
static void
fsreqthread(void* a)
{
Req* r = a;
threadsetname("fsreqthread");
switch(r->ifcall.type){
case Topen:
fsopen(r);
break;
case Tcreate:
fscreate(r);
break;
case Tremove:
fsremove(r);
break;
case Tread:
fsread(r);
break;
case Twrite:
fswrite(r);
break;
default:
sysfatal("bad request in fsthread type %d\n", r->ifcall.type);
}
threadexits(nil);
}
static void
fsthread(void*)
{
int i;
Req* r;
Con* c;
Panel* p;
Waitmsg*wm;
Alt a[] = {
{fsreqc, &r, CHANRCV},
{writeallc, &p, CHANRCV},
{nil, &wm, CHANRCV},
{panelhupc, &c, CHANRCV},
{nil, nil, CHANEND}};
threadsetname("fsthread");
a[2].c = threadwaitchan();
for(;;){
switch(alt(a)){
case 0:
threadcreate(fsreqthread, r, 32*1024);
break;
case 1:
if (p->dfile == nil || p->cfile == nil || p->file == nil || (p->flags&Pdead))
break;
panelok(p);
wlock(p->dfile);
panels[p->type]->writeall(p, p->writebuf, p->nwrite);
free(p->writebuf);
p->writebuf = nil;
p->nawrite = p->nwrite = 0;
wunlock(p->dfile);
break;
case 2:
edprint("pid %d exited\n", wm->pid);
for (i = 0; i < 4; i++)
if (oxpids[i] == wm->pid)
oxpids[i] = 0;
free(wm);
break;
case 3:
rmhuppanels(slash, c);
break;
default:
abort();
}
}
}
static int
islocal(char* addr)
{
static char* l = nil;
if (addr == nil)
return 1;
if (!strncmp(addr, "::", 2) || !strncmp(addr, "127.0.0.1", 8))
return 1;
if (l == nil)
l = getenv("sysaddr");
if (l && !strncmp(addr, l, strlen(l)))
return 1;
return 0;
}
static void
bauth9p(Req* r)
{
if (islocal(r->srv->addr)) {
r->srv->auth = nil; // no auth here any more
respond(r, "auth no required for local peers");
} else
auth9p(r);
}
static void
fsop(Req* r)
{
sendp(fsreqc, r);
}
static Srv sfs=
{
.auth = bauth9p,
.attach = fsattach,
.open = fsop,
.create = fsop,
.remove = fsop,
.read = fsop,
.write = fsop,
.destroyfid = fsclunk,
};
static char* menu[] = { "Row", "Col", "Ox", "Del" };
static void
mklayout(File* f)
{
File* d;
File* dd;
File* c;
Panel* p;
int i;
char name[50];
d = newfspanel(f, "row:stats", f->uid);
p = d->aux;
p->flags |= Ptag|Playout;
if (smallomero)
dd = newfspanel(d, "col:cmds", f->uid);
else
dd = newfspanel(d, "row:cmds", f->uid);
for (i = 0; i < nelem(menu); i++){
seprint(name, name+sizeof(name), "button:%s", menu[i]);
c = newfspanel(dd, name, f->uid);
closefile(c);
}
closefile(dd);
closefile(d);
d = newfspanel(f, "row:wins", f->uid);
p = dd->aux;
p->flags |= Playout;
dd = newfspanel(d, "col:1", f->uid);
p = dd->aux;
p->flags |= Playout;
closefile(dd);
if (!smallomero){
dd = newfspanel(d, "col:2", f->uid);
p = dd->aux;
p->flags |= Playout;
closefile(dd);
}
closefile(d);
resize();
}
static void
mktree(void)
{
Panel* p;
File* d;
sfs.tree = alloctree(nil, nil, DMDIR|0555, freefile);
if (sfs.tree == nil)
sysfatal("no tree: %r");
slash = sfs.tree->root;
setfileclass(slash, 2*Qcol);
incref(slash);
slash->aux = p = newpanel("col:/", nil);
p->file = slash;
p->path = filepath(slash);
p->flags &= ~Ptag;
p->flags |= Ptop|Playout;
p = newpanel("col:sys", p);
p->flags |= Ptag|Ptop|Playout;
d = createfile(slash, smprint("%sui", sname), "sys", DMDIR|0775, p);
setfileclass(d, 2*Qcol);
p->file = d;
p->path = filepath(d);
p->dfile = createfile(d, "data", "sys", 0660, p);
incref(p);
setfileclass(p->dfile, 2*Qcol);
closefile(p->dfile);
p->cfile = createfile(d, "ctl", "sys", 0660, p);
incref(p);
setfileclass(p->cfile, 2*Qcol+1);
closefile(p->cfile);
mklayout(d);
}
static void
mountuis(void)
{
int fd;
char spec[50];
char* loc;
loc = getenv("location");
if (loc == nil)
loc=strdup("home");
seprint(spec, spec+50, "*/devs/ui 'user=%s loc=%s'", getuser(), loc);
free(loc);
fd = open("/srv/vol", ORDWR);
if (fd < 0)
fd = open("#s/vol", ORDWR);
if (fd < 0)
fprint(2, "%s: /srv/vol: %r\n", argv0);
else {
if (mount(fd, -1, "/devs", MBEFORE|MCREATE, spec) < 0)
fprint(2, "%s: mount */devs/ui: %r\n", argv0);
close(fd);
}
}
void
startproc(void*a)
{
char* init = a;
int i;
int fd;
char* s;
char* r;
for (i = 3; i < 30; i++)
close(i);
fd = open("/dev/null", OREAD);
dup(fd, 0);
close(fd);
s = strchr(init, ' ');
r = nil;
if (s){
*s++ = 0;
r = strchr(s, ' ');
if (r)
*r++ = 0;
}
mountuis();
procexecl(pidc, init, init, s, r, nil);
procexecl(pidc, smprint("/bin/%s", init), init, s, r, nil);
fprint(2, "%s: %s: %r\n", argv0, init);
sendul(pidc, 0);
threadexits("init");
}
void
runoxloop(char* p)
{
char* prog;
char* remote;
char buf[100];
int i;
pidc = chancreate(sizeof(ulong), 0);
procrfork(startproc, Ox, 16*1024, RFNAMEG|RFENVG|RFNOTEG);
oxpids[0] = recvul(pidc);
if (p != nil){
proccreate(startproc, p, 16*1024);
recvul(pidc);
}
while(prog=recvp(startc)){
for (i = 0; i < 4; i++)
if (oxpids[i] == 0)
break;
if (i == 4){
fprint(2, "%s: no more oxs\n", argv0);
continue;
}
remote = strchr(prog, ' ');
if (remote){
*remote++ = 0;
fprint(2, "remote %s for %s not implemented\n", prog, remote);
/* To implement this beast:
* 1. dial to the remote machine's ox port.
* 2. write params: ox prog, space id, our vols
* 3. block reading (this would exit when ox dies).
* To implement the remote ox program:
* 1. read parms.
* 2. adjust name space to use terminal device vols
* 3. exec ox
*/
} else {
seprint(buf, buf+sizeof(buf), "%s -s%d", prog, i);
procrfork(startproc, buf, 16*1024, RFNAMEG|RFENVG|RFNOTEG);
oxpids[i] = recvul(pidc);
}
free(prog);
}
}
static void
announceproc(void*)
{
int afd = -1;
char* cnstr;
threadsetname("announceproc");
cnstr = strchr(vname, ' ');
if (cnstr)
*cnstr++ = 0;
for(;;){
afd = announcevol(afd, addr, vname, cnstr);
if (afd < 0) // try again
afd = announcevol(afd, addr, vname, cnstr);
sleep(10 * 1000);
}
}
static char*
getvolsys(char* vname)
{
char* s;
char* p;
char* e;
s = strdup(vname);
e = strstr(s, "sys=");
if (e == nil){
free(s);
return strdup(sysname());
}
e += 4;
p = strchr(e, ' ');
if (p)
*p = 0;
p = strdup(e);
free(s);
return p;
}
static char*
cleanvoladdr(char* addr)
{
char naddr[50];
char* p;
p = strchr(addr, '*');
if (p == nil)
return strdup(addr);
else {
if (p - addr)
strncpy(naddr, addr, p - addr);
naddr[p-addr] = 0;
strcat(naddr, sysname());
strcat(naddr, p + 1);
return strdup(naddr);
}
}
static void
mainproc(void*)
{
/* We are the process servicing /devs/*ui, avoid deadlocks
*/
unmount(0, "/devs/");
threadsetname("main");
threadcreate(fsthread, nil, 32*1024);
initui();
mktree();
rendezvous(mainproc, 0);
threadexits(nil);
}
/* BUG:
* The next few routines are similar to lib9p/thread.c and lib9p/listen.c
* They are modified here to try tcp!*!0 if the user-given address fails,
* to simplify calling for extra omeros. If a better way is found, this may go.
*/
static char*
getremotesys(char *ndir)
{
char buf[128], *serv, *sys;
int fd, n;
snprint(buf, sizeof buf, "%s/remote", ndir);
sys = nil;
fd = open(buf, OREAD);
if(fd >= 0){
n = read(fd, buf, sizeof(buf)-1);
if(n>0){
buf[n-1] = 0;
serv = strchr(buf, '!');
if(serv)
*serv = 0;
sys = estrdup9p(buf);
}
close(fd);
}
if(sys == nil)
sys = estrdup9p("unknown");
return sys;
}
static void
osrvproc(void* v)
{
int data;
Srv *s;
s = v;
data = s->infd;
if (chatty9p)
fprint(2, "%d %s: new srv: %s\n", getpid(), argv0, s->addr);
srv(s);
if (chatty9p)
fprint(2, "%d %s: exiting: %s\n", getpid(), argv0, s->addr);
close(data);
free(s->addr);
free(s);
}
static void
olistenproc(void *v)
{
char ndir[NETPATHLEN], dir[NETPATHLEN];
int ctl, data, nctl;
Srv *os, *s;
NetConnInfo* ni; // I love these names that break indent
os = v;
ctl = announce(os->addr, dir);
if(ctl < 0){
ctl = announce("tcp!*!0", dir);
if (ctl < 0)
sysfatal("announce %s: %r", os->addr);
}
ni = getnetconninfo(dir, ctl);
addr = smprint("tcp!%s!%s", sysname(), ni->lserv);
saddr = addr;
freenetconninfo(ni);
rendezvous(olistenproc, 0);
for(;;){
nctl = listen(dir, ndir);
if(nctl < 0){
fprint(2, "%s: listen %s: %r", argv0, os->addr);
break;
}
data = accept(ctl, ndir);
if(data < 0){
fprint(2, "%s: accept %s: %r\n", argv0, ndir);
continue;
}
s = emalloc9p(sizeof *s);
*s = *os;
s->addr = getremotesys(ndir);
s->infd = s->outfd = data;
s->fpool = nil;
s->rpool = nil;
s->rbuf = nil;
s->wbuf = nil;
proccreate(osrvproc, s, 32*1024);
}
free(os->addr);
free(os);
}
void
othreadlistensrv(Srv *os, char *addr)
{
Srv *s;
s = emalloc9p(sizeof *s);
*s = *os;
s->addr = estrdup9p(addr);
proccreate(olistenproc, s, 32*1024);
rendezvous(olistenproc, 0);
}
void
usage(void)
{
/* Nasty. We should remove most options. Now we know how we use this
* and what does not make sense.
*/
fprint(2, "usage: %s [-A] [-dDCFLBMTS] [-p] [-n addr] [-V vol] [initprog]\n", argv0);
exits("usage");
}
void
threadmain(int argc, char **argv)
{
char* srv;
int mflag;
char* mnt;
char* l;
int slow;
char* prog;
srv = nil;
mnt = nil;
addr = nil;
mflag = (MBEFORE|MCREATE);
slow = 0;
ARGBEGIN{
case 'A':
sfs.auth = nil;
break;
case 'D':
chatty9p++;
break;
case 'C':
condebug++;
break;
case 'F':
framedebug++;
break;
case 'L':
layoutdebug++;
break;
case 'B':
blockdebug++;
break;
case 'E':
case 'd':
eventdebug++;
break;
case 'M':
mainmem->flags |= POOL_PARANOIA;
break;
case 'T':
textdebug++;
break;
case 'S':
slow++;
slow9p = slow * 100;
break;
case 'p':
smallomero++;
break;
case 't':
setterm(EARGF(usage()));
break;
case 'n':
addr = EARGF(usage());
break;
case 'V':
vname = EARGF(usage());
break;
default:
usage();
}ARGEND;
prog = nil;
if (argc >= 1){
prog = argv[0];
argc--;
argv++;
}
while(argc > 0){
setterm(argv[0]);
argc--;
argv++;
}
if (l = getenv("local")){
if (!strcmp(l, "yes"))
sfs.auth = nil;
free(l);
}
if (vname != nil)
sname = getvolsys(vname);
else {
vname = strdup("/devs/ui");
sname = sysname();
}
if (sname == nil)
sysfatal("no screen name");
if (srv == nil && mnt == nil)
mnt = "/devs";
if (addr == nil)
addr= "tcp!*!11007";
if (addr != nil)
saddr = cleanvoladdr(addr);
rfork(RFENVG);
if (!chatty9p)
rfork(RFNOTEG);
fprint(2, "vname %s sname %s mnt %s addr %s\n", vname, sname, mnt, saddr);
fsreqc = chancreate(sizeof(Req*), 16);
destroyc = chancreate(sizeof(Panel*), 16);
startc = chancreate(sizeof(char*), 0);
writeallc = chancreate(sizeof(Panel*), 0);
panelhupc = chancreate(sizeof(Con*), 0);
procrfork(mainproc, 0, 16*1024, RFNAMEG);
rendezvous(mainproc, 0);
if (addr != nil){
othreadlistensrv(&sfs, addr);
if (vname == nil && !strcmp(addr, "tcp!*!11007"))
vname = strdup("/devs/ui");
}
dprint("vname %s srv %s mnt %s sname %s addr %s\n", vname, srv, mnt,sname, saddr);
if (srv != nil || mnt != nil)
threadpostmountsrv(&sfs, srv, mnt, mflag);
if (addr != nil && vname != nil)
proccreate(announceproc, 0, 8*1024);
putenv("omero", smprint("%s/%sui/row:wins/col:1", mnt, sname));
if (mnt != nil)
runoxloop(prog);
}
|