2016-12-11 01:19:51 +01:00
|
|
|
/*
|
|
|
|
* This file is part of Jehanne.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
|
|
|
*
|
|
|
|
* Jehanne is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, version 2 of the License.
|
|
|
|
*
|
|
|
|
* Jehanne is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "../port/error.h"
|
|
|
|
|
|
|
|
#define QID(q) ((((uint32_t)(q).path)&0x0000001F)>>0)
|
|
|
|
#define STATSIZE (2*KNAMELEN+NUMSIZE+9*NUMSIZE + 1)
|
|
|
|
|
|
|
|
/* devself provides to each process access to its own structures
|
|
|
|
* (pid, ppid, segments...).
|
|
|
|
*/
|
|
|
|
typedef enum SelfNodes
|
|
|
|
{
|
|
|
|
Qdir,
|
|
|
|
|
|
|
|
/* Process control */
|
|
|
|
Qpid,
|
|
|
|
Qppid,
|
|
|
|
Qsegments,
|
|
|
|
Qpipes,
|
2016-12-15 22:42:01 +01:00
|
|
|
Qwdir,
|
2016-12-11 01:19:51 +01:00
|
|
|
} SelfNodes;
|
|
|
|
|
|
|
|
typedef enum SegmentsCmd
|
|
|
|
{
|
|
|
|
CMsegattach,
|
|
|
|
CMsegdetach,
|
|
|
|
CMsegfree,
|
|
|
|
} SegmentsCmd;
|
|
|
|
|
|
|
|
static
|
|
|
|
Cmdtab proccmd[] = {
|
|
|
|
CMsegattach, "attach", 5,
|
|
|
|
CMsegdetach, "detach", 2,
|
|
|
|
CMsegfree, "free", 3,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef union PipeSet
|
|
|
|
{
|
|
|
|
long merged;
|
|
|
|
int fd[2];
|
|
|
|
} PipeSet;
|
|
|
|
|
|
|
|
static Dirtab selfdir[]={
|
|
|
|
".", {Qdir, 0, QTDIR}, 0, DMDIR|0777,
|
|
|
|
"pid", {Qpid}, 0, 0,
|
|
|
|
"ppid", {Qppid}, 0, 0,
|
|
|
|
"segments", {Qsegments}, 0, 0644,
|
|
|
|
"pipes", {Qpipes}, 0, 0,
|
2016-12-15 22:42:01 +01:00
|
|
|
"wdir", {Qwdir}, 0, 0644,
|
2016-12-11 01:19:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-12-15 22:42:01 +01:00
|
|
|
static int
|
|
|
|
selfgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
|
|
|
|
{
|
|
|
|
long length;
|
|
|
|
if(tab == 0)
|
|
|
|
return -1;
|
|
|
|
if(i == DEVDOTDOT){
|
|
|
|
if(QID(c->qid) != Qdir)
|
|
|
|
panic("selfwalk %llux", c->qid.path);
|
|
|
|
devdir(c, selfdir[0].qid, "#0", 0, up->user, selfdir[0].perm, dp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(name){
|
|
|
|
for(i=1; i<ntab; i++)
|
|
|
|
if(strcmp(tab[i].name, name) == 0)
|
|
|
|
break;
|
|
|
|
if(i==ntab)
|
|
|
|
return -1;
|
|
|
|
tab += i;
|
|
|
|
}else{
|
|
|
|
/* skip over the first element, that for . itself */
|
|
|
|
i++;
|
|
|
|
if(i >= ntab)
|
|
|
|
return -1;
|
|
|
|
tab += i;
|
|
|
|
}
|
|
|
|
if(tab->qid.path == Qwdir) {
|
|
|
|
/* file length might be relevant to the caller to
|
|
|
|
* malloc enough space in the buffer
|
|
|
|
*/
|
|
|
|
length = 1 + strlen(up->dot->path->s);
|
|
|
|
} else {
|
|
|
|
length = tab->length;
|
|
|
|
}
|
|
|
|
devdir(c, tab->qid, tab->name, length, up->user, tab->perm, dp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-12-11 01:19:51 +01:00
|
|
|
static Chan*
|
|
|
|
selfattach(Chan *c, Chan *ac, char *spec, int flags)
|
|
|
|
{
|
|
|
|
return devattach('0', spec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Walkqid*
|
|
|
|
selfwalk(Chan *c, Chan *nc, char **name, int nname)
|
|
|
|
{
|
2016-12-15 22:42:01 +01:00
|
|
|
return devwalk(c, nc, name, nname, selfdir, nelem(selfdir), selfgen);
|
2016-12-11 01:19:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
selfstat(Chan *c, uint8_t *dp, long n)
|
|
|
|
{
|
2016-12-15 22:42:01 +01:00
|
|
|
return devstat(c, dp, n, selfdir, nelem(selfdir), selfgen);
|
2016-12-11 01:19:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static Chan*
|
|
|
|
selfopen(Chan *c, unsigned long omode)
|
|
|
|
{
|
|
|
|
c->aux = nil;
|
2016-12-15 22:42:01 +01:00
|
|
|
c = devopen(c, omode, selfdir, nelem(selfdir), selfgen);
|
2016-12-11 01:19:51 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2016-12-15 22:42:01 +01:00
|
|
|
long
|
|
|
|
read_working_dir(Proc* p, void *va, long n, int64_t off)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
char *path;
|
|
|
|
Chan *dot;
|
|
|
|
|
|
|
|
dot = up->dot;
|
|
|
|
path = dot->path->s;
|
|
|
|
i = 1 + strlen(path);
|
|
|
|
if(va == nil){
|
|
|
|
/* the user is actually asking for the space */
|
|
|
|
if(off != 0 && off != ~0) {
|
|
|
|
/* #0/wdir does not allow offset in read */
|
|
|
|
error("offset reading wdir size");
|
|
|
|
}
|
|
|
|
errorl("not enough space in buffer", ~i);
|
|
|
|
}
|
|
|
|
if(off > i)
|
|
|
|
return 0;
|
|
|
|
j = i - off;
|
|
|
|
if(n < j)
|
|
|
|
j = n;
|
|
|
|
memmove(va, path, j);
|
|
|
|
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
long
|
|
|
|
write_working_dir(Proc* p, void *va, long n, int64_t off)
|
|
|
|
{
|
|
|
|
Chan *c, *dot;
|
|
|
|
char *path, *epath;
|
|
|
|
|
|
|
|
dot = p->dot;
|
|
|
|
path = va;
|
|
|
|
epath = vmemchr(path, 0, n);
|
|
|
|
|
|
|
|
if(n <= 0)
|
|
|
|
error(Ebadarg);
|
|
|
|
if(off != 0 && off != ~0)
|
|
|
|
error("offset writing wdir");
|
|
|
|
if(epath-path>=n)
|
|
|
|
error("no terminal zero writing wdir");
|
|
|
|
|
|
|
|
c = namec(path, Atodir, 0, 0);
|
|
|
|
if(CASV(&p->dot, dot, c)){
|
|
|
|
cclose(dot);
|
|
|
|
} else {
|
|
|
|
cclose(c);
|
|
|
|
error("race writing wdir");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-11 01:19:51 +01:00
|
|
|
static long
|
|
|
|
selfread(Chan *c, void *va, long n, int64_t off)
|
|
|
|
{
|
|
|
|
int64_t offset;
|
|
|
|
ProcSegment *sg;
|
|
|
|
int i, j;
|
|
|
|
char statbuf[NSEG*STATSIZE];
|
|
|
|
|
|
|
|
offset = off;
|
|
|
|
switch(QID(c->qid)){
|
2016-12-15 22:42:01 +01:00
|
|
|
case Qdir:
|
|
|
|
return devdirread(c, va, n, selfdir, nelem(selfdir), selfgen);
|
2016-12-11 01:19:51 +01:00
|
|
|
case Qsegments:
|
|
|
|
rlock(&up->seglock);
|
|
|
|
j = 0;
|
|
|
|
for(i = 0; i < NSEG; i++) {
|
|
|
|
sg = up->seg[i];
|
|
|
|
if(sg == 0)
|
|
|
|
continue;
|
|
|
|
j += sprint(statbuf+j, "%-6s %c%c %p %p %4d\n",
|
|
|
|
segment_name(sg),
|
|
|
|
!(sg->type&SgWrite) ? 'R' : ' ',
|
|
|
|
(sg->type&SgExecute) ? 'x' : ' ',
|
|
|
|
' ', // sg->profile ? 'P' : ' ', // here was profiling
|
|
|
|
sg->base, sg->top, sg->r.ref);
|
|
|
|
}
|
|
|
|
runlock(&up->seglock);
|
|
|
|
if(offset >= j)
|
|
|
|
return 0;
|
|
|
|
if(offset+n > j)
|
|
|
|
n = j-offset;
|
|
|
|
if(n == 0 && offset == 0)
|
|
|
|
exhausted("segments");
|
|
|
|
memmove(va, &statbuf[offset], n);
|
|
|
|
return n;
|
2016-12-15 22:42:01 +01:00
|
|
|
case Qwdir:
|
|
|
|
return read_working_dir(up, va, n, off);
|
2016-12-11 01:19:51 +01:00
|
|
|
default:
|
|
|
|
error(Egreg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uintptr_t
|
|
|
|
segattach(Proc* p, int attr, const char* name, uintptr_t va, usize len)
|
|
|
|
{
|
|
|
|
unsigned int sno, tmp;
|
|
|
|
ProcSegment *s;
|
|
|
|
uintptr_t pa, size;
|
|
|
|
|
|
|
|
|
|
|
|
if((va != 0 && va < UTZERO) || iskaddr(va))
|
|
|
|
error("virtual address below text or in kernel");
|
|
|
|
|
|
|
|
vmemchr((void *)name, 0, ~0);
|
|
|
|
|
|
|
|
for(sno = 0; sno < NSEG; sno++)
|
|
|
|
if(p->seg[sno] == nil && sno != ESEG)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(sno == NSEG)
|
|
|
|
error("too many segments in process");
|
|
|
|
|
|
|
|
len = ROUNDUP(va+len, PGSZ) - ROUNDDN(va, PGSZ);
|
|
|
|
va = ROUNDDN(va, PGSZ);
|
|
|
|
|
|
|
|
if(rawmem_find((char**)&name, &pa, &tmp, &size)){
|
|
|
|
s = 0;
|
|
|
|
wlock(&p->seglock);
|
|
|
|
if(pa){
|
|
|
|
if(!segment_physical(&s, attr&SegPermissionMask, attr&SegFlagMask, va, pa))
|
|
|
|
goto SegAttachEbadarg;
|
|
|
|
} else if(size != -1){
|
|
|
|
if(!segment_global(&s, attr&SegPermissionMask, va, (char*)name))
|
|
|
|
goto SegAttachEbadarg;
|
|
|
|
} else {
|
|
|
|
if(!segment_virtual(&s, tmp&SegmentTypesMask, attr&SegPermissionMask&~SgExecute, attr&SegFlagMask, va, va+len))
|
|
|
|
goto SegAttachEbadarg;
|
|
|
|
}
|
|
|
|
for(tmp = 0; tmp < NSEG; tmp++){
|
|
|
|
if(p->seg[tmp])
|
|
|
|
if((p->seg[tmp]->base > s->base && p->seg[tmp]->base < s->top)
|
|
|
|
|| (p->seg[tmp]->top > s->base && p->seg[tmp]->top < s->top)){
|
|
|
|
goto SegAttachEsoverlap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
up->seg[sno] = s;
|
|
|
|
wunlock(&p->seglock);
|
|
|
|
return s->base;
|
|
|
|
}
|
|
|
|
error("segment not found");
|
|
|
|
SegAttachEbadarg:
|
|
|
|
wunlock(&p->seglock);
|
|
|
|
error(Ebadarg);
|
|
|
|
SegAttachEsoverlap:
|
|
|
|
wunlock(&p->seglock);
|
|
|
|
segment_release(&s);
|
|
|
|
error(Esoverlap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uintptr_t
|
|
|
|
segdetach(Proc* p, uintptr_t va)
|
|
|
|
{
|
|
|
|
if(!proc_segment_detach(p, va))
|
|
|
|
error(Ebadarg);
|
|
|
|
|
|
|
|
mmuflush();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uintptr_t
|
|
|
|
segfree(Proc* p, uintptr_t va, unsigned long len)
|
|
|
|
{
|
|
|
|
ProcSegment *s;
|
|
|
|
uintptr_t from, to;
|
|
|
|
|
|
|
|
from = PTR2UINT(va);
|
|
|
|
to = ROUNDDN(from + len, PGSZ);
|
|
|
|
|
|
|
|
s = proc_segment(p, from);
|
|
|
|
if(s == nil || to < from || to > s->top)
|
|
|
|
error(Ebadarg);
|
|
|
|
|
|
|
|
from = ROUNDUP(from, PGSZ);
|
|
|
|
if(from == to)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
segment_free_pages(s, from, to);
|
|
|
|
mmuflush();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
procsegctl(Proc *p, char *va, int n)
|
|
|
|
{
|
|
|
|
Cmdbuf *cb;
|
|
|
|
Cmdtab *ct;
|
|
|
|
int attr;
|
|
|
|
const char *class;
|
|
|
|
uintptr_t vareq;
|
|
|
|
unsigned long len;
|
|
|
|
long result;
|
|
|
|
|
|
|
|
cb = parsecmd(va, n);
|
|
|
|
if(waserror()){
|
|
|
|
free(cb);
|
|
|
|
nexterror();
|
|
|
|
}
|
|
|
|
|
|
|
|
ct = lookupcmd(cb, proccmd, nelem(proccmd));
|
|
|
|
switch(ct->index){
|
|
|
|
default:
|
|
|
|
error(Ebadctl);
|
|
|
|
return -1;
|
|
|
|
case CMsegattach:
|
|
|
|
attr = strtoul(cb->f[1], 0, 0);
|
|
|
|
vareq = strtoull(cb->f[2], 0, 0);
|
|
|
|
len = strtoull(cb->f[3], 0, 0);
|
|
|
|
class = cb->f[4];
|
|
|
|
result = segattach(p, attr, class, vareq, len);
|
|
|
|
break;
|
|
|
|
case CMsegdetach:
|
|
|
|
vareq = strtoull(cb->f[1], 0, 0);
|
|
|
|
result = segdetach(p, vareq);
|
|
|
|
break;
|
|
|
|
case CMsegfree:
|
|
|
|
vareq = strtoull(cb->f[1], 0, 0);
|
|
|
|
len = strtoull(cb->f[2], 0, 0);
|
|
|
|
result = segfree(p, vareq, len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
poperror();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
selfwrite(Chan *c, void *va, long n, int64_t off)
|
|
|
|
{
|
|
|
|
switch(QID(c->qid)){
|
|
|
|
default:
|
|
|
|
error(Egreg);
|
|
|
|
case Qsegments:
|
2016-12-15 22:42:01 +01:00
|
|
|
return procsegctl(up, va, n);
|
|
|
|
case Qwdir:
|
|
|
|
return write_working_dir(up, va, n, off);
|
2016-12-11 01:19:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
newfd2(int fd[2], Chan *c[2])
|
|
|
|
{
|
|
|
|
extern int findfreefd(Fgrp *f, int start);
|
|
|
|
extern void unlockfgrp(Fgrp *f);
|
|
|
|
Fgrp *f;
|
|
|
|
|
|
|
|
f = up->fgrp;
|
|
|
|
lock(&f->l);
|
|
|
|
fd[0] = findfreefd(f, 0);
|
|
|
|
if(fd[0] < 0){
|
|
|
|
unlockfgrp(f);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fd[1] = findfreefd(f, fd[0]+1);
|
|
|
|
if(fd[1] < 0){
|
|
|
|
unlockfgrp(f);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(fd[1] > f->maxfd)
|
|
|
|
f->maxfd = fd[1];
|
|
|
|
f->fd[fd[0]] = c[0];
|
|
|
|
f->fd[fd[1]] = c[1];
|
|
|
|
unlockfgrp(f);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
newpipe(void)
|
|
|
|
{
|
|
|
|
PipeSet pipe;
|
|
|
|
Chan *c[2];
|
|
|
|
static char *datastr[] = {"data", "data1"};
|
|
|
|
|
|
|
|
c[0] = namec("#|", Atodir, 0, 0);
|
|
|
|
c[1] = nil;
|
|
|
|
pipe.fd[0] = -1;
|
|
|
|
pipe.fd[1] = -1;
|
|
|
|
|
|
|
|
if(waserror()){
|
|
|
|
cclose(c[0]);
|
|
|
|
if(c[1])
|
|
|
|
cclose(c[1]);
|
|
|
|
nexterror();
|
|
|
|
}
|
|
|
|
c[1] = cclone(c[0]);
|
|
|
|
if(walk(&c[0], datastr+0, 1, 1, nil) < 0)
|
|
|
|
error(Egreg);
|
|
|
|
if(walk(&c[1], datastr+1, 1, 1, nil) < 0)
|
|
|
|
error(Egreg);
|
|
|
|
c[0] = c[0]->dev->open(c[0], ORDWR);
|
|
|
|
c[1] = c[1]->dev->open(c[1], ORDWR);
|
|
|
|
if(newfd2(pipe.fd, c) < 0)
|
|
|
|
error(Enofd);
|
|
|
|
poperror();
|
|
|
|
|
|
|
|
return pipe.merged;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
selfremove(Chan* c)
|
|
|
|
{
|
|
|
|
long pipeset;
|
|
|
|
switch((uint32_t)c->qid.path){
|
|
|
|
case Qsegments:
|
|
|
|
error(Eperm);
|
|
|
|
break;
|
|
|
|
case Qpid:
|
|
|
|
errorl("got pid", up->pid);
|
|
|
|
break;
|
|
|
|
case Qppid:
|
|
|
|
errorl("got parent pid", up->parentpid);
|
|
|
|
break;
|
|
|
|
case Qpipes:
|
|
|
|
pipeset = newpipe();
|
|
|
|
errorl("got new pipes", pipeset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
selfclose(Chan *c)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Dev selfdevtab = {
|
|
|
|
'0',
|
|
|
|
"self",
|
|
|
|
|
|
|
|
devreset,
|
|
|
|
devinit,
|
|
|
|
devshutdown,
|
|
|
|
selfattach,
|
|
|
|
selfwalk,
|
|
|
|
selfstat,
|
|
|
|
selfopen,
|
|
|
|
devcreate,
|
|
|
|
selfclose,
|
|
|
|
selfread,
|
|
|
|
devbread,
|
|
|
|
selfwrite,
|
|
|
|
devbwrite,
|
|
|
|
selfremove,
|
|
|
|
devwstat,
|
|
|
|
};
|