[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#302079: dpkg: [PATCH] setting ulimit from start-stop-daemon



Package: dpkg
Version: 1.10.27
Severity: wishlist


Hello,
  with the attached patch, start-stop-daemon will look for
a ``limit'' file before loading a daemon, and set the ulimits
for the daemon accordingly. Just an example. Let's say 
we have an init script that launches:

   start-stop-daemon --start --exec /usr/sbin/adaemon -- $ARGS

With the attacched patch, start-stop-daemon will then look for a 
file called ``/etc/limits/adaemon'' containing something like:

  # Ok, limit core file sizes
core    soft    2048
core    hard    4096
nofile  soft    100
nofile  hard    200
#nproc   soft    50
#nproc   hard    150
cpu     soft    12
cpu     hard    15
data    soft    120000
data    hard    135000
#fsize   soft    14000
#fsize   hard    15000
rss     soft    10200
rss     hard    14500
stack   soft    120000
stack   hard    130000
memlock soft    15000
memlock hard    17000
as      soft    10000000
as      hard    10000000

  The patch should work quite fine. It respects the quiet and 
verbose flags, prints a warning in case of wrong lines or badly
formatted file, supports comments, and goes on as far as possible
in case of errors, ignores the absence of the file and should 
generally behave correctly in most conditions. I believe applying 
this patch would maintain complete backward compatibility, and only 
add useful functions. It adds ``transparent'' support for setting
ulimits on any daemon launched by start-stop-daemon.

  The patch should include support for *BSD systems, even if I 
haven't had the chance to test it on such systems.
I'm a DD too, and if there is any chance for it to be included
in the ``upstream'' dpkg, I'd be willing to work on writing
documentation, verify it works under kfreebsd and hurd, update
it for your current development tree and generally verify its
correct behavior.

  I believe it to be very important to have a system wide
method to set per-daemon limits, considering the always
existing problems about resource exaustion and oom.

  I don't like the resulting code pretty much, but should
be quite reliable (I put lot of attention on it), backward
compatible and generally compliant with the ``coding style''
used for start-stop-daemon.

  I would also love to have a start-stop-daemon with reload
and some lightweight monitoring features, either in the main
dpkg package, or as an enhanced-start-stop-daemon package.

Cheers, 
Carlo


-- System Information:
Debian Release: testing/unstable
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing')
Architecture: i386 (i686)
Kernel: Linux 2.6.8
Locale: LANG=C, LC_CTYPE=C

Versions of packages dpkg depends on:
ii  dselect                     1.10.20      a user tool to manage Debian packa
ii  libc6                       2.3.2.ds1-17 GNU C Library: Shared libraries an

-- no debconf information
diff -x configure -x config.h.in -x 'config.h.in~' -Naur ./dpkg-1.10.27/configure.in ./dpkg-1.10.27.cc/configure.in
--- ./dpkg-1.10.27/configure.in	2005-02-10 16:25:43.000000000 +0100
+++ ./dpkg-1.10.27.cc/configure.in	2005-03-30 00:40:37.000000000 +0200
@@ -175,6 +175,8 @@
 AC_CHECK_FUNCS(vsnprintf lchown snprintf)
 AC_CHECK_HEADERS(sys/cdefs.h syslog.h stddef.h)
 AC_CHECK_HEADERS(error.h locale.h)
+AC_CHECK_FUNCS(setrlimit getrlimit)
+AC_CHECK_HEADERS(sys/time.h sys/resource.h unistd.h)
 AC_DECL_SYS_SIGLIST
 AC_CHECK_LIB(ihash, ihash_create, SSD_LIBS="-lihash $SSD_LIBS")
 AC_CHECK_LIB(ps, proc_stat_list_create, SSD_LIBS="-lps $SSD_LIBS")
diff -x configure -x config.h.in -x 'config.h.in~' -Naur ./dpkg-1.10.27/utils/start-stop-daemon.c ./dpkg-1.10.27.cc/utils/start-stop-daemon.c
--- ./dpkg-1.10.27/utils/start-stop-daemon.c	2005-02-10 16:24:18.000000000 +0100
+++ ./dpkg-1.10.27.cc/utils/start-stop-daemon.c	2005-03-30 03:03:42.000000000 +0200
@@ -90,6 +90,21 @@
 #  include <error.h>
 #endif
 
+  /* setrlimit */
+#ifndef SSD_SETRLIMIT_PATH 
+#  define SSD_SETRLIMIT_PATH "/etc/limits"
+#endif
+
+#if defined(HAVE_SYS_TIME_H) && defined(HAVE_SYS_RESOURCE_H) && \
+    defined(HAVE_UNISTD_H) && defined(HAVE_SETRLIMIT) && defined(HAVE_GETRLIMIT)
+
+#  define SSD_SETRLIMIT 1
+
+#  include <sys/time.h>
+#  include <sys/resource.h>
+#endif
+
+
 static int testmode = 0;
 static int quietmode = 0;
 static int exitnodo = 1;
@@ -943,6 +958,272 @@
 }
 #endif /* OShpux */
 
+#ifdef SSD_SETRLIMIT
+static int
+do_getlimit(FILE * f, char ch, int * resource, const char ** rname) {
+  switch(ch) {
+    default:
+      break;
+
+      /* as */ 
+    case 'a':
+      if(fgetc(f) == 's') {
+	*resource=RLIMIT_AS;
+	*rname="as";
+	return 1; 
+      }
+      break;
+
+    case 'c':
+      /* core */ 
+      if((ch=fgetc(f)) == 'o' && fgetc(f) == 'r' && fgetc(f) == 'e') {
+	*resource=RLIMIT_CORE;
+	*rname="core";
+	return 1;
+      } 
+
+      /* cpu */
+      if(ch == 'p' && fgetc(f) == 'u') {
+	*resource=RLIMIT_CPU;
+	*rname="cpu";
+	return 1;
+      }
+      break;
+
+      /* data */ 
+    case 'd':
+      if(fgetc(f) == 'a' && fgetc(f) == 't' && fgetc(f) == 'a') {
+	*resource=RLIMIT_DATA;
+	*rname="data";
+	return 1;
+      }
+      break;
+
+    case 'f':
+      if(fgetc(f) == 's' && fgetc(f) == 'i' && fgetc(f) == 'z' && fgetc(f) == 'e') {
+	*resource=RLIMIT_FSIZE;
+	*rname="fsize";
+	return 1;
+      }
+      break;
+
+      /* locks */ 
+    case 'l':
+      if(fgetc(f) == 'o' && fgetc(f) == 'c' && fgetc(f) == 'k' && fgetc(f) == 's') {
+	*resource=RLIMIT_LOCKS;
+	*rname="locks";
+	return 1;
+      }
+      break;
+
+      /* memlock */ 
+    case 'm':
+      if(fgetc(f) == 'e' && fgetc(f) == 'm' && fgetc(f) == 'l' && 
+	 fgetc(f) == 'o' && fgetc(f) == 'c' && fgetc(f) == 'k') {
+	*resource=RLIMIT_MEMLOCK;
+	*rname="memlock";
+	return 1;
+      }
+      break;
+
+      /* nofile */ 
+    case 'n':
+      if((ch=fgetc(f)) == 'o' && fgetc(f) == 'f' && fgetc(f) == 'i' && 
+	 fgetc(f) == 'l' && fgetc(f) == 'e') {
+#if defined(OSOpenBSD) || defined(OSFreeBSD) || defined(OSNetBSD)
+	*resource=RLIMIT_OFILE;
+#else
+	*resource=RLIMIT_NOFILE;
+#endif
+
+	*rname="nofile";
+	return 1;
+      }
+
+      /* nproc */ 
+      if(ch == 'p' && fgetc(f) == 'r' && fgetc(f) == 'o' && fgetc(f) == 'c') {
+	*resource=RLIMIT_NPROC;
+	*rname="nproc";
+	return 1;
+      }
+      break;
+
+      /* rss */
+    case 'r':
+      if(fgetc(f) == 's' && fgetc(f) == 's') {
+	*resource=RLIMIT_RSS;
+	*rname="rss";
+	return 1;
+      }
+      break;
+
+      /* stack */ 
+    case 's':
+      if(fgetc(f) == 't' && fgetc(f) == 'a' &&
+	 fgetc(f) == 'c' && fgetc(f) == 'k') {
+	*resource=RLIMIT_STACK;
+	*rname="stack";
+	return 1;
+      }
+      break;
+  }
+
+  return 0;
+}
+
+
+static void
+do_loadlimits(void) 
+{
+  char * look=execname;
+
+  FILE * f;
+  char * name;
+  char * path;
+
+  char * lname;
+  struct rlimit limit;
+  rlim_t *lvalue;
+  unsigned long value;
+  int resource;
+
+
+  int line=1;
+  int ch;
+
+    /* Return immediately if we have nothing
+     * to run */
+  if(!look) {
+    if(!startas)
+      return;
+
+    look=startas;
+  }
+
+    /* Calculate name of configuration file */
+  name=strrchr(look, '/');
+  if(name)
+    name=name+1;
+  else
+    name=look;
+
+    /* Calculate name of the file to be read */
+  path=xmalloc(sizeof(SSD_SETRLIMIT_PATH)+strlen(name)+1);
+  sprintf(path, SSD_SETRLIMIT_PATH "/%s", name);
+
+    /* name of file/path to open */ 
+  f=fopen(path, "r");
+  if(!f) {
+    if(errno == ENOENT) {
+      if(quietmode < 0)
+        printf("%s: not loading limits -- missing file '%s'\n", name, path);
+      free(path);
+      return;
+    }
+
+    if(quietmode <= 0)
+      printf("%s: couldn't open limits file -- %s\n", name, strerror(errno)); 
+    free(path);
+    return;
+  }
+
+    /* Ok, file has been opened, read limits */
+  while(1) {
+
+      /* Skip blanks */
+    while((ch=fgetc(f)) != EOF && (ch == '\t' || ch == ' ' || ch == '\n'))  {
+      if(ch == '\n')
+	line++;
+    }
+      
+      /* Check if we found a comment */
+    if(ch == '#') {
+      while((ch=fgetc(f)) != EOF && ch != '\n')
+	;
+
+      if(ch == '\n')
+	line++;
+
+      continue;
+    }
+
+      /* Verify we didn't reach EOF */
+    if(ch == EOF) {
+      free(path);
+      return;
+    }
+
+      /* Get limit type */
+    if(do_getlimit(f, ch, &resource, (const char **)&lname)) {
+        /* Skip any blank */
+      while((ch=fgetc(f)) != EOF && (ch == '\t' || ch == ' ')) 
+	;
+
+        /* Set limit kind */
+      lvalue=NULL;
+      if(ch == 's' && fgetc(f) == 'o' && fgetc(f) == 'f' && fgetc(f) == 't')
+	lvalue=&(limit.rlim_cur);
+      else
+	if(ch == 'h' && fgetc(f) == 'a' && fgetc(f) == 'r' && fgetc(f) == 'd')
+	  lvalue=&(limit.rlim_max);
+
+        /* If we found some useful line value */
+      if(lvalue) {
+	while((ch=fgetc(f)) != EOF && (ch == ' ' || ch == '\t'))
+	  ;
+
+	  /* Try to get limit */
+	ungetc(ch, f);
+	if(fscanf(f, "%lu", &value) == 1) {
+	  if(!getrlimit(resource, &limit) == -1) {
+	    if(quietmode <= 0)
+	      printf("%s: getrlimit failed for %s (%s:%d): %s\n", name, lname, path, line, strerror(errno));
+	    continue;
+	  }
+
+	    /* Verify SOFT limit is <= of the HARD limit */
+	  *lvalue=(rlim_t)value;
+	  if(lvalue == &limit.rlim_max && limit.rlim_cur > limit.rlim_max) {
+	    if(quietmode < 0)
+	      printf("%s: SOFT LIMIT was > than HARD LIMIT -- decreased to HARD LIMIT value on %s:%d (%s)\n",
+		   name, path, line, lname);
+
+	    limit.rlim_cur=limit.rlim_max;
+	  }
+
+	      /* Skip Anything else until end of line */
+    	  while((ch=fgetc(f)) != EOF && ch != '\n')
+      	    ;
+	  line++;
+
+	  if(!setrlimit(resource, &limit) == -1) {
+	    if(quietmode <= 0)
+	      printf("%s: setrlimit failed for %s (%s:%d): %s\n", name, lname, path, line-1, strerror(errno));
+	    continue;
+	  } 
+
+	  if(quietmode < 0)
+	    printf("%s: setrlimit %s:%d: %s %s=%lu\n", name, path, line-1, lname, (lvalue == &(limit.rlim_cur) ? 
+		   "soft" : "hard"), value); 
+	  continue;
+	}
+      }
+    }
+
+    /* Something wrong into configuration file */
+    if(quietmode <= 0)
+      printf("%s: parse error in limits file on %s:%d\n", name, path, line);
+
+      /* Skip line */
+    while((ch=fgetc(f)) != EOF && ch != '\n')
+      ;
+    ungetc(ch, f);
+  }
+
+    /* should never be reached */
+  return;
+}
+#endif
 
 static void
 do_findprocs(void)
@@ -1191,6 +1472,9 @@
 		exit(i);
 	}
 
+#ifdef SSD_SETRLIMIT 
+	do_loadlimits();
+#endif
 	do_findprocs();
 
 	if (found) {

Reply to: