1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1997-2005
5 *	Herbert Xu <herbert@gondor.apana.org.au>.  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 * Miscelaneous builtins.
37 */
38
39#include <sys/types.h>		/* quad_t */
40#include <sys/param.h>		/* BSD4_4 */
41#include <sys/stat.h>
42#include <sys/time.h>
43#include <unistd.h>
44#include <stdlib.h>
45#include <ctype.h>
46#include <inttypes.h>
47
48#include "shell.h"
49#include "options.h"
50#include "var.h"
51#include "output.h"
52#include "memalloc.h"
53#include "error.h"
54#include "miscbltin.h"
55#include "mystring.h"
56#include "main.h"
57#include "expand.h"
58#include "parser.h"
59#include "trap.h"
60
61#undef rflag
62
63
64/** handle one line of the read command.
65 *  more fields than variables -> remainder shall be part of last variable.
66 *  less fields than variables -> remaining variables unset.
67 *
68 *  @param line complete line of input
69 *  @param ac argument count
70 *  @param ap argument (variable) list
71 *  @param len length of line including trailing '\0'
72 */
73static void
74readcmd_handle_line(char *s, int ac, char **ap)
75{
76	struct arglist arglist;
77	struct strlist *sl;
78
79	s = grabstackstr(s);
80
81	arglist.lastp = &arglist.list;
82
83	ifsbreakup(s, ac, &arglist);
84	*arglist.lastp = NULL;
85	ifsfree();
86
87	sl = arglist.list;
88
89	do {
90		if (!sl) {
91			/* nullify remaining arguments */
92			do {
93				setvar(*ap, nullstr, 0);
94			} while (*++ap);
95
96			return;
97		}
98
99		/* set variable to field */
100		rmescapes(sl->text);
101		setvar(*ap, sl->text, 0);
102		sl = sl->next;
103	} while (*++ap);
104}
105
106/*
107 * The read builtin.  The -e option causes backslashes to escape the
108 * following character. The -p option followed by an argument prompts
109 * with the argument.
110 *
111 * This uses unbuffered input, which may be avoidable in some cases.
112 */
113
114int
115readcmd(int argc, char **argv)
116{
117	char **ap;
118	char c;
119	int rflag;
120	char *prompt;
121	char *p;
122	int startloc;
123	int newloc;
124	int status;
125	int i;
126
127	rflag = 0;
128	prompt = NULL;
129	while ((i = nextopt("p:r")) != '\0') {
130		if (i == 'p')
131			prompt = optionarg;
132		else
133			rflag = 1;
134	}
135	if (prompt && isatty(0)) {
136		out2str(prompt);
137#ifdef FLUSHERR
138		flushall();
139#endif
140	}
141	if (*(ap = argptr) == NULL)
142		sh_error("arg count");
143
144	status = 0;
145	STARTSTACKSTR(p);
146
147	goto start;
148
149	for (;;) {
150		switch (read(0, &c, 1)) {
151		case 1:
152			break;
153		default:
154			if (errno == EINTR && !pendingsigs)
155				continue;
156				/* fall through */
157		case 0:
158			status = 1;
159			goto out;
160		}
161		if (c == '\0')
162			continue;
163		if (newloc >= startloc) {
164			if (c == '\n')
165				goto resetbs;
166			goto put;
167		}
168		if (!rflag && c == '\\') {
169			newloc = p - (char *)stackblock();
170			continue;
171		}
172		if (c == '\n')
173			break;
174put:
175		CHECKSTRSPACE(2, p);
176		if (strchr(qchars, c))
177			USTPUTC(CTLESC, p);
178		USTPUTC(c, p);
179
180		if (newloc >= startloc) {
181resetbs:
182			recordregion(startloc, newloc, 0);
183start:
184			startloc = p - (char *)stackblock();
185			newloc = startloc - 1;
186		}
187	}
188out:
189	recordregion(startloc, p - (char *)stackblock(), 0);
190	STACKSTRNUL(p);
191	readcmd_handle_line(p + 1, argc - (ap - argv), ap);
192	return status;
193}
194
195
196
197/*
198 * umask builtin
199 *
200 * This code was ripped from pdksh 5.2.14 and hacked for use with
201 * dash by Herbert Xu.
202 *
203 * Public domain.
204 */
205
206int
207umaskcmd(int argc, char **argv)
208{
209	char *ap;
210	int mask;
211	int i;
212	int symbolic_mode = 0;
213
214	while ((i = nextopt("S")) != '\0') {
215		symbolic_mode = 1;
216	}
217
218	INTOFF;
219	mask = umask(0);
220	umask(mask);
221	INTON;
222
223	if ((ap = *argptr) == NULL) {
224		if (symbolic_mode) {
225			char buf[18];
226			int j;
227
228			mask = ~mask;
229			ap = buf;
230			for (i = 0; i < 3; i++) {
231				*ap++ = "ugo"[i];
232				*ap++ = '=';
233				for (j = 0; j < 3; j++)
234					if (mask & (1 << (8 - (3*i + j))))
235						*ap++ = "rwx"[j];
236				*ap++ = ',';
237			}
238			ap[-1] = '\0';
239			out1fmt("%s\n", buf);
240		} else {
241			out1fmt("%.4o\n", mask);
242		}
243	} else {
244		int new_mask;
245
246		if (isdigit((unsigned char) *ap)) {
247			new_mask = 0;
248			do {
249				if (*ap >= '8' || *ap < '0')
250					sh_error(illnum, *argptr);
251				new_mask = (new_mask << 3) + (*ap - '0');
252			} while (*++ap != '\0');
253		} else {
254			int positions, new_val;
255			char op;
256
257			mask = ~mask;
258			new_mask = mask;
259			positions = 0;
260			while (*ap) {
261				while (*ap && strchr("augo", *ap))
262					switch (*ap++) {
263					case 'a': positions |= 0111; break;
264					case 'u': positions |= 0100; break;
265					case 'g': positions |= 0010; break;
266					case 'o': positions |= 0001; break;
267					}
268				if (!positions)
269					positions = 0111; /* default is a */
270				if (!strchr("=+-", op = *ap))
271					break;
272				ap++;
273				new_val = 0;
274				while (*ap && strchr("rwxugoXs", *ap))
275					switch (*ap++) {
276					case 'r': new_val |= 04; break;
277					case 'w': new_val |= 02; break;
278					case 'x': new_val |= 01; break;
279					case 'u': new_val |= mask >> 6;
280						  break;
281					case 'g': new_val |= mask >> 3;
282						  break;
283					case 'o': new_val |= mask >> 0;
284						  break;
285					case 'X': if (mask & 0111)
286							new_val |= 01;
287						  break;
288					case 's': /* ignored */
289						  break;
290					}
291				new_val = (new_val & 07) * positions;
292				switch (op) {
293				case '-':
294					new_mask &= ~new_val;
295					break;
296				case '=':
297					new_mask = new_val
298					    | (new_mask & ~(positions * 07));
299					break;
300				case '+':
301					new_mask |= new_val;
302				}
303				if (*ap == ',') {
304					positions = 0;
305					ap++;
306				} else if (!strchr("=+-", *ap))
307					break;
308			}
309			if (*ap) {
310				sh_error("Illegal mode: %s", *argptr);
311				return 1;
312			}
313			new_mask = ~new_mask;
314		}
315		umask(new_mask);
316	}
317	return 0;
318}
319
320#ifdef HAVE_GETRLIMIT
321/*
322 * ulimit builtin
323 *
324 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
325 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
326 * ash by J.T. Conklin.
327 *
328 * Public domain.
329 */
330
331struct limits {
332	const char *name;
333	int	cmd;
334	int	factor;	/* multiply by to get rlim_{cur,max} values */
335	char	option;
336};
337
338static const struct limits limits[] = {
339#ifdef RLIMIT_CPU
340	{ "time(seconds)",		RLIMIT_CPU,	   1, 't' },
341#endif
342#ifdef RLIMIT_FSIZE
343	{ "file(blocks)",		RLIMIT_FSIZE,	 512, 'f' },
344#endif
345#ifdef RLIMIT_DATA
346	{ "data(kbytes)",		RLIMIT_DATA,	1024, 'd' },
347#endif
348#ifdef RLIMIT_STACK
349	{ "stack(kbytes)",		RLIMIT_STACK,	1024, 's' },
350#endif
351#ifdef RLIMIT_CORE
352	{ "coredump(blocks)",		RLIMIT_CORE,	 512, 'c' },
353#endif
354#ifdef RLIMIT_RSS
355	{ "memory(kbytes)",		RLIMIT_RSS,	1024, 'm' },
356#endif
357#ifdef RLIMIT_MEMLOCK
358	{ "locked memory(kbytes)",	RLIMIT_MEMLOCK, 1024, 'l' },
359#endif
360#ifdef RLIMIT_NPROC
361	{ "process",			RLIMIT_NPROC,      1, 'p' },
362#endif
363#ifdef RLIMIT_NOFILE
364	{ "nofiles",			RLIMIT_NOFILE,     1, 'n' },
365#endif
366#ifdef RLIMIT_AS
367	{ "vmemory(kbytes)",		RLIMIT_AS,	1024, 'v' },
368#endif
369#ifdef RLIMIT_LOCKS
370	{ "locks",			RLIMIT_LOCKS,	   1, 'w' },
371#endif
372#ifdef RLIMIT_RTPRIO
373	{ "rtprio",			RLIMIT_RTPRIO,	   1, 'r' },
374#endif
375	{ (char *) 0,			0,		   0,  '\0' }
376};
377
378enum limtype { SOFT = 0x1, HARD = 0x2 };
379
380static void printlim(enum limtype how, const struct rlimit *limit,
381		     const struct limits *l)
382{
383	rlim_t val;
384
385	val = limit->rlim_max;
386	if (how & SOFT)
387		val = limit->rlim_cur;
388
389	if (val == RLIM_INFINITY)
390		out1fmt("unlimited\n");
391	else {
392		val /= l->factor;
393		out1fmt("%" PRIdMAX "\n", (intmax_t) val);
394	}
395}
396
397int
398ulimitcmd(int argc, char **argv)
399{
400	int	c;
401	rlim_t val = 0;
402	enum limtype how = SOFT | HARD;
403	const struct limits	*l;
404	int		set, all = 0;
405	int		optc, what;
406	struct rlimit	limit;
407
408	what = 'f';
409	while ((optc = nextopt("HSa"
410#ifdef RLIMIT_CPU
411			       "t"
412#endif
413#ifdef RLIMIT_FSIZE
414			       "f"
415#endif
416#ifdef RLIMIT_DATA
417			       "d"
418#endif
419#ifdef RLIMIT_STACK
420			       "s"
421#endif
422#ifdef RLIMIT_CORE
423			       "c"
424#endif
425#ifdef RLIMIT_RSS
426			       "m"
427#endif
428#ifdef RLIMIT_MEMLOCK
429			       "l"
430#endif
431#ifdef RLIMIT_NPROC
432			       "p"
433#endif
434#ifdef RLIMIT_NOFILE
435			       "n"
436#endif
437#ifdef RLIMIT_AS
438			       "v"
439#endif
440#ifdef RLIMIT_LOCKS
441			       "w"
442#endif
443	)) != '\0')
444		switch (optc) {
445		case 'H':
446			how = HARD;
447			break;
448		case 'S':
449			how = SOFT;
450			break;
451		case 'a':
452			all = 1;
453			break;
454		default:
455			what = optc;
456		}
457
458	for (l = limits; l->option != what; l++)
459		;
460
461	set = *argptr ? 1 : 0;
462	if (set) {
463		char *p = *argptr;
464
465		if (all || argptr[1])
466			sh_error("too many arguments");
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			{
474				val = (val * 10) + (long)(c - '0');
475				if (val < (rlim_t) 0)
476					break;
477			}
478			if (c)
479				sh_error("bad number");
480			val *= l->factor;
481		}
482	}
483	if (all) {
484		for (l = limits; l->name; l++) {
485			getrlimit(l->cmd, &limit);
486			out1fmt("%-20s ", l->name);
487			printlim(how, &limit, l);
488		}
489		return 0;
490	}
491
492	getrlimit(l->cmd, &limit);
493	if (set) {
494		if (how & HARD)
495			limit.rlim_max = val;
496		if (how & SOFT)
497			limit.rlim_cur = val;
498		if (setrlimit(l->cmd, &limit) < 0)
499			sh_error("error setting limit (%s)", strerror(errno));
500	} else {
501		printlim(how, &limit, l);
502	}
503	return 0;
504}
505#endif
506