about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2004-06-30 11:10:35 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2004-06-30 11:10:35 +0000
commitbf214787228dc097a268ee4207d87bf3bf09d2b3 (patch)
tree54d163bb7da1691d46f2602a39c804efa326bf13
parent91aeae74f2d9b6bec5187794dafbae22a675669d (diff)
downloadzsh-bf214787228dc097a268ee4207d87bf3bf09d2b3.tar.gz
zsh-bf214787228dc097a268ee4207d87bf3bf09d2b3.tar.xz
zsh-bf214787228dc097a268ee4207d87bf3bf09d2b3.zip
20118: improvements to limits builtins
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/builtins.yo30
-rw-r--r--Src/Builtins/rlimits.c482
3 files changed, 362 insertions, 156 deletions
diff --git a/ChangeLog b/ChangeLog
index 80d5861bf..c6ffe191e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2004-06-30  Peter Stephenson  <pws@csr.com>
+
+	* 20118: Doc/Zsh/builtins.yo, Src/Builtins/rlimits.c:
+	Attempt to handle unknown limits by number.  Also improve
+	output and collect common code.
+
 2004-06-28  Peter Stephenson  <pws@csr.com>
 
 	* 20112, changed as per 20113: Src/exec.c, Src/parse.c,
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 4325465ca..5d8f86f29 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -649,6 +649,10 @@ set the limit to the specified value.  If the tt(-h) flag
 is given, use hard limits instead of soft limits.
 If no var(resource) is given, print all limits.
 
+When looping over multiple resources, the shell will abort immediately if
+it detects a badly formed argument.  However, if it fails to set a limit
+for some other reason it will continue trying to set the remaining limits.
+
 var(resource) can be one of:
 
 startsitem()
@@ -672,7 +676,15 @@ sitem(tt(vmemorysize))(Maximum amount of virtual memory.)
 endsitem()
 
 Which of these resource limits are available depends on the system.
-var(resource) can be abbreviated to any unambiguous prefix.
+var(resource) can be abbreviated to any unambiguous prefix.  It
+can also be an integer, which corresponds to the integer defined
+for the resource by the operating system.
+
+If argument corresponds to a number which is out of the range of the
+resources configured into the shell, the shell will try to read or write
+the limit anyway, and will report an error if this fails.  As the shell
+does not store such resources internally, an attempt to set the limit will
+fail unless the tt(-s) option is present.
 
 var(limit) is a number, with an optional scaling factor, as follows:
 
@@ -1403,7 +1415,7 @@ enditem()
 findex(ulimit)
 cindex(resource limits)
 cindex(limits, resource)
-item(tt(ulimit) [ tt(-SHacdflmnpstv) [ var(limit) ] ... ])(
+item(tt(ulimit) [ [ tt(-SHacdflmnpstv) | tt(-N) var(resource) [ var(limit) ] ... ])(
 Set or display resource limits of the shell and the processes started by
 the shell.  The value of var(limit) can be a number in the unit specified
 below or the value `tt(unlimited)'.  By default, only soft limits are
@@ -1415,6 +1427,10 @@ var(limit) is omitted the current value of the specified resources are
 printed.  When more than one resource values are printed the limit name and
 unit is printed before each value.
 
+When looping over multiple resources, the shell will abort immediately if
+it detects a badly formed argument.  However, if it fails to set a limit
+for some other reson it will continue trying to set the remaining limits.
+
 startsitem()
 sitem(tt(-a))(Lists all of the current resource limits.)
 sitem(tt(-c))(512-byte blocks on the size of core dumps.)
@@ -1429,6 +1445,16 @@ sitem(tt(-u))(processes available to the user.)
 sitem(tt(-v))(K-bytes on the size of virtual memory.  On some systems this
 refers to the limit called `address space'.)
 endsitem()
+
+A resource may also be specified by integer in the form `tt(-N)
+var(resource)', where var(resource) corresponds to the integer defined for
+the resource by the operating system.  This may be used to set the limits
+for resources known to the shell which do not correspond to option letters.
+Such limits will be shown by number in the output of `tt(ulimit -a)'.
+
+The number may alternatively be out of the range of limits compiled into
+the shell.  The shell will try to read or write the limit anyway, and
+will report an error if this fails.
 )
 findex(umask)
 cindex(umask)
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index ff66d9594..cabf7975b 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -78,57 +78,108 @@ zstrtorlimt(const char *s, char **t, int base)
     return ret;
 }
 
-/* Display resource limits.  hard indicates whether `hard' or `soft'  *
- * limits should be displayed.  lim specifies the limit, or may be -1 *
- * to show all.                                                       */
-
 /**/
 static void
-showlimits(int hard, int lim)
+showlimitvalue(int lim, rlim_t val)
 {
-    int rt;
-    rlim_t val;
-
-    /* main loop over resource types */
-    for (rt = 0; rt != ZSH_NLIMITS; rt++)
-	if (rt == lim || lim == -1) {
-	    /* display limit for resource number rt */
-	    printf("%-16s", recs[rt]);
-	    val = (hard) ? limits[rt].rlim_max : limits[rt].rlim_cur;
-	    if (val == RLIM_INFINITY)
-		printf("unlimited\n");
-	    else if (limtype[rt] == ZLIMTYPE_TIME) {
-		/* time-type resource -- display as hours, minutes and
-		seconds. */
-		printf("%d:%02d:%02d\n", (int)(val / 3600),
-		       (int)(val / 60) % 60, (int)(val % 60));
-	    } else if (limtype[rt] == ZLIMTYPE_NUMBER || limtype[rt] == ZLIMTYPE_UNKNOWN) {
-		/* pure numeric resource */
-		printf("%d\n", (int)val);
-	    } else if (val >= 1024L * 1024L)
-		/* memory resource -- display with `K' or `M' modifier */
+    /* display limit for resource number lim */
+    if (lim < ZSH_NLIMITS)
+	printf("%-16s", recs[lim]);
+    else
+    {
+	/* Unknown limit, hence unknown units. */
+	printf("%-16d", lim);
+    }
+    if (val == RLIM_INFINITY)
+	printf("unlimited\n");
+    else if (lim >= ZSH_NLIMITS)
+    {
 # ifdef RLIM_T_IS_QUAD_T
-		printf("%qdMB\n", val / (1024L * 1024L));
-	    else
-		printf("%qdkB\n", val / 1024L);
+	printf("%qd\n", val);
+# else
+#  ifdef RLIM_T_IS_LONG_LONG
+	printf("%lld\n", val);
+#  else
+#   ifdef RLIM_T_IS_UNSIGNED
+	printf("%lu\n", val);
+#   else
+	printf("%ld\n", val);
+#   endif /* RLIM_T_IS_UNSIGNED */
+#  endif /* RLIM_T_IS_LONG_LONG */
+# endif /* RLIM_T_IS_QUAD_T */
+    }
+    else if (limtype[lim] == ZLIMTYPE_TIME) {
+	/* time-type resource -- display as hours, minutes and
+	   seconds. */
+	printf("%d:%02d:%02d\n", (int)(val / 3600),
+	       (int)(val / 60) % 60, (int)(val % 60));
+    } else if (limtype[lim] == ZLIMTYPE_NUMBER ||
+	       limtype[lim] == ZLIMTYPE_UNKNOWN) {
+	/* pure numeric resource */
+	printf("%d\n", (int)val);
+    } else if (val >= 1024L * 1024L)
+	/* memory resource -- display with `K' or `M' modifier */
+# ifdef RLIM_T_IS_QUAD_T
+	printf("%qdMB\n", val / (1024L * 1024L));
+    else
+	printf("%qdkB\n", val / 1024L);
 # else
 #  ifdef RLIM_T_IS_LONG_LONG
-		printf("%lldMB\n", val / (1024L * 1024L));
-            else
-		printf("%lldkB\n", val / 1024L);
+    printf("%lldMB\n", val / (1024L * 1024L));
+    else
+	printf("%lldkB\n", val / 1024L);
 #  else
 #   ifdef RLIM_T_IS_UNSIGNED
-		printf("%luMB\n", val / (1024L * 1024L));
-            else
-		printf("%lukB\n", val / 1024L);
+    printf("%luMB\n", val / (1024L * 1024L));
+    else
+	printf("%lukB\n", val / 1024L);
 #   else
-		printf("%ldMB\n", val / (1024L * 1024L));
-            else
-		printf("%ldkB\n", val / 1024L);
+    printf("%ldMB\n", val / (1024L * 1024L));
+    else
+	printf("%ldkB\n", val / 1024L);
 #   endif /* RLIM_T_IS_UNSIGNED */
 #  endif /* RLIM_T_IS_LONG_LONG */
 # endif /* RLIM_T_IS_QUAD_T */
+}
+
+/* Display resource limits.  hard indicates whether `hard' or `soft'  *
+ * limits should be displayed.  lim specifies the limit, or may be -1 *
+ * to show all.                                                       */
+
+/**/
+static int
+showlimits(char *nam, int hard, int lim)
+{
+    int rt;
+
+    if (lim >= ZSH_NLIMITS)
+    {
+	/*
+	 * Not configured into the shell.  Ask the OS
+	 * explicitly for this limit.
+	 */
+	struct rlimit vals;
+	if (getrlimit(lim, &vals) < 0)
+	{
+	    zwarnnam(nam, "can't read limit: %e", NULL, errno);
+	    return 1;
 	}
+	showlimitvalue(lim, hard ? vals.rlim_max : vals.rlim_cur);
+    }
+    else if (lim != -1)
+    {
+	showlimitvalue(lim, hard ? limits[lim].rlim_max :
+		       limits[lim].rlim_cur);
+    }
+    else
+    {
+	/* main loop over resource types */
+	for (rt = 0; rt != ZSH_NLIMITS; rt++)
+	    showlimitvalue(rt, (hard) ? limits[rt].rlim_max :
+			   limits[rt].rlim_cur);
+    }
+
+    return 0;
 }
 
 /* Display a resource limit, in ulimit style.  lim specifies which   *
@@ -136,40 +187,52 @@ showlimits(int hard, int lim)
  * soft limit should be displayed.                                   */
 
 /**/
-static void
-printulimit(int lim, int hard, int head)
+static int
+printulimit(char *nam, int lim, int hard, int head)
 {
     rlim_t limit;
 
     /* get the limit in question */
-    limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
+    if (lim >= ZSH_NLIMITS)
+    {
+	struct rlimit vals;
+
+	if (getrlimit(lim, &vals) < 0)
+	{
+	    zwarnnam(nam, "can't read limit: %e", NULL, errno);
+	    return 1;
+	}
+	limit = (hard) ? vals.rlim_max : vals.rlim_cur;
+    }
+    else
+	limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
     /* display the appropriate heading */
     switch (lim) {
     case RLIMIT_CPU:
 	if (head)
-	    printf("cpu time (seconds)         ");
+	    printf("-t: cpu time (seconds)         ");
 	break;
     case RLIMIT_FSIZE:
 	if (head)
-	    printf("file size (blocks)         ");
+	    printf("-f: file size (blocks)         ");
 	if (limit != RLIM_INFINITY)
 	    limit /= 512;
 	break;
     case RLIMIT_DATA:
 	if (head)
-	    printf("data seg size (kbytes)     ");
+	    printf("-d: data seg size (kbytes)     ");
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
 	break;
     case RLIMIT_STACK:
 	if (head)
-	    printf("stack size (kbytes)        ");
+	    printf("-s: stack size (kbytes)        ");
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
 	break;
     case RLIMIT_CORE:
 	if (head)
-	    printf("core file size (blocks)    ");
+	    printf("-c: core file size (blocks)    ");
 	if (limit != RLIM_INFINITY)
 	    limit /= 512;
 	break;
@@ -178,7 +241,7 @@ printulimit(int lim, int hard, int head)
 # if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS)
     case RLIMIT_RSS:
 	if (head)
-	    printf("resident set size (kbytes) ");
+	    printf("-m: resident set size (kbytes) ");
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
 	break;
@@ -186,7 +249,7 @@ printulimit(int lim, int hard, int head)
 # ifdef HAVE_RLIMIT_MEMLOCK
     case RLIMIT_MEMLOCK:
 	if (head)
-	    printf("locked-in-memory size (kb) ");
+	    printf("-l: locked-in-memory size (kb) ");
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
 	break;
@@ -194,22 +257,22 @@ printulimit(int lim, int hard, int head)
 # ifdef HAVE_RLIMIT_NPROC
     case RLIMIT_NPROC:
 	if (head)
-	    printf("processes                  ");
+	    printf("-u: processes                  ");
 	break;
 # endif /* HAVE_RLIMIT_NPROC */
 # ifdef HAVE_RLIMIT_NOFILE
     case RLIMIT_NOFILE:
 	if (head)
-	    printf("file descriptors           ");
+	    printf("-n: file descriptors           ");
 	break;
 # endif /* HAVE_RLIMIT_NOFILE */
 # ifdef HAVE_RLIMIT_VMEM
     case RLIMIT_VMEM:
 	if (head)
 #  if defined(HAVE_RLIMIT_RSS) && defined(RLIMIT_VMEM_IS_RSS)
-	    printf("memory size (kb)           ");
+	    printf("-m: memory size (kb)           ");
 #  else
-	    printf("virtual memory size (kb)   ");
+	    printf("-v: virtual memory size (kb)   ");
 #  endif
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
@@ -218,7 +281,7 @@ printulimit(int lim, int hard, int head)
 # if defined HAVE_RLIMIT_AS && !defined(RLIMIT_VMEM_IS_AS)
     case RLIMIT_AS:
 	if (head)
-	    printf("address space (kb)         ");
+	    printf("-v: address space (kb)         ");
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
 	break;
@@ -226,19 +289,19 @@ printulimit(int lim, int hard, int head)
 # ifdef HAVE_RLIMIT_TCACHE
     case RLIMIT_TCACHE:
 	if (head)
-	    printf("cached threads             ");
+	    printf("-N %2d: cached threads          ", RLIMIT_TCACHE);
 	break;
 # endif /* HAVE_RLIMIT_TCACHE */
 # ifdef HAVE_RLIMIT_AIO_OPS
     case RLIMIT_AIO_OPS:
 	if (head)
-	    printf("AIO operations             ");
+	    printf("-N %2d: AIO operations          ", RLIMIT_AIO_OPS);
 	break;
 # endif /* HAVE_RLIMIT_AIO_OPS */
 # ifdef HAVE_RLIMIT_AIO_MEM
     case RLIMIT_AIO_MEM:
 	if (head)
-	    printf("AIO locked-in-memory (kb)  ");
+	    printf("-N %2d: AIO locked-in-memory (kb) ", RLIMIT_AIO_MEM);
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
 	break;
@@ -246,7 +309,7 @@ printulimit(int lim, int hard, int head)
 # ifdef HAVE_RLIMIT_SBSIZE
     case RLIMIT_SBSIZE:
 	if (head)
-	    printf("socket buffer size (kb)    ");
+	    printf("-N %2d: socket buffer size (kb) ", RLIMIT_SBSIZE);
 	if (limit != RLIM_INFINITY)
 	    limit /= 1024;
 	break;
@@ -254,15 +317,19 @@ printulimit(int lim, int hard, int head)
 # ifdef HAVE_RLIMIT_PTHREAD
     case RLIMIT_PTHREAD:
 	if (head)
-	    printf("threads per process        ");
+	    printf("-N %2d: threads per process     ", RLIMIT_PTHREAD);
 	break;
 # endif /* HAVE_RLIMIT_PTHREAD */
 # ifdef HAVE_RLIMIT_LOCKS
     case RLIMIT_LOCKS:
 	if (head)
-	    printf("file locks                 ");
+	    printf("-N %2d: file locks              ", RLIMIT_LOCKS);
 	break;
 # endif /* HAVE_RLIMIT_LOCKS */
+    default:
+	if (head)
+	    printf("-N %2d:                         ", lim);
+	break;
     }
     /* display the limit */
     if (limit == RLIM_INFINITY)
@@ -282,6 +349,93 @@ printulimit(int lim, int hard, int head)
 #  endif /* RLIM_T_IS_LONG_LONG */
 # endif /* RLIM_T_IS_QUAD_T */
     }
+
+    return 0;
+}
+
+/**/
+static int
+do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set)
+{
+    if (lim >= ZSH_NLIMITS) {
+	struct rlimit vals;
+	if (getrlimit(lim, &vals) < 0)
+	{
+	    /* best guess about error */
+	    zwarnnam(nam, "can't read limit: %e", NULL, errno);
+	    return 1;
+	}
+	if (hard)
+	{
+	    if (val > vals.rlim_max && geteuid()) {
+		zwarnnam(nam, "can't raise hard limits", NULL, 0);
+		return 1;
+	    }
+	    vals.rlim_max = val;
+	    /*
+	     * not show if all systems will do this silently, but
+	     * best be safe...
+	     */
+	    if (val < vals.rlim_cur)
+		vals.rlim_cur = val;
+	}
+	if (soft || !hard) {
+	    if (val > vals.rlim_max) {
+		zwarnnam(nam, "limit exceeds hard limit", NULL, 0);
+		return 1;
+	    }
+	    else
+		vals.rlim_cur = val;
+	}
+	if (!set)
+	{
+	    zwarnnam(nam,
+		     "warning: unrecognised limit %d, use -s to set",
+		     NULL, lim);
+	    return 1;
+	}
+	else if (setrlimit(lim, &vals) < 0)
+	{
+	    zwarnnam(nam, "setrlimit failed: %e", NULL, errno);
+	    return 1;
+	}
+    } else {
+	/* new limit is valid and has been interpreted; apply it to the
+	specified resource */
+	if (hard) {
+	    /* can only raise hard limits if running as root */
+	    if (val > current_limits[lim].rlim_max && geteuid()) {
+		zwarnnam(nam, "can't raise hard limits", NULL, 0);
+		return 1;
+	    } else {
+		limits[lim].rlim_max = val;
+		if (val < limits[lim].rlim_cur)
+		    limits[lim].rlim_cur = val;
+	    }
+	}
+	if (soft || !hard) {
+	    if (val > limits[lim].rlim_max) {
+		/* no idea about this difference, don't intend to worry */
+		if (*nam == 'u')
+		{
+		    /* ulimit does this */
+		    if (val > current_limits[lim].rlim_max && geteuid()) {
+			zwarnnam(nam, "value exceeds hard limit", NULL, 0);
+			return 1;
+		    }
+		    limits[lim].rlim_max = limits[lim].rlim_cur = val;
+		} else {
+		    /* but limit does this */
+		    zwarnnam(nam, "limit exceeds hard limit", NULL, 0);
+		    return 1;
+		}
+	    } else
+		limits[lim].rlim_cur = val;
+	    if (set && zsetlimit(lim, "limit"))
+		return 1;
+	}
+    }
+    return 0;
 }
 
 /* limit: set or show resource limits.  The variable hard indicates *
@@ -289,7 +443,7 @@ printulimit(int lim, int hard, int head)
 
 /**/
 static int
-bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
+bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 {
     char *s;
     int hard, limnum, lim;
@@ -300,35 +454,46 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
     if (OPT_ISSET(ops,'s') && !*argv)
 	return setlimits(NULL);
     /* without arguments, display limits */
-    if (!*argv) {
-	showlimits(hard, -1);
-	return 0;
-    }
+    if (!*argv)
+	return showlimits(nam, hard, -1);
     while ((s = *argv++)) {
 	/* Search for the appropriate resource name.  When a name matches (i.e. *
 	 * starts with) the argument, the lim variable changes from -1 to the   *
 	 * number of the resource.  If another match is found, lim goes to -2.  */
-	for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
-	    if (!strncmp(recs[limnum], s, strlen(s))) {
-		if (lim != -1)
-		    lim = -2;
-		else
-		    lim = limnum;
-	    }
+	if (idigit(*s))
+	{
+	    lim = (int)zstrtol(s, NULL, 10);
+	}
+	else
+	    for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
+		if (!strncmp(recs[limnum], s, strlen(s))) {
+		    if (lim != -1)
+			lim = -2;
+		    else
+			lim = limnum;
+		}
 	/* lim==-1 indicates that no matches were found.       *
 	 * lim==-2 indicates that multiple matches were found. */
 	if (lim < 0) {
-	    zwarnnam("limit",
+	    zwarnnam(nam,
 		     (lim == -2) ? "ambiguous resource specification: %s"
 		     : "no such resource: %s", s, 0);
 	    return 1;
 	}
 	/* without value for limit, display the current limit */
-	if (!(s = *argv++)) {
-	    showlimits(hard, lim);
-	    return 0;
+	if (!(s = *argv++))
+	    return showlimits(nam, hard, lim);
+	if (lim >= ZSH_NLIMITS)
+	{
+	    val = zstrtorlimt(s, &s, 10);
+	    if (*s)
+	    {
+		/* unknown limit, no idea how to scale */
+		zwarnnam(nam, "unknown scaling factor: %s", s, 0);
+		return 1;
+	    }
 	}
-	if (limtype[lim] == ZLIMTYPE_TIME) {
+	else if (limtype[lim] == ZLIMTYPE_TIME) {
 	    /* time-type resource -- may be specified as seconds, or minutes or *
 	     * hours with the `m' and `h' modifiers, and `:' may be used to add *
 	     * together more than one of these.  It's easier to understand from *
@@ -342,7 +507,7 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
 		else if (*s == ':')
 		    val = val * 60 + zstrtorlimt(s + 1, &s, 10);
 		else {
-		    zwarnnam("limit", "unknown scaling factor: %s", s, 0);
+		    zwarnnam(nam, "unknown scaling factor: %s", s, 0);
 		    return 1;
 		}
 	    }
@@ -352,7 +517,7 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
 	    char *t = s;
 	    val = zstrtorlimt(t, &s, 10);
 	    if (s == t) {
-		zwarnnam("limit", "limit must be a number", NULL, 0);
+		zwarnnam(nam, "limit must be a number", NULL, 0);
 		return 1;
 	    }
 	} else {
@@ -365,31 +530,60 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
 	    } else if ((*s == 'M' || *s == 'm') && !s[1])
 		val *= 1024L * 1024;
 	    else {
-		zwarnnam("limit", "unknown scaling factor: %s", s, 0);
+		zwarnnam(nam, "unknown scaling factor: %s", s, 0);
 		return 1;
 	    }
 	}
-	/* new limit is valid and has been interpreted; apply it to the
-	specified resource */
+	if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's')))
+	    ret++;
+    }
+    return ret;
+}
+
+/**/
+static int
+do_unlimit(char *nam, int lim, int hard, int soft, int set, int euid)
+{
+    /* remove specified limit */
+    if (lim >= ZSH_NLIMITS) {
+	struct rlimit vals;
+	if (getrlimit(lim, &vals) < 0)
+	{
+	    zwarnnam(nam, "can't read limit: %e", NULL, errno);
+	    return 1;
+	}
 	if (hard) {
-	    /* can only raise hard limits if running as root */
-	    if (val > current_limits[lim].rlim_max && geteuid()) {
-		zwarnnam("limit", "can't raise hard limits", NULL, 0);
+	    if (euid && vals.rlim_max != RLIM_INFINITY) {
+		zwarnnam(nam, "can't remove hard limits", NULL, 0);
 		return 1;
-	    } else {
-		limits[lim].rlim_max = val;
-		if (val < limits[lim].rlim_cur)
-		    limits[lim].rlim_cur = val;
-	    }
-	} else if (val > limits[lim].rlim_max) {
-	    zwarnnam("limit", "limit exceeds hard limit", NULL, 0);
+	    } else
+		vals.rlim_max = RLIM_INFINITY;
+	}
+	if (!hard || soft)
+	    vals.rlim_cur = vals.rlim_max;
+	if (!set) {
+	    zwarnnam(nam,
+		     "warning: unrecognised limit %d, use -s to set",
+		     NULL, lim);
+	    return 1;
+	} else if (setrlimit(lim, &vals) < 0) {
+	    zwarnnam(nam, "setrlimit failed: %e", NULL, errno);
+	    return 1;
+	}
+    } else {
+	if (hard) {
+	    if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) {
+		zwarnnam(nam, "can't remove hard limits", NULL, 0);
+		return 1;
+	    } else
+		limits[lim].rlim_max = RLIM_INFINITY;
+	}
+	if (!hard || soft)
+	    limits[lim].rlim_cur = limits[lim].rlim_max;
+	if (set && zsetlimit(lim, nam))
 	    return 1;
-	} else
-	    limits[lim].rlim_cur = val;
-	if (OPT_ISSET(ops,'s') && zsetlimit(lim, "limit"))
-	    ret++;
     }
-    return ret;
+    return 0;
 }
 
 /* unlimit: remove resource limits.  Much of this code is the same as *
@@ -425,13 +619,17 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func))
 	     * matches (i.e. starts with) the argument, the lim variable  *
 	     * changes from -1 to the number of the resource.  If another *
 	     * match is found, lim goes to -2.                            */
-	    for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
-		if (!strncmp(recs[limnum], *argv, strlen(*argv))) {
-		    if (lim != -1)
-			lim = -2;
-		    else
-			lim = limnum;
-		}
+	    if (idigit(**argv)) {
+		lim = (int)zstrtol(*argv, NULL, 10);
+	    } else {
+		for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
+		    if (!strncmp(recs[limnum], *argv, strlen(*argv))) {
+			if (lim != -1)
+			    lim = -2;
+			else
+			    lim = limnum;
+		    }
+	    }
 	    /* lim==-1 indicates that no matches were found.       *
 	     * lim==-2 indicates that multiple matches were found. */
 	    if (lim < 0) {
@@ -440,16 +638,8 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func))
 			 : "no such resource: %s", *argv, 0);
 		return 1;
 	    }
-	    /* remove specified limit */
-	    if (hard) {
-		if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) {
-		    zwarnnam(nam, "can't remove hard limits", NULL, 0);
-		    ret++;
-		} else
-		    limits[lim].rlim_max = RLIM_INFINITY;
-	    } else
-		limits[lim].rlim_cur = limits[lim].rlim_max;
-	    if (OPT_ISSET(ops,'s') && zsetlimit(lim, nam))
+	    else if (do_unlimit(nam, lim, hard, !hard, OPT_ISSET(ops, 's'),
+				euid))
 		ret++;
 	}
     }
@@ -462,7 +652,7 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func))
 static int
 bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 {
-    int res, resmask = 0, hard = 0, soft = 0, nres = 0, all = 0;
+    int res, resmask = 0, hard = 0, soft = 0, nres = 0, all = 0, ret = 0;
     char *options;
 
     do {
@@ -485,6 +675,22 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		case 'S':
 		    soft = 1;
 		    continue;
+		case 'N':
+		    if (options[1]) {
+			res = (int)zstrtol(options+1, NULL, 10);
+		    } else if (*argv) {
+			res = (int)zstrtol(*argv++, NULL, 10);
+		    } else {
+			zwarnnam(name, "number required after -N",
+				 NULL, 0);
+			return 1;
+		    }
+		    /*
+		     * fake it so it looks like we just finished an option...
+		     */
+		    while (options[1])
+			options++;
+		    break;
 		case 'a':
 		    if (resmask) {
 			zwarnnam(name, "no limits allowed with -a",
@@ -606,50 +812,18 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		limit *= 1024;
 		break;
 	    }
-	    if (hard) {
-		/* can't raise hard limit unless running as root */
-		if (limit > current_limits[res].rlim_max && geteuid()) {
-		    zwarnnam(name, "can't raise hard limits", NULL, 0);
-		    return 1;
-		}
-		limits[res].rlim_max = limit;
-		if (limit < limits[res].rlim_cur)
-		    limits[res].rlim_cur = limit;
-	    }
-	    if (!hard || soft) {
-		/* can't raise soft limit above hard limit */
-		if (limit > limits[res].rlim_max) {
-		    if (limit > current_limits[res].rlim_max && geteuid()) {
-			zwarnnam(name, "value exceeds hard limit", NULL, 0);
-			return 1;
-		    }
-		    limits[res].rlim_max = limits[res].rlim_cur = limit;
-		} else
-		    limits[res].rlim_cur = limit;
-	    }
+	    if (do_limit(name, res, limit, hard, soft, 1))
+		ret++;
 	} else {
-	    /* remove specified limit */
-	    if (hard) {
-		/* can't remove hard limit unless running as root */
-		if (current_limits[res].rlim_max != RLIM_INFINITY && geteuid()) {
-		    zwarnnam(name, "can't remove hard limits", NULL, 0);
-		    return 1;
-		}
-		limits[res].rlim_max = RLIM_INFINITY;
-	    }
-	    if (!hard || soft)
-		/* `removal' of soft limit means setting it equal to the
-		   corresponding hard limit */
-		limits[res].rlim_cur = limits[res].rlim_max;
+	    if (do_unlimit(name, res, hard, soft, 1, geteuid()))
+		ret++;
 	}
-	if (zsetlimit(res, name))
-	    return 1;
 	argv++;
     } while (*argv);
-    for (res = 0; res < RLIM_NLIMITS; res++, resmask >>= 1)
-	if (resmask & 1)
-	    printulimit(res, hard, nres > 1);
-    return 0;
+    for (res = 0; resmask; res++, resmask >>= 1)
+	if ((resmask & 1) && printulimit(name, res, hard, nres > 1))
+	    ret++;
+    return ret;
 }
 
 #else /* !HAVE_GETRLIMIT || !RLIM_INFINITY */