Subject: bin/15011: addition of -c and -r to inetd(8)
To: None <gnats-bugs@gnats.netbsd.org>
From: Tomas Svensson <tsn@gbdev.net>
List: netbsd-bugs
Date: 12/21/2001 00:25:54
>Number: 15011
>Category: bin
>Synopsis: addition of -c and -r to inetd(8)
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: bin-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Thu Dec 20 16:27:00 PST 2001
>Closed-Date:
>Last-Modified:
>Originator: Tomas Svensson
>Release: NetBSD 1.5Z
>Organization:
>Environment:
>Description:
This adds the possibility to enforce a currurency limit for each
service. Unlike the rate limit, connection attempts are queued up
until an existing child process exits. New flags added to inetd(8) :
-c maximum
Specify the default maximum number of simultaneous invocations of
each service; the default is unlimited. May be overridden on a
per-service basis with the "max-child" parameter.
It also gives the possibility to change the default "rate" limit
(used to be hardcoded in inetd.c):
-r rate
Specify the default maximum number of times a service can be in-
voked in one minute; the default is 40. May be overridden on a
per-service basis with the "max-rate" parameter.
It can be specified for each service in inetd.conf by:
wait/nowait[:[max-rate][:max-child]]
Examples:
"nowait:256" would set the rate to 256 invocations per minute and
use the default concurrency limit:
"nowait::50" would set the concurrency limit to 50 and use the
default rate limit.
"nowait:256:50" would set the rate limit to 256 and the concurrency
limit to 50.
>How-To-Repeat:
>Fix:
Index: inetd.8
===================================================================
RCS file: /cvsroot/basesrc/usr.sbin/inetd/inetd.8,v
retrieving revision 1.36
diff -u -r1.36 inetd.8
--- inetd.8 2001/03/16 08:19:13 1.36
+++ inetd.8 2001/12/20 22:14:48
@@ -78,8 +78,9 @@
.Dq super-server
.Sh SYNOPSIS
.Nm
-.Op Fl d
-.Op Fl l
+.Op Fl dl
+.Op Fl c Ar maximum
+.Op Fl r Ar rate
.Op Ar configuration file
.Sh DESCRIPTION
.Nm
@@ -102,6 +103,10 @@
.\" Why doesn't just `.Nm :' work?
.Nm "" :
.Bl -tag -width Ds
+.It Fl c Ar maximum
+Specify the default maximum number of simultaneous invocations of each
+service; the default is unlimited. May be overridden on a per-service
+basis with the "max-child" parameter.
.It Fl d
Turns on debugging.
.El
@@ -109,6 +114,10 @@
.Bl -tag -width Ds
.It Fl l
Turns on libwrap connection logging.
+.It Fl r Ar rate
+Specify the default maximum number of times a service can be invoked in
+one minute; the default is 40. May be overridden on a per-service basis
+with the "max-rate" parameter.
.El
.Pp
Upon execution,
@@ -132,7 +141,7 @@
[addr:]service-name
socket-type
protocol[,sndbuf=size][,rcvbuf=size]
-wait/nowait[:max]
+wait/nowait[:[max-rate][:max-child]]
user[:group]
server-program
server program arguments
@@ -146,7 +155,7 @@
service-name/version
socket-type
rpc/protocol[,sndbuf=size][,rcvbuf=size]
-wait/nowait[:max]
+wait/nowait[:[max-rate][:max-child]]
user[:group]
server-program
server program arguments
@@ -306,7 +315,7 @@
.Nm
to check for new service requests to spawn new servers.
The optional
-.Dq max
+.Dq max-rate
suffix (separated from
.Dq wait
or
@@ -315,9 +324,25 @@
be spawned from
.Nm
within an interval of 60 seconds.
+The optional
+.Dq max-child
+suffix (separated from
+.Dq max-rate
+by a dot or colon) specifies the maximum number of simultaneous invocations
+that may be spawned from
+.Nm
+within an interval of 60 seconds. Once the maximum is reached, further
+connection attempts will be queued up until an existing child process exits.
When omitted,
-.Dq max
-defaults to 40.
+.Dq max-rate
+defaults to 40 and
+.Dq max-child
+to unlimited (unless new defaults have been specified using the
+.Fl c
+and
+.Fl k
+options).
+
.Pp
Stream servers are usually marked as
.Dq nowait
Index: inetd.c
===================================================================
RCS file: /cvsroot/basesrc/usr.sbin/inetd/inetd.c,v
retrieving revision 1.74
diff -u -r1.74 inetd.c
--- inetd.c 2001/04/06 11:13:47 1.74
+++ inetd.c 2001/12/20 22:14:50
@@ -261,7 +261,10 @@
int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
#endif
-#define TOOMANY 40 /* don't start more than TOOMANY */
+#define TOOMANYRATE 40 /* don't start more than TOOMANY
+ per CNT_INTVL seconds */
+#define TOOMANYCONC 0 /* concurrent invocations allowed
+ 0 = unlimited */
#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
#define RETRYTIME (60*10) /* retry after bind or server fail */
@@ -275,6 +278,8 @@
fd_set allsock;
int options;
int timingout;
+int toomanyconc;
+int toomanyrate;
struct servent *sp;
char *curdom;
#ifdef NI_WITHSCOPEID
@@ -333,6 +338,9 @@
int se_max; /* max # of instances of this service */
int se_count; /* number started since se_time */
struct timeval se_time; /* start of se_count */
+ int se_max_total; /* max # of concurrent instances */
+ int se_count_total; /* total concurrent children */
+ pid_t *se_pid;
#ifdef MULOG
int se_log;
#define MULOG_RFC931 0x40000000
@@ -443,7 +451,10 @@
struct sigvec sv;
int ch, dofork;
pid_t pid;
+ char *ep;
+ toomanyrate = TOOMANYRATE;
+ toomanyconc = TOOMANYCONC;
Argv = argv;
if (envp == 0 || *envp == 0)
envp = argv;
@@ -453,12 +464,20 @@
while ((ch = getopt(argc, argv,
#ifdef LIBWRAP
- "dl"
+ "c:dlr:"
#else
- "d"
+ "c:dr:"
#endif
)) != -1)
switch(ch) {
+ case 'c':
+ toomanyconc = (int)strtol(optarg, &ep, 10);
+ if (toomanyconc <= 0 || *ep != '\0') {
+ fprintf(stderr, "illegal number -- %s\n",
+ optarg);
+ usage();
+ }
+ break;
case 'd':
debug = 1;
options |= SO_DEBUG;
@@ -468,6 +487,14 @@
lflag = 1;
break;
#endif
+ case 'r':
+ toomanyrate = (int)strtol(optarg, &ep, 10);
+ if (toomanyrate <= 0 || *ep != '\0') {
+ fprintf(stderr, "illegal number -- %s\n",
+ optarg);
+ usage();
+ }
+ break;
case '?':
default:
usage();
@@ -604,6 +631,13 @@
sleep(1);
continue;
}
+ if (pid != 0 && sep->se_max_total > 0) {
+ sep->se_pid[sep->se_count_total++] = pid;
+ if (sep->se_count_total == sep->se_max_total) {
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ }
+ }
if (pid != 0 && sep->se_wait) {
sep->se_wait = pid;
FD_CLR(sep->se_fd, &allsock);
@@ -747,7 +781,7 @@
reapchild(signo)
int signo;
{
- int status;
+ int i, status;
pid_t pid;
struct servtab *sep;
@@ -774,7 +808,24 @@
if (debug)
fprintf(stderr, "restored %s, fd %d\n",
sep->se_service, sep->se_fd);
- }
+ } else
+ if (sep->se_max_total > 0)
+ for (i = 0; i < sep->se_count_total; i++)
+ if (sep->se_pid[i] == pid) {
+ sep->se_count_total--;
+ if (i != sep->se_count_total)
+ memcpy(&sep->se_pid[i],
+ &sep->se_pid[i + 1],
+ sizeof(sep->se_pid[0]) *
+ sep->se_count_total - i);
+ if (sep->se_count_total + 1 ==
+ sep->se_max_total) {
+ FD_SET(sep->se_fd,
+ &allsock);
+ nsock++;
+ }
+ break;
+ }
}
}
@@ -784,7 +835,7 @@
{
struct servtab *sep, *cp, **sepp;
long omask;
- int n;
+ int enabled, n;
if (!setconfig()) {
syslog(LOG_ERR, "%s: %m", CONFIG);
@@ -805,6 +856,13 @@
#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
omask = sigblock(SIGBLOCK);
+ if (sep->se_max_total > 0 && cp->se_max_total > 0) {
+ if (sep->se_count_total > cp->se_max_total)
+ sep->se_count_total = cp->se_max_total;
+ memcpy(cp->se_pid, sep->se_pid,
+ sep->se_count_total * sizeof(pid_t));
+ } else
+ sep->se_count_total = 0;
/*
* sep->se_wait may be holding the pid of a daemon
* that we're waiting for. If so, don't overwrite
@@ -822,8 +880,10 @@
#ifdef IPSEC
SWAP(char *, sep->se_policy, cp->se_policy);
#endif
+ SWAP(pid_t *, sep->se_pid, cp->se_pid);
SWAP(int, cp->se_type, sep->se_type);
SWAP(int, cp->se_max, sep->se_max);
+ SWAP(int, cp->se_max_total, sep->se_max_total);
#undef SWAP
if (isrpcservice(sep))
unregister_rpc(sep);
@@ -953,10 +1013,27 @@
} else
#endif
{
- if (sep->se_fd >= 0)
+ if (sep->se_fd >= 0) {
+ enabled = FD_ISSET(sep->se_fd,
+ &allsock);
close_sep(sep);
+ }
if (sep->se_fd == -1 && !ISMUX(sep))
setup(sep);
+ if (sep->se_fd >= 0) {
+ if (sep->se_max_total > 0 &&
+ sep->se_count_total ==
+ sep->se_max_total) {
+ FD_CLR(sep->se_fd, &allsock);
+ if (enabled)
+ nsock--;
+ } else {
+ FD_SET(sep->se_fd, &allsock);
+ if (!enabled)
+ nsock++;
+ }
+ }
+
}
}
}
@@ -1103,7 +1180,7 @@
return;
}
if (sep->se_socktype == SOCK_STREAM)
- listen(sep->se_fd, 10);
+ listen(sep->se_fd, 64);
FD_SET(sep->se_fd, &allsock);
nsock++;
@@ -1554,14 +1631,26 @@
}
arg = sskip(&cp);
{
- char *cp;
+ char *cp,*cp2;
if ((cp = strchr(arg, ':')) == NULL)
cp = strchr(arg, '.');
if (cp != NULL) {
*cp++ = '\0';
- sep->se_max = atoi(cp);
- } else
- sep->se_max = TOOMANY;
+ if (*cp == ':' || *cp == '.')
+ sep->se_max = toomanyrate;
+ else
+ sep->se_max = atoi(cp);
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ cp2 = strchr(cp, '.');
+ if (cp2 != NULL) {
+ *cp2++ = '\0';
+ sep->se_max_total = atoi(cp2);
+ } else
+ sep->se_max_total = toomanyconc;
+ } else {
+ sep->se_max = toomanyrate;
+ sep->se_max_total = toomanyconc;
+ }
}
sep->se_wait = strcmp(arg, "wait") == 0;
if (ISMUX(sep)) {
@@ -1607,6 +1696,12 @@
sep->se_wait = bi->bi_wait;
} else
sep->se_bi = NULL;
+ if (sep->se_max_total > 0)
+ if ((sep->se_pid = malloc(sep->se_max_total * sizeof(pid_t)))
+ == NULL) {
+ syslog(LOG_ERR, "Out of memory.");
+ exit(1);
+ }
argc = 0;
for (arg = skip(&cp); cp; arg = skip(&cp)) {
#if MULOG
@@ -1667,6 +1762,8 @@
/* Note: se_group is part of the newstr'ed se_user */
if (cp->se_server)
free(cp->se_server);
+ if (cp->se_pid)
+ free(cp->se_pid);
for (i = 0; i < MAXARGV; i++)
if (cp->se_argv[i])
free(cp->se_argv[i]);
@@ -2107,9 +2204,11 @@
{
#ifdef LIBWRAP
- (void)fprintf(stderr, "usage: %s [-dl] [conf]\n", getprogname());
+ (void)fprintf(stderr, "usage: %s [-dl] [-c maximum] [-r rate] [conf]\n"
+ , getprogname());
#else
- (void)fprintf(stderr, "usage: %s [-d] [conf]\n", getprogname());
+ (void)fprintf(stderr, "usage: %s [-d] [-c maximum] [-r rate] [conf]\n",
+ getprogname());
#endif
exit(1);
}
>Release-Note:
>Audit-Trail:
>Unformatted: