xref: /netbsd-src/bin/ksh/c_ulimit.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
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
36 extern	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 
49 int
50 c_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