xref: /netbsd/bin/sh/miscbltin.c (revision 07c06699)
1 /*	$NetBSD: miscbltin.c,v 1.53 2022/12/11 08:23:10 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.53 2022/12/11 08:23:10 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
readcmd(int argc,char ** argv)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 
106 	end = '\n';				/* record delimiter */
107 	rflag = 0;
108 	prompt = NULL;
109 	while ((i = nextopt("d:p:r")) != '\0') {
110 		switch (i) {
111 		case 'd':
112 			end = *optionarg;	/* even if '\0' */
113 			break;
114 		case 'p':
115 			prompt = optionarg;
116 			break;
117 		case 'r':
118 			rflag = 1;
119 			break;
120 		}
121 	}
122 
123 	if (*(ap = argptr) == NULL)
124 		error("variable name required\n"
125 			"Usage: read [-r] [-p prompt] var...");
126 
127 	if (prompt && isatty(0)) {
128 		out2str(prompt);
129 		flushall();
130 	}
131 
132 	if ((ifs = bltinlookup("IFS", 1)) == NULL)
133 		ifs = " \t\n";
134 
135 	status = 0;
136 	startword = 2;
137 	STARTSTACKSTR(p);
138 	for (;;) {
139 		if (read(0, &c, 1) != 1) {
140 			status = 1;
141 			break;
142 		}
143 		if (c == '\\' && c != end && !rflag) {
144 			if (read(0, &c, 1) != 1) {
145 				status = 1;
146 				break;
147 			}
148 			if (c != '\n')	/* \ \n is always just removed */
149 				goto wdch;
150 			continue;
151 		}
152 		if (c == end)
153 			break;
154 		if (c == '\0')
155 			continue;
156 		if (strchr(ifs, c))
157 			is_ifs = strchr(" \t\n", c) ? 1 : 2;
158 		else
159 			is_ifs = 0;
160 
161 		if (startword != 0) {
162 			if (is_ifs == 1) {
163 				/* Ignore leading IFS whitespace */
164 				if (saveall)
165 					STPUTC(c, p);
166 				continue;
167 			}
168 			if (is_ifs == 2 && startword == 1) {
169 				/* Only one non-whitespace IFS per word */
170 				startword = 2;
171 				if (saveall)
172 					STPUTC(c, p);
173 				continue;
174 			}
175 		}
176 
177 		if (is_ifs == 0) {
178   wdch:;
179 			if (c == '\0')	/* always ignore attempts to input \0 */
180 				continue;
181 			/* append this character to the current variable */
182 			startword = 0;
183 			if (saveall)
184 				/* Not just a spare terminator */
185 				saveall++;
186 			STPUTC(c, p);
187 			wordlen = p - stackblock();
188 			continue;
189 		}
190 
191 		/* end of variable... */
192 		startword = is_ifs;
193 
194 		if (ap[1] == NULL) {
195 			/* Last variable needs all IFS chars */
196 			saveall++;
197 			STPUTC(c, p);
198 			continue;
199 		}
200 
201 		STACKSTRNUL(p);
202 		setvar(*ap, stackblock(), 0);
203 		ap++;
204 		STARTSTACKSTR(p);
205 		wordlen = 0;
206 	}
207 	STACKSTRNUL(p);
208 
209 	/* Remove trailing IFS chars */
210 	for (; stackblock() + wordlen <= --p; *p = 0) {
211 		if (!strchr(ifs, *p))
212 			break;
213 		if (strchr(" \t\n", *p))
214 			/* Always remove whitespace */
215 			continue;
216 		if (saveall > 1)
217 			/* Don't remove non-whitespace unless it was naked */
218 			break;
219 	}
220 	setvar(*ap, stackblock(), 0);
221 
222 	/* Set any remaining args to "" */
223 	while (*++ap != NULL)
224 		setvar(*ap, nullstr, 0);
225 	return status;
226 }
227 
228 
229 
230 int
umaskcmd(int argc,char ** argv)231 umaskcmd(int argc, char **argv)
232 {
233 	char *ap;
234 	mode_t mask;
235 	int i;
236 	int symbolic_mode = 0;
237 
238 	while ((i = nextopt("S")) != '\0') {
239 		symbolic_mode = 1;
240 	}
241 
242 	INTOFF;
243 	mask = umask(0);
244 	umask(mask);
245 	INTON;
246 
247 	if ((ap = *argptr) == NULL) {
248 		if (symbolic_mode) {
249 			char u[4], g[4], o[4];
250 
251 			i = 0;
252 			if ((mask & S_IRUSR) == 0)
253 				u[i++] = 'r';
254 			if ((mask & S_IWUSR) == 0)
255 				u[i++] = 'w';
256 			if ((mask & S_IXUSR) == 0)
257 				u[i++] = 'x';
258 			u[i] = '\0';
259 
260 			i = 0;
261 			if ((mask & S_IRGRP) == 0)
262 				g[i++] = 'r';
263 			if ((mask & S_IWGRP) == 0)
264 				g[i++] = 'w';
265 			if ((mask & S_IXGRP) == 0)
266 				g[i++] = 'x';
267 			g[i] = '\0';
268 
269 			i = 0;
270 			if ((mask & S_IROTH) == 0)
271 				o[i++] = 'r';
272 			if ((mask & S_IWOTH) == 0)
273 				o[i++] = 'w';
274 			if ((mask & S_IXOTH) == 0)
275 				o[i++] = 'x';
276 			o[i] = '\0';
277 
278 			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
279 		} else {
280 			out1fmt("%.4o\n", mask);
281 		}
282 	} else {
283 		if (isdigit((unsigned char)*ap)) {
284 			int range = 0;
285 
286 			mask = 0;
287 			do {
288 				if (*ap >= '8' || *ap < '0')
289 					error("Not a valid octal number: '%s'",
290 					    *argptr);
291 				mask = (mask << 3) + (*ap - '0');
292 				if (mask & ~07777)
293 					range = 1;
294 			} while (*++ap != '\0');
295 			if (range)
296 			    error("Mask constant '%s' out of range", *argptr);
297 			umask(mask);
298 		} else {
299 			void *set;
300 
301 			INTOFF;
302 			if ((set = setmode(ap)) != 0) {
303 				mask = getmode(set, ~mask & 0777);
304 				ckfree(set);
305 			}
306 			INTON;
307 			if (!set)
308 				error("Cannot set mode `%s' (%s)", ap,
309 				    strerror(errno));
310 
311 			umask(~mask & 0777);
312 		}
313 	}
314 	flushout(out1);
315 	if (io_err(out1)) {
316 		out2str("umask: I/O error\n");
317 		return 1;
318 	}
319 	return 0;
320 }
321 
322 /*
323  * ulimit builtin
324  *
325  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
326  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
327  * ash by J.T. Conklin.
328  *
329  * Public domain.
330  */
331 
332 struct limits {
333 	const char *name;
334 	const char *unit;
335 	char	option;
336 	int8_t	cmd;		/* all RLIMIT_xxx are <= 127 */
337 	unsigned short factor;	/* multiply by to get rlim_{cur,max} values */
338 };
339 
340 #define	OPTSTRING_BASE "HSa"
341 
342 static const struct limits limits[] = {
343 #ifdef RLIMIT_CPU
344 	{ "time",	"seconds",	't',	RLIMIT_CPU,	   1 },
345 #define	OPTSTRING_t	OPTSTRING_BASE "t"
346 #else
347 #define	OPTSTRING_t	OPTSTRING_BASE
348 #endif
349 #ifdef RLIMIT_FSIZE
350 	{ "file",	"blocks",	'f',	RLIMIT_FSIZE,	 512 },
351 #define	OPTSTRING_f	OPTSTRING_t "f"
352 #else
353 #define	OPTSTRING_f	OPTSTRING_t
354 #endif
355 #ifdef RLIMIT_DATA
356 	{ "data",	"kbytes",	'd',	RLIMIT_DATA,	1024 },
357 #define	OPTSTRING_d	OPTSTRING_f "d"
358 #else
359 #define	OPTSTRING_d	OPTSTRING_f
360 #endif
361 #ifdef RLIMIT_STACK
362 	{ "stack",	"kbytes",	's',	RLIMIT_STACK,	1024 },
363 #define	OPTSTRING_s	OPTSTRING_d "s"
364 #else
365 #define	OPTSTRING_s	OPTSTRING_d
366 #endif
367 #ifdef RLIMIT_CORE
368 	{ "coredump",	"blocks",	'c',	RLIMIT_CORE,	 512 },
369 #define	OPTSTRING_c	OPTSTRING_s "c"
370 #else
371 #define	OPTSTRING_c	OPTSTRING_s
372 #endif
373 #ifdef RLIMIT_RSS
374 	{ "memory",	"kbytes",	'm',	RLIMIT_RSS,	1024 },
375 #define	OPTSTRING_m	OPTSTRING_c "m"
376 #else
377 #define	OPTSTRING_m	OPTSTRING_c
378 #endif
379 #ifdef RLIMIT_MEMLOCK
380 	{ "locked memory","kbytes",	'l',	RLIMIT_MEMLOCK, 1024 },
381 #define	OPTSTRING_l	OPTSTRING_m "l"
382 #else
383 #define	OPTSTRING_l	OPTSTRING_m
384 #endif
385 #ifdef RLIMIT_NTHR
386 	{ "thread",	"threads",	'r',	RLIMIT_NTHR,       1 },
387 #define	OPTSTRING_r	OPTSTRING_l "r"
388 #else
389 #define	OPTSTRING_r	OPTSTRING_l
390 #endif
391 #ifdef RLIMIT_NPROC
392 	{ "process",	"processes",	'p',	RLIMIT_NPROC,      1 },
393 #define	OPTSTRING_p	OPTSTRING_r "p"
394 #else
395 #define	OPTSTRING_p	OPTSTRING_r
396 #endif
397 #ifdef RLIMIT_NOFILE
398 	{ "nofiles",	"descriptors",	'n',	RLIMIT_NOFILE,     1 },
399 #define	OPTSTRING_n	OPTSTRING_p "n"
400 #else
401 #define	OPTSTRING_n	OPTSTRING_p
402 #endif
403 #ifdef RLIMIT_VMEM
404 	{ "vmemory",	"kbytes",	'v',	RLIMIT_VMEM,	1024 },
405 #define	OPTSTRING_v	OPTSTRING_n "v"
406 #else
407 #define	OPTSTRING_v	OPTSTRING_n
408 #endif
409 #ifdef RLIMIT_SWAP
410 	{ "swap",	"kbytes",	'w',	RLIMIT_SWAP,	1024 },
411 #define	OPTSTRING_w	OPTSTRING_v "w"
412 #else
413 #define	OPTSTRING_w	OPTSTRING_v
414 #endif
415 #ifdef RLIMIT_SBSIZE
416 	{ "sbsize",	"bytes",	'b',	RLIMIT_SBSIZE,	   1 },
417 #define	OPTSTRING_b	OPTSTRING_w "b"
418 #else
419 #define	OPTSTRING_b	OPTSTRING_w
420 #endif
421 	{ NULL,		NULL,		'\0',	0,		   0 }
422 };
423 #define	OPTSTRING	OPTSTRING_b
424 
425 int
ulimitcmd(int argc,char ** argv)426 ulimitcmd(int argc, char **argv)
427 {
428 	int	c;
429 	rlim_t val = 0;
430 	enum { SOFT = 0x1, HARD = 0x2 }
431 			how = 0, which;
432 	const struct limits	*l;
433 	int		set, all = 0;
434 	int		optc, what;
435 	struct rlimit	limit;
436 
437 	what = 'f';
438 	while ((optc = nextopt(OPTSTRING)) != '\0')
439 		switch (optc) {
440 		case 'H':
441 			how |= HARD;
442 			break;
443 		case 'S':
444 			how |= SOFT;
445 			break;
446 		case 'a':
447 			all = 1;
448 			break;
449 		default:
450 			what = optc;
451 		}
452 
453 	for (l = limits; l->name && l->option != what; l++)
454 		;
455 	if (!l->name)
456 		error("internal error (%c)", what);
457 
458 	set = *argptr ? 1 : 0;
459 	if (set) {
460 		char *p = *argptr;
461 
462 		if (all || argptr[1])
463 			error("too many arguments");
464 		if (how == 0)
465 			how = HARD | SOFT;
466 
467 		if (strcmp(p, "unlimited") == 0)
468 			val = RLIM_INFINITY;
469 		else {
470 			val = (rlim_t) 0;
471 
472 			while ((c = *p++) >= '0' && c <= '9') {
473 				if (val >= RLIM_INFINITY/10)
474 					error("%s: value overflow", *argptr);
475 				val = (val * 10);
476 				if (val >= RLIM_INFINITY - (long)(c - '0'))
477 					error("%s: value overflow", *argptr);
478 				val += (long)(c - '0');
479 			}
480 			if (c)
481 				error("%s: bad number", *argptr);
482 			if (val > RLIM_INFINITY / l->factor)
483 				error("%s: value overflow", *argptr);
484 			val *= l->factor;
485 		}
486 	} else if (how == 0)
487 		how = SOFT;
488 
489 	if (all) {
490 		for (l = limits; l->name; l++) {
491 			getrlimit(l->cmd, &limit);
492 			out1fmt("%-13s (-%c %-11s)    ", l->name, l->option,
493 			    l->unit);
494 
495 			which = how;
496 			while (which != 0) {
497 				if (which & SOFT) {
498 					val = limit.rlim_cur;
499 					which &= ~SOFT;
500 				} else if (which & HARD) {
501 					val = limit.rlim_max;
502 					which &= ~HARD;
503 				}
504 
505 				if (val == RLIM_INFINITY)
506 					out1fmt("unlimited");
507 				else {
508 					val /= l->factor;
509 #ifdef BSD4_4
510 					out1fmt("%9lld", (long long) val);
511 #else
512 					out1fmt("%9ld", (long) val);
513 #endif
514 				}
515 				out1fmt("%c", which ? '\t' : '\n');
516 			}
517 		}
518 		goto done;
519 	}
520 
521 	if (getrlimit(l->cmd, &limit) == -1)
522 		error("error getting limit (%s)", strerror(errno));
523 	if (set) {
524 		if (how & HARD)
525 			limit.rlim_max = val;
526 		if (how & SOFT)
527 			limit.rlim_cur = val;
528 		if (setrlimit(l->cmd, &limit) < 0)
529 			error("error setting limit (%s)", strerror(errno));
530 		if (l->cmd == RLIMIT_NOFILE)
531 			user_fd_limit = sysconf(_SC_OPEN_MAX);
532 	} else {
533 		if (how & SOFT)
534 			val = limit.rlim_cur;
535 		else if (how & HARD)
536 			val = limit.rlim_max;
537 
538 		if (val == RLIM_INFINITY)
539 			out1fmt("unlimited\n");
540 		else
541 		{
542 			val /= l->factor;
543 #ifdef BSD4_4
544 			out1fmt("%lld\n", (long long) val);
545 #else
546 			out1fmt("%ld\n", (long) val);
547 #endif
548 		}
549 	}
550   done:;
551 	flushout(out1);
552 	if (io_err(out1)) {
553 		out2str("ulimit: I/O error (stdout)\n");
554 		return 1;
555 	}
556 	return 0;
557 }
558