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: