summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@centraldogma>2011-12-11 18:35:03 +0100
committercinap_lenrek <cinap_lenrek@centraldogma>2011-12-11 18:35:03 +0100
commit3d189a44527a92743c8739d01b175757cdaf8c73 (patch)
treecde781de588b783dadc09675e73d411d5f64737d
parent4dd4213677184294116caee0a41b169324bccba1 (diff)
downloadplan9front-3d189a44527a92743c8739d01b175757cdaf8c73.tar.xz
listen: add process limit option
-rw-r--r--sys/man/8/listen14
-rw-r--r--sys/src/cmd/aux/listen.c90
2 files changed, 91 insertions, 13 deletions
diff --git a/sys/man/8/listen b/sys/man/8/listen
index 3406f3a48..ede886d6d 100644
--- a/sys/man/8/listen
+++ b/sys/man/8/listen
@@ -10,6 +10,8 @@ listen, listen1, tcp7, tcp9, tcp19, tcp21, tcp22, tcp23, tcp25, tcp53, tcp110, t
.IR trustsrvdir ]
.RB [ -n
.IR namespace ]
+.RB [ -p
+.IR maxprocs ]
.RI [ net ]
.PP
.B aux/listen1
@@ -56,6 +58,18 @@ but
.B -n
selects an alternate
.IR namespace .
+The
+.B -p
+option limits the number of processes that
+.I listen
+spawns to service the connections. If the
+.I maxprocs
+limit is reached,
+.I listen
+will log the event and delay servicing until the number
+of connection processes drops below the limit again. A
+.I maxprocs
+smaller or equal zero means no limit (default).
Option
.B -q
suppresses affirmative log information.
diff --git a/sys/src/cmd/aux/listen.c b/sys/src/cmd/aux/listen.c
index b396c7d06..fd236c6d6 100644
--- a/sys/src/cmd/aux/listen.c
+++ b/sys/src/cmd/aux/listen.c
@@ -22,7 +22,7 @@ struct Announce
};
int readstr(char*, char*, char*, int);
-void dolisten(char*, char*, int, char*, char*);
+void dolisten(char*, char*, int, char*, char*, long*);
void newcall(int, char*, char*, Service*);
int findserv(char*, char*, Service*, char*);
int getserv(char*, char*, Service*);
@@ -33,6 +33,8 @@ void listendir(char*, char*, int);
char listenlog[] = "listen";
+long procs;
+long maxprocs;
int quiet;
int immutable;
char *cpu;
@@ -45,7 +47,7 @@ char *namespace;
void
usage(void)
{
- error("usage: aux/listen [-q] [-n namespace] [-d servdir] [-t trustdir]"
+ error("usage: aux/listen [-q] [-n namespace] [-d servdir] [-t trustdir] [-p maxprocs]"
" [proto]");
}
@@ -88,6 +90,7 @@ main(int argc, char *argv[])
quiet = 0;
immutable = 0;
argv0 = argv[0];
+ maxprocs = 0;
cpu = getenv("cputype");
if(cpu == 0)
error("can't get cputype");
@@ -105,6 +108,9 @@ main(int argc, char *argv[])
case 'n':
namespace = EARGF(usage());
break;
+ case 'p':
+ maxprocs = atoi(EARGF(usage()));
+ break;
case 'i':
/*
* fixed configuration, no periodic
@@ -161,9 +167,11 @@ void
listendir(char *protodir, char *srvdir, int trusted)
{
int ctl, pid, start;
- char dir[40], err[128];
+ char dir[40], err[128], ds[128];
+ long childs;
Announce *a;
Waitmsg *wm;
+ int whined;
if (srvdir == 0)
return;
@@ -201,27 +209,32 @@ listendir(char *protodir, char *srvdir, int trusted)
sleep((pid*10)%200);
+ /* copy to stack */
+ strncpy(ds, a->a, sizeof(ds));
+ whined = a->whined;
+
/* a process per service */
- switch(pid = rfork(RFFDG|RFPROC)){
+ switch(pid = rfork(RFFDG|RFPROC|RFMEM)){
case -1:
- syslog(1, listenlog, "couldn't fork for %s", a->a);
+ syslog(1, listenlog, "couldn't fork for %s", ds);
break;
case 0:
+ childs = 0;
for(;;){
- ctl = announce(a->a, dir);
+ ctl = announce(ds, dir);
if(ctl < 0) {
errstr(err, sizeof err);
- if (!a->whined)
+ if (!whined)
syslog(1, listenlog,
"giving up on %s: %r",
- a->a);
+ ds);
if(strstr(err, "address in use")
!= nil)
exits("addr-in-use");
else
exits("ctl");
}
- dolisten(proto, dir, ctl, srvdir, a->a);
+ dolisten(proto, dir, ctl, srvdir, ds, &childs);
close(ctl);
}
default:
@@ -377,14 +390,57 @@ becomenone(void)
}
void
-dolisten(char *proto, char *dir, int ctl, char *srvdir, char *dialstr)
+dolisten(char *proto, char *dir, int ctl, char *srvdir, char *dialstr, long *pchilds)
{
Service s;
- char ndir[40];
- int nctl, data;
+ char ndir[40], wbuf[64];
+ int nctl, data, wfd, nowait;
procsetname("%s %s", dir, dialstr);
+
+ wfd = -1;
+ nowait = RFNOWAIT;
+ if(pchilds && maxprocs > 0){
+ snprint(wbuf, sizeof(wbuf), "/proc/%d/wait", getpid());
+ if((wfd = open(wbuf, OREAD)) >= 0)
+ nowait = 0;
+ }
+
for(;;){
+ if(!nowait){
+ static int hit = 0;
+ Dir *d;
+
+ /*
+ * check for exited subprocesses
+ */
+ if(procs >= maxprocs || (*pchilds % 8) == 0)
+ while(*pchilds > 0){
+ d = dirfstat(wfd);
+ if(d == nil || d->length == 0){
+ free(d);
+ break;
+ }
+ free(d);
+ if(read(wfd, wbuf, sizeof(wbuf)) > 0){
+ adec(&procs);
+ pchilds[0]--;
+ }
+ }
+
+ if(procs >= maxprocs){
+ if(!quiet && !hit)
+ syslog(1, listenlog, "%s: process limit of %ld reached",
+ proto, maxprocs);
+ if(hit < 8)
+ hit++;
+ sleep(10<<hit);
+ continue;
+ }
+ if(hit > 0)
+ hit--;
+ }
+
/*
* wait for a call (or an error)
*/
@@ -392,13 +448,15 @@ dolisten(char *proto, char *dir, int ctl, char *srvdir, char *dialstr)
if(nctl < 0){
if(!quiet)
syslog(1, listenlog, "listen: %r");
+ if(wfd >= 0)
+ close(wfd);
return;
}
/*
* start a subprocess for the connection
*/
- switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG)){
+ switch(rfork(RFFDG|RFPROC|RFMEM|RFENVG|RFNAMEG|RFNOTEG|nowait)){
case -1:
reject(nctl, ndir, "host overloaded");
close(nctl);
@@ -423,10 +481,16 @@ dolisten(char *proto, char *dir, int ctl, char *srvdir, char *dialstr)
fprint(nctl, "keepalive");
close(ctl);
close(nctl);
+ if(wfd >= 0)
+ close(wfd);
newcall(data, proto, ndir, &s);
exits(0);
default:
close(nctl);
+ if(nowait)
+ break;
+ ainc(&procs);
+ pchilds[0]++;
break;
}
}