rsalz@uunet.uu.net (Rich Salz) (03/28/89)
Submitted-by: Dave Settle <mcvax!ucms!dave> Posting-number: Volume 18, Issue 67 Archive-name: diskhog2 [ I had really mangled this last time I posted it. Here's a good version. Thanks to Mark Washburn for tracking this down and getting it to me. --r$ ] 'diskhog' is a set of scripts to allow you to enforce disk quotas under system V. You need to specify the blocks allowed for each. user (as reported by 'du'), and run a check script every night. Users who are listed in the 'allowed' file have disk quotas; those which are not listed have not. You can also specify 'alternate' directories which are counted as well as the user's home directory. (for example, I have "/usr/dave" and "~nuucp/dave"). All the files, irrespective of owner, are counted in these directories, which is perhaps a little unfair ... Dave Settle, SMB Business Software, Thorn EMI Datasolve [ Now Universal (CMS) Ltd.] #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: Makefile README allowed alternate configure csh_hog.c # dcheck dcheck.1 diskhog diskhog.1 diskhog.h nohog.1 nohog.c # Wrapped by rsalz@papaya.bbn.com on Mon Mar 27 12:53:44 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(860 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# XBIN=/usr/local/bin # directory for binaries XDQUOTAS=/usr/local/lib/disk X# XSRC1 = README Makefile configure allowed dcheck.1 alternate XSRC2 = dcheck diskhog diskhog.h nohog.c csh_hog.c nohog.1 diskhog.1 X Xconfig: .config X X.config: X sh configure X @touch .config X Xinstall: nohog dcheck diskhog allowed ${DQUOTAS}/hogs ${DQUOTAS} X @echo "If your system has csh, maybe you need to 'make csh'" X @echo "Try 'make -n csh' first." X chown root nohog X chmod 4111 nohog X cp nohog ${BIN} X chmod +x dcheck diskhog X cp dcheck diskhog ${BIN} X cp allowed ${DQUOTAS} X Xcsh: csh_hog X if [ ! -f /etc/csh ] ; then mv /bin/csh /etc/csh ; fi X cp csh_hog /bin/csh X X${DQUOTAS}/hogs: X mkdir ${DQUOTAS}/hogs X X${DQUOTAS}: X mkdir ${DQUOTAS} X Xshar: ${SRC} X shar -cv -p X ${SRC1} > diskhog.shar.1 X shar -cv -p X ${SRC2} > diskhog.shar.2 X XREADME: README.nr X nroff -cm README.nr > README END_OF_FILE if test 860 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(4958 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' X[ NOTE: You might have to edit "diskhog" to turn the printable control X characters in the "stty" line into their real values. --r$ ] X XDISKHOG. X X1. Introduction. X X 'diskhog' is a set of scripts to allow you to enforce disk quotas X under system V. You need to specify the blocks allowed for each. X user (as reported by 'du'), and run a check script every night. Users X who are listed in the 'allowed' file have disk quotas; those which are X not listed have not. X X You can also specify 'alternate' directories which are counted X as well as the user's home directory. (for example, I have X "/usr/dave" and "~nuucp/dave"). All the files, irrespective of X owner, are counted in these directories, which is perhaps a little X unfair ... X X X2. How it works. X X Basically, there is a file called "allowed", which has X a list of the number of disk blocks (as per "du") which each X user is allowed. The format of this file is X X <user> <allowance> X X where <user> is the user's login name, and <allowance> is X the maximum number of block they are allowed (as per "du"). X X There is also an optional file called "alternate", X containing additional directories to be searched for X particular users. This is in the form: X X user directory .... X e.g. X dave /usr/spool/uucppublic/dave /sys/dave X X Each night, the script "dcheck" is run from the root X crontab entry, and checks each user's allowance. If the user X has more than the allowance, a mail message is sent to the X user asking them to remove some files. X X If after a certain number of days the user has not X removed enough files, then a diskhog "tag" is created. The X next time the user logs on, the shell notices this tag, and X spawns a restricted shell via the script "diskhog". X X When running via "diskhog", the user's PATH is set to X "/diskhog:/usr/diskhog", so that you can restrict the X commands which are available. On my system, all the commands X in these directories relate to removing files, and X formatting floppy disks to put them on. X X When the user logs out of the restricted shell, another X disk check is performed, and if enough blocks have been X removed, the login is allowed to proceed. If not, "diskhog" X is run again. "Diskhog" is interrupt-proof, but can be X killed by a SIGHUP signal (i.e. turning off the terminal). X X In order to prevent the user from removing the diskhog X "tag" file, the tag file is placed in a directory owned by X root, which is not writeable by anyone else. The tag file is X removed by a special command "nohog", which is executed by X "diskhog" when enough files have been removed. "Nohog" runs X suid root, and is (hopefully) immune from fraud: it always X removes the tag of the login user. X X Obviously, you should not put "nohog" in the restricted X PATH, or the user would be able to remove their own diskhog X tag! X X All you need to run disk quotas is: X X 1. An "allowed" file, containing disk allowances. X X 2. If your system has "csh", then you need to type "make X csh", since csh does not have a system-wide init file. X A small program is provided, which makes the relevant X check, and then calls the real csh. X X 3. Add a line to the beginning of the file "/etc/profile" X which reads: X X if [ -f $DQUOTAS/hogs/$LOGNAME ] ; then diskhog ; fi X X where DQUTOAS is defined as your disk quota admin X directory (see Makefile). X X 4. Make yourself an entry in the root crontab, which runs X the program "dcheck" sometime during the night. X X This will read something like: X X 03 01 * * * /usr/bin/dcheck X X to run at 01:30 am every night. X X X 3. Using csh with diskhog X X Since my "csh" doesn't have a system-wide X initialisation file, it's difficult to intercept the logins X of people using the c-shell. X X I decided to intercept /bin/csh itself, so I moved the X real csh to /etc/csh, and wrote a stub program which just X checks for a tag file, and calls diskhog if it finds it. X Then it calls the real csh. X X This works fine on my system, but make sure it's not X going to interfere with things on your system before you X install it. X X Have fun. X X X Dave Settle, SMB Business Software, Thorn EMI Datasolve X [ Now Universal (CMS) Ltd.] END_OF_FILE if test 4958 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'allowed' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'allowed'\" else echo shar: Extracting \"'allowed'\" \(67 characters\) sed "s/^X//" >'allowed' <<'END_OF_FILE' Xdave 100000 # Superman - God lives! Xallan 500 # Dimbo Xjane 10000 END_OF_FILE if test 67 -ne `wc -c <'allowed'`; then echo shar: \"'allowed'\" unpacked with wrong size! fi # end of 'allowed' fi if test -f 'alternate' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'alternate'\" else echo shar: Extracting \"'alternate'\" \(40 characters\) sed "s/^X//" >'alternate' <<'END_OF_FILE' Xdave /usr/spool/uucppublic/dave /tmp/ds END_OF_FILE if test 40 -ne `wc -c <'alternate'`; then echo shar: \"'alternate'\" unpacked with wrong size! fi # end of 'alternate' fi if test -f 'configure' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'configure'\" else echo shar: Extracting \"'configure'\" \(1488 characters\) sed "s/^X//" >'configure' <<'END_OF_FILE' X X# X# shell script to install diskhog X# XDQUOTAS="/usr/lib/disk" XBIN="/usr/bin" X Xecho "Hi there, just a few questions about your system ..." Xcat << EOF XI need a place to hold all the information about disk usage and allowances. X XI would suggest using the directory $DQUOTAS. X XEOF Xecho "If you want a different one, enter it here: \c" Xread ANS Xif [ "$ANS" != "" ] Xthen X DQUOTAS=$ANS X echo "OK, using $DQUOTAS" Xfi Xif [ -f $DQUOTAS ] Xthen X echo "Oh dear, that seems to be a file" X echo "That won't do at all ..." X exit 0 Xelse X if [ -d $DQUOTAS ] X then X echo "That directory already exists - I hope it's empty\n" X else X echo "Making a new directory" X mkdir $DQUOTAS $DQUOTAS/hogs X chmod 755 $DQUOTAS/hogs X chown root $DQUOTAS/hogs X fi Xfi Xcat << EOF XWhere do you intend to install the programs? X XI would suggest $BIN, but you might use another directory. X XEOF Xecho "If you want a different directory, enter it here: \c" Xread ANS Xif [ "$ANS" != "" ] Xthen X BIN=$ANS X echo "OK - using $BIN" Xfi Xecho "OK - reconfiguring the system ... \c" Xcat << EOF > diskhog.h X#define DQUOTAS "$DQUOTAS" X#define BIN "$BIN" XEOF Xed - dcheck << EOF > /dev/null X/^DQUOTAS=/c XDQUOTAS=$DQUOTAS # directory containing all the info X. Xw Xq XEOF Xed - diskhog << EOF > /dev/null X/^DQUOTAS=/c XDQUOTAS=$DQUOTAS # directory containing all the info X. Xw Xq XEOF Xed Makefile << EOF > /dev/null X/^BIN=/c XBIN=$BIN # directory for binaries X. X/^DQUOTAS=/c XDQUOTAS=$DQUOTAS X. Xw Xq XEOF Xecho "OK - your system has been configured" END_OF_FILE if test 1488 -ne `wc -c <'configure'`; then echo shar: \"'configure'\" unpacked with wrong size! fi chmod +x 'configure' # end of 'configure' fi if test -f 'csh_hog.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'csh_hog.c'\" else echo shar: Extracting \"'csh_hog.c'\" \(419 characters\) sed "s/^X//" >'csh_hog.c' <<'END_OF_FILE' X/* X * csh_hog.c: intercept "csh" and check for diskhog tags first X */ X X#include "diskhog.h" X#include <sys/types.h> X#include <sys/stat.h> Xchar *getenv(); X Xmain(argc, argv) Xchar **argv; X{ X char *who = getenv(LOGNAME), cmd[64], fname[64]; X struct stat s; X argv[argc] = 0; X sprintf(fname, "%s/hogs/%s", DQUOTAS, who); X sprintf(cmd, "%s/diskhog", BIN); X if(stat(fname, &s) != -1) system(cmd); X execv("/etc/csh", argv); X} X END_OF_FILE if test 419 -ne `wc -c <'csh_hog.c'`; then echo shar: \"'csh_hog.c'\" unpacked with wrong size! fi # end of 'csh_hog.c' fi if test -f 'dcheck' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dcheck'\" else echo shar: Extracting \"'dcheck'\" \(3290 characters\) sed "s/^X//" >'dcheck' <<'END_OF_FILE' X X# X# check all users disk allocations, and mail disk hogs X# X# change IFS so that only <NL> is a separator - needed to pick up lines from X# /etc/passwd X# XIFS=' X' XTEMP=/tmp/dcheck # temp file for mail etc. XDQUOTAS=/usr/local/lib/disk # directory containing all the info XUSAGE=$DQUOTAS/usage # usages recorded here each night XMALIASES=/usr/lib/mailx/mailx.rc # system mail aliases. XVIPS=100 # logins with uids less than this are not checked XLOG=$DQUOTAS/log # log of warnings etc. XBOOK=$DQUOTAS/allowed # where the index of allowances is kept XALTERNATE=$DQUOTAS/alternate # list of other directories to add in XADMIN=root # user informed of diskhogs XMAX=5 # no of warnings issued before login restricted X# Xrm -f $USAGE XMC=`uname` Xfor u in `cat /etc/passwd` Xdo X# X# scan /etc/passwd file for users. X# get USER (name) UID, and HDIR (home directory). X# X USER=`echo $u | awk 'BEGIN{FS=":"} {print $1}'` X UID=`echo $u | awk 'BEGIN{FS=":"} {print $3}'` X HDIR=`echo $u | awk 'BEGIN{FS=":"} {print $6}'` X if [ -f "$ALTERNATE" ] X then X OTHERS=`grep "$USER" "$ALTERNATE" | sed 's/[a-zA-Z]* //'` X else X OTHERS="" X fi X# X# check MALIASES for "alias $USER real_user" X# X MALIAS=`grep "alias $USER " /usr/lib/mailx/mailx.rc | awk '{print $3}'` X if [ "$MALIAS" = "" ] X then X MALIAS=$USER X fi X# X# ignore users with uids less than $VIPS X# X if [ $UID -lt "$VIPS" ] X then X continue X fi X ALLOWED=`grep "^$USER[ ]" $BOOK | awk '{print $2}'` X if [ "$ALLOWED" = "" ] X then X continue # no definition of disk usage X fi X DISK=`du -s $HDIR $OTHERS | awk '{total += $1} END{print total}'` X# X# keep record of current disk use X# X echo "$USER has $DISK, allowed $ALLOWED" >> $USAGE X# X# send warning if disk usage is over 90% of allowed X# X THRESHOLD=`expr "$ALLOWED" - \( "$ALLOWED" / 10 \)` X if [ "$DISK" -gt "$THRESHOLD" ] && [ "$DISK" -lt "$ALLOWED" ] X then X /bin/mail $MALIAS << EOF XSubject: disk usage warning X XYour disk usage ($ALLOWED) is nearly used up XYou have $DISK blocks XEOF X fi X if [ "$DISK" -gt "$ALLOWED" ] X then X TIME=`date` X# X# if no count file present, then create one. X# X if [ ! -f $DQUOTAS/$USER ] X then X cat "1" > $DQUOTAS/$USER X fi X COUNT=`cat $DQUOTAS/$USER` X EXCESS=`expr "$DISK" - "$ALLOWED"` X /bin/mail $MALIAS << EOF XSubject: Disk usage X XYour disk usage on $MC is $DISK blocks. XYou are allowed only $ALLOWED blocks -- please remove $EXCESS blocks. XEOF X echo "$USER allowed $ALLOWED has $DISK - warned $TIME" >> $LOG X COUNT=`expr "$COUNT" + 1` X echo $COUNT > $DQUOTAS/$USER X if [ "$COUNT" -gt "$MAX" ] X then X# X# warned too many times - X# mail supervisor, and give restricted logins until files removed. X# X /bin/mail $ADMIN << EOF XSubject: Disk hog $USER X X$USER has ignored all my warnings about disk quotas. I have therefore Xrestricted ${USER}'s login. X$USER has $EXCESS too many blocks on $MC, with a quota of $ALLOWED blocks. XEOF X echo "Disk hog $USER restricted on $TIME" >> $LOG X# X# create tag file, to be found by the login shell X# X touch $DQUOTAS/hogs/$USER X# X# make sure the files can't be accessed by anyone else - peter logs in as X# other people! You can remove the check for peter, if you want it to work X# for everyone. X# X# if [ "$USER" = "peter" ] X# then X chmod go-rx $HDIR X# fi X fi X else X echo "1" > $DQUOTAS/$USER X fi Xdone X X END_OF_FILE if test 3290 -ne `wc -c <'dcheck'`; then echo shar: \"'dcheck'\" unpacked with wrong size! fi chmod +x 'dcheck' # end of 'dcheck' fi if test -f 'dcheck.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dcheck.1'\" else echo shar: Extracting \"'dcheck.1'\" \(460 characters\) sed "s/^X//" >'dcheck.1' <<'END_OF_FILE' X.TH DCHECK 1M X.SH SYNOPSIS Xdcheck - send mail to potential disk hogs X.SH DESCRIPTION X.P X\fBdcheck\fR is normally run from the \fBcrons\fR procedure to check Xif users are taking up too much space. XIt creates a \fBtag\fP file for such users, will send them warning Xmail, and also sends mail to the system administrator about what it's Xdone. X.SH SEE ALSO X.P Xdiskhog(1). X.SH FILES X.P X$DQUOTAS/hogs/$LOGNAME - tag file indicating that \fBdiskhog\fR Xshould be run END_OF_FILE if test 460 -ne `wc -c <'dcheck.1'`; then echo shar: \"'dcheck.1'\" unpacked with wrong size! fi # end of 'dcheck.1' fi if test -f 'diskhog' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'diskhog'\" else echo shar: Extracting \"'diskhog'\" \(1472 characters\) sed "s/^X//" >'diskhog' <<'END_OF_FILE' X# X# script that naughty people get when they don't remove files X# Xtrap '' 2 3 Xstty erase ^H kill ^U echoe intr ^? quit ^\ XSHELL=/bin/sh Xexport SHELL PATH X# X# re-check the disk allocation X# XDQUOTAS=/usr/local/lib/disk # directory containing all the info XHOG=1 Xecho "\nOvernight check shows you have too much disk space" Xwhile [ "$HOG" = "1" ] Xdo X PATH=/bin:/usr/bin:/usr/ucb X echo "Re-checking your disk usage .... \c" X ALLOWED=`grep $LOGNAME $DQUOTAS/allowed | awk '{print $2}'` X DISK=`du -s . | awk '{print $1}'` X echo "done" X if [ "$DISK" -gt "$ALLOWED" ] X then X echo "You have $DISK blocks, but are allowed only $ALLOWED" X echo "You must remove some files before you can logon" X echo "You now have a restricted shell with the following commands" X ls -C /diskhog /usr/diskhog X echo "Remove some files and then type CONTROL-D" X X# X# if we allow interrupts in this shell, we get zapped on return X# so - fork a shell, and allow interrupts there X# we have to give a command to trap, so that spawned commands get SIG_DFL, X# or we can't reset them when we get there! X# You may have to say "trap 3" to unlock the QUIT signal. X# X trap 'echo "** interrupt ignored **"' 2 3 X /bin/sh << EOF XPATH=/diskhog:/usr/diskhog XSHELL=/bin/rsh Xexport PATH SHELL Xexec /bin/rsh < /dev/tty # signals get set to SIG_DFL XEOF X trap '' 2 3 # ignore SIGINT SIGQUIT X else X echo "I see that you have removed your excess disk blocks - thankyou" X nohog X HOG=0 X chmod og+rx $HOME X fi Xdone END_OF_FILE echo shar: 1 control character may be missing from \"'diskhog'\" if test 1472 -ne `wc -c <'diskhog'`; then echo shar: \"'diskhog'\" unpacked with wrong size! fi chmod +x 'diskhog' # end of 'diskhog' fi if test -f 'diskhog.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'diskhog.1'\" else echo shar: Extracting \"'diskhog.1'\" \(922 characters\) sed "s/^X//" >'diskhog.1' <<'END_OF_FILE' X.TH DISKHOG 1 X.SH SYNOPSIS Xdiskhog - run a restricted shell to persuade a user to remove some files. X.SH DESCRIPTION X.P X\fBdiskhog\fR is a shell script initiated for a user who has failed to Xremove excess disk blocks. X.P XIt rechecks the current disk usage, and if this is still greater than the Xmaximum allowed, runs a restricted shell (\fB/bin/rsh\fR), with a \fBPATH\fR Xset to \fB/diskhog:/usr/diskhog\fR. X.P XWhen this shell terminates, a further check is made of the disk usage, and Xif it is now acceptable, the normal login process will continue. Otherwise Xthe process will be repeated. X.P XThe \fBdiskhog\fR procedure is interrupt-proof, and can only be exited by Xeither removing sufficient blocks, or on receipt of a SIGHUP signal X(e.g. turning the terminal off). X.SH SEE ALSO X.P Xdcheck(1M), du(1), sh(1), csh(1), nohog(1) X.SH FILES X.P X$DQUOTAS/hogs/$LOGNAME - tag file indicating that \fBdiskhog\fR Xshould be run END_OF_FILE if test 922 -ne `wc -c <'diskhog.1'`; then echo shar: \"'diskhog.1'\" unpacked with wrong size! fi # end of 'diskhog.1' fi if test -f 'diskhog.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'diskhog.h'\" else echo shar: Extracting \"'diskhog.h'\" \(67 characters\) sed "s/^X//" >'diskhog.h' <<'END_OF_FILE' X#define DQUOTAS "/usr/local/lib/disk" X#define BIN "/usr/local/bin" END_OF_FILE if test 67 -ne `wc -c <'diskhog.h'`; then echo shar: \"'diskhog.h'\" unpacked with wrong size! fi # end of 'diskhog.h' fi if test -f 'nohog.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'nohog.1'\" else echo shar: Extracting \"'nohog.1'\" \(429 characters\) sed "s/^X//" >'nohog.1' <<'END_OF_FILE' X.TH NOHOG 1M X.SH SYNOPSIS Xnohog - remove the diskhog tag file for the invoking user. X.SH DESCRIPTION X.P X\fBnohog\fR is normally run from the \fBdiskhog\fR procedure, when the user Xhas removed sufficient disk blocks. X.P XIt removes the \fBtag\fR file created by \fBdcheck\fR. X.SH SEE ALSO X.P Xdcheck(1M), diskhog(1), du(1), sh(1), csh(1), X.SH FILES X.P X$DQUOTAS/hogs/$LOGNAME - tag file indicating that \fBdiskhog\fR Xshould be run END_OF_FILE if test 429 -ne `wc -c <'nohog.1'`; then echo shar: \"'nohog.1'\" unpacked with wrong size! fi # end of 'nohog.1' fi if test -f 'nohog.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'nohog.c'\" else echo shar: Extracting \"'nohog.c'\" \(860 characters\) sed "s/^X//" >'nohog.c' <<'END_OF_FILE' X/* X * nohog.c: remove diskhog tag from /etc/hogs directory X * X * find out who the user is, and then remove the tag from the hogs directory. X * X * It's supposed to be proof against fraud, though I wouldn't care to put X * any money on it. X * X * runs suid root, so that the user can't just 'rm' the file. X * X * This program should NOT be accessible to diskhogs running the "diskhog" X * shell - i.e. don't link it to /diskhog or /usr/diskhog X */ X#include <sys/types.h> X#include <utmp.h> X#include <stdio.h> X X#include "diskhog.h" Xchar *ttyname(); Xstruct utmp *getutline(); X Xmain(){ X char *p; X struct utmp *utmp, mine; X int prefix = sizeof "/dev/"; X chdir(DQUOTAS); X chdir("hogs"); X p = ttyname(fileno(stderr)); /* find tty device */ X strcpy(mine.ut_line, p + 5); X if(utmp = getutline(&mine)) X unlink(utmp->ut_user); X else printf("who are you?\n"); X return(0); X} END_OF_FILE if test 860 -ne `wc -c <'nohog.c'`; then echo shar: \"'nohog.c'\" unpacked with wrong size! fi # end of 'nohog.c' fi echo shar: End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.