miscbltin.c revision 212339
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 212339 2010-09-08 20:35:43Z jilles $");
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	const 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			startword = 0;
178			if (c != '\n')
179				STPUTC(c, p);
180			continue;
181		}
182		if (!rflag && c == '\\') {
183			backslash++;
184			continue;
185		}
186		if (c == '\n')
187			break;
188		if (strchr(ifs, c))
189			is_ifs = strchr(" \t\n", c) ? 1 : 2;
190		else
191			is_ifs = 0;
192
193		if (startword != 0) {
194			if (is_ifs == 1) {
195				/* Ignore leading IFS whitespace */
196				if (saveall)
197					STPUTC(c, p);
198				continue;
199			}
200			if (is_ifs == 2 && startword == 1) {
201				/* Only one non-whitespace IFS per word */
202				startword = 2;
203				if (saveall)
204					STPUTC(c, p);
205				continue;
206			}
207		}
208
209		if (is_ifs == 0) {
210			/* append this character to the current variable */
211			startword = 0;
212			if (saveall)
213				/* Not just a spare terminator */
214				saveall++;
215			STPUTC(c, p);
216			continue;
217		}
218
219		/* end of variable... */
220		startword = is_ifs;
221
222		if (ap[1] == NULL) {
223			/* Last variable needs all IFS chars */
224			saveall++;
225			STPUTC(c, p);
226			continue;
227		}
228
229		STACKSTRNUL(p);
230		setvar(*ap, stackblock(), 0);
231		ap++;
232		STARTSTACKSTR(p);
233	}
234	STACKSTRNUL(p);
235
236	/* Remove trailing IFS chars */
237	for (; stackblock() <= --p; *p = 0) {
238		if (!strchr(ifs, *p))
239			break;
240		if (strchr(" \t\n", *p))
241			/* Always remove whitespace */
242			continue;
243		if (saveall > 1)
244			/* Don't remove non-whitespace unless it was naked */
245			break;
246	}
247	setvar(*ap, stackblock(), 0);
248
249	/* Set any remaining args to "" */
250	while (*++ap != NULL)
251		setvar(*ap, nullstr, 0);
252	return status;
253}
254
255
256
257int
258umaskcmd(int argc __unused, char **argv __unused)
259{
260	char *ap;
261	int mask;
262	int i;
263	int symbolic_mode = 0;
264
265	while ((i = nextopt("S")) != '\0') {
266		symbolic_mode = 1;
267	}
268
269	INTOFF;
270	mask = umask(0);
271	umask(mask);
272	INTON;
273
274	if ((ap = *argptr) == NULL) {
275		if (symbolic_mode) {
276			char u[4], g[4], o[4];
277
278			i = 0;
279			if ((mask & S_IRUSR) == 0)
280				u[i++] = 'r';
281			if ((mask & S_IWUSR) == 0)
282				u[i++] = 'w';
283			if ((mask & S_IXUSR) == 0)
284				u[i++] = 'x';
285			u[i] = '\0';
286
287			i = 0;
288			if ((mask & S_IRGRP) == 0)
289				g[i++] = 'r';
290			if ((mask & S_IWGRP) == 0)
291				g[i++] = 'w';
292			if ((mask & S_IXGRP) == 0)
293				g[i++] = 'x';
294			g[i] = '\0';
295
296			i = 0;
297			if ((mask & S_IROTH) == 0)
298				o[i++] = 'r';
299			if ((mask & S_IWOTH) == 0)
300				o[i++] = 'w';
301			if ((mask & S_IXOTH) == 0)
302				o[i++] = 'x';
303			o[i] = '\0';
304
305			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
306		} else {
307			out1fmt("%.4o\n", mask);
308		}
309	} else {
310		if (isdigit(*ap)) {
311			mask = 0;
312			do {
313				if (*ap >= '8' || *ap < '0')
314					error("Illegal number: %s", *argptr);
315				mask = (mask << 3) + (*ap - '0');
316			} while (*++ap != '\0');
317			umask(mask);
318		} else {
319			void *set;
320			INTOFF;
321			if ((set = setmode (ap)) == 0)
322				error("Illegal number: %s", ap);
323
324			mask = getmode (set, ~mask & 0777);
325			umask(~mask & 0777);
326			free(set);
327			INTON;
328		}
329	}
330	return 0;
331}
332
333/*
334 * ulimit builtin
335 *
336 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
337 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
338 * ash by J.T. Conklin.
339 *
340 * Public domain.
341 */
342
343struct limits {
344	const char *name;
345	const char *units;
346	int	cmd;
347	int	factor;	/* multiply by to get rlim_{cur,max} values */
348	char	option;
349};
350
351static const struct limits limits[] = {
352#ifdef RLIMIT_CPU
353	{ "cpu time",		"seconds",	RLIMIT_CPU,	   1, 't' },
354#endif
355#ifdef RLIMIT_FSIZE
356	{ "file size",		"512-blocks",	RLIMIT_FSIZE,	 512, 'f' },
357#endif
358#ifdef RLIMIT_DATA
359	{ "data seg size",	"kbytes",	RLIMIT_DATA,	1024, 'd' },
360#endif
361#ifdef RLIMIT_STACK
362	{ "stack size",		"kbytes",	RLIMIT_STACK,	1024, 's' },
363#endif
364#ifdef  RLIMIT_CORE
365	{ "core file size",	"512-blocks",	RLIMIT_CORE,	 512, 'c' },
366#endif
367#ifdef RLIMIT_RSS
368	{ "max memory size",	"kbytes",	RLIMIT_RSS,	1024, 'm' },
369#endif
370#ifdef RLIMIT_MEMLOCK
371	{ "locked memory",	"kbytes",	RLIMIT_MEMLOCK, 1024, 'l' },
372#endif
373#ifdef RLIMIT_NPROC
374	{ "max user processes",	(char *)0,	RLIMIT_NPROC,      1, 'u' },
375#endif
376#ifdef RLIMIT_NOFILE
377	{ "open files",		(char *)0,	RLIMIT_NOFILE,     1, 'n' },
378#endif
379#ifdef RLIMIT_VMEM
380	{ "virtual mem size",	"kbytes",	RLIMIT_VMEM,	1024, 'v' },
381#endif
382#ifdef RLIMIT_SWAP
383	{ "swap limit",		"kbytes",	RLIMIT_SWAP,	1024, 'w' },
384#endif
385#ifdef RLIMIT_SBSIZE
386	{ "sbsize",		"bytes",	RLIMIT_SBSIZE,	   1, 'b' },
387#endif
388#ifdef RLIMIT_NPTS
389	{ "pseudo-terminals",	(char *)0,	RLIMIT_NPTS,	   1, 'p' },
390#endif
391	{ (char *) 0,		(char *)0,	0,		   0, '\0' }
392};
393
394int
395ulimitcmd(int argc __unused, char **argv __unused)
396{
397	int	c;
398	rlim_t val = 0;
399	enum { SOFT = 0x1, HARD = 0x2 }
400			how = SOFT | HARD;
401	const struct limits	*l;
402	int		set, all = 0;
403	int		optc, what;
404	struct rlimit	limit;
405
406	what = 'f';
407	while ((optc = nextopt("HSatfdsmcnuvlbpw")) != '\0')
408		switch (optc) {
409		case 'H':
410			how = HARD;
411			break;
412		case 'S':
413			how = SOFT;
414			break;
415		case 'a':
416			all = 1;
417			break;
418		default:
419			what = optc;
420		}
421
422	for (l = limits; l->name && l->option != what; l++)
423		;
424	if (!l->name)
425		error("internal error (%c)", what);
426
427	set = *argptr ? 1 : 0;
428	if (set) {
429		char *p = *argptr;
430
431		if (all || argptr[1])
432			error("too many arguments");
433		if (strcmp(p, "unlimited") == 0)
434			val = RLIM_INFINITY;
435		else {
436			val = 0;
437
438			while ((c = *p++) >= '0' && c <= '9')
439			{
440				val = (val * 10) + (long)(c - '0');
441				if (val < 0)
442					break;
443			}
444			if (c)
445				error("bad number");
446			val *= l->factor;
447		}
448	}
449	if (all) {
450		for (l = limits; l->name; l++) {
451			char optbuf[40];
452			if (getrlimit(l->cmd, &limit) < 0)
453				error("can't get limit: %s", strerror(errno));
454			if (how & SOFT)
455				val = limit.rlim_cur;
456			else if (how & HARD)
457				val = limit.rlim_max;
458
459			if (l->units)
460				snprintf(optbuf, sizeof(optbuf),
461					"(%s, -%c) ", l->units, l->option);
462			else
463				snprintf(optbuf, sizeof(optbuf),
464					"(-%c) ", l->option);
465			out1fmt("%-18s %18s ", l->name, optbuf);
466			if (val == RLIM_INFINITY)
467				out1fmt("unlimited\n");
468			else
469			{
470				val /= l->factor;
471				out1fmt("%jd\n", (intmax_t)val);
472			}
473		}
474		return 0;
475	}
476
477	if (getrlimit(l->cmd, &limit) < 0)
478		error("can't get limit: %s", strerror(errno));
479	if (set) {
480		if (how & SOFT)
481			limit.rlim_cur = val;
482		if (how & HARD)
483			limit.rlim_max = val;
484		if (setrlimit(l->cmd, &limit) < 0)
485			error("bad limit: %s", strerror(errno));
486	} else {
487		if (how & SOFT)
488			val = limit.rlim_cur;
489		else if (how & HARD)
490			val = limit.rlim_max;
491
492		if (val == RLIM_INFINITY)
493			out1fmt("unlimited\n");
494		else
495		{
496			val /= l->factor;
497			out1fmt("%jd\n", (intmax_t)val);
498		}
499	}
500	return 0;
501}
502