[mod.sources] v06i049: Add ksh-style 'ulimit' to 4.2BSD /bin/sh

sources-request@mirror.UUCP (07/11/86)

Submitted by: seismo!gatech!emory!arnold (Arnold D. Robbins {EUCC})
Mod.sources: Volume 6, Issue 49
Archive-name: sh.ulimit

The 4.2 BSD sh does not have any functional equivalent of the csh's 'limit'
built-in command. Below are some patches that provide the "ulimit" builtin,
basically compatible with that implemented in the ksh, which understands the
various BSD resource limits, as well as those available in System V. (Actually,
it is a little different in the output format; but at the time I wrote the code,
I only had ksh doc to go by, and not the ksh to test against. Oh well. 95% now
is better than 100% in two weeks.)

*** WARNING: This code has only been minimally tested in the BSD sh!!! ***
It is however, very straightforward, and shouldn't give you too many problems.
I pulled it out of the work I've been doing on the System V Release 2 shell.
(That work is still in progress, and maybe once things settle down with 4.3,
I'll be able to get back to it and post something.) It should be adaptable
to System V shells currently running under 4.2, such as the one in Doug
Gwyn's S5 emulation, and/or the Sun 3.0 shell (if you have source). Sun is
welcome to pick this up and add it in, as it is mostly based on Doug's code,
which is public domain.

This posting was motivated by some discussion a while back in net.unix and
net.unix-wizards. I hope it meets a need. (As for those who want job control
for the shell, see the nine part sh posting in volume 1 of mod.sources that I
did last summer. Several parts of that were diffs to the BSD sh, as well as
the S5R2 sh. I am working on that some more too.)

Enjoy,
    Arnold Robbins
    CSNET:	arnold@emory	BITNET:	arnold@emoryu1
    ARPA:	arnold%emory.csnet@csnet-relay.arpa
    UUCP:	{ akgua, decvax, gatech, sb1, sb6, sunatl }!emory!arnold
-------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	sh.patch
#	ulimit.c
# This archive created: Mon Jun 30 16:14:11 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'sh.patch'" '(6246 characters)'
if test -f 'sh.patch'
then
	echo shar: will not over-write existing file "'sh.patch'"
else
cat << \SHAR_EOF > 'sh.patch'
*** /usr/src/bin/sh/Makefile	Fri Jul  1 06:48:58 1983
--- Makefile	Mon Jun 30 15:47:27 1986
***************
*** 19,24
  sh:	xec.o service.o error.o io.o
  sh:	print.o macro.o expand.o
  sh:	ctype.o msg.o
  blok.o:		brkincr.h
  fault.o:	brkincr.h
  main.o:		brkincr.h

--- 19,25 -----
  sh:	xec.o service.o error.o io.o
  sh:	print.o macro.o expand.o
  sh:	ctype.o msg.o
+ sh:	ulimit.o
  blok.o:		brkincr.h
  fault.o:	brkincr.h
  main.o:		brkincr.h
*** /usr/src/bin/sh/defs.h	Sat Jun 11 02:38:06 1983
--- defs.h	Mon Jun 30 15:44:51 1986
***************
*** 54,59
  #define SYSREAD 17
  #define SYSTST	18
  #define	SYSUMASK	19
  
  /* used for input and output of shell */
  #define INIO 10

--- 54,60 -----
  #define SYSREAD 17
  #define SYSTST	18
  #define	SYSUMASK	19
+ #define SYSULIMIT	20
  
  /* used for input and output of shell */
  #define INIO 10
***************
*** 264,269
  MSG		badopt;
  MSG		badparam;
  MSG		badsub;
  MSG		nospace;
  MSG		notfound;
  MSG		badtrap;

--- 265,271 -----
  MSG		badopt;
  MSG		badparam;
  MSG		badsub;
+ MSG		badulimit;
  MSG		nospace;
  MSG		notfound;
  MSG		badtrap;
*** /usr/src/bin/sh/msg.c	Thu Aug 11 23:21:53 1983
--- msg.c	Mon Jun 30 15:45:24 1986
***************
*** 26,31
  MSG	badnum		= "bad number";
  MSG	badparam	= "parameter not set";
  MSG	badsub		= "bad substitution";
  MSG	badcreate	= "cannot create";
  MSG	illegal		= "illegal io";
  MSG	restricted	= "restricted";

--- 26,32 -----
  MSG	badnum		= "bad number";
  MSG	badparam	= "parameter not set";
  MSG	badsub		= "bad substitution";
+ MSG badulimit	= "bad ulimit";
  MSG	badcreate	= "cannot create";
  MSG	illegal		= "illegal io";
  MSG	restricted	= "restricted";
***************
*** 135,139
  		{"exec",	SYSEXEC},
  		{"times",	SYSTIMES},
  		{"umask",	SYSUMASK},
  		{0,	0},
  };

--- 136,141 -----
  		{"exec",	SYSEXEC},
  		{"times",	SYSTIMES},
  		{"umask",	SYSUMASK},
+ 		{"ulimit",	SYSULIMIT},
  		{0,	0},
  };
*** /usr/src/bin/sh/xec.c	Thu Aug 11 23:21:56 1983
--- xec.c	Mon Jun 30 16:07:08 1986
***************
*** 222,227
                                          }
                                          break;
  	
  				default:
  					internal=builtin(argn,com);
  	

--- 222,305 -----
                                          }
                                          break;
  	
+ 					case SYSULIMIT:	
+ 					{	
+ 						long int i;	
+ 						long ulimit();	
+ 						int command = 2;	
+ 		
+ 						if (*a1 == '-')	
+ 						{
+ 							switch(*(a1+1))	{	
+ 							case 'f':	
+ 								command = 2;	
+ 								break;	
+ 		
+ #ifdef rt	
+ 							case 'p':	
+ 								command = 5;	
+ 								break;	
+ 		
+ #endif	
+ 							case 'c':
+ 								command = 7;
+ 								break;
+ 
+ 							case 'd':
+ 								command = 9;
+ 								break;
+ 
+ 							case 'm':
+ 								command = 11;
+ 								break;
+ 
+ 							case 't':
+ 								command = 13;
+ 								break;
+ 
+ 							default:	
+ 								error(badopt);	
+ 							}	
+ 							a1 = com[2];	
+ 						}	
+ 						if (a1)	
+ 						{	
+ 							int c;	
+ 
+ 							i = 0;	
+ 							while ((c = *a1++) >= '0' && c <= '9')	
+ 							{	
+ 								i = (i * 10) + (long)(c - '0');	
+ 								if (i < 0)	
+ 									error(badulimit);	
+ 							}	
+ 							if (c || i < 0)	
+ 									error(badulimit);	
+ 						}	
+ 						else	
+ 						{	
+ 							i = -1;	
+ 							command--;	
+ 						}	
+ 									
+ 						if ((i = ulimit(command,i)) < 0)	
+ 								error(badulimit);	
+ 		
+ 						switch (command) {	
+ 						case 1:
+ #ifdef rt
+ 						case 4:
+ #endif
+ 						case 6:
+ 						case 8:
+ 						case 10:
+ 						case 12:
+ 							prl(i);	
+ 							prc(NL);	
+ 						}	
+ 						break;	
+ 					}				
+ 
  				default:
  					internal=builtin(argn,com);
  	
***************
*** 422,425
  	FI
  	execute(cmd(NL, NLFLG|MTFLG),0);
  	pop();
  }

--- 500,532 -----
  	FI
  	execute(cmd(NL, NLFLG|MTFLG),0);
  	pop();
+ }
+ 
+ /*
+  * standard itos expects 16 bit ints, so write a prl() routine
+  */
+ 
+ static int prl (n)
+ register long n;
+ {
+ 	register int i, j;
+ 	char buf[12], c;
+ 
+ 	buf[0] = '0';
+ 	buf[1] = buf[11] = 0;
+ 	for (i = 0; n && i < 11; i++)
+ 	{
+ 		buf[i] = n % 10 + '0';
+ 		n /= 10;
+ 	}
+ 	buf[i] = 0;
+ 
+ 	for (j = --i, i = 0; i < j ; i++, j--)
+ 	{
+ 		c = buf[j];
+ 		buf[j] = buf[i];
+ 		buf[i] = c;
+ 	}
+ 
+ 	prs(buf);
  }
*** /usr/man/man1/sh.1	Thu Jul 28 17:52:12 1983
--- sh.1	Mon Jun 30 15:44:06 1986
***************
*** 1,6
  .TH SH 1 "7 February 1983"
  .SH NAME
! sh, for, case, if, while, \fB:\fP, \fB.\fP, break, continue, cd, eval, exec, exit, export, login, read, readonly, set, shift, times, trap, umask, wait \- command language
  .SH SYNOPSIS
  .B sh
  [

--- 1,6 -----
  .TH SH 1 "7 February 1983"
  .SH NAME
! sh, for, case, if, while, \fB:\fP, \fB.\fP, break, continue, cd, eval, exec, exit, export, login, read, readonly, set, shift, times, trap, ulimit, umask, wait \- command language
  .SH SYNOPSIS
  .B sh
  [
***************
*** 762,767
  .IR sigvec (2).
  .I Trap
  with no arguments prints a list of commands associated with each signal number.
  .TP
  \fBumask \fR[ \fInnn\fR ]
  The user file creation mask is set to the octal value

--- 762,813 -----
  .IR sigvec (2).
  .I Trap
  with no arguments prints a list of commands associated with each signal number.
+ .TP
+ \fBulimit\fP \*(OK \fB\-cdfmpt\fP \*(CK \*(OK \f2n\^\fP \*(CK
+ imposes a size limit of
+ .IR n\^ .
+ .RS
+ .TP
+ .B \-c
+ imposes a size limit of
+ .I n\^
+ blocks on the size of core dumps.
+ .TP
+ .B \-d
+ imposes a size limit of
+ .I n\^
+ blocks on the size of the data area.
+ .TP
+ .B \-f
+ imposes a size limit of 
+ .I n
+ blocks on files written by child processes (files of any size may be read).
+ .TP
+ .B \-m
+ imposes a soft limit of
+ .I n\^
+ blocks on the size of physical memory.
+ .TP
+ .B \-p
+ changes the pipe size to
+ .I n
+ (\s-1UNIX\s+1/\s-1RT\s+1 only).
+ .TP
+ .B \-t
+ imposes a time limit of
+ .I n\^
+ seconds to be used by each process.
+ .PP
+ If no option is given,
+ .B \-f
+ is assumed.
+ If
+ .I n\^
+ is not given, the current limit is printed.
+ (As far as
+ .B ulimit
+ is concerned, a block is 512 bytes.)
+ .RE
  .TP
  \fBumask \fR[ \fInnn\fR ]
  The user file creation mask is set to the octal value
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'ulimit.c'" '(2911 characters)'
if test -f 'ulimit.c'
then
	echo shar: will not over-write existing file "'ulimit.c'"
else
cat << \SHAR_EOF > 'ulimit.c'
#ifndef lint
static char RCSid[] = "$Header: ulimit.c,v 1.4 85/12/09 13:02:22 arnold Exp $";
#endif

/*
 * $Log:	ulimit.c,v $
 * Revision 1.4  85/12/09  13:02:22  arnold
 * changed the declaration of limit to keep lint happy
 * 
 * Revision 1.3  85/12/03  16:18:42  arnold
 * Fixed original code to use symbolic constants from the header files
 * Added many more commands for use with the ksh style ulimit command,
 * for the BSD limits.
 * 
 * Revision 1.2  85/11/18  10:04:46  arnold
 * Updated from bare S5R2 to my first release
 * 
 * Revision 1.1  85/11/15  12:19:05  arnold
 * Initial revision
 * 
 * 
 */

/*
	ulimit -- system call emulation for Bourne shell on 4.2BSD

	last edit:	22-Aug-1983	D A Gwyn
*/

#include	<sys/time.h>
#include	<sys/resource.h>
#include	<errno.h>

extern int	getrlimit(), setrlimit();
extern int	errno;

#define BLOCK	512L	/* all these things are done in "blocks" */

long
ulimit (cmd, newlimit)
int	cmd;			/* subcommand */
long	newlimit;		/* desired new limit */
{
	struct rlimit limit;	/* data being gotten/set */

	switch (cmd) {
	case 1: 			/* get file size limit */
		if (getrlimit (RLIMIT_FSIZE, &limit) != 0 )
			return -1L;	/* errno is already set */
		return limit.rlim_max / BLOCK;

	case 2: 			/* set file size limit */
		limit.rlim_cur = limit.rlim_max = newlimit * BLOCK;
		return setrlimit (RLIMIT_FSIZE, &limit);

	case 3: 			/* get maximum break value */
		if (getrlimit (RLIMIT_DATA, &limit) != 0)
			return -1L;	/* errno is already set */
		return limit.rlim_max;

	case 6:		/* get core file size limit */
		if (getrlimit (RLIMIT_CORE, &limit) != 0)
			return -1L;	/* errno already set */
		return limit.rlim_max / BLOCK;

	case 7:		/* set core file size limit */
		limit.rlim_cur = limit.rlim_max = newlimit * BLOCK;
		return setrlimit (RLIMIT_CORE, &limit);

	case 8:		/* get data segment size limit */
		if (getrlimit (RLIMIT_DATA, &limit) != 0)
			return -1L;	/* errno already set */
		return limit.rlim_max / BLOCK;

	case 9:		/* set data segment size limit */
		limit.rlim_cur = limit.rlim_max = newlimit * BLOCK;
		return setrlimit (RLIMIT_DATA, &limit);

	case 10:	/* get physical memory limit */
		if (getrlimit (RLIMIT_RSS, &limit) != 0)
			return -1L;	/* errno already set */
		return limit.rlim_max / BLOCK;

	case 11:	/* set physical memory limit */
		limit.rlim_cur = newlimit * BLOCK;
		return setrlimit (RLIMIT_RSS, &limit);

	case 12:	/* get cpu time limit */
		if (getrlimit (RLIMIT_CPU, &limit) != 0)
			return -1L;	/* errno already set */
		return limit.rlim_max / 1000L;	/* system uses milliseconds */

	case 13:	/* set cpu time limit */
		limit.rlim_cur = limit.rlim_max = newlimit * 1000L;
		return setrlimit (RLIMIT_CPU, &limit);

	case 4:
	case 5:
		/* These two are for getting and setting the pipe */
		/* size under UNIX/RT -- not applicable here */
		/* so fall thru and complain */
	default:
		errno = EINVAL;
		return -1L;
	}
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0