xref: /netbsd-src/bin/sh/miscbltin.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
1 /*	$NetBSD: miscbltin.c,v 1.54 2023/10/05 20:33:31 kre Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: miscbltin.c,v 1.54 2023/10/05 20:33:31 kre Exp $");
41 #endif
42 #endif /* not lint */
43 
44 /*
45  * Miscellaneous builtins.
46  */
47 
48 #include <sys/types.h>		/* quad_t */
49 #include <sys/param.h>		/* BSD4_4 */
50 #include <sys/stat.h>
51 #include <sys/time.h>
52 #include <sys/resource.h>
53 #include <unistd.h>
54 #include <stdlib.h>
55 #include <ctype.h>
56 #include <errno.h>
57 
58 #include "shell.h"
59 #include "options.h"
60 #include "var.h"
61 #include "output.h"
62 #include "memalloc.h"
63 #include "error.h"
64 #include "builtins.h"
65 #include "mystring.h"
66 #include "redir.h"		/* for user_fd_limit */
67 
68 #undef rflag
69 
70 
71 
72 /*
73  * The read builtin.
74  * Backslashes escape the next char unless -r is specified.
75  *
76  * This uses unbuffered input, which may be avoidable in some cases.
77  *
78  * Note that if IFS=' :' then read x y should work so that:
79  * 'a b'	x='a', y='b'
80  * ' a b '	x='a', y='b'
81  * ':b'		x='',  y='b'
82  * ':'		x='',  y=''
83  * '::'		x='',  y=''
84  * ': :'	x='',  y=''
85  * ':::'	x='',  y='::'
86  * ':b c:'	x='',  y='b c:'
87  */
88 
89 int
90 readcmd(int argc, char **argv)
91 {
92 	char **ap;
93 	char c;
94 	char end;
95 	int rflag;
96 	char *prompt;
97 	const char *ifs;
98 	char *p;
99 	int startword;
100 	int status;
101 	int i;
102 	int is_ifs;
103 	int saveall = 0;
104 	ptrdiff_t wordlen = 0;
105 	char *newifs = NULL;
106 	struct stackmark mk;
107 
108 	end = '\n';				/* record delimiter */
109 	rflag = 0;
110 	prompt = NULL;
111 	while ((i = nextopt("d:p:r")) != '\0') {
112 		switch (i) {
113 		case 'd':
114 			end = *optionarg;	/* even if '\0' */
115 			break;
116 		case 'p':
117 			prompt = optionarg;
118 			break;
119 		case 'r':
120 			rflag = 1;
121 			break;
122 		}
123 	}
124 
125 	if (*(ap = argptr) == NULL)
126 		error("variable name required\n"
127 			"Usage: read [-r] [-p prompt] var...");
128 
129 	if (prompt && isatty(0)) {
130 		out2str(prompt);
131 		flushall();
132 	}
133 
134 	if ((ifs = bltinlookup("IFS", 1)) == NULL)
135 		ifs = " \t\n";
136 
137 	setstackmark(&mk);
138 	status = 0;
139 	startword = 2;
140 	STARTSTACKSTR(p);
141 	for (;;) {
142 		if (read(0, &c, 1) != 1) {
143 			status = 1;
144 			break;
145 		}
146 		if (c == '\\' && c != end && !rflag) {
147 			if (read(0, &c, 1) != 1) {
148 				status = 1;
149 				break;
150 			}
151 			if (c != '\n')	/* \ \n is always just removed */
152 				goto wdch;
153 			continue;
154 		}
155 		if (c == end)
156 			break;
157 		if (c == '\0')
158 			continue;
159 		if (strchr(ifs, c))
160 			is_ifs = strchr(" \t\n", c) ? 1 : 2;
161 		else
162 			is_ifs = 0;
163 
164 		if (startword != 0) {
165 			if (is_ifs == 1) {
166 				/* Ignore leading IFS whitespace */
167 				if (saveall)
168 					STPUTC(c, p);
169 				continue;
170 			}
171 			if (is_ifs == 2 && startword == 1) {
172 				/* Only one non-whitespace IFS per word */
173 				startword = 2;
174 				if (saveall)
175 					STPUTC(c, p);
176 				continue;
177 			}
178 		}
179 
180 		if (is_ifs == 0) {
181   wdch:;
182 			if (c == '\0')	/* always ignore attempts to input \0 */
183 				continue;
184 			/* append this character to the current variable */
185 			startword = 0;
186 			if (saveall)
187 				/* Not just a spare terminator */
188 				saveall++;
189 			STPUTC(c, p);
190 			wordlen = p - stackblock();
191 			continue;
192 		}
193 
194 		/* end of variable... */
195 		startword = is_ifs;
196 
197 		if (ap[1] == NULL) {
198 			/* Last variable needs all IFS chars */
199 			saveall++;
200 			STPUTC(c, p);
201 			continue;
202 		}
203 
204 		if (equal(*ap, "IFS")) {
205 			/*
206 			 * we must not alter the value of IFS, as our
207 			 * local "ifs" var is (perhaps) pointing at it,
208 			 * at best we would be using data after free()
209 			 * the next time we reference ifs - but that mem
210 			 * may have been reused for something different.
211 			 *
212 			 * note that this might occur several times
213 			 */
214 			STPUTC('\0', p);
215 			newifs = grabstackstr(p);
216 		} else {
217 			STACKSTRNUL(p);
218 			setvar(*ap, stackblock(), 0);
219 		}
220 		ap++;
221 		STARTSTACKSTR(p);
222 		wordlen = 0;
223 	}
224 	STACKSTRNUL(p);
225 
226 	/* Remove trailing IFS chars */
227 	for (; stackblock() + wordlen <= --p; *p = 0) {
228 		if (!strchr(ifs, *p))
229 			break;
230 		if (strchr(" \t\n", *p))
231 			/* Always remove whitespace */
232 			continue;
233 		if (saveall > 1)
234 			/* Don't remove non-whitespace unless it was naked */
235 			break;
236 	}
237 
238 	/*
239 	 * If IFS was one of the variables named, we can finally set it now
240 	 * (no further references to ifs will be made)
241 	 */
242 	if (newifs != NULL)
243 		setvar("IFS", newifs, 0);
244 
245 	/*
246 	 * Now we can assign to the final variable (which might
247 	 * also be IFS, hence the ordering here)
248 	 */
249 	setvar(*ap, stackblock(), 0);
250 
251 	/* Set any remaining args to "" */
252 	while (*++ap != NULL)
253 		setvar(*ap, nullstr, 0);
254 
255 	popstackmark(&mk);
256 	return status;
257 }
258 
259 
260 
261 int
262 umaskcmd(int argc, char **argv)
263 {
264 	char *ap;
265 	mode_t mask;
266 	int i;
267 	int symbolic_mode = 0;
268 
269 	while ((i = nextopt("S")) != '\0') {
270 		symbolic_mode = 1;
271 	}
272 
273 	INTOFF;
274 	mask = umask(0);
275 	umask(mask);
276 	INTON;
277 
278 	if ((ap = *argptr) == NULL) {
279 		if (symbolic_mode) {
280 			char u[4], g[4], o[4];
281 
282 			i = 0;
283 			if ((mask & S_IRUSR) == 0)
284 				u[i++] = 'r';
285 			if ((mask & S_IWUSR) == 0)
286 				u[i++] = 'w';
287 			if ((mask & S_IXUSR) == 0)
288 				u[i++] = 'x';
289 			u[i] = '\0';
290 
291 			i = 0;
292 			if ((mask & S_IRGRP) == 0)
293 				g[i++] = 'r';
294 			if ((mask & S_IWGRP) == 0)
295 				g[i++] = 'w';
296 			if ((mask & S_IXGRP) == 0)
297 				g[i++] = 'x';
298 			g[i] = '\0';
299 
300 			i = 0;
301 			if ((mask & S_IROTH) == 0)
302 				o[i++] = 'r';
303 			if ((mask & S_IWOTH) == 0)
304 				o[i++] = 'w';
305 			if ((mask & S_IXOTH) == 0)
306 				o[i++] = 'x';
307 			o[i] = '\0';
308 
309 			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
310 		} else {
311 			out1fmt("%.4o\n", mask);
312 		}
313 	} else {
314 		if (isdigit((unsigned char)*ap)) {
315 			int range = 0;
316 
317 			mask = 0;
318 			do {
319 				if (*ap >= '8' || *ap < '0')
320 					error("Not a valid octal number: '%s'",
321 					    *argptr);
322 				mask = (mask << 3) + (*ap - '0');
323 				if (mask & ~07777)
324 					range = 1;
325 			} while (*++ap != '\0');
326 			if (range)
327 			    error("Mask constant '%s' out of range", *argptr);
328 			umask(mask);
329 		} else {
330 			void *set;
331 
332 			INTOFF;
333 			if ((set = setmode(ap)) != 0) {
334 				mask = getmode(set, ~mask & 0777);
335 				ckfree(set);
336 			}
337 			INTON;
338 			if (!set)
339 				error("Cannot set mode `%s' (%s)", ap,
340 				    strerror(errno));
341 
342 			umask(~mask & 0777);
343 		}
344 	}
345 	flushout(out1);
346 	if (io_err(out1)) {
347 		out2str("umask: I/O error\n");
348 		return 1;
349 	}
350 	return 0;
351 }
352 
353 /*
354  * ulimit builtin
355  *
356  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
357  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
358  * ash by J.T. Conklin.
359  *
360  * Public domain.
361  */
362 
363 struct limits {
364 	const char *name;
365 	const char *unit;
366 	char	option;
367 	int8_t	cmd;		/* all RLIMIT_xxx are <= 127 */
368 	unsigned short factor;	/* multiply by to get rlim_{cur,max} values */
369 };
370 
371 #define	OPTSTRING_BASE "HSa"
372 
373 static const struct limits limits[] = {
374 #ifdef RLIMIT_CPU
375 	{ "time",	"seconds",	't',	RLIMIT_CPU,	   1 },
376 #define	OPTSTRING_t	OPTSTRING_BASE "t"
377 #else
378 #define	OPTSTRING_t	OPTSTRING_BASE
379 #endif
380 #ifdef RLIMIT_FSIZE
381 	{ "file",	"blocks",	'f',	RLIMIT_FSIZE,	 512 },
382 #define	OPTSTRING_f	OPTSTRING_t "f"
383 #else
384 #define	OPTSTRING_f	OPTSTRING_t
385 #endif
386 #ifdef RLIMIT_DATA
387 	{ "data",	"kbytes",	'd',	RLIMIT_DATA,	1024 },
388 #define	OPTSTRING_d	OPTSTRING_f "d"
389 #else
390 #define	OPTSTRING_d	OPTSTRING_f
391 #endif
392 #ifdef RLIMIT_STACK
393 	{ "stack",	"kbytes",	's',	RLIMIT_STACK,	1024 },
394 #define	OPTSTRING_s	OPTSTRING_d "s"
395 #else
396 #define	OPTSTRING_s	OPTSTRING_d
397 #endif
398 #ifdef RLIMIT_CORE
399 	{ "coredump",	"blocks",	'c',	RLIMIT_CORE,	 512 },
400 #define	OPTSTRING_c	OPTSTRING_s "c"
401 #else
402 #define	OPTSTRING_c	OPTSTRING_s
403 #endif
404 #ifdef RLIMIT_RSS
405 	{ "memory",	"kbytes",	'm',	RLIMIT_RSS,	1024 },
406 #define	OPTSTRING_m	OPTSTRING_c "m"
407 #else
408 #define	OPTSTRING_m	OPTSTRING_c
409 #endif
410 #ifdef RLIMIT_MEMLOCK
411 	{ "locked memory","kbytes",	'l',	RLIMIT_MEMLOCK, 1024 },
412 #define	OPTSTRING_l	OPTSTRING_m "l"
413 #else
414 #define	OPTSTRING_l	OPTSTRING_m
415 #endif
416 #ifdef RLIMIT_NTHR
417 	{ "thread",	"threads",	'r',	RLIMIT_NTHR,       1 },
418 #define	OPTSTRING_r	OPTSTRING_l "r"
419 #else
420 #define	OPTSTRING_r	OPTSTRING_l
421 #endif
422 #ifdef RLIMIT_NPROC
423 	{ "process",	"processes",	'p',	RLIMIT_NPROC,      1 },
424 #define	OPTSTRING_p	OPTSTRING_r "p"
425 #else
426 #define	OPTSTRING_p	OPTSTRING_r
427 #endif
428 #ifdef RLIMIT_NOFILE
429 	{ "nofiles",	"descriptors",	'n',	RLIMIT_NOFILE,     1 },
430 #define	OPTSTRING_n	OPTSTRING_p "n"
431 #else
432 #define	OPTSTRING_n	OPTSTRING_p
433 #endif
434 #ifdef RLIMIT_VMEM
435 	{ "vmemory",	"kbytes",	'v',	RLIMIT_VMEM,	1024 },
436 #define	OPTSTRING_v	OPTSTRING_n "v"
437 #else
438 #define	OPTSTRING_v	OPTSTRING_n
439 #endif
440 #ifdef RLIMIT_SWAP
441 	{ "swap",	"kbytes",	'w',	RLIMIT_SWAP,	1024 },
442 #define	OPTSTRING_w	OPTSTRING_v "w"
443 #else
444 #define	OPTSTRING_w	OPTSTRING_v
445 #endif
446 #ifdef RLIMIT_SBSIZE
447 	{ "sbsize",	"bytes",	'b',	RLIMIT_SBSIZE,	   1 },
448 #define	OPTSTRING_b	OPTSTRING_w "b"
449 #else
450 #define	OPTSTRING_b	OPTSTRING_w
451 #endif
452 	{ NULL,		NULL,		'\0',	0,		   0 }
453 };
454 #define	OPTSTRING	OPTSTRING_b
455 
456 int
457 ulimitcmd(int argc, char **argv)
458 {
459 	int	c;
460 	rlim_t val = 0;
461 	enum { SOFT = 0x1, HARD = 0x2 }
462 			how = 0, which;
463 	const struct limits	*l;
464 	int		set, all = 0;
465 	int		optc, what;
466 	struct rlimit	limit;
467 
468 	what = 'f';
469 	while ((optc = nextopt(OPTSTRING)) != '\0')
470 		switch (optc) {
471 		case 'H':
472 			how |= HARD;
473 			break;
474 		case 'S':
475 			how |= SOFT;
476 			break;
477 		case 'a':
478 			all = 1;
479 			break;
480 		default:
481 			what = optc;
482 		}
483 
484 	for (l = limits; l->name && l->option != what; l++)
485 		;
486 	if (!l->name)
487 		error("internal error (%c)", what);
488 
489 	set = *argptr ? 1 : 0;
490 	if (set) {
491 		char *p = *argptr;
492 
493 		if (all || argptr[1])
494 			error("too many arguments");
495 		if (how == 0)
496 			how = HARD | SOFT;
497 
498 		if (strcmp(p, "unlimited") == 0)
499 			val = RLIM_INFINITY;
500 		else {
501 			val = (rlim_t) 0;
502 
503 			while ((c = *p++) >= '0' && c <= '9') {
504 				if (val >= RLIM_INFINITY/10)
505 					error("%s: value overflow", *argptr);
506 				val = (val * 10);
507 				if (val >= RLIM_INFINITY - (long)(c - '0'))
508 					error("%s: value overflow", *argptr);
509 				val += (long)(c - '0');
510 			}
511 			if (c)
512 				error("%s: bad number", *argptr);
513 			if (val > RLIM_INFINITY / l->factor)
514 				error("%s: value overflow", *argptr);
515 			val *= l->factor;
516 		}
517 	} else if (how == 0)
518 		how = SOFT;
519 
520 	if (all) {
521 		for (l = limits; l->name; l++) {
522 			getrlimit(l->cmd, &limit);
523 			out1fmt("%-13s (-%c %-11s)    ", l->name, l->option,
524 			    l->unit);
525 
526 			which = how;
527 			while (which != 0) {
528 				if (which & SOFT) {
529 					val = limit.rlim_cur;
530 					which &= ~SOFT;
531 				} else if (which & HARD) {
532 					val = limit.rlim_max;
533 					which &= ~HARD;
534 				}
535 
536 				if (val == RLIM_INFINITY)
537 					out1fmt("unlimited");
538 				else {
539 					val /= l->factor;
540 #ifdef BSD4_4
541 					out1fmt("%9lld", (long long) val);
542 #else
543 					out1fmt("%9ld", (long) val);
544 #endif
545 				}
546 				out1fmt("%c", which ? '\t' : '\n');
547 			}
548 		}
549 		goto done;
550 	}
551 
552 	if (getrlimit(l->cmd, &limit) == -1)
553 		error("error getting limit (%s)", strerror(errno));
554 	if (set) {
555 		if (how & HARD)
556 			limit.rlim_max = val;
557 		if (how & SOFT)
558 			limit.rlim_cur = val;
559 		if (setrlimit(l->cmd, &limit) < 0)
560 			error("error setting limit (%s)", strerror(errno));
561 		if (l->cmd == RLIMIT_NOFILE)
562 			user_fd_limit = sysconf(_SC_OPEN_MAX);
563 	} else {
564 		if (how & SOFT)
565 			val = limit.rlim_cur;
566 		else if (how & HARD)
567 			val = limit.rlim_max;
568 
569 		if (val == RLIM_INFINITY)
570 			out1fmt("unlimited\n");
571 		else
572 		{
573 			val /= l->factor;
574 #ifdef BSD4_4
575 			out1fmt("%lld\n", (long long) val);
576 #else
577 			out1fmt("%ld\n", (long) val);
578 #endif
579 		}
580 	}
581   done:;
582 	flushout(out1);
583 	if (io_err(out1)) {
584 		out2str("ulimit: I/O error (stdout)\n");
585 		return 1;
586 	}
587 	return 0;
588 }
589