miscbltin.c revision 50394
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
41static const char rcsid[] =
42	"$Id: miscbltin.c,v 1.19 1999/05/08 10:21:56 kris Exp $";
43#endif /* not lint */
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
70extern char **argptr;		/* argument list for builtin command */
71
72
73/*
74 * The read builtin.  The -r option causes backslashes to be treated like
75 * ordinary characters.
76 *
77 * This uses unbuffered input, which may be avoidable in some cases.
78 */
79
80int
81readcmd(argc, argv)
82	int argc __unused;
83	char **argv __unused;
84{
85	char **ap;
86	int backslash;
87	char c;
88	int rflag;
89	char *prompt;
90	char *ifs;
91	char *p;
92	int startword;
93	int status;
94	int i;
95	struct timeval tv;
96	char *tvptr;
97	fd_set ifds;
98	struct termios told, tnew;
99	int tsaved;
100
101	rflag = 0;
102	prompt = NULL;
103	tv.tv_sec = -1;
104	tv.tv_usec = 0;
105	while ((i = nextopt("erp:t:")) != '\0') {
106		switch(i) {
107		case 'p':
108			prompt = optarg;
109			break;
110		case 'e':
111			break;
112		case 'r':
113			rflag = 1;
114			break;
115		case 't':
116			tv.tv_sec = strtol(optarg, &tvptr, 0);
117			if (tvptr == optarg)
118				error("timeout value");
119			switch(*tvptr) {
120			case 0:
121			case 's':
122				break;
123			case 'h':
124				tv.tv_sec *= 60;
125				/* FALLTHROUGH */
126			case 'm':
127				tv.tv_sec *= 60;
128				break;
129			default:
130				error("timeout unit");
131			}
132			break;
133		}
134	}
135	if (prompt && isatty(0)) {
136		out2str(prompt);
137		flushall();
138	}
139	if (*(ap = argptr) == NULL)
140		error("arg count");
141	if ((ifs = bltinlookup("IFS", 1)) == NULL)
142		ifs = nullstr;
143
144	if (tv.tv_sec >= 0) {
145		/*
146		 * See if we can disable input processing; this will
147		 * not give the desired result if we are in a pipeline
148		 * and someone upstream is still in line-by-line mode.
149		 */
150		tsaved = 0;
151		if (tcgetattr(0, &told) == 0) {
152			memcpy(&tnew, &told, sizeof(told));
153			cfmakeraw(&tnew);
154			tcsetattr(0, TCSANOW, &tnew);
155			tsaved = 1;
156		}
157		/*
158		 * Wait for something to become available.
159		 */
160		FD_ZERO(&ifds);
161		FD_SET(0, &ifds);
162		status = select(1, &ifds, NULL, NULL, &tv);
163		if (tsaved)
164			tcsetattr(0, TCSANOW, &told);
165		/*
166		 * If there's nothing ready, return an error.
167		 */
168		if (status <= 0)
169			return(1);
170	}
171
172	status = 0;
173	startword = 1;
174	backslash = 0;
175	STARTSTACKSTR(p);
176	for (;;) {
177		if (read(0, &c, 1) != 1) {
178			status = 1;
179			break;
180		}
181		if (c == '\0')
182			continue;
183		if (backslash) {
184			backslash = 0;
185			if (c != '\n')
186				STPUTC(c, p);
187			continue;
188		}
189		if (!rflag && c == '\\') {
190			backslash++;
191			continue;
192		}
193		if (c == '\n')
194			break;
195		if (startword && *ifs == ' ' && strchr(ifs, c)) {
196			continue;
197		}
198		startword = 0;
199		if (backslash && c == '\\') {
200			if (read(0, &c, 1) != 1) {
201				status = 1;
202				break;
203			}
204			STPUTC(c, p);
205		} else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
206			STACKSTRNUL(p);
207			setvar(*ap, stackblock(), 0);
208			ap++;
209			startword = 1;
210			STARTSTACKSTR(p);
211		} else {
212			STPUTC(c, p);
213		}
214	}
215	STACKSTRNUL(p);
216	setvar(*ap, stackblock(), 0);
217	while (*++ap != NULL)
218		setvar(*ap, nullstr, 0);
219	return status;
220}
221
222
223
224int
225umaskcmd(argc, argv)
226	int argc __unused;
227	char **argv;
228{
229	char *ap;
230	int mask;
231	int i;
232	int symbolic_mode = 0;
233
234	while ((i = nextopt("S")) != '\0') {
235		symbolic_mode = 1;
236	}
237
238	INTOFF;
239	mask = umask(0);
240	umask(mask);
241	INTON;
242
243	if ((ap = *argptr) == NULL) {
244		if (symbolic_mode) {
245			char u[4], g[4], o[4];
246
247			i = 0;
248			if ((mask & S_IRUSR) == 0)
249				u[i++] = 'r';
250			if ((mask & S_IWUSR) == 0)
251				u[i++] = 'w';
252			if ((mask & S_IXUSR) == 0)
253				u[i++] = 'x';
254			u[i] = '\0';
255
256			i = 0;
257			if ((mask & S_IRGRP) == 0)
258				g[i++] = 'r';
259			if ((mask & S_IWGRP) == 0)
260				g[i++] = 'w';
261			if ((mask & S_IXGRP) == 0)
262				g[i++] = 'x';
263			g[i] = '\0';
264
265			i = 0;
266			if ((mask & S_IROTH) == 0)
267				o[i++] = 'r';
268			if ((mask & S_IWOTH) == 0)
269				o[i++] = 'w';
270			if ((mask & S_IXOTH) == 0)
271				o[i++] = 'x';
272			o[i] = '\0';
273
274			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
275		} else {
276			out1fmt("%.4o\n", mask);
277		}
278	} else {
279		if (isdigit(*ap)) {
280			mask = 0;
281			do {
282				if (*ap >= '8' || *ap < '0')
283					error("Illegal number: %s", argv[1]);
284				mask = (mask << 3) + (*ap - '0');
285			} while (*++ap != '\0');
286			umask(mask);
287		} else {
288			void *set;
289			if ((set = setmode (ap)) == 0)
290				error("Illegal number: %s", ap);
291
292			mask = getmode (set, ~mask & 0777);
293			umask(~mask & 0777);
294			free(set);
295		}
296	}
297	return 0;
298}
299
300/*
301 * ulimit builtin
302 *
303 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
304 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
305 * ash by J.T. Conklin.
306 *
307 * Public domain.
308 */
309
310struct limits {
311	const char *name;
312	const char *units;
313	int	cmd;
314	int	factor;	/* multiply by to get rlim_{cur,max} values */
315	char	option;
316};
317
318static const struct limits limits[] = {
319#ifdef RLIMIT_CPU
320	{ "cpu time",		"seconds",	RLIMIT_CPU,	   1, 't' },
321#endif
322#ifdef RLIMIT_FSIZE
323	{ "file size",		"512-blocks",	RLIMIT_FSIZE,	 512, 'f' },
324#endif
325#ifdef RLIMIT_DATA
326	{ "data seg size",	"kbytes",	RLIMIT_DATA,	1024, 'd' },
327#endif
328#ifdef RLIMIT_STACK
329	{ "stack size",		"kbytes",	RLIMIT_STACK,	1024, 's' },
330#endif
331#ifdef  RLIMIT_CORE
332	{ "core file size",	"512-blocks",	RLIMIT_CORE,	 512, 'c' },
333#endif
334#ifdef RLIMIT_RSS
335	{ "max memory size",	"kbytes",	RLIMIT_RSS,	1024, 'm' },
336#endif
337#ifdef RLIMIT_MEMLOCK
338	{ "locked memory",	"kbytes",	RLIMIT_MEMLOCK, 1024, 'l' },
339#endif
340#ifdef RLIMIT_NPROC
341	{ "max user processes",	(char *)0,	RLIMIT_NPROC,      1, 'u' },
342#endif
343#ifdef RLIMIT_NOFILE
344	{ "open files",		(char *)0,	RLIMIT_NOFILE,     1, 'n' },
345#endif
346#ifdef RLIMIT_VMEM
347	{ "virtual mem size",	"kbytes",	RLIMIT_VMEM,	1024, 'v' },
348#endif
349#ifdef RLIMIT_SWAP
350	{ "swap limit",		"kbytes",	RLIMIT_SWAP,	1024, 'w' },
351#endif
352	{ (char *) 0,		(char *)0,	0,		   0, '\0' }
353};
354
355int
356ulimitcmd(argc, argv)
357	int argc __unused;
358	char **argv __unused;
359{
360	int	c;
361	quad_t val = 0;
362	enum { SOFT = 0x1, HARD = 0x2 }
363			how = SOFT | HARD;
364	const struct limits	*l;
365	int		set, all = 0;
366	int		optc, what;
367	struct rlimit	limit;
368
369	what = 'f';
370	while ((optc = nextopt("HSatfdsmcnul")) != '\0')
371		switch (optc) {
372		case 'H':
373			how = HARD;
374			break;
375		case 'S':
376			how = SOFT;
377			break;
378		case 'a':
379			all = 1;
380			break;
381		default:
382			what = optc;
383		}
384
385	for (l = limits; l->name && l->option != what; l++)
386		;
387	if (!l->name)
388		error("ulimit: internal error (%c)", what);
389
390	set = *argptr ? 1 : 0;
391	if (set) {
392		char *p = *argptr;
393
394		if (all || argptr[1])
395			error("ulimit: too many arguments");
396		if (strcmp(p, "unlimited") == 0)
397			val = RLIM_INFINITY;
398		else {
399			val = (quad_t) 0;
400
401			while ((c = *p++) >= '0' && c <= '9')
402			{
403				val = (val * 10) + (long)(c - '0');
404				if (val < (quad_t) 0)
405					break;
406			}
407			if (c)
408				error("ulimit: bad number");
409			val *= l->factor;
410		}
411	}
412	if (all) {
413		for (l = limits; l->name; l++) {
414			char optbuf[40];
415			if (getrlimit(l->cmd, &limit) < 0)
416				error("ulimit: can't get limit: %s", strerror(errno));
417			if (how & SOFT)
418				val = limit.rlim_cur;
419			else if (how & HARD)
420				val = limit.rlim_max;
421
422			if (l->units)
423				snprintf(optbuf, sizeof(optbuf),
424					"(%s, -%c) ", l->units, l->option);
425			else
426				snprintf(optbuf, sizeof(optbuf),
427					"(-%c) ", l->option);
428			out1fmt("%-18s %18s ", l->name, optbuf);
429			if (val == RLIM_INFINITY)
430				out1fmt("unlimited\n");
431			else
432			{
433				val /= l->factor;
434				out1fmt("%qd\n", (quad_t) val);
435			}
436		}
437		return 0;
438	}
439
440	if (getrlimit(l->cmd, &limit) < 0)
441		error("ulimit: can't get limit: %s", strerror(errno));
442	if (set) {
443		if (how & SOFT)
444			limit.rlim_cur = val;
445		if (how & HARD)
446			limit.rlim_max = val;
447		if (setrlimit(l->cmd, &limit) < 0)
448			error("ulimit: bad limit: %s", strerror(errno));
449	} else {
450		if (how & SOFT)
451			val = limit.rlim_cur;
452		else if (how & HARD)
453			val = limit.rlim_max;
454
455		if (val == RLIM_INFINITY)
456			out1fmt("unlimited\n");
457		else
458		{
459			val /= l->factor;
460			out1fmt("%qd\n", (quad_t) val);
461		}
462	}
463	return 0;
464}
465