xref: /freebsd/bin/sh/miscbltin.c (revision 0e97acdf58fe27b09c4824a474b0344daf997c5f)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Kenneth Almquist.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
36 #endif
37 #endif /* not lint */
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 /*
42  * Miscellaneous builtins.
43  */
44 
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/time.h>
48 #include <sys/resource.h>
49 #include <unistd.h>
50 #include <errno.h>
51 #include <stdint.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 
55 #include "shell.h"
56 #include "options.h"
57 #include "var.h"
58 #include "output.h"
59 #include "memalloc.h"
60 #include "error.h"
61 #include "mystring.h"
62 #include "syntax.h"
63 #include "trap.h"
64 
65 #undef eflag
66 
67 int readcmd(int, char **);
68 int umaskcmd(int, char **);
69 int ulimitcmd(int, char **);
70 
71 /*
72  * The read builtin.  The -r option causes backslashes to be treated like
73  * ordinary characters.
74  *
75  * This uses unbuffered input, which may be avoidable in some cases.
76  *
77  * Note that if IFS=' :' then read x y should work so that:
78  * 'a b'	x='a', y='b'
79  * ' a b '	x='a', y='b'
80  * ':b'		x='',  y='b'
81  * ':'		x='',  y=''
82  * '::'		x='',  y=''
83  * ': :'	x='',  y=''
84  * ':::'	x='',  y='::'
85  * ':b c:'	x='',  y='b c:'
86  */
87 
88 int
89 readcmd(int argc __unused, char **argv __unused)
90 {
91 	char **ap;
92 	int backslash;
93 	char c;
94 	int rflag;
95 	char *prompt;
96 	const char *ifs;
97 	char *p;
98 	int startword;
99 	int status;
100 	int i;
101 	int is_ifs;
102 	int saveall = 0;
103 	struct timeval tv;
104 	char *tvptr;
105 	fd_set ifds;
106 	ssize_t nread;
107 	int sig;
108 
109 	rflag = 0;
110 	prompt = NULL;
111 	tv.tv_sec = -1;
112 	tv.tv_usec = 0;
113 	while ((i = nextopt("erp:t:")) != '\0') {
114 		switch(i) {
115 		case 'p':
116 			prompt = shoptarg;
117 			break;
118 		case 'e':
119 			break;
120 		case 'r':
121 			rflag = 1;
122 			break;
123 		case 't':
124 			tv.tv_sec = strtol(shoptarg, &tvptr, 0);
125 			if (tvptr == shoptarg)
126 				error("timeout value");
127 			switch(*tvptr) {
128 			case 0:
129 			case 's':
130 				break;
131 			case 'h':
132 				tv.tv_sec *= 60;
133 				/* FALLTHROUGH */
134 			case 'm':
135 				tv.tv_sec *= 60;
136 				break;
137 			default:
138 				error("timeout unit");
139 			}
140 			break;
141 		}
142 	}
143 	if (prompt && isatty(0)) {
144 		out2str(prompt);
145 		flushall();
146 	}
147 	if (*(ap = argptr) == NULL)
148 		error("arg count");
149 	if ((ifs = bltinlookup("IFS", 1)) == NULL)
150 		ifs = " \t\n";
151 
152 	if (tv.tv_sec >= 0) {
153 		/*
154 		 * Wait for something to become available.
155 		 */
156 		FD_ZERO(&ifds);
157 		FD_SET(0, &ifds);
158 		status = select(1, &ifds, NULL, NULL, &tv);
159 		/*
160 		 * If there's nothing ready, return an error.
161 		 */
162 		if (status <= 0) {
163 			sig = pendingsig;
164 			return (128 + (sig != 0 ? sig : SIGALRM));
165 		}
166 	}
167 
168 	status = 0;
169 	startword = 2;
170 	backslash = 0;
171 	STARTSTACKSTR(p);
172 	for (;;) {
173 		nread = read(STDIN_FILENO, &c, 1);
174 		if (nread == -1) {
175 			if (errno == EINTR) {
176 				sig = pendingsig;
177 				if (sig == 0)
178 					continue;
179 				status = 128 + sig;
180 				break;
181 			}
182 			warning("read error: %s", strerror(errno));
183 			status = 2;
184 			break;
185 		} else if (nread != 1) {
186 			status = 1;
187 			break;
188 		}
189 		if (c == '\0')
190 			continue;
191 		CHECKSTRSPACE(1, p);
192 		if (backslash) {
193 			backslash = 0;
194 			startword = 0;
195 			if (c != '\n')
196 				USTPUTC(c, p);
197 			continue;
198 		}
199 		if (!rflag && c == '\\') {
200 			backslash++;
201 			continue;
202 		}
203 		if (c == '\n')
204 			break;
205 		if (strchr(ifs, c))
206 			is_ifs = strchr(" \t\n", c) ? 1 : 2;
207 		else
208 			is_ifs = 0;
209 
210 		if (startword != 0) {
211 			if (is_ifs == 1) {
212 				/* Ignore leading IFS whitespace */
213 				if (saveall)
214 					USTPUTC(c, p);
215 				continue;
216 			}
217 			if (is_ifs == 2 && startword == 1) {
218 				/* Only one non-whitespace IFS per word */
219 				startword = 2;
220 				if (saveall)
221 					USTPUTC(c, p);
222 				continue;
223 			}
224 		}
225 
226 		if (is_ifs == 0) {
227 			/* append this character to the current variable */
228 			startword = 0;
229 			if (saveall)
230 				/* Not just a spare terminator */
231 				saveall++;
232 			USTPUTC(c, p);
233 			continue;
234 		}
235 
236 		/* end of variable... */
237 		startword = is_ifs;
238 
239 		if (ap[1] == NULL) {
240 			/* Last variable needs all IFS chars */
241 			saveall++;
242 			USTPUTC(c, p);
243 			continue;
244 		}
245 
246 		STACKSTRNUL(p);
247 		setvar(*ap, stackblock(), 0);
248 		ap++;
249 		STARTSTACKSTR(p);
250 	}
251 	STACKSTRNUL(p);
252 
253 	/* Remove trailing IFS chars */
254 	for (; stackblock() <= --p; *p = 0) {
255 		if (!strchr(ifs, *p))
256 			break;
257 		if (strchr(" \t\n", *p))
258 			/* Always remove whitespace */
259 			continue;
260 		if (saveall > 1)
261 			/* Don't remove non-whitespace unless it was naked */
262 			break;
263 	}
264 	setvar(*ap, stackblock(), 0);
265 
266 	/* Set any remaining args to "" */
267 	while (*++ap != NULL)
268 		setvar(*ap, nullstr, 0);
269 	return status;
270 }
271 
272 
273 
274 int
275 umaskcmd(int argc __unused, char **argv __unused)
276 {
277 	char *ap;
278 	int mask;
279 	int i;
280 	int symbolic_mode = 0;
281 
282 	while ((i = nextopt("S")) != '\0') {
283 		symbolic_mode = 1;
284 	}
285 
286 	INTOFF;
287 	mask = umask(0);
288 	umask(mask);
289 	INTON;
290 
291 	if ((ap = *argptr) == NULL) {
292 		if (symbolic_mode) {
293 			char u[4], g[4], o[4];
294 
295 			i = 0;
296 			if ((mask & S_IRUSR) == 0)
297 				u[i++] = 'r';
298 			if ((mask & S_IWUSR) == 0)
299 				u[i++] = 'w';
300 			if ((mask & S_IXUSR) == 0)
301 				u[i++] = 'x';
302 			u[i] = '\0';
303 
304 			i = 0;
305 			if ((mask & S_IRGRP) == 0)
306 				g[i++] = 'r';
307 			if ((mask & S_IWGRP) == 0)
308 				g[i++] = 'w';
309 			if ((mask & S_IXGRP) == 0)
310 				g[i++] = 'x';
311 			g[i] = '\0';
312 
313 			i = 0;
314 			if ((mask & S_IROTH) == 0)
315 				o[i++] = 'r';
316 			if ((mask & S_IWOTH) == 0)
317 				o[i++] = 'w';
318 			if ((mask & S_IXOTH) == 0)
319 				o[i++] = 'x';
320 			o[i] = '\0';
321 
322 			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
323 		} else {
324 			out1fmt("%.4o\n", mask);
325 		}
326 	} else {
327 		if (is_digit(*ap)) {
328 			mask = 0;
329 			do {
330 				if (*ap >= '8' || *ap < '0')
331 					error("Illegal number: %s", *argptr);
332 				mask = (mask << 3) + (*ap - '0');
333 			} while (*++ap != '\0');
334 			umask(mask);
335 		} else {
336 			void *set;
337 			INTOFF;
338 			if ((set = setmode (ap)) == 0)
339 				error("Illegal number: %s", ap);
340 
341 			mask = getmode (set, ~mask & 0777);
342 			umask(~mask & 0777);
343 			free(set);
344 			INTON;
345 		}
346 	}
347 	return 0;
348 }
349 
350 /*
351  * ulimit builtin
352  *
353  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
354  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
355  * ash by J.T. Conklin.
356  *
357  * Public domain.
358  */
359 
360 struct limits {
361 	const char *name;
362 	const char *units;
363 	int	cmd;
364 	int	factor;	/* multiply by to get rlim_{cur,max} values */
365 	char	option;
366 };
367 
368 static const struct limits limits[] = {
369 #ifdef RLIMIT_CPU
370 	{ "cpu time",		"seconds",	RLIMIT_CPU,	   1, 't' },
371 #endif
372 #ifdef RLIMIT_FSIZE
373 	{ "file size",		"512-blocks",	RLIMIT_FSIZE,	 512, 'f' },
374 #endif
375 #ifdef RLIMIT_DATA
376 	{ "data seg size",	"kbytes",	RLIMIT_DATA,	1024, 'd' },
377 #endif
378 #ifdef RLIMIT_STACK
379 	{ "stack size",		"kbytes",	RLIMIT_STACK,	1024, 's' },
380 #endif
381 #ifdef  RLIMIT_CORE
382 	{ "core file size",	"512-blocks",	RLIMIT_CORE,	 512, 'c' },
383 #endif
384 #ifdef RLIMIT_RSS
385 	{ "max memory size",	"kbytes",	RLIMIT_RSS,	1024, 'm' },
386 #endif
387 #ifdef RLIMIT_MEMLOCK
388 	{ "locked memory",	"kbytes",	RLIMIT_MEMLOCK, 1024, 'l' },
389 #endif
390 #ifdef RLIMIT_NPROC
391 	{ "max user processes",	(char *)0,	RLIMIT_NPROC,      1, 'u' },
392 #endif
393 #ifdef RLIMIT_NOFILE
394 	{ "open files",		(char *)0,	RLIMIT_NOFILE,     1, 'n' },
395 #endif
396 #ifdef RLIMIT_VMEM
397 	{ "virtual mem size",	"kbytes",	RLIMIT_VMEM,	1024, 'v' },
398 #endif
399 #ifdef RLIMIT_SWAP
400 	{ "swap limit",		"kbytes",	RLIMIT_SWAP,	1024, 'w' },
401 #endif
402 #ifdef RLIMIT_SBSIZE
403 	{ "sbsize",		"bytes",	RLIMIT_SBSIZE,	   1, 'b' },
404 #endif
405 #ifdef RLIMIT_NPTS
406 	{ "pseudo-terminals",	(char *)0,	RLIMIT_NPTS,	   1, 'p' },
407 #endif
408 #ifdef RLIMIT_KQUEUES
409 	{ "kqueues",		(char *)0,	RLIMIT_KQUEUES,	   1, 'k' },
410 #endif
411 	{ (char *) 0,		(char *)0,	0,		   0, '\0' }
412 };
413 
414 enum limithow { SOFT = 0x1, HARD = 0x2 };
415 
416 static void
417 printlimit(enum limithow how, const struct rlimit *limit,
418     const struct limits *l)
419 {
420 	rlim_t val = 0;
421 
422 	if (how & SOFT)
423 		val = limit->rlim_cur;
424 	else if (how & HARD)
425 		val = limit->rlim_max;
426 	if (val == RLIM_INFINITY)
427 		out1str("unlimited\n");
428 	else
429 	{
430 		val /= l->factor;
431 		out1fmt("%jd\n", (intmax_t)val);
432 	}
433 }
434 
435 int
436 ulimitcmd(int argc __unused, char **argv __unused)
437 {
438 	rlim_t val = 0;
439 	enum limithow how = SOFT | HARD;
440 	const struct limits	*l;
441 	int		set, all = 0;
442 	int		optc, what;
443 	struct rlimit	limit;
444 
445 	what = 'f';
446 	while ((optc = nextopt("HSatfdsmcnuvlbpwk")) != '\0')
447 		switch (optc) {
448 		case 'H':
449 			how = HARD;
450 			break;
451 		case 'S':
452 			how = SOFT;
453 			break;
454 		case 'a':
455 			all = 1;
456 			break;
457 		default:
458 			what = optc;
459 		}
460 
461 	for (l = limits; l->name && l->option != what; l++)
462 		;
463 	if (!l->name)
464 		error("internal error (%c)", what);
465 
466 	set = *argptr ? 1 : 0;
467 	if (set) {
468 		char *p = *argptr;
469 
470 		if (all || argptr[1])
471 			error("too many arguments");
472 		if (strcmp(p, "unlimited") == 0)
473 			val = RLIM_INFINITY;
474 		else {
475 			char *end;
476 			uintmax_t uval;
477 
478 			if (*p < '0' || *p > '9')
479 				error("bad number");
480 			errno = 0;
481 			uval = strtoumax(p, &end, 10);
482 			if (errno != 0 || *end != '\0')
483 				error("bad number");
484 			if (uval > UINTMAX_MAX / l->factor)
485 				error("bad number");
486 			uval *= l->factor;
487 			val = (rlim_t)uval;
488 			if (val < 0 || (uintmax_t)val != uval ||
489 			    val == RLIM_INFINITY)
490 				error("bad number");
491 		}
492 	}
493 	if (all) {
494 		for (l = limits; l->name; l++) {
495 			char optbuf[40];
496 			if (getrlimit(l->cmd, &limit) < 0)
497 				error("can't get limit: %s", strerror(errno));
498 
499 			if (l->units)
500 				snprintf(optbuf, sizeof(optbuf),
501 					"(%s, -%c) ", l->units, l->option);
502 			else
503 				snprintf(optbuf, sizeof(optbuf),
504 					"(-%c) ", l->option);
505 			out1fmt("%-18s %18s ", l->name, optbuf);
506 			printlimit(how, &limit, l);
507 		}
508 		return 0;
509 	}
510 
511 	if (getrlimit(l->cmd, &limit) < 0)
512 		error("can't get limit: %s", strerror(errno));
513 	if (set) {
514 		if (how & SOFT)
515 			limit.rlim_cur = val;
516 		if (how & HARD)
517 			limit.rlim_max = val;
518 		if (setrlimit(l->cmd, &limit) < 0)
519 			error("bad limit: %s", strerror(errno));
520 	} else
521 		printlimit(how, &limit, l);
522 	return 0;
523 }
524