Mercurial > dovecot > original-hg > dovecot-1.2
changeset 6032:6edca218d48a HEAD
Added quota warning commands. Based lightly on a patch by Nicolas Boullis.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 16 Jul 2007 02:41:40 +0300 |
parents | b272eff19a18 |
children | 61bf911dad91 |
files | dovecot-example.conf src/plugins/quota/quota-plugin.c src/plugins/quota/quota-private.h src/plugins/quota/quota.c src/plugins/quota/quota.h |
diffstat | 5 files changed, 169 insertions(+), 32 deletions(-) [+] |
line wrap: on
line diff
--- 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 @@ # 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
--- 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 @@ 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 @@ 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 @@ 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();
--- 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 @@ -24,6 +24,13 @@ int64_t bytes_limit, count_limit; }; +struct quota_warning_rule { + uint64_t bytes_limit; + uint64_t count_limit; + + char *command; +}; + struct quota_backend_vfuncs { struct quota_root *(*alloc)(void); int (*init)(struct quota_root *root, const char *args); @@ -64,6 +71,7 @@ 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);
--- 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 @@ } 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 @@ 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); @@ -152,6 +155,11 @@ } *_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 @@ } } +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) { @@ -523,38 +569,6 @@ 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) { struct quota_root *const *roots; @@ -597,6 +611,84 @@ 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) {
--- a/src/plugins/quota/quota.h Mon Jul 16 02:00:35 2007 +0300 +++ b/src/plugins/quota/quota.h Mon Jul 16 02:41:40 2007 +0300 @@ -26,6 +26,10 @@ /* Add a new rule too the quota root. Returns 0 if ok, -1 if rule is invalid. */ int quota_root_add_rule(struct quota_root *root, const char *rule_def, const char **error_r); +/* Add a new warning rule for the quota root. Returns 0 if ok, -1 if rule is + invalid. */ +int quota_root_add_warning_rule(struct quota_root *root, const char *rule_def, + const char **error_r); /* List all quota roots. Returned quota roots are freed by quota_deinit(). */ struct quota_root_iter *