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