blob: 5a4cd88cef1ec5a8b2d458e933f427ae0df8fbce [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <regexp.h>
#include <thread.h>
#include <plumb.h>
#include "plumber.h"
/*
static char*
nonnil(char *s)
{
if(s == nil)
return "";
return s;
}
*/
int
verbis(int obj, Plumbmsg *m, Rule *r)
{
switch(obj){
default:
fprint(2, "unimplemented 'is' object %d\n", obj);
break;
case OData:
return strcmp(m->data, r->qarg) == 0;
case ODst:
return strcmp(m->dst, r->qarg) == 0;
case OType:
return strcmp(m->type, r->qarg) == 0;
case OWdir:
return strcmp(m->wdir, r->qarg) == 0;
case OSrc:
return strcmp(m->src, r->qarg) == 0;
}
return 0;
}
static void
setvar(Resub rs[10], char *match[10])
{
int i, n;
for(i=0; i<10; i++){
free(match[i]);
match[i] = nil;
}
for(i=0; i<10 && rs[i].s.sp!=nil; i++){
n = rs[i].e.ep-rs[i].s.sp;
match[i] = emalloc(n+1);
memmove(match[i], rs[i].s.sp, n);
match[i][n] = '\0';
}
}
int
clickmatch(Reprog *re, char *text, Resub rs[10], int click)
{
char *clickp;
int i, w;
Rune r;
/* click is in characters, not bytes */
for(i=0; i<click && text[i]!='\0'; i+=w)
w = chartorune(&r, text+i);
clickp = text+i;
for(i=0; i<=click; i++){
memset(rs, 0, 10*sizeof(Resub));
if(regexec(re, text+i, rs, 10))
if(rs[0].s.sp<=clickp && clickp<=rs[0].e.ep)
return 1;
}
return 0;
}
int
verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
{
Resub rs[10];
char *clickval, *alltext;
int p0, p1, ntext;
memset(rs, 0, sizeof rs);
ntext = -1;
switch(obj){
default:
fprint(2, "unimplemented 'matches' object %d\n", obj);
break;
case OData:
clickval = plumblookup(m->attr, "click");
if(clickval == nil){
alltext = m->data;
ntext = m->ndata;
goto caseAlltext;
}
if(!clickmatch(r->regex, m->data, rs, atoi(clickval)))
break;
p0 = rs[0].s.sp - m->data;
p1 = rs[0].e.ep - m->data;
if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1))
break;
e->clearclick = 1;
e->setdata = 1;
e->p0 = p0;
e->p1 = p1;
setvar(rs, e->match);
return 1;
case ODst:
alltext = m->dst;
goto caseAlltext;
case OType:
alltext = m->type;
goto caseAlltext;
case OWdir:
alltext = m->wdir;
goto caseAlltext;
case OSrc:
alltext = m->src;
/* fall through */
caseAlltext:
/* must match full text */
if(ntext < 0)
ntext = strlen(alltext);
if(!regexec(r->regex, alltext, rs, 10) || rs[0].s.sp!=alltext || rs[0].e.ep!=alltext+ntext)
break;
setvar(rs, e->match);
return 1;
}
return 0;
}
int
isfile(char *file, ulong maskon, ulong maskoff)
{
Dir *d;
int mode;
d = dirstat(file);
if(d == nil)
return 0;
mode = d->mode;
free(d);
if((mode & maskon) == 0)
return 0;
if(mode & maskoff)
return 0;
return 1;
}
char*
absolute(char *dir, char *file)
{
char *p;
if(file[0] == '/')
return estrdup(file);
p = emalloc(strlen(dir)+1+strlen(file)+1);
sprint(p, "%s/%s", dir, file);
return cleanname(p);
}
int
verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var)
{
char *file;
switch(obj){
default:
fprint(2, "unimplemented 'isfile' object %d\n", obj);
break;
case OArg:
file = absolute(m->wdir, expand(e, r->arg, nil));
if(isfile(file, maskon, maskoff)){
*var = file;
return 1;
}
free(file);
break;
case OData:
case OWdir:
file = absolute(m->wdir, obj==OData? m->data : m->wdir);
if(isfile(file, maskon, maskoff)){
*var = file;
return 1;
}
free(file);
break;
}
return 0;
}
int
verbset(int obj, Plumbmsg *m, Rule *r, Exec *e)
{
char *new;
switch(obj){
default:
fprint(2, "unimplemented 'is' object %d\n", obj);
break;
case OData:
new = estrdup(expand(e, r->arg, nil));
m->ndata = strlen(new);
free(m->data);
m->data = new;
e->p0 = -1;
e->p1 = -1;
e->setdata = 0;
return 1;
case ODst:
new = estrdup(expand(e, r->arg, nil));
free(m->dst);
m->dst = new;
return 1;
case OType:
new = estrdup(expand(e, r->arg, nil));
free(m->type);
m->type = new;
return 1;
case OWdir:
new = estrdup(expand(e, r->arg, nil));
free(m->wdir);
m->wdir = new;
return 1;
case OSrc:
new = estrdup(expand(e, r->arg, nil));
free(m->src);
m->src = new;
return 1;
}
return 0;
}
int
verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e)
{
switch(obj){
default:
fprint(2, "unimplemented 'add' object %d\n", obj);
break;
case OAttr:
m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil)));
return 1;
}
return 0;
}
int
verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e)
{
char *a;
switch(obj){
default:
fprint(2, "unimplemented 'delete' object %d\n", obj);
break;
case OAttr:
a = expand(e, r->arg, nil);
if(plumblookup(m->attr, a) == nil)
break;
m->attr = plumbdelattr(m->attr, a);
return 1;
}
return 0;
}
int
matchpat(Plumbmsg *m, Exec *e, Rule *r)
{
switch(r->verb){
default:
fprint(2, "unimplemented verb %d\n", r->verb);
break;
case VAdd:
return verbadd(r->obj, m, r, e);
case VDelete:
return verbdelete(r->obj, m, r, e);
case VIs:
return verbis(r->obj, m, r);
case VIsdir:
return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir);
case VIsfile:
return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file);
case VMatches:
return verbmatches(r->obj, m, r, e);
case VSet:
verbset(r->obj, m, r, e);
return 1;
}
return 0;
}
void
freeexec(Exec *exec)
{
int i;
if(exec == nil)
return;
free(exec->dir);
free(exec->file);
for(i=0; i<10; i++)
free(exec->match[i]);
free(exec);
}
Exec*
newexec(Plumbmsg *m)
{
Exec *exec;
exec = emalloc(sizeof(Exec));
exec->msg = m;
exec->p0 = -1;
exec->p1 = -1;
return exec;
}
void
rewrite(Plumbmsg *m, Exec *e)
{
Plumbattr *a, *prev;
if(e->clearclick){
prev = nil;
for(a=m->attr; a!=nil; a=a->next){
if(strcmp(a->name, "click") == 0){
if(prev == nil)
m->attr = a->next;
else
prev->next = a->next;
free(a->name);
free(a->value);
free(a);
break;
}
prev = a;
}
if(e->setdata){
free(m->data);
m->data = estrdup(expand(e, "$0", nil));
m->ndata = strlen(m->data);
}
}
}
char**
buildargv(char *s, Exec *e)
{
char **av;
int ac;
ac = 0;
av = nil;
for(;;){
av = erealloc(av, (ac+1) * sizeof(char*));
av[ac] = nil;
while(*s==' ' || *s=='\t')
s++;
if(*s == '\0')
break;
av[ac++] = estrdup(expand(e, s, &s));
}
return av;
}
Exec*
matchruleset(Plumbmsg *m, Ruleset *rs)
{
int i;
Exec *exec;
if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0)
return nil;
exec = newexec(m);
for(i=0; i<rs->npat; i++)
if(!matchpat(m, exec, rs->pat[i])){
freeexec(exec);
return nil;
}
if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){
free(m->dst);
m->dst = estrdup(rs->port);
}
rewrite(m, exec);
return exec;
}
enum
{
NARGS = 100,
NARGCHAR = 8*1024,
EXECSTACK = 32*1024+(NARGS+1)*sizeof(char*)+NARGCHAR
};
/* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
void
stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
{
int i, n;
char *s, *a;
s = args;
for(i=0; i<NARGS; i++){
a = inargv[i];
if(a == nil)
break;
n = strlen(a)+1;
if((s-args)+n >= NARGCHAR) /* too many characters */
break;
argv[i] = s;
memmove(s, a, n);
s += n;
free(a);
}
argv[i] = nil;
}
void
execproc(void *v)
{
int fd[3];
char **av;
char *args[NARGS+1], argc[NARGCHAR];
fd[0] = open("/dev/null", OREAD);
fd[1] = dup(1, -1);
fd[2] = dup(2, -1);
av = v;
stackargv(av, args, argc);
free(av);
threadexec(nil, fd, args[0], args);
threadexits("can't exec");
}
char*
startup(Ruleset *rs, Exec *e)
{
char **argv;
int i;
if(rs != nil)
for(i=0; i<rs->nact; i++){
if(rs->act[i]->verb == VStart)
goto Found;
if(rs->act[i]->verb == VClient){
if(e->msg->dst==nil || e->msg->dst[0]=='\0')
return "no port for \"client\" rule";
e->holdforclient = 1;
goto Found;
}
}
return "no start action for plumb message";
Found:
argv = buildargv(rs->act[i]->arg, e);
if(argv[0] == nil)
return "empty argument list";
threadcreate(execproc, argv, EXECSTACK);
return nil;
}