1 /* vi: set sw=4 ts=4: */
2 /*
3  * Adapted from ash applet code
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Kenneth Almquist.
7  *
8  * Copyright (c) 1989, 1991, 1993, 1994
9  *      The Regents of the University of California.  All rights reserved.
10  *
11  * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
12  * was re-ported from NetBSD and debianized.
13  *
14  * Copyright (c) 2010 Denys Vlasenko
15  * Split from ash.c
16  *
17  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
18  */
19 #include "libbb.h"
20 #include "shell_common.h"
21 #include <sys/resource.h> /* getrlimit */
22 
23 const char defifsvar[] ALIGN1 = "IFS= \t\n";
24 const char defoptindvar[] ALIGN1 = "OPTIND=1";
25 
26 
is_well_formed_var_name(const char * s,char terminator)27 int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
28 {
29 	if (!s || !(isalpha(*s) || *s == '_'))
30 		return 0;
31 
32 	do
33 		s++;
34 	while (isalnum(*s) || *s == '_');
35 
36 	return *s == terminator;
37 }
38 
39 /* read builtin */
40 
41 /* Needs to be interruptible: shell must handle traps and shell-special signals
42  * while inside read. To implement this, be sure to not loop on EINTR
43  * and return errno == EINTR reliably.
44  */
45 //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
46 //string. hush naturally has it, and ash has setvareq().
47 //Here we can simply store "VAR=" at buffer start and store read data directly
48 //after "=", then pass buffer to setvar() to consume.
49 const char* FAST_FUNC
shell_builtin_read(void FAST_FUNC (* setvar)(const char * name,const char * val),char ** argv,const char * ifs,int read_flags,const char * opt_n,const char * opt_p,const char * opt_t,const char * opt_u)50 shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
51 	char       **argv,
52 	const char *ifs,
53 	int        read_flags,
54 	const char *opt_n,
55 	const char *opt_p,
56 	const char *opt_t,
57 	const char *opt_u
58 )
59 {
60 	unsigned err;
61 	unsigned end_ms; /* -t TIMEOUT */
62 	int fd; /* -u FD */
63 	int nchars; /* -n NUM */
64 	char **pp;
65 	char *buffer;
66 	struct termios tty, old_tty;
67 	const char *retval;
68 	int bufpos; /* need to be able to hold -1 */
69 	int startword;
70 	smallint backslash;
71 
72 	errno = err = 0;
73 
74 	pp = argv;
75 	while (*pp) {
76 		if (!is_well_formed_var_name(*pp, '\0')) {
77 			/* Mimic bash message */
78 			bb_error_msg("read: '%s': not a valid identifier", *pp);
79 			return (const char *)(uintptr_t)1;
80 		}
81 		pp++;
82 	}
83 
84 	nchars = 0; /* if != 0, -n is in effect */
85 	if (opt_n) {
86 		nchars = bb_strtou(opt_n, NULL, 10);
87 		if (nchars < 0 || errno)
88 			return "invalid count";
89 		/* note: "-n 0": off (bash 3.2 does this too) */
90 	}
91 	end_ms = 0;
92 	if (opt_t) {
93 		end_ms = bb_strtou(opt_t, NULL, 10);
94 		if (errno || end_ms > UINT_MAX / 2048)
95 			return "invalid timeout";
96 		end_ms *= 1000;
97 #if 0 /* even bash has no -t N.NNN support */
98 		ts.tv_sec = bb_strtou(opt_t, &p, 10);
99 		ts.tv_usec = 0;
100 		/* EINVAL means number is ok, but not terminated by NUL */
101 		if (*p == '.' && errno == EINVAL) {
102 			char *p2;
103 			if (*++p) {
104 				int scale;
105 				ts.tv_usec = bb_strtou(p, &p2, 10);
106 				if (errno)
107 					return "invalid timeout";
108 				scale = p2 - p;
109 				/* normalize to usec */
110 				if (scale > 6)
111 					return "invalid timeout";
112 				while (scale++ < 6)
113 					ts.tv_usec *= 10;
114 			}
115 		} else if (ts.tv_sec < 0 || errno) {
116 			return "invalid timeout";
117 		}
118 		if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
119 			return "invalid timeout";
120 		}
121 #endif /* if 0 */
122 	}
123 	fd = STDIN_FILENO;
124 	if (opt_u) {
125 		fd = bb_strtou(opt_u, NULL, 10);
126 		if (fd < 0 || errno)
127 			return "invalid file descriptor";
128 	}
129 
130 	if (opt_p && isatty(fd)) {
131 		fputs(opt_p, stderr);
132 		fflush_all();
133 	}
134 
135 	if (ifs == NULL)
136 		ifs = defifs;
137 
138 	if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
139 		tcgetattr(fd, &tty);
140 		old_tty = tty;
141 		if (nchars) {
142 			tty.c_lflag &= ~ICANON;
143 			// Setting it to more than 1 breaks poll():
144 			// it blocks even if there's data. !??
145 			//tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
146 			/* reads would block only if < 1 char is available */
147 			tty.c_cc[VMIN] = 1;
148 			/* no timeout (reads block forever) */
149 			tty.c_cc[VTIME] = 0;
150 		}
151 		if (read_flags & BUILTIN_READ_SILENT) {
152 			tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
153 		}
154 		/* This forces execution of "restoring" tcgetattr later */
155 		read_flags |= BUILTIN_READ_SILENT;
156 		/* if tcgetattr failed, tcsetattr will fail too.
157 		 * Ignoring, it's harmless. */
158 		tcsetattr(fd, TCSANOW, &tty);
159 	}
160 
161 	retval = (const char *)(uintptr_t)0;
162 	startword = 1;
163 	backslash = 0;
164 	if (end_ms) /* NB: end_ms stays nonzero: */
165 		end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
166 	buffer = NULL;
167 	bufpos = 0;
168 	do {
169 		char c;
170 		struct pollfd pfd[1];
171 		int timeout;
172 
173 		if ((bufpos & 0xff) == 0)
174 			buffer = xrealloc(buffer, bufpos + 0x101);
175 
176 		timeout = -1;
177 		if (end_ms) {
178 			timeout = end_ms - (unsigned)monotonic_ms();
179 			if (timeout <= 0) { /* already late? */
180 				retval = (const char *)(uintptr_t)1;
181 				goto ret;
182 			}
183 		}
184 
185 		/* We must poll even if timeout is -1:
186 		 * we want to be interrupted if signal arrives,
187 		 * regardless of SA_RESTART-ness of that signal!
188 		 */
189 		errno = 0;
190 		pfd[0].fd = fd;
191 		pfd[0].events = POLLIN;
192 		if (poll(pfd, 1, timeout) != 1) {
193 			/* timed out, or EINTR */
194 			err = errno;
195 			retval = (const char *)(uintptr_t)1;
196 			goto ret;
197 		}
198 		if (read(fd, &buffer[bufpos], 1) != 1) {
199 			err = errno;
200 			retval = (const char *)(uintptr_t)1;
201 			break;
202 		}
203 
204 		c = buffer[bufpos];
205 		if (c == '\0')
206 			continue;
207 		if (backslash) {
208 			backslash = 0;
209 			if (c != '\n')
210 				goto put;
211 			continue;
212 		}
213 		if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
214 			backslash = 1;
215 			continue;
216 		}
217 		if (c == '\n')
218 			break;
219 
220 		/* $IFS splitting. NOT done if we run "read"
221 		 * without variable names (bash compat).
222 		 * Thus, "read" and "read REPLY" are not the same.
223 		 */
224 		if (argv[0]) {
225 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
226 			const char *is_ifs = strchr(ifs, c);
227 			if (startword && is_ifs) {
228 				if (isspace(c))
229 					continue;
230 				/* it is a non-space ifs char */
231 				startword--;
232 				if (startword == 1) /* first one? */
233 					continue; /* yes, it is not next word yet */
234 			}
235 			startword = 0;
236 			if (argv[1] != NULL && is_ifs) {
237 				buffer[bufpos] = '\0';
238 				bufpos = 0;
239 				setvar(*argv, buffer);
240 				argv++;
241 				/* can we skip one non-space ifs char? (2: yes) */
242 				startword = isspace(c) ? 2 : 1;
243 				continue;
244 			}
245 		}
246  put:
247 		bufpos++;
248 	} while (--nchars);
249 
250 	if (argv[0]) {
251 		/* Remove trailing space $IFS chars */
252 		while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
253 			continue;
254 		buffer[bufpos + 1] = '\0';
255 		/* Use the remainder as a value for the next variable */
256 		setvar(*argv, buffer);
257 		/* Set the rest to "" */
258 		while (*++argv)
259 			setvar(*argv, "");
260 	} else {
261 		/* Note: no $IFS removal */
262 		buffer[bufpos] = '\0';
263 		setvar("REPLY", buffer);
264 	}
265 
266  ret:
267 	free(buffer);
268 	if (read_flags & BUILTIN_READ_SILENT)
269 		tcsetattr(fd, TCSANOW, &old_tty);
270 
271 	errno = err;
272 	return retval;
273 }
274 
275 /* ulimit builtin */
276 
277 struct limits {
278 	uint8_t cmd;            /* RLIMIT_xxx fit into it */
279 	uint8_t factor_shift;   /* shift by to get rlim_{cur,max} values */
280 	char option;
281 	const char *name;
282 };
283 
284 static const struct limits limits_tbl[] = {
285 #ifdef RLIMIT_FSIZE
286 	{ RLIMIT_FSIZE,		9,	'f',	"file size (blocks)" },
287 #endif
288 #ifdef RLIMIT_CPU
289 	{ RLIMIT_CPU,		0,	't',	"cpu time (seconds)" },
290 #endif
291 #ifdef RLIMIT_DATA
292 	{ RLIMIT_DATA,		10,	'd',	"data seg size (kb)" },
293 #endif
294 #ifdef RLIMIT_STACK
295 	{ RLIMIT_STACK,		10,	's',	"stack size (kb)" },
296 #endif
297 #ifdef RLIMIT_CORE
298 	{ RLIMIT_CORE,		9,	'c',	"core file size (blocks)" },
299 #endif
300 #ifdef RLIMIT_RSS
301 	{ RLIMIT_RSS,		10,	'm',	"resident set size (kb)" },
302 #endif
303 #ifdef RLIMIT_MEMLOCK
304 	{ RLIMIT_MEMLOCK,	10,	'l',	"locked memory (kb)" },
305 #endif
306 #ifdef RLIMIT_NPROC
307 	{ RLIMIT_NPROC,		0,	'p',	"processes" },
308 #endif
309 #ifdef RLIMIT_NOFILE
310 	{ RLIMIT_NOFILE,	0,	'n',	"file descriptors" },
311 #endif
312 #ifdef RLIMIT_AS
313 	{ RLIMIT_AS,		10,	'v',	"address space (kb)" },
314 #endif
315 #ifdef RLIMIT_LOCKS
316 	{ RLIMIT_LOCKS,		0,	'w',	"locks" },
317 #endif
318 #ifdef RLIMIT_NICE
319 	{ RLIMIT_NICE,		0,	'e',	"scheduling priority" },
320 #endif
321 #ifdef RLIMIT_RTPRIO
322 	{ RLIMIT_RTPRIO,	0,	'r',	"real-time priority" },
323 #endif
324 };
325 
326 enum {
327 	OPT_hard = (1 << 0),
328 	OPT_soft = (1 << 1),
329 };
330 
331 /* "-": treat args as parameters of option with ASCII code 1 */
332 static const char ulimit_opt_string[] ALIGN1 = "-HSa"
333 #ifdef RLIMIT_FSIZE
334 			"f::"
335 #endif
336 #ifdef RLIMIT_CPU
337 			"t::"
338 #endif
339 #ifdef RLIMIT_DATA
340 			"d::"
341 #endif
342 #ifdef RLIMIT_STACK
343 			"s::"
344 #endif
345 #ifdef RLIMIT_CORE
346 			"c::"
347 #endif
348 #ifdef RLIMIT_RSS
349 			"m::"
350 #endif
351 #ifdef RLIMIT_MEMLOCK
352 			"l::"
353 #endif
354 #ifdef RLIMIT_NPROC
355 			"p::"
356 #endif
357 #ifdef RLIMIT_NOFILE
358 			"n::"
359 #endif
360 #ifdef RLIMIT_AS
361 			"v::"
362 #endif
363 #ifdef RLIMIT_LOCKS
364 			"w::"
365 #endif
366 #ifdef RLIMIT_NICE
367 			"e::"
368 #endif
369 #ifdef RLIMIT_RTPRIO
370 			"r::"
371 #endif
372 			;
373 
printlim(unsigned opts,const struct rlimit * limit,const struct limits * l)374 static void printlim(unsigned opts, const struct rlimit *limit,
375 			const struct limits *l)
376 {
377 	rlim_t val;
378 
379 	val = limit->rlim_max;
380 	if (!(opts & OPT_hard))
381 		val = limit->rlim_cur;
382 
383 	if (val == RLIM_INFINITY)
384 		puts("unlimited");
385 	else {
386 		val >>= l->factor_shift;
387 		printf("%llu\n", (long long) val);
388 	}
389 }
390 
391 int FAST_FUNC
shell_builtin_ulimit(char ** argv)392 shell_builtin_ulimit(char **argv)
393 {
394 	unsigned opts;
395 	unsigned argc;
396 
397 	/* We can't use getopt32: need to handle commands like
398 	 * ulimit 123 -c2 -l 456
399 	 */
400 
401 	/* In case getopt was already called:
402 	 * reset the libc getopt() function, which keeps internal state.
403 	 */
404 #ifdef __GLIBC__
405 	optind = 0;
406 #else /* BSD style */
407 	optind = 1;
408 	/* optreset = 1; */
409 #endif
410 	/* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
411 
412 	argc = 1;
413 	while (argv[argc])
414 		argc++;
415 
416 	opts = 0;
417 	while (1) {
418 		struct rlimit limit;
419 		const struct limits *l;
420 		int opt_char = getopt(argc, argv, ulimit_opt_string);
421 
422 		if (opt_char == -1)
423 			break;
424 		if (opt_char == 'H') {
425 			opts |= OPT_hard;
426 			continue;
427 		}
428 		if (opt_char == 'S') {
429 			opts |= OPT_soft;
430 			continue;
431 		}
432 
433 		if (opt_char == 'a') {
434 			for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
435 				getrlimit(l->cmd, &limit);
436 				printf("-%c: %-30s ", l->option, l->name);
437 				printlim(opts, &limit, l);
438 			}
439 			continue;
440 		}
441 
442 		if (opt_char == 1)
443 			opt_char = 'f';
444 		for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
445 			if (opt_char == l->option) {
446 				char *val_str;
447 
448 				getrlimit(l->cmd, &limit);
449 
450 				val_str = optarg;
451 				if (!val_str && argv[optind] && argv[optind][0] != '-')
452 					val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
453 				if (val_str) {
454 					rlim_t val;
455 
456 					if (strcmp(val_str, "unlimited") == 0)
457 						val = RLIM_INFINITY;
458 					else {
459 						if (sizeof(val) == sizeof(int))
460 							val = bb_strtou(val_str, NULL, 10);
461 						else if (sizeof(val) == sizeof(long))
462 							val = bb_strtoul(val_str, NULL, 10);
463 						else
464 							val = bb_strtoull(val_str, NULL, 10);
465 						if (errno) {
466 							bb_error_msg("invalid number '%s'", val_str);
467 							return EXIT_FAILURE;
468 						}
469 						val <<= l->factor_shift;
470 					}
471 //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
472 					/* from man bash: "If neither -H nor -S
473 					 * is specified, both the soft and hard
474 					 * limits are set. */
475 					if (!opts)
476 						opts = OPT_hard + OPT_soft;
477 					if (opts & OPT_hard)
478 						limit.rlim_max = val;
479 					if (opts & OPT_soft)
480 						limit.rlim_cur = val;
481 //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
482 					if (setrlimit(l->cmd, &limit) < 0) {
483 						bb_perror_msg("error setting limit");
484 						return EXIT_FAILURE;
485 					}
486 				} else {
487 					printlim(opts, &limit, l);
488 				}
489 				break;
490 			}
491 		} /* for (every possible opt) */
492 
493 		if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
494 			/* bad option. getopt already complained. */
495 			break;
496 		}
497 	} /* while (there are options) */
498 
499 	return 0;
500 }
501