]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
doveadm: Add mailbox cache manipulation commands
authorAki Tuomi <aki.tuomi@dovecot.fi>
Fri, 13 Apr 2018 10:28:47 +0000 (13:28 +0300)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Fri, 27 Apr 2018 06:18:46 +0000 (09:18 +0300)
This allows changing cache decisions and dropping cache pointer
for wanted mails.

src/doveadm/Makefile.am
src/doveadm/doveadm-mail-mailbox-cache.c [new file with mode: 0644]
src/doveadm/doveadm-mail.c
src/doveadm/doveadm-mail.h

index eeab429ee152ede67c0e66ffd955c1328ea1780c..e82d3a8fc8f0b15c39ef99dfd0f91dc6c28dd494 100644 (file)
@@ -106,7 +106,8 @@ doveadm_common_mail_cmds = \
        doveadm-mailbox-list-iter.c \
        doveadm-mail-save.c \
        doveadm-mail-search.c \
-       doveadm-mail-server.c
+       doveadm-mail-server.c \
+       doveadm-mail-mailbox-cache.c
 
 # these aren't actually useful in doveadm-server, but plugins may implement
 # both dumping and some other commands inside a single plugin. not having the
diff --git a/src/doveadm/doveadm-mail-mailbox-cache.c b/src/doveadm/doveadm-mail-mailbox-cache.c
new file mode 100644 (file)
index 0000000..14085f2
--- /dev/null
@@ -0,0 +1,371 @@
+/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "time-util.h"
+#include "mail-index-private.h"
+#include "mail-cache-private.h"
+#include "mail-namespace.h"
+#include "mail-storage-private.h"
+#include "doveadm-print.h"
+#include "doveadm-mail-iter.h"
+#include "doveadm-mailbox-list-iter.h"
+#include "doveadm-mail.h"
+
+struct mailbox_cache_cmd_context {
+       struct doveadm_mail_cmd_context ctx;
+
+       const char *const *boxes;
+       const char *const *fields;
+       uint64_t last_used;
+       enum mail_cache_decision_type decision;
+       bool all_fields;
+       bool set_decision;
+       bool set_last_used;
+       bool remove;
+};
+
+static int cmd_mailbox_cache_open_box(struct doveadm_mail_cmd_context *ctx,
+                                     struct mail_user *user,
+                                     const char *boxname,
+                                     struct mailbox **box_r)
+{
+       struct mailbox *box = doveadm_mailbox_find(user, boxname);
+
+       if (mailbox_open(box) < 0 || mailbox_sync(box, 0) < 0) {
+               i_error("Cannot open mailbox %s: %s",
+                       mailbox_get_vname(box),
+                       mailbox_get_last_internal_error(box, NULL));
+               doveadm_mail_failed_mailbox(ctx, box);
+               mailbox_free(&box);
+               return -1;
+       }
+
+       *box_r = box;
+
+       return 0;
+}
+
+static void cmd_mailbox_cache_decision_init(struct doveadm_mail_cmd_context *_ctx,
+                                           const char *const args[])
+{
+       struct mailbox_cache_cmd_context *ctx =
+               container_of(_ctx, struct mailbox_cache_cmd_context, ctx);
+       const char *fields;
+
+       doveadm_print_header("mailbox", "mailbox", DOVEADM_PRINT_HEADER_FLAG_STICKY);
+       doveadm_print_header_simple("field");
+       doveadm_print_header_simple("decision");
+       doveadm_print_header_simple("last-used");
+
+       if (!ctx->all_fields &&
+           !doveadm_cmd_param_str(_ctx->cctx, "fieldstr", &fields)) {
+               i_error("Missing fields parameter");
+               _ctx->exit_code = EX_USAGE;
+               return;
+       } else if (!ctx->all_fields) {
+               ctx->fields = t_strsplit_spaces(fields, ", ");
+       }
+
+       ctx->boxes = args;
+}
+
+static bool
+cmd_mailbox_cache_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c)
+{
+       struct mailbox_cache_cmd_context *ctx =
+               container_of(_ctx, struct mailbox_cache_cmd_context, ctx);
+
+       switch(c) {
+       case 'a':
+               ctx->all_fields = TRUE;
+               return TRUE;
+       /* this is handled in doveadm-mail as 'fieldstr' field */
+       case 'f':
+               return TRUE;
+       case 'l':
+               if (str_to_uint64(optarg, &ctx->last_used) < 0) {
+                       i_error("Invalid last-used '%s': not a number", optarg);
+                       return FALSE;
+               }
+               ctx->set_last_used = TRUE;
+               return TRUE;
+       case 'd':
+               if (ctx->set_decision) {
+                       i_error("Only one decision flag allowed");
+                       return FALSE;
+               }
+               if (strcmp(optarg, "no") == 0) {
+                       ctx->decision = MAIL_CACHE_DECISION_NO;
+               } else if (strcmp(optarg, "temp") == 0) {
+                       ctx->decision = MAIL_CACHE_DECISION_TEMP;
+               } else if (strcmp(optarg, "yes") == 0) {
+                       ctx->decision = MAIL_CACHE_DECISION_YES;
+               } else {
+                       i_error("Invalid decision '%s': " \
+                               "must be one of yes, temp, no",
+                               optarg);
+                       return FALSE;
+               }
+               ctx->set_decision = TRUE;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static const char *
+cmd_mailbox_cache_decision_to_str(enum mail_cache_decision_type decision)
+{
+       string_t *ret = t_str_new(10);
+       switch((decision & ~MAIL_CACHE_DECISION_FORCED)) {
+       case MAIL_CACHE_DECISION_NO:
+               str_append(ret, "no");
+               break;
+       case MAIL_CACHE_DECISION_TEMP:
+               str_append(ret, "temp");
+               break;
+       case MAIL_CACHE_DECISION_YES:
+               str_append(ret, "yes");
+               break;
+       }
+       return str_c(ret);
+}
+
+static void
+cmd_mailbox_cache_decision_process_field(struct mailbox_cache_cmd_context *ctx,
+                                        struct mail_cache_field_private *field)
+{
+       if (ctx->set_decision) {
+               field->field.decision = ctx->decision;
+               field->decision_dirty = TRUE;
+       }
+
+       if (ctx->set_last_used) {
+               field->field.last_used = (time_t)ctx->last_used;
+               field->decision_dirty = TRUE;
+       }
+
+       doveadm_print(cmd_mailbox_cache_decision_to_str(field->field.decision));
+       doveadm_print(t_strflocaltime("%F %T %Z", field->field.last_used));
+}
+
+static void
+cmd_mailbox_cache_decision_run_per_field(struct mailbox_cache_cmd_context *ctx,
+                                        struct mail_cache *cache)
+{
+       const char *const *field_name;
+       for(field_name = ctx->fields; *field_name != NULL; field_name++) {
+               doveadm_print(*field_name);
+               /* see if the field exists */
+               unsigned int idx = mail_cache_register_lookup(cache,
+                                                             *field_name);
+               if (idx == UINT_MAX) {
+                       doveadm_print("<not found>");
+                       doveadm_print("");
+                       continue;
+               }
+
+               cmd_mailbox_cache_decision_process_field(ctx, &cache->fields[idx]);
+       }
+}
+
+static void
+cmd_mailbox_cache_decision_run_all_fields(struct mailbox_cache_cmd_context *ctx,
+                                         struct mail_cache *cache)
+{
+       /* get all fields */
+       for(unsigned int i = 0; i < cache->fields_count; i++) {
+               doveadm_print(cache->fields[i].field.name);
+               cmd_mailbox_cache_decision_process_field(ctx, &cache->fields[i]);
+       }
+}
+
+static int cmd_mailbox_cache_decision_run_box(struct mailbox_cache_cmd_context *ctx,
+                                             struct mailbox *box)
+{
+       struct mailbox_transaction_context *t =
+               mailbox_transaction_begin(box, 0, "mailbox cache decision");
+       struct mail_cache *cache = t->box->cache;
+       struct mail_cache_view *view;
+
+       if (mail_cache_open_and_verify(cache) < 0 ||
+           MAIL_CACHE_IS_UNUSABLE(cache)) {
+               mailbox_transaction_rollback(&t);
+               i_error("Cache is unusable");
+               ctx->ctx.exit_code = EX_TEMPFAIL;
+               return -1;
+       }
+
+       view = mail_cache_view_open(cache, t->box->view);
+
+       if (ctx->all_fields)
+               cmd_mailbox_cache_decision_run_all_fields(ctx, cache);
+       else
+               cmd_mailbox_cache_decision_run_per_field(ctx, cache);
+
+       /* update headers */
+       if (ctx->set_decision || ctx->set_last_used)
+               mail_cache_header_fields_update(cache);
+
+       mail_cache_view_close(&view);
+
+       if (mailbox_transaction_commit(&t) < 0) {
+               i_error("mailbox_transaction_commit() failed: %s",
+                       mailbox_get_last_internal_error(box, NULL));
+               doveadm_mail_failed_mailbox(&ctx->ctx, box);
+               return -1;
+       }
+       return 0;
+}
+
+static int cmd_mailbox_cache_decision_run(struct doveadm_mail_cmd_context *_ctx,
+                                         struct mail_user *user)
+{
+       struct mailbox_cache_cmd_context *ctx =
+               container_of(_ctx, struct mailbox_cache_cmd_context, ctx);
+       const char *const *boxname;
+       int ret = 0;
+
+       for(boxname = ctx->boxes; ret == 0 && *boxname != NULL; boxname++) {
+               struct mailbox *box;
+               if ((ret = cmd_mailbox_cache_open_box(_ctx, user, *boxname, &box)) < 0)
+                       break;
+               doveadm_print_sticky("mailbox", mailbox_get_vname(box));
+               ret = cmd_mailbox_cache_decision_run_box(ctx, box);
+               mailbox_free(&box);
+       }
+
+       return ret;
+}
+
+static int cmd_mailbox_cache_remove_box(struct mailbox_cache_cmd_context *ctx,
+                                       const struct mailbox_info *info)
+{
+       struct doveadm_mail_iter *iter;
+       struct mailbox *box;
+       struct mail *mail;
+       void *empty = NULL;
+       int ret = 0, count = 0;
+
+       if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args,
+                                  0, NULL, FALSE, &iter) < 0)
+               return -1;
+
+       box = doveadm_mail_iter_get_mailbox(iter);
+
+       struct mail_index_transaction *t =
+               mail_index_transaction_begin(box->view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
+       struct mail_cache_view *view =
+               mail_cache_view_open(box->cache, box->view);
+
+       while (doveadm_mail_iter_next(iter, &mail)) {
+               count++;
+               doveadm_print(mailbox_get_vname(box));
+               doveadm_print(dec2str(mail->uid));
+               /* drop cache pointer */
+               mail_index_update_ext(t, mail->seq, view->cache->ext_id, &empty, NULL);
+               doveadm_print("ok");
+       }
+
+       if (mail_index_transaction_commit(&t) < 0) {
+               i_error("mail_index_transaction_commit() failed: %s",
+                       mailbox_get_last_internal_error(box, NULL));
+               doveadm_mail_failed_mailbox(&ctx->ctx, box);
+               ret = -1;
+       } else {
+                mail_cache_expunge_count(view->cache, count);
+       }
+
+       mail_cache_view_close(&view);
+
+       if (doveadm_mail_iter_deinit(&iter) < 0)
+               ret = -1;
+
+       return ret;
+}
+
+static int cmd_mailbox_cache_remove_run(struct doveadm_mail_cmd_context *_ctx,
+                                       struct mail_user *user)
+{
+       struct mailbox_cache_cmd_context *ctx =
+               container_of(_ctx, struct mailbox_cache_cmd_context, ctx);
+       const enum mailbox_list_iter_flags iter_flags =
+               MAILBOX_LIST_ITER_NO_AUTO_BOXES |
+               MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
+       struct doveadm_mailbox_list_iter *iter;
+       const struct mailbox_info *info;
+       int ret = 0;
+
+       iter = doveadm_mailbox_list_iter_init(&ctx->ctx, user, ctx->ctx.search_args,
+                                             iter_flags);
+       while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
+               if (cmd_mailbox_cache_remove_box(ctx, info) < 0)
+                       ret = -1;
+       } T_END;
+       if (doveadm_mailbox_list_iter_deinit(&iter) < 0)
+               ret = -1;
+       return ret;
+}
+
+static void cmd_mailbox_cache_remove_init(struct doveadm_mail_cmd_context *_ctx,
+                                         const char *const args[])
+{
+       struct mailbox_cache_cmd_context *ctx =
+               container_of(_ctx, struct mailbox_cache_cmd_context, ctx);
+
+       doveadm_print_header_simple("mailbox");
+       doveadm_print_header_simple("uid");
+       doveadm_print_header_simple("result");
+
+       ctx->ctx.search_args = doveadm_mail_build_search_args(args);
+}
+
+static struct doveadm_mail_cmd_context *cmd_mailbox_cache_decision_alloc(void)
+{
+       struct mailbox_cache_cmd_context *ctx =
+               doveadm_mail_cmd_alloc(struct mailbox_cache_cmd_context);
+       ctx->ctx.v.init = cmd_mailbox_cache_decision_init;
+       ctx->ctx.v.parse_arg = cmd_mailbox_cache_parse_arg;
+       ctx->ctx.v.run = cmd_mailbox_cache_decision_run;
+       ctx->ctx.getopt_args = "al:f:d:";
+       doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE);
+       return &ctx->ctx;
+}
+
+static struct doveadm_mail_cmd_context *cmd_mailbox_cache_remove_alloc(void)
+{
+       struct mailbox_cache_cmd_context *ctx =
+               doveadm_mail_cmd_alloc(struct mailbox_cache_cmd_context);
+       ctx->ctx.v.init = cmd_mailbox_cache_remove_init;
+       ctx->ctx.v.parse_arg = cmd_mailbox_cache_parse_arg;
+       ctx->ctx.v.run = cmd_mailbox_cache_remove_run;
+       ctx->ctx.getopt_args = "";
+       doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE);
+       return &ctx->ctx;
+}
+
+struct doveadm_cmd_ver2 doveadm_cmd_mailbox_cache_decision = {
+       .name = "mailbox cache decision",
+       .mail_cmd = cmd_mailbox_cache_decision_alloc,
+       .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"--all --fields <fields> " \
+                       "--last-used <timestamp> --decision <decision> " \
+                       "<mailbox> [<mailbox> ... ]",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_MAIL_COMMON
+DOVEADM_CMD_PARAM('a', "all", CMD_PARAM_BOOL, 0)
+DOVEADM_CMD_PARAM('f', "fieldstr", CMD_PARAM_STR, 0)
+DOVEADM_CMD_PARAM('l', "last-used", CMD_PARAM_INT64, 0)
+DOVEADM_CMD_PARAM('d', "decision", CMD_PARAM_STR, 0)
+DOVEADM_CMD_PARAM('\0', "mailbox", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+};
+
+struct doveadm_cmd_ver2 doveadm_cmd_mailbox_cache_remove = {
+       .name = "mailbox cache remove",
+       .mail_cmd = cmd_mailbox_cache_remove_alloc,
+       .usage = DOVEADM_CMD_MAIL_USAGE_PREFIX"<search string>",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_MAIL_COMMON
+DOVEADM_CMD_PARAM('\0', "query", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+};
index 4a899a8c935005865c54ae9fe0eca045d0f0d8d7..4e00767965d8bb784b3b6fb620dd1cbb40358e70 100644 (file)
@@ -918,7 +918,9 @@ static struct doveadm_cmd_ver2 *mail_commands_ver2[] = {
        &doveadm_cmd_purge_ver2,
        &doveadm_cmd_search_ver2,
        &doveadm_cmd_copy_ver2,
-       &doveadm_cmd_move_ver2
+       &doveadm_cmd_move_ver2,
+       &doveadm_cmd_mailbox_cache_decision,
+       &doveadm_cmd_mailbox_cache_remove,
 };
 
 void doveadm_mail_init(void)
index 960705052be4f497632901721fa61cef8df107a4..b060621bd17cfe2d5e4eb8e4ea2f9b7b1b94d5ea 100644 (file)
@@ -195,6 +195,8 @@ extern struct doveadm_cmd_ver2 doveadm_cmd_copy_ver2;
 extern struct doveadm_cmd_ver2 doveadm_cmd_move_ver2;
 extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_update_ver2;
 extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_path_ver2;
+extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_cache_decision;
+extern struct doveadm_cmd_ver2 doveadm_cmd_mailbox_cache_remove;
 
 #define DOVEADM_CMD_MAIL_COMMON \
 DOVEADM_CMD_PARAM('A', "all-users", CMD_PARAM_BOOL, 0) \