1 /* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
5 * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
6 * 2019, 2020
7 * mirabilos <m@mirbsd.org>
8 *
9 * Provided that these terms and disclaimer and all copyright notices
10 * are retained or reproduced in an accompanying document, permission
11 * is granted to deal in this work without restriction, including un-
12 * limited rights to use, publicly perform, distribute, sell, modify,
13 * merge, give away, or sublicence.
14 *
15 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
16 * the utmost extent permitted by applicable law, neither express nor
17 * implied; without malicious intent or gross negligence. In no event
18 * may a licensor, author or contributor be held liable for indirect,
19 * direct, other damage, loss, or other issues arising in any way out
20 * of dealing in the work, even if advised of the possibility of such
21 * damage or existence of a defect, except proven that it results out
22 * of said person's immediate fault when using the work as intended.
23 */
24
25 #include "sh.h"
26
27 __RCSID("$MirOS: src/bin/mksh/ulimit.c,v 1.3 2020/07/24 21:08:26 tg Exp $");
28
29 #define SOFT 0x1
30 #define HARD 0x2
31
32 #if HAVE_RLIMIT
33
34 #if !HAVE_RLIM_T
35 typedef unsigned long rlim_t;
36 #endif
37
38 /* Magic to divine the 'm' and 'v' limits */
39
40 #ifdef RLIMIT_AS
41 #if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
42 !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
43 #define ULIMIT_V_IS_AS
44 #elif defined(RLIMIT_VMEM)
45 #if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
46 #define ULIMIT_V_IS_AS
47 #else
48 #define ULIMIT_V_IS_VMEM
49 #endif
50 #endif
51 #endif
52
53 #ifdef RLIMIT_RSS
54 #ifdef ULIMIT_V_IS_VMEM
55 #define ULIMIT_M_IS_RSS
56 #elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
57 #define ULIMIT_M_IS_VMEM
58 #else
59 #define ULIMIT_M_IS_RSS
60 #endif
61 #if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && \
62 !defined(__APPLE__) && (RLIMIT_RSS == RLIMIT_AS)
63 /* On Mac OSX keep -m as -v alias for pkgsrc and other software expecting it */
64 #undef ULIMIT_M_IS_RSS
65 #endif
66 #endif
67
68 #if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
69 #define ULIMIT_V_IS_VMEM
70 #endif
71
72 #if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
73 (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
74 #define ULIMIT_M_IS_VMEM
75 #endif
76
77 #if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
78 (RLIMIT_VMEM == RLIMIT_AS)
79 #undef ULIMIT_M_IS_VMEM
80 #endif
81
82 #if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM)
83 # error nonsensical m ulimit
84 #endif
85
86 #if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS)
87 # error nonsensical v ulimit
88 #endif
89
90 #define LIMITS_GEN "rlimits.gen"
91
92 #else /* !HAVE_RLIMIT */
93
94 #undef RLIMIT_CORE /* just in case */
95
96 #if defined(UL_GETFSIZE)
97 #define KSH_UL_GFIL UL_GETFSIZE
98 #elif defined(UL_GFILLIM)
99 #define KSH_UL_GFIL UL_GFILLIM
100 #elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST)
101 #define KSH_UL_GFIL 1
102 #endif
103
104 #if defined(UL_SETFSIZE)
105 #define KSH_UL_SFIL UL_SETFSIZE
106 #elif defined(UL_SFILLIM)
107 #define KSH_UL_SFIL UL_SFILLIM
108 #elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST)
109 #define KSH_UL_SFIL 2
110 #endif
111
112 #if defined(KSH_UL_SFIL)
113 #define KSH_UL_WFIL true
114 #else
115 #define KSH_UL_WFIL false
116 #define KSH_UL_SFIL 0
117 #endif
118
119 #if defined(UL_GETMAXBRK)
120 #define KSH_UL_GBRK UL_GETMAXBRK
121 #elif defined(UL_GMEMLIM)
122 #define KSH_UL_GBRK UL_GMEMLIM
123 #elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST)
124 #define KSH_UL_GBRK 3
125 #endif
126
127 #if defined(UL_GDESLIM)
128 #define KSH_UL_GDES UL_GDESLIM
129 #elif defined(__GLIBC__) || defined(KSH_ULIMIT2_TEST)
130 #define KSH_UL_GDES 4
131 #endif
132
133 extern char etext;
134 extern long ulimit(int, long);
135
136 #define LIMITS_GEN "ulimits.gen"
137
138 #endif /* !HAVE_RLIMIT */
139
140 struct limits {
141 /* limit resource / read command */
142 int resource;
143 #if HAVE_RLIMIT
144 /* multiply by to get rlim_{cur,max} values */
145 unsigned int factor;
146 #else
147 /* write command */
148 int wesource;
149 /* writable? */
150 bool writable;
151 #endif
152 /* getopts char */
153 char optchar;
154 /* limit name */
155 char name[1];
156 };
157
158 #define RLIMITS_DEFNS
159 #if HAVE_RLIMIT
160 #define FN(lname,lid,lfac,lopt) \
161 static const struct { \
162 int resource; \
163 unsigned int factor; \
164 char optchar; \
165 char name[sizeof(lname)]; \
166 } rlimits_ ## lid = { \
167 lid, lfac, lopt, lname \
168 };
169 #else
170 #define FN(lname,lg,ls,lw,lopt) \
171 static const struct { \
172 int rcmd; \
173 int wcmd; \
174 bool writable; \
175 char optchar; \
176 char name[sizeof(lname)]; \
177 } rlimits_ ## lg = { \
178 lg, ls, lw, lopt, lname \
179 };
180 #endif
181 #include LIMITS_GEN
182
183 static void print_ulimit(const struct limits *, int);
184 static int set_ulimit(const struct limits *, const char *, int);
185
186 static const struct limits * const rlimits[] = {
187 #define RLIMITS_ITEMS
188 #include LIMITS_GEN
189 };
190
191 static const char rlimits_opts[] =
192 #define RLIMITS_OPTCS
193 #include LIMITS_GEN
194 #ifndef RLIMIT_CORE
195 "c"
196 #endif
197 ;
198
199 int
c_ulimit(const char ** wp)200 c_ulimit(const char **wp)
201 {
202 size_t i = 0;
203 int how = SOFT | HARD, optc;
204 char what = 'f';
205 bool all = false;
206
207 while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1)
208 switch (optc) {
209 case ORD('H'):
210 how = HARD;
211 break;
212 case ORD('S'):
213 how = SOFT;
214 break;
215 case ORD('a'):
216 all = true;
217 break;
218 case ORD('?'):
219 bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts);
220 return (1);
221 default:
222 what = optc;
223 }
224
225 while (i < NELEM(rlimits)) {
226 if (rlimits[i]->optchar == what)
227 goto found;
228 ++i;
229 }
230 #ifndef RLIMIT_CORE
231 if (what == ORD('c'))
232 /* silently accept */
233 return 0;
234 #endif
235 internal_warningf("ulimit: %c", what);
236 return (1);
237 found:
238 if (wp[builtin_opt.optind]) {
239 if (all || wp[builtin_opt.optind + 1]) {
240 bi_errorf(Ttoo_many_args);
241 return (1);
242 }
243 return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
244 }
245 if (!all)
246 print_ulimit(rlimits[i], how);
247 else for (i = 0; i < NELEM(rlimits); ++i) {
248 shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name);
249 print_ulimit(rlimits[i], how);
250 }
251 return (0);
252 }
253
254 #if HAVE_RLIMIT
255 #define RL_T rlim_t
256 #define RL_U (rlim_t)RLIM_INFINITY
257 #else
258 #define RL_T long
259 #define RL_U LONG_MAX
260 #endif
261
262 static int
set_ulimit(const struct limits * l,const char * v,int how MKSH_A_UNUSED)263 set_ulimit(const struct limits *l, const char *v, int how MKSH_A_UNUSED)
264 {
265 RL_T val = (RL_T)0;
266 #if HAVE_RLIMIT
267 struct rlimit limit;
268 #endif
269
270 if (strcmp(v, "unlimited") == 0)
271 val = RL_U;
272 else {
273 mksh_uari_t rval;
274
275 if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
276 return (1);
277 /*
278 * Avoid problems caused by typos that evaluate misses due
279 * to evaluating unset parameters to 0...
280 * If this causes problems, will have to add parameter to
281 * evaluate() to control if unset params are 0 or an error.
282 */
283 if (!rval && !ctype(v[0], C_DIGIT)) {
284 bi_errorf("invalid %s limit: %s", l->name, v);
285 return (1);
286 }
287 #if HAVE_RLIMIT
288 val = (rlim_t)((rlim_t)rval * l->factor);
289 #else
290 val = (RL_T)rval;
291 #endif
292 }
293
294 #if HAVE_RLIMIT
295 if (getrlimit(l->resource, &limit) < 0) {
296 #ifndef MKSH_SMALL
297 bi_errorf("limit %s could not be read, contact the mksh developers: %s",
298 l->name, cstrerror(errno));
299 #endif
300 /* some can't be read */
301 limit.rlim_cur = RLIM_INFINITY;
302 limit.rlim_max = RLIM_INFINITY;
303 }
304 if (how & SOFT)
305 limit.rlim_cur = val;
306 if (how & HARD)
307 limit.rlim_max = val;
308 if (!setrlimit(l->resource, &limit))
309 return (0);
310 #else
311 if (l->writable == false) {
312 /* check.t:ulimit-2 fails if we return 1 and/or do:
313 bi_errorf(Tf_ro, l->name);
314 */
315 return (0);
316 }
317 if (ulimit(l->wesource, val) != -1L)
318 return (0);
319 #endif
320 if (errno == EPERM)
321 bi_errorf("%s exceeds allowable %s limit", v, l->name);
322 else
323 bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
324 return (1);
325 }
326
327 static void
print_ulimit(const struct limits * l,int how MKSH_A_UNUSED)328 print_ulimit(const struct limits *l, int how MKSH_A_UNUSED)
329 {
330 RL_T val = (RL_T)0;
331 #if HAVE_RLIMIT
332 struct rlimit limit;
333
334 if (getrlimit(l->resource, &limit))
335 #else
336 if ((val = ulimit(l->resource, 0)) < 0)
337 #endif
338 {
339 shf_puts("unknown\n", shl_stdout);
340 return;
341 }
342 #if HAVE_RLIMIT
343 if (how & SOFT)
344 val = limit.rlim_cur;
345 else if (how & HARD)
346 val = limit.rlim_max;
347 #endif
348 if (val == RL_U)
349 shf_puts("unlimited\n", shl_stdout);
350 else {
351 #if HAVE_RLIMIT
352 val /= l->factor;
353 #elif defined(KSH_UL_GBRK)
354 if (l->resource == KSH_UL_GBRK)
355 val = (RL_T)(((size_t)val - (size_t)&etext) /
356 (size_t)1024);
357 #endif
358 shprintf("%lu\n", (unsigned long)val);
359 }
360 }
361