|
1 //rlimits.c - resource limit builtins |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1992-1997 Paul Falstad |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Paul Falstad or the Zsh Development Group be liable |
|
18 * to any party for direct, indirect, special, incidental, or consequential |
|
19 * damages arising out of the use of this software and its documentation, |
|
20 * even if Paul Falstad and the Zsh Development Group have been advised of |
|
21 * the possibility of such damage. |
|
22 * |
|
23 * Paul Falstad and the Zsh Development Group specifically disclaim any |
|
24 * warranties, including, but not limited to, the implied warranties of |
|
25 * merchantability and fitness for a particular purpose. The software |
|
26 * provided hereunder is on an "as is" basis, and Paul Falstad and the |
|
27 * Zsh Development Group have no obligation to provide maintenance, |
|
28 * support, updates, enhancements, or modifications. |
|
29 * |
|
30 */ |
|
31 #include "rlimits.mdh" |
|
32 #include "rlimits.pro" |
|
33 |
|
34 #ifdef __SYMBIAN32__ |
|
35 #include "dummy.h" |
|
36 #endif //__SYMBIAN32__ |
|
37 |
|
38 #ifdef __SYMBIAN32__ |
|
39 #ifdef __WINSCW__ |
|
40 #pragma warn_unusedarg off |
|
41 #endif//__WINSCW__ |
|
42 #endif//__SYMBIAN32__ |
|
43 |
|
44 #if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY) |
|
45 |
|
46 enum { |
|
47 ZLIMTYPE_MEMORY, |
|
48 ZLIMTYPE_NUMBER, |
|
49 ZLIMTYPE_TIME, |
|
50 ZLIMTYPE_UNKNOWN |
|
51 }; |
|
52 |
|
53 /* Generated rec array containing limits required for the limit builtin. * |
|
54 * They must appear in this array in numerical order of the RLIMIT_* macros. */ |
|
55 |
|
56 # include "rlimits.h" |
|
57 |
|
58 static rlim_t |
|
59 zstrtorlimt(const char *s, char **t, int base) |
|
60 { |
|
61 rlim_t ret = 0; |
|
62 |
|
63 if (strcmp(s, "unlimited") == 0) { |
|
64 if (t) |
|
65 *t = (char *) s + 9; |
|
66 return RLIM_INFINITY; |
|
67 } |
|
68 # if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED) |
|
69 if (!base) { |
|
70 if (*s != '0') |
|
71 base = 10; |
|
72 else if (*++s == 'x' || *s == 'X') |
|
73 base = 16, s++; |
|
74 else |
|
75 base = 8; |
|
76 } |
|
77 if (base <= 10) |
|
78 for (; *s >= '0' && *s < ('0' + base); s++) |
|
79 ret = ret * base + *s - '0'; |
|
80 else |
|
81 for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) |
|
82 || (*s >= 'A' && *s < ('A' + base - 10)); s++) |
|
83 ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); |
|
84 if (t) |
|
85 *t = (char *)s; |
|
86 # else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */ |
|
87 ret = zstrtol(s, t, base); |
|
88 # endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */ |
|
89 return ret; |
|
90 } |
|
91 |
|
92 /**/ |
|
93 static void |
|
94 showlimitvalue(int lim, rlim_t val) |
|
95 { |
|
96 /* display limit for resource number lim */ |
|
97 if (lim < ZSH_NLIMITS) |
|
98 printf("%-16s", recs[lim]); |
|
99 else |
|
100 { |
|
101 /* Unknown limit, hence unknown units. */ |
|
102 printf("%-16d", lim); |
|
103 } |
|
104 if (val == RLIM_INFINITY) |
|
105 printf("unlimited\n"); |
|
106 else if (lim >= ZSH_NLIMITS) |
|
107 { |
|
108 # ifdef RLIM_T_IS_QUAD_T |
|
109 printf("%qd\n", val); |
|
110 # else |
|
111 # ifdef RLIM_T_IS_LONG_LONG |
|
112 printf("%lld\n", val); |
|
113 # else |
|
114 # ifdef RLIM_T_IS_UNSIGNED |
|
115 printf("%lu\n", val); |
|
116 # else |
|
117 printf("%ld\n", val); |
|
118 # endif /* RLIM_T_IS_UNSIGNED */ |
|
119 # endif /* RLIM_T_IS_LONG_LONG */ |
|
120 # endif /* RLIM_T_IS_QUAD_T */ |
|
121 } |
|
122 else if (limtype[lim] == ZLIMTYPE_TIME) { |
|
123 /* time-type resource -- display as hours, minutes and |
|
124 seconds. */ |
|
125 printf("%d:%02d:%02d\n", (int)(val / 3600), |
|
126 (int)(val / 60) % 60, (int)(val % 60)); |
|
127 } else if (limtype[lim] == ZLIMTYPE_NUMBER || |
|
128 limtype[lim] == ZLIMTYPE_UNKNOWN) { |
|
129 /* pure numeric resource */ |
|
130 printf("%d\n", (int)val); |
|
131 } else if (val >= 1024L * 1024L) |
|
132 /* memory resource -- display with `K' or `M' modifier */ |
|
133 # ifdef RLIM_T_IS_QUAD_T |
|
134 printf("%qdMB\n", val / (1024L * 1024L)); |
|
135 else |
|
136 printf("%qdkB\n", val / 1024L); |
|
137 # else |
|
138 # ifdef RLIM_T_IS_LONG_LONG |
|
139 printf("%lldMB\n", val / (1024L * 1024L)); |
|
140 else |
|
141 printf("%lldkB\n", val / 1024L); |
|
142 # else |
|
143 # ifdef RLIM_T_IS_UNSIGNED |
|
144 printf("%luMB\n", val / (1024L * 1024L)); |
|
145 else |
|
146 printf("%lukB\n", val / 1024L); |
|
147 # else |
|
148 printf("%ldMB\n", val / (1024L * 1024L)); |
|
149 else |
|
150 printf("%ldkB\n", val / 1024L); |
|
151 # endif /* RLIM_T_IS_UNSIGNED */ |
|
152 # endif /* RLIM_T_IS_LONG_LONG */ |
|
153 # endif /* RLIM_T_IS_QUAD_T */ |
|
154 } |
|
155 |
|
156 /* Display resource limits. hard indicates whether `hard' or `soft' * |
|
157 * limits should be displayed. lim specifies the limit, or may be -1 * |
|
158 * to show all. */ |
|
159 |
|
160 /**/ |
|
161 static int |
|
162 showlimits(char *nam, int hard, int lim) |
|
163 { |
|
164 int rt; |
|
165 |
|
166 if (lim >= ZSH_NLIMITS) |
|
167 { |
|
168 /* |
|
169 * Not configured into the shell. Ask the OS |
|
170 * explicitly for this limit. |
|
171 */ |
|
172 struct rlimit vals; |
|
173 if (getrlimit(lim, &vals) < 0) |
|
174 { |
|
175 zwarnnam(nam, "can't read limit: %e", NULL, errno); |
|
176 return 1; |
|
177 } |
|
178 showlimitvalue(lim, hard ? vals.rlim_max : vals.rlim_cur); |
|
179 } |
|
180 else if (lim != -1) |
|
181 { |
|
182 showlimitvalue(lim, hard ? limits[lim].rlim_max : |
|
183 limits[lim].rlim_cur); |
|
184 } |
|
185 else |
|
186 { |
|
187 /* main loop over resource types */ |
|
188 for (rt = 0; rt != ZSH_NLIMITS; rt++) |
|
189 showlimitvalue(rt, (hard) ? limits[rt].rlim_max : |
|
190 limits[rt].rlim_cur); |
|
191 } |
|
192 |
|
193 return 0; |
|
194 } |
|
195 |
|
196 /* Display a resource limit, in ulimit style. lim specifies which * |
|
197 * limit should be displayed, and hard indicates whether the hard or * |
|
198 * soft limit should be displayed. */ |
|
199 |
|
200 /**/ |
|
201 static int |
|
202 printulimit(char *nam, int lim, int hard, int head) |
|
203 { |
|
204 rlim_t limit; |
|
205 |
|
206 /* get the limit in question */ |
|
207 if (lim >= ZSH_NLIMITS) |
|
208 { |
|
209 struct rlimit vals; |
|
210 |
|
211 if (getrlimit(lim, &vals) < 0) |
|
212 { |
|
213 zwarnnam(nam, "can't read limit: %e", NULL, errno); |
|
214 return 1; |
|
215 } |
|
216 limit = (hard) ? vals.rlim_max : vals.rlim_cur; |
|
217 } |
|
218 else |
|
219 limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur; |
|
220 /* display the appropriate heading */ |
|
221 switch (lim) { |
|
222 case RLIMIT_CORE: |
|
223 if (head) |
|
224 printf("-c: core file size (blocks) "); |
|
225 if (limit != RLIM_INFINITY) |
|
226 limit /= 512; |
|
227 break; |
|
228 case RLIMIT_DATA: |
|
229 if (head) |
|
230 printf("-d: data seg size (kbytes) "); |
|
231 if (limit != RLIM_INFINITY) |
|
232 limit /= 1024; |
|
233 break; |
|
234 case RLIMIT_FSIZE: |
|
235 if (head) |
|
236 printf("-f: file size (blocks) "); |
|
237 if (limit != RLIM_INFINITY) |
|
238 limit /= 512; |
|
239 break; |
|
240 # ifdef HAVE_RLIMIT_SIGPENDING |
|
241 case RLIMIT_SIGPENDING: |
|
242 if (head) |
|
243 printf("-i: pending signals "); |
|
244 break; |
|
245 # endif |
|
246 # ifdef HAVE_RLIMIT_MEMLOCK |
|
247 case RLIMIT_MEMLOCK: |
|
248 if (head) |
|
249 printf("-l: locked-in-memory size (kb) "); |
|
250 if (limit != RLIM_INFINITY) |
|
251 limit /= 1024; |
|
252 break; |
|
253 # endif /* HAVE_RLIMIT_MEMLOCK */ |
|
254 /* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid * |
|
255 * duplicate case statement. Observed on QNX Neutrino 6.1.0. */ |
|
256 # if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS) |
|
257 case RLIMIT_RSS: |
|
258 if (head) |
|
259 printf("-m: resident set size (kbytes) "); |
|
260 if (limit != RLIM_INFINITY) |
|
261 limit /= 1024; |
|
262 break; |
|
263 # endif /* HAVE_RLIMIT_RSS */ |
|
264 # if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_RSS) && defined(RLIMIT_VMEM_IS_RSS) |
|
265 case RLIMIT_VMEM: |
|
266 if (head) |
|
267 printf("-m: memory size (kb) "); |
|
268 if (limit != RLIM_INFINITY) |
|
269 limit /= 1024; |
|
270 break; |
|
271 # endif /* HAVE_RLIMIT_VMEM */ |
|
272 # ifdef HAVE_RLIMIT_NOFILE |
|
273 case RLIMIT_NOFILE: |
|
274 if (head) |
|
275 printf("-n: file descriptors "); |
|
276 break; |
|
277 # endif /* HAVE_RLIMIT_NOFILE */ |
|
278 # ifdef HAVE_RLIMIT_MSGQUEUE |
|
279 case RLIMIT_MSGQUEUE: |
|
280 if (head) |
|
281 printf("-q: bytes in POSIX msg queues "); |
|
282 break; |
|
283 # endif |
|
284 case RLIMIT_STACK: |
|
285 if (head) |
|
286 printf("-s: stack size (kbytes) "); |
|
287 if (limit != RLIM_INFINITY) |
|
288 limit /= 1024; |
|
289 break; |
|
290 case RLIMIT_CPU: |
|
291 if (head) |
|
292 printf("-t: cpu time (seconds) "); |
|
293 break; |
|
294 # ifdef HAVE_RLIMIT_NPROC |
|
295 case RLIMIT_NPROC: |
|
296 if (head) |
|
297 printf("-u: processes "); |
|
298 break; |
|
299 # endif /* HAVE_RLIMIT_NPROC */ |
|
300 # if defined(HAVE_RLIMIT_VMEM) && (!defined(HAVE_RLIMIT_RSS) || !defined(RLIMIT_VMEM_IS_RSS)) |
|
301 case RLIMIT_VMEM: |
|
302 if (head) |
|
303 printf("-v: virtual memory size (kb) "); |
|
304 if (limit != RLIM_INFINITY) |
|
305 limit /= 1024; |
|
306 break; |
|
307 # endif /* HAVE_RLIMIT_VMEM */ |
|
308 # if defined HAVE_RLIMIT_AS && !defined(RLIMIT_VMEM_IS_AS) |
|
309 case RLIMIT_AS: |
|
310 if (head) |
|
311 printf("-v: address space (kb) "); |
|
312 if (limit != RLIM_INFINITY) |
|
313 limit /= 1024; |
|
314 break; |
|
315 # endif /* HAVE_RLIMIT_AS */ |
|
316 # ifdef HAVE_RLIMIT_LOCKS |
|
317 case RLIMIT_LOCKS: |
|
318 if (head) |
|
319 printf("-x: file locks "); |
|
320 break; |
|
321 # endif /* HAVE_RLIMIT_LOCKS */ |
|
322 # ifdef HAVE_RLIMIT_AIO_MEM |
|
323 case RLIMIT_AIO_MEM: |
|
324 if (head) |
|
325 printf("-N %2d: AIO locked-in-memory (kb) ", RLIMIT_AIO_MEM); |
|
326 if (limit != RLIM_INFINITY) |
|
327 limit /= 1024; |
|
328 break; |
|
329 # endif /* HAVE_RLIMIT_AIO_MEM */ |
|
330 # ifdef HAVE_RLIMIT_AIO_OPS |
|
331 case RLIMIT_AIO_OPS: |
|
332 if (head) |
|
333 printf("-N %2d: AIO operations ", RLIMIT_AIO_OPS); |
|
334 break; |
|
335 # endif /* HAVE_RLIMIT_AIO_OPS */ |
|
336 # ifdef HAVE_RLIMIT_TCACHE |
|
337 case RLIMIT_TCACHE: |
|
338 if (head) |
|
339 printf("-N %2d: cached threads ", RLIMIT_TCACHE); |
|
340 break; |
|
341 # endif /* HAVE_RLIMIT_TCACHE */ |
|
342 # ifdef HAVE_RLIMIT_SBSIZE |
|
343 case RLIMIT_SBSIZE: |
|
344 if (head) |
|
345 printf("-N %2d: socket buffer size (kb) ", RLIMIT_SBSIZE); |
|
346 if (limit != RLIM_INFINITY) |
|
347 limit /= 1024; |
|
348 break; |
|
349 # endif /* HAVE_RLIMIT_SBSIZE */ |
|
350 # ifdef HAVE_RLIMIT_PTHREAD |
|
351 case RLIMIT_PTHREAD: |
|
352 if (head) |
|
353 printf("-N %2d: threads per process ", RLIMIT_PTHREAD); |
|
354 break; |
|
355 # endif /* HAVE_RLIMIT_PTHREAD */ |
|
356 default: |
|
357 if (head) |
|
358 printf("-N %2d: ", lim); |
|
359 break; |
|
360 } |
|
361 /* display the limit */ |
|
362 if (limit == RLIM_INFINITY) |
|
363 printf("unlimited\n"); |
|
364 else { |
|
365 # ifdef RLIM_T_IS_QUAD_T |
|
366 printf("%qd\n", limit); |
|
367 # else |
|
368 # ifdef RLIM_T_IS_LONG_LONG |
|
369 printf("%lld\n", limit); |
|
370 # else |
|
371 # ifdef RLIM_T_IS_UNSIGNED |
|
372 printf("%lu\n", limit); |
|
373 # else |
|
374 printf("%ld\n", limit); |
|
375 # endif /* RLIM_T_IS_UNSIGNED */ |
|
376 # endif /* RLIM_T_IS_LONG_LONG */ |
|
377 # endif /* RLIM_T_IS_QUAD_T */ |
|
378 } |
|
379 |
|
380 return 0; |
|
381 } |
|
382 |
|
383 /**/ |
|
384 static int |
|
385 do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set) |
|
386 { |
|
387 if (lim >= ZSH_NLIMITS) { |
|
388 struct rlimit vals; |
|
389 if (getrlimit(lim, &vals) < 0) |
|
390 { |
|
391 /* best guess about error */ |
|
392 zwarnnam(nam, "can't read limit: %e", NULL, errno); |
|
393 return 1; |
|
394 } |
|
395 if (hard) |
|
396 { |
|
397 if (val > vals.rlim_max && geteuid()) { |
|
398 zwarnnam(nam, "can't raise hard limits", NULL, 0); |
|
399 return 1; |
|
400 } |
|
401 vals.rlim_max = val; |
|
402 /* |
|
403 * not show if all systems will do this silently, but |
|
404 * best be safe... |
|
405 */ |
|
406 if (val < vals.rlim_cur) |
|
407 vals.rlim_cur = val; |
|
408 } |
|
409 if (soft || !hard) { |
|
410 if (val > vals.rlim_max) { |
|
411 zwarnnam(nam, "limit exceeds hard limit", NULL, 0); |
|
412 return 1; |
|
413 } |
|
414 else |
|
415 vals.rlim_cur = val; |
|
416 } |
|
417 if (!set) |
|
418 { |
|
419 zwarnnam(nam, |
|
420 "warning: unrecognised limit %d, use -s to set", |
|
421 NULL, lim); |
|
422 return 1; |
|
423 } |
|
424 else if (setrlimit(lim, &vals) < 0) |
|
425 { |
|
426 zwarnnam(nam, "setrlimit failed: %e", NULL, errno); |
|
427 return 1; |
|
428 } |
|
429 } else { |
|
430 /* new limit is valid and has been interpreted; apply it to the |
|
431 specified resource */ |
|
432 if (hard) { |
|
433 /* can only raise hard limits if running as root */ |
|
434 if (val > current_limits[lim].rlim_max && geteuid()) { |
|
435 zwarnnam(nam, "can't raise hard limits", NULL, 0); |
|
436 return 1; |
|
437 } else { |
|
438 limits[lim].rlim_max = val; |
|
439 if (val < limits[lim].rlim_cur) |
|
440 limits[lim].rlim_cur = val; |
|
441 } |
|
442 } |
|
443 if (soft || !hard) { |
|
444 if (val > limits[lim].rlim_max) { |
|
445 /* no idea about this difference, don't intend to worry */ |
|
446 if (*nam == 'u') |
|
447 { |
|
448 /* ulimit does this */ |
|
449 if (val > current_limits[lim].rlim_max && geteuid()) { |
|
450 zwarnnam(nam, "value exceeds hard limit", NULL, 0); |
|
451 return 1; |
|
452 } |
|
453 limits[lim].rlim_max = limits[lim].rlim_cur = val; |
|
454 } else { |
|
455 /* but limit does this */ |
|
456 zwarnnam(nam, "limit exceeds hard limit", NULL, 0); |
|
457 return 1; |
|
458 } |
|
459 } else |
|
460 limits[lim].rlim_cur = val; |
|
461 if (set && zsetlimit(lim, "limit")) |
|
462 return 1; |
|
463 } |
|
464 } |
|
465 return 0; |
|
466 } |
|
467 |
|
468 /* limit: set or show resource limits. The variable hard indicates * |
|
469 * whether `hard' or `soft' resource limits are being set/shown. */ |
|
470 |
|
471 /**/ |
|
472 static int |
|
473 bin_limit(char *nam, char **argv, Options ops, UNUSED(int func)) |
|
474 { |
|
475 char *s; |
|
476 int hard, limnum, lim; |
|
477 rlim_t val; |
|
478 int ret = 0; |
|
479 |
|
480 hard = OPT_ISSET(ops,'h'); |
|
481 if (OPT_ISSET(ops,'s') && !*argv) |
|
482 return setlimits(NULL); |
|
483 /* without arguments, display limits */ |
|
484 if (!*argv) |
|
485 return showlimits(nam, hard, -1); |
|
486 while ((s = *argv++)) { |
|
487 /* Search for the appropriate resource name. When a name matches (i.e. * |
|
488 * starts with) the argument, the lim variable changes from -1 to the * |
|
489 * number of the resource. If another match is found, lim goes to -2. */ |
|
490 if (idigit(*s)) |
|
491 { |
|
492 lim = (int)zstrtol(s, NULL, 10); |
|
493 } |
|
494 else |
|
495 for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) |
|
496 if (!strncmp(recs[limnum], s, strlen(s))) { |
|
497 if (lim != -1) |
|
498 lim = -2; |
|
499 else |
|
500 lim = limnum; |
|
501 } |
|
502 /* lim==-1 indicates that no matches were found. * |
|
503 * lim==-2 indicates that multiple matches were found. */ |
|
504 if (lim < 0) { |
|
505 zwarnnam(nam, |
|
506 (lim == -2) ? "ambiguous resource specification: %s" |
|
507 : "no such resource: %s", s, 0); |
|
508 return 1; |
|
509 } |
|
510 /* without value for limit, display the current limit */ |
|
511 if (!(s = *argv++)) |
|
512 return showlimits(nam, hard, lim); |
|
513 if (lim >= ZSH_NLIMITS) |
|
514 { |
|
515 val = zstrtorlimt(s, &s, 10); |
|
516 if (*s) |
|
517 { |
|
518 /* unknown limit, no idea how to scale */ |
|
519 zwarnnam(nam, "unknown scaling factor: %s", s, 0); |
|
520 return 1; |
|
521 } |
|
522 } |
|
523 else if (limtype[lim] == ZLIMTYPE_TIME) { |
|
524 /* time-type resource -- may be specified as seconds, or minutes or * |
|
525 * hours with the `m' and `h' modifiers, and `:' may be used to add * |
|
526 * together more than one of these. It's easier to understand from * |
|
527 * the code: */ |
|
528 val = zstrtorlimt(s, &s, 10); |
|
529 if (*s) { |
|
530 if ((*s == 'h' || *s == 'H') && !s[1]) |
|
531 val *= 3600L; |
|
532 else if ((*s == 'm' || *s == 'M') && !s[1]) |
|
533 val *= 60L; |
|
534 else if (*s == ':') |
|
535 val = val * 60 + zstrtorlimt(s + 1, &s, 10); |
|
536 else { |
|
537 zwarnnam(nam, "unknown scaling factor: %s", s, 0); |
|
538 return 1; |
|
539 } |
|
540 } |
|
541 } else if (limtype[lim] == ZLIMTYPE_NUMBER || limtype[lim] == ZLIMTYPE_UNKNOWN) { |
|
542 /* pure numeric resource -- only a straight decimal number is |
|
543 permitted. */ |
|
544 char *t = s; |
|
545 val = zstrtorlimt(t, &s, 10); |
|
546 if (s == t) { |
|
547 zwarnnam(nam, "limit must be a number", NULL, 0); |
|
548 return 1; |
|
549 } |
|
550 } else { |
|
551 /* memory-type resource -- `k' and `M' modifiers are permitted, |
|
552 meaning (respectively) 2^10 and 2^20. */ |
|
553 val = zstrtorlimt(s, &s, 10); |
|
554 if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) { |
|
555 if (val != RLIM_INFINITY) |
|
556 val *= 1024L; |
|
557 } else if ((*s == 'M' || *s == 'm') && !s[1]) |
|
558 val *= 1024L * 1024; |
|
559 else { |
|
560 zwarnnam(nam, "unknown scaling factor: %s", s, 0); |
|
561 return 1; |
|
562 } |
|
563 } |
|
564 if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's'))) |
|
565 ret++; |
|
566 } |
|
567 return ret; |
|
568 } |
|
569 |
|
570 /**/ |
|
571 static int |
|
572 do_unlimit(char *nam, int lim, int hard, int soft, int set, int euid) |
|
573 { |
|
574 /* remove specified limit */ |
|
575 if (lim >= ZSH_NLIMITS) { |
|
576 struct rlimit vals; |
|
577 if (getrlimit(lim, &vals) < 0) |
|
578 { |
|
579 zwarnnam(nam, "can't read limit: %e", NULL, errno); |
|
580 return 1; |
|
581 } |
|
582 if (hard) { |
|
583 if (euid && vals.rlim_max != RLIM_INFINITY) { |
|
584 zwarnnam(nam, "can't remove hard limits", NULL, 0); |
|
585 return 1; |
|
586 } else |
|
587 vals.rlim_max = RLIM_INFINITY; |
|
588 } |
|
589 if (!hard || soft) |
|
590 vals.rlim_cur = vals.rlim_max; |
|
591 if (!set) { |
|
592 zwarnnam(nam, |
|
593 "warning: unrecognised limit %d, use -s to set", |
|
594 NULL, lim); |
|
595 return 1; |
|
596 } else if (setrlimit(lim, &vals) < 0) { |
|
597 zwarnnam(nam, "setrlimit failed: %e", NULL, errno); |
|
598 return 1; |
|
599 } |
|
600 } else { |
|
601 if (hard) { |
|
602 if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) { |
|
603 zwarnnam(nam, "can't remove hard limits", NULL, 0); |
|
604 return 1; |
|
605 } else |
|
606 limits[lim].rlim_max = RLIM_INFINITY; |
|
607 } |
|
608 if (!hard || soft) |
|
609 limits[lim].rlim_cur = limits[lim].rlim_max; |
|
610 if (set && zsetlimit(lim, nam)) |
|
611 return 1; |
|
612 } |
|
613 return 0; |
|
614 } |
|
615 |
|
616 /* unlimit: remove resource limits. Much of this code is the same as * |
|
617 * that in bin_limit(). */ |
|
618 |
|
619 /**/ |
|
620 static int |
|
621 bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func)) |
|
622 { |
|
623 int hard, limnum, lim; |
|
624 int ret = 0; |
|
625 uid_t euid = geteuid(); |
|
626 |
|
627 hard = OPT_ISSET(ops,'h'); |
|
628 /* Without arguments, remove all limits. */ |
|
629 if (!*argv) { |
|
630 for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) { |
|
631 if (hard) { |
|
632 if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY) |
|
633 ret++; |
|
634 else |
|
635 limits[limnum].rlim_max = RLIM_INFINITY; |
|
636 } else |
|
637 limits[limnum].rlim_cur = limits[limnum].rlim_max; |
|
638 } |
|
639 if (OPT_ISSET(ops,'s')) |
|
640 ret += setlimits(nam); |
|
641 if (ret) |
|
642 zwarnnam(nam, "can't remove hard limits", NULL, 0); |
|
643 } else { |
|
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); |
|
651 } else { |
|
652 for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) |
|
653 if (!strncmp(recs[limnum], *argv, strlen(*argv))) { |
|
654 if (lim != -1) |
|
655 lim = -2; |
|
656 else |
|
657 lim = limnum; |
|
658 } |
|
659 } |
|
660 /* lim==-1 indicates that no matches were found. * |
|
661 * lim==-2 indicates that multiple matches were found. */ |
|
662 if (lim < 0) { |
|
663 zwarnnam(nam, |
|
664 (lim == -2) ? "ambiguous resource specification: %s" |
|
665 : "no such resource: %s", *argv, 0); |
|
666 return 1; |
|
667 } |
|
668 else if (do_unlimit(nam, lim, hard, !hard, OPT_ISSET(ops, 's'), |
|
669 euid)) |
|
670 ret++; |
|
671 } |
|
672 } |
|
673 return ret; |
|
674 } |
|
675 |
|
676 /* ulimit: set or display resource limits */ |
|
677 |
|
678 /**/ |
|
679 static int |
|
680 bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) |
|
681 { |
|
682 int res, resmask = 0, hard = 0, soft = 0, nres = 0, all = 0, ret = 0; |
|
683 char *options; |
|
684 |
|
685 do { |
|
686 options = *argv; |
|
687 if (options && *options == '-' && !options[1]) { |
|
688 zwarnnam(name, "missing option letter", NULL, 0); |
|
689 return 1; |
|
690 } |
|
691 res = -1; |
|
692 if (options && *options == '-') { |
|
693 argv++; |
|
694 while (*++options) { |
|
695 if(*options == Meta) |
|
696 *++options ^= 32; |
|
697 res = -1; |
|
698 switch (*options) { |
|
699 case 'H': |
|
700 hard = 1; |
|
701 continue; |
|
702 case 'S': |
|
703 soft = 1; |
|
704 continue; |
|
705 case 'N': |
|
706 if (options[1]) { |
|
707 res = (int)zstrtol(options+1, NULL, 10); |
|
708 } else if (*argv) { |
|
709 res = (int)zstrtol(*argv++, NULL, 10); |
|
710 } else { |
|
711 zwarnnam(name, "number required after -N", |
|
712 NULL, 0); |
|
713 return 1; |
|
714 } |
|
715 /* |
|
716 * fake it so it looks like we just finished an option... |
|
717 */ |
|
718 while (options[1]) |
|
719 options++; |
|
720 break; |
|
721 case 'a': |
|
722 if (resmask) { |
|
723 zwarnnam(name, "no limits allowed with -a", |
|
724 NULL, 0); |
|
725 return 1; |
|
726 } |
|
727 all = 1; |
|
728 resmask = (1 << RLIM_NLIMITS) - 1; |
|
729 nres = RLIM_NLIMITS; |
|
730 continue; |
|
731 case 't': |
|
732 res = RLIMIT_CPU; |
|
733 break; |
|
734 case 'f': |
|
735 res = RLIMIT_FSIZE; |
|
736 break; |
|
737 case 'd': |
|
738 res = RLIMIT_DATA; |
|
739 break; |
|
740 case 's': |
|
741 res = RLIMIT_STACK; |
|
742 break; |
|
743 case 'c': |
|
744 res = RLIMIT_CORE; |
|
745 break; |
|
746 # ifdef HAVE_RLIMIT_RSS |
|
747 case 'm': |
|
748 res = RLIMIT_RSS; |
|
749 break; |
|
750 # endif /* HAVE_RLIMIT_RSS */ |
|
751 # ifdef HAVE_RLIMIT_MEMLOCK |
|
752 case 'l': |
|
753 res = RLIMIT_MEMLOCK; |
|
754 break; |
|
755 # endif /* HAVE_RLIMIT_MEMLOCK */ |
|
756 # ifdef HAVE_RLIMIT_NOFILE |
|
757 case 'n': |
|
758 res = RLIMIT_NOFILE; |
|
759 break; |
|
760 # endif /* HAVE_RLIMIT_NOFILE */ |
|
761 # ifdef HAVE_RLIMIT_NPROC |
|
762 case 'u': |
|
763 res = RLIMIT_NPROC; |
|
764 break; |
|
765 # endif /* HAVE_RLIMIT_NPROC */ |
|
766 # if defined(HAVE_RLIMIT_VMEM) || defined(HAVE_RLIMIT_AS) |
|
767 case 'v': |
|
768 # ifdef HAVE_RLIMIT_VMEM |
|
769 res = RLIMIT_VMEM; |
|
770 # else |
|
771 res = RLIMIT_AS; |
|
772 # endif |
|
773 break; |
|
774 # endif /* HAVE_RLIMIT_VMEM */ |
|
775 # ifdef HAVE_RLIMIT_LOCKS |
|
776 case 'x': |
|
777 res = RLIMIT_LOCKS; |
|
778 break; |
|
779 # endif |
|
780 # ifdef HAVE_RLIMIT_SIGPENDING |
|
781 case 'i': |
|
782 res = RLIMIT_SIGPENDING; |
|
783 break; |
|
784 # endif |
|
785 # ifdef HAVE_RLIMIT_MSGQUEUE |
|
786 case 'q': |
|
787 res = RLIMIT_MSGQUEUE; |
|
788 break; |
|
789 # endif |
|
790 default: |
|
791 /* unrecognised limit */ |
|
792 zwarnnam(name, "bad option: -%c", NULL, *options); |
|
793 return 1; |
|
794 } |
|
795 if (options[1]) { |
|
796 resmask |= 1 << res; |
|
797 nres++; |
|
798 } |
|
799 if (all && res != -1) { |
|
800 zwarnnam(name, "no limits allowed with -a", |
|
801 NULL, 0); |
|
802 return 1; |
|
803 } |
|
804 } |
|
805 } |
|
806 if (!*argv || **argv == '-') { |
|
807 if (res < 0) { |
|
808 if (*argv || nres) |
|
809 continue; |
|
810 else |
|
811 res = RLIMIT_FSIZE; |
|
812 } |
|
813 resmask |= 1 << res; |
|
814 nres++; |
|
815 continue; |
|
816 } |
|
817 if (all) { |
|
818 zwarnnam(name, "no arguments allowed after -a", NULL, 0); |
|
819 return 1; |
|
820 } |
|
821 if (res < 0) |
|
822 res = RLIMIT_FSIZE; |
|
823 if (strcmp(*argv, "unlimited")) { |
|
824 /* set limit to specified value */ |
|
825 rlim_t limit; |
|
826 |
|
827 limit = zstrtorlimt(*argv, NULL, 10); |
|
828 /* scale appropriately */ |
|
829 switch (res) { |
|
830 case RLIMIT_FSIZE: |
|
831 case RLIMIT_CORE: |
|
832 limit *= 512; |
|
833 break; |
|
834 case RLIMIT_DATA: |
|
835 case RLIMIT_STACK: |
|
836 # ifdef HAVE_RLIMIT_RSS |
|
837 case RLIMIT_RSS: |
|
838 # endif /* HAVE_RLIMIT_RSS */ |
|
839 # ifdef HAVE_RLIMIT_MEMLOCK |
|
840 case RLIMIT_MEMLOCK: |
|
841 # endif /* HAVE_RLIMIT_MEMLOCK */ |
|
842 /* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid * |
|
843 * duplicate case statement. Observed on QNX Neutrino 6.1.0. */ |
|
844 # if defined(HAVE_RLIMIT_VMEM) && !defined(RLIMIT_VMEM_IS_RSS) |
|
845 case RLIMIT_VMEM: |
|
846 # endif /* HAVE_RLIMIT_VMEM */ |
|
847 /* ditto RLIMIT_VMEM and RLIMIT_AS */ |
|
848 # if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS) && !defined(RLIMIT_RSS_IS_AS) |
|
849 case RLIMIT_AS: |
|
850 # endif /* HAVE_RLIMIT_AS */ |
|
851 # ifdef HAVE_RLIMIT_AIO_MEM |
|
852 case RLIMIT_AIO_MEM: |
|
853 # endif /* HAVE_RLIMIT_AIO_MEM */ |
|
854 limit *= 1024; |
|
855 break; |
|
856 } |
|
857 if (do_limit(name, res, limit, hard, soft, 1)) |
|
858 ret++; |
|
859 } else { |
|
860 if (do_unlimit(name, res, hard, soft, 1, geteuid())) |
|
861 ret++; |
|
862 } |
|
863 argv++; |
|
864 } while (*argv); |
|
865 for (res = 0; resmask; res++, resmask >>= 1) |
|
866 if ((resmask & 1) && printulimit(name, res, hard, nres > 1)) |
|
867 ret++; |
|
868 return ret; |
|
869 } |
|
870 |
|
871 #else /* !HAVE_GETRLIMIT || !RLIM_INFINITY */ |
|
872 |
|
873 # define bin_limit bin_notavail |
|
874 # define bin_ulimit bin_notavail |
|
875 # define bin_unlimit bin_notavail |
|
876 |
|
877 #endif /* !HAVE_GETRLIMIT || !RLIM_INFINITY */ |
|
878 |
|
879 static struct builtin bintab[] = { |
|
880 BUILTIN("limit", 0, bin_limit, 0, -1, 0, "sh", NULL), |
|
881 BUILTIN("ulimit", 0, bin_ulimit, 0, -1, 0, NULL, NULL), |
|
882 BUILTIN("unlimit", 0, bin_unlimit, 0, -1, 0, "hs", NULL), |
|
883 }; |
|
884 |
|
885 /**/ |
|
886 int |
|
887 setup_(UNUSED(Module m)) |
|
888 { |
|
889 return 0; |
|
890 } |
|
891 |
|
892 /**/ |
|
893 int |
|
894 boot_(Module m) |
|
895 { |
|
896 return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); |
|
897 } |
|
898 |
|
899 /**/ |
|
900 int |
|
901 cleanup_(Module m) |
|
902 { |
|
903 deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); |
|
904 return 0; |
|
905 } |
|
906 |
|
907 /**/ |
|
908 int |
|
909 finish_(UNUSED(Module m)) |
|
910 { |
|
911 return 0; |
|
912 } |