xref: /openbsd-src/bin/ksh/c_ulimit.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: c_ulimit.c,v 1.24 2015/12/14 13:59:42 tb 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 
21 #include <sys/resource.h>
22 
23 #include <ctype.h>
24 #include <errno.h>
25 #include <string.h>
26 
27 #include "sh.h"
28 
29 #define SOFT	0x1
30 #define HARD	0x2
31 
32 struct limits {
33 	const char *name;
34 	int	resource;	/* resource to get/set */
35 	int	factor;		/* multiply by to get rlim_{cur,max} values */
36 	char	option;		/* option character (-d, -f, ...) */
37 };
38 
39 static void print_ulimit(const struct limits *, int);
40 static int set_ulimit(const struct limits *, const char *, int);
41 
42 int
43 c_ulimit(char **wp)
44 {
45 	static const struct limits limits[] = {
46 		/* Do not use options -H, -S or -a or change the order. */
47 		{ "time(cpu-seconds)", RLIMIT_CPU, 1, 't' },
48 		{ "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
49 		{ "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
50 		{ "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
51 		{ "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
52 		{ "lockedmem(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
53 		{ "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
54 		{ "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
55 		{ "processes", RLIMIT_NPROC, 1, 'p' },
56 #ifdef RLIMIT_VMEM
57 		{ "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' },
58 #endif /* RLIMIT_VMEM */
59 		{ NULL }
60 	};
61 	static char	options[4 + NELEM(limits) * 2];
62 	int		how = SOFT | HARD;
63 	const struct limits	*l;
64 	int		optc, all = 0;
65 
66 	if (!options[0]) {
67 		/* build options string on first call - yuck */
68 		char *p = options;
69 
70 		*p++ = 'H'; *p++ = 'S'; *p++ = 'a';
71 		for (l = limits; l->name; l++) {
72 			*p++ = l->option;
73 			*p++ = '#';
74 		}
75 		*p = '\0';
76 	}
77 	/* First check for -a, -H and -S. */
78 	while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1)
79 		switch (optc) {
80 		case 'H':
81 			how = HARD;
82 			break;
83 		case 'S':
84 			how = SOFT;
85 			break;
86 		case 'a':
87 			all = 1;
88 			break;
89 		case '?':
90 			return 1;
91 		default:
92 			break;
93 		}
94 
95 	if (wp[builtin_opt.optind] != NULL) {
96 		bi_errorf("usage: ulimit [-acdfHlmnpSst] [value]");
97 		return 1;
98 	}
99 
100 	/* Then parse and act on the actual limits, one at a time */
101 	ksh_getopt_reset(&builtin_opt, GF_ERROR);
102 	while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1)
103 		switch (optc) {
104 		case 'a':
105 		case 'H':
106 		case 'S':
107 			break;
108 		case '?':
109 			return 1;
110 		default:
111 			for (l = limits; l->name && l->option != optc; l++)
112 				;
113 			if (!l->name) {
114 				internal_errorf(0, "ulimit: %c", optc);
115 				return 1;
116 			}
117 			if (builtin_opt.optarg) {
118 				if (set_ulimit(l, builtin_opt.optarg, how))
119 					return 1;
120 			} else
121 				print_ulimit(l, how);
122 			break;
123 		}
124 
125 	wp += builtin_opt.optind;
126 
127 	if (all) {
128 		for (l = limits; l->name; l++) {
129 			shprintf("%-20s ", l->name);
130 			print_ulimit(l, how);
131 		}
132 	} else if (builtin_opt.optind == 1) {
133 		/* No limit specified, use file size */
134 		l = &limits[1];
135 		if (wp[0] != NULL) {
136 			if (set_ulimit(l, wp[0], how))
137 				return 1;
138 			wp++;
139 		} else {
140 			print_ulimit(l, how);
141 		}
142 	}
143 
144 	return 0;
145 }
146 
147 static int
148 set_ulimit(const struct limits *l, const char *v, int how)
149 {
150 	rlim_t		val = 0;
151 	struct rlimit	limit;
152 
153 	if (strcmp(v, "unlimited") == 0)
154 		val = RLIM_INFINITY;
155 	else {
156 		long rval;
157 
158 		if (!evaluate(v, &rval, KSH_RETURN_ERROR, false))
159 			return 1;
160 		/*
161 		 * Avoid problems caused by typos that evaluate misses due
162 		 * to evaluating unset parameters to 0...
163 		 * If this causes problems, will have to add parameter to
164 		 * evaluate() to control if unset params are 0 or an error.
165 		 */
166 		if (!rval && !digit(v[0])) {
167 			bi_errorf("invalid limit: %s", v);
168 			return 1;
169 		}
170 		val = (rlim_t)rval * l->factor;
171 	}
172 
173 	getrlimit(l->resource, &limit);
174 	if (how & SOFT)
175 		limit.rlim_cur = val;
176 	if (how & HARD)
177 		limit.rlim_max = val;
178 	if (setrlimit(l->resource, &limit) < 0) {
179 		if (errno == EPERM)
180 			bi_errorf("-%c exceeds allowable limit", l->option);
181 		else
182 			bi_errorf("bad -%c limit: %s", l->option,
183 			    strerror(errno));
184 		return 1;
185 	}
186 	return 0;
187 }
188 
189 static void
190 print_ulimit(const struct limits *l, int how)
191 {
192 	rlim_t		val = 0;
193 	struct rlimit	limit;
194 
195 	getrlimit(l->resource, &limit);
196 	if (how & SOFT)
197 		val = limit.rlim_cur;
198 	else if (how & HARD)
199 		val = limit.rlim_max;
200 	if (val == RLIM_INFINITY)
201 		shprintf("unlimited\n");
202 	else {
203 		val /= l->factor;
204 		shprintf("%ld\n", (long) val);
205 	}
206 }
207