dovecot: Added quota warning commands. Based lightly on a patch ...
dovecot at dovecot.org
dovecot at dovecot.org
Mon Jul 16 02:41:44 EEST 2007
details: http://hg.dovecot.org/dovecot/rev/6edca218d48a
changeset: 6032:6edca218d48a
user: Timo Sirainen <tss at iki.fi>
date: Mon Jul 16 02:41:40 2007 +0300
description:
Added quota warning commands. Based lightly on a patch by Nicolas Boullis.
diffstat:
5 files changed, 169 insertions(+), 32 deletions(-)
dovecot-example.conf | 6 +
src/plugins/quota/quota-plugin.c | 27 ++++++
src/plugins/quota/quota-private.h | 8 +
src/plugins/quota/quota.c | 156 +++++++++++++++++++++++++++++--------
src/plugins/quota/quota.h | 4
diffs (truncated from 314 to 300 lines):
diff -r b272eff19a18 -r 6edca218d48a dovecot-example.conf
--- a/dovecot-example.conf Mon Jul 16 02:00:35 2007 +0300
+++ b/dovecot-example.conf Mon Jul 16 02:41:40 2007 +0300
@@ -1083,6 +1083,12 @@ plugin {
# quota2_rule = *:storage=1048576
# Gives each user their own 100MB quota and one shared 1GB quota within
# the domain.
+ #
+ # You can execute a given command when user exceeds a specified quota limit.
+ # Each quota root has separate limits. Only the command for the first
+ # exceeded limit is excecuted, so put the highest limit first.
+ # quota_warning = storage=95% /usr/local/bin/quota-warning.sh 95
+ # quota_warning2 = storage=80% /usr/local/bin/quota-warning.sh 80
#quota = maildir
# ACL plugin. vfile backend reads ACLs from "dovecot-acl" file from maildir
diff -r b272eff19a18 -r 6edca218d48a src/plugins/quota/quota-plugin.c
--- a/src/plugins/quota/quota-plugin.c Mon Jul 16 02:00:35 2007 +0300
+++ b/src/plugins/quota/quota-plugin.c Mon Jul 16 02:41:40 2007 +0300
@@ -42,6 +42,31 @@ static void quota_root_add_rules(const c
t_pop();
}
+static void quota_root_add_warning_rules(const char *root_name,
+ struct quota_root *root)
+{
+ const char *rule_name, *rule, *error;
+ unsigned int i;
+
+ t_push();
+
+ rule_name = t_strconcat(root_name, "_WARNING", NULL);
+ for (i = 2;; i++) {
+ rule = getenv(rule_name);
+
+ if (rule == NULL)
+ break;
+
+ if (quota_root_add_warning_rule(root, rule, &error) < 0) {
+ i_fatal("Quota root %s: Invalid warning rule: %s",
+ root_name, rule);
+ }
+ rule_name = t_strdup_printf("%s_WARNING%d", root_name, i);
+ }
+
+ t_pop();
+}
+
void quota_plugin_init(void)
{
struct quota_root *root;
@@ -58,6 +83,7 @@ void quota_plugin_init(void)
if (root == NULL)
i_fatal("Couldn't create quota root: %s", env);
quota_root_add_rules("QUOTA", root);
+ quota_root_add_warning_rules("QUOTA", root);
t_push();
for (i = 2;; i++) {
@@ -73,6 +99,7 @@ void quota_plugin_init(void)
if (root == NULL)
i_fatal("Couldn't create quota root: %s", env);
quota_root_add_rules(root_name, root);
+ quota_root_add_warning_rules(root_name, root);
}
t_pop();
diff -r b272eff19a18 -r 6edca218d48a src/plugins/quota/quota-private.h
--- a/src/plugins/quota/quota-private.h Mon Jul 16 02:00:35 2007 +0300
+++ b/src/plugins/quota/quota-private.h Mon Jul 16 02:41:40 2007 +0300
@@ -22,6 +22,13 @@ struct quota_rule {
char *mailbox_name;
int64_t bytes_limit, count_limit;
+};
+
+struct quota_warning_rule {
+ uint64_t bytes_limit;
+ uint64_t count_limit;
+
+ char *command;
};
struct quota_backend_vfuncs {
@@ -64,6 +71,7 @@ struct quota_root {
struct quota_backend backend;
struct quota_rule default_rule;
ARRAY_DEFINE(rules, struct quota_rule);
+ ARRAY_DEFINE(warning_rules, struct quota_warning_rule);
/* Module-specific contexts. See quota_module_id. */
ARRAY_DEFINE(quota_module_contexts, void);
diff -r b272eff19a18 -r 6edca218d48a src/plugins/quota/quota.c
--- a/src/plugins/quota/quota.c Mon Jul 16 02:00:35 2007 +0300
+++ b/src/plugins/quota/quota.c Mon Jul 16 02:41:40 2007 +0300
@@ -9,6 +9,7 @@
#include <ctype.h>
#include <stdlib.h>
+#include <sys/wait.h>
#define RULE_NAME_ALL_MAILBOXES "*"
@@ -124,6 +125,7 @@ struct quota_root *quota_root_init(struc
}
i_array_init(&root->rules, 4);
+ i_array_init(&root->warning_rules, 4);
array_create(&root->quota_module_contexts, default_pool,
sizeof(void *), 5);
@@ -143,6 +145,7 @@ void quota_root_deinit(struct quota_root
struct quota_root *root = *_root;
pool_t pool = root->pool;
struct quota_root *const *roots;
+ struct quota_warning_rule *warnings;
unsigned int i, count;
roots = array_get(&root->quota->roots, &count);
@@ -151,6 +154,11 @@ void quota_root_deinit(struct quota_root
array_delete(&root->quota->roots, i, 1);
}
*_root = NULL;
+
+ warnings = array_get_modifiable(&root->warning_rules, &count);
+ for (i = 0; i < count; i++)
+ i_free(warnings[i].command);
+ array_free(&root->warning_rules);
array_free(&root->rules);
array_free(&root->quota_module_contexts);
@@ -397,6 +405,44 @@ void quota_remove_user_storage(struct qu
}
}
+int quota_root_add_warning_rule(struct quota_root *root, const char *rule_def,
+ const char **error_r)
+{
+ struct quota_warning_rule *warning;
+ struct quota_rule rule;
+ const char *p;
+ int ret;
+
+ p = strchr(rule_def, ' ');
+ if (p == NULL) {
+ *error_r = "No command specified";
+ return -1;
+ }
+
+ memset(&rule, 0, sizeof(rule));
+ t_push();
+ ret = quota_rule_parse_limits(root, &rule, t_strdup_until(rule_def, p),
+ error_r);
+ t_pop();
+ if (ret < 0)
+ return -1;
+
+ if (rule.bytes_limit < 0) {
+ *error_r = "Bytes limit can't be negative";
+ return -1;
+ }
+ if (rule.count_limit < 0) {
+ *error_r = "Count limit can't be negative";
+ return -1;
+ }
+
+ warning = array_append_space(&root->warning_rules);
+ warning->command = i_strdup(p+1);
+ warning->bytes_limit = rule.bytes_limit;
+ warning->count_limit = rule.count_limit;
+ return 0;
+}
+
struct quota_root_iter *
quota_root_iter_init(struct quota *quota, struct mailbox *box)
{
@@ -521,38 +567,6 @@ struct quota_transaction_context *quota_
ctx->bytes_left = (uint64_t)-1;
ctx->count_left = (uint64_t)-1;
return ctx;
-}
-
-int quota_transaction_commit(struct quota_transaction_context **_ctx)
-{
- struct quota_transaction_context *ctx = *_ctx;
- struct quota_root *const *roots;
- unsigned int i, count;
- int ret = 0;
-
- *_ctx = NULL;
-
- if (ctx->failed)
- ret = -1;
- else if (ctx->bytes_used != 0 || ctx->count_used != 0 ||
- ctx->recalculate) {
- roots = array_get(&ctx->quota->roots, &count);
- for (i = 0; i < count; i++) {
- if (roots[i]->backend.v.update(roots[i], ctx) < 0)
- ret = -1;
- }
- }
-
- i_free(ctx);
- return ret;
-}
-
-void quota_transaction_rollback(struct quota_transaction_context **_ctx)
-{
- struct quota_transaction_context *ctx = *_ctx;
-
- *_ctx = NULL;
- i_free(ctx);
}
static int quota_transaction_set_limits(struct quota_transaction_context *ctx)
@@ -597,6 +611,84 @@ static int quota_transaction_set_limits(
return 0;
}
+static void quota_warning_execute(const char *cmd)
+{
+ int ret = system(cmd);
+
+ if (ret < 0) {
+ i_error("system(%s) failed: %m", cmd);
+ } else if (WIFSIGNALED(ret)) {
+ i_error("system(%s) died with signal %d", cmd, WTERMSIG(ret));
+ } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
+ i_error("system(%s) exited with status %d",
+ cmd, WIFEXITED(ret) ? WEXITSTATUS(ret) : ret);
+ }
+}
+
+static void quota_warnings_execute(struct quota_root *root,
+ struct quota_transaction_context *ctx)
+{
+ struct quota_warning_rule *warnings;
+ unsigned int i, count;
+ uint64_t bytes_current, bytes_limit, count_current, count_limit;
+
+ warnings = array_get_modifiable(&root->warning_rules, &count);
+ if (count == 0)
+ return;
+
+ if (quota_get_resource(root, "", QUOTA_NAME_STORAGE_BYTES,
+ &bytes_current, &bytes_limit) < 0)
+ return;
+ if (quota_get_resource(root, "", QUOTA_NAME_MESSAGES,
+ &count_current, &count_limit) < 0)
+ return;
+
+ for (i = 0; i < count; i++) {
+ if ((bytes_current < warnings[i].bytes_limit &&
+ bytes_current +
+ ctx->bytes_used >= warnings[i].bytes_limit) ||
+ (count_current < warnings[i].count_limit &&
+ count_current +
+ ctx->count_used >= warnings[i].count_limit)) {
+ quota_warning_execute(warnings[i].command);
+ break;
+ }
+ }
+}
+
+int quota_transaction_commit(struct quota_transaction_context **_ctx)
+{
+ struct quota_transaction_context *ctx = *_ctx;
+ struct quota_root *const *roots;
+ unsigned int i, count;
+ int ret = 0;
+
+ *_ctx = NULL;
+
+ if (ctx->failed)
+ ret = -1;
+ else if (ctx->bytes_used != 0 || ctx->count_used != 0 ||
+ ctx->recalculate) {
+ roots = array_get(&ctx->quota->roots, &count);
+ for (i = 0; i < count; i++) {
+ quota_warnings_execute(roots[i], ctx);
+ if (roots[i]->backend.v.update(roots[i], ctx) < 0)
+ ret = -1;
+ }
+ }
+
+ i_free(ctx);
+ return ret;
+}
+
+void quota_transaction_rollback(struct quota_transaction_context **_ctx)
+{
+ struct quota_transaction_context *ctx = *_ctx;
+
+ *_ctx = NULL;
+ i_free(ctx);
+}
+
int quota_try_alloc(struct quota_transaction_context *ctx,
struct mail *mail, bool *too_large_r)
{
More information about the dovecot-cvs
mailing list