2
* rlimits.c - resource limit builtins
4
* This file is part of zsh, the Z shell.
6
* Copyright (c) 1992-1997 Paul Falstad
9
* Permission is hereby granted, without written agreement and without
10
* license or royalty fees, to use, copy, modify, and distribute this
11
* software and to distribute modified versions of this software for any
12
* purpose, provided that the above copyright notice and the following
13
* two paragraphs appear in all copies of this software.
15
* In no event shall Paul Falstad or the Zsh Development Group be liable
16
* to any party for direct, indirect, special, incidental, or consequential
17
* damages arising out of the use of this software and its documentation,
18
* even if Paul Falstad and the Zsh Development Group have been advised of
19
* the possibility of such damage.
21
* Paul Falstad and the Zsh Development Group specifically disclaim any
22
* warranties, including, but not limited to, the implied warranties of
23
* merchantability and fitness for a particular purpose. The software
24
* provided hereunder is on an "as is" basis, and Paul Falstad and the
25
* Zsh Development Group have no obligation to provide maintenance,
26
* support, updates, enhancements, or modifications.
30
#include "rlimits.mdh"
31
#include "rlimits.pro"
33
#if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY)
42
/* Generated rec array containing limits required for the limit builtin. *
43
* They must appear in this array in numerical order of the RLIMIT_* macros. */
48
zstrtorlimt(const char *s, char **t, int base)
52
if (strcmp(s, "unlimited") == 0) {
57
# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED)
61
else if (*++s == 'x' || *s == 'X')
67
for (; *s >= '0' && *s < ('0' + base); s++)
68
ret = ret * base + *s - '0';
70
for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
71
|| (*s >= 'A' && *s < ('A' + base - 10)); s++)
72
ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
75
# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
76
ret = zstrtol(s, t, base);
77
# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
83
showlimitvalue(int lim, rlim_t val)
85
/* display limit for resource number lim */
86
if (lim < ZSH_NLIMITS)
87
printf("%-16s", recs[lim]);
90
/* Unknown limit, hence unknown units. */
93
if (val == RLIM_INFINITY)
94
printf("unlimited\n");
95
else if (lim >= ZSH_NLIMITS)
97
# ifdef RLIM_T_IS_QUAD_T
100
# ifdef RLIM_T_IS_LONG_LONG
101
printf("%lld\n", val);
103
# ifdef RLIM_T_IS_UNSIGNED
104
printf("%lu\n", val);
106
printf("%ld\n", val);
107
# endif /* RLIM_T_IS_UNSIGNED */
108
# endif /* RLIM_T_IS_LONG_LONG */
109
# endif /* RLIM_T_IS_QUAD_T */
111
else if (limtype[lim] == ZLIMTYPE_TIME) {
112
/* time-type resource -- display as hours, minutes and
114
printf("%d:%02d:%02d\n", (int)(val / 3600),
115
(int)(val / 60) % 60, (int)(val % 60));
116
} else if (limtype[lim] == ZLIMTYPE_NUMBER ||
117
limtype[lim] == ZLIMTYPE_UNKNOWN) {
118
/* pure numeric resource */
119
printf("%d\n", (int)val);
120
} else if (val >= 1024L * 1024L)
121
/* memory resource -- display with `K' or `M' modifier */
122
# ifdef RLIM_T_IS_QUAD_T
123
printf("%qdMB\n", val / (1024L * 1024L));
125
printf("%qdkB\n", val / 1024L);
127
# ifdef RLIM_T_IS_LONG_LONG
128
printf("%lldMB\n", val / (1024L * 1024L));
130
printf("%lldkB\n", val / 1024L);
132
# ifdef RLIM_T_IS_UNSIGNED
133
printf("%luMB\n", val / (1024L * 1024L));
135
printf("%lukB\n", val / 1024L);
137
printf("%ldMB\n", val / (1024L * 1024L));
139
printf("%ldkB\n", val / 1024L);
140
# endif /* RLIM_T_IS_UNSIGNED */
141
# endif /* RLIM_T_IS_LONG_LONG */
142
# endif /* RLIM_T_IS_QUAD_T */
145
/* Display resource limits. hard indicates whether `hard' or `soft' *
146
* limits should be displayed. lim specifies the limit, or may be -1 *
151
showlimits(char *nam, int hard, int lim)
155
if (lim >= ZSH_NLIMITS)
158
* Not configured into the shell. Ask the OS
159
* explicitly for this limit.
162
if (getrlimit(lim, &vals) < 0)
164
zwarnnam(nam, "can't read limit: %e", errno);
167
showlimitvalue(lim, hard ? vals.rlim_max : vals.rlim_cur);
171
showlimitvalue(lim, hard ? limits[lim].rlim_max :
172
limits[lim].rlim_cur);
176
/* main loop over resource types */
177
for (rt = 0; rt != ZSH_NLIMITS; rt++)
178
showlimitvalue(rt, (hard) ? limits[rt].rlim_max :
179
limits[rt].rlim_cur);
185
/* Display a resource limit, in ulimit style. lim specifies which *
186
* limit should be displayed, and hard indicates whether the hard or *
187
* soft limit should be displayed. */
191
printulimit(char *nam, int lim, int hard, int head)
195
/* get the limit in question */
196
if (lim >= ZSH_NLIMITS)
200
if (getrlimit(lim, &vals) < 0)
202
zwarnnam(nam, "can't read limit: %e", errno);
205
limit = (hard) ? vals.rlim_max : vals.rlim_cur;
208
limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
209
/* display the appropriate heading */
213
printf("-c: core file size (blocks) ");
214
if (limit != RLIM_INFINITY)
219
printf("-d: data seg size (kbytes) ");
220
if (limit != RLIM_INFINITY)
225
printf("-f: file size (blocks) ");
226
if (limit != RLIM_INFINITY)
229
# ifdef HAVE_RLIMIT_SIGPENDING
230
case RLIMIT_SIGPENDING:
232
printf("-i: pending signals ");
235
# ifdef HAVE_RLIMIT_MEMLOCK
238
printf("-l: locked-in-memory size (kb) ");
239
if (limit != RLIM_INFINITY)
242
# endif /* HAVE_RLIMIT_MEMLOCK */
243
/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid *
244
* duplicate case statement. Observed on QNX Neutrino 6.1.0. */
245
# if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS)
248
printf("-m: resident set size (kbytes) ");
249
if (limit != RLIM_INFINITY)
252
# endif /* HAVE_RLIMIT_RSS */
253
# if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_RSS) && defined(RLIMIT_VMEM_IS_RSS)
256
printf("-m: memory size (kb) ");
257
if (limit != RLIM_INFINITY)
260
# endif /* HAVE_RLIMIT_VMEM */
261
# ifdef HAVE_RLIMIT_NOFILE
264
printf("-n: file descriptors ");
266
# endif /* HAVE_RLIMIT_NOFILE */
267
# ifdef HAVE_RLIMIT_MSGQUEUE
268
case RLIMIT_MSGQUEUE:
270
printf("-q: bytes in POSIX msg queues ");
275
printf("-s: stack size (kbytes) ");
276
if (limit != RLIM_INFINITY)
281
printf("-t: cpu time (seconds) ");
283
# ifdef HAVE_RLIMIT_NPROC
286
printf("-u: processes ");
288
# endif /* HAVE_RLIMIT_NPROC */
289
# if defined(HAVE_RLIMIT_VMEM) && (!defined(HAVE_RLIMIT_RSS) || !defined(RLIMIT_VMEM_IS_RSS))
292
printf("-v: virtual memory size (kb) ");
293
if (limit != RLIM_INFINITY)
296
# endif /* HAVE_RLIMIT_VMEM */
297
# if defined HAVE_RLIMIT_AS && !defined(RLIMIT_VMEM_IS_AS)
300
printf("-v: address space (kb) ");
301
if (limit != RLIM_INFINITY)
304
# endif /* HAVE_RLIMIT_AS */
305
# ifdef HAVE_RLIMIT_LOCKS
308
printf("-x: file locks ");
310
# endif /* HAVE_RLIMIT_LOCKS */
311
# ifdef HAVE_RLIMIT_AIO_MEM
314
printf("-N %2d: AIO locked-in-memory (kb) ", RLIMIT_AIO_MEM);
315
if (limit != RLIM_INFINITY)
318
# endif /* HAVE_RLIMIT_AIO_MEM */
319
# ifdef HAVE_RLIMIT_AIO_OPS
322
printf("-N %2d: AIO operations ", RLIMIT_AIO_OPS);
324
# endif /* HAVE_RLIMIT_AIO_OPS */
325
# ifdef HAVE_RLIMIT_TCACHE
328
printf("-N %2d: cached threads ", RLIMIT_TCACHE);
330
# endif /* HAVE_RLIMIT_TCACHE */
331
# ifdef HAVE_RLIMIT_SBSIZE
334
printf("-N %2d: socket buffer size (kb) ", RLIMIT_SBSIZE);
335
if (limit != RLIM_INFINITY)
338
# endif /* HAVE_RLIMIT_SBSIZE */
339
# ifdef HAVE_RLIMIT_PTHREAD
342
printf("-N %2d: threads per process ", RLIMIT_PTHREAD);
344
# endif /* HAVE_RLIMIT_PTHREAD */
345
# ifdef HAVE_RLIMIT_NICE
348
printf("-e: max nice ");
350
# endif /* HAVE_RLIMIT_NICE */
351
# ifdef HAVE_RLIMIT_RTPRIO
354
printf("-r: max rt priority ");
356
# endif /* HAVE_RLIMIT_RTPRIO */
359
printf("-N %2d: ", lim);
362
/* display the limit */
363
if (limit == RLIM_INFINITY)
364
printf("unlimited\n");
366
# ifdef RLIM_T_IS_QUAD_T
367
printf("%qd\n", limit);
369
# ifdef RLIM_T_IS_LONG_LONG
370
printf("%lld\n", limit);
372
# ifdef RLIM_T_IS_UNSIGNED
373
printf("%lu\n", limit);
375
printf("%ld\n", limit);
376
# endif /* RLIM_T_IS_UNSIGNED */
377
# endif /* RLIM_T_IS_LONG_LONG */
378
# endif /* RLIM_T_IS_QUAD_T */
386
do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set)
388
if (lim >= ZSH_NLIMITS) {
390
if (getrlimit(lim, &vals) < 0)
392
/* best guess about error */
393
zwarnnam(nam, "can't read limit: %e", errno);
398
if (val > vals.rlim_max && geteuid()) {
399
zwarnnam(nam, "can't raise hard limits");
404
* not show if all systems will do this silently, but
407
if (val < vals.rlim_cur)
411
if (val > vals.rlim_max) {
412
zwarnnam(nam, "limit exceeds hard limit");
421
"warning: unrecognised limit %d, use -s to set",
425
else if (setrlimit(lim, &vals) < 0)
427
zwarnnam(nam, "setrlimit failed: %e", errno);
431
/* new limit is valid and has been interpreted; apply it to the
432
specified resource */
434
/* can only raise hard limits if running as root */
435
if (val > current_limits[lim].rlim_max && geteuid()) {
436
zwarnnam(nam, "can't raise hard limits");
439
limits[lim].rlim_max = val;
440
if (val < limits[lim].rlim_cur)
441
limits[lim].rlim_cur = val;
445
if (val > limits[lim].rlim_max) {
446
/* no idea about this difference, don't intend to worry */
449
/* ulimit does this */
450
if (val > current_limits[lim].rlim_max && geteuid()) {
451
zwarnnam(nam, "value exceeds hard limit");
454
limits[lim].rlim_max = limits[lim].rlim_cur = val;
456
/* but limit does this */
457
zwarnnam(nam, "limit exceeds hard limit");
461
limits[lim].rlim_cur = val;
462
if (set && zsetlimit(lim, "limit"))
469
/* limit: set or show resource limits. The variable hard indicates *
470
* whether `hard' or `soft' resource limits are being set/shown. */
474
bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
477
int hard, limnum, lim;
481
hard = OPT_ISSET(ops,'h');
482
if (OPT_ISSET(ops,'s') && !*argv)
483
return setlimits(NULL);
484
/* without arguments, display limits */
486
return showlimits(nam, hard, -1);
487
while ((s = *argv++)) {
488
/* Search for the appropriate resource name. When a name matches (i.e. *
489
* starts with) the argument, the lim variable changes from -1 to the *
490
* number of the resource. If another match is found, lim goes to -2. */
493
lim = (int)zstrtol(s, NULL, 10);
496
for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
497
if (!strncmp(recs[limnum], s, strlen(s))) {
503
/* lim==-1 indicates that no matches were found. *
504
* lim==-2 indicates that multiple matches were found. */
507
(lim == -2) ? "ambiguous resource specification: %s"
508
: "no such resource: %s", s);
511
/* without value for limit, display the current limit */
513
return showlimits(nam, hard, lim);
514
if (lim >= ZSH_NLIMITS)
516
val = zstrtorlimt(s, &s, 10);
519
/* unknown limit, no idea how to scale */
520
zwarnnam(nam, "unknown scaling factor: %s", s);
524
else if (limtype[lim] == ZLIMTYPE_TIME) {
525
/* time-type resource -- may be specified as seconds, or minutes or *
526
* hours with the `m' and `h' modifiers, and `:' may be used to add *
527
* together more than one of these. It's easier to understand from *
529
val = zstrtorlimt(s, &s, 10);
531
if ((*s == 'h' || *s == 'H') && !s[1])
533
else if ((*s == 'm' || *s == 'M') && !s[1])
536
val = val * 60 + zstrtorlimt(s + 1, &s, 10);
538
zwarnnam(nam, "unknown scaling factor: %s", s);
542
} else if (limtype[lim] == ZLIMTYPE_NUMBER || limtype[lim] == ZLIMTYPE_UNKNOWN) {
543
/* pure numeric resource -- only a straight decimal number is
546
val = zstrtorlimt(t, &s, 10);
548
zwarnnam(nam, "limit must be a number");
552
/* memory-type resource -- `k' and `M' modifiers are permitted,
553
meaning (respectively) 2^10 and 2^20. */
554
val = zstrtorlimt(s, &s, 10);
555
if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) {
556
if (val != RLIM_INFINITY)
558
} else if ((*s == 'M' || *s == 'm') && !s[1])
561
zwarnnam(nam, "unknown scaling factor: %s", s);
565
if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's')))
573
do_unlimit(char *nam, int lim, int hard, int soft, int set, int euid)
575
/* remove specified limit */
576
if (lim >= ZSH_NLIMITS) {
578
if (getrlimit(lim, &vals) < 0)
580
zwarnnam(nam, "can't read limit: %e", errno);
584
if (euid && vals.rlim_max != RLIM_INFINITY) {
585
zwarnnam(nam, "can't remove hard limits");
588
vals.rlim_max = RLIM_INFINITY;
591
vals.rlim_cur = vals.rlim_max;
594
"warning: unrecognised limit %d, use -s to set", lim);
596
} else if (setrlimit(lim, &vals) < 0) {
597
zwarnnam(nam, "setrlimit failed: %e", errno);
602
if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) {
603
zwarnnam(nam, "can't remove hard limits");
606
limits[lim].rlim_max = RLIM_INFINITY;
609
limits[lim].rlim_cur = limits[lim].rlim_max;
610
if (set && zsetlimit(lim, nam))
616
/* unlimit: remove resource limits. Much of this code is the same as *
617
* that in bin_limit(). */
621
bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func))
623
int hard, limnum, lim;
625
uid_t euid = geteuid();
627
hard = OPT_ISSET(ops,'h');
628
/* Without arguments, remove all limits. */
630
for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) {
632
if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY)
635
limits[limnum].rlim_max = RLIM_INFINITY;
637
limits[limnum].rlim_cur = limits[limnum].rlim_max;
639
if (OPT_ISSET(ops,'s'))
640
ret += setlimits(nam);
642
zwarnnam(nam, "can't remove hard limits");
644
for (; *argv; argv++) {
645
/* Search for the appropriate resource name. When a name *
646
* matches (i.e. starts with) the argument, the lim variable *
647
* changes from -1 to the number of the resource. If another *
648
* match is found, lim goes to -2. */
649
if (idigit(**argv)) {
650
lim = (int)zstrtol(*argv, NULL, 10);
652
for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
653
if (!strncmp(recs[limnum], *argv, strlen(*argv))) {
660
/* lim==-1 indicates that no matches were found. *
661
* lim==-2 indicates that multiple matches were found. */
664
(lim == -2) ? "ambiguous resource specification: %s"
665
: "no such resource: %s", *argv);
668
else if (do_unlimit(nam, lim, hard, !hard, OPT_ISSET(ops, 's'),
676
/* ulimit: set or display resource limits */
680
bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
682
int res, resmask = 0, hard = 0, soft = 0, nres = 0, all = 0, ret = 0;
687
if (options && *options == '-' && !options[1]) {
688
zwarnnam(name, "missing option letter");
692
if (options && *options == '-') {
707
res = (int)zstrtol(options+1, NULL, 10);
709
res = (int)zstrtol(*argv++, NULL, 10);
711
zwarnnam(name, "number required after -N");
715
* fake it so it looks like we just finished an option...
722
zwarnnam(name, "no limits allowed with -a");
726
resmask = (1 << RLIM_NLIMITS) - 1;
744
# ifdef HAVE_RLIMIT_RSS
748
# endif /* HAVE_RLIMIT_RSS */
749
# ifdef HAVE_RLIMIT_MEMLOCK
751
res = RLIMIT_MEMLOCK;
753
# endif /* HAVE_RLIMIT_MEMLOCK */
754
# ifdef HAVE_RLIMIT_NOFILE
758
# endif /* HAVE_RLIMIT_NOFILE */
759
# ifdef HAVE_RLIMIT_NPROC
763
# endif /* HAVE_RLIMIT_NPROC */
764
# if defined(HAVE_RLIMIT_VMEM) || defined(HAVE_RLIMIT_AS)
766
# ifdef HAVE_RLIMIT_VMEM
772
# endif /* HAVE_RLIMIT_VMEM */
773
# ifdef HAVE_RLIMIT_LOCKS
778
# ifdef HAVE_RLIMIT_SIGPENDING
780
res = RLIMIT_SIGPENDING;
783
# ifdef HAVE_RLIMIT_MSGQUEUE
785
res = RLIMIT_MSGQUEUE;
788
# ifdef HAVE_RLIMIT_NICE
793
# ifdef HAVE_RLIMIT_RTPRIO
799
/* unrecognised limit */
800
zwarnnam(name, "bad option: -%c", *options);
807
if (all && res != -1) {
808
zwarnnam(name, "no limits allowed with -a");
813
if (!*argv || **argv == '-') {
825
zwarnnam(name, "no arguments allowed after -a");
830
if (strcmp(*argv, "unlimited")) {
831
/* set limit to specified value */
834
limit = zstrtorlimt(*argv, NULL, 10);
835
/* scale appropriately */
843
# ifdef HAVE_RLIMIT_RSS
845
# endif /* HAVE_RLIMIT_RSS */
846
# ifdef HAVE_RLIMIT_MEMLOCK
848
# endif /* HAVE_RLIMIT_MEMLOCK */
849
/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid *
850
* duplicate case statement. Observed on QNX Neutrino 6.1.0. */
851
# if defined(HAVE_RLIMIT_VMEM) && !defined(RLIMIT_VMEM_IS_RSS)
853
# endif /* HAVE_RLIMIT_VMEM */
854
/* ditto RLIMIT_VMEM and RLIMIT_AS */
855
# if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS) && !defined(RLIMIT_RSS_IS_AS)
857
# endif /* HAVE_RLIMIT_AS */
858
# ifdef HAVE_RLIMIT_AIO_MEM
860
# endif /* HAVE_RLIMIT_AIO_MEM */
864
if (do_limit(name, res, limit, hard, soft, 1))
867
if (do_unlimit(name, res, hard, soft, 1, geteuid()))
872
for (res = 0; resmask; res++, resmask >>= 1)
873
if ((resmask & 1) && printulimit(name, res, hard, nres > 1))
878
#else /* !HAVE_GETRLIMIT || !RLIM_INFINITY */
880
# define bin_limit bin_notavail
881
# define bin_ulimit bin_notavail
882
# define bin_unlimit bin_notavail
884
#endif /* !HAVE_GETRLIMIT || !RLIM_INFINITY */
886
static struct builtin bintab[] = {
887
BUILTIN("limit", 0, bin_limit, 0, -1, 0, "sh", NULL),
888
BUILTIN("ulimit", 0, bin_ulimit, 0, -1, 0, NULL, NULL),
889
BUILTIN("unlimit", 0, bin_unlimit, 0, -1, 0, "hs", NULL),
892
static struct features module_features = {
893
bintab, sizeof(bintab)/sizeof(*bintab),
902
setup_(UNUSED(Module m))
909
features_(Module m, char ***features)
911
*features = featuresarray(m, &module_features);
917
enables_(Module m, int **enables)
919
return handlefeatures(m, &module_features, enables);
933
return setfeatureenables(m, &module_features, NULL);
939
finish_(UNUSED(Module m))