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