IMP Quota command driver, was Re: [dev] Bounties

Etienne Goyer etienne.goyer at linuxquebec.com
Fri Jan 30 09:07:02 PST 2004


Hi,

Following the discussion of last week concerning the command Quota
driver in IMP, I tried my hand at supporting the new style of output
that is not currently supported.  I did not include a patch, as it was
bigger than the file itself; thus I attached the command.php file
instead.

This modified command driver Work For Me (TM), in my test setup.  The
code is quite convulated and I am not really satisfied about it.  I plan
on fixing that up once I receive feedback on my current approach to the
problem.

AJ, if you want to try it out, just replace
horde/imp/lib/Quota/command.php with the attached one (make a backup
first !).  You will need to edit you horde/imp/config/servers.php
config; the quota section of your server description should look like
this :

    'quota' => array(
        'driver' => 'command',
        'params' => array(
            'new_quota' => true,
            'quota_path' => 'sudo quota',
            'partition' => '/dev/sda2'
        ),
    ), 

Replace the 'partition' value with the partition name for which you want
to retrieve for.  I suggest you use sudo to run quota, in which case the
following must be added to your /etc/sudoers (do 'visudo' to edit it) :

Cmnd_Alias QUOTA = /usr/bin/quota
apache  ALL= (ALL)NOPASSWD: QUOTA

Let me know if work for you or not.  I'll try to help you along if it
does'nt.

Thanks everybody, I await your comments.


On Fri, Jan 23, 2004 at 02:40:34PM -0500, AJ wrote:
> Let me try to explain the difference, because I don't have access to an 
> old style quota binary either.
> Here is some output from the NEW quota command:
> 
> Disk quotas for user test1 (uid 503):
>       Filesystem  blocks   quota   limit   grace   files   quota   limit
>        /dev/hda2    2724    6000    6000              32       0       0 
> 
>        /dev/hda1       1    6400    6400               1       0       0
> 
> The problem is under the filesystem heading.  Here, it displays it as a 
> device.  With the old style command, it displays it as the actual 
> filesystem, i.e. /home
>  From what I can tell (I may be wrong), the command.php quota driver 
> looks in the passwd file for the user's home directory and tries to find 
> that in the quota output, which will fail for the new quota binaries.
> This is the problem.  Did that help at all?  I hope :)

-- 
Etienne Goyer                    Linux Québec Technologies Inc.
http://www.LinuxQuebec.com       etienne.goyer at linuxquebec.com

Kernel Preemption is a bad idea. Who are the users to think their 
trivial tasks are more important than the kernel's ? 
-------------- next part --------------
<?php
/**
 * Implementation of the Quota API for IMAP servers with a unix quota command.
 * This requires a modified "quota" command that allows the httpd server
 * account to get quotas for other users.  You could use sudo, in which case 
 * you must have something similar to this in /etc/sudoers :
 *
 *    Cmnd_Alias QUOTA = /usr/bin/quota
 *    apache  ALL= (ALL)NOPASSWD: QUOTA
 *
 * It also requires that your web server and imap server be the same server or 
 * at least have shared authentication and file servers (e.g. via NIS/NFS).  And
 * last, it (as written) requires the POSIX PHP extensions.
 *
 * You must configure this in horde/imp/config/servers.php.  You must
 * pass the path to your quota and grep commands as parameters.  An example
 * config would be something like:
 *
 * 'quota' => array(
 *     'driver' => 'command',
 *     'params' => array(
 *         'quota_path' => '/usr/bin/quota',
 *         'grep_path'  => '/bin/grep'
 *     )
 * )
 *
 * If your quota is relatively recent, its output may look like this :
 * 
 * Disk quotas for user egoyer (uid 500):
 *      Filesystem  blocks   quota   limit   grace   files   quota   limit   grace
 *       /dev/sda2      17       0   10240               8       0       0
 *
 * If this is the case, you need to use new style of quota parsing.  Your 
 * configuration should look like this :
 *
 *  'quota' => array(
 *      'driver' => 'command',
 *      'params' => array(
 *          'new_quota' => true,
 *          'quota_path' => 'sudo /usr/sbin/quota',
 *          'partition' => '/dev/sda2'
 *      ),
 *  )
 *
 * The partition argument must be the partition name of the filesystem you want 
 * to get quota for.
 *
 * $Horde: imp/lib/Quota/command.php,v 1.9 2003/09/30 18:33:34 slusarz Exp $
 *
 * Copyright 2002-2003 Eric Jon Rostetter <eric.rostetter at physics.utexas.edu>
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Eric Rostetter <eric.rostetter at physics.utexas.edu>
 * @version $Revision: 1.9 $
 * @since   IMP 4.0
 * @package imp.quota
 */
class IMP_Quota_command extends IMP_Quota {
    
    /**
     * Constructor 
     *
     * @access public
     *
     * @param optional array $params  Hash containing connection parameters.
     */
    function IMP_Quota_command($params = array())
    {
        $this->_params = array(
            'quota_path' => 'quota',
            'grep_path'  => 'grep'
        );

        $this->_params = array_merge($this->_params, $params);
    }

    /**
     * Get quota information (used/allocated), in bytes.
     *
     * @access public
     *
     * @return mixed  An associative array.
     *                'limit' = Maximum quota allowed
     *                'usage' = Currently used portion of quota (in bytes)
     *                Returns PEAR_Error on failure.
     */
    function getQuota()
    {
        global $imp;

        $imap_user = strtolower($imp['user']);
        $passwd_array = posix_getpwnam($imap_user);
        $homedir = split('/', $passwd_array['dir']);
        if (isset($this->_params['new_quota'])) {
            if (!isset($this->_params['partition'])) {
                return PEAR::raiseError(_("You must specify the partition to retrieve quota for"), 'horde.error');
            }
            $cmd = $this->_params['quota_path'] . " $imap_user";
            exec($cmd, $cmd_output, $rc);
            // Output should be one line for header, then one line per partition for
            // for which there is a quota
            if ($rc == 0) {
                if ((count($cmd_output) == 1) and 
                    preg_match("/Disk quotas for user .+ \(uid \d+\): none/", $cmd_output[0])) {
                    // No quota fo this user
                    return array('usage' => 0, 'limit' => 0); // FIXME : is this corrct ?

                } else if (count($cmd_output > 1)) { // Parse quota output 
                    // Throw away header
                    array_shift($cmd_output);
                    $wrap = false;
                    $pattern = '/\s*' . str_replace('/', '\/', $this->_params['partition']) . '/';
                    foreach ($cmd_output as $line) {
                        $line = preg_replace('/^\s*/', '//', $line);
                        if ($wrap === true) {
                            // Partition name wrapped, this line is the output we are looking for
                            $fields = preg_split('/\s+/', $line);
                            if (isset($fields[0]) and isset($fields[2])) {
                                // FIXME this assume 1024 bytes per blocks
                                return array('usage' => $fields[0] * 1024, 'limit' => $fields[2] * 1024);
                            } else {
                                return PEAR::raiseError(_("Unable to retrieve quota"), 'horde.error');
                            }
                        }
                        if (preg_match($pattern, $line)) {
                            // When partition name is longer that 15 chars, fields goes on the next line
                            if (strlen($this->_params['partition']) > 15) {
                                $wrap = true;
                            } else {
                                $fields = preg_split('/\s+/', $line);
                                if (isset($fields[1]) and isset($fields[3])) {
                                    // FIXME this assume 1024 bytes per blocks
                                    return array('usage' => $fields[1] * 1024, 'limit' => $fields[3] * 1024);
                                } else {
                                    return PEAR::raiseError(_("Unable to retrieve quota"), 'horde.error');
                                }
                            }
                        }
                    }
                }
            }
                       
        } else {
            $cmdline = $this->_params['quota_path'] . " -u $imap_user | " . 
                       $this->_params['grep_path'] . " $homedir[1]";
            $junk = exec($cmdline, $quota_data, $return_code);
            if (($return_code == 0) && (count($quota_data) == 1)) {
                $quota = split("[[:blank:]]+", trim($quota_data[0]));
                return array('usage' => $quota[1] * 1024, 'limit' => $quota[2] * 1024);
            }
        }

        return PEAR::raiseError(_("Unable to retrieve quota"), 'horde.error');
    }

}


More information about the dev mailing list