miscbltin.c revision 104208
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 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
40#endif
41#endif /* not lint */
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD: head/bin/sh/miscbltin.c 104208 2002-09-30 13:29:32Z tjr $");
44
45/*
46 * Miscellaneous builtins.
47 */
48
49#include <sys/types.h>
50#include <sys/stat.h>
51#include <sys/time.h>
52#include <sys/resource.h>
53#include <unistd.h>
54#include <ctype.h>
55#include <errno.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <termios.h>
59
60#include "shell.h"
61#include "options.h"
62#include "var.h"
63#include "output.h"
64#include "memalloc.h"
65#include "error.h"
66#include "mystring.h"
67
68#undef eflag
69
70/*
71 * The read builtin.  The -r option causes backslashes to be treated like
72 * ordinary characters.
73 *
74 * This uses unbuffered input, which may be avoidable in some cases.
75 */
76
77int
78readcmd(int argc __unused, char **argv __unused)
79{
80	char **ap;
81	int backslash;
82	char c;
83	int rflag;
84	char *prompt;
85	char *ifs;
86	char *p;
87	int startword;
88	int status;
89	int i;
90	struct timeval tv;
91	char *tvptr;
92	fd_set ifds;
93	struct termios told, tnew;
94	int tsaved;
95
96	rflag = 0;
97	prompt = NULL;
98	tv.tv_sec = -1;
99	tv.tv_usec = 0;
100	while ((i = nextopt("erp:t:")) != '\0') {
101		switch(i) {
102		case 'p':
103			prompt = shoptarg;
104			break;
105		case 'e':
106			break;
107		case 'r':
108			rflag = 1;
109			break;
110		case 't':
111			tv.tv_sec = strtol(shoptarg, &tvptr, 0);
112			if (tvptr == shoptarg)
113				error("timeout value");
114			switch(*tvptr) {
115			case 0:
116			case 's':
117				break;
118			case 'h':
119				tv.tv_sec *= 60;
120				/* FALLTHROUGH */
121			case 'm':
122				tv.tv_sec *= 60;
123				break;
124			default:
125				error("timeout unit");
126			}
127			break;
128		}
129	}
130	if (prompt && isatty(0)) {
131		out2str(prompt);
132		flushall();
133	}
134	if (*(ap = argptr) == NULL)
135		error("arg count");
136	if ((ifs = bltinlookup("IFS", 1)) == NULL)
137		ifs = nullstr;
138
139	if (tv.tv_sec >= 0) {
140		/*
141		 * See if we can disable input processing; this will
142		 * not give the desired result if we are in a pipeline
143		 * and someone upstream is still in line-by-line mode.
144		 */
145		tsaved = 0;
146		if (tcgetattr(0, &told) == 0) {
147			memcpy(&tnew, &told, sizeof(told));
148			cfmakeraw(&tnew);
149			tcsetattr(0, TCSANOW, &tnew);
150			tsaved = 1;
151		}
152		/*
153		 * Wait for something to become available.
154		 */
155		FD_ZERO(&ifds);
156		FD_SET(0, &ifds);
157		status = select(1, &ifds, NULL, NULL, &tv);
158		if (tsaved)
159			tcsetattr(0, TCSANOW, &told);
160		/*
161		 * If there's nothing ready, return an error.
162		 */
163		if (status <= 0)
164			return(1);
165	}
166
167	status = 0;
168	startword = 1;
169	backslash = 0;
170	STARTSTACKSTR(p);
171	for (;;) {
172		if (read(STDIN_FILENO, &c, 1) != 1) {
173			status = 1;
174			break;
175		}
176		if (c == '\0')
177			continue;
178		if (backslash) {
179			backslash = 0;
180			if (c != '\n')
181				STPUTC(c, p);
182			continue;
183		}
184		if (!rflag && c == '\\') {
185			backslash++;
186			continue;
187		}
188		if (c == '\n')
189			break;
190		if (startword && *ifs == ' ' && strchr(ifs, c)) {
191			continue;
192		}
193		startword = 0;
194		if (backslash && c == '\\') {
195			if (read(STDIN_FILENO, &c, 1) != 1) {
196				status = 1;
197				break;
198			}
199			STPUTC(c, p);
200		} else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
201			STACKSTRNUL(p);
202			setvar(*ap, stackblock(), 0);
203			ap++;
204			startword = 1;
205			STARTSTACKSTR(p);
206		} else {
207			STPUTC(c, p);
208		}
209	}
210	STACKSTRNUL(p);
211	setvar(*ap, stackblock(), 0);
212	while (*++ap != NULL)
213		setvar(*ap, nullstr, 0);
214	return status;
215}
216
217
218
219int
220umaskcmd(int argc __unused, char **argv)
221{
222	char *ap;
223	int mask;
224	int i;
225	int symbolic_mode = 0;
226
227	while ((i = nextopt("S")) != '\0') {
228		symbolic_mode = 1;
229	}
230
231	INTOFF;
232	mask = umask(0);
233	umask(mask);
234	INTON;
235
236	if ((ap = *argptr) == NULL) {
237		if (symbolic_mode) {
238			char u[4], g[4], o[4];
239
240			i = 0;
241			if ((mask & S_IRUSR) == 0)
242				u[i++] = 'r';
243			if ((mask & S_IWUSR) == 0)
244				u[i++] = 'w';
245			if ((mask & S_IXUSR) == 0)
246				u[i++] = 'x';
247			u[i] = '\0';
248
249			i = 0;
250			if ((mask & S_IRGRP) == 0)
251				g[i++] = 'r';
252			if ((mask & S_IWGRP) == 0)
253				g[i++] = 'w';
254			if ((mask & S_IXGRP) == 0)
255				g[i++] = 'x';
256			g[i] = '\0';
257
258			i = 0;
259			if ((mask & S_IROTH) == 0)
260				o[i++] = 'r';
261			if ((mask & S_IWOTH) == 0)
262				o[i++] = 'w';
263			if ((mask & S_IXOTH) == 0)
264				o[i++] = 'x';
265			o[i] = '\0';
266
267			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
268		} else {
269			out1fmt("%.4o\n", mask);
270		}
271	} else {
272		if (isdigit(*ap)) {
273			mask = 0;
274			do {
275				if (*ap >= '8' || *ap < '0')
276					error("Illegal number: %s", argv[1]);
277				mask = (mask << 3) + (*ap - '0');
278			} while (*++ap != '\0');
279			umask(mask);
280		} else {
281			void *set;
282			if ((set = setmode (ap)) == 0)
283				error("Illegal number: %s", ap);
284
285			mask = getmode (set, ~mask & 0777);
286			umask(~mask & 0777);
287			free(set);
288		}
289	}
290	return 0;
291}
292
293/*
294 * ulimit builtin
295 *
296 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
297 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
298 * ash by J.T. Conklin.
299 *
300 * Public domain.
301 */
302
303struct limits {
304	const char *name;
305	const char *units;
306	int	cmd;
307	int	factor;	/* multiply by to get rlim_{cur,max} values */
308	char	option;
309};
310
311static const struct limits limits[] = {
312#ifdef RLIMIT_CPU
313	{ "cpu time",		"seconds",	RLIMIT_CPU,	   1, 't' },
314#endif
315#ifdef RLIMIT_FSIZE
316	{ "file size",		"512-blocks",	RLIMIT_FSIZE,	 512, 'f' },
317#endif
318#ifdef RLIMIT_DATA
319	{ "data seg size",	"kbytes",	RLIMIT_DATA,	1024, 'd' },
320#endif
321#ifdef RLIMIT_STACK
322	{ "stack size",		"kbytes",	RLIMIT_STACK,	1024, 's' },
323#endif
324#ifdef  RLIMIT_CORE
325	{ "core file size",	"512-blocks",	RLIMIT_CORE,	 512, 'c' },
326#endif
327#ifdef RLIMIT_RSS
328	{ "max memory size",	"kbytes",	RLIMIT_RSS,	1024, 'm' },
329#endif
330#ifdef RLIMIT_MEMLOCK
331	{ "locked memory",	"kbytes",	RLIMIT_MEMLOCK, 1024, 'l' },
332#endif
333#ifdef RLIMIT_NPROC
334	{ "max user processes",	(char *)0,	RLIMIT_NPROC,      1, 'u' },
335#endif
336#ifdef RLIMIT_NOFILE
337	{ "open files",		(char *)0,	RLIMIT_NOFILE,     1, 'n' },
338#endif
339#ifdef RLIMIT_VMEM
340	{ "virtual mem size",	"kbytes",	RLIMIT_VMEM,	1024, 'v' },
341#endif
342#ifdef RLIMIT_SWAP
343	{ "swap limit",		"kbytes",	RLIMIT_SWAP,	1024, 'w' },
344#endif
345#ifdef RLIMIT_SBSIZE
346	{ "sbsize",		"bytes",	RLIMIT_SBSIZE,	   1, 'b' },
347#endif
348	{ (char *) 0,		(char *)0,	0,		   0, '\0' }
349};
350
351int
352ulimitcmd(int argc __unused, char **argv __unused)
353{
354	int	c;
355	quad_t val = 0;
356	enum { SOFT = 0x1, HARD = 0x2 }
357			how = SOFT | HARD;
358	const struct limits	*l;
359	int		set, all = 0;
360	int		optc, what;
361	struct rlimit	limit;
362
363	what = 'f';
364	while ((optc = nextopt("HSatfdsmcnuvlb")) != '\0')
365		switch (optc) {
366		case 'H':
367			how = HARD;
368			break;
369		case 'S':
370			how = SOFT;
371			break;
372		case 'a':
373			all = 1;
374			break;
375		default:
376			what = optc;
377		}
378
379	for (l = limits; l->name && l->option != what; l++)
380		;
381	if (!l->name)
382		error("internal error (%c)", what);
383
384	set = *argptr ? 1 : 0;
385	if (set) {
386		char *p = *argptr;
387
388		if (all || argptr[1])
389			error("too many arguments");
390		if (strcmp(p, "unlimited") == 0)
391			val = RLIM_INFINITY;
392		else {
393			val = (quad_t) 0;
394
395			while ((c = *p++) >= '0' && c <= '9')
396			{
397				val = (val * 10) + (long)(c - '0');
398				if (val < (quad_t) 0)
399					break;
400			}
401			if (c)
402				error("bad number");
403			val *= l->factor;
404		}
405	}
406	if (all) {
407		for (l = limits; l->name; l++) {
408			char optbuf[40];
409			if (getrlimit(l->cmd, &limit) < 0)
410				error("can't get limit: %s", strerror(errno));
411			if (how & SOFT)
412				val = limit.rlim_cur;
413			else if (how & HARD)
414				val = limit.rlim_max;
415
416			if (l->units)
417				snprintf(optbuf, sizeof(optbuf),
418					"(%s, -%c) ", l->units, l->option);
419			else
420				snprintf(optbuf, sizeof(optbuf),
421					"(-%c) ", l->option);
422			out1fmt("%-18s %18s ", l->name, optbuf);
423			if (val == RLIM_INFINITY)
424				out1fmt("unlimited\n");
425			else
426			{
427				val /= l->factor;
428				out1fmt("%qd\n", (quad_t) val);
429			}
430		}
431		return 0;
432	}
433
434	if (getrlimit(l->cmd, &limit) < 0)
435		error("can't get limit: %s", strerror(errno));
436	if (set) {
437		if (how & SOFT)
438			limit.rlim_cur = val;
439		if (how & HARD)
440			limit.rlim_max = val;
441		if (setrlimit(l->cmd, &limit) < 0)
442			error("bad limit: %s", strerror(errno));
443	} else {
444		if (how & SOFT)
445			val = limit.rlim_cur;
446		else if (how & HARD)
447			val = limit.rlim_max;
448
449		if (val == RLIM_INFINITY)
450			out1fmt("unlimited\n");
451		else
452		{
453			val /= l->factor;
454			out1fmt("%qd\n", (quad_t) val);
455		}
456	}
457	return 0;
458}
459