1/*	$NetBSD: c_ulimit.c,v 1.9 2008/09/14 05:00:23 sjg Exp $	*/
2
3/*
4	ulimit -- handle "ulimit" builtin
5
6	Reworked to use getrusage() and ulimit() at once (as needed on
7	some schizophrenic systems, eg, HP-UX 9.01), made argument parsing
8	conform to at&t ksh, added autoconf support.  Michael Rendell, May, '94
9
10	Eric Gisin, September 1988
11	Adapted to PD KornShell. Removed AT&T code.
12
13	last edit:	06-Jun-1987	D A Gwyn
14
15	This started out as the BRL UNIX System V system call emulation
16	for 4.nBSD, and was later extended by Doug Kingston to handle
17	the extended 4.nBSD resource limits.  It now includes the code
18	that was originally under case SYSULIMIT in source file "xec.c".
19*/
20#include <sys/cdefs.h>
21
22#ifndef lint
23__RCSID("$NetBSD: c_ulimit.c,v 1.9 2008/09/14 05:00:23 sjg Exp $");
24#endif
25
26
27#include "sh.h"
28#include "ksh_time.h"
29#ifdef HAVE_SYS_RESOURCE_H
30# include <sys/resource.h>
31#endif /* HAVE_SYS_RESOURCE_H */
32#ifdef HAVE_ULIMIT_H
33# include <ulimit.h>
34#else /* HAVE_ULIMIT_H */
35# ifdef HAVE_ULIMIT
36extern	long ulimit();
37# endif /* HAVE_ULIMIT */
38#endif /* HAVE_ULIMIT_H */
39
40#define SOFT	0x1
41#define HARD	0x2
42
43#ifdef RLIM_INFINITY
44# define KSH_RLIM_INFINITY RLIM_INFINITY
45#else
46# define KSH_RLIM_INFINITY ((rlim_t) 1 << (sizeof(rlim_t) * 8 - 1) - 1)
47#endif /* RLIM_INFINITY */
48
49int
50c_ulimit(wp)
51	char **wp;
52{
53	static const struct limits {
54		const char	*name;
55		enum { RLIMIT, ULIMIT } which;
56		int	gcmd;	/* get command */
57		int	scmd;	/* set command (or -1, if no set command) */
58		int	factor;	/* multiply by to get rlim_{cur,max} values */
59		char	option;
60	} limits[] = {
61		/* Do not use options -H, -S or -a */
62#ifdef RLIMIT_CPU
63		{ "time(cpu-seconds)", RLIMIT, RLIMIT_CPU, RLIMIT_CPU, 1, 't' },
64#endif
65#ifdef RLIMIT_FSIZE
66		{ "file(blocks)", RLIMIT, RLIMIT_FSIZE, RLIMIT_FSIZE, 512, 'f' },
67#else /* RLIMIT_FSIZE */
68# ifdef UL_GETFSIZE /* x/open */
69		{ "file(blocks)", ULIMIT, UL_GETFSIZE, UL_SETFSIZE, 1, 'f' },
70# else /* UL_GETFSIZE */
71#  ifdef UL_GFILLIM /* svr4/xenix */
72		{ "file(blocks)", ULIMIT, UL_GFILLIM, UL_SFILLIM, 1, 'f' },
73#  else /* UL_GFILLIM */
74		{ "file(blocks)", ULIMIT, 1, 2, 1, 'f' },
75#  endif /* UL_GFILLIM */
76# endif /* UL_GETFSIZE */
77#endif /* RLIMIT_FSIZE */
78#ifdef RLIMIT_CORE
79		{ "coredump(blocks)", RLIMIT, RLIMIT_CORE, RLIMIT_CORE, 512, 'c' },
80#endif
81#ifdef RLIMIT_DATA
82		{ "data(kbytes)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, 1024, 'd' },
83#endif
84#ifdef RLIMIT_STACK
85		{ "stack(kbytes)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, 1024, 's' },
86#endif
87#ifdef RLIMIT_MEMLOCK
88		{ "lockedmem(kbytes)", RLIMIT, RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, 1024, 'l' },
89#endif
90#ifdef RLIMIT_RSS
91		{ "memory(kbytes)", RLIMIT, RLIMIT_RSS, RLIMIT_RSS, 1024, 'm' },
92#endif
93#ifdef RLIMIT_NOFILE
94		{ "nofiles(descriptors)", RLIMIT, RLIMIT_NOFILE, RLIMIT_NOFILE, 1, 'n' },
95#else /* RLIMIT_NOFILE */
96# ifdef UL_GDESLIM /* svr4/xenix */
97		{ "nofiles(descriptors)", ULIMIT, UL_GDESLIM, -1, 1, 'n' },
98# endif /* UL_GDESLIM */
99#endif /* RLIMIT_NOFILE */
100#ifdef RLIMIT_NPROC
101		{ "processes", RLIMIT, RLIMIT_NPROC, RLIMIT_NPROC, 1, 'p' },
102#endif
103#ifdef RLIMIT_VMEM
104		{ "vmemory(kbytes)", RLIMIT, RLIMIT_VMEM, RLIMIT_VMEM, 1024, 'v' },
105#else /* RLIMIT_VMEM */
106  /* These are not quite right - really should subtract etext or something */
107# ifdef UL_GMEMLIM /* svr4/xenix */
108		{ "vmemory(maxaddr)", ULIMIT, UL_GMEMLIM, -1, 1, 'v' },
109# else /* UL_GMEMLIM */
110#  ifdef UL_GETBREAK /* osf/1 */
111		{ "vmemory(maxaddr)", ULIMIT, UL_GETBREAK, -1, 1, 'v' },
112#  else /* UL_GETBREAK */
113#   ifdef UL_GETMAXBRK /* hpux */
114		{ "vmemory(maxaddr)", ULIMIT, UL_GETMAXBRK, -1, 1, 'v' },
115#   endif /* UL_GETMAXBRK */
116#  endif /* UL_GETBREAK */
117# endif /* UL_GMEMLIM */
118#endif /* RLIMIT_VMEM */
119#ifdef RLIMIT_SWAP
120		{ "swap(kbytes)", RLIMIT, RLIMIT_SWAP, RLIMIT_SWAP, 1024, 'w' },
121#endif
122#ifdef RLIMIT_SBSIZE
123		{ "sbsize(bytes)", RLIMIT, RLIMIT_SBSIZE, RLIMIT_SBSIZE, 1, 'b' },
124#endif
125		{ .name = NULL }
126	    };
127	static char	options[3 + NELEM(limits)];
128	rlim_t		UNINITIALIZED(val);
129	int		how = SOFT | HARD;
130	const struct limits	*l;
131	int		set, all = 0;
132	int		optc, what;
133#ifdef HAVE_SETRLIMIT
134	struct rlimit	limit;
135#endif /* HAVE_SETRLIMIT */
136
137	if (!options[0]) {
138		/* build options string on first call - yuck */
139		char *p = options;
140
141		*p++ = 'H'; *p++ = 'S'; *p++ = 'a';
142		for (l = limits; l->name; l++)
143			*p++ = l->option;
144		*p = '\0';
145	}
146	what = 'f';
147	while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
148		switch (optc) {
149		  case 'H':
150			how = HARD;
151			break;
152		  case 'S':
153			how = SOFT;
154			break;
155		  case 'a':
156			all = 1;
157			break;
158		  case '?':
159			return 1;
160		  default:
161			what = optc;
162		}
163
164	for (l = limits; l->name && l->option != what; l++)
165		;
166	if (!l->name) {
167		internal_errorf(0, "ulimit: %c", what);
168		return 1;
169	}
170
171	wp += builtin_opt.optind;
172	set = *wp ? 1 : 0;
173	if (set) {
174		if (all || wp[1]) {
175			bi_errorf("too many arguments");
176			return 1;
177		}
178		if (strcmp(wp[0], "unlimited") == 0)
179			val = KSH_RLIM_INFINITY;
180		else {
181			long rval;
182
183			if (!evaluate(wp[0], &rval, KSH_RETURN_ERROR))
184				return 1;
185			/* Avoid problems caused by typos that
186			 * evaluate misses due to evaluating unset
187			 * parameters to 0...
188			 * If this causes problems, will have to
189			 * add parameter to evaluate() to control
190			 * if unset params are 0 or an error.
191			 */
192			if (!rval && !digit(wp[0][0])) {
193			    bi_errorf("invalid limit: %s", wp[0]);
194			    return 1;
195			}
196			val = (u_long)rval * l->factor;
197		}
198	}
199	if (all) {
200		for (l = limits; l->name; l++) {
201#ifdef HAVE_SETRLIMIT
202			if (l->which == RLIMIT) {
203				getrlimit(l->gcmd, &limit);
204				if (how & SOFT)
205					val = limit.rlim_cur;
206				else if (how & HARD)
207					val = limit.rlim_max;
208			} else
209#endif /* HAVE_SETRLIMIT */
210#ifdef HAVE_ULIMIT
211			{
212				val = ulimit(l->gcmd, (rlim_t) 0);
213			}
214#else /* HAVE_ULIMIT */
215				;
216#endif /* HAVE_ULIMIT */
217			shprintf("%-20s ", l->name);
218#ifdef RLIM_INFINITY
219			if (val == RLIM_INFINITY)
220				shprintf("unlimited\n");
221			else
222#endif /* RLIM_INFINITY */
223			{
224				val /= l->factor;
225				shprintf("%ld\n", (long) val);
226			}
227		}
228		return 0;
229	}
230#ifdef HAVE_SETRLIMIT
231	if (l->which == RLIMIT) {
232		getrlimit(l->gcmd, &limit);
233		if (set) {
234			if (how & SOFT)
235				limit.rlim_cur = val;
236			if (how & HARD)
237				limit.rlim_max = val;
238			if (setrlimit(l->scmd, &limit) < 0) {
239				if (errno == EPERM)
240					bi_errorf("exceeds allowable limit");
241				else
242					bi_errorf("bad limit: %s",
243						strerror(errno));
244				return 1;
245			}
246		} else {
247			if (how & SOFT)
248				val = limit.rlim_cur;
249			else if (how & HARD)
250				val = limit.rlim_max;
251		}
252	} else
253#endif /* HAVE_SETRLIMIT */
254#ifdef HAVE_ULIMIT
255	{
256		if (set) {
257			if (l->scmd == -1) {
258				bi_errorf("can't change limit");
259				return 1;
260			} else if (ulimit(l->scmd, val) < 0) {
261				bi_errorf("bad limit: %s", strerror(errno));
262				return 1;
263			}
264		} else
265			val = ulimit(l->gcmd, (rlim_t) 0);
266	}
267#else /* HAVE_ULIMIT */
268		;
269#endif /* HAVE_ULIMIT */
270	if (!set) {
271#ifdef RLIM_INFINITY
272		if (val == RLIM_INFINITY)
273			shprintf("unlimited\n");
274		else
275#endif /* RLIM_INFINITY */
276		{
277			val /= l->factor;
278			shprintf("%ld\n", (long) val);
279		}
280	}
281	return 0;
282}
283