xref: /third_party/mksh/ulimit.c (revision c84f3f3c)
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
35typedef 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
133extern char etext;
134extern long ulimit(int, long);
135
136#define LIMITS_GEN	"ulimits.gen"
137
138#endif /* !HAVE_RLIMIT */
139
140struct 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
183static void print_ulimit(const struct limits *, int);
184static int set_ulimit(const struct limits *, const char *, int);
185
186static const struct limits * const rlimits[] = {
187#define RLIMITS_ITEMS
188#include LIMITS_GEN
189};
190
191static const char rlimits_opts[] =
192#define RLIMITS_OPTCS
193#include LIMITS_GEN
194#ifndef RLIMIT_CORE
195	"c"
196#endif
197    ;
198
199int
200c_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
262static int
263set_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
327static void
328print_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