Subject: v06i049: Add ksh-style 'ulimit' to 4.2BSD /bin/sh (sh.ulimit) Newsgroups: mod.sources Approved: rs@mirror.UUCP 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 #include #include 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