xref: /freebsd/bin/sh/miscbltin.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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 #ifndef lint
36 #if 0
37 static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
38 #endif
39 #endif /* not lint */
40 #include <sys/cdefs.h>
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 #define	READ_BUFLEN	1024
68 struct fdctx {
69 	int	fd;
70 	size_t	off;	/* offset in buf */
71 	size_t	buflen;
72 	char	*ep;	/* tail pointer */
73 	char	buf[READ_BUFLEN];
74 };
75 
76 static void fdctx_init(int, struct fdctx *);
77 static void fdctx_destroy(struct fdctx *);
78 static ssize_t fdgetc(struct fdctx *, char *);
79 int readcmd(int, char **);
80 int umaskcmd(int, char **);
81 int ulimitcmd(int, char **);
82 
83 static void
84 fdctx_init(int fd, struct fdctx *fdc)
85 {
86 	off_t cur;
87 
88 	/* Check if fd is seekable. */
89 	cur = lseek(fd, 0, SEEK_CUR);
90 	*fdc = (struct fdctx){
91 		.fd = fd,
92 		.buflen = (cur != -1) ? READ_BUFLEN : 1,
93 		.ep = &fdc->buf[0],	/* No data */
94 	};
95 }
96 
97 static ssize_t
98 fdgetc(struct fdctx *fdc, char *c)
99 {
100 	ssize_t nread;
101 
102 	if (&fdc->buf[fdc->off] == fdc->ep) {
103 		nread = read(fdc->fd, fdc->buf, fdc->buflen);
104 		if (nread > 0) {
105 			fdc->off = 0;
106 			fdc->ep = fdc->buf + nread;
107 		} else
108 			return (nread);
109 	}
110 	*c = fdc->buf[fdc->off++];
111 
112 	return (1);
113 }
114 
115 static void
116 fdctx_destroy(struct fdctx *fdc)
117 {
118 	off_t residue;
119 
120 	if (fdc->buflen > 1) {
121 	/*
122 	 * Reposition the file offset.  Here is the layout of buf:
123 	 *
124 	 *     | off
125 	 *     v
126 	 * |*****************|-------|
127 	 * buf               ep   buf+buflen
128 	 *     |<- residue ->|
129 	 *
130 	 * off: current character
131 	 * ep:  offset just after read(2)
132 	 * residue: length for reposition
133 	 */
134 		residue = (fdc->ep - fdc->buf) - fdc->off;
135 		if (residue > 0)
136 			(void) lseek(fdc->fd, -residue, SEEK_CUR);
137 	}
138 }
139 
140 /*
141  * The read builtin.  The -r option causes backslashes to be treated like
142  * ordinary characters.
143  *
144  * Note that if IFS=' :' then read x y should work so that:
145  * 'a b'	x='a', y='b'
146  * ' a b '	x='a', y='b'
147  * ':b'		x='',  y='b'
148  * ':'		x='',  y=''
149  * '::'		x='',  y=''
150  * ': :'	x='',  y=''
151  * ':::'	x='',  y='::'
152  * ':b c:'	x='',  y='b c:'
153  */
154 
155 int
156 readcmd(int argc __unused, char **argv __unused)
157 {
158 	char **ap;
159 	int backslash;
160 	char c;
161 	int rflag;
162 	char *prompt;
163 	const char *ifs;
164 	char *p;
165 	int startword;
166 	int status;
167 	int i;
168 	int is_ifs;
169 	int saveall = 0;
170 	ptrdiff_t lastnonifs, lastnonifsws;
171 	struct timeval tv;
172 	char *tvptr;
173 	fd_set ifds;
174 	ssize_t nread;
175 	int sig;
176 	struct fdctx fdctx;
177 
178 	rflag = 0;
179 	prompt = NULL;
180 	tv.tv_sec = -1;
181 	tv.tv_usec = 0;
182 	while ((i = nextopt("erp:t:")) != '\0') {
183 		switch(i) {
184 		case 'p':
185 			prompt = shoptarg;
186 			break;
187 		case 'e':
188 			break;
189 		case 'r':
190 			rflag = 1;
191 			break;
192 		case 't':
193 			tv.tv_sec = strtol(shoptarg, &tvptr, 0);
194 			if (tvptr == shoptarg)
195 				error("timeout value");
196 			switch(*tvptr) {
197 			case 0:
198 			case 's':
199 				break;
200 			case 'h':
201 				tv.tv_sec *= 60;
202 				/* FALLTHROUGH */
203 			case 'm':
204 				tv.tv_sec *= 60;
205 				break;
206 			default:
207 				error("timeout unit");
208 			}
209 			break;
210 		}
211 	}
212 	if (prompt && isatty(0)) {
213 		out2str(prompt);
214 		flushall();
215 	}
216 	if (*(ap = argptr) == NULL)
217 		error("arg count");
218 	if ((ifs = bltinlookup("IFS", 1)) == NULL)
219 		ifs = " \t\n";
220 
221 	if (tv.tv_sec >= 0) {
222 		/*
223 		 * Wait for something to become available.
224 		 */
225 		FD_ZERO(&ifds);
226 		FD_SET(0, &ifds);
227 		status = select(1, &ifds, NULL, NULL, &tv);
228 		/*
229 		 * If there's nothing ready, return an error.
230 		 */
231 		if (status <= 0) {
232 			while (*ap != NULL)
233 				setvar(*ap++, "", 0);
234 			sig = pendingsig;
235 			return (128 + (sig != 0 ? sig : SIGALRM));
236 		}
237 	}
238 
239 	status = 0;
240 	startword = 2;
241 	backslash = 0;
242 	STARTSTACKSTR(p);
243 	lastnonifs = lastnonifsws = -1;
244 	fdctx_init(STDIN_FILENO, &fdctx);
245 	for (;;) {
246 		c = 0;
247 		nread = fdgetc(&fdctx, &c);
248 		if (nread == -1) {
249 			if (errno == EINTR) {
250 				sig = pendingsig;
251 				if (sig == 0)
252 					continue;
253 				status = 128 + sig;
254 				break;
255 			}
256 			warning("read error: %s", strerror(errno));
257 			status = 2;
258 			break;
259 		} else if (nread != 1) {
260 			status = 1;
261 			break;
262 		}
263 		if (c == '\0')
264 			continue;
265 		CHECKSTRSPACE(1, p);
266 		if (backslash) {
267 			backslash = 0;
268 			if (c != '\n') {
269 				startword = 0;
270 				lastnonifs = lastnonifsws = p - stackblock();
271 				USTPUTC(c, p);
272 			}
273 			continue;
274 		}
275 		if (!rflag && c == '\\') {
276 			backslash++;
277 			continue;
278 		}
279 		if (c == '\n')
280 			break;
281 		if (strchr(ifs, c))
282 			is_ifs = strchr(" \t\n", c) ? 1 : 2;
283 		else
284 			is_ifs = 0;
285 
286 		if (startword != 0) {
287 			if (is_ifs == 1) {
288 				/* Ignore leading IFS whitespace */
289 				if (saveall)
290 					USTPUTC(c, p);
291 				continue;
292 			}
293 			if (is_ifs == 2 && startword == 1) {
294 				/* Only one non-whitespace IFS per word */
295 				startword = 2;
296 				if (saveall) {
297 					lastnonifsws = p - stackblock();
298 					USTPUTC(c, p);
299 				}
300 				continue;
301 			}
302 		}
303 
304 		if (is_ifs == 0) {
305 			/* append this character to the current variable */
306 			startword = 0;
307 			if (saveall)
308 				/* Not just a spare terminator */
309 				saveall++;
310 			lastnonifs = lastnonifsws = p - stackblock();
311 			USTPUTC(c, p);
312 			continue;
313 		}
314 
315 		/* end of variable... */
316 		startword = is_ifs;
317 
318 		if (ap[1] == NULL) {
319 			/* Last variable needs all IFS chars */
320 			saveall++;
321 			if (is_ifs == 2)
322 				lastnonifsws = p - stackblock();
323 			USTPUTC(c, p);
324 			continue;
325 		}
326 
327 		STACKSTRNUL(p);
328 		setvar(*ap, stackblock(), 0);
329 		ap++;
330 		STARTSTACKSTR(p);
331 		lastnonifs = lastnonifsws = -1;
332 	}
333 	fdctx_destroy(&fdctx);
334 	STACKSTRNUL(p);
335 
336 	/*
337 	 * Remove trailing IFS chars: always remove whitespace, don't remove
338 	 * non-whitespace unless it was naked
339 	 */
340 	if (saveall <= 1)
341 		lastnonifsws = lastnonifs;
342 	stackblock()[lastnonifsws + 1] = '\0';
343 	setvar(*ap, stackblock(), 0);
344 
345 	/* Set any remaining args to "" */
346 	while (*++ap != NULL)
347 		setvar(*ap, "", 0);
348 	return status;
349 }
350 
351 
352 
353 int
354 umaskcmd(int argc __unused, char **argv __unused)
355 {
356 	char *ap;
357 	int mask;
358 	int i;
359 	int symbolic_mode = 0;
360 
361 	while ((i = nextopt("S")) != '\0') {
362 		symbolic_mode = 1;
363 	}
364 
365 	INTOFF;
366 	mask = umask(0);
367 	umask(mask);
368 	INTON;
369 
370 	if ((ap = *argptr) == NULL) {
371 		if (symbolic_mode) {
372 			char u[4], g[4], o[4];
373 
374 			i = 0;
375 			if ((mask & S_IRUSR) == 0)
376 				u[i++] = 'r';
377 			if ((mask & S_IWUSR) == 0)
378 				u[i++] = 'w';
379 			if ((mask & S_IXUSR) == 0)
380 				u[i++] = 'x';
381 			u[i] = '\0';
382 
383 			i = 0;
384 			if ((mask & S_IRGRP) == 0)
385 				g[i++] = 'r';
386 			if ((mask & S_IWGRP) == 0)
387 				g[i++] = 'w';
388 			if ((mask & S_IXGRP) == 0)
389 				g[i++] = 'x';
390 			g[i] = '\0';
391 
392 			i = 0;
393 			if ((mask & S_IROTH) == 0)
394 				o[i++] = 'r';
395 			if ((mask & S_IWOTH) == 0)
396 				o[i++] = 'w';
397 			if ((mask & S_IXOTH) == 0)
398 				o[i++] = 'x';
399 			o[i] = '\0';
400 
401 			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
402 		} else {
403 			out1fmt("%.4o\n", mask);
404 		}
405 	} else {
406 		if (is_digit(*ap)) {
407 			mask = 0;
408 			do {
409 				if (*ap >= '8' || *ap < '0')
410 					error("Illegal number: %s", *argptr);
411 				mask = (mask << 3) + (*ap - '0');
412 			} while (*++ap != '\0');
413 			umask(mask);
414 		} else {
415 			void *set;
416 			INTOFF;
417 			if ((set = setmode (ap)) == NULL)
418 				error("Illegal number: %s", ap);
419 
420 			mask = getmode (set, ~mask & 0777);
421 			umask(~mask & 0777);
422 			free(set);
423 			INTON;
424 		}
425 	}
426 	return 0;
427 }
428 
429 /*
430  * ulimit builtin
431  *
432  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
433  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
434  * ash by J.T. Conklin.
435  *
436  * Public domain.
437  */
438 
439 struct limits {
440 	const char *name;
441 	const char *units;
442 	int	cmd;
443 	short	factor;	/* multiply by to get rlim_{cur,max} values */
444 	char	option;
445 };
446 
447 static const struct limits limits[] = {
448 #ifdef RLIMIT_CPU
449 	{ "cpu time",		"seconds",	RLIMIT_CPU,	   1, 't' },
450 #endif
451 #ifdef RLIMIT_FSIZE
452 	{ "file size",		"512-blocks",	RLIMIT_FSIZE,	 512, 'f' },
453 #endif
454 #ifdef RLIMIT_DATA
455 	{ "data seg size",	"kbytes",	RLIMIT_DATA,	1024, 'd' },
456 #endif
457 #ifdef RLIMIT_STACK
458 	{ "stack size",		"kbytes",	RLIMIT_STACK,	1024, 's' },
459 #endif
460 #ifdef  RLIMIT_CORE
461 	{ "core file size",	"512-blocks",	RLIMIT_CORE,	 512, 'c' },
462 #endif
463 #ifdef RLIMIT_RSS
464 	{ "max memory size",	"kbytes",	RLIMIT_RSS,	1024, 'm' },
465 #endif
466 #ifdef RLIMIT_MEMLOCK
467 	{ "locked memory",	"kbytes",	RLIMIT_MEMLOCK, 1024, 'l' },
468 #endif
469 #ifdef RLIMIT_NPROC
470 	{ "max user processes",	(char *)0,	RLIMIT_NPROC,      1, 'u' },
471 #endif
472 #ifdef RLIMIT_NOFILE
473 	{ "open files",		(char *)0,	RLIMIT_NOFILE,     1, 'n' },
474 #endif
475 #ifdef RLIMIT_VMEM
476 	{ "virtual mem size",	"kbytes",	RLIMIT_VMEM,	1024, 'v' },
477 #endif
478 #ifdef RLIMIT_SWAP
479 	{ "swap limit",		"kbytes",	RLIMIT_SWAP,	1024, 'w' },
480 #endif
481 #ifdef RLIMIT_SBSIZE
482 	{ "socket buffer size",	"bytes",	RLIMIT_SBSIZE,	   1, 'b' },
483 #endif
484 #ifdef RLIMIT_NPTS
485 	{ "pseudo-terminals",	(char *)0,	RLIMIT_NPTS,	   1, 'p' },
486 #endif
487 #ifdef RLIMIT_KQUEUES
488 	{ "kqueues",		(char *)0,	RLIMIT_KQUEUES,	   1, 'k' },
489 #endif
490 #ifdef RLIMIT_UMTXP
491 	{ "umtx shared locks",	(char *)0,	RLIMIT_UMTXP,	   1, 'o' },
492 #endif
493 	{ (char *) 0,		(char *)0,	0,		   0, '\0' }
494 };
495 
496 enum limithow { SOFT = 0x1, HARD = 0x2 };
497 
498 static void
499 printlimit(enum limithow how, const struct rlimit *limit,
500     const struct limits *l)
501 {
502 	rlim_t val = 0;
503 
504 	if (how & SOFT)
505 		val = limit->rlim_cur;
506 	else if (how & HARD)
507 		val = limit->rlim_max;
508 	if (val == RLIM_INFINITY)
509 		out1str("unlimited\n");
510 	else
511 	{
512 		val /= l->factor;
513 		out1fmt("%jd\n", (intmax_t)val);
514 	}
515 }
516 
517 int
518 ulimitcmd(int argc __unused, char **argv __unused)
519 {
520 	rlim_t val = 0;
521 	enum limithow how = SOFT | HARD;
522 	const struct limits	*l;
523 	int		set, all = 0;
524 	int		optc, what;
525 	struct rlimit	limit;
526 
527 	what = 'f';
528 	while ((optc = nextopt("HSatfdsmcnuvlbpwko")) != '\0')
529 		switch (optc) {
530 		case 'H':
531 			how = HARD;
532 			break;
533 		case 'S':
534 			how = SOFT;
535 			break;
536 		case 'a':
537 			all = 1;
538 			break;
539 		default:
540 			what = optc;
541 		}
542 
543 	for (l = limits; l->name && l->option != what; l++)
544 		;
545 	if (!l->name)
546 		error("internal error (%c)", what);
547 
548 	set = *argptr ? 1 : 0;
549 	if (set) {
550 		char *p = *argptr;
551 
552 		if (all || argptr[1])
553 			error("too many arguments");
554 		if (strcmp(p, "unlimited") == 0)
555 			val = RLIM_INFINITY;
556 		else {
557 			char *end;
558 			uintmax_t uval;
559 
560 			if (*p < '0' || *p > '9')
561 				error("bad number");
562 			errno = 0;
563 			uval = strtoumax(p, &end, 10);
564 			if (errno != 0 || *end != '\0')
565 				error("bad number");
566 			if (uval > UINTMAX_MAX / l->factor)
567 				error("bad number");
568 			uval *= l->factor;
569 			val = (rlim_t)uval;
570 			if (val < 0 || (uintmax_t)val != uval ||
571 			    val == RLIM_INFINITY)
572 				error("bad number");
573 		}
574 	}
575 	if (all) {
576 		for (l = limits; l->name; l++) {
577 			char optbuf[40];
578 			if (getrlimit(l->cmd, &limit) < 0)
579 				error("can't get limit: %s", strerror(errno));
580 
581 			if (l->units)
582 				snprintf(optbuf, sizeof(optbuf),
583 					"(%s, -%c) ", l->units, l->option);
584 			else
585 				snprintf(optbuf, sizeof(optbuf),
586 					"(-%c) ", l->option);
587 			out1fmt("%-18s %18s ", l->name, optbuf);
588 			printlimit(how, &limit, l);
589 		}
590 		return 0;
591 	}
592 
593 	if (getrlimit(l->cmd, &limit) < 0)
594 		error("can't get limit: %s", strerror(errno));
595 	if (set) {
596 		if (how & SOFT)
597 			limit.rlim_cur = val;
598 		if (how & HARD)
599 			limit.rlim_max = val;
600 		if (setrlimit(l->cmd, &limit) < 0)
601 			error("bad limit: %s", strerror(errno));
602 	} else
603 		printlimit(how, &limit, l);
604 	return 0;
605 }
606