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.
24 #include <sys/cdefs.h>
25 __FBSDID("$FreeBSD$");
30 #include <sys/types.h>
32 #include <sys/sysctl.h>
34 #include <sys/param.h>
42 #include <login_cap.h>
44 #include <sys/resource.h>
51 SH_BASH
, /* gnu bash */
60 /* eval emitter for popular shells.
61 * Why aren't there any standards here? Most shells support either
62 * the csh 'limit' or sh 'ulimit' command, but each varies just
63 * enough that they aren't very compatible from one to the other.
66 const char * name
; /* Name of shell */
67 const char * inf
; /* Name used for 'unlimited' resource */
68 const char * cmd
; /* Intro text */
69 const char * hard
; /* Hard limit text */
70 const char * soft
; /* Soft limit text */
71 const char * both
; /* Hard+Soft limit text */
79 { "", "infinity", "Resource limits%s%s:\n", "-max", "-cur", "",
81 { " cputime%-4s %8s", " secs\n", 1 },
82 { " filesize%-4s %8s", " kB\n", 1024 },
83 { " datasize%-4s %8s", " kB\n", 1024 },
84 { " stacksize%-4s %8s", " kB\n", 1024 },
85 { " coredumpsize%-4s %8s", " kB\n", 1024 },
86 { " memoryuse%-4s %8s", " kB\n", 1024 },
87 { " memorylocked%-4s %8s", " kB\n", 1024 },
88 { " maxprocesses%-4s %8s", "\n", 1 },
89 { " openfiles%-4s %8s", "\n", 1 },
90 { " sbsize%-4s %8s", " bytes\n", 1 },
91 { " vmemoryuse%-4s %8s", " kB\n", 1024 },
92 { " pseudo-terminals%-4s %8s", "\n", 1 },
93 { " swapuse%-4s %8s", " kB\n", 1024 },
94 { " kqueues%-4s %8s", "\n", 1 },
97 { "sh", "unlimited", "", " -H", " -S", "",
99 { "ulimit%s -t %s", ";\n", 1 },
100 { "ulimit%s -f %s", ";\n", 512 },
101 { "ulimit%s -d %s", ";\n", 1024 },
102 { "ulimit%s -s %s", ";\n", 1024 },
103 { "ulimit%s -c %s", ";\n", 512 },
104 { "ulimit%s -m %s", ";\n", 1024 },
105 { "ulimit%s -l %s", ";\n", 1024 },
106 { "ulimit%s -u %s", ";\n", 1 },
107 { "ulimit%s -n %s", ";\n", 1 },
108 { "ulimit%s -b %s", ";\n", 1 },
109 { "ulimit%s -v %s", ";\n", 1024 },
110 { "ulimit%s -p %s", ";\n", 1 },
111 { "ulimit%s -w %s", ";\n", 1024 },
112 { "ulimit%s -k %s", ";\n", 1 },
115 { "csh", "unlimited", "", " -h", "", NULL
,
117 { "limit%s cputime %s", ";\n", 1 },
118 { "limit%s filesize %s", ";\n", 1024 },
119 { "limit%s datasize %s", ";\n", 1024 },
120 { "limit%s stacksize %s", ";\n", 1024 },
121 { "limit%s coredumpsize %s", ";\n", 1024 },
122 { "limit%s memoryuse %s", ";\n", 1024 },
123 { "limit%s memorylocked %s", ";\n", 1024 },
124 { "limit%s maxproc %s", ";\n", 1 },
125 { "limit%s openfiles %s", ";\n", 1 },
126 { "limit%s sbsize %s", ";\n", 1 },
127 { "limit%s vmemoryuse %s", ";\n", 1024 },
128 { "limit%s pseudoterminals %s", ";\n", 1 },
129 { "limit%s swapsize %s", ";\n", 1024 },
130 { "limit%s kqueues %s", ";\n", 1 },
133 { "bash|bash2", "unlimited", "", " -H", " -S", "",
135 { "ulimit%s -t %s", ";\n", 1 },
136 { "ulimit%s -f %s", ";\n", 1024 },
137 { "ulimit%s -d %s", ";\n", 1024 },
138 { "ulimit%s -s %s", ";\n", 1024 },
139 { "ulimit%s -c %s", ";\n", 1024 },
140 { "ulimit%s -m %s", ";\n", 1024 },
141 { "ulimit%s -l %s", ";\n", 1024 },
142 { "ulimit%s -u %s", ";\n", 1 },
143 { "ulimit%s -n %s", ";\n", 1 },
144 { "ulimit%s -b %s", ";\n", 1 },
145 { "ulimit%s -v %s", ";\n", 1024 },
146 { "ulimit%s -p %s", ";\n", 1 },
147 { "ulimit%s -w %s", ";\n", 1024 }
150 { "tcsh", "unlimited", "", " -h", "", NULL
,
152 { "limit%s cputime %s", ";\n", 1 },
153 { "limit%s filesize %s", ";\n", 1024 },
154 { "limit%s datasize %s", ";\n", 1024 },
155 { "limit%s stacksize %s", ";\n", 1024 },
156 { "limit%s coredumpsize %s", ";\n", 1024 },
157 { "limit%s memoryuse %s", ";\n", 1024 },
158 { "limit%s memorylocked %s", ";\n", 1024 },
159 { "limit%s maxproc %s", ";\n", 1 },
160 { "limit%s descriptors %s", ";\n", 1 },
161 { "limit%s sbsize %s", ";\n", 1 },
162 { "limit%s vmemoryuse %s", ";\n", 1024 },
163 { "limit%s pseudoterminals %s", ";\n", 1 },
164 { "limit%s swapsize %s", ";\n", 1024 },
165 { "limit%s kqueues %s", ";\n", 1 },
168 { "ksh|pdksh", "unlimited", "", " -H", " -S", "",
170 { "ulimit%s -t %s", ";\n", 1 },
171 { "ulimit%s -f %s", ";\n", 512 },
172 { "ulimit%s -d %s", ";\n", 1024 },
173 { "ulimit%s -s %s", ";\n", 1024 },
174 { "ulimit%s -c %s", ";\n", 512 },
175 { "ulimit%s -m %s", ";\n", 1024 },
176 { "ulimit%s -l %s", ";\n", 1024 },
177 { "ulimit%s -p %s", ";\n", 1 },
178 { "ulimit%s -n %s", ";\n", 1 },
179 { "ulimit%s -b %s", ";\n", 1 },
180 { "ulimit%s -v %s", ";\n", 1024 },
181 { "ulimit%s -p %s", ";\n", 1 },
182 { "ulimit%s -w %s", ";\n", 1024 }
185 { "zsh", "unlimited", "", " -H", " -S", "",
187 { "ulimit%s -t %s", ";\n", 1 },
188 { "ulimit%s -f %s", ";\n", 512 },
189 { "ulimit%s -d %s", ";\n", 1024 },
190 { "ulimit%s -s %s", ";\n", 1024 },
191 { "ulimit%s -c %s", ";\n", 512 },
192 { "ulimit%s -m %s", ";\n", 1024 },
193 { "ulimit%s -l %s", ";\n", 1024 },
194 { "ulimit%s -u %s", ";\n", 1 },
195 { "ulimit%s -n %s", ";\n", 1 },
196 { "ulimit%s -b %s", ";\n", 1 },
197 { "ulimit%s -v %s", ";\n", 1024 },
198 { "ulimit%s -p %s", ";\n", 1 },
199 { "ulimit%s -w %s", ";\n", 1024 }
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 { "limit%s pseudoterminals %s", ";\n", 1 },
216 { "limit%s swapuse %s", ";\n", 1024 }
219 { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
226 rlim_t (*func
)(login_cap_t
*, const char *, rlim_t
, rlim_t
);
227 } resources
[RLIM_NLIMITS
] = {
228 { "cputime", login_getcaptime
},
229 { "filesize", login_getcapsize
},
230 { "datasize", login_getcapsize
},
231 { "stacksize", login_getcapsize
},
232 { "coredumpsize", login_getcapsize
},
233 { "memoryuse", login_getcapsize
},
234 { "memorylocked", login_getcapsize
},
235 { "maxproc", login_getcapnum
},
236 { "openfiles", login_getcapnum
},
237 { "sbsize", login_getcapsize
},
238 { "vmemoryuse", login_getcapsize
},
239 { "pseudoterminals",login_getcapnum
},
240 { "swapuse", login_getcapsize
},
241 { "kqueues", login_getcapnum
},
245 * One letter for each resource levels.
246 * NOTE: There is a dependency on the corresponding
247 * letter index being equal to the resource number.
248 * If sys/resource.h defines are changed, this needs
249 * to be modified accordingly!
252 #define RCS_STRING "tfdscmlunbvpwk"
254 static rlim_t
resource_num(int which
, int ch
, const char *str
);
255 static void usage(void);
256 static int getshelltype(void);
257 static void print_limit(rlim_t limit
, unsigned divisor
, const char *inf
,
258 const char *pfx
, const char *sfx
, const char *which
);
259 static void getrlimit_proc(pid_t pid
, int resource
, struct rlimit
*rlp
);
260 static void setrlimit_proc(pid_t pid
, int resource
, const struct rlimit
*rlp
);
261 extern char **environ
;
263 static const char rcs_string
[] = RCS_STRING
;
266 main(int argc
, char *argv
[])
268 char *p
, *cls
= NULL
;
270 struct passwd
* pwd
= NULL
;
271 int rcswhich
, shelltype
;
272 int i
, num_limits
= 0;
273 int ch
, doeval
= 0, doall
= 0;
275 login_cap_t
* lc
= NULL
;
276 enum { ANY
=0, SOFT
=1, HARD
=2, BOTH
=3, DISPLAYONLY
=4 } type
= ANY
;
277 enum { RCSUNKNOWN
=0, RCSSET
=1, RCSSEL
=2 } todo
= RCSUNKNOWN
;
278 int which_limits
[RLIM_NLIMITS
];
279 rlim_t set_limits
[RLIM_NLIMITS
];
280 struct rlimit limits
[RLIM_NLIMITS
];
283 /* init resource tables */
284 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
285 which_limits
[i
] = 0; /* Don't set/display any */
286 set_limits
[i
] = RLIM_INFINITY
;
291 while ((ch
= getopt(argc
, argv
,
292 ":EeC:U:BSHP:ab:c:d:f:l:m:n:s:t:u:v:p:w:k:")) != -1) {
308 if ((pwd
= getpwnam(optarg
)) == NULL
) {
309 if (!isdigit(*optarg
) ||
310 (pwd
= getpwuid(atoi(optarg
))) == NULL
) {
311 warnx("invalid user `%s'", optarg
);
326 if (!isdigit(*optarg
) || (pid
= atoi(optarg
)) < 0) {
327 warnx("invalid pid `%s'", optarg
);
332 case ':': /* Without arg */
333 if ((p
= strchr(rcs_string
, optopt
)) != NULL
) {
334 int rcswhich1
= p
- rcs_string
;
335 if (optarg
&& *optarg
== '-') { /* 'arg' is actually a switch */
336 --optind
; /* back one arg, and make arg NULL */
339 todo
= optarg
== NULL
? RCSSEL
: RCSSET
;
342 which_limits
[rcswhich1
] = optarg
? type
: DISPLAYONLY
;
343 set_limits
[rcswhich1
] = resource_num(rcswhich1
, optopt
, optarg
);
356 warnx("-C cannot be used with -P option");
360 warnx("-U cannot be used with -P option");
365 /* Get current resource values */
367 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
369 getrlimit(i
, &limits
[i
]);
370 } else if (doall
|| num_limits
== 0) {
371 getrlimit_proc(pid
, i
, &limits
[i
]);
372 } else if (which_limits
[i
] != 0) {
373 getrlimit_proc(pid
, i
, &limits
[i
]);
378 /* If user was specified, get class from that */
380 lc
= login_getpwclass(pwd
);
381 else if (cls
!= NULL
&& *cls
!= '\0') {
382 lc
= login_getclassbyname(cls
, NULL
);
383 if (lc
== NULL
|| strcmp(cls
, lc
->lc_class
) != 0)
384 fprintf(stderr
, "login class '%s' non-existent, using %s\n",
385 cls
, lc
?lc
->lc_class
:"current settings");
388 /* If we have a login class, update resource table from that */
390 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
394 /* current value overridden by resourcename or resourcename-cur */
395 sprintf(str
, "%s-cur", resources
[rcswhich
].cap
);
396 val
= resources
[rcswhich
].func(lc
, resources
[rcswhich
].cap
, limits
[rcswhich
].rlim_cur
, limits
[rcswhich
].rlim_cur
);
397 limits
[rcswhich
].rlim_cur
= resources
[rcswhich
].func(lc
, str
, val
, val
);
398 /* maximum value overridden by resourcename or resourcename-max */
399 sprintf(str
, "%s-max", resources
[rcswhich
].cap
);
400 val
= resources
[rcswhich
].func(lc
, resources
[rcswhich
].cap
, limits
[rcswhich
].rlim_max
, limits
[rcswhich
].rlim_max
);
401 limits
[rcswhich
].rlim_max
= resources
[rcswhich
].func(lc
, str
, val
, val
);
405 /* now, let's determine what we wish to do with all this */
409 /* If we're setting limits or doing an eval (ie. we're not just
410 * displaying), then check that hard limits are not lower than
411 * soft limits, and force rasing the hard limit if we need to if
412 * we are raising the soft limit, or lower the soft limit if we
413 * are lowering the hard limit.
415 if ((*argv
|| doeval
) && getuid() == 0) {
417 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
418 if (limits
[rcswhich
].rlim_max
!= RLIM_INFINITY
) {
419 if (limits
[rcswhich
].rlim_cur
== RLIM_INFINITY
) {
420 limits
[rcswhich
].rlim_max
= RLIM_INFINITY
;
421 which_limits
[rcswhich
] |= HARD
;
422 } else if (limits
[rcswhich
].rlim_cur
> limits
[rcswhich
].rlim_max
) {
423 if (which_limits
[rcswhich
] == SOFT
) {
424 limits
[rcswhich
].rlim_max
= limits
[rcswhich
].rlim_cur
;
425 which_limits
[rcswhich
] |= HARD
;
426 } else if (which_limits
[rcswhich
] == HARD
) {
427 limits
[rcswhich
].rlim_cur
= limits
[rcswhich
].rlim_max
;
428 which_limits
[rcswhich
] |= SOFT
;
430 /* else.. if we're specifically setting both to
431 * silly values, then let it error out.
439 /* See if we've overridden anything specific on the command line */
440 if (num_limits
&& todo
== RCSSET
) {
441 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
442 if (which_limits
[rcswhich
] & HARD
)
443 limits
[rcswhich
].rlim_max
= set_limits
[rcswhich
];
444 if (which_limits
[rcswhich
] & SOFT
)
445 limits
[rcswhich
].rlim_cur
= set_limits
[rcswhich
];
449 /* If *argv is not NULL, then we are being asked to
450 * (perhaps) set environment variables and run a program
454 warnx("-e cannot be used with `cmd' option");
458 warnx("-P cannot be used with `cmd' option");
464 /* set leading environment variables, like eval(1) */
465 while (*argv
&& (p
= strchr(*argv
, '='))) {
467 rtrn
= setenv(*argv
++, p
+ 1, 1);
470 err(EXIT_FAILURE
, "setenv %s", *argv
);
474 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
475 if (doall
|| num_limits
== 0 || which_limits
[rcswhich
] != 0)
476 if (setrlimit(rcswhich
, &limits
[rcswhich
]) == -1)
477 err(1, "setrlimit %s", resources
[rcswhich
].cap
);
488 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
489 if (which_limits
[rcswhich
] != 0)
490 setrlimit_proc(pid
, rcswhich
, &limits
[rcswhich
]);
495 shelltype
= doeval
? getshelltype() : SH_NONE
;
497 if (type
== ANY
) /* Default to soft limits */
501 printf(shellparm
[shelltype
].cmd
,
502 lc
? " for class " : " (current)",
503 lc
? lc
->lc_class
: "");
505 for (rcswhich
= 0; rcswhich
< RLIM_NLIMITS
; rcswhich
++) {
506 if (doall
|| num_limits
== 0 || which_limits
[rcswhich
] != 0) {
507 if (which_limits
[rcswhich
] == ANY
|| which_limits
[rcswhich
])
508 which_limits
[rcswhich
] = type
;
509 if (shellparm
[shelltype
].lprm
[rcswhich
].pfx
) {
510 if (shellparm
[shelltype
].both
&& limits
[rcswhich
].rlim_cur
== limits
[rcswhich
].rlim_max
) {
511 print_limit(limits
[rcswhich
].rlim_max
,
512 shellparm
[shelltype
].lprm
[rcswhich
].divisor
,
513 shellparm
[shelltype
].inf
,
514 shellparm
[shelltype
].lprm
[rcswhich
].pfx
,
515 shellparm
[shelltype
].lprm
[rcswhich
].sfx
,
516 shellparm
[shelltype
].both
);
518 if (which_limits
[rcswhich
] & HARD
) {
519 print_limit(limits
[rcswhich
].rlim_max
,
520 shellparm
[shelltype
].lprm
[rcswhich
].divisor
,
521 shellparm
[shelltype
].inf
,
522 shellparm
[shelltype
].lprm
[rcswhich
].pfx
,
523 shellparm
[shelltype
].lprm
[rcswhich
].sfx
,
524 shellparm
[shelltype
].hard
);
526 if (which_limits
[rcswhich
] & SOFT
) {
527 print_limit(limits
[rcswhich
].rlim_cur
,
528 shellparm
[shelltype
].lprm
[rcswhich
].divisor
,
529 shellparm
[shelltype
].inf
,
530 shellparm
[shelltype
].lprm
[rcswhich
].pfx
,
531 shellparm
[shelltype
].lprm
[rcswhich
].sfx
,
532 shellparm
[shelltype
].soft
);
547 (void)fprintf(stderr
,
548 "usage: limits [-C class|-P pid|-U user] [-eaSHBE] "
549 "[-bcdflmnstuvpwk [val]] [[name=val ...] cmd]\n");
554 print_limit(rlim_t limit
, unsigned divisor
, const char * inf
, const char * pfx
, const char * sfx
, const char * which
)
558 if (limit
== RLIM_INFINITY
)
561 sprintf(numbr
, "%jd", (intmax_t)((limit
+ divisor
/2) / divisor
));
562 printf(pfx
, which
, numbr
);
569 resource_num(int which
, int ch
, const char *str
)
571 rlim_t res
= RLIM_INFINITY
;
574 !(strcasecmp(str
, "inf") == 0 ||
575 strcasecmp(str
, "infinity") == 0 ||
576 strcasecmp(str
, "unlimit") == 0 ||
577 strcasecmp(str
, "unlimited") == 0)) {
578 const char * s
= str
;
582 case RLIMIT_CPU
: /* time values */
586 rlim_t tim
= strtoq(s
, &e
, 0);
587 if (e
== NULL
|| e
== s
|| errno
)
590 case 0: /* end of string */
593 case 's': case 'S': /* seconds */
595 case 'm': case 'M': /* minutes */
598 case 'h': case 'H': /* hours */
601 case 'd': case 'D': /* days */
602 tim
*= (60L * 60L * 24L);
604 case 'w': case 'W': /* weeks */
605 tim
*= (60L * 60L * 24L * 7L);
606 case 'y': case 'Y': /* Years */
607 tim
*= (60L * 60L * 24L * 365L);
613 case RLIMIT_FSIZE
: /* Size values */
625 rlim_t mult
, tim
= strtoq(s
, &e
, 0);
626 if (e
== NULL
|| e
== s
|| errno
)
629 case 0: /* end of string */
634 case 'b': case 'B': /* 512-byte blocks */
637 case 'k': case 'K': /* 1024-byte Kilobytes */
640 case 'm': case 'M': /* 1024-k kbytes */
643 case 'g': case 'G': /* 1Gbyte */
644 mult
= 1024 * 1024 * 1024;
646 case 't': case 'T': /* 1TBte */
647 mult
= 1024LL * 1024LL * 1024LL * 1024LL;
658 res
= strtoq(s
, &e
, 0);
663 warnx("invalid value -%c `%s'", ch
, str
);
672 getshellbyname(const char * shell
)
676 const char * p
= strrchr(shell
, '/');
679 for (i
= 0; (q
= shellparm
[i
].name
) != NULL
; i
++) {
681 int j
= strcspn(q
, "|");
685 if (strncmp(p
, q
, j
) == 0)
696 * Determine the type of shell our parent process is
697 * This is quite tricky, not 100% reliable and probably
698 * not nearly as thorough as it should be. Basically, this
699 * is a "best guess" only, but hopefully will work in
706 pid_t ppid
= getppid();
709 struct kinfo_proc kp
;
711 char path
[MAXPATHLEN
];
712 char * shell
= getenv("SHELL");
720 if (shell
!= NULL
&& stat(shell
, &st
) != -1) {
723 mib
[2] = KERN_PROC_PATHNAME
;
725 if (sysctl(mib
, 4, path
, &len
, NULL
, 0) != -1) {
726 /* $SHELL is actual shell? */
727 if (stat(path
, &st1
) != -1 && memcmp(&st
, &st1
, sizeof st
) == 0)
728 return getshellbyname(shell
);
731 mib
[2] = KERN_PROC_PID
;
733 if (sysctl(mib
, 4, &kp
, &len
, NULL
, 0) != -1)
734 return getshellbyname(kp
.ki_comm
);
740 getrlimit_proc(pid_t pid
, int resource
, struct rlimit
*rlp
)
748 name
[2] = KERN_PROC_RLIMIT
;
752 error
= sysctl(name
, 5, rlp
, &len
, NULL
, 0);
754 err(EXIT_FAILURE
, "sysctl: kern.proc.rlimit: %d", pid
);
755 if (len
!= sizeof(*rlp
))
756 errx(EXIT_FAILURE
, "sysctl() returns wrong size");
760 setrlimit_proc(pid_t pid
, int resource
, const struct rlimit
*rlp
)
767 name
[2] = KERN_PROC_RLIMIT
;
770 error
= sysctl(name
, 5, NULL
, 0, rlp
, sizeof(*rlp
));
772 err(EXIT_FAILURE
, "sysctl: kern.proc.rlimit: %d", pid
);