summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRene Mayorga <rmayorga@debian.org>2007-07-30 17:30:49 +0000
committerRene Mayorga <rmayorga@debian.org>2007-07-30 17:30:49 +0000
commit949544810672c3a955341d390fd7b737d0a92189 (patch)
tree214ef4c0b2b3620b892c6df464d5e8254c6ebba1
[svn-inject] Installing original source of libquota-perl
-rw-r--r--CHANGES220
-rw-r--r--INSTALL101
-rw-r--r--MANIFEST33
-rw-r--r--Makefile.PL121
-rw-r--r--Quota.pm356
-rw-r--r--Quota.xs909
-rw-r--r--README281
-rw-r--r--afsquota.c172
-rw-r--r--contrib/README15
-rw-r--r--contrib/mount-list-qcarg.pl23
-rw-r--r--contrib/mount-list.pl15
-rw-r--r--contrib/quotadm/README35
-rwxr-xr-xcontrib/quotadm/quotadm211
-rw-r--r--contrib/quotadm/quotawarn.msg13
-rw-r--r--contrib/quotamgmt/Author5
-rw-r--r--contrib/quotamgmt/config52
-rw-r--r--contrib/quotamgmt/quotamgmt158
-rw-r--r--contrib/report-quota113
-rwxr-xr-xcontrib/test-group.pl49
-rw-r--r--hints/aix_4_1.h45
-rw-r--r--hints/bsd.h65
-rw-r--r--hints/dec_osf.h44
-rw-r--r--hints/hpux.h45
-rw-r--r--hints/irix_5.h39
-rw-r--r--hints/irix_6.h51
-rw-r--r--hints/linux.h93
-rw-r--r--hints/none.h127
-rw-r--r--hints/solaris_2.h56
-rw-r--r--hints/sunos_4_1.h40
-rw-r--r--include/afsquota.h7
-rw-r--r--include/quotaio_xfs.h159
-rw-r--r--include/rquota.h94
-rw-r--r--include/stdio_wrap.h8
-rw-r--r--include/vxquotactl.h17
-rw-r--r--linuxapi.c373
-rw-r--r--stdio_wrap.c17
-rwxr-xr-xtest.pl171
37 files changed, 4333 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..88c8efd
--- /dev/null
+++ b/CHANGES
@@ -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.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..e3dfa99
--- /dev/null
+++ b/INSTALL
@@ -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
diff --git a/README b/README
new file mode 100644
index 0000000..4d26ee7
--- /dev/null
+++ b/README
@@ -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);
+}
+
diff --git a/test.pl b/test.pl
new file mode 100755
index 0000000..0b80463
--- /dev/null
+++ b/test.pl
@@ -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";
+}
+