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 *