2 * Copyright (c) 1997 by
3 * David L. Nugent <davidn@blaze.net.au>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, is permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice immediately at the beginning of the file, without modification,
11 * this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. This work was done expressly for inclusion into FreeBSD. Other use
16 * is permitted provided this notation is included.
17 * 4. Absolutely no warranty of function or purpose is made by the authors.
18 * 5. Modifications may be freely made to this file providing the above
21 * Display/change(+runprogram)/eval resource limits.
23 * $FreeBSD: src/usr.bin/limits/limits.c,v 1.7.2.3 2003/05/22 09:26:57 sheldonh Exp $
24 * $DragonFly: src/usr.bin/limits/limits.c,v 1.6 2006/03/06 03:10:51 swildner Exp $
30 #include <sys/types.h>
32 #include <sys/param.h>
39 #include <login_cap.h>
41 #include <sys/resource.h>
48 SH_BASH
, /* gnu bash */
57 /* eval emitter for popular shells.
58 * Why aren't there any standards here? Most shells support either
59 * the csh 'limit' or sh 'ulimit' command, but each varies just
60 * enough that they aren't very compatible from one to the other.
63 const char * name
; /* Name of shell */
64 const char * inf
; /* Name used for 'unlimited' resource */
65 const char * cmd
; /* Intro text */
66 const char * hard
; /* Hard limit text */
67 const char * soft
; /* Soft limit text */
68 const char * both
; /* Hard+Soft limit text */
76 { "", "infinity", "Resource limits%s%s:\n", "-max", "-cur", "",
78 { " cputime%-4s %8s", " secs\n", 1 },
79 { " filesize%-4s %8s", " kb\n", 1024 },
80 { " datasize%-4s %8s", " kb\n", 1024 },
81 { " stacksize%-4s %8s", " kb\n", 1024 },
82 { " coredumpsize%-4s %8s", " kb\n", 1024 },
83 { " memoryuse%-4s %8s", " kb\n", 1024 },
84 { " memorylocked%-4s %8s", " kb\n", 1024 },
85 { " maxprocesses%-4s %8s", "\n", 1 },
86 { " openfiles%-4s %8s", "\n", 1 },
87 { " sbsize%-4s %8s", " bytes\n", 1 },
88 { " vmemoryuse%-4s %8s", " kb\n", 1024 },
89 #ifdef RLIMIT_POSIXLOCKS
90 { " posixlocks%-4s %8s", "\n", 1 },
94 { "sh", "unlimited", "", " -H", " -S", "",
96 { "ulimit%s -t %s", ";\n", 1 },
97 { "ulimit%s -f %s", ";\n", 512 },
98 { "ulimit%s -d %s", ";\n", 1024 },
99 { "ulimit%s -s %s", ";\n", 1024 },
100 { "ulimit%s -c %s", ";\n", 512 },
101 { "ulimit%s -m %s", ";\n", 1024 },
102 { "ulimit%s -l %s", ";\n", 1024 },
103 { "ulimit%s -u %s", ";\n", 1 },
104 { "ulimit%s -n %s", ";\n", 1 },
105 { "ulimit%s -b %s", ";\n", 1 },
106 { "ulimit%s -v %s", ";\n", 1024 },
107 #ifdef RLIMIT_POSIXLOCKS
108 { "ulimit%s -k %s", ";\n", 1 },
112 { "csh", "unlimited", "", " -h", "", NULL
,
114 { "limit%s cputime %s", ";\n", 1 },
115 { "limit%s filesize %s", ";\n", 1024 },
116 { "limit%s datasize %s", ";\n", 1024 },
117 { "limit%s stacksize %s", ";\n", 1024 },
118 { "limit%s coredumpsize %s", ";\n", 1024 },
119 { "limit%s memoryuse %s", ";\n", 1024 },
120 { "limit%s memorylocked %s", ";\n", 1024 },
121 { "limit%s maxproc %s", ";\n", 1 },
122 { "limit%s openfiles %s", ";\n", 1 },
123 { "limit%s sbsize %s", ";\n", 1 },
124 { "limit%s vmemoryuse %s", ";\n", 1024 },
125 #ifdef RLIMIT_POSIXLOCKS
126 { "limit%s advlocks %s", ";\n", 1 },
130 { "bash|bash2", "unlimited", "", " -H", " -S", "",
132 { "ulimit%s -t %s", ";\n", 1 },
133 { "ulimit%s -f %s", ";\n", 1024 },
134 { "ulimit%s -d %s", ";\n", 1024 },
135 { "ulimit%s -s %s", ";\n", 1024 },
136 { "ulimit%s -c %s", ";\n", 1024 },
137 { "ulimit%s -m %s", ";\n", 1024 },
138 { "ulimit%s -l %s", ";\n", 1024 },
139 { "ulimit%s -u %s", ";\n", 1 },
140 { "ulimit%s -n %s", ";\n", 1 },
141 { "ulimit%s -b %s", ";\n", 1 },
142 { "ulimit%s -v %s", ";\n", 1024 },
143 #ifdef RLIMIT_POSIXLOCKS
144 { "ulimit%s -k %s", ";\n", 1 },
148 { "tcsh", "unlimited", "", " -h", "", NULL
,
150 { "limit%s cputime %s", ";\n", 1 },
151 { "limit%s filesize %s", ";\n", 1024 },
152 { "limit%s datasize %s", ";\n", 1024 },
153 { "limit%s stacksize %s", ";\n", 1024 },
154 { "limit%s coredumpsize %s", ";\n", 1024 },
155 { "limit%s memoryuse %s", ";\n", 1024 },
156 { "limit%s memorylocked %s", ";\n", 1024 },
157 { "limit%s maxproc %s", ";\n", 1 },
158 { "limit%s descriptors %s", ";\n", 1 },
159 { "limit%s sbsize %s", ";\n", 1 },
160 { "limit%s vmemoryuse %s", ";\n", 1024 },
161 #ifdef RLIMIT_POSIXLOCKS
162 { "limit%s advlocks %s", ";\n", 1 },
166 { "ksh|pdksh", "unlimited", "", " -H", " -S", "",
168 { "ulimit%s -t %s", ";\n", 1 },
169 { "ulimit%s -f %s", ";\n", 512 },
170 { "ulimit%s -d %s", ";\n", 1024 },
171 { "ulimit%s -s %s", ";\n", 1024 },
172 { "ulimit%s -c %s", ";\n", 512 },
173 { "ulimit%s -m %s", ";\n", 1024 },
174 { "ulimit%s -l %s", ";\n", 1024 },
175 { "ulimit%s -p %s", ";\n", 1 },
176 { "ulimit%s -n %s", ";\n", 1 },
177 { "ulimit%s -b %s", ";\n", 1 },
178 { "ulimit%s -v %s", ";\n", 1024 },
179 #ifdef RLIMIT_POSIXLOCKS
180 { "ulimit%s -k %s", ";\n", 1 },
184 { "zsh", "unlimited", "", " -H", " -S", "",
186 { "ulimit%s -t %s", ";\n", 1 },
187 { "ulimit%s -f %s", ";\n", 512 },
188 { "ulimit%s -d %s", ";\n", 1024 },
189 { "ulimit%s -s %s", ";\n", 1024 },
190 { "ulimit%s -c %s", ";\n", 512 },
191 { "ulimit%s -m %s", ";\n", 1024 },
192 { "ulimit%s -l %s", ";\n", 1024 },
193 { "ulimit%s -u %s", ";\n", 1 },
194 { "ulimit%s -n %s", ";\n", 1 },
195 { "ulimit%s -b %s", ";\n", 1 },
196 { "ulimit%s -v %s", ";\n", 1024 },
197 #ifdef RLIMIT_POSIXLOCKS
198 { "ulimit%s -k %s", ";\n", 1 },
202 { "rc|es", "unlimited", "", " -h", "", NULL
,
204 { "limit%s cputime %s", ";\n", 1 },
205 { "limit%s filesize %s", ";\n", 1024 },
206 { "limit%s datasize %s", ";\n", 1024 },
207 { "limit%s stacksize %s", ";\n", 1024 },
208 { "limit%s coredumpsize %s", ";\n", 1024 },
209 { "limit%s memoryuse %s", ";\n", 1024 },
210 { "limit%s lockedmemory %s", ";\n", 1024 },
211 { "limit%s processes %s", ";\n", 1 },
212 { "limit%s descriptors %s", ";\n", 1 },
213 { "limit%s sbsize %s", ";\n", 1 },
214 { "limit%s vmemoryuse %s", ";\n", 1024 },
215 #ifdef RLIMIT_POSIXLOCKS
216 { "limit%s advlocks %s", ";\n", 1 },
220 { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, {} }
225 rlim_t (*func
)(login_cap_t
*, const char *, rlim_t
, rlim_t
);
226 } resources
[RLIM_NLIMITS
] = {
227 { "cputime", login_getcaptime
},
228 { "filesize", login_getcapsize
},
229 { "datasize", login_getcapsize
},
230 { "stacksize", login_getcapsize
},
231 { "coredumpsize", login_getcapsize
},
232 { "memoryuse", login_getcapsize
},
233 { "memorylocked", login_getcapsize
},
234 { "maxproc", login_getcapnum
},
235 { "openfiles", login_getcapnum
},
236 { "sbsize", login_getcapsize
},
237 { "vmemoryuse", login_getcapsize
},
238 { "posixlocks", login_getcapnum
},
242 * One letter for each resource levels.
243 * NOTE: There is a dependancy on the corresponding
244 * letter index being equal to the resource number.
245 * If sys/resource.h defines are changed, this needs
246 * to be modified accordingly!
249 #define RCS_STRING "tfdscmlunbvk"
251 static rlim_t
resource_num(int which
, int ch
, const char *str
);
252 static void usage(void);
253 static int getshelltype(void);
254 static void print_limit(rlim_t limit
, unsigned divisor
, const char *inf
,
255 const char *pfx
, const char *sfx
, const char *which
);
256 extern char **environ
;
258 static const char rcs_string
[] = RCS_STRING
;
261 main(int argc
, char *argv
[])
263 char *p
, *cls
= NULL
;
265 struct passwd
* pwd
= NULL
;
266 int rcswhich
, shelltype
;
267 int i
, num_limits
= 0;
268 int ch
, doeval
= 0, doall
= 0;
269 login_cap_t
* lc
= NULL
;
270 enum { ANY
=0, SOFT
=1, HARD
=2, BOTH
=3, DISPLAYONLY
=4 } type
= ANY
;
271 enum { RCSUNKNOWN
=0, RCSSET
=1, RCSSEL
=2 } todo
= RCSUNKNOWN
;
272 int which_limits
[RLIM_NLIMITS
];
273 rlim_t set_limits
[RLIM_NLIMITS
];
274 struct rlimit limits
[RLIM_NLIMITS
];
276 /* init resource tables */
277 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
278 which_limits
[i
] = 0; /* Don't set/display any */
279 set_limits
[i
] = RLIM_INFINITY
;
280 /* Get current resource values */
281 if (getrlimit(i
, &limits
[i
]) < 0) {
282 limits
[i
].rlim_cur
= -1;
283 limits
[i
].rlim_max
= -1;
288 while ((ch
= getopt(argc
, argv
, ":EeC:U:BSHabc:d:f:k:l:m:n:s:t:u:v:")) != -1) {
304 if ((pwd
= getpwnam(optarg
)) == NULL
) {
305 if (!isdigit(*optarg
) ||
306 (pwd
= getpwuid(atoi(optarg
))) == NULL
) {
307 warnx("invalid user `%s'", optarg
);
322 case ':': /* Without arg */
323 if ((p
= strchr(rcs_string
, optopt
)) != NULL
) {
324 rcswhich
= p
- rcs_string
;
327 * Backwards compatibility with earlier kernel which might
328 * support fewer resources.
330 if (rcswhich
>= RLIM_NLIMITS
) {
334 if (optarg
&& *optarg
== '-') { /* 'arg' is actually a switch */
335 --optind
; /* back one arg, and make arg NULL */
338 todo
= optarg
== NULL
? RCSSEL
: RCSSET
;
341 which_limits
[rcswhich
] = optarg
? type
: DISPLAYONLY
;
342 set_limits
[rcswhich
] = resource_num(rcswhich
, optopt
, optarg
);
353 /* If user was specified, get class from that */
355 lc
= login_getpwclass(pwd
);
356 else if (cls
!= NULL
&& *cls
!= '\0') {
357 lc
= login_getclassbyname(cls
, NULL
);
358 if (lc
== NULL
|| strcmp(cls
, lc
->lc_class
) != 0)
359 fprintf(stderr
, "login class '%s' non-existent, using %s\n",
360 cls
, lc
?lc
->lc_class
:"current settings");
363 /* If we have a login class, update resource table from that */
365 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
369 /* current value overridden by resourcename or resourcename-cur */
370 sprintf(str
, "%s-cur", resources
[rcswhich
].cap
);
371 val
= resources
[rcswhich
].func(lc
, resources
[rcswhich
].cap
, limits
[rcswhich
].rlim_cur
, limits
[rcswhich
].rlim_cur
);
372 limits
[rcswhich
].rlim_cur
= resources
[rcswhich
].func(lc
, str
, val
, val
);
373 /* maximum value overridden by resourcename or resourcename-max */
374 sprintf(str
, "%s-max", resources
[rcswhich
].cap
);
375 val
= resources
[rcswhich
].func(lc
, resources
[rcswhich
].cap
, limits
[rcswhich
].rlim_max
, limits
[rcswhich
].rlim_max
);
376 limits
[rcswhich
].rlim_max
= resources
[rcswhich
].func(lc
, str
, val
, val
);
380 /* now, let's determine what we wish to do with all this */
384 /* If we're setting limits or doing an eval (ie. we're not just
385 * displaying), then check that hard limits are not lower than
386 * soft limits, and force rasing the hard limit if we need to if
387 * we are raising the soft limit, or lower the soft limit if we
388 * are lowering the hard limit.
390 if ((*argv
|| doeval
) && getuid() == 0) {
392 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
393 if (limits
[rcswhich
].rlim_max
!= RLIM_INFINITY
) {
394 if (limits
[rcswhich
].rlim_cur
== RLIM_INFINITY
) {
395 limits
[rcswhich
].rlim_max
= RLIM_INFINITY
;
396 which_limits
[rcswhich
] |= HARD
;
397 } else if (limits
[rcswhich
].rlim_cur
> limits
[rcswhich
].rlim_max
) {
398 if (which_limits
[rcswhich
] == SOFT
) {
399 limits
[rcswhich
].rlim_max
= limits
[rcswhich
].rlim_cur
;
400 which_limits
[rcswhich
] |= HARD
;
401 } else if (which_limits
[rcswhich
] == HARD
) {
402 limits
[rcswhich
].rlim_cur
= limits
[rcswhich
].rlim_max
;
403 which_limits
[rcswhich
] |= SOFT
;
405 /* else.. if we're specifically setting both to
406 * silly values, then let it error out.
414 /* See if we've overridden anything specific on the command line */
415 if (num_limits
&& todo
== RCSSET
) {
416 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
417 if (which_limits
[rcswhich
] & HARD
)
418 limits
[rcswhich
].rlim_max
= set_limits
[rcswhich
];
419 if (which_limits
[rcswhich
] & SOFT
)
420 limits
[rcswhich
].rlim_cur
= set_limits
[rcswhich
];
424 /* If *argv is not NULL, then we are being asked to
425 * (perhaps) set environment variables and run a program
429 warnx("-e cannot be used with `cmd' option");
435 /* set leading environment variables, like eval(1) */
436 while (*argv
&& (p
= strchr(*argv
, '='))) {
438 if (setenv(*argv
++, p
+ 1, 1) == -1)
439 err(1, "setenv: cannot set %s=%s", *argv
, p
+ 1);
444 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
445 if (doall
|| num_limits
== 0 || which_limits
[rcswhich
] != 0)
446 if (setrlimit(rcswhich
, &limits
[rcswhich
]) == -1)
447 err(1, "setrlimit %s", resources
[rcswhich
].cap
);
457 shelltype
= doeval
? getshelltype() : SH_NONE
;
459 if (type
== ANY
) /* Default to soft limits */
463 printf(shellparm
[shelltype
].cmd
,
464 lc
? " for class " : " (current)",
465 lc
? lc
->lc_class
: "");
467 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
468 if (doall
|| num_limits
== 0 || which_limits
[rcswhich
] != 0) {
469 if (which_limits
[rcswhich
] == ANY
|| which_limits
[rcswhich
])
470 which_limits
[rcswhich
] = type
;
471 if (shellparm
[shelltype
].lprm
[rcswhich
].pfx
) {
472 if (shellparm
[shelltype
].both
&& limits
[rcswhich
].rlim_cur
== limits
[rcswhich
].rlim_max
) {
473 print_limit(limits
[rcswhich
].rlim_max
,
474 shellparm
[shelltype
].lprm
[rcswhich
].divisor
,
475 shellparm
[shelltype
].inf
,
476 shellparm
[shelltype
].lprm
[rcswhich
].pfx
,
477 shellparm
[shelltype
].lprm
[rcswhich
].sfx
,
478 shellparm
[shelltype
].both
);
480 if (which_limits
[rcswhich
] & HARD
) {
481 print_limit(limits
[rcswhich
].rlim_max
,
482 shellparm
[shelltype
].lprm
[rcswhich
].divisor
,
483 shellparm
[shelltype
].inf
,
484 shellparm
[shelltype
].lprm
[rcswhich
].pfx
,
485 shellparm
[shelltype
].lprm
[rcswhich
].sfx
,
486 shellparm
[shelltype
].hard
);
488 if (which_limits
[rcswhich
] & SOFT
) {
489 print_limit(limits
[rcswhich
].rlim_cur
,
490 shellparm
[shelltype
].lprm
[rcswhich
].divisor
,
491 shellparm
[shelltype
].inf
,
492 shellparm
[shelltype
].lprm
[rcswhich
].pfx
,
493 shellparm
[shelltype
].lprm
[rcswhich
].sfx
,
494 shellparm
[shelltype
].soft
);
509 (void)fprintf(stderr
,
510 "usage: limits [-C class|-U user] [-eaSHBE] [-bcdflmnstuvk [val]] [[name=val ...] cmd]\n");
515 print_limit(rlim_t limit
, unsigned divisor
, const char * inf
, const char * pfx
, const char * sfx
, const char * which
)
519 if (limit
== RLIM_INFINITY
)
522 sprintf(numbr
, "%jd", (intmax_t)((limit
+ divisor
/2) / divisor
));
523 printf(pfx
, which
, numbr
);
530 resource_num(int which
, int ch
, const char *str
)
532 rlim_t res
= RLIM_INFINITY
;
535 !(strcasecmp(str
, "inf") == 0 ||
536 strcasecmp(str
, "infinity") == 0 ||
537 strcasecmp(str
, "unlimit") == 0 ||
538 strcasecmp(str
, "unlimited") == 0)) {
539 const char * s
= str
;
543 case RLIMIT_CPU
: /* time values */
547 rlim_t tim
= strtoq(s
, &e
, 0);
548 if (e
== NULL
|| e
== s
|| errno
)
551 case 0: /* end of string */
554 case 's': case 'S': /* seconds */
556 case 'm': case 'M': /* minutes */
559 case 'h': case 'H': /* hours */
562 case 'd': case 'D': /* days */
563 tim
*= (60L * 60L * 24L);
565 case 'w': case 'W': /* weeks */
566 tim
*= (60L * 60L * 24L * 7L);
567 case 'y': case 'Y': /* Years */
568 tim
*= (60L * 60L * 24L * 365L);
574 case RLIMIT_FSIZE
: /* Size values */
584 rlim_t mult
, tim
= strtoq(s
, &e
, 0);
585 if (e
== NULL
|| e
== s
|| errno
)
588 case 0: /* end of string */
593 case 'b': case 'B': /* 512-byte blocks */
596 case 'k': case 'K': /* 1024-byte Kilobytes */
599 case 'm': case 'M': /* 1024-k kbytes */
602 case 'g': case 'G': /* 1Gbyte */
603 mult
= 1024 * 1024 * 1024;
605 case 't': case 'T': /* 1TBte */
606 mult
= 1024LL * 1024LL * 1024LL * 1024LL;
615 res
= strtoq(s
, &e
, 0);
620 warnx("invalid value -%c `%s'", ch
, str
);
629 getshellbyname(const char * shell
)
633 const char * p
= strrchr(shell
, '/');
635 p
= p
? p
+ 1 : shell
;
636 for (i
= 0; (q
= shellparm
[i
].name
) != NULL
; i
++) {
638 int j
= strcspn(q
, "|");
642 if (strncmp(p
, q
, j
) == 0)
653 * Determine the type of shell our parent process is
654 * This is quite tricky, not 100% reliable and probably
655 * not nearly as thorough as it should be. Basically, this
656 * is a "best guess" only, but hopefully will work in
663 pid_t ppid
= getppid();
668 char procdir
[MAXPATHLEN
], buf
[128];
669 int l
= sprintf(procdir
, "/proc/%ld/", (long)ppid
);
670 char * shell
= getenv("SHELL");
672 if (shell
!= NULL
&& stat(shell
, &st
) != -1) {
675 strcpy(procdir
+l
, "file");
676 /* $SHELL is actual shell? */
677 if (stat(procdir
, &st1
) != -1 && memcmp(&st
, &st1
, sizeof st
) == 0)
678 return getshellbyname(shell
);
680 strcpy(procdir
+l
, "status");
681 if (stat(procdir
, &st
) == 0 && (fp
= fopen(procdir
, "r")) != NULL
) {
682 char * p
= fgets(buf
, sizeof buf
, fp
)==NULL
? NULL
: strtok(buf
, " \t");
685 return getshellbyname(p
);