ProFTPD module mod_quotatab_sql

This mod_quotatab submodule is contained in the mod_quotatab_sql.c, and is found in the contrib/ area of the ProFTPD source distribution. See the mod_quotatab installation instructions.

This submodule provides the SQL database "driver" for storing quota table information in SQL tables.


Please contact TJ Saunders <tj at> with any questions, concerns, or suggestions regarding this module.


2002-04-01: Thanks to Tomasz Konefal <tomk at> for his great feedback and support in developing this module.

2002-04-03: Thanks to Noah <sitz at> for suggesting the better form of the UPDATE query.

2004-01-20: Thanks to Alex Ursu <alex.ursu at> for supplying SQL Server FREEFORM query syntax.

SQL Quota Tables

The mod_quotatab_sql module supports the "sql" string for the source-type parameter of the QuotaLimitTable and QuotaTallyTable configuration directives. If the "sql" source-type is used, then the source-info parameter must be as described below. Note that support for SQL-based quota tables requires the use of mod_sql.

mod_quotatab_sql requires five SQL-related configuration directives: SQLConnectInfo, and four SQLNamedQuery directives. The SQLConnectInfo is needed to allow the module to connect to the SQL database. The other four directives define the SQL queries that will be used by mod_quotatab for its storage/retrieval operations involving quota data.

When using SQL tables and mod_sql, the quota tables should appear in the database named by the SQLConnectInfo directive, which will probably also contain the authentication information tables. This way your proftpd-specific tables are kept together. Also, it is probably better to use a connection policy of PERSESSION, otherwise there will be more overhead associated with each new connection made to the database for each transaction; mod_quotatab makes a lot of reads and writes to its tables, each of which will require mod_quotatab_sql to use a database connection.

One SQLNamedQuery is needed to SELECT quota information from the limit table. The defined SQL statement for this must return ten values, in the following order:

A similar SQLNamedQuery is needed to SELECT quota information from the tally table; the SQL statement defined for this must return eight values, in the following order: The remaining two SQLNamedQuery directives are used to define the necessary UPDATE and INSERT operations on the tally table. Failure to define any of the four required SQLNamedQuery directives will cause an error during mod_quotatab_sql's operation.

For SQL tables, the format for the QuotaLimitTable directive is:

  QuotaLimitTable sql:/SELECT-SQLNamedQuery
And, for the QuotaTallyTable directive:
  QuotaTallyTable sql:/SELECT-SQLNamedQuery/UPDATE-SQLNamedQuery/INSERT-SQLNamedQuery

Also note that SQL-based tally tables have an issue with proper synchronization of updates, especially when multiple sessions involving the same tally are ocurring. In order to prevent the tally table from becoming out of sync, you are strongly encouraged to define a QuotaLock file.

SQL Quota Tables Example
Here are example SQLNamedQuery directives to help demonstrate how the mod_sql hooks are used by mod_quotatab. These example SQL statements assume the existence of two tables: a quotalimits table that defines limit records, and a quotatallies table that holds the current tally records. Note that these queries would appear all on a single line in your proftpd.conf.

  SQLNamedQuery get-quota-limit SELECT "name, quota_type, per_session, limit_type, bytes_in_avail, \
    bytes_out_avail, bytes_xfer_avail, files_in_avail, files_out_avail, files_xfer_avail FROM quotalimits \
    WHERE name = '%{0}' AND quota_type = '%{1}'"
This defines the SQL statement to return the required limit column values, in the necessary order. The %{0} and %{1} meta sequences will be substituted with the name being looked up (e.g. user name, primary group name, class name (if in effect), or the empty string) and the corresponding quota type (e.g. "user", "group", "class", or "all"), respectively.
  SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used, bytes_out_used, \
    bytes_xfer_used, files_in_used, files_out_used, files_xfer_used FROM quotatallies \
    WHERE name = '%{0}' AND quota_type = '%{1}'"
Very similar to the above example, the SQL statement defined here is for the tally table, rather than the limit table. The meta sequences will be substituted with the same values as above.
  SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, \
    bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, \
    files_in_used = files_in_used + %{3}, files_out_used = files_out_used + %{4}, \
    files_xfer_used = files_xfer_used + %{5} \
    WHERE name = '%{6}' AND quota_type = '%{7}'" quotatallies
This SQL statement is used to update the tally table with the current tally values for the session. The meta sequences will be substituted with the "deltas", the difference in the known versus current tallies. The reason for forming the UPDATE query this way is to provide as atomic an update operation as possible; this query will update the current values in the table at the time of the update, in order that any race condition between a SELECT and an UPDATE query on that information be reduced to as small a window as possible.
  SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4}, %{5}, %{6}, %{7}" quotatallies
This last SQL statement is used to create a new tally record, in the event that a limit record is found for the current session but no matching tally record is found. The meta sequences will be substituted with: name, quota type, upload tally in bytes, download tally in bytes, transfer tally in bytes, upload tally in files, download tally in files, and transfer tally in files, in that order. These values are initialized to zero for newly-created tally records by default.

Note: SQL Server uses a slightly different INSERT syntax. This means that if you are using mod_quotatab_sql in conjunction with the mod_sql_tds module to speak to a SQL Server, your INSERT query may need to look like:

  SQLNamedQuery insert-quota-tally FREEFORM \
    "INSERT INTO quotatallies VALUES ('%{0}','%{1}','%{2}','%{3}','%{4}','%{5}','%{6}','%{7}')"

Now, using the above defined queries, the table configuration directives would be:

  QuotaLock /var/run/ftpd/tally.lock
  QuotaLimitTable sql:/get-quota-limit
  QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally

Example Schema
Here are some example table schema for SQL-based quota tables:

mod_quotatab_sql treats any number zero or less for a bytes limit as "unlimited". Similarly, it treats a value of zero for any of the files limits as "unlimited". "Unlimited" values are ignored, in that any limit that is "unlimited" is not used in any of mod_quotatab's calculations. NULL values should be avoided whenever possible.

© Copyright 2000-2017 TJ Saunders
All Rights Reserved