
devproc assumes that when we hold the Proc.debug qlock, the process will be prevented from exiting. but there is another race where the process has already exited and the Proc* slot gets reused. to solve this, on process creation we also have to acquire the debug qlock while initializing the fields of the process. this also means newproc() should only initialize fields *not* protected by the debug qlock. always acquire the Proc.debug qlock when changing strings in the proc structure to avoid doublefree on concurrent update. for changing the user string, we add a procsetuser() function that does this for auth.c and devcap. remove pgrpnote() from pgrp.c and replace by static postnotepg() in devproc. avoid the assumption that the Proc* entries returned by proctab() are continuous. fixed devproc permission issues: - make sure only eve can access /proc/trace - none should only be allowed to read its own /proc/n/text - move Proc.kp checks into procopen() pid reuse was not handled correctly, as we where only checking if a pid had a living process, but there still could be processes expecting a particular parentpid or noteid. this is now addressed with reference counted Pid structures which are organized in a hash table. read access to the hash table does not require locks which will be usefull for dtracy later.
289 lines
4.5 KiB
C
289 lines
4.5 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
|
|
enum {
|
|
Whinesecs = 10, /* frequency of out-of-resources printing */
|
|
};
|
|
|
|
static Ref pgrpid;
|
|
static Ref mountid;
|
|
|
|
Pgrp*
|
|
newpgrp(void)
|
|
{
|
|
Pgrp *p;
|
|
|
|
p = smalloc(sizeof(Pgrp));
|
|
p->ref = 1;
|
|
p->pgrpid = incref(&pgrpid);
|
|
return p;
|
|
}
|
|
|
|
Rgrp*
|
|
newrgrp(void)
|
|
{
|
|
Rgrp *r;
|
|
|
|
r = smalloc(sizeof(Rgrp));
|
|
r->ref = 1;
|
|
return r;
|
|
}
|
|
|
|
void
|
|
closergrp(Rgrp *r)
|
|
{
|
|
if(decref(r) == 0)
|
|
free(r);
|
|
}
|
|
|
|
void
|
|
closepgrp(Pgrp *p)
|
|
{
|
|
Mhead **h, **e, *f;
|
|
Mount *m;
|
|
|
|
if(decref(p))
|
|
return;
|
|
|
|
e = &p->mnthash[MNTHASH];
|
|
for(h = p->mnthash; h < e; h++) {
|
|
while((f = *h) != nil){
|
|
*h = f->hash;
|
|
wlock(&f->lock);
|
|
m = f->mount;
|
|
f->mount = nil;
|
|
wunlock(&f->lock);
|
|
mountfree(m);
|
|
putmhead(f);
|
|
}
|
|
}
|
|
free(p);
|
|
}
|
|
|
|
void
|
|
pgrpinsert(Mount **order, Mount *m)
|
|
{
|
|
Mount *f;
|
|
|
|
m->order = nil;
|
|
if(*order == nil) {
|
|
*order = m;
|
|
return;
|
|
}
|
|
for(f = *order; f != nil; f = f->order) {
|
|
if(m->mountid < f->mountid) {
|
|
m->order = f;
|
|
*order = m;
|
|
return;
|
|
}
|
|
order = &f->order;
|
|
}
|
|
*order = m;
|
|
}
|
|
|
|
/*
|
|
* pgrpcpy MUST preserve the mountid allocation order of the parent group
|
|
*/
|
|
void
|
|
pgrpcpy(Pgrp *to, Pgrp *from)
|
|
{
|
|
Mount *n, *m, **link, *order;
|
|
Mhead *f, **tom, **l, *mh;
|
|
int i;
|
|
|
|
wlock(&to->ns);
|
|
rlock(&from->ns);
|
|
order = nil;
|
|
tom = to->mnthash;
|
|
for(i = 0; i < MNTHASH; i++) {
|
|
l = tom++;
|
|
for(f = from->mnthash[i]; f != nil; f = f->hash) {
|
|
rlock(&f->lock);
|
|
mh = newmhead(f->from);
|
|
*l = mh;
|
|
l = &mh->hash;
|
|
link = &mh->mount;
|
|
for(m = f->mount; m != nil; m = m->next) {
|
|
n = smalloc(sizeof(Mount));
|
|
n->mountid = m->mountid;
|
|
n->mflag = m->mflag;
|
|
n->to = m->to;
|
|
incref(n->to);
|
|
if(m->spec != nil)
|
|
kstrdup(&n->spec, m->spec);
|
|
pgrpinsert(&order, n);
|
|
*link = n;
|
|
link = &n->next;
|
|
}
|
|
runlock(&f->lock);
|
|
}
|
|
}
|
|
/*
|
|
* Allocate mount ids in the same sequence as the parent group
|
|
*/
|
|
for(m = order; m != nil; m = m->order)
|
|
m->mountid = incref(&mountid);
|
|
runlock(&from->ns);
|
|
wunlock(&to->ns);
|
|
}
|
|
|
|
Fgrp*
|
|
dupfgrp(Fgrp *f)
|
|
{
|
|
Fgrp *new;
|
|
Chan *c;
|
|
int i;
|
|
|
|
new = smalloc(sizeof(Fgrp));
|
|
if(f == nil){
|
|
new->fd = smalloc(DELTAFD*sizeof(Chan*));
|
|
new->nfd = DELTAFD;
|
|
new->ref = 1;
|
|
return new;
|
|
}
|
|
|
|
lock(f);
|
|
/* Make new fd list shorter if possible, preserving quantization */
|
|
new->nfd = f->maxfd+1;
|
|
i = new->nfd%DELTAFD;
|
|
if(i != 0)
|
|
new->nfd += DELTAFD - i;
|
|
new->fd = malloc(new->nfd*sizeof(Chan*));
|
|
if(new->fd == nil){
|
|
unlock(f);
|
|
free(new);
|
|
error("no memory for fgrp");
|
|
}
|
|
new->ref = 1;
|
|
|
|
new->maxfd = f->maxfd;
|
|
for(i = 0; i <= f->maxfd; i++) {
|
|
if((c = f->fd[i]) != nil){
|
|
incref(c);
|
|
new->fd[i] = c;
|
|
}
|
|
}
|
|
unlock(f);
|
|
|
|
return new;
|
|
}
|
|
|
|
void
|
|
closefgrp(Fgrp *f)
|
|
{
|
|
int i;
|
|
Chan *c;
|
|
|
|
if(f == nil || decref(f))
|
|
return;
|
|
|
|
/*
|
|
* If we get into trouble, forceclosefgrp
|
|
* will bail us out.
|
|
*/
|
|
up->closingfgrp = f;
|
|
for(i = 0; i <= f->maxfd; i++)
|
|
if((c = f->fd[i]) != nil){
|
|
f->fd[i] = nil;
|
|
cclose(c);
|
|
}
|
|
up->closingfgrp = nil;
|
|
|
|
free(f->fd);
|
|
free(f);
|
|
}
|
|
|
|
/*
|
|
* Called from interrupted() because up is in the middle
|
|
* of closefgrp and just got a kill ctl message.
|
|
* This usually means that up has wedged because
|
|
* of some kind of deadly embrace with mntclose
|
|
* trying to talk to itself. To break free, hand the
|
|
* unclosed channels to the close queue. Once they
|
|
* are finished, the blocked cclose that we've
|
|
* interrupted will finish by itself.
|
|
*/
|
|
void
|
|
forceclosefgrp(void)
|
|
{
|
|
int i;
|
|
Chan *c;
|
|
Fgrp *f;
|
|
|
|
if(up->procctl != Proc_exitme || up->closingfgrp == nil){
|
|
print("bad forceclosefgrp call");
|
|
return;
|
|
}
|
|
|
|
f = up->closingfgrp;
|
|
for(i = 0; i <= f->maxfd; i++)
|
|
if((c = f->fd[i]) != nil){
|
|
f->fd[i] = nil;
|
|
ccloseq(c);
|
|
}
|
|
}
|
|
|
|
|
|
Mount*
|
|
newmount(Chan *to, int flag, char *spec)
|
|
{
|
|
Mount *m;
|
|
|
|
m = smalloc(sizeof(Mount));
|
|
m->to = to;
|
|
incref(to);
|
|
m->mountid = incref(&mountid);
|
|
m->mflag = flag;
|
|
if(spec != nil)
|
|
kstrdup(&m->spec, spec);
|
|
|
|
return m;
|
|
}
|
|
|
|
void
|
|
mountfree(Mount *m)
|
|
{
|
|
Mount *f;
|
|
|
|
while((f = m) != nil) {
|
|
m = m->next;
|
|
cclose(f->to);
|
|
free(f->spec);
|
|
free(f);
|
|
}
|
|
}
|
|
|
|
void
|
|
resrcwait(char *reason)
|
|
{
|
|
static ulong lastwhine;
|
|
ulong now;
|
|
char *p;
|
|
|
|
if(up == nil)
|
|
panic("resrcwait");
|
|
|
|
p = up->psstate;
|
|
if(reason != nil) {
|
|
if(waserror()){
|
|
up->psstate = p;
|
|
nexterror();
|
|
}
|
|
up->psstate = reason;
|
|
now = seconds();
|
|
/* don't tie up the console with complaints */
|
|
if(now - lastwhine > Whinesecs) {
|
|
lastwhine = now;
|
|
print("%s\n", reason);
|
|
}
|
|
}
|
|
tsleep(&up->sleep, return0, 0, 100+nrand(200));
|
|
if(reason != nil) {
|
|
up->psstate = p;
|
|
poperror();
|
|
}
|
|
}
|