diff options
author | davidn <davidn@FreeBSD.org> | 1997-01-21 15:05:15 +0000 |
---|---|---|
committer | davidn <davidn@FreeBSD.org> | 1997-01-21 15:05:15 +0000 |
commit | 2fcbfa8ff9f529d5a6e57a21fb4d8378e3cb895b (patch) | |
tree | d66c822c824894a58636d32d8bcef0033ab5f2be /usr.bin/limits | |
parent | 7b15aed0158133c1b601c7a1ddc9136c04393555 (diff) | |
download | FreeBSD-src-2fcbfa8ff9f529d5a6e57a21fb4d8378e3cb895b.zip FreeBSD-src-2fcbfa8ff9f529d5a6e57a21fb4d8378e3cb895b.tar.gz |
limits(1): set and display process resources.
Diffstat (limited to 'usr.bin/limits')
-rw-r--r-- | usr.bin/limits/Makefile | 13 | ||||
-rw-r--r-- | usr.bin/limits/limits.1 | 304 | ||||
-rw-r--r-- | usr.bin/limits/limits.c | 637 |
3 files changed, 954 insertions, 0 deletions
diff --git a/usr.bin/limits/Makefile b/usr.bin/limits/Makefile new file mode 100644 index 0000000..885930f --- /dev/null +++ b/usr.bin/limits/Makefile @@ -0,0 +1,13 @@ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 + +PROG= limits +SRCS= limits.c + +CFLAGS+=-Wall +LDADD+= -lutil +DPADD+= ${LIBUTIL} + +BINOWN= root +BINMODE=0555 + +.include <bsd.prog.mk> diff --git a/usr.bin/limits/limits.1 b/usr.bin/limits/limits.1 new file mode 100644 index 0000000..ebcc7b5 --- /dev/null +++ b/usr.bin/limits/limits.1 @@ -0,0 +1,304 @@ +.\" Copyright (c) 1996 David Nugent <davidn@blaze.net.au> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, is permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice immediately at the beginning of the file, without modification, +.\" this list of conditions, and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. This work was done expressly for inclusion into FreeBSD. Other use +.\" is permitted provided this notation is included. +.\" 4. Absolutely no warranty of function or purpose is made by the author +.\" David Nugent. +.\" 5. Modifications may be freely made to this file providing the above +.\" conditions are met. +.\" +.\" $FreeBSD$ +.\" +.Dd January 15, 1996 +.Dt LIMITS 1 +.Os FreeBSD +.Sh NAME +.Nm limits +.Nd Set or display process resource limits +.Sh SYNOPSIS +.Nm limits +.Op Fl C Ar class | Fl U Ar user +.Op Fl SHB +.Op Fl e +.Op Fl cdflmnstu Op val +.Nm limits +.Op Fl C Ar class | Fl U Ar user +.Op Fl SHB +.Op Fl cdflmnstu Op val +.Op Fl E +.Op Ar name=value ... +.Op Ar command +.Sh DESCRIPTION +.Nm Limits +ether prints or sets kernel resource limits, and may optionally set +environment variables like +.Xr env 1 +and run a program with the selected resources. +Three uses of the +.Nm limits +command are possible: +.Pp +.Bl -hang -width indent +.It Nm limits Op Ar limitflags +.Op Ar name=value +.Ar command +.Pp +This usage sets limits according to +.Ar limitflags , +optionally sets environment variables given as +.Ar name=value +pairs, and then runs the specified command. +.It Nm limits Op Ar limitflags +.Pp +This usage determines values of resource settings according to +.Ar limitflags , +does not attempt to set them and outputs these values to +standard output. +By default, this will output the current kernel resource settings +active for the calling process. +Using the +.Fl C Ar class +or +.Fl U Ar user +flags, you may also display the current resource settings modified +by the the appropriate login class resource limit entries from +the +.Xr login.conf 5 +login capabilities database. +.It Nm limits Fl e Op Ar limitflags +.Pp +This usage determines values of resource settings according to +.Ar limitflags , +but does not set them itself. +Like the previous usage it outputs these values to standard +output, except that it will emit them in +.Em eval +format, suitable for the calling shell. +The calling shell is determined by examining the entries in the +.Pa /proc +filesystem for the parent process. +If the shell is known (ie. it is one of sh, csh, bash, tcsh, ksh, +pdksh or rc), +.Nm limits +emits 'limit' or 'ulimit' commands in the format understood by +that shell. +If the name of the shell cannot be determined, then the 'ulimit' +format used by +.Pa /bin/sh +is used. +.Pp +This is very useful for setting limits used by scripts, or prior +launching of daemons and other background tasks with specific +resource limit settings, and provides the benefit of allowing +global configuration of maximum resource usage by maintaining a +central database of settings in the login class database. +.Pp +Within a shell script, +.Nm limits +will normally be used with eval within backticks as follows: +.Pp +.Dl eval `limits -e -C daemon` +.Pp +which causes the output of +.Nm limits +to be evaluated and set by the current shell. +.El +.Pp +The value of limitflags specified in the above contains one or more of the +following options: +.Pp +.Bl -tag -width "-d [limit]" +.It Fl C Ar class +Use current resource values, modified by the resource entries applicable +for the login class "class". +.It Fl U Ar user +Use current resource values, modified by the resource entries applicable +to the login class which "user" belongs to. +If the user does not belong to a class, then the resource capabilities +for the "default" class are used, if it exists, or the "root" class if +the user is a superuser account. +.It Fl S +Selects display or setting of "soft" (or current) resource limits. +If specific limits settings follow this switch, only soft limits are +affected unless overridden later with either the +.Fl H +or +.Fl B +flags. +.It Fl H +Selects display or setting of "hard" (or maximum) resource limits. +If specific limits settings follow this switch, only hard limits are +affected until overridden later with either the +.Fl S +or +.Fl B +flags. +.It Fl B +Selects display or setting of both "soft" (current) or "hard" (maximum) +resource limits. +If specific limits settings follow this switch, both soft and hard +limits are affected until overridden later with either the +.Fl S +or +.Fl H +flags. +.Fl e +Selects "eval mode" formatting for output. +This is valid only on display mode and cannot be used when running a +command. +The exact syntax used for output depeneds upon the type of shell from +which +.Nm limits +is invoked. +.It Fl c Op Ar limit +Selects or sets (if 'limit' is specified) the +.Em coredumsize +resource limit. +A value of 0 disables core dumps. +.It Fl d Op Ar limit +Selects or sets (if 'limit' is specified) the +.Em datasize +resource limit. +.It Fl f Op Ar limit +Selects or sets the +.Em filesize +resource limit. +.It Fl l Op Ar limit +Selects or sets the +.Em memorylocked +resource limit. +.It Fl m Op Ar limit +Selects or sets the +.Em memoryuse +size limit +.It Fl n Op Ar limit +Selects or sets the +.Em openfiles +resource limit. +.It Fl s Op Ar limit +Selects or sets the +.Em stacksize +resource limit. +.It Fl t Op Ar limit +Selects or sets the +.Em cputime +resource limit. +.It Fl u Op Ar limit +Selects or sets the +.Em maxproc +resource limit. +.Pp +Valid values for 'limit' in the above set of flags consist of either the +string 'infinity' or 'inf' for an infinite (or kernel-defined maximum) +limit, or a numeric value maybe followed by a suffix. +Values which relate to size default to a value in bytes, or one of the +following suffixes may be used as a multiplier: +.Pp +.Bl -tag -offset indent -width "xxxx" -compact +.It b +512 byte blocks. +.It k +kilobytes (1024 bytes). +.It m +megabytes (1024*1024 bytes). +.It g +gigabytes. +.It t +terrabytes. +.El +.Pp +The +.Em cputime +resource defaults to a number of seconds, but a multiplier may be +used, and as with size values, multiple values separated by a valid +suffix are added together: +.Bl -tag -offset indent -width "xxxx" -compact +.It s +seconds. +.It m +minutes. +.It h +hours. +.It d +days. +.It w +weeks. +.It y +365 day years. +.El +.Pp +.It Fl E +The option +.Sq Fl E +causes +.Nm limits +to completely ignore the environment it inherits. +.It Fl a +This option forces all resource settings to be displayed even if +other specific resource settings have been specified. +For example, if you wish to disable core dumps when starting up +the usenet news system, but wish to set all other resource settings +as well that apply to the 'news' account, you might use: +.Pp +.Dl eval `limits -U news -aBec 0` +.Pp +As with the +.Xr setrlimit 3 +call, only the superuser may raise process "hard" resource limits. +Non-root users may, however, lower them or change "soft" resource limits +within to any value below the hard limit. +When invoked to execute a program, the failure of +.Nm limits +to raise a hard limit is considered a fatal error. +.El +.Sh DIAGNOSTICS +.Nm Limits +exits with EXIT_FAILURE if usage is incorrect in any way; ie. an invalid +option, or set/display options are selected in the same invocation, +.Fl e +is used when running a program, etc. +When run in display or eval mode, +.Nm limits +exits with with a status of EXIT_SUCCESS. +When run in command mode and execution of the command succeeds, the exit status +will be whatever the executed program returns. +.Sh SEE ALSO +.Xr csh 1 , +.Xr env 1 , +.Xr limit 1 , +.Xr sh 1 , +.Xr ulimit 1 , +.Xr getrlimit 3 , +.Xr setrlimit 3 , +.Xr login_cap 3 , +.Xr login.conf 5 +.Sh BUGS +.Nm Limits +does not handle commands with equal (``='') signs in their +names, for obvious reasons. +.Pp +When eval output is selected, the /proc filesystem must be installed +and mounted for the shell to be correctly determined, and therefore +output syntax correct for the running shell. +The default output is valid for /bin/sh, so this means that any +usage of +.Nm limits +in eval mode prior mounting /proc may only occur in standard bourne +shell scripts. +.Pp +.Nm Limits +makes no effort to ensure that resource settings emitted or displayed +are valid and settable by the current user. +Only a superuser account may raise hard limits, and and when doing so +the FreeBSD kernel will silently lower limits to values less than +specified if the values given are too high. diff --git a/usr.bin/limits/limits.c b/usr.bin/limits/limits.c new file mode 100644 index 0000000..302da3a --- /dev/null +++ b/usr.bin/limits/limits.c @@ -0,0 +1,637 @@ +/*- + * Copyright (c) 1997 by + * David L. Nugent <davidn@blaze.net.au> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. This work was done expressly for inclusion into FreeBSD. Other use + * is permitted provided this notation is included. + * 4. Absolutely no warranty of function or purpose is made by the authors. + * 5. Modifications may be freely made to this file providing the above + * conditions are met. + * + * Display/change(+runprogram)/eval resource limits. + * + * $FreeBSD$ + */ + +#include <err.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> +#include <pwd.h> +#include <login_cap.h> +#include <sys/time.h> +#include <sys/resource.h> + +enum +{ + SH_NONE, + SH_SH, /* sh */ + SH_CSH, /* csh */ + SH_BASH, /* gnu bash */ + SH_TCSH, /* tcsh */ + SH_KSH, /* (pd)ksh */ + SH_ZSH, /* zsh */ + SH_RC, /* rc or es */ + SH_NUMBER +}; + + +/* eval emitter for popular shells. + * Why aren't there any standards here? Most shells support either + * the csh 'limit' or sh 'ulimit' command, but each varies just + * enough that they aren't very compatible from one to the other. + */ +static struct { + const char * name; /* Name of shell */ + const char * inf; /* Name used for 'unlimited' resource */ + const char * cmd; /* Intro text */ + const char * hard; /* Hard limit text */ + const char * soft; /* Soft limit text */ + const char * both; /* Hard+Soft limit text */ + struct { + const char * pfx; + const char * sfx; + int divisor; + } lprm[RLIM_NLIMITS]; +} shellparm[] = +{ + { "", "infinity", "Resource limits%s%s:\n", "-max", "-cur", "", + { + { " cputime%-4s %8s", " secs\n", 1 }, + { " filesize%-4s %8s", " kb\n", 1024 }, + { " datasize%-4s %8s", " kb\n", 1024 }, + { " stacksize%-4s %8s", " kb\n", 1024 }, + { " coredumpsize%-4s %8s", " kb\n", 1024 }, + { " memoryuse%-4s %8s", " kb\n", 1024 }, + { " memorylocked%-4s %8s", " kb\n", 1024 }, + { " maxprocesses%-4s %8s", "\n", 1 }, + { " openfiles%-4s %8s", "\n", 1 } + } + }, + { "sh", "unlimited", "", " -H", " -S", "", + { + { "ulimit%s -t %s", ";\n", 1 }, + { "ulimit%s -f %s", ";\n", 512 }, + { "ulimit%s -d %s", ";\n", 1024 }, + { "ulimit%s -s %s", ";\n", 1024 }, + { "ulimit%s -c %s", ";\n", 512 }, + { "ulimit%s -m %s", ";\n", 1024 }, + { "ulimit%s -l %s", ";\n", 1024 }, + { "ulimit%s -u %s", ";\n", 1 }, + { "ulimit%s -n %s", ";\n", 1 } + } + }, + { "csh", "unlimited", "", " -h", "", NULL, + { + { "limit%s cputime %s", ";\n", 1 }, + { "limit%s filesize %s", ";\n", 1024 }, + { "limit%s datasize %s", ";\n", 1024 }, + { "limit%s stacksize %s", ";\n", 1024 }, + { "limit%s coredumpsize %s", ";\n", 1024 }, + { "limit%s memoryuse %s", ";\n", 1024 }, + { "limit%s memorylocked %s", ";\n", 1024 }, + { "limit%s maxproc %s", ";\n", 1 }, + { "limit%s openfiles %s", ";\n", 1 } + } + }, + { "bash", "unlimited", "", " -H", " -S", "", + { + { "ulimit%s -t %s", ";\n", 1 }, + { "ulimit%s -f %s", ";\n", 1024 }, + { "ulimit%s -d %s", ";\n", 1024 }, + { "ulimit%s -s %s", ";\n", 1024 }, + { "ulimit%s -c %s", ";\n", 1024 }, + { "ulimit%s -v %s", ";\n", 1024 }, + { "ulimit%s -m %s", ";\n", 1024 }, + { "ulimit%s -u %s", ";\n", 1 }, + { "ulimit%s -n %s", ";\n", 1 } + } + }, + { "tcsh", "unlimited", "", " -h", "", NULL, + { + { "limit%s cputime %s", ";\n", 1 }, + { "limit%s filesize %s", ";\n", 1024 }, + { "limit%s datasize %s", ";\n", 1024 }, + { "limit%s stacksize %s", ";\n", 1024 }, + { "limit%s coredumpsize %s", ";\n", 1024 }, + { "limit%s memoryuse %s", ";\n", 1024 }, + { "limit%s memorylocked %s", ";\n", 1024 }, + { "limit%s maxproc %s", ";\n", 1 }, + { "limit%s descriptors %s", ";\n", 1 } + } + }, + { "ksh|pdksh", "unlimited", "", " -H", " -S", "", + { + { "ulimit%s -t %s", ";\n", 1 }, + { "ulimit%s -f %s", ";\n", 512 }, + { "ulimit%s -d %s", ";\n", 1024 }, + { "ulimit%s -s %s", ";\n", 1024 }, + { "ulimit%s -c %s", ";\n", 512 }, + { "ulimit%s -m %s", ";\n", 1024 }, + { "ulimit%s -l %s", ";\n", 1024 }, + { "ulimit%s -p %s", ";\n", 1 }, + { "ulimit%s -n %s", ";\n", 1 } + } + }, + { "zsh", "unlimited", "", " -H", " -S", "", + { + { "ulimit%s -t %s", ";\n", 1 }, + { "ulimit%s -f %s", ";\n", 512 }, + { "ulimit%s -d %s", ";\n", 1024 }, + { "ulimit%s -s %s", ";\n", 1024 }, + { "ulimit%s -c %s", ";\n", 512 }, + { "ulimit%s -m %s", ";\n", 1024 }, + { "ulimit%s -l %s", ";\n", 1024 }, + { "ulimit%s -u %s", ";\n", 1 }, + { "ulimit%s -n %s", ";\n", 1 } + } + }, + /* Note: I recommend you don't use rc/es with 'limits' + * as this seems to be fairly buggy. The values below + * are set according to the manpage, but, for example, + * the string 'unlimited' is not actually accepted by + * the shell. 'es' is also prone to core dumping when + * displaying or modifying limits. Hopefully this will + * be fixed. DLN - Wed Jan 15 20:30 1997 + */ + { "rc|es", "unlimited", "", " -h", "", NULL, + { + { "limit%s cputime %s", ";\n", 1 }, + { "limit%s filesize %s", ";\n", 1024 }, + { "limit%s datasize %s", ";\n", 1024 }, + { "limit%s stacksize %s", ";\n", 1024 }, + { "limit%s coredumpsize %s", ";\n", 1024 }, + { "limit%s memoryuse %s", ";\n", 1024 }, + { "limit%s lockedmemory %s", ";\n", 1024 }, + { "limit%s processes %s", ";\n", 1 }, + { "limit%s descriptors %s", ";\n", 1 } + } + }, + { NULL } +}; + +static struct { + const char * cap; + rlim_t (*func)(login_cap_t *, const char *, rlim_t, rlim_t); +} resources[RLIM_NLIMITS] = { + { "cputime", login_getcaptime }, + { "filesize", login_getcapsize }, + { "datasize", login_getcapsize }, + { "stacksize", login_getcapsize }, + { "coredumpsize", login_getcapsize }, + { "memoryuse", login_getcapsize }, + { "memorylocked", login_getcapsize }, + { "maxproc", login_getcapnum, }, + { "openfiles", login_getcapnum } +}; + +/* + * One letter for each resource levels. + * NOTE: There is a dependancy on the corresponding + * letter index being equal to the resource number. + * If sys/resource.h defines are changed, this needs + * to be modified accordingly! + */ + +#define RCS_STRING "tfdscmlun" + +static rlim_t resource_num(int which, int ch, const char *str); +static void usage(const char *msg, ...); +static int getshelltype(void); +static void print_limit(rlim_t limit, unsigned divisor, const char * inf, const char * pfx, const char * sfx, const char * which); +extern char **environ; + +static const char rcs_string[] = RCS_STRING; + +int +main(int argc, char *argv[]) +{ + char *p, *cls = NULL; + char *cleanenv[1]; + struct passwd * pwd = NULL; + int rcswhich, shelltype; + int i, num_limits = 0; + int ch, doeval = 0, doall = 0; + login_cap_t * lc = NULL; + enum { ANY=0, SOFT=1, HARD=2, BOTH=3, DISPLAYONLY=4 } type = ANY; + enum { RCSUNKNOWN=0, RCSSET=1, RCSSEL=2 } todo = RCSUNKNOWN; + int which_limits[RLIM_NLIMITS]; + rlim_t set_limits[RLIM_NLIMITS]; + struct rlimit limits[RLIM_NLIMITS]; + + /* init resource tables */ + for (i = 0; i < RLIM_NLIMITS; i++) + { + which_limits[i] = 0; /* Don't set/display any */ + set_limits[i] = RLIM_INFINITY; + /* Get current resource values */ + getrlimit(i, &limits[i]); + } + + optarg = NULL; + while ((ch = getopt(argc, argv, ":EeC:U:BSHac:d:f:l:m:n:s:t:u:")) != EOF) { + switch(ch) { + case 'a': + doall = 1; + break; + case 'E': + environ = cleanenv; + cleanenv[0] = NULL; + break; + case 'e': + doeval = 1; + break; + case 'C': + cls = optarg; + break; + case 'U': + if ((pwd = getpwnam(optarg)) == NULL) { + if (!isdigit(*optarg) || (pwd = getpwuid(atoi(optarg))) == NULL) { + usage("Invalid user `%s'\n", optarg); + } + } + break; + case 'H': + type = HARD; + break; + case 'S': + type = SOFT; + break; + case 'B': + type = SOFT|HARD; + break; + default: + case ':': /* Without arg */ + if ((p = strchr(rcs_string, optopt)) != NULL) { + int rcswhich = p - rcs_string; + if (optarg && *optarg == '-') { /* 'arg' is actually a switch */ + --optind; /* back one arg, and make arg NULL */ + optarg = NULL; + } + todo = optarg == NULL ? RCSSEL : RCSSET; + if (type == ANY) + type = BOTH; + which_limits[rcswhich] = optarg ? type : DISPLAYONLY; + set_limits[rcswhich] = resource_num(rcswhich, optopt, optarg); + num_limits++; + break; + } + /* FALLTHRU */ + case '?': + usage(NULL); + } + optarg = NULL; + } + + /* If user was specified, get class from that */ + if (pwd != NULL) + lc = login_getclass(pwd); + else if (cls != NULL) { + lc = login_getclassbyname(cls, NULL); + if (lc == NULL || strcmp(cls, lc->lc_class) != 0) + fprintf(stderr, "login class '%s' non-existent, using %s\n", cls, lc?lc->lc_class:"current settings"); + } + + /* If we have a login class, update resource table from that */ + if (lc != NULL) { + for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { + char str[40]; + rlim_t val; + + /* current value overridden by resourcename or resourcename-cur */ + sprintf(str, "%s-cur", resources[rcswhich].cap); + val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_cur, limits[rcswhich].rlim_cur); + limits[rcswhich].rlim_cur = resources[rcswhich].func(lc, str, val, val); + /* maximum value overridden by resourcename or resourcename-max */ + sprintf(str, "%s-max", resources[rcswhich].cap); + val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_max, limits[rcswhich].rlim_max); + limits[rcswhich].rlim_max = resources[rcswhich].func(lc, str, val, val); + } + } + + /* now, let's determine what we wish to do with all this */ + + argv += optind; + + /* If we're setting limits or doing an eval (ie. we're not just + * displaying), then check that hard limits are not lower than + * soft limits, and force rasing the hard limit if we need to if + * we are raising the soft limit, or lower the soft limit if we + * are lowering the hard limit. + */ + if ((*argv || doeval) && getuid() == 0) { + for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { + if (limits[rcswhich].rlim_max != RLIM_INFINITY) { + if (limits[rcswhich].rlim_cur == RLIM_INFINITY) { + limits[rcswhich].rlim_max = RLIM_INFINITY; + which_limits[rcswhich] |= HARD; + } else if (limits[rcswhich].rlim_cur > limits[rcswhich].rlim_max) { + if (which_limits[rcswhich] == SOFT) { + limits[rcswhich].rlim_max = limits[rcswhich].rlim_cur; + which_limits[rcswhich] |= HARD; + } else if (which_limits[rcswhich] == HARD) { + limits[rcswhich].rlim_cur = limits[rcswhich].rlim_max; + which_limits[rcswhich] |= SOFT; + } else { + /* else.. if we're specifically setting both to + * silly values, then let it error out. + */ + } + } + } + } + } + + /* See if we've overridden anything specific on the command line */ + if (num_limits && todo == RCSSET) { + for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { + if (which_limits[rcswhich] & HARD) + limits[rcswhich].rlim_max = set_limits[rcswhich]; + if (which_limits[rcswhich] & SOFT) + limits[rcswhich].rlim_cur = set_limits[rcswhich]; + } + } + + /* If *argv is not NULL, then we are being asked to + * (perhaps) set environment variables and run a program + */ + if (*argv) { + if (doeval) + usage("-e cannot be used with `cmd' option\n"); + + login_close(lc); + + /* set leading environment variables, like eval(1) */ + while (*argv && (p = strchr(*argv, '='))) + (void)setenv(*argv++, ++p, 1); + + /* Set limits */ + for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { + if (doall || num_limits == 0 || which_limits[rcswhich] != 0) + if (setrlimit(rcswhich, &limits[rcswhich]) == -1) + err(1, "setrlimit %s", resources[rcswhich].cap); + } + + if (*argv == NULL) + usage(NULL); + + execvp(*argv, argv); + err(1, "%s", *argv); + } + + shelltype = doeval ? getshelltype() : SH_NONE; + + if (type == ANY) /* Default to soft limits */ + type = SOFT; + + /* Display limits */ + printf(shellparm[shelltype].cmd, lc ? " for class " : " (current)", lc ? lc->lc_class : ""); + for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { + if (doall || num_limits == 0 || which_limits[rcswhich] != 0) { + if (which_limits[rcswhich] == ANY || which_limits[rcswhich]) + which_limits[rcswhich] = type; + if (shellparm[shelltype].lprm[rcswhich].pfx) { + if (shellparm[shelltype].both && limits[rcswhich].rlim_cur == limits[rcswhich].rlim_max) { + print_limit(limits[rcswhich].rlim_max, + shellparm[shelltype].lprm[rcswhich].divisor, + shellparm[shelltype].inf, + shellparm[shelltype].lprm[rcswhich].pfx, + shellparm[shelltype].lprm[rcswhich].sfx, + shellparm[shelltype].both); + } else { + if (which_limits[rcswhich] & HARD) { + print_limit(limits[rcswhich].rlim_max, + shellparm[shelltype].lprm[rcswhich].divisor, + shellparm[shelltype].inf, + shellparm[shelltype].lprm[rcswhich].pfx, + shellparm[shelltype].lprm[rcswhich].sfx, + shellparm[shelltype].hard); + } + if (which_limits[rcswhich] & SOFT) { + print_limit(limits[rcswhich].rlim_cur, + shellparm[shelltype].lprm[rcswhich].divisor, + shellparm[shelltype].inf, + shellparm[shelltype].lprm[rcswhich].pfx, + shellparm[shelltype].lprm[rcswhich].sfx, + shellparm[shelltype].soft); + } + } + } + } + } + + login_close(lc); + exit(EXIT_SUCCESS); +} + + +static void +usage(char const *msg, ...) +{ + if (msg) { + va_list argp; + va_start(argp, msg); + vfprintf(stderr, msg, argp); + va_end(argp); + } + (void)fprintf(stderr, "limits [-C class|-U user] [-eaSHBE] [-cdflmnstu [val]] [[name=val ...] cmd]\n"); + exit(EXIT_FAILURE); +} + +static void +print_limit(rlim_t limit, unsigned divisor, const char * inf, const char * pfx, const char * sfx, const char * which) +{ + char numbr[64]; + if (limit == RLIM_INFINITY) + strcpy(numbr, inf); + else +#ifdef RLIM_LONG + sprintf(numbr, "%ql", (long)((limit + divisor/2) / divisor)); +#else + sprintf(numbr, "%qd", (quad_t)((limit + divisor/2) / divisor)); +#endif + printf(pfx, which, numbr); + printf(sfx, which); + +} + + +#ifdef RLIM_LONG +# define STRTOV strtol +#else +# define STRTOV strtoq +#endif + +static rlim_t +resource_num(int which, int ch, const char *str) +{ + rlim_t res = RLIM_INFINITY; + + if (str != NULL && !(strcasecmp(str, "inf")==0 || strcasecmp(str, "infinity")==0 || strcasecmp(str, "unlimited")==0)) { + const char * s = str; + char *e; + switch (which) + { + case RLIMIT_CPU: /* time values */ + errno = 0; + res = 0; + while (*s) { + rlim_t tim = STRTOV(s, &e, 0); + if (e == NULL || e == s || errno) + break; + switch (*e++) { + case 0: /* end of string */ + e--; + default: + case 's': case 'S': /* seconds */ + break; + case 'm': case 'M': /* minutes */ + tim *= 60L; + break; + case 'h': case 'H': /* hours */ + tim *= (60L * 60L); + break; + case 'd': case 'D': /* days */ + tim *= (60L * 60L * 24L); + break; + case 'w': case 'W': /* weeks */ + tim *= (60L * 60L * 24L * 7L); + case 'y': case 'Y': /* Years */ + tim *= (60L * 60L * 24L * 365L); + } + s = e; + res += tim; + } + break; + case RLIMIT_FSIZE: /* Size values */ + case RLIMIT_DATA: + case RLIMIT_STACK: + case RLIMIT_CORE: + case RLIMIT_RSS: + case RLIMIT_MEMLOCK: + errno = 0; + res = 0; + while (*s) { + rlim_t mult, tim = STRTOV(s, &e, 0); + if (e == NULL || e == s || errno) + break; + switch (*e++) { + case 0: /* end of string */ + e--; + default: + mult = 1; + break; + case 'b': case 'B': /* 512-byte blocks */ + mult = 512; + break; + case 'k': case 'K': /* 1024-byte Kilobytes */ + mult = 1024; + break; + case 'm': case 'M': /* 1024-k kbytes */ + mult = 1024 * 1024; + break; + case 'g': case 'G': /* 1Gbyte */ + mult = 1024 * 1024 * 1024; + break; +#ifndef RLIM_LONG + case 't': case 'T': /* 1TBte */ + mult = 1024LL * 1024LL * 1024LL * 1024LL; + break; +#endif + } + s = e; + res += (tim * mult); + } + break; + case RLIMIT_NPROC: + case RLIMIT_NOFILE: + res = STRTOV(s, &e, 0); + s = e; + break; + } + if (*s) + usage("invalid value -%c `%s'\n", ch, str); + } + return res; +} + + +static int +getshellbyname(const char * shell) +{ + int i; + const char * q; + const char * p = strrchr(shell, '/'); + + p = p ? ++p : shell; + for (i = 0; (q = shellparm[i].name) != NULL; i++) { + while (*q) + { + int j = strcspn(q, "|"); + if (j == 0) + break; + if (strncmp(p, q, j) == 0) + return i; + if (*(q += j)) + ++q; + } + } + return SH_SH; +} + +/* + * Determine the type of shell our parent process is + * This is quite tricky, not 100% reliable and probably + * not nearly as thorough as it should be. Basically, this + * is a "best guess" only, but hopefully will work in + * most cases. + */ + +static int +getshelltype(void) +{ + pid_t ppid = getppid(); + + if (ppid != 1) { + FILE * fp; + struct stat st; + char procdir[MAXPATHLEN], buf[128]; + int l = sprintf(procdir, "/proc/%ld/", (long)ppid); + char * shell = getenv("SHELL"); + + if (shell != NULL && stat(shell, &st) != -1) + { + struct stat st1; + strcpy(procdir+l, "file"); + /* $SHELL is actual shell? */ + if (stat(procdir, &st1) != -1 && memcmp(&st, &st1, sizeof st) == 0) + return getshellbyname(shell); + } + strcpy(procdir+l, "status"); + if (stat(procdir, &st) == 0 && (fp = fopen(procdir, "r")) != NULL) { + char * p = fgets(buf, sizeof buf, fp)==NULL ? NULL : strtok(buf, " \t"); + fclose(fp); + if (p != NULL) + return getshellbyname(p); + } + } + return SH_SH; +} + |