miscbltin.c revision 194767
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
35static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: head/bin/sh/miscbltin.c 194767 2009-06-23 20:57:27Z kib $");
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 <ctype.h>
51#include <errno.h>
52#include <stdint.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <termios.h>
56
57#include "shell.h"
58#include "options.h"
59#include "var.h"
60#include "output.h"
61#include "memalloc.h"
62#include "error.h"
63#include "mystring.h"
64
65#undef eflag
66
67int readcmd(int, char **);
68int umaskcmd(int, char **);
69int ulimitcmd(int, char **);
70
71/*
72 * The read builtin.  The -r option causes backslashes to be treated like
73 * ordinary characters.
74 *
75 * This uses unbuffered input, which may be avoidable in some cases.
76 *
77 * Note that if IFS=' :' then read x y should work so that:
78 * 'a b'	x='a', y='b'
79 * ' a b '	x='a', y='b'
80 * ':b'		x='',  y='b'
81 * ':'		x='',  y=''
82 * '::'		x='',  y=''
83 * ': :'	x='',  y=''
84 * ':::'	x='',  y='::'
85 * ':b c:'	x='',  y='b c:'
86 */
87
88int
89readcmd(int argc __unused, char **argv __unused)
90{
91	char **ap;
92	int backslash;
93	char c;
94	int rflag;
95	char *prompt;
96	char *ifs;
97	char *p;
98	int startword;
99	int status;
100	int i;
101	int is_ifs;
102	int saveall = 0;
103	struct timeval tv;
104	char *tvptr;
105	fd_set ifds;
106
107	rflag = 0;
108	prompt = NULL;
109	tv.tv_sec = -1;
110	tv.tv_usec = 0;
111	while ((i = nextopt("erp:t:")) != '\0') {
112		switch(i) {
113		case 'p':
114			prompt = shoptarg;
115			break;
116		case 'e':
117			break;
118		case 'r':
119			rflag = 1;
120			break;
121		case 't':
122			tv.tv_sec = strtol(shoptarg, &tvptr, 0);
123			if (tvptr == shoptarg)
124				error("timeout value");
125			switch(*tvptr) {
126			case 0:
127			case 's':
128				break;
129			case 'h':
130				tv.tv_sec *= 60;
131				/* FALLTHROUGH */
132			case 'm':
133				tv.tv_sec *= 60;
134				break;
135			default:
136				error("timeout unit");
137			}
138			break;
139		}
140	}
141	if (prompt && isatty(0)) {
142		out2str(prompt);
143		flushall();
144	}
145	if (*(ap = argptr) == NULL)
146		error("arg count");
147	if ((ifs = bltinlookup("IFS", 1)) == NULL)
148		ifs = " \t\n";
149
150	if (tv.tv_sec >= 0) {
151		/*
152		 * Wait for something to become available.
153		 */
154		FD_ZERO(&ifds);
155		FD_SET(0, &ifds);
156		status = select(1, &ifds, NULL, NULL, &tv);
157		/*
158		 * If there's nothing ready, return an error.
159		 */
160		if (status <= 0)
161			return(1);
162	}
163
164	status = 0;
165	startword = 2;
166	backslash = 0;
167	STARTSTACKSTR(p);
168	for (;;) {
169		if (read(STDIN_FILENO, &c, 1) != 1) {
170			status = 1;
171			break;
172		}
173		if (c == '\0')
174			continue;
175		if (backslash) {
176			backslash = 0;
177			if (c != '\n')
178				STPUTC(c, p);
179			continue;
180		}
181		if (!rflag && c == '\\') {
182			backslash++;
183			continue;
184		}
185		if (c == '\n')
186			break;
187		if (strchr(ifs, c))
188			is_ifs = strchr(" \t\n", c) ? 1 : 2;
189		else
190			is_ifs = 0;
191
192		if (startword != 0) {
193			if (is_ifs == 1) {
194				/* Ignore leading IFS whitespace */
195				if (saveall)
196					STPUTC(c, p);
197				continue;
198			}
199			if (is_ifs == 2 && startword == 1) {
200				/* Only one non-whitespace IFS per word */
201				startword = 2;
202				if (saveall)
203					STPUTC(c, p);
204				continue;
205			}
206		}
207
208		if (is_ifs == 0) {
209			/* append this character to the current variable */
210			startword = 0;
211			if (saveall)
212				/* Not just a spare terminator */
213				saveall++;
214			STPUTC(c, p);
215			continue;
216		}
217
218		/* end of variable... */
219		startword = is_ifs;
220
221		if (ap[1] == NULL) {
222			/* Last variable needs all IFS chars */
223			saveall++;
224			STPUTC(c, p);
225			continue;
226		}
227
228		STACKSTRNUL(p);
229		setvar(*ap, stackblock(), 0);
230		ap++;
231		STARTSTACKSTR(p);
232	}
233	STACKSTRNUL(p);
234
235	/* Remove trailing IFS chars */
236	for (; stackblock() <= --p; *p = 0) {
237		if (!strchr(ifs, *p))
238			break;
239		if (strchr(" \t\n", *p))
240			/* Always remove whitespace */
241			continue;
242		if (saveall > 1)
243			/* Don't remove non-whitespace unless it was naked */
244			break;
245	}
246	setvar(*ap, stackblock(), 0);
247
248	/* Set any remaining args to "" */
249	while (*++ap != NULL)
250		setvar(*ap, nullstr, 0);
251	return status;
252}
253
254
255
256int
257umaskcmd(int argc __unused, char **argv)
258{
259	char *ap;
260	int mask;
261	int i;
262	int symbolic_mode = 0;
263
264	while ((i = nextopt("S")) != '\0') {
265		symbolic_mode = 1;
266	}
267
268	INTOFF;
269	mask = umask(0);
270	umask(mask);
271	INTON;
272
273	if ((ap = *argptr) == NULL) {
274		if (symbolic_mode) {
275			char u[4], g[4], o[4];
276
277			i = 0;
278			if ((mask & S_IRUSR) == 0)
279				u[i++] = 'r';
280			if ((mask & S_IWUSR) == 0)
281				u[i++] = 'w';
282			if ((mask & S_IXUSR) == 0)
283				u[i++] = 'x';
284			u[i] = '\0';
285
286			i = 0;
287			if ((mask & S_IRGRP) == 0)
288				g[i++] = 'r';
289			if ((mask & S_IWGRP) == 0)
290				g[i++] = 'w';
291			if ((mask & S_IXGRP) == 0)
292				g[i++] = 'x';
293			g[i] = '\0';
294
295			i = 0;
296			if ((mask & S_IROTH) == 0)
297				o[i++] = 'r';
298			if ((mask & S_IWOTH) == 0)
299				o[i++] = 'w';
300			if ((mask & S_IXOTH) == 0)
301				o[i++] = 'x';
302			o[i] = '\0';
303
304			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
305		} else {
306			out1fmt("%.4o\n", mask);
307		}
308	} else {
309		if (isdigit(*ap)) {
310			mask = 0;
311			do {
312				if (*ap >= '8' || *ap < '0')
313					error("Illegal number: %s", *argptr);
314				mask = (mask << 3) + (*ap - '0');
315			} while (*++ap != '\0');
316			umask(mask);
317		} else {
318			void *set;
319			INTOFF;
320			if ((set = setmode (ap)) == 0)
321				error("Illegal number: %s", ap);
322
323			mask = getmode (set, ~mask & 0777);
324			umask(~mask & 0777);
325			free(set);
326			INTON;
327		}
328	}
329	return 0;
330}
331
332/*
333 * ulimit builtin
334 *
335 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
336 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
337 * ash by J.T. Conklin.
338 *
339 * Public domain.
340 */
341
342struct limits {
343	const char *name;
344	const char *units;
345	int	cmd;
346	int	factor;	/* multiply by to get rlim_{cur,max} values */
347	char	option;
348};
349
350static const struct limits limits[] = {
351#ifdef RLIMIT_CPU
352	{ "cpu time",		"seconds",	RLIMIT_CPU,	   1, 't' },
353#endif
354#ifdef RLIMIT_FSIZE
355	{ "file size",		"512-blocks",	RLIMIT_FSIZE,	 512, 'f' },
356#endif
357#ifdef RLIMIT_DATA
358	{ "data seg size",	"kbytes",	RLIMIT_DATA,	1024, 'd' },
359#endif
360#ifdef RLIMIT_STACK
361	{ "stack size",		"kbytes",	RLIMIT_STACK,	1024, 's' },
362#endif
363#ifdef  RLIMIT_CORE
364	{ "core file size",	"512-blocks",	RLIMIT_CORE,	 512, 'c' },
365#endif
366#ifdef RLIMIT_RSS
367	{ "max memory size",	"kbytes",	RLIMIT_RSS,	1024, 'm' },
368#endif
369#ifdef RLIMIT_MEMLOCK
370	{ "locked memory",	"kbytes",	RLIMIT_MEMLOCK, 1024, 'l' },
371#endif
372#ifdef RLIMIT_NPROC
373	{ "max user processes",	(char *)0,	RLIMIT_NPROC,      1, 'u' },
374#endif
375#ifdef RLIMIT_NOFILE
376	{ "open files",		(char *)0,	RLIMIT_NOFILE,     1, 'n' },
377#endif
378#ifdef RLIMIT_VMEM
379	{ "virtual mem size",	"kbytes",	RLIMIT_VMEM,	1024, 'v' },
380#endif
381#ifdef RLIMIT_SWAP
382	{ "swap limit",		"kbytes",	RLIMIT_SWAP,	1024, 'w' },
383#endif
384#ifdef RLIMIT_SBSIZE
385	{ "sbsize",		"bytes",	RLIMIT_SBSIZE,	   1, 'b' },
386#endif
387#ifdef RLIMIT_NPTS
388	{ "pseudo-terminals",	(char *)0,	RLIMIT_NPTS,	   1, 'p' },
389#endif
390	{ (char *) 0,		(char *)0,	0,		   0, '\0' }
391};
392
393int
394ulimitcmd(int argc __unused, char **argv __unused)
395{
396	int	c;
397	rlim_t val = 0;
398	enum { SOFT = 0x1, HARD = 0x2 }
399			how = SOFT | HARD;
400	const struct limits	*l;
401	int		set, all = 0;
402	int		optc, what;
403	struct rlimit	limit;
404
405	what = 'f';
406	while ((optc = nextopt("HSatfdsmcnuvlbpw")) != '\0')
407		switch (optc) {
408		case 'H':
409			how = HARD;
410			break;
411		case 'S':
412			how = SOFT;
413			break;
414		case 'a':
415			all = 1;
416			break;
417		default:
418			what = optc;
419		}
420
421	for (l = limits; l->name && l->option != what; l++)
422		;
423	if (!l->name)
424		error("internal error (%c)", what);
425
426	set = *argptr ? 1 : 0;
427	if (set) {
428		char *p = *argptr;
429
430		if (all || argptr[1])
431			error("too many arguments");
432		if (strcmp(p, "unlimited") == 0)
433			val = RLIM_INFINITY;
434		else {
435			val = 0;
436
437			while ((c = *p++) >= '0' && c <= '9')
438			{
439				val = (val * 10) + (long)(c - '0');
440				if (val < 0)
441					break;
442			}
443			if (c)
444				error("bad number");
445			val *= l->factor;
446		}
447	}
448	if (all) {
449		for (l = limits; l->name; l++) {
450			char optbuf[40];
451			if (getrlimit(l->cmd, &limit) < 0)
452				error("can't get limit: %s", strerror(errno));
453			if (how & SOFT)
454				val = limit.rlim_cur;
455			else if (how & HARD)
456				val = limit.rlim_max;
457
458			if (l->units)
459				snprintf(optbuf, sizeof(optbuf),
460					"(%s, -%c) ", l->units, l->option);
461			else
462				snprintf(optbuf, sizeof(optbuf),
463					"(-%c) ", l->option);
464			out1fmt("%-18s %18s ", l->name, optbuf);
465			if (val == RLIM_INFINITY)
466				out1fmt("unlimited\n");
467			else
468			{
469				val /= l->factor;
470				out1fmt("%jd\n", (intmax_t)val);
471			}
472		}
473		return 0;
474	}
475
476	if (getrlimit(l->cmd, &limit) < 0)
477		error("can't get limit: %s", strerror(errno));
478	if (set) {
479		if (how & SOFT)
480			limit.rlim_cur = val;
481		if (how & HARD)
482			limit.rlim_max = val;
483		if (setrlimit(l->cmd, &limit) < 0)
484			error("bad limit: %s", strerror(errno));
485	} else {
486		if (how & SOFT)
487			val = limit.rlim_cur;
488		else if (how & HARD)
489			val = limit.rlim_max;
490
491		if (val == RLIM_INFINITY)
492			out1fmt("unlimited\n");
493		else
494		{
495			val /= l->factor;
496			out1fmt("%jd\n", (intmax_t)val);
497		}
498	}
499	return 0;
500}
501