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