diff options
author | Rene Mayorga <rmayorga@debian.org> | 2007-07-30 17:30:49 +0000 |
---|---|---|
committer | Rene Mayorga <rmayorga@debian.org> | 2007-07-30 17:30:49 +0000 |
commit | 949544810672c3a955341d390fd7b737d0a92189 (patch) | |
tree | 214ef4c0b2b3620b892c6df464d5e8254c6ebba1 |
[svn-inject] Installing original source of libquota-perl
37 files changed, 4333 insertions, 0 deletions
@@ -0,0 +1,220 @@ +Changes in 1.5.1 (July 2005) +- Added new API function rpcauth: use non-default authentication for RPC + Patch submitted by Karol Lassak +- Makefile fix for Linux: compile linuxapi.c with default CFLAGS + because for 64-bit systems -fPIC is required. Patches submitted + by David Lee and James Olin Oden; used a different solution though. + +Changes in 1.5.0 (Nov 2004) +- Added new API function rpcpeer: set port and protocol for RPC queries. + On request by Sten Spans (sten@blinkenlights.nl) + +Changes in 1.4.11 (Aug 2004) +- Updated VxFS detection in Makefile.PL for recent Solaris/VxFS versions + Thanks to Joshua Frigerio for the info and to Keith Clay for verification. +- tiny syntax cleanup in linuxapi.c + +Changes in 1.4.10 (Jan 2004) +- Applied patch by Antti Tapaninen (aet at cc.hut.fi) to add support + for MacOS X (based on BSD config) and NFS3 support for AIX. +- Applied patch by Chris Adams (cmadams at hiwaay.net) to avoid warning + in Quota::getqcarg() on systems where getmntent(3) may return invalid + mount points, e.g. Tru64 UNIX + +Changes in 1.4.9 (Aug 2003) +- Applied patch by Wolfgang.Friebel@desy.de: Changed AFS quota support, + now based on OpenAFS instead of arla and Kerberos4. + +Changes in 1.4.8 (May 2003) +- fixed bug in linuxapi.c, thanks to Shane DeRidder + +Changes in 1.4.7 (May 2003) +- adapted for the latest fashion of Linux quota APIs (version 6.5.1) + Thanks to Jay R. Wren, Shane DeRidder, Roman "romke" and Geoffrey Pan + for your infos and feedback. +- added quota threshold monitoring script to the contrib directory. + Thanks to Ziaur Rahman. + +Changes in 1.4.6 (August 2002) +- changed the test script to exit if STDIN is not a terminal device. + This is an interactive script which cannot be run by CPANPLUS. + +Changes in 1.4.5 (July 2002) +- replaced use of macro BCOPY with libc function memcpy() because of build + problems on Solaris 2.8 (bcopy is depreciated now anyways) + Thanks to Jost Krieger and Paul Sand for reporting this problem. + +Changes in 1.4.4 (June 2002) +- bugfix in Quota::getqcarg(): the function failed if the device id returned + by stat(2) for the queried path was zero (e.g. for the root fs on NetBSD) + Thanks to Jake Fan (jake@chaogic.com) for reporting this bug. + +Changes in 1.4.3 (May 2002) +- updated for new version of Solaris VxFS (Veritas Filesystem) + by David Lee (T.D.Lee@durham.ac.uk) + +Changes in 1.4.2 (Jan. - Mar. 2002) +- fixed test.pl to allow uids with more than 5 digits. + Thanks to Neil Prockter (perl@iamafreeman.com) for the fix. +- updated Linux quota version detection with quota-tools 3.04; + removed compile-time version detection because it caused problems; + added switch LINUX_API_VERSION to hints/linux.h to allow to hard-wire + the API in case the automatic version detection fails. + +Changes in 1.4.1 (Sep. 2001 - Jan. 2002) +- added support for an older (intermediate?) version of the new Linux Quota + API which contains a mixture of v1 and v2 command ids (uses the old GETSTAT + id, however with the new and larger struct as argument). Required for + RedHat-7.1. Thanks to Andy Choi (andy@ensim.com) for pointing this out. +- enabled RPC in hints/bsd.h + Confirmed to work on FreeBSD by Alex Batko (abatko@cs.mcgill.ca) +- fixed several glitches in the manual page. + +Changes in 1.4 (August 2001) +- added support for the Alan Cox (ac) branch of the Linux kernel which uses a + new and completely backwards incompatible Quota API. The API version is + determined dynamically by use of Q_GETSTATS/v2, i.e. the same module + binary will work on kernels with either API. Since the Linux quota API now + needs some very special handling, I moved it into a separate file called + linuxapi.c. NOTE: internally the module still uses the old (v1) dqblk + structure, so any advantages the new struct mem_dqblk might have are not + present here. Let me know if this is a problem for you. +- commented out #define LINUX_RQUOTAD_BUG in hints/linux.h as this should + no longer be needed on most systems. Updated INSTALL and README accordingly. +- removed the Linux syscall wrapper in quotactl.c as this is now in libc. +- changed copyright from "none/public domain" to Artistic License (not to + restrict usage, but simply to include the legal disclaimers) +- fixed bug in Quota::query() and setqlim(): when argument isgrp was present + but 0, the functions still did work on group quotas instead of user quotas. + Thanks to Szymon Juraszczyk (szymon@ssk.pl) for pointing this out. + +Changes in 1.3.4 (May 2001) +- added support for SGI XFS on Linux. Thanks to Brian Johnson + (brian@dev.brianj.com) for providing a development account. + +Changes in 1.3.3 (May 2001) +- bugfix Quota::query, branch NO_RPC: forgot to set error flag, arbitrary + results were returned; Pointed out by Mahlon Smith <reich@internetcds.com> +- fixed declaration of GQR_* macros for RPC in hints/bsd.h + RPC still untested for BSD though +- fixed OpenBSD2.7 fix from last release: replaced macro OpenBSD2_7 with + __OpenBSD__ because the former is not defined in 2.8 anymore. + Reported by Lou Hevly (lou@visca.com) +- fixed hints/linux.h for RedHat 7.1: use sys/quota.h instead of linux/quota.h + because the former has an incompatible definition of struct dqblk. + [NOTE: this change proved to be wrong and was undone in 1.4] + Reported by Waldemar Krotkiewicz (wk@brenet.de), Andy Choi (andy@ensim.com) + and Brian Johnson (brian@dev.brianj.com). + +Changes in 1.3.2 (February 2001) +- please note my new email address: tomzo AT nefkom DOT net +- fixed AFS detection in Makefile.PL for Perl 5.6 + thanks to Wolfgang Friebel <friebel@ifh.de> +- adapted getmntent for incompatible change of struct statfs in OpenBSD 2.7 + thanks to andrew@ugh.net.au +- adapted getqcarg for for OpenBSD and BSDi: define QCARG_MNTPT for all BSD os + as reported by andrew@ugh.net.au and Chee-Wai Yeung <cheewai@cs.ust.hk> +- fixed block to kB conversion in Quota::query() for VxFS + as reported by Rui Monteiro <rmonteiro@whatevernet.pt> +- renamed config.h symlink to myconfig.h to avoid conflict with Perl config + +Changes in 1.3.1 (October 2000) +- added support for NetBSD, merged hints for BSD-based systems + by Jaromir Dolecek <jdolecek@NetBSD.org> +- fixed include file name for vxquotactl.h + changed quota block factors Q_DIV/Q_MUL from 1 to 2 for Veritas fs + Thanks to David Lee (T.D.Lee@durham.ac.uk) +- added automatic recognition of HPUX B.11 in Makefile.PL + by Albert Chin (china@thewrittenword.com) + +Changes in 1.3: (January 2000) +- bugfix/enhanced support for OSF/1 and Digital Unix: + getqcarg used wrong path to quotas file and + NFS file systems were not recognized when in "/path@host" format in mtab + provided by Victor Mora (Victor.Mora@ac.upc.es) and + Alessandro Miotto (Alessandro.Miotto@cern.ch) +- added support for FreeBSD. + provided by Kurt Jaeger (pi@complx.LF.net) + and Jon Schewe (schewe@tcfreenet.org) +- added support for Veritas file system (VxFS) on Solaris + provided by David Lee (T.D.Lee@durham.ac.uk), + Michael Gerdts (gerdts@cae.wisc.edu) and + Daniel Hagerty (hag@shore.net). + Beta-tested by John Randell Smith (jrsmith@eng.utah.edu). +- added workaround for yet another bug in Linux rpc.rquotad: + rquotad reports grace times as absolute values instead of relative + bug found by Seth Vidal (skvidal@phy.duke.edu) +- fixed grace time output for Y2K in test.pl +- fixed bug in group-quota patch as pointed out by asun@cobaltnet.com + incorporated the patch into the distribution. +- fixed possible integer overflow in RPC quota block size conversions for + very large quota limits; pointed out by Peter.Pickford (ppickfor@jaguar.com) +- added warning to Makefile.PL if config.h symlink already exists + +Changes in 1.2.3: (April 1999) +- added patch-file "group-quota.patch" that provides optional + support for group-quotas (on some OS versions and localhost only) +- added auto-detection for AFS by Wolfgang Friebel +- fixed include path for AFS in Quota.xs + +Changes in 1.2.2: (December 1998) +- fixed 2 problems in getqcarg() + thanks to py@ecst.csuchico.edu for pointing those out. + +Changes in 1.2: (November 1998) +- added support for AIX 4.1 (thanks to Wolfgang Friebel (friebel@ifh.de) + for providing a development account) +- added support for AFS (Andrew File System) by arla-0.13 + on the following platforms: AIX, SunOS, Solaris, HP-UX, IRIX 6, Linux + with much help from Wolfgang Friebel. + +Changes in 1.1.2: (August 1998) +- changed names of tar archive and module directory +- fixed message for getqcarg failure in test.pl +- compatibility with sfio (moved fopen and fclose to seperate file) + suggested by Christoph Lupe (lupe@alanya.m.isar.de) - yet UNTESTED +- fixed problems with Solaris automounter (ignore lofs mounts in getqcarg) + +Changes in 1.1: (August 1998) +- added support for Linux (thanks to Steve Nolan (nolansj@bookmark.com) + for providing a development account) +- added OpenBSD 2.2 support, provided by James Shelburne (reilly@eramp.net) +- added syntax fixes (2 additional #includes) for IRIX 6.4 + provided by Subhendu Ghosh (sghosh@menger.eecs.stevens-tech.edu) +- support for IRIX xfs filesystems (additional to the older efs fs) + provided by David Lloyd (cclloyd@monotreme.cc.missouri.edu) + + +Changes in 1.0: +(That's the version that's been released with O'Reillys Perl Resource Books) +- improved documentation, now provided in pod format inside Quota.pm +- finally fixed errno.h problem in Quota.pm + Thanks to Tobias Oetiker (oetiker@ee.ethz.ch) +- added BSDI port by Jim Hribnak (hribnak@nucleus.com) + unfortunately without RPC support. +- small fixes for OSF/1 +- more hints in hints/none.h +- I've again received requests for Linux ports. However since I don't + have access to an installation with quota support, I can't do this + myself. I've included a config file that compiles without warnings + on our system ("Red Hat for Sparc" or something), but I can't run it. + If anyone gets it to work with or without RPC, please contact me. + +Changes in 0.3a: +- started port for Linux 2.0; still needs some work. + Compiles correctly, but RPC call fails. +- workarounds for HP-UX/10 bug in test script + (SYNC without arguments doesn't work) +- some cleanup in Query.pm + fixes 0.2a's problems with the autoloader under perl-5.003 + +Changes in 0.2a: +- need generic getqcarg instead of getdev, because: +- added support for Solaris, OSF/1 (which operate without access to + the according block device) Required extensive code changes. +- getqcarg recognizes if path points into a NFS file system +- query() may take NFS path as argument (in form host:/path) + + Thanks to David Lee <T.D.Lee@durham.ac.uk> for alpha-testing on Solaris + and suggesting the two latter improvements. + @@ -0,0 +1,101 @@ +Installation Instructions +------------------------- + +Usually all you have to do is: + + perl Makefile.PL + make + make test + make install + make clean + +If this fails, regard the following instructions step by step. +Note that this module does not support installation by the CPAN(++) module. + +Options: + +-> Support for Solaris VxFS should be added automatically if the module + is installed. If not, #define or #undef SOLARIS_VXFS in myconfig.h + +-> If you want support for AFS, you need the OpenAFS package + (tested with OpenAFS on (SuSE) Linux, HP-UX10, Solaris 2.6, 2.8, AIX 4.3 + and IRIX 6.5). + The Makefile.PL tries to detect automatically if AFS is preset on a + system. If that fails, edit the Makefile.PL to always define $hasafs + (and drop me a mail). + + +1) On Linux systems make sure you have configured your kernel with quota + support. You also need the quota utilities and headers. E.g., with + the SuSE Linux 6.0 distribution you'll find them in package ap::quota. + If your distribution doesn't include the package you can get it from + <URL:http://sourceforge.net/projects/linuxquota>. See also (6) below. + +2) Link or create the hints file. + + a) Should be done by Makefile.PL for all supported systems. If not, and + there is a file below hints/ for your operating system (maybe for a + previous release), just create a link named "myconfig.h" to that file: + ln -s hints/my-os.h myconfig.h + + b) Else, use hints/none.h to create a myconfig.h of your own. Check which + #includes you need in the online manuals. Contact me before you invest + a lot of time, it might already be done. + + Edit Makefile.PL to maybe add/delete some libraries. Usually you'll + need only librpcsvc for the quota RPC mechanism. If you don't have + this library or it lacks the quota routines, #define MY_XDR in + myconfig.h to include the routines provided with this module. If you + don't have /usr/include/rpcsvc/rquota.h, include "include/rquota.h" + instead. If you don't need RPC at all, just define NO_RPC. + +3) Generate the Makefile: perl Makefile.PL + +4) Compile the module: make + +5) Run "make test" to check if the module routines do work correctly. + (Since release 1.0 you can test the module without installing) + +6) Linux specials: + + a) If you're using an ancient version of the Linux quota tools on a NFS + server, check Quota::query() results for block usage and limits. + Old versions of the rpc.rquotad did report a wrong block size, hence + all sizes were 4 times too large. If your system shows this error, + you should uncomment #define LINUX_RQUOTAD_BUG in the hints file and + recompile. The better solution is to update your quota tools though. + + b) If you're using RedHat 7.1 or later, or the Alan Cox (ac) branch of + the Linux 2.4.x kernels, you're victim of a new and completely + backwards incompatible Quota file format and quotactl API. If you've + downloaded the kernel separately, make sure to install the latest + version of the quota tools: see (1). + + If the Quota module does not find quotas for any users anymore, the + reason may be you've not set up the new aquota.user/group files yet. + Remember: after creating these files with quotackeck(8) or + convertquota(8) you have to execute quotaon(8) another time. You'll + not see this requirement with quota(1) because that program reads the + quota files directly if the kernel query fails; the module however only + goes through the kernel. If in doubt, do an strace(1) on the quota(1) + call. Normally you should see no access to the aquota files in the trace. + Alternatively, make the quota files readable for root only (recommended + anyway) and try quota -v again under a non-root login. If this fails, + the kernel has not enabled quota support. + +7) To install the module in your perl tree: "make install" + You may need to su root before this step. + + The contrib directory contains some usage examples, donated by + users. No guarantee from me that they do anything useful or don't + format your hard drive. + +8) Before you start for another OS, type "make clean" + +Please mail me any changes in the hints files or Makefile.PL you had to +apply to get the package to compile. Please remember to include in your +mail the name of used OS and version numbers of OS (uname -rs) and module. + +Tom +--- +email: tomzo AT nefkom DOT net diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..4d318b6 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,33 @@ +CHANGES +INSTALL +MANIFEST +Makefile.PL +Quota.pm +Quota.xs +linuxapi.c +afsquota.c +vxquotactl.c +stdio_wrap.c +README +test.pl +hints/aix_4_1.h +hints/bsd.h +hints/dec_osf.h +hints/hpux.h +hints/irix_5.h +hints/irix_6.h +hints/linux.h +hints/none.h +hints/solaris_2.h +hints/sunos_4_1.h +include/rquota.h +include/afsquota.h +include/vxquotactl.h +include/stdio_wrap.h +include/quotaio_xfs.h +contrib/README +contrib/test-group.pl +contrib/report-quota +contrib/quotamgmt/Author +contrib/quotamgmt/config +contrib/quotamgmt/quotamgmt diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..116ffbb --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,121 @@ +#!/usr/local/bin/perl + +# You shouldn't need to change anything here. +# All configuration is done in the hints/ directory. +#-----------------------------------------------------------------------------# +# +# Automagically choose the right configuration +# +chop($os = `uname -rs 2>/dev/null`); +if ($os =~ /^SunOS 4\.1/){ $config='sunos_4_1.h'; } +elsif($os =~ /^SunOS 5/) { $config='solaris_2.h'; } +elsif($os =~ /^HP-UX (A\.09|B\.10|[BC]\.11)/) { $config='hpux.h'; } +elsif($os =~ /^IRIX 5/) { $config='irix_5.h'; } +elsif($os =~ /^IRIX\d* 6/) { $config='irix_6.h'; } +elsif($os =~ /^OSF1/) { $config='dec_osf.h'; } +elsif($os =~ /^Linux 2/) { $config='linux.h'; $picobj='linuxapi.o'; } +elsif($os =~ /^AIX/) { $config='aix_4_1.h'; } +elsif($os =~ /^BSD\/OS 2/ || + $os =~ /^Darwin/ || + $os =~ /^FreeBSD/ || + $os =~ /^NetBSD/ || + $os =~ /^OpenBSD/) { $config='bsd.h'; } + +if (defined($config)) { + print "Using hints/$config for myconfig.h\n"; + if (-e "myconfig.h" && (!(-l "myconfig.h") || (readlink("myconfig.h") ne "hints/$config"))) { + die "\nFATAL: myconfig.h already exists.\n\n" . + "You need to do a `make clean' before you configure for a new platform.\n". + "If that doesn't help, remove myconfig.h manually.\n"; + } +} +else { + warn "WARNING: No appropriate hints found for this OS: '$os - see INSTALL'\n"; +} + + +# check whether the Andrew File System (AFS) is installed and running + +if ( -d "/afs" ) { + my $afs = `df /afs 2>/dev/null`; + if ($afs =~ /\nAFS|\(AFS/) { + $hasafs = '-DAFSQUOTA'; + $AFSHOME = -d "/usr/afsws" ? "/usr/afsws" : "/usr"; + $extrainc = "-I$AFSHOME/include -I$AFSHOME/include/afs"; + $extralibs = "-L$AFSHOME/lib -L$AFSHOME/lib/afs -lsys -lrx -lrxkad -llwp"; + $afsquota = "afsquota.o"; + } +} + +# check to see if we have a kernel module for the Veritas file system +if ( $os =~ /^SunOS/ ) { + if ( -f '/usr/include/sys/fs/vx_quota.h' ) { + $hasvxfs = '-DSOLARIS_VXFS'; + $extraobj = "$extraobj vxquotactl.o"; + print "Configured with the VERITAS File System on Solaris\n"; + } + # no warning because newer versions of Solaris have internal VxFS support + # else { + # print "Configured without VxFS support\n"; + # } +} + +#-----------------------------------------------------------------------------# + +use ExtUtils::MakeMaker; + +&WriteMakefile('NAME' => 'Quota', + 'OBJECT' => '$(BASEEXT)$(OBJ_EXT) stdio_wrap.o '. + "$afsquota $picobj $extraobj ". $hint{'OBJ'}, + 'INC' => $extrainc .' '. $hint{'INC'}, + 'DEFINE' => "$hasafs $hasvxfs", + 'LIBS' => [ "-lrpcsvc $extralibs" ], + 'H' => [ 'myconfig.h' ], + 'VERSION_FROM' => 'Quota.pm', + 'clean' => { FILES => 'myconfig.h' }, +); + + +# +# Add rules for hints (myconfig.h) +# and extra objects that need special compiler arguments +# + +sub MY::postamble +{ + my $ret = ''; + my $extrac; + + if(!defined $config) { + $ret .= ' +myconfig.h: + @echo "You need to make a myconfig.h. See the file INSTALL."; + @false +' + } + else { + $ret .= " +myconfig.h: + rm -f myconfig.h + ln -s hints/$config myconfig.h +" + } + + # objects that must not be compiled with the CCCDL arguments + + if (defined $extraobj) { + ($extrac = $extraobj) =~ s/\.o(\s+|$)/.c/g; + $ret .= "\n$extraobj :\n\t". + '$(CC) -c $(INC) $(CCFLAGS) $(OPTIMIZE) '."$extrac\n\n"; + } + + # extract objects from a library to link them in statically + # for lame OSes that have problems with LD_PATH recording in DLOs + + if (defined $hint{'ARXLIBOBJ'}) { + $hint{'ARXLIBOBJ'} =~ /\s+/; + $ret .= "\n$' :\n\t\$(AR) x " . $hint{'ARXLIBOBJ'} ."\n\n"; + } + + $ret; +} diff --git a/Quota.pm b/Quota.pm new file mode 100644 index 0000000..a4af311 --- /dev/null +++ b/Quota.pm @@ -0,0 +1,356 @@ +package Quota; + +require Exporter; +use AutoLoader; +require DynaLoader; + +@ISA = qw(Exporter DynaLoader); +@EXPORT = (); + +$VERSION = '1.5.1'; + +bootstrap Quota; + +use Carp; +use POSIX qw(:errno_h); +use strict; + +## +## Get block device for locally mounted file system +## !! Do not use this to get the argument for the quota-functions in this +## !! module, since not all operating systems use the device path for the +## !! quotactl system call and e.g. Solaris doesn't even use a system call +## !! Always use getqcarg() instead. +## + +sub getdev { + ($#_ > 0) && croak("Usage: Quota::getdev(path)"); + my($target) = (($#_ == -1) ? "." : $_[0]); + my($dev) = (stat($target))[0]; + my($ret) = undef; + my($fsname,$path); + + if($dev && ($target ne "") && !Quota::setmntent()) { + while(($fsname,$path) = Quota::getmntent()) { + ($ret=$fsname, last) if ($dev == (stat($path))[0]); + } + $! = 0; + } + Quota::endmntent(); + $ret; +} + +## +## Get "device" argument for this module's Quota-functions +## + +sub getqcarg { + ($#_ > 0) && croak("Usage: Quota::getqcarg(path)"); + my($target) = (($#_ == -1) ? "." : $_[0]); + my($dev) = (stat($target))[0]; + my($ret) = undef; + my($argtyp,$fsupp) = (Quota::getqcargtype() =~ /([^,]*)(,.*)?/); + my($fsname,$path,$fstyp); + + if(defined($dev) && ($target ne "") && !Quota::setmntent()) { + while(($fsname,$path,$fstyp) = Quota::getmntent()) { + next if $fstyp =~ /^(lofs|ignore|auto.*|proc)$/; + my($pdev) = (stat($path))[0]; + if (defined($pdev) && ($dev == $pdev)) { + if($fsname =~ m|^[^/]+:/|) { $ret = $fsname } #NFS host:/path + elsif (($fstyp =~ /^nfs/i) && ($fsname =~ m#^(/.*)\@([^/]+)$#)) + { $ret = "$2:$1" } #NFS /path@host + elsif($argtyp eq "dev") { $ret = $fsname } + elsif($argtyp eq "qfile") { $ret = "$path/quotas" } + elsif($argtyp eq "any") { $ret = $target } + else { $ret = $path } #($argtyp eq "mntpt") + + # XFS, VxFS and AFS quotas require separate access methods + # (optional for VxFS: later versions use 'normal' quota interface) + if (($fstyp eq "xfs") && ($fsupp =~ /,XFS/)) { $ret = "(XFS)$ret" } + elsif(($fstyp eq "vxfs") && + defined($fsupp) && ($fsupp =~ /,VXFS/)) { $ret = "(VXFS)$ret" } + elsif((($fstyp eq "afs") || ($fsname eq "AFS")) && + ($fsupp =~ /,AFS/)) { $ret = "(AFS)$target"; } + last; + } + } + $! = 0; + } + Quota::endmntent(); + $ret; +} + +## +## Translate error codes of quotactl syscall and ioctl +## + +sub strerr { + ($#_ != -1) && croak("Usage: Quota::strerr()"); + my($str); + + eval { + if(($! == &EINVAL) || ($! == &ENOTTY) || ($! == &ENOENT)) + { $str = "No quotas on this system" } + elsif($! == &ENODEV) { $str = "Not a standard file system" } + elsif($! == &EPERM) { $str = "Not privileged" } + elsif($! == &EACCES) { $str = "Access denied" } + elsif($! == &ESRCH) { $str = "No quota for this user" } + elsif($! == &EUSERS) { $str = "Quota table overflow" } + else { die "unknown quota error\n" } + }; + if($@) { + my($err) = $! + 0; + $str = "error #$err"; + }; + $str; +} + +package Quota; # return to package Quota so AutoSplit is happy +1; +__END__ + +=head1 NAME + +Quota - Perl interface to file system quotas + +=head1 SYNOPSIS + + use Quota; + + ($block_curr, $block_soft, $block_hard, $block_timelimit, + $inode_curr, $inode_soft, $inode_hard, $inode_timelimit) = + Quota::query($dev [,$uid [,isgrp]]); + + ($block_curr, $block_soft, $block_hard, $block_timelimit, + $inode_curr, $inode_soft, $inode_hard, $inode_timelimit) = + Quota::rpcquery($host, $path [,$uid]); + + Quota::rpcpeer([$port [,$use_tcp [,timeout]]]); + + Quota::rpcauth([$uid [,$gid [,$hostname]]]); + + Quota::setqlim($dev, $uid, $block_soft, $block_hard, + $inode_soft, $inode_hard [,$tlo [,isgrp]]); + + Quota::sync([$dev]); + + $arg = Quota::getqcarg([$path]); + + Quota::setmntent(); + ($dev, $path, $type, $opts) = Quota::getmntent(); + Quota::endmntent(); + +=head1 DESCRIPTION + +The B<Quota> module provides access to file system quotas. +The quotactl system call or ioctl is used to query or set quotas +on the local host, or queries are submitted via RPC to a remote host. +Mount tables can be parsed with B<getmntent> and paths can be +translated to device files (or whatever the actual B<quotactl> +implementations needs as argument) of the according file system. + +=head2 Functions + +=over 4 + +=item I<($bc,$bs,$bh,$bt, $ic,$is,$ih,$it) = Quota::query($dev, $uid, $isgrp)> + +Get current usage and quota limits for a given file system and user. +The user is specified by its numeric uid; defaults to the process' +real uid. + +The type of I<$dev> varies from system to system. It's the argument +which is used by the B<quotactl> implementation to address a specific +file system. It may be the path of a device file (e.g. B</dev/sd0a>) +or the path of the mount point or the quotas file at the top of +the file system (e.g. B</home.stand/quotas>). However you do not +have to worry about that; use B<Quota::getqcarg> to automatically +translate any path inside a file system to the required I<$dev> argument. + +I<$dev> may also be in the form of B<hostname:path>, which has the +module transparently query the given host via a remote procedure call +(RPC). In case you have B<NFS> (or similar network mounts), this type +of argument may also be produced by B<Quota::getqcarg>. Note: RPC +queries require I<rquotad(1m)> to be running on the target system. If +the daemon or host are down, the timeout is 12 seconds. + +In I<$bc> and I<$ic> the current usage in blocks and inodes is returned. +I<$bs> and I<$is> are the soft limits, I<$bh> and I<$ih> hard limits. If the +soft limit is exceeded, writes by this user will fail for blocks or +inodes after I<$bt> or I<$it> is reached. These times are expressed +as usual, i.e. in elapsed seconds since 00:00 1/Jan/1970 GMT. + +Note: When the quota limits are not exceeded, the timestamps +are meaningless and should be ignored. When hard and soft +limits are zero, there is no limit for that user. On most +systems Quota::query will return undef in that case and +errno will be set to ESRCH. + +When I<$isgrp> is given and set to 1, I<$uid> is taken as gid and +group quotas are queried. This is B<not> supported across RPC and +even locally only on a few architectures (e.g. Linux and other BSD +based Unix variants, OSF/1 and AIX - check the quotactl(2) man page +on your systems). If unsupported, this flag is ignored. + +=item I<Quota::setqlim($dev, $uid, $bs,$bh, $is,$ih, $tlo, $isgrp)> + +Sets quota limits for the given user. Meanings of I<$dev>, I<$uid>, +I<$bs>, I<$bh>, I<$is> and I<$ih> are the same as in B<Quota::query>. + +I<$tlo> decides how the time limits are initialized: +I<0>: The time limits are set to B<NOT STARTED>, i.e. the time limits +are not initialized until the first write attempt by this user. +This is the default. +I<1>: The time limits are set to B<7.0 days>. +More alternatives (i.e. setting a specific time) aren't available in +most implementations. + +When I<$isgrp> is given and set to 1, I<$uid> is taken as gid and +group quota limits are set. This is supported only on a few +architectures (see above). If unsupported, this flag is ignored. + +Note: if you want to set the quota of a particular user to zero, i.e. +no write permission, you must not set all limits to zero, since that +is equivalent to unlimited access. Instead set only the hard limit +to 0 and the soft limit for example to 1. + +Note that you cannot set quotas via RPC. + +=item I<Quota::sync($dev)> + +Have the kernel update the quota file on disk or all quota files +if no argument given (the latter doesn't work on all systems, +in particular on B<HP-UX 10.10>). + +The main purpose of this function is to check if quota is enabled +in the kernel and for a particular file system. Read the B<quotaon(1m)> +man page on how to enable quotas on a file system. + +Note: on some systems this function always returns a success indication, +even on partitions which do not have quotas enabled (e.g. Linux 2.4). +This is not a bug in this module; it's a limitation in certain kernels. + +=item I<($bc,$bs,$bh,$bt, $ic,$is,$ih,$it) => + +I<Quota::rpcquery($host,$path,$uid)> + +This is equivalent to B<Quota::query("$host:$path",$uid)>, i.e. +query quota for a given user on a given remote host via RPC. +I<$path> is the path of any file or directory inside the wanted +file system on the remote host. + +=item I<Quota::rpcpeer($port,$use_tcp,timeout)> + +Configure parameters for subsequent RPC queries; all parameters are +optional. By default the portmapper on the remote host is used +(i.e. default port is 0, protocol is UDP) The default timeout is +4 seconds. + +=item I<Quota::rpcauth($uid,$gid,$hostname)> + +Configure authorization parameters for subsequent RPC queries; +all parameters are optional. By default uid and gid are taken from +owner of the process and hostname is the host name of current machine. + +=item I<$arg = Quota::getqcarg($path)> + +Get the required I<$dev> argument for B<Quota::query> and B<Quota::setqlim> +for the file system you want to operate on. I<$path> is any path of an +existing file or directory inside that file system. The path argument is +optional and defaults to the current working directory. + +The type of I<$dev> varies between operating systems, i.e. different +implementations of the quotactl functionality. Hence it's important for +compatibility to always use this module function and not really pass +a device file to B<Quota::query> (as returned by B<Quota::getdev>). +See also above at I<Quota::query> + +=item I<$dev = Quota::getdev($path)> + +Returns the device entry in the mount table for a particular file system, +specified by any path of an existing file or directory inside it. I<$path> +defaults to the working directory. This device entry need not really be +a device. For example on network mounts (B<NFS>) it's I<"host:mountpath">, +with I<amd(1m)> it may be something completely different. + +I<NEVER> use this to produce a I<$dev> argument for other functions of +this module, since it's not compatible. On some systems I<quotactl> +does not work on devices but on the I<quotas> file or some other kind of +argument. Always use B<Quota::getqcarg>. + +=item I<Quota::setmntent()> + +Opens or resets the mount table. This is required before the first +invocation of B<Quota::getmntent>. + +Note: on some systems there is no equivalent function in the C library. +But you still have to call this module procedure for initialization of +module-internal variables. + +=item I<($dev, $path, $type, $opts) = Quota::getmntent()> + +Returns the next entry in the system mount table. This table contains +information about all currently mounted (local or remote) file systems. +The format and location of this table (e.g. B</etc/mtab>) vary from +system to system. This function is provided as a compatible way to +parse it. (On some systems, like B<OSF/1>, this table isn't +accessible as a file at all, i.e. only via B<Quota::getmntent>). + +=item I<Quota::endmntent()> + +Close the mount table. Should be called after the last use of +B<Quota::getmntent> to free possibly allocated file handles and memory. +Always returns undef. + +=item I<Quota::strerr()> + +Translates B<$!> to a quota-specific error text. You should always +use this function to output error messages, since the normal messages +don't always make sense for quota errors +(e.g. I<ESRCH>: B<No such process>, here: B<No quota for this user>) + +Note that this function only returns a defined result if you called a +Quota command directly before which returned an error indication. + +=head1 RETURN VALUES + +Functions that are supposed return lists or scalars, return I<undef> upon +errors. As usual B<$!> contains the error code (see B<Quota::strerr>). + +B<Quota::endmntent> always returns I<undef>. +All other functions return 0 upon success, non-zero integer otherwise. + +=head1 EXAMPLES + +An example for each function can be found in the test script +I<test.pl>. See also the contrib directory, which contains +some longer scripts, kindly donated by users of the module. + +=head1 BUGS + +With remote quotas we have to rely on the remote system to +state correctly which block size the quota values are +referring to. Old versions of the Linux rpc.rquotad +reported a block size of 4 kilobytes, which was wildly +incorrect. For more info on this and other Linux bugs please +see INSTALL. + +=head1 AUTHORS + +This module was created 1995 by Tom Zoerner +(email: tomzo AT nefkom DOT net) +and since then continually improved and ported to +many operating- and file-systems. Numerous people +have contributed to this process; for a complete +list of names please see the CHANGES document. + +=head1 SEE ALSO + +perl(1), edquota(1m), +quotactl(2) or quotactl(7I), +mount(1m), mtab(4) or mnttab(4), quotaon(1m), +setmntent(3), getmntent(3) or getmntinfo(3), endmntent(3), +rpc(3), rquotad(1m). + +=cut diff --git a/Quota.xs b/Quota.xs new file mode 100644 index 0000000..16cbc53 --- /dev/null +++ b/Quota.xs @@ -0,0 +1,909 @@ +#ifdef __cplusplus +extern "C" { +#endif +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" +#ifdef __cplusplus +} +#endif + +#include "myconfig.h" + +#ifdef SFIO_VERSION +#include "stdio_wrap.h" +#else +#define std_fopen fopen +#define std_fclose fclose +#endif + +#ifdef AFSQUOTA +#include "include/afsquota.h" +#endif + +#ifdef SOLARIS_VXFS +#include "include/vxquotactl.h" +#endif + +#ifndef AIX +#ifndef NO_MNTENT +FILE *mtab = NULL; +#else +struct statfs *mntp, *mtab = NULL; +int mtab_size = 0; +#endif +#else /* AIX */ +static struct vmount *mtab = NULL; +static aix_mtab_idx, aix_mtab_count; +#endif + +#define RPC_DEFAULT_TIMEOUT 4000 + +#ifndef NO_RPC +static struct +{ + char use_tcp; + unsigned short port; + unsigned timeout; +} quota_rpc_cfg = {FALSE, 0, 4000}; + +static struct +{ + int uid; + int gid; + char hostname[MAX_MACHINE_NAME + 1]; +} quota_rpc_auth = {-1, -1, {0} }; + +/* + * fetch quotas from remote host + */ + +int +callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out) + char *host; + xdrproc_t inproc, outproc; + char *in, *out; +{ + struct sockaddr_in remaddr; + struct hostent *hp; + enum clnt_stat clnt_stat; + struct timeval rep_time, timeout; + CLIENT *client; + int socket = RPC_ANYSOCK; + + /* + * Get IP address; by default the port is determined via remote + * portmap daemon; different ports and protocols can be configured + */ + hp = gethostbyname(host); + if (hp == NULL) + return ((int) RPC_UNKNOWNHOST); + + rep_time.tv_sec = quota_rpc_cfg.timeout / 1000; + rep_time.tv_usec = (quota_rpc_cfg.timeout % 1000) * 1000; + memcpy((char *)&remaddr.sin_addr, (char *)hp->h_addr, hp->h_length); + remaddr.sin_family = AF_INET; + remaddr.sin_port = htons(quota_rpc_cfg.port); + + /* + * Create client RPC handle + */ + client = NULL; + if (!quota_rpc_cfg.use_tcp) { + client = (CLIENT *)clntudp_create(&remaddr, prognum, + versnum, rep_time, &socket); + } + else { + client = (CLIENT *)clnttcp_create(&remaddr, prognum, + versnum, &socket, 0, 0); + } + + if (client == NULL) + return ((int) rpc_createerr.cf_stat); + + /* + * Create an authentication handle + */ + if ((quota_rpc_auth.uid != -1) && (quota_rpc_auth.gid != -1)) { + client->cl_auth = authunix_create(quota_rpc_auth.hostname, + quota_rpc_auth.uid, + quota_rpc_auth.gid, 0, 0); + } + else { + client->cl_auth = authunix_create_default(); + } + + /* + * Call remote server + */ + timeout.tv_sec = quota_rpc_cfg.timeout / 1000; + timeout.tv_usec = (quota_rpc_cfg.timeout % 1000) * 1000; + clnt_stat = clnt_call(client, procnum, + inproc, in, outproc, out, timeout); + if (client) clnt_destroy(client); + + return ((int) clnt_stat); +} + +int +getnfsquota(hostp, fsnamep, uid, dqp) + char *hostp, *fsnamep; + int uid; + struct dqblk *dqp; +{ + struct getquota_args gq_args; + struct getquota_rslt gq_rslt; + + gq_args.gqa_pathp = fsnamep; + gq_args.gqa_uid = uid; + if (callaurpc(hostp, RQUOTAPROG, RQUOTAVERS, RQUOTAPROC_GETQUOTA, + xdr_getquota_args, &gq_args, xdr_getquota_rslt, &gq_rslt) != 0) { + return (-1); + } + switch (gq_rslt.GQR_STATUS) { + case Q_OK: + { + struct timeval tv; + + gettimeofday(&tv, NULL); +#ifdef LINUX_RQUOTAD_BUG + /* Since Linux reports a bogus block size value (4k), we must not + * use it. Thankfully Linux at least always uses 1k block sizes + * for quota reports, so we just leave away all conversions. + * If you have a mixed environment, you have a problem though. + * Complain to the Linux authors or apply my patch (see INSTALL) + */ + dqp->QS_BHARD = gq_rslt.GQR_RQUOTA.rq_bhardlimit; + dqp->QS_BSOFT = gq_rslt.GQR_RQUOTA.rq_bsoftlimit; + dqp->QS_BCUR = gq_rslt.GQR_RQUOTA.rq_curblocks; +#else /* not buggy */ + if (gq_rslt.GQR_RQUOTA.rq_bsize >= DEV_QBSIZE) { + /* we rely on the fact that block sizes are always powers of 2 */ + /* so the conversion factor will never be a fraction */ + int qb_fac = gq_rslt.GQR_RQUOTA.rq_bsize / DEV_QBSIZE; + dqp->QS_BHARD = gq_rslt.GQR_RQUOTA.rq_bhardlimit * qb_fac; + dqp->QS_BSOFT = gq_rslt.GQR_RQUOTA.rq_bsoftlimit * qb_fac; + dqp->QS_BCUR = gq_rslt.GQR_RQUOTA.rq_curblocks * qb_fac; + } + else { + int qb_fac = DEV_QBSIZE / gq_rslt.GQR_RQUOTA.rq_bsize; + dqp->QS_BHARD = gq_rslt.GQR_RQUOTA.rq_bhardlimit / qb_fac; + dqp->QS_BSOFT = gq_rslt.GQR_RQUOTA.rq_bsoftlimit / qb_fac; + dqp->QS_BCUR = gq_rslt.GQR_RQUOTA.rq_curblocks / qb_fac; + } +#endif /* LINUX_RQUOTAD_BUG */ + dqp->QS_FHARD = gq_rslt.GQR_RQUOTA.rq_fhardlimit; + dqp->QS_FSOFT = gq_rslt.GQR_RQUOTA.rq_fsoftlimit; + dqp->QS_FCUR = gq_rslt.GQR_RQUOTA.rq_curfiles; + + /* if time is given relative to actual time, add actual time */ + /* Note: all systems except Linux return relative times */ + if (gq_rslt.GQR_RQUOTA.rq_btimeleft == 0) + dqp->QS_BTIME = 0; + else if (gq_rslt.GQR_RQUOTA.rq_btimeleft + 10*365*24*60*60 < tv.tv_sec) + dqp->QS_BTIME = tv.tv_sec + gq_rslt.GQR_RQUOTA.rq_btimeleft; + else + dqp->QS_BTIME = gq_rslt.GQR_RQUOTA.rq_btimeleft; + + if (gq_rslt.GQR_RQUOTA.rq_ftimeleft == 0) + dqp->QS_FTIME = 0; + else if (gq_rslt.GQR_RQUOTA.rq_ftimeleft + 10*365*24*60*60 < tv.tv_sec) + dqp->QS_FTIME = tv.tv_sec + gq_rslt.GQR_RQUOTA.rq_ftimeleft; + else + dqp->QS_FTIME = gq_rslt.GQR_RQUOTA.rq_ftimeleft; + + if(dqp->QS_BHARD==0 && dqp->QS_BSOFT==0 && + dqp->QS_FHARD==0 && dqp->QS_FSOFT==0) { + errno = ESRCH; + return(-1); + } + return (0); + } + + case Q_NOQUOTA: + errno = ESRCH; + break; + + case Q_EPERM: + errno = EPERM; + break; + + default: + errno = EINVAL; + break; + } + return (-1); +} + +#ifdef MY_XDR + +struct xdr_discrim gq_des[2] = { + { (int)Q_OK, (xdrproc_t)xdr_rquota }, + { 0, NULL } +}; + +bool_t +xdr_getquota_args(xdrs, gqp) +XDR *xdrs; +struct getquota_args *gqp; +{ + return (xdr_string(xdrs, &gqp->gqa_pathp, 1024) && + xdr_int(xdrs, &gqp->gqa_uid)); +} + +bool_t +xdr_getquota_rslt(xdrs, gqp) +XDR *xdrs; +struct getquota_rslt *gqp; +{ + return (xdr_union(xdrs, + (int *) &gqp->GQR_STATUS, (char *) &gqp->GQR_RQUOTA, + gq_des, (xdrproc_t) xdr_void)); +} + +bool_t +xdr_rquota(xdrs, rqp) +XDR *xdrs; +struct rquota *rqp; +{ + return (xdr_int(xdrs, &rqp->rq_bsize) && + xdr_bool(xdrs, &rqp->rq_active) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_bhardlimit) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_bsoftlimit) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_curblocks) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_fhardlimit) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_fsoftlimit) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_curfiles) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_btimeleft) && + xdr_u_long(xdrs, (unsigned long *)&rqp->rq_ftimeleft) ); +} +#endif +#endif + +/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + * + * The Perl interface + * + */ + +MODULE = Quota PACKAGE = Quota +PROTOTYPES: DISABLE + +void +query(dev,uid=getuid(),isgrp=0) + char * dev + int uid + int isgrp + PPCODE: + { + struct dqblk dqblk; + char *p = NULL; + int err; +#ifdef USE_IOCTL + struct quotactl qp; + int fd = -1; +#endif +#ifdef SGI_XFS + if(!strncmp(dev, "(XFS)", 5)) { + fs_disk_quota_t xfs_dqblk; +#ifndef linux + err = quotactl(Q_XGETQUOTA, dev+5, uid, CADR &xfs_dqblk); +#else + err = quotactl(QCMD(Q_XGETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev+5, uid, CADR &xfs_dqblk); +#endif + if(!err) { + EXTEND(sp, 8); + PUSHs(sv_2mortal(newSViv(QX_DIV(xfs_dqblk.d_bcount)))); + PUSHs(sv_2mortal(newSViv(QX_DIV(xfs_dqblk.d_blk_softlimit)))); + PUSHs(sv_2mortal(newSViv(QX_DIV(xfs_dqblk.d_blk_hardlimit)))); + PUSHs(sv_2mortal(newSViv(xfs_dqblk.d_btimer))); + PUSHs(sv_2mortal(newSViv(xfs_dqblk.d_icount))); + PUSHs(sv_2mortal(newSViv(xfs_dqblk.d_ino_softlimit))); + PUSHs(sv_2mortal(newSViv(xfs_dqblk.d_ino_hardlimit))); + PUSHs(sv_2mortal(newSViv(xfs_dqblk.d_itimer))); + } + } + else +#endif +#ifdef SOLARIS_VXFS + if(!strncmp(dev, "(VXFS)", 6)) { + struct vx_dqblk vxfs_dqb; + err = vx_quotactl(VX_GETQUOTA, dev+6, uid, CADR &vxfs_dqb); + if(!err) { + EXTEND(sp,8); + PUSHs(sv_2mortal(newSViv(Q_DIV(vxfs_dqb.dqb_curblocks)))); + PUSHs(sv_2mortal(newSViv(Q_DIV(vxfs_dqb.dqb_bsoftlimit)))); + PUSHs(sv_2mortal(newSViv(Q_DIV(vxfs_dqb.dqb_bhardlimit)))); + PUSHs(sv_2mortal(newSViv(vxfs_dqb.dqb_btimelimit))); + PUSHs(sv_2mortal(newSViv(vxfs_dqb.dqb_curfiles))); + PUSHs(sv_2mortal(newSViv(vxfs_dqb.dqb_fsoftlimit))); + PUSHs(sv_2mortal(newSViv(vxfs_dqb.dqb_fhardlimit))); + PUSHs(sv_2mortal(newSViv(vxfs_dqb.dqb_ftimelimit))); + } + } + else +#endif +#ifdef AFSQUOTA + if(!strncmp(dev, "(AFS)", 5)) { + if (!afs_check()) { /* check is *required* as setup! */ + errno = EINVAL; + } + else { + int maxQuota, blocksUsed; + + err = afs_getquota(dev + 5, &maxQuota, &blocksUsed); + if(!err) { + EXTEND(sp, 8); + PUSHs(sv_2mortal(newSViv(blocksUsed))); + PUSHs(sv_2mortal(newSViv(maxQuota))); + PUSHs(sv_2mortal(newSViv(maxQuota))); + PUSHs(sv_2mortal(newSViv(0))); + PUSHs(sv_2mortal(newSViv(0))); + PUSHs(sv_2mortal(newSViv(0))); + PUSHs(sv_2mortal(newSViv(0))); + PUSHs(sv_2mortal(newSViv(0))); + } + } + } + else +#endif + { + if((*dev != '/') && (p = strchr(dev, ':'))) { +#ifndef NO_RPC + *p = '\0'; + err = getnfsquota(dev, p+1, uid, &dqblk); + *p = ':'; +#else /* NO_RPC */ + errno = ENOSYS; + err = -1; +#endif /* NO_RPC */ + } + else { +#ifdef USE_IOCTL + qp.op = Q_GETQUOTA; + qp.uid = uid; + qp.addr = (char *)&dqblk; + err = (((fd = open(dev, O_RDONLY)) == -1) || + (ioctl(fd, Q_QUOTACTL, &qp) == -1)); +#else /* not USE_IOCTL */ +#ifdef Q_CTL_V3 /* Linux */ + err = linuxquota_query(dev, uid, isgrp, &dqblk); +#else /* not Q_CTL_V3 */ +#ifdef Q_CTL_V2 +#ifdef AIX + /* AIX quotactl doesn't fail if path does not exist!? */ + struct stat st; + if (stat(dev, &st)) err = 1; + else +#endif + err = quotactl(dev, QCMD(Q_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), uid, CADR &dqblk); +#else /* not Q_CTL_V2 */ + err = quotactl(Q_GETQUOTA, dev, uid, CADR &dqblk); +#endif /* not Q_CTL_V2 */ +#endif /* Q_CTL_V3 */ +#endif /* not USE_IOCTL */ + } + if(!err) { + EXTEND(sp, 8); + PUSHs(sv_2mortal(newSViv(Q_DIV(dqblk.QS_BCUR)))); + PUSHs(sv_2mortal(newSViv(Q_DIV(dqblk.QS_BSOFT)))); + PUSHs(sv_2mortal(newSViv(Q_DIV(dqblk.QS_BHARD)))); + PUSHs(sv_2mortal(newSViv(dqblk.QS_BTIME))); + PUSHs(sv_2mortal(newSViv(dqblk.QS_FCUR))); + PUSHs(sv_2mortal(newSViv(dqblk.QS_FSOFT))); + PUSHs(sv_2mortal(newSViv(dqblk.QS_FHARD))); + PUSHs(sv_2mortal(newSViv(dqblk.QS_FTIME))); + } +#ifdef USE_IOCTL + if(fd != -1) close(fd); +#endif + } + } + +int +setqlim(dev,uid,bs,bh,fs,fh,timelimflag=0,isgrp=0) + char * dev + int uid + int bs + int bh + int fs + int fh + int timelimflag + int isgrp + CODE: + { + struct dqblk dqblk; +#ifdef USE_IOCTL + struct quotactl qp; + int fd; + + qp.op = Q_SETQLIM; + qp.uid = uid; + qp.addr = (char *)&dqblk; +#endif + if(timelimflag != 0) timelimflag = 1; +#ifdef SGI_XFS + if(!strncmp(dev, "(XFS)", 5)) { + fs_disk_quota_t xfs_dqblk; + + xfs_dqblk.d_blk_softlimit = QX_MUL(bs); + xfs_dqblk.d_blk_hardlimit = QX_MUL(bh); + xfs_dqblk.d_btimer = timelimflag; + xfs_dqblk.d_ino_softlimit = fs; + xfs_dqblk.d_ino_hardlimit = fh; + xfs_dqblk.d_itimer = timelimflag; + xfs_dqblk.d_fieldmask = FS_DQ_LIMIT_MASK; + xfs_dqblk.d_flags = XFS_USER_QUOTA; +#ifndef linux + RETVAL = quotactl(Q_XSETQLIM, dev+5, uid, CADR &xfs_dqblk); +#else + RETVAL = quotactl(QCMD(Q_XSETQLIM, (isgrp ? GRPQUOTA : USRQUOTA)), dev+5, uid, CADR &xfs_dqblk); +#endif + } + else + /* if not xfs, than it's a classic IRIX efs file system */ +#endif +#ifdef SOLARIS_VXFS + if(!strncmp(dev, "(VXFS)", 6)) { + struct vx_dqblk vxfs_dqb; + + vxfs_dqb.dqb_bsoftlimit = Q_MUL(bs); + vxfs_dqb.dqb_bhardlimit = Q_MUL(bh); + vxfs_dqb.dqb_btimelimit = timelimflag; + vxfs_dqb.dqb_fsoftlimit = fs; + vxfs_dqb.dqb_fhardlimit = fh; + vxfs_dqb.dqb_ftimelimit = timelimflag; + RETVAL = vx_quotactl(VX_SETQUOTA, dev+6, uid, CADR &vxfs_dqb); + } + else +#endif +#ifdef AFSQUOTA + if(!strncmp(dev, "(AFS)", 5)) { + if (!afs_check()) { /* check is *required* as setup! */ + errno = EINVAL; + RETVAL = -1; + } + else + RETVAL = afs_setqlim(dev + 5, bh); + } + else +#endif + { + dqblk.QS_BSOFT = Q_MUL(bs); + dqblk.QS_BHARD = Q_MUL(bh); + dqblk.QS_BTIME = timelimflag; + dqblk.QS_FSOFT = fs; + dqblk.QS_FHARD = fh; + dqblk.QS_FTIME = timelimflag; +#ifdef USE_IOCTL + if((fd = open(dev, O_RDONLY)) != -1) { + RETVAL = (ioctl(fd, Q_QUOTACTL, &qp) != 0); + close(fd); + } + else + RETVAL = -1; +#else +#ifdef Q_CTL_V3 /* Linux */ + RETVAL = linuxquota_setqlim (dev, uid, isgrp, &dqblk); +#else +#ifdef Q_CTL_V2 + RETVAL = quotactl (dev, QCMD(Q_SETQUOTA,(isgrp ? GRPQUOTA : USRQUOTA)), uid, CADR &dqblk); +#else + RETVAL = quotactl (Q_SETQLIM, dev, uid, CADR &dqblk); +#endif +#endif +#endif + } + } + OUTPUT: + RETVAL + +int +sync(dev=NULL) + char * dev + CODE: +#ifdef SOLARIS_VXFS + if ((dev != NULL) && !strncmp(dev, "(VXFS)", 6)) { + RETVAL = vx_quotactl(VX_QSYNCALL, dev+6, 0, NULL); + } + else +#endif +#ifdef AFSQUOTA + if ((dev != NULL) && !strncmp(dev, "(AFS)", 5)) { + if (!afs_check()) { + errno = EINVAL; + RETVAL = -1; + } + else { + int foo1, foo2; + RETVAL = (afs_getquota(dev + 5, &foo1, &foo2) ? -1 : 0); + } + } + else +#endif +#ifdef USE_IOCTL + { + struct quotactl qp; + int fd; + + if(dev == NULL) { + qp.op = Q_ALLSYNC; + dev = "/"; /* is probably ignored anyways */ + } + else + qp.op = Q_SYNC; + if((fd = open(dev, O_RDONLY)) != -1) { + RETVAL = (ioctl(fd, Q_QUOTACTL, &qp) != 0); + if(errno == ESRCH) errno = EINVAL; + close(fd); + } + else + RETVAL = -1; + } +#else + { +#ifdef Q_CTL_V3 /* Linux */ +#ifdef SGI_XFS + if ((dev != NULL) && (!strncmp(dev, "(XFS)", 5))) { + struct fs_quota_stat fsq_stat; + + if (!quotactl(QCMD(Q_XGETQSTAT, USRQUOTA), dev+5, 0, CADR &fsq_stat)) { + if (fsq_stat.qs_flags & (XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_GDQ_ACCT)) + RETVAL = 0; + else if ( (strcmp(dev+5, "/") == 0) && + (((fsq_stat.qs_flags & 0xff00) >> 8) & (XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_GDQ_ACCT)) ) + RETVAL = 0; + else { + errno = ENOENT; + RETVAL = -1; + } + } + else { + errno = ENOENT; + RETVAL = -1; + } + } + else +#endif + RETVAL = linuxquota_sync (dev, FALSE); +#else +#ifdef Q_CTL_V2 +#ifdef AIX + struct stat st; +#endif + if(dev == NULL) dev = "/"; +#ifdef AIX + if (stat(dev, &st)) RETVAL = -1; + else +#endif + RETVAL = quotactl(dev, QCMD(Q_SYNC, USRQUOTA), 0, NULL); +#else +#ifdef SGI_XFS +#define XFS_UQUOTA (XFS_QUOTA_UDQ_ACCT|XFS_QUOTA_UDQ_ENFD) + /* Q_SYNC is not supported on XFS filesystems, so emulate it */ + if ((dev != NULL) && (!strncmp(dev, "(XFS)", 5))) { + fs_quota_stat_t fsq_stat; + + sync(); + + RETVAL = quotactl(Q_GETQSTAT, dev+5, 0, CADR &fsq_stat); + + if (!RETVAL && ((fsq_stat.qs_flags & XFS_UQUOTA) != XFS_UQUOTA)) { + errno = ENOENT; + RETVAL = -1; + } + } + else +#endif + RETVAL = quotactl(Q_SYNC, dev, 0, NULL); +#endif +#endif + } +#endif + OUTPUT: + RETVAL + +void +rpcquery(host,path,uid=getuid()) + char * host + char * path + int uid + PPCODE: + { +#ifndef NO_RPC + struct dqblk dqblk; + if(getnfsquota(host, path, uid, &dqblk) == 0) { + EXTEND(sp, 8); + PUSHs(sv_2mortal(newSViv(Q_DIV(dqblk.QS_BCUR)))); + PUSHs(sv_2mortal(newSViv(Q_DIV(dqblk.QS_BSOFT)))); + PUSHs(sv_2mortal(newSViv(Q_DIV(dqblk.QS_BHARD)))); + PUSHs(sv_2mortal(newSViv(dqblk.QS_BTIME))); + + PUSHs(sv_2mortal(newSViv((int) dqblk.QS_FCUR))); + PUSHs(sv_2mortal(newSViv((int) dqblk.QS_FSOFT))); + PUSHs(sv_2mortal(newSViv((int) dqblk.QS_FHARD))); + PUSHs(sv_2mortal(newSViv((int) dqblk.QS_FTIME))); + } +#else + errno = ENOSYS; +#endif + } + +void +rpcpeer(port=0,use_tcp=FALSE,timeout=RPC_DEFAULT_TIMEOUT) + unsigned port + unsigned use_tcp + unsigned timeout + PPCODE: + { +#ifndef NO_RPC + quota_rpc_cfg.port = port; + quota_rpc_cfg.use_tcp = use_tcp; + quota_rpc_cfg.timeout = timeout; +#endif + } + +int +rpcauth(uid=-1,gid=-1,hostname=NULL) + int uid + int gid + char * hostname + CODE: + { +#ifndef NO_RPC + if ((uid == -1) && (gid == -1) && (hostname==NULL)) { + /* reset to default values */ + quota_rpc_auth.uid = uid; + quota_rpc_auth.gid = gid; + quota_rpc_auth.hostname[0] = 0; + RETVAL = 0; + + } else { + if (uid == -1) + quota_rpc_auth.uid = getuid(); + else + quota_rpc_auth.uid = uid; + + if (gid == -1) + quota_rpc_auth.gid = getgid(); + else + quota_rpc_auth.gid = gid; + + if (hostname == NULL) { + RETVAL = gethostname(quota_rpc_auth.hostname, MAX_MACHINE_NAME); + } else if (strlen(hostname) < MAX_MACHINE_NAME) { + strcpy(quota_rpc_auth.hostname, hostname); + RETVAL = 0; + } else { + errno = EINVAL; + RETVAL = -1; + } + } +#endif + } + OUTPUT: + RETVAL + +int +setmntent() + CODE: + { +#ifndef AIX +#ifndef NO_MNTENT +#ifndef NO_OPEN_MNTTAB + if(mtab != NULL) endmntent(mtab); + if((mtab = setmntent(MOUNTED, "r")) == NULL) +#else + if(mtab != NULL) fclose(mtab); + if((mtab = std_fopen (MOUNTED,"r")) == NULL) +#endif + RETVAL = -1; + else + RETVAL = 0; +#else + /* if(mtab != NULL) free(mtab); */ + if((mtab_size = getmntinfo(&mtab, MNT_NOWAIT)) <= 0) + RETVAL = -1; + else + RETVAL = 0; + mntp = mtab; +#endif +#else /* AIX */ + int count, space; + + if(mtab != NULL) free(mtab); + count = mntctl(MCTL_QUERY, sizeof(space), (struct vmount *) &space); + if (count == 0) { + mtab = (struct vmount *) malloc(space); + if (mtab != NULL) { + count = mntctl(MCTL_QUERY, space, mtab); + if (count > 0) { + aix_mtab_count = count; + aix_mtab_idx = 0; + RETVAL = 0; + } + else { /* error, or size changed between calls */ + if (count == 0) errno = EINTR; + RETVAL = -1; + } + } + else + RETVAL = -1; + } + else if (count < 0) + RETVAL = -1; + else { /* should never happen */ + errno = ENOENT; + RETVAL = -1; + } +#endif + } + OUTPUT: + RETVAL + +void +getmntent() + PPCODE: + { +#ifndef AIX +#ifndef NO_MNTENT +#ifndef NO_OPEN_MNTTAB + struct mntent *mntp; + if(mtab != NULL) { + mntp = getmntent(mtab); + if(mntp != NULL) { + EXTEND(sp, 4); + PUSHs(sv_2mortal(newSVpv(mntp->mnt_fsname, strlen(mntp->mnt_fsname)))); + PUSHs(sv_2mortal(newSVpv(mntp->mnt_dir, strlen(mntp->mnt_dir)))); + PUSHs(sv_2mortal(newSVpv(mntp->mnt_type, strlen(mntp->mnt_type)))); + PUSHs(sv_2mortal(newSVpv(mntp->mnt_opts, strlen(mntp->mnt_opts)))); + } + } + else + errno = EBADF; +#else + struct mnttab mntp; + if(mtab != NULL) { + if(getmntent(mtab, &mntp) == 0) { + EXTEND(sp, 4); + PUSHs(sv_2mortal(newSVpv(mntp.mnt_special, strlen(mntp.mnt_special)))); + PUSHs(sv_2mortal(newSVpv(mntp.mnt_mountp, strlen(mntp.mnt_mountp)))); + PUSHs(sv_2mortal(newSVpv(mntp.mnt_fstype, strlen(mntp.mnt_fstype)))); + PUSHs(sv_2mortal(newSVpv(mntp.mnt_mntopts, strlen(mntp.mnt_mntopts)))); + } + } + else + errno = EBADF; +#endif +#else +#ifdef OSF_QUOTA + char *fstype = getvfsbynumber((int)mntp->f_type); +#endif + if((mtab != NULL) && mtab_size) { + EXTEND(sp,4); + PUSHs(sv_2mortal(newSVpv(mntp->f_mntfromname, strlen(mntp->f_mntfromname)))); + PUSHs(sv_2mortal(newSVpv(mntp->f_mntonname, strlen(mntp->f_mntonname)))); +#ifdef OSF_QUOTA + if (fstype != (char *) -1) + PUSHs(sv_2mortal(newSVpv(fstype, strlen(fstype)))); + else +#endif +#ifndef __OpenBSD__ + PUSHs(sv_2mortal(newSViv((IV)mntp->f_type))); +#else + /* OpenBSD struct statfs lacks the f_type member (starting with release 2.7) */ + PUSHs(sv_2mortal(newSViv((IV)""))); +#endif + PUSHs(sv_2mortal(newSViv((IV)mntp->f_flags))); + mtab_size--; + mntp++; + } +#endif +#else /* AIX */ + struct vmount *vmp; + char *cp; + int i; + + if ((mtab != NULL) && (aix_mtab_idx < aix_mtab_count)) { + cp = (char *) mtab; + for (i=0; i<aix_mtab_idx; i++) { + vmp = (struct vmount *) cp; + cp += vmp->vmt_length; + } + vmp = (struct vmount *) cp; + aix_mtab_idx += 1; + + EXTEND(sp,4); + if ((vmp->vmt_gfstype != MNT_NFS) && (vmp->vmt_gfstype != MNT_NFS3)) { + cp = vmt2dataptr(vmp, VMT_OBJECT); + PUSHs(sv_2mortal(newSVpv(cp, strlen(cp)))); + } + else { + uchar *mp, *cp2; + cp = vmt2dataptr(vmp, VMT_HOST); + cp2 = vmt2dataptr(vmp, VMT_OBJECT); + mp = malloc(strlen(cp) + strlen(cp2) + 2); + if (mp != NULL) { + strcpy(mp, cp); + strcat(mp, ":"); + strcat(mp, cp2); + PUSHs(sv_2mortal(newSVpv(mp, strlen(mp)))); + free(mp); + } + else { + cp = "?"; + PUSHs(sv_2mortal(newSVpv(cp, strlen(cp)))); + } + } + cp = vmt2dataptr(vmp, VMT_STUB); + PUSHs(sv_2mortal(newSVpv(cp, strlen(cp)))); + + switch(vmp->vmt_gfstype) { + case MNT_AIX: cp = "aix"; break; + case MNT_NFS: cp = "nfs"; break; + case MNT_NFS3: cp = "nfs"; break; + case MNT_JFS: cp = "jfs"; break; + case 4: cp = "afs"; break; + case MNT_CDROM: cp = "cdrom,ignore"; break; + default: cp = "unknown,ignore"; break; + } + PUSHs(sv_2mortal(newSVpv(cp, strlen(cp)))); + + cp = vmt2dataptr(vmp, VMT_ARGS); + PUSHs(sv_2mortal(newSVpv(cp, strlen(cp)))); + } +#endif + } + +void +endmntent() + PPCODE: + { + if(mtab != NULL) { +#ifndef AIX +#ifndef NO_MNTENT +#ifndef NO_OPEN_MNTTAB + endmntent(mtab); /* returns always 1 in SunOS */ +#else + std_fclose (mtab); +#endif + /* #else: if(mtab != NULL) free(mtab); */ +#endif +#else /* AIX */ + free(mtab); +#endif + mtab = NULL; + } + } + +char * +getqcargtype() + CODE: + static char ret[25]; +#if defined(USE_IOCTL) || defined(QCARG_MNTPT) + strcpy(ret, "mntpt"); +#else +#if defined(AIX) || defined(OSF_QUOTA) + strcpy(ret, "any"); +#else +#ifdef Q_CTL_V2 + strcpy(ret, "qfile"); +#else +#ifdef SGI_XFS + strcpy(ret, "dev,XFS"); +#else +/* this branch applies to Q_CTL_V3 (Linux) too */ + strcpy(ret, "dev"); +#endif +#endif +#endif +#endif +#ifdef AFSQUOTA + strcat(ret, ",AFS"); +#endif +#ifdef SOLARIS_VXFS + strcat(ret, ",VXFS"); +#endif + RETVAL = ret; + OUTPUT: + RETVAL @@ -0,0 +1,281 @@ +Quota extension module for Perl +------------------------------- + +Author: Tom Zoerner (email: tomzo AT nefkom DOT net) + +Version: 1.5.1 +Date: July 2005 +DSLI-code: Rdcf + - stable release + - support by developer + - C compiler required for installation + - plain functions, no references used +Supported: SunOS 4.1.3, + Solaris 2.4 & 2.5 & 2.6, + HP-UX 9.0x & 10.10 & 10.20 & 11.00, + IRIX 5.2 & 5.3 & 6.2 - 6.5, + OSF/1 & Digital Unix 4, + BSDi 2, FreeBSD 3, OpenBSD & NetBSD (no RPC), + Linux - kernel 2.0.30 and later, incl. Quota API v2, + AIX 4.1 & 4.2. + AFS (Andrew File System) on many of the above (see INSTALL), + VxFS (Veritas File System) on Solaris 2. +Location: http://www.perl.com/CPAN/authors/Tom_Zoerner/ +Author: TOMZO (Tom Zoerner) +License: Same as Perl: Artistic License or GPL; + See also http://www.opensource.org/licenses/ + +Documentation is in pod format at the end of Quota.pm, +installation hints are in a file named INSTALL inside this package. +See also CHANGES for a history of updates to this module. + +----------------------------------------------------------------------------- + THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +----------------------------------------------------------------------------- + + +NAME + Quota - Perl interface to file system quotas + +SYNOPSIS + use Quota; + + ($block_curr, $block_soft, $block_hard, $block_timelimit, + $inode_curr, $inode_soft, $inode_hard, $inode_timelimit) = + Quota::query($dev [,$uid [,isgrp]]); + + ($block_curr, $block_soft, $block_hard, $block_timelimit, + $inode_curr, $inode_soft, $inode_hard, $inode_timelimit) = + Quota::rpcquery($host, $path [,$uid]); + + Quota::rpcpeer([$port [,$use_tcp [,timeout]]]); + + Quota::setqlim($dev, $uid, $block_soft, $block_hard, + $inode_soft, $inode_hard [,$tlo [,isgrp]]); + + Quota::sync([$dev]); + + $arg = Quota::getqcarg([$path]); + + Quota::setmntent(); + ($dev, $path, $type, $opts) = Quota::getmntent(); + Quota::endmntent(); + + +DESCRIPTION + The Quota module provides access to file system quotas. The + quotactl system call or ioctl is used to query or set quotas + on the local host, or queries are submitted via RPC to a + remote host. Mount tables can be parsed with getmntent and + paths can be translated to device files (or whatever the + actual quotactl implementations needs as argument) of the + according file system. + + Functions + + ($bc,$bs,$bh,$bt, $ic,$is,$ih,$it) = Quota::query($dev, $uid, $isgrp) + Get current usage and quota limits for a given file + system and user. The user is specified by its numeric + uid; defaults to the process' real uid. + + The type of $dev varies from system to system. It's the + argument which is used by the quotactl implementation to + address a specific file system. It may be the path of a + device file (e.g. /dev/sd0a) or the path of the mount + point or the quotas file at the top of the file system + (e.g. /home.stand/quotas). However you do not have to + worry about that; use Quota::getqcarg to automatically + translate any path inside a file system to the required + $dev argument. + + $dev may also be in the form of hostname:path, which has + the module transparently query the given host via a + remote procedure call (RPC). In case you have NFS (or + similar network mounts), this type of argument may also + be produced by Quota::getqcarg. Note: RPC queries + require rquotad(1m) to be running on the target system. + If the daemon or host are down, the timeout is 12 + seconds. + + In $bc and $ic the current usage in blocks and inodes is + returned. $bs and $is are the soft limits, $bh and $ih + hard limits. If the soft limit is exceeded, writes by + this user will fail for blocks or inodes after $bt or + $it is reached. These times are expressed as usual, i.e. + in elapsed seconds since 00:00 1/Jan/1970 GMT. + + Note: When the quota limits are not exceeded, the timestamps + are meaningless and should be ignored. When hard and soft + limits are zero, there is no limit for that user. On most + systems Quota::query will return undef in that case and + errno will be set to ESRCH. + + When $isgrp is given and set to 1, $uid is taken as gid + and group quotas are queried. This is not supported across + RPC and even locally only on a few architectures (e.g. Linux + and other BSD based Unix variants, OSF/1 and AIX - check the + quotactl(2) man page on your systems). If unsupported, this + flag is ignored. + + Quota::setqlim($dev, $uid, $bs,$bh, $is,$ih, $tlo, $isgrp) + Sets quota limits for the given user. Meanings of $dev, + $uid, $bs, $bh, $is and $ih are the same as in + Quota::query. + + $tlo decides how the time limits are initialized: 0: + The time limits are set to NOT STARTED, i.e. the time + limits are not initialized until the first write attempt + by this user. This is the default. 1: The time limits + are set to 7.0 days. More alternatives (i.e. setting a + specific time) aren't available in most implementations. + + When $isgrp is given and set to 1, $uid is taken as gid + and group quota limits are set. This is supported only on a + few architectures (see above). If unsupported, this flag + is ignored. + + Note: if you want to set the quota of a particular user + to zero, i.e. no write permission, you must not set all + limits to zero, since that is equivalent to unlimited + access. Instead set only the hard limit to 0 and the + soft limit for example to 1. + + Note that you cannot set quotas via RPC. + + Quota::sync($dev) + Have the kernel update the quota file on disk or all + quota files if no argument given (the latter doesn't + work on all systems, in particular on HP-UX 10.10). + + The main purpose of this function is to check if quota + is enabled in the kernel and for a particular file + system. Read the quotaon(1m) man page on how to enable + quotas on a file system. + + Note: on some systems this function always returns a + success indication, even on partitions which do not + have quotas enabled (e.g. Linux). This is not a bug + in this module; it's a limitation in certain kernels. + + ($bc,$bs,$bh,$bt, $ic,$is,$ih,$it) = + Quota::rpcquery($host,$path,$uid) + + This is equivalent to Quota::query("$host:$path",$uid), + i.e. query quota for a given user on a given remote + host via RPC. $path is the path of any file or + directory inside the wanted file system on the remote + host. + + Quota::rpcpeer($port,$use_tcp,timeout) + Configure parameters for subsequent RPC queries; all + parameters are optional. By default the portmapper on + the remote host is used (i.e. default port is 0, + protocol is UDP) The default timeout is 4 seconds. + + Quota::rpcauth($uid,$gid,$hostname) + Configure authorization parameters for subsequent + RPC queries; all parameters are optional. By default + uid and gid are taken from owner of the process and + hostname is the host name of current machine. + + $arg = Quota::getqcarg($path) + Get the required $dev argument for Quota::query and + Quota::setqlim for the file system you want to operate + on. $path is any path of an existing file or directory + inside that file system. The path argument is optional + and defaults to the current working directory. + + The type of $dev varies between operating systems, i.e. + different implementations of the quotactl functionality. + Hence it's important for compatibility to always use + this module function and not really pass a device file + to Quota::query (as returned by Quota::getdev). See + also above at Quota::query + + $dev = Quota::getdev($path) + Returns the device entry in the mount table for a + particular file system, specified by any path of an + existing file or directory inside it. $path defaults to + the working directory. This device entry need not really + be a device. For example on network mounts (NFS) it's + "host:mountpath", with amd(1m) it may be something + completely different. + + NEVER use this to produce a $dev argument for other + functions of this module, since it's not compatible. On + some systems quotactl does not work on devices but on + the quotas file or some other kind of argument. Always + use Quota::getqcarg. + + Quota::setmntent() + Opens or resets the mount table. This is required before + the first invocation of Quota::getmntent. + + Note: on some systems there is no equivalent function in + the C library. But you still have to call this module + procedure for initialization of module-internal + variables. + + ($dev, $path, $type, $opts) = Quota::getmntent() + Returns the next entry in the system mount table. This + table contains information about all currently mounted + (local or remote) file systems. The format and location + of this table (e.g. /etc/mtab) vary from system to + system. This function is provided as a compatible way to + parse it. (On some systems, like OSF/1, this table isn't + accessible as a file at all, i.e. only via + Quota::getmntent). + + Quota::endmntent() + Close the mount table. Should be called after the last + use of Quota::getmntent to free possibly allocated file + handles and memory. Always returns undef. + + Quota::strerr() + Translates $! to a quota-specific error text. You should + always use this function to output error messages, since + the normal messages don't always make sense for quota + errors (e.g. ESRCH: No such process, here: No quota for + this user) + + Note that this function only returns a defined result if + you called a Quota command directly before which returned + an error indication. + +RETURN VALUES + Functions that are supposed return lists or scalars, return + undef upon errors. As usual $! contains the error code (see + Quota::strerr). + + Quota::endmntent always returns undef. All other functions + return 0 upon success, non-zero integer otherwise. + +EXAMPLES + An example for each function can be found in the test script + test.pl. See also the contrib directory, which contains + some longer scripts, kindly donated by users of the module. + +BUGS + With remote quotas we have to rely on the remote system to + state correctly which block size the quota values are + referring to. Old versions of the Linux rpc.rquotad + reported a block size of 4 kilobytes, which was wildly + incorrect. For more info on this and other Linux bugs please + see INSTALL. + +AUTHORS + This module was created 1995 by Tom Zoerner + (email: tomzo AT nefkom DOT net) + and since then continually improved and ported to + many operating- and file-systems. Numerous people + have contributed to this process; for a complete + list of names please see the CHANGES document. + +SEE ALSO + perl(1), edquota(1m), quotactl(2) or quotactl(7I), + mount(1m), mtab(4) or mnttab(4), quotaon(1m), setmntent(3), + getmntent(3) or getmntinfo(3), endmntent(3), rpc(3), + rquotad(1m). + diff --git a/afsquota.c b/afsquota.c new file mode 100644 index 0000000..fba977d --- /dev/null +++ b/afsquota.c @@ -0,0 +1,172 @@ +/* + * Interface to OpenAFS + */ + +#if defined( __hpux) +#define IGNORE_STDS_H +#endif +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/ioctl.h> + +#define MAXSIZE 2048 +#define MAXDNAME 2048 + +#include <afs/afsint.h> +#include <afs/venus.h> +#include <resolv.h> + +#ifdef SunOS4 +#define _VICEIOCTL(id) ((unsigned int ) _IOW(V, id, struct ViceIoctl)) +#endif + + +/* + * Check if AFS is installed + */ + +int afs_check(void) +{ + struct ViceIoctl vi; + int32_t code; + char space[MAXSIZE]; + + vi.in_size = 0; + vi.out_size = MAXSIZE; + vi.out = (caddr_t) space; + code = pioctl(NULL, VIOC_GET_WS_CELL, &vi, 0); + return ! code; +} + + +/* + * report quota + */ + +int afs_getquota(char *path, int *maxQuota, int *blocksUsed) +{ + struct ViceIoctl a_params; + struct VolumeStatus *vs; + + a_params.in_size = 0; + a_params.out_size = MAXSIZE; + a_params.in = NULL; + a_params.out = malloc(MAXSIZE); + + if (a_params.out == NULL) { + errno = ENOMEM; + return -1; + } + + if (pioctl(path, VIOCGETVOLSTAT,&a_params,1) == -1) { + free(a_params.out); + return -1; + } + + vs = (struct VolumeStatus *) a_params.out; + + *maxQuota = vs->MaxQuota; + *blocksUsed = vs->BlocksInUse; + + free(a_params.out); + return 0; +} + +/* + * Usage: fs sq <path> <max quota in kbytes> + */ + +int afs_setqlim(char *path, int maxQuota) +{ + struct ViceIoctl a_params; + struct VolumeStatus *vs; + int insize; + + a_params.in_size = 0; + a_params.out_size = MAXSIZE; + a_params.in = NULL; + a_params.out = malloc(MAXSIZE); + + if (a_params.out == NULL) { + errno = ENOMEM; + return -1; + } + + /* Read the old volume status */ + if(pioctl(path,VIOCGETVOLSTAT,&a_params,1) == -1) { + free(a_params.out); + return -1; + } + + insize = sizeof(struct VolumeStatus) + strlen(path) + 2; + + a_params.in_size = ((MAXSIZE < insize) ? MAXSIZE : insize); + a_params.out_size = 0; + a_params.in = a_params.out; + a_params.out = NULL; + + vs = (struct VolumeStatus *) a_params.in; + vs->MaxQuota = maxQuota; + + if(pioctl(path,VIOCSETVOLSTAT,&a_params,1) == -1) { + free(a_params.in); + return -1; + } + + free(a_params.in); + return 0; +} + +#ifdef __hpux +int sigvec( int sig, struct sigvec *vec, struct sigvec *ovec ) +{ + return sigvector(sig, vec, ovec); +} +#endif + +#ifdef STAND_ALONE +int main(int argc, char **argv) +{ + int usage, limit; + + if (afs_check()) { + if (afs_getquota("/afs/ifh.de/user/z/zorner", &limit, &usage) == 0) { + printf("limit=%d usage=%d\n", limit, usage); + } + else + perror("Not an AFS filesystem"); + } + else { + printf("No AFS available\n"); + } +} +#endif + +/* + * Compiler options for standalone compilation + */ + +/* +AIX: +cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb -lroken -lld + +IRIX: +cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb \ + -Wl,-rpath -Wl,/products/security/athena/lib + +Linux: +cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb \ + -Wl,-rpath -Wl,/products/security/athena/lib + +HP-UX: +cc -Ae afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb + +Solaris: (Workshop compiler) +cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb \ + -Wl,-R -Wl,/products/security/athena/lib + +SunOS: +acc afsquota.c -U__STDC__ -DSunOS4 \ + -L/products/security/athena/lib -lkafs -ldes -lkrb +*/ diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..acea728 --- /dev/null +++ b/contrib/README @@ -0,0 +1,15 @@ +Perl Quota Module: Contributions +-------------------------------- + +This directory is not an integral part of the module. You'll find here +some scripts that demonstrate it's usage and purpose. Please don't +contact me if you have questions - address the various authors directly. + +Feel free to send me more (short) scripts if you want them included here +in a future release. + + +THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + diff --git a/contrib/mount-list-qcarg.pl b/contrib/mount-list-qcarg.pl new file mode 100644 index 0000000..b1fb2fd --- /dev/null +++ b/contrib/mount-list-qcarg.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl + +use blib; +use Quota; + +my($fsname,$path,$fstyp); + +if(!Quota::setmntent()) { + while(($fsname,$path,$fstyp) = Quota::getmntent()) + { + push(@Mtab, "#$fsname#$path#$fstyp#"); + } +} +Quota::endmntent(); + +foreach (@Mtab) +{ + $path = (split(/#/))[2]; + $qcarg = Quota::getqcarg($path); + $qcarg = "*UNDEF*" unless defined $qcarg; + $dev = (stat($path))[0]; + print "${_}$qcarg#$dev\n"; +} diff --git a/contrib/mount-list.pl b/contrib/mount-list.pl new file mode 100644 index 0000000..3839030 --- /dev/null +++ b/contrib/mount-list.pl @@ -0,0 +1,15 @@ +#!/usr/bin/perl + +use blib; +use Quota; + +my($fsname,$path,$fstyp); + +if(!Quota::setmntent()) { + while(($fsname,$path,$fstyp) = Quota::getmntent()) + { + print "#$fsname#$path#$fstyp#\n"; + } +} +Quota::endmntent(); + diff --git a/contrib/quotadm/README b/contrib/quotadm/README new file mode 100644 index 0000000..ebf40bb --- /dev/null +++ b/contrib/quotadm/README @@ -0,0 +1,35 @@ +Date: Sun, 20 Oct 2002 17:35:11 +0800 +From: Ziaur Rahman +To: tomzo@ +Subject: Contrib: Quota warning based on threshold. +X-Sender: ziaur.rahman +User-Agent: Internet Messaging Program (IMP) 4.0-cvs +X-Webmail-Company: QALA Singapore Pte Ltd. + +Hi, + +First, let me thank you from the bottom of my heart for writing this fantastic +module that saved my life. + +I wrote a script to send warning message if user's disk usage is close to or +over their quota limit. As I couldn't find a perl script or any script which +can handle threshold, I had to come up with this script. It uses your Quota +module and also some code snippets from test.pl that is included with the +module. Administrators can run this and get a full report of disk usage with a +predefined threshold status of all users or a particular user. They can also +use it in the crontab to send out warning messages to users whose disk usage +reached their threshold. + +Hope you consider this as worthy enough to include in your contrib directory. + +I have attached the script (tar file), with a sample quotawarn.msg file which +is a configurable template for sending mails to user. You can also download it +from: + +http://mzrahman.com/codes/quotadm.tar + +Regards, + +Ziaur Rahman +Systems Engineer +Singapore. diff --git a/contrib/quotadm/quotadm b/contrib/quotadm/quotadm new file mode 100755 index 0000000..d723787 --- /dev/null +++ b/contrib/quotadm/quotadm @@ -0,0 +1,211 @@ +#!/usr/bin/perl +# +# History: +# +# 17-10-02 Z Written. +# 20-10-02 Z Modified to give QMAIL support. +# +# Author: Ziaur Rahman <zia@qalacom.com> +# +# Synopsis: +# +# File quotadm +# +# Program Name Quota Administration (QUOTA ADMinistration). +# +# Description This script will show user quotas in the system, and if --email is mentioned +# it will email a warning msg to users when the threshold is reached or exceeded. +# +# Variables $msgfile - Default: /etc/quotawarn.msg, this file contains the email that will +# be sent to the users whose quota has reached or exceeded $threshold. +# Inside this file you can use %user to mention the Username, %print to print the +# table of quota information, %quota to how much quota is assigned to the user. +# +# $mta - Default: /usr/lib/sendmial, this is where you set the location of +# of your MTA. Currently, this script only supports SENDMAIL and QMAIL. If you +# are using QMAIL, please enter the location of your qmail-inject. +# +# $echo - Default: /bin/echo, this is the location of the echo command which +# is used to send mail for QMAIL. +# +# $threshold - Default: 90, this is the "high water mark" for the users. When +# user's disk usage equals/exceeds [>=] user's quota threshold, i.e. more +# than or equal to 90% of user's quota, then the script will send a warning +# message (/etc/quotawarn.msg) to the user. + +use Quota; + +# +# Configurable Global variables start here... +# +# Define the global variables here +$msgfile = "/etc/quotawarn.msg"; +$mta = "/usr/lib/sendmail"; # define your MTA location here. +$echo = "/bin/echo"; +$threshold = 90; +# +# Configurable Global variables end here... +# + +while(1) { + $path = "/"; + + while(1) { + $dev = Quota::getqcarg($path); + if(!$dev) { + warn "$path: mount point not found\n"; + } + last; + } + redo if !$dev; + +## Check if quotas are present on this filesystem + + if($dev =~ m#^[^/]+:#) { + print "$dev is a remote file system\n"; + last; + } + elsif(Quota::sync($dev) && ($! != 1)) { # ignore EPERM + warn "Quota::sync: ".Quota::strerr."\n"; + warn "Choose another file system - quotas not functional on this one\n"; + } + else { + #Quotas are present on this filesystem (sync ok) + last; + } +} + +sub sendemail +{ + +local ($user,$printit,$quotaa) = @_; + +$quotadec = sprintf("%0.2d",$quotaa); + +open (MSGFILE, "$msgfile") or die "can not open $msgfile\n"; +while (<MSGFILE>) { + + $line = $_; + $line =~ s/%user/$user/; + $line =~ s/%print/$printit/; + $line =~ s/%quota/$quotadec/; + $msg .= $line; + +} + +if ($mta =~ "sendmail") { + +open (SENDMAIL, "| $mta -t -n") or die "Cannot open sendmial from $sendmail\n";; +print SENDMAIL <<End_of_Mail2; +$msg + +End_of_Mail2 +close(SENDMAIL); + +} elsif ($mta =~ "qmail-inject") { + +`$echo -e "$msg" |$mta`; + +} else { + print "Sorry, I only support SENDMAIL or QMAIL at the moment to send mails\n"; +} + +} + +## Get the user quota. +sub doquota +{ + local($user,$email,$pargv) = @_; + my $thresholds = "$threshold" . "%"; + ($name,$coded_pwd,$uid,$gid,$x,$y,$z,$gcos,$home,$shell) = getpwnam($user); + ($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::query($dev, $uid); + if(defined($bc)) { + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($bt); + $bt = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $bt; + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ft); + $ft = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $ft; + + #print "Usage and limits for $user are $bc ($bs,$bh,$bt) $fc ($fs,$fh,$ft)\n\n"; + goto ALL if $pargv eq "--all"; + if ($bs != 0) { + ALL:{ + $usageinMB = $bc/1024; + $quotainMB = $bs/1024; + $percentUsage = (($bc/$bs)*100) if $bs != 0; + $perUsage = sprintf ("%.2f%s",$percentUsage,"%"); + if ($percentUsage >= $threshold) { + $diffth = ($percentUsage - $threshold); + $diffthresh = sprintf ("%.2f%s%s",$diffth,"%","+"); + } elsif ($threshold >= $percentUsage) { + $diffth = ($threshold - $percentUsage); + $diffthresh = sprintf ("%.2f%s%s",$diffth,"%","-"); + } + if ($email eq "--email") { + $printem .= sprintf ("%15s%15s%15s%15s%15s%15s\n\n","UserName","Usage(MB)","Quota(MB)","Usage(%)","ThreshHold(%)","Diff(+/-)"); + $printem .= sprintf ("%15s%15.2f%15.2f%15s%15s%15s\n",$user,$usageinMB,$quotainMB,$perUsage,$thresholds,$diffthresh); + &sendemail($user,$printem,$quotainMB) if $bs != 0; + } else { + printf ("%15s%15.2f%15.2f%15s%15s%15s\n",$user,$usageinMB,$quotainMB,$perUsage,$thresholds,$diffthresh); + } + } + } + } + else { + warn "Quota::query($dev,$uid): ",Quota::strerr,"\n\n"; + } + +} + +sub doall +{ + local($argvp,$mail) = @_; + open(PASSWD,"/etc/passwd"); + while(<PASSWD>) + { + chop; + ($userid,$dpw,$uin) = split (':',$_); + &doquota($userid,$mail,$argvp); + } + close PASSWD; + +} + +sub usage +{ + print "\nUsage: quotadm [OPTION]...\n"; + print "Show quota information of particular user or all users.\n\n"; + print "-u <username> Single user's quota information with threshold info.\n"; + print "--all-quota Show those user's quota information whose quota is on.\n"; + print "--all Show all user's quota information.\n"; + print "--email Email the user if he/she has reached/exceeded threshold for quota.\n"; + print "\n"; + print "Examples of combining OPTIONS:\n\n"; + print "Example 1: \n\"quotadm -u tom --email\" \n- will send an email to tom if his quota has reached or exceeded the threshold.\n\n"; + print "Example 2: \n\"quotadm --all-quota --email\" \n- will send an email to each user whose quota has reached or exceeded the threshold.\n"; + print "\n"; + exit; +} + + +# +# The main program starts here. +# +$argv1 = $ARGV[0]; +$argv2 = $ARGV[1]; +$argv3 = $ARGV[2]; +chomp ($argv1); +chomp ($argv2); +chomp ($argv3); + + +if ($argv1 eq "-u") { + printf ("%15s%15s%15s%15s%15s%15s\n\n","UserName","Usage(MB)","Quota(MB)","Usage(%)","ThreshHold(%)","Diff(+/-)") if $argv3 ne "--email"; + &doquota($argv2,$argv3); +} elsif ($argv1 eq "--all-quota" || $argv1 eq "--all") { + &usage if defined($argv3); + printf ("%15s%15s%15s%15s%15s%15s\n\n","UserName","Usage(MB)","Quota(MB)","Usage(%)","ThreshHold(%)","Diff(+/-)") if $argv2 ne "--email"; + &doall($argv1,$argv2); +} else { + &usage(); +} + diff --git a/contrib/quotadm/quotawarn.msg b/contrib/quotadm/quotawarn.msg new file mode 100644 index 0000000..8e7964f --- /dev/null +++ b/contrib/quotadm/quotawarn.msg @@ -0,0 +1,13 @@ +From: "Quota Administrator" <quotadmin@domain.com> +To: %user@domain.com +Subject: Warning: Your disk usage is close to or over quota!!! + +You account is extremely close to your quota limit. + +Once the quota limit is reached, no more data can be stored. That +includes you mail directories also. So, mail will not be saved also. +Consider moving some data to another location or increasing the limit. + +Please see your quota details below: + +%print diff --git a/contrib/quotamgmt/Author b/contrib/quotamgmt/Author new file mode 100644 index 0000000..37841f3 --- /dev/null +++ b/contrib/quotamgmt/Author @@ -0,0 +1,5 @@ + ______ __ _ +/_ __/_ / / (_) Oetiker, Timelord & SysMgr @ EE-Dept ETH-Zurich + / // _ \/ _ \/ / TEL:+41(0)1-6325286 FAX:+41(0)1-6321194 +/_/ \___/_.__/_/ oetiker@ee.ethz.ch http://www.ee.ethz.ch/~oetiker + diff --git a/contrib/quotamgmt/config b/contrib/quotamgmt/config new file mode 100644 index 0000000..e353aed --- /dev/null +++ b/contrib/quotamgmt/config @@ -0,0 +1,52 @@ +# user . a username
+# . g:regexp matched to gcos
+#
+# filesystem . path to a filesystem
+# . f:<regexp> (matched on mountpath)
+# . HOME filesystem holding the users homedirectory
+#
+# space . space on the fs in Bloks (1024)
+#
+# inodes . number of inodes
+#
+# tlo . 0 - NOT Started
+# . 1 - 7 Days
+#
+
+
+#user filesystem space (soft/hard) inodes (soft/hard) tlo
+
+g:. f:. 1 1 1 1 0
+g:. HOME 20000 21000 2000 2100 0
+g:,staff, HOME 30000 30000 3000 3000 0
+g:,proj, HOME 50000 51000 5000 5100 0
+g:,syst, f:. 0 0 0 0 0
+g:,ueb, HOME 0 0 0 0 0
+
+
+
+# individual settings
+
+ahartman HOME 30000 30000 2000 2000 0
+bodermat HOME 50000 50000 2000 2000 0
+cbachofn /usr/tardis/stud7 20000 20000 2000 2000 0
+cmaeder HOME 30000 30000 2000 2000 0
+csrandaz HOME 50000 50000 2000 2000 0
+dgallegr HOME 30000 30000 2000 2000 0
+dweseman HOME 40000 40000 10000 10000 0
+fpvass HOME 30000 30000 2000 2000 0
+jgfluri /usr/tardis/stud7 20000 20000 1000 1000 0
+jhaeflig HOME 150000 150000 10000 10000 0
+jpchauny HOME 50000 50000 10000 10000 0
+mastreif HOME 50000 50000 2000 2000 0
+mcgastpa HOME 40000 40000 4000 4000 0
+mdbraend HOME 60000 60000 2000 2000 0
+mtibolla HOME 40000 40000 1000 1000 0
+pmzwimpf HOME 30000 30000 3000 3000 0
+uefleisc HOME 30000 30000 3000 3000 0
+dip94w1 HOME 230000 230000 23000 23000 0
+gisrael HOME 150000 151000 15000 15000 0
+sem94w8 HOME 100000 100000 10000 10000 0
+sem94w5 HOME 60000 60000 6000 6000 0
+# till 30.3.97 because of diplom arbeit
+ktschmid HOME 60000 60000 6000 6000 0
diff --git a/contrib/quotamgmt/quotamgmt b/contrib/quotamgmt/quotamgmt new file mode 100644 index 0000000..0ad20d0 --- /dev/null +++ b/contrib/quotamgmt/quotamgmt @@ -0,0 +1,158 @@ +#!/usr/drwho/local/bin/perl -w
+use Quota;
+select(STDERR); $|=1;
+select(STDOUT);$|=1;
+setpwent;
+my($cfg) = &ReadCfg;
+my($user) = &ReadUsers;
+my($fses) = &AllQuotaFs;
+
+print "Here we go\n";
+# go through all quota fses
+foreach $fs (@{$fses}) {
+ print "FS: $fs\n";
+ for($i=0;defined($$user[$i]{user});$i++) {
+ my($block_soft,$block_hard,$inode_soft,$inode_hard,$tlo);
+ for($ii=0;defined($$cfg[$ii][0]);$ii++){
+ # try match for username
+ my($reg) = '';
+ if ($$cfg[$ii][0] =~/^g:(.+)/) {
+ $reg = $1;
+ }
+
+ next unless ($$user[$i]{user} eq $$cfg[$ii][0]) or
+ ($reg and ($$user[$i]{gcos} =~ /$reg/));
+
+ # try match for Filesystem
+ my($fsr) = '';
+ if ($$cfg[$ii][1] =~/^f:(.+)/) {
+ $fsr = $1;
+ }
+
+ my($homp) = '';
+ if ($$cfg[$ii][1] eq 'HOME') {
+ $homp = $$user[$i]{home};
+ }
+
+ next unless ($fs eq $$cfg[$ii][1]) or
+ ($fsr and ($fs =~ /$fsr/)) or
+ ($homp and ($fs eq $homp));
+
+ # if we come till here we set the defaults
+ $block_soft = $$cfg[$ii][2];
+ $block_hard = $$cfg[$ii][3];
+ $inode_soft = $$cfg[$ii][4];
+ $inode_hard = $$cfg[$ii][5];
+ $tlo = $$cfg[$ii][6];
+ }
+
+ my($qbc,$qbs,$qbh,$qic, $qis,$qih)=
+ (Quota::query($fs, $$user[$i]{uid}))[0,1,2,4,5,6];
+
+ if (not defined $qbc) {
+ $qbc=0;$qbs=0;$qbh=0;$qic=0;$qis=0;$qih=0;}
+
+ if (($qbs != $block_soft) ||
+ ($qbh != $block_hard) ||
+ ($qis != $inode_soft) ||
+ ($qih != $inode_hard)) {
+ Quota::setqlim($fs,
+ $$user[$i]{uid},
+ $block_soft, $block_hard, $inode_soft, $inode_hard, $tlo);
+ printf "%-20s %-20s %6.0f %6.0f %6.0f %6.0f\n",
+ $fs, "$$user[$i]{user}-".(split ',', "$$user[$i]{gcos}")[1],
+ $block_soft, $block_hard, $inode_soft, $inode_hard;
+ };
+
+ if (($qbc > $block_hard) ||
+ ($qic > $inode_hard)) {
+ printf "Oops %-20s %-20s %6.0f %6.0f %6.0f %6.0f\n",
+ $fs, "$$user[$i]{user}-".(split ',', "$$user[$i]{gcos}")[1],
+ $qbc, $block_hard, $qic, $inode_hard;
+ }
+
+ }
+ Quota::sync($fs);
+}
+
+
+
+exit;
+
+sub Physical{
+ my($dir)=$_[0];
+
+ while (1) {
+ if (-l $dir) { $dir=readlink($dir);}
+ else {last; }
+ }
+
+ if (! -e $dir) {
+ return -1; }
+ else {
+ return $dir;
+ }
+
+}
+
+
+sub AllQuotaFs {
+ my(@Fs);
+ my($dev, $path, $type, $opts) = ();
+ Quota::setmntent;
+
+ while (($dev, $path, $type, $opts) = Quota::getmntent()) {
+ push @Fs , $path if $opts =~ /quota/;
+ }
+ return \@Fs;
+}
+
+sub ReadCfg{
+ my(@cfg,$i);
+ $i=0;
+ my($qfs) = &AllQuotaFs;
+ my($cfgfile)="./quotacfg";
+ warn "Reading $cfgfile ...\n";
+ open (CFG, "./quotacfg") ||
+ die ("Can't open /usr/local/etc/quotacfg");
+ while (<CFG>) {
+ next if /^\s*\#/ or /^\s*$/;
+ s/^\s+//;
+ push @{$cfg[$i++]}, split (/\s+/);
+ my($entry)=$#cfg;
+
+ die ("Error on Line $.: UserName '$cfg[$entry][0]' does not make sense")
+ unless ($cfg[$entry][0] =~ /^g:/) or
+ getpwnam($cfg[$entry][0]);
+
+ die ("Error on Line $.: Filesystem '$cfg[$entry][1]' does not exist")
+ unless ($cfg[$entry][1] eq 'HOME') or
+ ($cfg[$entry][1] =~ /^f:/) or
+ grep ($cfg[$entry][1], @{$qfs});
+
+ my($i);
+ for ($i=2;$i<7;$i++) {
+ die ("Error on Line $.: Element $1 should be a number\n")
+ unless $cfg[$entry][$i] =~ /^\d+$/ ;
+
+ }
+ }
+ close CFG;
+ return \@cfg;
+}
+
+sub ReadUsers {
+ my(@Users);
+ warn "Reading Userdata ...\n";
+ while (($user,$uid,$gcos,$home)=(getpwent)[0,2,6,7]) {
+ %{$Users[$#Users+1]} =
+ ( 'user' => $user,
+ 'uid' => $uid,
+ 'gcos' => $gcos,
+ 'home' => Quota::getqcarg(&Physical($home)));
+# last if $#Users>50;
+ }
+ return \@Users;
+}
+
+
diff --git a/contrib/report-quota b/contrib/report-quota new file mode 100644 index 0000000..a50930e --- /dev/null +++ b/contrib/report-quota @@ -0,0 +1,113 @@ +#!/usr/local/bin/perl + +# Report Quota +# +# Please keep my name associated with this program. If you add features to it, +# please drop me a copy. +# +# This is a little util I wrote to report users who have NO quotas. +# use at your own risk as I could only test on a BSDI 2.1 machine +# It does not modify any quotas, just reads each users quota... +# And email you the results. +# +# Written by Jim Hribnak (hribnak@nucleus.com) Aug 28 1997 +# + +use Quota; + +$mailprog = '/usr/sbin/sendmail'; # change path to sendmail +$email = 'hribnak@nucleus.com'; # put your email address here +$from = 'root@nucleus.com'; # put root@yourdomain here +$subject = "User Quota Report\n"; +$dev1= "/"; #filesystem with quota +$dev2="/usr"; #filesystem with quota +$dev3="/bbsusers"; #filesystem with quota +&Email; + +# +# uncomment format if you wish to use this header +# + +#format STDOUT_TOP = +#Quota's for @<<<<<<<<<<<<<<<< UID[@>>>>>>] +#$user, $uid +# Blocks Quota Quota +#Filesystem Used Limit +#---------------------------------------------------------------------------- +#. + +format STDOUT = +@<<<<<<<<< @###### @>>>>>> @>>>>>> Username: @<<<<<<<<<<<<<<<< +$dev, $bc, $bs, $bh, $user +. +open (PW, "/etc/passwd") || die "Error opening password file"; +while (<PW>) { +($user,$passwd,$uid)=split(/:/); + +# +# Change $dev to match a filesystem with Quotas +# + +$dev=$dev1; + +($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::query($dev, $uid); +if(defined($bc)) { + +&Sub1; + +write ; +} +else { + warn Quota::strerr,"\n\n"; +} +# +# Change $dev to match a filesystem with Quotas +# + +$dev=$dev2; +($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::query($dev, $uid); +if(defined($bc)) { +&Sub1; +write; +} +else { + warn Quota::strerr,"\n\n"; +} +# +# Change $dev to match a filesystem with Quotas +# +$dev=$dev3; +($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::query($dev, $uid); +if(defined($bc)) { +&Sub1; +write; +print "----------------------------------------------------------------------------"; +print "\n"; +} +else { + warn Quota::strerr,"\n\n"; +} + +} + +close(MAIL); +exit; + +sub Sub1 +{ +if (($bh eq "0") && ($bs eq "0")) { + $bh = "None"; + $bs = "None"; + print MAIL "$user does not have a quota on $dev\n" + } +} + +sub Email +{ +open(MAIL,"|$mailprog -t"); # open accessto Sendmail +print MAIL "To: $email\n"; +print MAIL "From: $from\n"; +print MAIL "Subject: $subject \n\n"; +print MAIL "\n"; +} + diff --git a/contrib/test-group.pl b/contrib/test-group.pl new file mode 100755 index 0000000..24f2393 --- /dev/null +++ b/contrib/test-group.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# +# testing group quota support -tom Apr/02/1999 + +use blib; +use Quota; + +## +## insert your test case constants here: +## +$path = "."; +$ugid = 2001; +$dogrp = 1; +@setq = qw(123 124 51 52); + + +$typnam = ($dogrp ? "group" : "user"); +$dev = Quota::getqcarg($path); +die "$path: mount point not found\n" unless $dev; +print "Using device/argument \"$dev\"\n"; + +if(Quota::sync($dev) && ($! != 1)) { + die "Quota::sync: ".Quota::strerr."\n"; +} + +print "\nQuery this fs with $typnam id $ugid\n"; +($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::query($dev,$ugid,$dogrp); +if(defined($bc)) { + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($bt); + $bt = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $bt; + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ft); + $ft = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $ft; + + print "$typnam usage and limits are $bc ($bs,$bh,$bt) $fc ($fs,$fh,$ft)\n\n"; +} +else { + die "Quota::query($dev,$ugid,$dogrp): ",Quota::strerr,"\n"; +} + +## +## set quota block & file limits for user +## + +Quota::setqlim($dev, $ugid, @setq, 1, $dogrp) && die Quota::strerr,"\n"; +print "$typnam quotas set for id $ugid\n"; + +Quota::sync($dev) && ($! != 1) && die "Quota::sync: ".Quota::strerr."\n"; + + diff --git a/hints/aix_4_1.h b/hints/aix_4_1.h new file mode 100644 index 0000000..f7908b3 --- /dev/null +++ b/hints/aix_4_1.h @@ -0,0 +1,45 @@ +/* + * Configuration for AIX 4.1 + * + * For AFS support look at the end of this file + */ + +/* See hints/none.h for a complete list of options with explanations */ + + +#include <sys/param.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include "include/rquota.h" + +#include <jfs/quota.h> +#include <sys/mntctl.h> +#include <sys/vmount.h> + +#define AIX +#define Q_CTL_V2 + +#define Q_DIV(X) (X) +#define Q_MUL(X) (X) +#define DEV_QBSIZE DEV_BSIZE + +#define CADR (caddr_t) + +#define GQR_STATUS status +#define GQR_RQUOTA getquota_rslt_u.gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_ihardlimit +#define QS_FSOFT dqb_isoftlimit +#define QS_FCUR dqb_curinodes +#define QS_BTIME dqb_btime +#define QS_FTIME dqb_itime diff --git a/hints/bsd.h b/hints/bsd.h new file mode 100644 index 0000000..c31ff76 --- /dev/null +++ b/hints/bsd.h @@ -0,0 +1,65 @@ +/* + * Configuration example for BSD-based systems - + * BSDi, FreeBSD, NetBSD, OpenBSD + * + * Ported to BSDI 2.0 by Jim Hribnak (hribnak@nucleus.com) Aug 28 1997 + * with the help of the original author Tom Zoerner + * OpenBSD 2.0 mods provided by James Shelburne (reilly@eramp.net) + * FreeBSD mods provided by Kurt Jaeger <pi@complx.LF.net> + * and Jon Schewe <schewe@tcfreenet.org> + * NetBSD mods and merge of *BSD-related hints provided by + * Jaromir Dolecek <jdolecek@NetBSD.org> + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <fstab.h> +#include <ufs/ufs/quota.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpc/svc.h> + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <rpcsvc/rquota.h> +#else /* BSDi */ +#include "include/rquota.h" +#endif + +#include <sys/socket.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> + +#define Q_DIV(X) ((X) / 2) +#define Q_MUL(X) ((X) * 2) +#define DEV_QBSIZE DEV_BSIZE +#define Q_CTL_V2 +#define Q_SETQLIM Q_SETQUOTA +#define CADR (caddr_t) + +#define QCARG_MNTPT + +#define MY_XDR + +#define NO_MNTENT + +#define GQR_STATUS status +#define GQR_RQUOTA getquota_rslt_u.gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#if defined(__APPLE__) +#define QS_BCUR dqb_curbytes +#else +#define QS_BCUR dqb_curblocks +#endif +#define QS_FHARD dqb_ihardlimit +#define QS_FSOFT dqb_isoftlimit +#define QS_FCUR dqb_curinodes +#define QS_BTIME dqb_btime +#define QS_FTIME dqb_itime + diff --git a/hints/dec_osf.h b/hints/dec_osf.h new file mode 100644 index 0000000..717b5e8 --- /dev/null +++ b/hints/dec_osf.h @@ -0,0 +1,44 @@ +/* + * Configuration for DEC OSF/1 V3.2 - 4.0 + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <sys/types.h> +#include <sys/param.h> +#include <ufs/quota.h> +#include <sys/mount.h> +#include <malloc.h> +#include <alloca.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/rquota.h> +#include <sys/socket.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> + +#define Q_DIV(X) (X) +#define Q_MUL(X) (X) +#define DEV_QBSIZE DEV_BSIZE +#define Q_CTL_V2 +#define CADR + +#define NO_MNTENT +#define OSF_QUOTA +extern char *getvfsbynumber(); /* prototype missing!? */ + +#define GQR_STATUS gqr_status +#define GQR_RQUOTA gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_ihardlimit +#define QS_FSOFT dqb_isoftlimit +#define QS_FCUR dqb_curinodes +#define QS_BTIME dqb_btime +#define QS_FTIME dqb_itime + diff --git a/hints/hpux.h b/hints/hpux.h new file mode 100644 index 0000000..f3bbb49 --- /dev/null +++ b/hints/hpux.h @@ -0,0 +1,45 @@ +/* + * Configuration for HP-UX 9.0.x & HP-UX 10.10 & HP-UX 10.20 & HP-UX 11.00 + * + * For AFS support look at the end of this file + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <sys/param.h> +#include <sys/quota.h> +#include <sys/vfs.h> +#include <mntent.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/rquota.h> +#include <sys/socket.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> + +#define Q_DIV(X) (X) +#define Q_MUL(X) (X) +#define DEV_QBSIZE DEV_BSIZE +#define CADR (caddr_t) + +#define MNTENT mntent + +/* HP-UX has no shared librpcsvc. So we need to include the + * XDR routines supplied with this module. + */ +#define MY_XDR + +#define GQR_STATUS gqr_status +#define GQR_RQUOTA gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_fhardlimit +#define QS_FSOFT dqb_fsoftlimit +#define QS_FCUR dqb_curfiles +#define QS_BTIME dqb_btimelimit +#define QS_FTIME dqb_ftimelimit diff --git a/hints/irix_5.h b/hints/irix_5.h new file mode 100644 index 0000000..b1855b0 --- /dev/null +++ b/hints/irix_5.h @@ -0,0 +1,39 @@ +/* + * Configuration for IRIX 5.3 + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/quota.h> +#include <mntent.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/rquota.h> +#include <sys/socket.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> + +#define Q_DIV(X) (X) +#define Q_MUL(X) (X) +#define DEV_QBSIZE DEV_BSIZE +#define CADR (caddr_t) + +#define MNTENT mntent + +#define GQR_STATUS gqr_status +#define GQR_RQUOTA gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_fhardlimit +#define QS_FSOFT dqb_fsoftlimit +#define QS_FCUR dqb_curfiles +#define QS_BTIME dqb_btimelimit +#define QS_FTIME dqb_ftimelimit + diff --git a/hints/irix_6.h b/hints/irix_6.h new file mode 100644 index 0000000..e5f36fc --- /dev/null +++ b/hints/irix_6.h @@ -0,0 +1,51 @@ +/* + * Configuration for 6.2 - 6.4 + * (the only difference to IRIX 5 is XFS and AFS support) + * + * If you use XFS with IRIX 6.2 you *must* install the latest xfs patch sets! + * For AFS support look at the end of this file + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +#include <sys/param.h> +#include <sys/types.h> +/* For IRIX 6.5.1 you need the following hack */ +#define __SYS_SEMA_H__ /* prevent sys/sema.h from being loaded */ +#include <sys/quota.h> +#include <mntent.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/rquota.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> + +#define Q_DIV(X) (X) +#define Q_MUL(X) (X) +#define DEV_QBSIZE DEV_BSIZE +#define CADR (caddr_t) + +#define MNTENT mntent + +#define GQR_STATUS gqr_status +#define GQR_RQUOTA gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_fhardlimit +#define QS_FSOFT dqb_fsoftlimit +#define QS_FCUR dqb_curfiles +#define QS_BTIME dqb_btimelimit +#define QS_FTIME dqb_ftimelimit + +/* optional: for support of SGI XFS file systems - comment out if not needed */ +#define SGI_XFS +#define QX_DIV(X) (X) +#define QX_MUL(X) (X) diff --git a/hints/linux.h b/hints/linux.h new file mode 100644 index 0000000..8f739fa --- /dev/null +++ b/hints/linux.h @@ -0,0 +1,93 @@ +/* + * Configuration for Linux - kernel version 2.0.22 and later + * + * For AFS support look at the end of this file + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <sys/param.h> +#include <sys/types.h> +/* #include <linux/types.h> */ +/* <asm/types.h> is required only on some distributions (Debian 2.0, RedHat) + if your's doesn't have it you can simply remove the following line */ +#include <asm/types.h> +#include <sys/syscall.h> +#include <mntent.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/rquota.h> +/* #include "include/rquota.h" */ +#include <sys/socket.h> +#include <netdb.h> + +#include <string.h> +#include <stdio.h> + +/* definitions from sys/quota.h */ +#define USRQUOTA 0 /* element used for user quotas */ +#define GRPQUOTA 1 /* element used for group quotas */ + +/* + * Command definitions for the 'quotactl' system call. + * The commands are broken into a main command defined below + * and a subcommand that is used to convey the type of + * quota that is being manipulated (see above). + */ +#define SUBCMDMASK 0x00ff +#define SUBCMDSHIFT 8 +#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK)) + +/* declare an internal version of the quota block struct */ +typedef unsigned int qsize_t; +struct dqblk { + qsize_t dqb_ihardlimit; /* absolute limit on allocated inodes */ + qsize_t dqb_isoftlimit; /* preferred inode limit */ + qsize_t dqb_curinodes; /* current # allocated inodes */ + qsize_t dqb_bhardlimit; /* absolute limit on disk blks alloc */ + qsize_t dqb_bsoftlimit; /* preferred limit on disk blks */ + qsize_t dqb_curblocks; /* current block count */ + time_t dqb_btime; /* time limit for excessive disk use */ + time_t dqb_itime; /* time limit for excessive inode use */ +}; +/* you can use this switch to hard-wire the quota API if it's not identified correctly */ +/* #define LINUX_API_VERSION 1 */ /* API range [1..3] */ + +int linuxquota_query( const char * dev, int uid, int isgrp, struct dqblk * dqb ); +int linuxquota_setqlim( const char * dev, int uid, int isgrp, struct dqblk * dqb ); +int linuxquota_sync( const char * dev, int isgrp ); + + +#define Q_DIV(X) (X) +#define Q_MUL(X) (X) +#define DEV_QBSIZE 1024 + +#define Q_CTL_V3 +#define CADR (caddr_t) + +#define MY_XDR + +#define MNTENT mntent + +#define GQR_STATUS status +#define GQR_RQUOTA getquota_rslt_u.gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_ihardlimit +#define QS_FSOFT dqb_isoftlimit +#define QS_FCUR dqb_curinodes +#define QS_BTIME dqb_btime +#define QS_FTIME dqb_itime + +/* uncomment this is you're using NFS with a version of the quota tools < 3.0 */ +/* #define LINUX_RQUOTAD_BUG */ + +/* optional: for support of SGI XFS file systems - comment out if not needed */ +#define SGI_XFS +#define QX_DIV(X) ((X) >> 1) +#define QX_MUL(X) ((X) << 1) +#include "include/quotaio_xfs.h" + diff --git a/hints/none.h b/hints/none.h new file mode 100644 index 0000000..9febfe5 --- /dev/null +++ b/hints/none.h @@ -0,0 +1,127 @@ +/* + * Configuration options + */ + +#include <sys/param.h> + +/* Defines all kinds of standard types (e.g. ulong). You may have to add + * types.h include files from other /usr/include/ subdirectories */ +#include <sys/types.h> + +/* This is needed for the quotactl syscall. See man quotactl(2) */ +#include <ufs/quota.h> + +/* This is needed for the mntent library routines. See man getmntent(3) + another probable name is mnttab or mtab. Basically that's the name + of the file where mount(1m) keeps track of the current mounts. + See FILES section of man mount for the name of that file */ +#include <mntent.h> + +/* See includes list man callrpc(3) and man rquota(3) */ +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> + +/* Select one of the following, preferring the first */ +#include <rpcsvc/rquota.h> /**/ +/* #include "include/rquota.h" /**/ + +/* See man socket(2) and man gethostbyname(3) */ +#include <sys/socket.h> +#include <netdb.h> + +/* Needed for definition of type FILE for set/getmntent(3) routines */ +#include <stdio.h> + +/* Needed (at least) for memcpy */ +#include <string.h> + +/* These factors depend on the blocksize of your filesystem. + Scale it in a way that quota values are in kB */ +#define Q_DIV(X) ((X) / 2) +#define Q_MUL(X) ((X) * 2) + +/* Specify what parameters the quotactl call expects (see man quotactl) */ +/* group quotas are supported only with BSD and Linux (see INSTALL) */ + +/* BSD style: quotactl(dev, QCMD(Q_GETQUOTA, USRQUOTA), uid, &dqblk); */ +/* #define Q_CTL_V2 */ + +/* Linux special: quotactl(QCMD(Q_GETQUOTA, USRQUOTA), dev, uid, &dqblk); */ +/* #define Q_CTL_V3 */ + +/* Solaris uses ioctl() instead of quotactl() */ +/* #define USE_IOCTL */ + +/* if none of the above defined: + * old style: quotactl(Q_GETQUOTA, dev, uid, CADR &dqblk); */ + +/* Normally quota should be reported in file system block sizes. + * On Linux though all values are converted to 1k blocks. So we + * must not use DEV_BSIZE (usually 512) but 1024 instead. On all + * other systems use the file system block size. This value is + * used only with RPC, else only Q_DIV and Q_MUL are relevant. */ +#define DEV_QBSIZE DEV_BSIZE + +/* Turn off attempt to convert remote quota block reports to 1k sizes. + * This assumes that the remote system always reports in 1k blocks. + * Only needed when the remote system also reports a bogus block + * size value in the rquota structure (like Linux does). */ +/* #define LINUX_RQUOTAD_BUG /**/ + +/* Some systems need to cast the dqblk structure + Do change only if your compiler complains */ +#define CADR (caddr_t) + +/* define if you don't want the RPC query functionality, + i.e. you want to operate on the local host only */ +/* #define NO_RPC /**/ + +/* This is for systems that lack librpcsvc and don't have xdr_getquota_args + et. al. in libc either. If you do have /usr/include/rpcsvc/rquota.x + you can generate these routines with rpcgen, too */ +/* #define MY_XDR /**/ + +/* needed only if MOUNTED is not defined in <mnttab.h> (see above) */ +/* define MOUNTED mnttab /**/ + +/* name of the structure used by getmntent(3) */ +#define MNTENT mntent + +/* on some systems setmntent/endmntend do not exist */ +/* #define NO_OPEN_MNTTAB /**/ + +/* if your system doesn't have /etc/mnttab, and hence no getmntent, + use getmntinfo instead then (e.g. in OSF) */ +/* #define NO_MNTENT /**/ + +/* name of the status entry in struc getquota_rslt and name of the struct + * or union that contains the quota values. See include <rpcsvc/rquota.h> + * or "include/rquota.h" if you're using MY_XDR */ +#define GQR_STATUS gqr_status +#define GQR_RQUOTA gqr_rquota + +/* members of the dqblk structure, see the include named in man quotactl */ +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_fhardlimit +#define QS_FSOFT dqb_fsoftlimit +#define QS_FCUR dqb_curfiles +#define QS_BTIME dqb_btimelimit +#define QS_FTIME dqb_ftimelimit + +/* SFIO_VERSION should get defined automatically if sfio routines + * are used with Perl instead of stdio; but you might wanna define + * it here if you are using PerlIO and experience problems with + * Quota::getqcarg() or the Quota::getmntent() family. + * If PerlIO is used, PERLIO_IS_STDIO is not defined */ +/* #ifndef PERLIO_IS_STDIO /**/ +/* #define SFIO_VERSION x.x /**/ +/* #endif /**/ + + +/* If you have AFS (e.g. arla-0.13) then modify the lines below + * and insert your paths to the Kerberos libraries and header files. + * Depending on your compiler you may have to change the compiler + * and linker arguments. See man cc(1) + */ diff --git a/hints/solaris_2.h b/hints/solaris_2.h new file mode 100644 index 0000000..b74726b --- /dev/null +++ b/hints/solaris_2.h @@ -0,0 +1,56 @@ +/* + * Configuration for Solaris 2.4 & 2.5.x & 2.6 - NOT 2.7 + * (this is for use with SYSV flavour, not /usr/bsd) + * + * For AFS support look at the end of this file + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <sys/param.h> +#include <sys/fs/ufs_quota.h> +#include <sys/mnttab.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/rquota.h> +#include <sys/socket.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> + +#define USE_IOCTL +#define Q_DIV(X) ((X) / 2) +#define Q_MUL(X) ((X) * 2) +#define DEV_QBSIZE DEV_BSIZE +#define CADR (caddr_t) + +/* Haven't found definition of quotactl-struct in any include, + just mentioned in man quotactl(7) */ +struct quotactl { + int op; + uid_t uid; + caddr_t addr; +}; + +#define NO_OPEN_MNTTAB +#define MOUNTED MNTTAB +#define MNTENT mnttab + +/* + * Solaris seems to lack xdr routines for rquota. Use my own. + */ +#define MY_XDR + +#define GQR_STATUS status +#define GQR_RQUOTA getquota_rslt_u.gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_fhardlimit +#define QS_FSOFT dqb_fsoftlimit +#define QS_FCUR dqb_curfiles +#define QS_BTIME dqb_btimelimit +#define QS_FTIME dqb_ftimelimit diff --git a/hints/sunos_4_1.h b/hints/sunos_4_1.h new file mode 100644 index 0000000..0f7937f --- /dev/null +++ b/hints/sunos_4_1.h @@ -0,0 +1,40 @@ +/* + * Configuration for SunOS 4.1.3 + * + * For AFS support look at the end of this file + * For arla AFS an ANSI C compiler is required (SC1.0 acc or gcc) + */ + +/* See hints/none.h for a complete list of options with explanations */ + +#include <sys/param.h> +#include <ufs/quota.h> +#include <mntent.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/rquota.h> +#include <sys/socket.h> +#include <netdb.h> + +#include <strings.h> +#include <stdio.h> + +#define Q_DIV(X) ((X) / 2) +#define Q_MUL(X) ((X) * 2) +#define DEV_QBSIZE DEV_BSIZE +#define CADR + +#define MNTENT mntent + +#define GQR_STATUS gqr_status +#define GQR_RQUOTA gqr_rquota + +#define QS_BHARD dqb_bhardlimit +#define QS_BSOFT dqb_bsoftlimit +#define QS_BCUR dqb_curblocks +#define QS_FHARD dqb_fhardlimit +#define QS_FSOFT dqb_fsoftlimit +#define QS_FCUR dqb_curfiles +#define QS_BTIME dqb_btimelimit +#define QS_FTIME dqb_ftimelimit diff --git a/include/afsquota.h b/include/afsquota.h new file mode 100644 index 0000000..0e4cc01 --- /dev/null +++ b/include/afsquota.h @@ -0,0 +1,7 @@ +/* + * prototype declarations for AFS quota interface + */ + +int afs_check(void); +int afs_getquota(char *path, int *maxQuota, int *blocksUsed); +int afs_setqlim(char *path, int maxQuota); diff --git a/include/quotaio_xfs.h b/include/quotaio_xfs.h new file mode 100644 index 0000000..2010856 --- /dev/null +++ b/include/quotaio_xfs.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ +#ifndef _QUOTAIO_XFS_H +#define _QUOTAIO_XFS_H + +#include <linux/types.h> + +#define XQM_CMD(cmd) ( ('X'<<8)+(cmd) ) +#define IS_XQM_CMD(cmd) ( ((int)(cmd)>>8) == 'X' ) + +/* + * Disk quota - quotactl(2) commands for XFS Quota Manager (XQM). + */ +#define Q_XQUOTAON XQM_CMD(0x1) /* enable quota accounting/enforcement */ +#define Q_XQUOTAOFF XQM_CMD(0x2) /* disable quota accounting/enforcement */ +#define Q_XGETQUOTA XQM_CMD(0x3) /* get disk limits & usage */ +#define Q_XSETQLIM XQM_CMD(0x4) /* set disk limits only */ +#define Q_XGETQSTAT XQM_CMD(0x5) /* returns fs_quota_stat_t struct */ +#define Q_XQUOTARM XQM_CMD(0x6) /* free quota files' space */ + +/* + * fs_disk_quota structure: + * + * This contains the current quota information regarding a user/proj/group. + * It is 64-bit aligned, and all the blk units are in BBs (Basic Blocks) of + * 512 bytes. + */ +#define FS_DQUOT_VERSION 1 /* fs_disk_quota.d_version */ +typedef struct fs_disk_quota { + __s8 d_version; /* version of this structure */ + __s8 d_flags; /* XFS_{USER,PROJ,GROUP}_QUOTA */ + __u16 d_fieldmask; /* field specifier */ + __u32 d_id; /* user, project, or group ID */ + __u64 d_blk_hardlimit; /* absolute limit on disk blks */ + __u64 d_blk_softlimit; /* preferred limit on disk blks */ + __u64 d_ino_hardlimit; /* maximum # allocated inodes */ + __u64 d_ino_softlimit; /* preferred inode limit */ + __u64 d_bcount; /* # disk blocks owned by the user */ + __u64 d_icount; /* # inodes owned by the user */ + __s32 d_itimer; /* zero if within inode limits */ + /* if not, we refuse service */ + __s32 d_btimer; /* similar to above; for disk blocks */ + __u16 d_iwarns; /* # warnings issued wrt num inodes */ + __u16 d_bwarns; /* # warnings issued wrt disk blocks */ + __s32 d_padding2; /* padding2 - for future use */ + __u64 d_rtb_hardlimit; /* absolute limit on realtime blks */ + __u64 d_rtb_softlimit; /* preferred limit on RT disk blks */ + __u64 d_rtbcount; /* # realtime blocks owned */ + __s32 d_rtbtimer; /* similar to above; for RT disk blks */ + __u16 d_rtbwarns; /* # warnings issued wrt RT disk blks */ + __s16 d_padding3; /* padding3 - for future use */ + char d_padding4[8]; /* yet more padding */ +} fs_disk_quota_t; + +/* + * These fields are sent to Q_XSETQLIM to specify fields that need to change. + */ +#define FS_DQ_ISOFT (1<<0) +#define FS_DQ_IHARD (1<<1) +#define FS_DQ_BSOFT (1<<2) +#define FS_DQ_BHARD (1<<3) +#define FS_DQ_RTBSOFT (1<<4) +#define FS_DQ_RTBHARD (1<<5) +#define FS_DQ_LIMIT_MASK (FS_DQ_ISOFT | FS_DQ_IHARD | FS_DQ_BSOFT | \ + FS_DQ_BHARD | FS_DQ_RTBSOFT | FS_DQ_RTBHARD) +/* + * These timers can only be set in super user's dquot. For others, timers are + * automatically started and stopped. Superusers timer values set the limits + * for the rest. In case these values are zero, the DQ_{F,B}TIMELIMIT values + * defined below are used. + * These values also apply only to the d_fieldmask field for Q_XSETQLIM. + */ +#define FS_DQ_BTIMER (1<<6) +#define FS_DQ_ITIMER (1<<7) +#define FS_DQ_RTBTIMER (1<<8) +#define FS_DQ_TIMER_MASK (FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER) + +/* + * The following constants define the default amount of time given a user + * before the soft limits are treated as hard limits (usually resulting + * in an allocation failure). These may be modified by the quotactl(2) + * system call with the Q_XSETQLIM command. + */ +#define DQ_FTIMELIMIT (7 * 24*60*60) /* 1 week */ +#define DQ_BTIMELIMIT (7 * 24*60*60) /* 1 week */ + +/* + * Various flags related to quotactl(2). Only relevant to XFS filesystems. + */ +#define XFS_QUOTA_UDQ_ACCT (1<<0) /* user quota accounting */ +#define XFS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */ +#define XFS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */ +#define XFS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */ + +#define XFS_USER_QUOTA (1<<0) /* user quota type */ +#define XFS_PROJ_QUOTA (1<<1) /* (IRIX) project quota type */ +#define XFS_GROUP_QUOTA (1<<2) /* group quota type */ + +/* + * fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system. + * Provides a centralized way to get meta infomation about the quota subsystem. + * eg. space taken up for user and group quotas, number of dquots currently + * incore. + */ +#define FS_QSTAT_VERSION 1 /* fs_quota_stat.qs_version */ + +/* + * Some basic infomation about 'quota files'. + */ +typedef struct fs_qfilestat { + __u64 qfs_ino; /* inode number */ + __u64 qfs_nblks; /* number of BBs 512-byte-blks */ + __u32 qfs_nextents; /* number of extents */ +} fs_qfilestat_t; + +typedef struct fs_quota_stat { + __s8 qs_version; /* version number for future changes */ + __u16 qs_flags; /* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */ + __s8 qs_pad; /* unused */ + fs_qfilestat_t qs_uquota; /* user quota storage information */ + fs_qfilestat_t qs_gquota; /* group quota storage information */ + __u32 qs_incoredqs; /* number of dquots incore */ + __s32 qs_btimelimit; /* limit for blks timer */ + __s32 qs_itimelimit; /* limit for inodes timer */ + __s32 qs_rtbtimelimit; /* limit for rt blks timer */ + __u16 qs_bwarnlimit; /* limit for num warnings */ + __u16 qs_iwarnlimit; /* limit for num warnings */ +} fs_quota_stat_t; + +#endif /* _QUOTAIO_XFS_H */ diff --git a/include/rquota.h b/include/rquota.h new file mode 100644 index 0000000..ecb5b48 --- /dev/null +++ b/include/rquota.h @@ -0,0 +1,94 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _RQUOTA_H_RPCGEN +#define _RQUOTA_H_RPCGEN + +#include <rpc/rpc.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define RQ_PATHLEN 1024 + +struct getquota_args { + char *gqa_pathp; + int gqa_uid; +}; +typedef struct getquota_args getquota_args; + +struct rquota { + int rq_bsize; + bool_t rq_active; + u_int rq_bhardlimit; + u_int rq_bsoftlimit; + u_int rq_curblocks; + u_int rq_fhardlimit; + u_int rq_fsoftlimit; + u_int rq_curfiles; + u_int rq_btimeleft; + u_int rq_ftimeleft; +}; +typedef struct rquota rquota; + +enum gqr_status { + Q_OK = 1, + Q_NOQUOTA = 2, + Q_EPERM = 3 +}; +typedef enum gqr_status gqr_status; + +struct getquota_rslt { + gqr_status status; + union { + rquota gqr_rquota; + } getquota_rslt_u; +}; +typedef struct getquota_rslt getquota_rslt; + +#define RQUOTAPROG ((unsigned long)(100011)) +#define RQUOTAVERS ((unsigned long)(1)) + +#if defined(__STDC__) || defined(__cplusplus) +#define RQUOTAPROC_GETQUOTA ((unsigned long)(1)) +extern getquota_rslt * rquotaproc_getquota_1(getquota_args *, CLIENT *); +extern getquota_rslt * rquotaproc_getquota_1_svc(getquota_args *, struct svc_req *); +#define RQUOTAPROC_GETACTIVEQUOTA ((unsigned long)(2)) +extern getquota_rslt * rquotaproc_getactivequota_1(getquota_args *, CLIENT *); +extern getquota_rslt * rquotaproc_getactivequota_1_svc(getquota_args *, struct svc_req *); +extern int rquotaprog_1_freeresult(SVCXPRT *, xdrproc_t, caddr_t); + +#else /* K&R C */ +#define RQUOTAPROC_GETQUOTA ((unsigned long)(1)) +extern getquota_rslt * rquotaproc_getquota_1(); +extern getquota_rslt * rquotaproc_getquota_1_svc(); +#define RQUOTAPROC_GETACTIVEQUOTA ((unsigned long)(2)) +extern getquota_rslt * rquotaproc_getactivequota_1(); +extern getquota_rslt * rquotaproc_getactivequota_1_svc(); +extern int rquotaprog_1_freeresult(); +#endif /* K&R C */ + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_getquota_args(XDR *, getquota_args*); +extern bool_t xdr_rquota(XDR *, rquota*); +extern bool_t xdr_gqr_status(XDR *, gqr_status*); +extern bool_t xdr_getquota_rslt(XDR *, getquota_rslt*); + +#else /* K&R C */ +extern bool_t xdr_getquota_args(); +extern bool_t xdr_rquota(); +extern bool_t xdr_gqr_status(); +extern bool_t xdr_getquota_rslt(); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_RQUOTA_H_RPCGEN */ diff --git a/include/stdio_wrap.h b/include/stdio_wrap.h new file mode 100644 index 0000000..c2abf70 --- /dev/null +++ b/include/stdio_wrap.h @@ -0,0 +1,8 @@ +/* + * Access to the std I/O routines for people using sfio + * stdio FILE operators are needed by getmntent(3) + */ + +FILE *std_fopen(const char *filename, const char *mode); +int std_fclose(FILE *fd); + diff --git a/include/vxquotactl.h b/include/vxquotactl.h new file mode 100644 index 0000000..3e68b74 --- /dev/null +++ b/include/vxquotactl.h @@ -0,0 +1,17 @@ +#ifndef INC_VXQUOTACTL_H +#define INC_VXQUOTACTL_H + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/vfs.h> +#include <sys/fs/vxio.h> +#include <sys/fs/vx_solaris.h> +#include <sys/fs/vx_machdep.h> +#include <sys/fs/vx_ioctl.h> +#include <sys/fs/vx_layout.h> +#include <sys/fs/vx_aioctl.h> +#include <sys/fs/vx_quota.h> + +int vx_quotactl(int cmd, char *mntpt, uid_t uid, void *addr); + +#endif /* INC_VXQUOTACTL_H */ diff --git a/linuxapi.c b/linuxapi.c new file mode 100644 index 0000000..55f7a0c --- /dev/null +++ b/linuxapi.c @@ -0,0 +1,373 @@ +/* +** Linux quotactl wrapper +** Required to support 3 official and intermediate quotactl() versions +*/ + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> + +#include "myconfig.h" + +/* API v1 command definitions */ +#define Q_V1_GETQUOTA 0x0300 +#define Q_V1_SYNC 0x0600 +#define Q_V1_SETQLIM 0x0700 +#define Q_V1_GETSTATS 0x0800 +/* API v2 command definitions */ +#define Q_V2_SYNC 0x0600 +#define Q_V2_SETQLIM 0x0700 +#define Q_V2_GETQUOTA 0x0D00 +#define Q_V2_GETSTATS 0x1100 +/* proc API command definitions */ +#define Q_V3_SYNC 0x800001 +#define Q_V3_GETQUOTA 0x800007 +#define Q_V3_SETQUOTA 0x800008 + +/* Interface versions */ +#define IFACE_UNSET 0 +#define IFACE_VFSOLD 1 +#define IFACE_VFSV0 2 +#define IFACE_GENERIC 3 + +/* format supported by current kernel */ +static int kernel_iface = IFACE_UNSET; + + +/* + * Quota structure used for communication with userspace via quotactl + * Following flags are used to specify which fields are valid + */ +#define QIF_BLIMITS 1 +#define QIF_SPACE 2 +#define QIF_ILIMITS 4 +#define QIF_INODES 8 +#define QIF_BTIME 16 +#define QIF_ITIME 32 +#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS) +#define QIF_USAGE (QIF_SPACE | QIF_INODES) +#define QIF_TIMES (QIF_BTIME | QIF_ITIME) +#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES) + + +/* +** Copy of struct declarations in the v2 quota.h header file +** (with structure names changed to avoid conflicts with v2 headers). +** This is required to be able to compile with v1 kernel headers. +*/ + +struct dqblk_v3 { + u_int64_t dqb_bhardlimit; + u_int64_t dqb_bsoftlimit; + u_int64_t dqb_curspace; + u_int64_t dqb_ihardlimit; + u_int64_t dqb_isoftlimit; + u_int64_t dqb_curinodes; + u_int64_t dqb_btime; + u_int64_t dqb_itime; + u_int32_t dqb_valid; +}; + +struct dqstats_v2 { + u_int32_t lookups; + u_int32_t drops; + u_int32_t reads; + u_int32_t writes; + u_int32_t cache_hits; + u_int32_t allocated_dquots; + u_int32_t free_dquots; + u_int32_t syncs; + u_int32_t version; +}; + + +struct dqblk_v2 { + unsigned int dqb_ihardlimit; + unsigned int dqb_isoftlimit; + unsigned int dqb_curinodes; + unsigned int dqb_bhardlimit; + unsigned int dqb_bsoftlimit; + qsize_t dqb_curspace; + time_t dqb_btime; + time_t dqb_itime; +}; + +struct dqblk_v1 { + u_int32_t dqb_bhardlimit; + u_int32_t dqb_bsoftlimit; + u_int32_t dqb_curblocks; + u_int32_t dqb_ihardlimit; + u_int32_t dqb_isoftlimit; + u_int32_t dqb_curinodes; + time_t dqb_btime; + time_t dqb_itime; +}; + + + +/* +** Check kernel quota version +** Taken from quota-tools 3.08 by Jan Kara <jack@suse.cz> +*/ +static void linuxquota_get_api( void ) +{ +#ifndef LINUX_API_VERSION + struct stat st; + + if (stat("/proc/sys/fs/quota", &st) == 0) { + kernel_iface = IFACE_GENERIC; + } + else { + struct dqstats_v2 v2_stats; + struct sigaction sig; + struct sigaction oldsig; + + /* This signal handling is needed because old kernels send us SIGSEGV as they try to resolve the device */ + sig.sa_handler = SIG_IGN; + sig.sa_sigaction = NULL; + sig.sa_flags = 0; + sigemptyset(&sig.sa_mask); + if (sigaction(SIGSEGV, &sig, &oldsig) < 0) { + fprintf(stderr, "linuxapi.c warning: cannot set SEGV signal handler: %s\n", strerror(errno)); + goto failure; + } + if (quotactl(QCMD(Q_V2_GETSTATS, 0), NULL, 0, (void *)&v2_stats) >= 0) { + kernel_iface = IFACE_VFSV0; + } + else if (errno != ENOSYS && errno != ENOTSUP) { + /* RedHat 7.1 (2.4.2-2) newquota check + * Q_V2_GETSTATS in it's old place, Q_GETQUOTA in the new place + * (they haven't moved Q_GETSTATS to its new value) */ + int err_stat = 0; + int err_quota = 0; + char tmp[1024]; /* Just temporary buffer */ + + if (quotactl(QCMD(Q_V1_GETSTATS, 0), NULL, 0, tmp)) + err_stat = errno; + if (quotactl(QCMD(Q_V1_GETQUOTA, 0), "/dev/null", 0, tmp)) + err_quota = errno; + + /* On a RedHat 2.4.2-2 we expect 0, EINVAL + * On a 2.4.x we expect 0, ENOENT + * On a 2.4.x-ac we wont get here */ + if (err_stat == 0 && err_quota == EINVAL) { + kernel_iface = IFACE_VFSV0; + } + else { + kernel_iface = IFACE_VFSOLD; + } + } + else { + /* This branch is *not* in quota-tools 3.08 + ** but without it quota version is not correctly + ** identified for the original SuSE 8.0 kernel */ + unsigned int vers_no; + FILE * qf; + + if ((qf = fopen("/proc/fs/quota", "r"))) { + if (fscanf(qf, "Version %u", &vers_no) == 1) { + if ( (vers_no == (6*10000 + 5*100 + 0)) || + (vers_no == (6*10000 + 5*100 + 1)) ) { + kernel_iface = IFACE_VFSV0; + } + } + fclose(qf); + } + } + if (sigaction(SIGSEGV, &oldsig, NULL) < 0) { + fprintf(stderr, "linuxapi.c warning: cannot reset signal handler: %s\n", strerror(errno)); + goto failure; + } + } + +failure: + if (kernel_iface == IFACE_UNSET) + kernel_iface = IFACE_VFSOLD; + +#else /* defined LINUX_API_VERSION */ + kernel_iface = LINUX_API_VERSION; +#endif +} + + +/* +** Wrapper for the quotactl(GETQUOTA) call. +** For API v2 the results are copied back into a v1 structure. +*/ +int linuxquota_query( const char * dev, int uid, int isgrp, struct dqblk * dqb ) +{ + int ret; + + if (kernel_iface == IFACE_UNSET) + linuxquota_get_api(); + + if (kernel_iface == IFACE_GENERIC) + { + struct dqblk_v3 dqb3; + + ret = quotactl(QCMD(Q_V3_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb3); + if (ret == 0) + { + dqb->dqb_bhardlimit = dqb3.dqb_bhardlimit; + dqb->dqb_bsoftlimit = dqb3.dqb_bsoftlimit; + dqb->dqb_curblocks = dqb3.dqb_curspace / DEV_QBSIZE; + dqb->dqb_ihardlimit = dqb3.dqb_ihardlimit; + dqb->dqb_isoftlimit = dqb3.dqb_isoftlimit; + dqb->dqb_curinodes = dqb3.dqb_curinodes; + dqb->dqb_btime = dqb3.dqb_btime; + dqb->dqb_itime = dqb3.dqb_itime; + } + } + else if (kernel_iface == IFACE_VFSV0) + { + struct dqblk_v2 dqb2; + + ret = quotactl(QCMD(Q_V2_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb2); + if (ret == 0) + { + dqb->dqb_bhardlimit = dqb2.dqb_bhardlimit; + dqb->dqb_bsoftlimit = dqb2.dqb_bsoftlimit; + dqb->dqb_curblocks = dqb2.dqb_curspace / DEV_QBSIZE; + dqb->dqb_ihardlimit = dqb2.dqb_ihardlimit; + dqb->dqb_isoftlimit = dqb2.dqb_isoftlimit; + dqb->dqb_curinodes = dqb2.dqb_curinodes; + dqb->dqb_btime = dqb2.dqb_btime; + dqb->dqb_itime = dqb2.dqb_itime; + } + } + else /* if (kernel_iface == IFACE_VFSOLD) */ + { + struct dqblk_v1 dqb1; + + ret = quotactl(QCMD(Q_V1_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb1); + if (ret == 0) + { + dqb->dqb_bhardlimit = dqb1.dqb_bhardlimit; + dqb->dqb_bsoftlimit = dqb1.dqb_bsoftlimit; + dqb->dqb_curblocks = dqb1.dqb_curblocks; + dqb->dqb_ihardlimit = dqb1.dqb_ihardlimit; + dqb->dqb_isoftlimit = dqb1.dqb_isoftlimit; + dqb->dqb_curinodes = dqb1.dqb_curinodes; + dqb->dqb_btime = dqb1.dqb_btime; + dqb->dqb_itime = dqb1.dqb_itime; + } + } + return ret; +} + +/* +** Wrapper for the quotactl(GETQUOTA) call. +** For API v2 and v3 the parameters are copied into the internal structure. +*/ +int linuxquota_setqlim( const char * dev, int uid, int isgrp, struct dqblk * dqb ) +{ + int ret; + + if (kernel_iface == IFACE_UNSET) + linuxquota_get_api(); + + if (kernel_iface == IFACE_GENERIC) + { + struct dqblk_v3 dqb3; + + dqb3.dqb_bhardlimit = dqb->dqb_bhardlimit; + dqb3.dqb_bsoftlimit = dqb->dqb_bsoftlimit; + dqb3.dqb_curspace = 0; + dqb3.dqb_ihardlimit = dqb->dqb_ihardlimit; + dqb3.dqb_isoftlimit = dqb->dqb_isoftlimit; + dqb3.dqb_curinodes = 0; + dqb3.dqb_btime = dqb->dqb_btime; + dqb3.dqb_itime = dqb->dqb_itime; + dqb3.dqb_valid = (QIF_BLIMITS | QIF_ILIMITS); + + ret = quotactl (QCMD(Q_V3_SETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb3); + } + else if (kernel_iface == IFACE_VFSV0) + { + struct dqblk_v2 dqb2; + + dqb2.dqb_bhardlimit = dqb->dqb_bhardlimit; + dqb2.dqb_bsoftlimit = dqb->dqb_bsoftlimit; + dqb2.dqb_curspace = 0; + dqb2.dqb_ihardlimit = dqb->dqb_ihardlimit; + dqb2.dqb_isoftlimit = dqb->dqb_isoftlimit; + dqb2.dqb_curinodes = 0; + dqb2.dqb_btime = dqb->dqb_btime; + dqb2.dqb_itime = dqb->dqb_itime; + + ret = quotactl (QCMD(Q_V2_SETQLIM, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb2); + } + else /* if (kernel_iface == IFACE_VFSOLD) */ + { + struct dqblk_v1 dqb1; + + dqb1.dqb_bhardlimit = dqb->dqb_bhardlimit; + dqb1.dqb_bsoftlimit = dqb->dqb_bsoftlimit; + dqb1.dqb_curblocks = 0; + dqb1.dqb_ihardlimit = dqb->dqb_ihardlimit; + dqb1.dqb_isoftlimit = dqb->dqb_isoftlimit; + dqb1.dqb_curinodes = 0; + dqb1.dqb_btime = dqb->dqb_btime; + dqb1.dqb_itime = dqb->dqb_itime; + + ret = quotactl (QCMD(Q_V1_SETQLIM, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb1); + } + + return ret; +} + +/* +** Wrapper for the quotactl(SYNC) call. +*/ +int linuxquota_sync( const char * dev, int isgrp ) +{ + int ret; + + if (kernel_iface == IFACE_UNSET) + linuxquota_get_api(); + + if (kernel_iface == IFACE_GENERIC) + { + ret = quotactl (QCMD(Q_V3_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL); + } + else if (kernel_iface == IFACE_VFSV0) + { + ret = quotactl (QCMD(Q_V2_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL); + } + else /* if (kernel_iface == IFACE_VFSOLD) */ + { + ret = quotactl (QCMD(Q_V1_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL); + } + + return ret; +} + +#if 0 +#define DEVICE_PATH "/dev/hda6" +main() +{ + struct dqblk dqb; + + linuxquota_get_api(); + printf("API=%d\n", kernel_iface); + + if (linuxquota_sync(DEVICE_PATH, FALSE) != 0) + perror("Q_SYNC"); + + if (linuxquota_query(DEVICE_PATH, getuid(), 0, &dqb) == 0) + { + printf("blocks: usage %d soft %d hard %d expire %s", + dqb.dqb_curblocks, dqb.dqb_bhardlimit, dqb.dqb_bsoftlimit, + ((dqb.dqb_btime != 0) ? (char*)ctime(&dqb.dqb_btime) : "n/a\n")); + printf("inodes: usage %d soft %d hard %d expire %s", + dqb.dqb_curinodes, dqb.dqb_ihardlimit, dqb.dqb_isoftlimit, + ((dqb.dqb_itime != 0) ? (char*)ctime(&dqb.dqb_itime) : "n/a\n")); + } + else + perror("Q_GETQUOTA"); +} +#endif + diff --git a/stdio_wrap.c b/stdio_wrap.c new file mode 100644 index 0000000..7de1a40 --- /dev/null +++ b/stdio_wrap.c @@ -0,0 +1,17 @@ +/* + * Access to the std I/O routines for people using sfio + * stdio FILE operators are needed by getmntent(3) + */ + +#include <stdio.h> + +FILE *std_fopen(const char *filename, const char *mode) +{ + return fopen(filename, mode); +} + +int std_fclose(FILE *fd) +{ + return fclose(fd); +} + @@ -0,0 +1,171 @@ +#!../../../perl + +use blib; +use Quota; + +if (! -t) { + print STDERR "\nThis is an interactive test script - input must be a tty\nExiting now.\n"; + exit; +} + +while(1) { + print "\nEnter path to get quota for (NFS possible; default '.'): "; + chomp($path = <STDIN>); + $path = "." unless $path =~ /\S/; + + while(1) { + $dev = Quota::getqcarg($path); + if(!$dev) { + warn "$path: mount point not found\n"; + if(-d $path && $path !~ m#/.$#) { + # + # try to append "/." to get past automounter fs + # + $path .= "/."; + warn "Trying $path instead...\n"; + redo; + } + } + last; + } + redo if !$dev; + print "Using device/argument \"$dev\"\n"; + +## +## Check if quotas are present on this filesystem +## + + if($dev =~ m#^[^/]+:#) { + print "Is a remote file system\n"; + last; + } + elsif(Quota::sync($dev) && ($! != 1)) { # ignore EPERM + warn "Quota::sync: ".Quota::strerr."\n"; + warn "Choose another file system - quotas not functional on this one\n"; + } + else { + print "Quotas are present on this filesystem (sync ok)\n"; + last; + } +} + +## +## call with one argument (uid defaults to getuid() +## + +print "\nQuery this fs with default uid (which is real uid) $>\n"; +($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::query($dev); +if(defined($bc)) { + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($bt); + $bt = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $bt; + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ft); + $ft = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $ft; + + print "Your usage and limits are $bc ($bs,$bh,$bt) $fc ($fs,$fh,$ft)\n\n"; +} +else { + warn "Quota::query($dev): ",Quota::strerr,"\n\n"; +} + +## +## call with two arguments +## + +{ + print "Enter a uid to get quota for: "; + chomp($uid = <STDIN>); + unless($uid =~ /^\d{1,5}$/) { + print "You have to enter a numerical uid in range 0..65535 here.\n"; + redo; + } +} + +($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::query($dev, $uid); +if(defined($bc)) { + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($bt); + $bt = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $bt; + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ft); + $ft = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $ft; + + print "Usage and limits for $uid are $bc ($bs,$bh,$bt) $fc ($fs,$fh,$ft)\n\n"; +} +else { + warn "Quota::query($dev,$uid): ",Quota::strerr,"\n\n"; +} + +## +## get quotas via RPC +## + +if($dev =~ m#^/#) { + print "Query localhost via RPC.\n"; + + ($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::rpcquery('localhost', $path); + if(defined($bc)) { + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($bt); + $bt = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $bt; + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ft); + $ft = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $ft; + + print "Your Usage and limits are $bc ($bs,$bh,$bt) $fc ($fs,$fh,$ft)\n\n"; + } + else { + warn Quota::strerr,"\n\n"; + } + print "Query localhost via RPC for $uid.\n"; + + ($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::rpcquery('localhost', $path, $uid); + if(!defined($bc)) { + warn Quota::strerr,"\n\n"; + print "Retrying with fake authentication for UID $uid.\n"; + Quota::rpcauth($uid); + ($bc,$bs,$bh,$bt,$fc,$fs,$fh,$ft) = Quota::rpcquery('localhost', $path, $uid); + Quota::rpcauth(); + } + + if(defined($bc)) { + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($bt); + $bt = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $bt; + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ft); + $ft = sprintf("%04d-%02d-%02d/%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min) if $ft; + + print "Usage and limits for $uid are $bc ($bs,$bh,$bt) $fc ($fs,$fh,$ft)\n\n"; + } + else { + warn Quota::strerr,"\n\n"; + } + +} +else { + print "Skipping RPC query test - already done above.\n\n"; +} + +## +## set quota block & file limits for user +## + +print "Enter path to set quota (empty to skip): "; +chomp($path = <STDIN>); + +if($path =~ /\S/) { + print "New quota limits bs,bh,fs,fh for $uid (empty to abort): "; + chomp($in = <STDIN>); + if($in =~ /\S/) { + $dev = Quota::getqcarg($path) || die "$path: $!\n"; + unless(Quota::setqlim($dev, $uid, split(/\s*,\s*/, $in), 1)) { + print "Quota set for $uid\n"; + } + else { + warn Quota::strerr,"\n"; + } + } +} + +## +## Force immediate update on disk +## + +if($dev !~ m#^[^/]+:#) { + Quota::sync($dev) && ($! != 1) && die "Quota::sync: ".Quota::strerr."\n"; +} + |