miscbltin.c revision 190298
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD: head/bin/sh/miscbltin.c 190298 2009-03-22 22:57:53Z stefanf $");
401556Srgrimes
411556Srgrimes/*
4246684Skris * Miscellaneous builtins.
431556Srgrimes */
441556Srgrimes
4517987Speter#include <sys/types.h>
4617987Speter#include <sys/stat.h>
4717987Speter#include <sys/time.h>
4817987Speter#include <sys/resource.h>
4917987Speter#include <unistd.h>
5017987Speter#include <ctype.h>
5118016Speter#include <errno.h>
52104282Smux#include <stdint.h>
5318018Speter#include <stdio.h>
5438536Scracauer#include <stdlib.h>
5529983Smsmith#include <termios.h>
5617987Speter
571556Srgrimes#include "shell.h"
581556Srgrimes#include "options.h"
591556Srgrimes#include "var.h"
601556Srgrimes#include "output.h"
611556Srgrimes#include "memalloc.h"
621556Srgrimes#include "error.h"
631556Srgrimes#include "mystring.h"
641556Srgrimes
651556Srgrimes#undef eflag
661556Srgrimes
67149018Sstefanfint readcmd(int, char **);
68149018Sstefanfint umaskcmd(int, char **);
69149018Sstefanfint ulimitcmd(int, char **);
70149018Sstefanf
711556Srgrimes/*
7250394Stg * The read builtin.  The -r option causes backslashes to be treated like
7350394Stg * ordinary characters.
741556Srgrimes *
751556Srgrimes * This uses unbuffered input, which may be avoidable in some cases.
76190295Sstefanf *
77190295Sstefanf * Note that if IFS=' :' then read x y should work so that:
78190295Sstefanf * 'a b'	x='a', y='b'
79190295Sstefanf * ' a b '	x='a', y='b'
80190295Sstefanf * ':b'		x='',  y='b'
81190295Sstefanf * ':'		x='',  y=''
82190295Sstefanf * '::'		x='',  y=''
83190295Sstefanf * ': :'	x='',  y=''
84190295Sstefanf * ':::'	x='',  y='::'
85190295Sstefanf * ':b c:'	x='',  y='b c:'
861556Srgrimes */
871556Srgrimes
8817987Speterint
8990111Simpreadcmd(int argc __unused, char **argv __unused)
9017987Speter{
911556Srgrimes	char **ap;
921556Srgrimes	int backslash;
931556Srgrimes	char c;
9450394Stg	int rflag;
951556Srgrimes	char *prompt;
961556Srgrimes	char *ifs;
971556Srgrimes	char *p;
981556Srgrimes	int startword;
991556Srgrimes	int status;
1001556Srgrimes	int i;
101190295Sstefanf	int is_ifs;
102190295Sstefanf	int saveall = 0;
10329983Smsmith	struct timeval tv;
10429983Smsmith	char *tvptr;
10529983Smsmith	fd_set ifds;
10629983Smsmith	struct termios told, tnew;
10729983Smsmith	int tsaved;
1081556Srgrimes
10950394Stg	rflag = 0;
1101556Srgrimes	prompt = NULL;
11129983Smsmith	tv.tv_sec = -1;
11229983Smsmith	tv.tv_usec = 0;
11350394Stg	while ((i = nextopt("erp:t:")) != '\0') {
11429983Smsmith		switch(i) {
11529983Smsmith		case 'p':
11659436Scracauer			prompt = shoptarg;
11729983Smsmith			break;
11829983Smsmith		case 'e':
11929983Smsmith			break;
12050394Stg		case 'r':
12150394Stg			rflag = 1;
12250394Stg			break;
12329983Smsmith		case 't':
12459436Scracauer			tv.tv_sec = strtol(shoptarg, &tvptr, 0);
12559436Scracauer			if (tvptr == shoptarg)
12629983Smsmith				error("timeout value");
12729983Smsmith			switch(*tvptr) {
12829983Smsmith			case 0:
12929983Smsmith			case 's':
13029983Smsmith				break;
13129983Smsmith			case 'h':
13229983Smsmith				tv.tv_sec *= 60;
13329983Smsmith				/* FALLTHROUGH */
13429983Smsmith			case 'm':
13529983Smsmith				tv.tv_sec *= 60;
13629983Smsmith				break;
13729983Smsmith			default:
13829983Smsmith				error("timeout unit");
13929983Smsmith			}
14029983Smsmith			break;
14129983Smsmith		}
1421556Srgrimes	}
1431556Srgrimes	if (prompt && isatty(0)) {
1441556Srgrimes		out2str(prompt);
1451556Srgrimes		flushall();
1461556Srgrimes	}
1471556Srgrimes	if (*(ap = argptr) == NULL)
1481556Srgrimes		error("arg count");
1491556Srgrimes	if ((ifs = bltinlookup("IFS", 1)) == NULL)
150190298Sstefanf		ifs = " \t\n";
15129983Smsmith
15229983Smsmith	if (tv.tv_sec >= 0) {
15329983Smsmith		/*
15429983Smsmith		 * See if we can disable input processing; this will
15529983Smsmith		 * not give the desired result if we are in a pipeline
15629983Smsmith		 * and someone upstream is still in line-by-line mode.
15729983Smsmith		 */
15829983Smsmith		tsaved = 0;
15929983Smsmith		if (tcgetattr(0, &told) == 0) {
16029983Smsmith			memcpy(&tnew, &told, sizeof(told));
16129983Smsmith			cfmakeraw(&tnew);
162189542Sed			tnew.c_iflag |= told.c_iflag & ICRNL;
16329983Smsmith			tcsetattr(0, TCSANOW, &tnew);
16429983Smsmith			tsaved = 1;
16529983Smsmith		}
16629983Smsmith		/*
16729983Smsmith		 * Wait for something to become available.
16829983Smsmith		 */
16929983Smsmith		FD_ZERO(&ifds);
17029983Smsmith		FD_SET(0, &ifds);
17129983Smsmith		status = select(1, &ifds, NULL, NULL, &tv);
17229983Smsmith		if (tsaved)
17329983Smsmith			tcsetattr(0, TCSANOW, &told);
17429983Smsmith		/*
17529983Smsmith		 * If there's nothing ready, return an error.
17629983Smsmith		 */
17729983Smsmith		if (status <= 0)
17829983Smsmith			return(1);
17929983Smsmith	}
18029983Smsmith
1811556Srgrimes	status = 0;
182190295Sstefanf	startword = 2;
1831556Srgrimes	backslash = 0;
1841556Srgrimes	STARTSTACKSTR(p);
1851556Srgrimes	for (;;) {
18680381Ssheldonh		if (read(STDIN_FILENO, &c, 1) != 1) {
1871556Srgrimes			status = 1;
1881556Srgrimes			break;
1891556Srgrimes		}
1901556Srgrimes		if (c == '\0')
1911556Srgrimes			continue;
1921556Srgrimes		if (backslash) {
1931556Srgrimes			backslash = 0;
1941556Srgrimes			if (c != '\n')
1951556Srgrimes				STPUTC(c, p);
1961556Srgrimes			continue;
1971556Srgrimes		}
19850394Stg		if (!rflag && c == '\\') {
1991556Srgrimes			backslash++;
2001556Srgrimes			continue;
2011556Srgrimes		}
2021556Srgrimes		if (c == '\n')
2031556Srgrimes			break;
204190295Sstefanf		if (strchr(ifs, c))
205190295Sstefanf			is_ifs = strchr(" \t\n", c) ? 1 : 2;
206190295Sstefanf		else
207190295Sstefanf			is_ifs = 0;
208190295Sstefanf
209190295Sstefanf		if (startword != 0) {
210190295Sstefanf			if (is_ifs == 1) {
211190295Sstefanf				/* Ignore leading IFS whitespace */
212190295Sstefanf				if (saveall)
213190295Sstefanf					STPUTC(c, p);
214190295Sstefanf				continue;
215190295Sstefanf			}
216190295Sstefanf			if (is_ifs == 2 && startword == 1) {
217190295Sstefanf				/* Only one non-whitespace IFS per word */
218190295Sstefanf				startword = 2;
219190295Sstefanf				if (saveall)
220190295Sstefanf					STPUTC(c, p);
221190295Sstefanf				continue;
222190295Sstefanf			}
223190295Sstefanf		}
224190295Sstefanf
225190295Sstefanf		if (is_ifs == 0) {
226190295Sstefanf			/* append this character to the current variable */
227190295Sstefanf			startword = 0;
228190295Sstefanf			if (saveall)
229190295Sstefanf				/* Not just a spare terminator */
230190295Sstefanf				saveall++;
231190295Sstefanf			STPUTC(c, p);
2321556Srgrimes			continue;
2331556Srgrimes		}
234190295Sstefanf
235190295Sstefanf		/* end of variable... */
236190295Sstefanf		startword = is_ifs;
237190295Sstefanf
238190295Sstefanf		if (ap[1] == NULL) {
239190295Sstefanf			/* Last variable needs all IFS chars */
240190295Sstefanf			saveall++;
2411556Srgrimes			STPUTC(c, p);
242190295Sstefanf			continue;
2431556Srgrimes		}
244190295Sstefanf
245190295Sstefanf		STACKSTRNUL(p);
246190295Sstefanf		setvar(*ap, stackblock(), 0);
247190295Sstefanf		ap++;
248190295Sstefanf		STARTSTACKSTR(p);
2491556Srgrimes	}
2501556Srgrimes	STACKSTRNUL(p);
251190295Sstefanf
252190295Sstefanf	/* Remove trailing IFS chars */
253190295Sstefanf	for (; stackblock() <= --p; *p = 0) {
254190295Sstefanf		if (!strchr(ifs, *p))
255190295Sstefanf			break;
256190295Sstefanf		if (strchr(" \t\n", *p))
257190295Sstefanf			/* Always remove whitespace */
258190295Sstefanf			continue;
259190295Sstefanf		if (saveall > 1)
260190295Sstefanf			/* Don't remove non-whitespace unless it was naked */
261190295Sstefanf			break;
262190295Sstefanf	}
2631556Srgrimes	setvar(*ap, stackblock(), 0);
264190295Sstefanf
265190295Sstefanf	/* Set any remaining args to "" */
2661556Srgrimes	while (*++ap != NULL)
2671556Srgrimes		setvar(*ap, nullstr, 0);
2681556Srgrimes	return status;
2691556Srgrimes}
2701556Srgrimes
2711556Srgrimes
2721556Srgrimes
27317987Speterint
27490111Simpumaskcmd(int argc __unused, char **argv)
27517987Speter{
27617987Speter	char *ap;
2771556Srgrimes	int mask;
2781556Srgrimes	int i;
27917987Speter	int symbolic_mode = 0;
2801556Srgrimes
28117987Speter	while ((i = nextopt("S")) != '\0') {
28217987Speter		symbolic_mode = 1;
2831556Srgrimes	}
28411571Sjoerg
28517987Speter	INTOFF;
28617987Speter	mask = umask(0);
28717987Speter	umask(mask);
28817987Speter	INTON;
28911571Sjoerg
29017987Speter	if ((ap = *argptr) == NULL) {
29117987Speter		if (symbolic_mode) {
29217987Speter			char u[4], g[4], o[4];
29311571Sjoerg
29417987Speter			i = 0;
29517987Speter			if ((mask & S_IRUSR) == 0)
29617987Speter				u[i++] = 'r';
29717987Speter			if ((mask & S_IWUSR) == 0)
29817987Speter				u[i++] = 'w';
29917987Speter			if ((mask & S_IXUSR) == 0)
30017987Speter				u[i++] = 'x';
30117987Speter			u[i] = '\0';
30211571Sjoerg
30317987Speter			i = 0;
30417987Speter			if ((mask & S_IRGRP) == 0)
30517987Speter				g[i++] = 'r';
30617987Speter			if ((mask & S_IWGRP) == 0)
30717987Speter				g[i++] = 'w';
30817987Speter			if ((mask & S_IXGRP) == 0)
30917987Speter				g[i++] = 'x';
31017987Speter			g[i] = '\0';
31111571Sjoerg
31217987Speter			i = 0;
31317987Speter			if ((mask & S_IROTH) == 0)
31417987Speter				o[i++] = 'r';
31517987Speter			if ((mask & S_IWOTH) == 0)
31617987Speter				o[i++] = 'w';
31717987Speter			if ((mask & S_IXOTH) == 0)
31817987Speter				o[i++] = 'x';
31917987Speter			o[i] = '\0';
32011571Sjoerg
32117987Speter			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
32217987Speter		} else {
32317987Speter			out1fmt("%.4o\n", mask);
32417987Speter		}
32517987Speter	} else {
32617987Speter		if (isdigit(*ap)) {
32717987Speter			mask = 0;
32817987Speter			do {
32917987Speter				if (*ap >= '8' || *ap < '0')
330149918Sstefanf					error("Illegal number: %s", *argptr);
33117987Speter				mask = (mask << 3) + (*ap - '0');
33217987Speter			} while (*++ap != '\0');
33317987Speter			umask(mask);
33417987Speter		} else {
33520425Ssteve			void *set;
336151795Sstefanf			INTOFF;
33717987Speter			if ((set = setmode (ap)) == 0)
33841844Simp				error("Illegal number: %s", ap);
33911571Sjoerg
34017987Speter			mask = getmode (set, ~mask & 0777);
34117987Speter			umask(~mask & 0777);
34241844Simp			free(set);
343151795Sstefanf			INTON;
34417987Speter		}
34517987Speter	}
34611571Sjoerg	return 0;
34711571Sjoerg}
34811571Sjoerg
34917987Speter/*
35017987Speter * ulimit builtin
35117987Speter *
35217987Speter * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
35317987Speter * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
35417987Speter * ash by J.T. Conklin.
35517987Speter *
35617987Speter * Public domain.
35717987Speter */
35811571Sjoerg
35917987Speterstruct limits {
36017987Speter	const char *name;
36118016Speter	const char *units;
36217987Speter	int	cmd;
36317987Speter	int	factor;	/* multiply by to get rlim_{cur,max} values */
36417987Speter	char	option;
36517987Speter};
36611571Sjoerg
36717987Speterstatic const struct limits limits[] = {
36817987Speter#ifdef RLIMIT_CPU
36918016Speter	{ "cpu time",		"seconds",	RLIMIT_CPU,	   1, 't' },
37017987Speter#endif
37117987Speter#ifdef RLIMIT_FSIZE
37218016Speter	{ "file size",		"512-blocks",	RLIMIT_FSIZE,	 512, 'f' },
37317987Speter#endif
37417987Speter#ifdef RLIMIT_DATA
37518016Speter	{ "data seg size",	"kbytes",	RLIMIT_DATA,	1024, 'd' },
37617987Speter#endif
37717987Speter#ifdef RLIMIT_STACK
37818016Speter	{ "stack size",		"kbytes",	RLIMIT_STACK,	1024, 's' },
37917987Speter#endif
38017987Speter#ifdef  RLIMIT_CORE
38118016Speter	{ "core file size",	"512-blocks",	RLIMIT_CORE,	 512, 'c' },
38217987Speter#endif
38317987Speter#ifdef RLIMIT_RSS
38418016Speter	{ "max memory size",	"kbytes",	RLIMIT_RSS,	1024, 'm' },
38517987Speter#endif
38617987Speter#ifdef RLIMIT_MEMLOCK
38718016Speter	{ "locked memory",	"kbytes",	RLIMIT_MEMLOCK, 1024, 'l' },
38817987Speter#endif
38917987Speter#ifdef RLIMIT_NPROC
39018016Speter	{ "max user processes",	(char *)0,	RLIMIT_NPROC,      1, 'u' },
39117987Speter#endif
39217987Speter#ifdef RLIMIT_NOFILE
39318016Speter	{ "open files",		(char *)0,	RLIMIT_NOFILE,     1, 'n' },
39417987Speter#endif
39517987Speter#ifdef RLIMIT_VMEM
39618016Speter	{ "virtual mem size",	"kbytes",	RLIMIT_VMEM,	1024, 'v' },
39717987Speter#endif
39817987Speter#ifdef RLIMIT_SWAP
39918016Speter	{ "swap limit",		"kbytes",	RLIMIT_SWAP,	1024, 'w' },
40017987Speter#endif
40152072Sgreen#ifdef RLIMIT_SBSIZE
40252072Sgreen	{ "sbsize",		"bytes",	RLIMIT_SBSIZE,	   1, 'b' },
40352072Sgreen#endif
404181905Sed#ifdef RLIMIT_NPTS
405181905Sed	{ "pseudo-terminals",	(char *)0,	RLIMIT_NPTS,	   1, 'p' },
406181905Sed#endif
40718016Speter	{ (char *) 0,		(char *)0,	0,		   0, '\0' }
40817987Speter};
40917987Speter
41017987Speterint
41190111Simpulimitcmd(int argc __unused, char **argv __unused)
41217987Speter{
41325222Ssteve	int	c;
414104282Smux	rlim_t val = 0;
41517987Speter	enum { SOFT = 0x1, HARD = 0x2 }
41617987Speter			how = SOFT | HARD;
41717987Speter	const struct limits	*l;
41817987Speter	int		set, all = 0;
41917987Speter	int		optc, what;
42017987Speter	struct rlimit	limit;
42111571Sjoerg
42217987Speter	what = 'f';
423181905Sed	while ((optc = nextopt("HSatfdsmcnuvlbp")) != '\0')
42417987Speter		switch (optc) {
42511571Sjoerg		case 'H':
42617987Speter			how = HARD;
42711571Sjoerg			break;
42811571Sjoerg		case 'S':
42917987Speter			how = SOFT;
43011571Sjoerg			break;
43111571Sjoerg		case 'a':
43217987Speter			all = 1;
43311571Sjoerg			break;
43417987Speter		default:
43517987Speter			what = optc;
43611571Sjoerg		}
43711571Sjoerg
43817987Speter	for (l = limits; l->name && l->option != what; l++)
43917987Speter		;
44017987Speter	if (!l->name)
441104208Stjr		error("internal error (%c)", what);
44217987Speter
44317987Speter	set = *argptr ? 1 : 0;
44417987Speter	if (set) {
44517987Speter		char *p = *argptr;
44617987Speter
44717987Speter		if (all || argptr[1])
448104208Stjr			error("too many arguments");
44917987Speter		if (strcmp(p, "unlimited") == 0)
45011571Sjoerg			val = RLIM_INFINITY;
45111571Sjoerg		else {
452104282Smux			val = 0;
45317987Speter
45417987Speter			while ((c = *p++) >= '0' && c <= '9')
45517987Speter			{
45617987Speter				val = (val * 10) + (long)(c - '0');
457104282Smux				if (val < 0)
45817987Speter					break;
45917987Speter			}
46017987Speter			if (c)
461104208Stjr				error("bad number");
46217987Speter			val *= l->factor;
46311571Sjoerg		}
46417987Speter	}
46517987Speter	if (all) {
466155301Sschweikh		for (l = limits; l->name; l++) {
46718016Speter			char optbuf[40];
46818016Speter			if (getrlimit(l->cmd, &limit) < 0)
469104208Stjr				error("can't get limit: %s", strerror(errno));
47017987Speter			if (how & SOFT)
47117987Speter				val = limit.rlim_cur;
47217987Speter			else if (how & HARD)
47317987Speter				val = limit.rlim_max;
47417987Speter
47518016Speter			if (l->units)
47618016Speter				snprintf(optbuf, sizeof(optbuf),
47718019Speter					"(%s, -%c) ", l->units, l->option);
47818016Speter			else
47918016Speter				snprintf(optbuf, sizeof(optbuf),
48018019Speter					"(-%c) ", l->option);
48118019Speter			out1fmt("%-18s %18s ", l->name, optbuf);
48217987Speter			if (val == RLIM_INFINITY)
48317987Speter				out1fmt("unlimited\n");
48417987Speter			else
48517987Speter			{
48617987Speter				val /= l->factor;
487104282Smux				out1fmt("%jd\n", (intmax_t)val);
48817987Speter			}
48911571Sjoerg		}
49017987Speter		return 0;
49111571Sjoerg	}
49217987Speter
49318016Speter	if (getrlimit(l->cmd, &limit) < 0)
494104208Stjr		error("can't get limit: %s", strerror(errno));
49517987Speter	if (set) {
49617987Speter		if (how & SOFT)
49717987Speter			limit.rlim_cur = val;
49817987Speter		if (how & HARD)
49917987Speter			limit.rlim_max = val;
50017987Speter		if (setrlimit(l->cmd, &limit) < 0)
501104208Stjr			error("bad limit: %s", strerror(errno));
50217987Speter	} else {
50317987Speter		if (how & SOFT)
50417987Speter			val = limit.rlim_cur;
50517987Speter		else if (how & HARD)
50617987Speter			val = limit.rlim_max;
50717987Speter
50817987Speter		if (val == RLIM_INFINITY)
50917987Speter			out1fmt("unlimited\n");
51017987Speter		else
51117987Speter		{
51217987Speter			val /= l->factor;
513104282Smux			out1fmt("%jd\n", (intmax_t)val);
51417987Speter		}
51517987Speter	}
51611571Sjoerg	return 0;
51711571Sjoerg}
518