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