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
62struct 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
70static void fdctx_init(int, struct fdctx *);
71static void fdctx_destroy(struct fdctx *);
72static ssize_t fdgetc(struct fdctx *, char *);
73int readcmd(int, char **);
74int umaskcmd(int, char **);
75int ulimitcmd(int, char **);
76
77static void
78fdctx_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
91static ssize_t
92fdgetc(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
109static void
110fdctx_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
149int
150readcmd(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
347int
348umaskcmd(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
433struct 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
441static 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
490enum limithow { SOFT = 0x1, HARD = 0x2 };
491
492static void
493printlimit(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
511int
512ulimitcmd(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