comparison src/lib-storage/list/mailbox-list-index-notify.c @ 19118:f600285c3df2

lib-storage: Mailbox list notifications didn't work for INBOX. Because of some earlier optimizations that changes to INBOX aren't written to dovecot.list.index.log file.
author Timo Sirainen <tss@iki.fi>
date Tue, 08 Sep 2015 00:07:55 +0300
parents fa979ccfa34c
children 390d55d627a8
comparison
equal deleted inserted replaced
19117:fa979ccfa34c 19118:f600285c3df2
3 #include "lib.h" 3 #include "lib.h"
4 #include "ioloop.h" 4 #include "ioloop.h"
5 #include "str.h" 5 #include "str.h"
6 #include "mail-index-private.h" 6 #include "mail-index-private.h"
7 #include "mail-transaction-log-private.h" 7 #include "mail-transaction-log-private.h"
8 #include "mail-storage.h" 8 #include "mail-storage-private.h"
9 #include "mailbox-list-notify.h" 9 #include "mailbox-list-notify.h"
10 #include "mailbox-list-notify-tree.h" 10 #include "mailbox-list-notify-tree.h"
11 #include "mailbox-list-index.h" 11 #include "mailbox-list-index.h"
12 12
13 #include <sys/stat.h> 13 #include <sys/stat.h>
42 enum ilist_ext_type cur_ext; 42 enum ilist_ext_type cur_ext;
43 uint32_t cur_ext_id; 43 uint32_t cur_ext_id;
44 44
45 void (*wait_callback)(void *context); 45 void (*wait_callback)(void *context);
46 void *wait_context; 46 void *wait_context;
47 struct io *io_wait; 47 struct io *io_wait, *io_wait_inbox;
48 struct timeout *to_wait, *to_notify; 48 struct timeout *to_wait, *to_notify;
49 49
50 ARRAY_TYPE(seq_range) new_uids, expunged_uids, changed_uids; 50 ARRAY_TYPE(seq_range) new_uids, expunged_uids, changed_uids;
51 ARRAY_TYPE(const_string) new_subscriptions, new_unsubscriptions; 51 ARRAY_TYPE(const_string) new_subscriptions, new_unsubscriptions;
52 ARRAY(struct mailbox_list_notify_rename) renames; 52 ARRAY(struct mailbox_list_notify_rename) renames;
56 unsigned int rename_idx, subscription_idx, unsubscription_idx; 56 unsigned int rename_idx, subscription_idx, unsubscription_idx;
57 57
58 struct mailbox_list_notify_rec notify_rec; 58 struct mailbox_list_notify_rec notify_rec;
59 string_t *rec_name; 59 string_t *rec_name;
60 60
61 struct stat last_st; 61 char *list_log_path, *inbox_log_path;
62 struct stat list_last_st, inbox_last_st;
62 63
63 unsigned int initialized:1; 64 unsigned int initialized:1;
64 unsigned int read_failed:1; 65 unsigned int read_failed:1;
66 unsigned int inbox_event_pending:1;
65 }; 67 };
66 68
67 int mailbox_list_index_notify_init(struct mailbox_list *list, 69 int mailbox_list_index_notify_init(struct mailbox_list *list,
68 enum mailbox_list_notify_event mask, 70 enum mailbox_list_notify_event mask,
69 struct mailbox_list_notify **notify_r) 71 struct mailbox_list_notify **notify_r)
70 { 72 {
71 struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); 73 struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
72 struct mailbox_list_notify_index *inotify; 74 struct mailbox_list_notify_index *inotify;
75 const char *index_dir;
73 76
74 if (ilist == NULL) { 77 if (ilist == NULL) {
75 /* can't do this without mailbox list indexes */ 78 /* can't do this without mailbox list indexes */
76 return -1; 79 return -1;
77 } 80 }
95 MAILBOX_LIST_NOTIFY_UNSUBSCRIBE)) != 0) { 98 MAILBOX_LIST_NOTIFY_UNSUBSCRIBE)) != 0) {
96 (void)mailbox_list_iter_subscriptions_refresh(list); 99 (void)mailbox_list_iter_subscriptions_refresh(list);
97 mailbox_tree_sort(list->subscriptions); 100 mailbox_tree_sort(list->subscriptions);
98 inotify->subscriptions = mailbox_tree_dup(list->subscriptions); 101 inotify->subscriptions = mailbox_tree_dup(list->subscriptions);
99 } 102 }
103 inotify->list_log_path = i_strdup(ilist->index->log->filepath);
104 if ((list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
105 mailbox_list_get_path(list, "INBOX", MAILBOX_LIST_PATH_TYPE_INDEX,
106 &index_dir) > 0) {
107 /* FIXME: annoyingly hardcoded filename. */
108 inotify->inbox_log_path = i_strdup_printf(
109 "%s/"MAIL_INDEX_PREFIX".log", index_dir);
110 }
100 111
101 *notify_r = &inotify->notify; 112 *notify_r = &inotify->notify;
102 return 1; 113 return 1;
103 } 114 }
104 115
110 121
111 if (inotify->subscriptions != NULL) 122 if (inotify->subscriptions != NULL)
112 mailbox_tree_deinit(&inotify->subscriptions); 123 mailbox_tree_deinit(&inotify->subscriptions);
113 if (inotify->io_wait != NULL) 124 if (inotify->io_wait != NULL)
114 io_remove(&inotify->io_wait); 125 io_remove(&inotify->io_wait);
126 if (inotify->io_wait_inbox != NULL)
127 io_remove(&inotify->io_wait_inbox);
115 if (inotify->to_wait != NULL) 128 if (inotify->to_wait != NULL)
116 timeout_remove(&inotify->to_wait); 129 timeout_remove(&inotify->to_wait);
117 if (inotify->to_notify != NULL) 130 if (inotify->to_notify != NULL)
118 timeout_remove(&inotify->to_notify); 131 timeout_remove(&inotify->to_notify);
119 if (inotify->sync_ctx != NULL) 132 if (inotify->sync_ctx != NULL)
126 array_free(&inotify->new_uids); 139 array_free(&inotify->new_uids);
127 array_free(&inotify->expunged_uids); 140 array_free(&inotify->expunged_uids);
128 array_free(&inotify->changed_uids); 141 array_free(&inotify->changed_uids);
129 array_free(&inotify->renames); 142 array_free(&inotify->renames);
130 str_free(&inotify->rec_name); 143 str_free(&inotify->rec_name);
144 i_free(inotify->list_log_path);
145 i_free(inotify->inbox_log_path);
131 i_free(inotify); 146 i_free(inotify);
132 } 147 }
133 148
134 static struct mailbox_list_index_node * 149 static struct mailbox_list_index_node *
135 notify_lookup_guid(struct mailbox_list_notify_index *inotify, 150 notify_lookup_guid(struct mailbox_list_notify_index *inotify,
164 return index_node; 179 return index_node;
165 } 180 }
166 181
167 static void notify_update_stat(struct mailbox_list_notify_index *inotify) 182 static void notify_update_stat(struct mailbox_list_notify_index *inotify)
168 { 183 {
169 struct mailbox_list_index *ilist = 184 bool call = FALSE;
170 INDEX_LIST_CONTEXT(inotify->notify.list); 185
171 const char *path = ilist->index->log->filepath; 186 if (stat(inotify->list_log_path, &inotify->list_last_st) < 0 &&
172 187 errno != ENOENT) {
173 if (stat(path, &inotify->last_st) < 0 && errno != ENOENT) { 188 i_error("stat(%s) failed: %m", inotify->list_log_path);
174 i_error("stat(%s) failed: %m", path); 189 call = TRUE;
190 }
191 if (inotify->inbox_log_path != NULL) {
192 if (stat(inotify->inbox_log_path, &inotify->inbox_last_st) < 0 &&
193 errno != ENOENT) {
194 i_error("stat(%s) failed: %m", inotify->inbox_log_path);
195 call = TRUE;
196 }
197 }
198 if (call)
175 mailbox_list_index_notify_wait(&inotify->notify, NULL, NULL); 199 mailbox_list_index_notify_wait(&inotify->notify, NULL, NULL);
176 }
177 } 200 }
178 201
179 static void 202 static void
180 mailbox_list_index_notify_sync_init(struct mailbox_list_notify_index *inotify) 203 mailbox_list_index_notify_sync_init(struct mailbox_list_notify_index *inotify)
181 { 204 {
758 return 1; 781 return 1;
759 } else { 782 } else {
760 /* caller doesn't care about this change */ 783 /* caller doesn't care about this change */
761 } 784 }
762 } 785 }
786 if (inotify->inbox_event_pending) {
787 inotify->inbox_event_pending = FALSE;
788 memset(&inotify->notify_rec, 0, sizeof(inotify->notify_rec));
789 inotify->notify_rec.vname = "INBOX";
790 inotify->notify_rec.storage_name = "INBOX";
791 /* Don't bother trying to figure out which event exactly this
792 is. Just send them all and let the caller handle it. */
793 inotify->notify_rec.events = MAILBOX_LIST_NOTIFY_APPENDS |
794 MAILBOX_LIST_NOTIFY_EXPUNGES |
795 MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
796 MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES;
797 *rec_r = &inotify->notify_rec;
798 return 1;
799 }
763 800
764 mailbox_list_index_notify_read_deinit(inotify); 801 mailbox_list_index_notify_read_deinit(inotify);
765 return inotify->read_failed ? -1 : 0; 802 return inotify->read_failed ? -1 : 0;
766 } 803 }
767 804
771 inotify->wait_callback(inotify->wait_context); 808 inotify->wait_callback(inotify->wait_context);
772 } 809 }
773 810
774 static void notify_callback(struct mailbox_list_notify_index *inotify) 811 static void notify_callback(struct mailbox_list_notify_index *inotify)
775 { 812 {
776 struct stat prev_st = inotify->last_st; 813 #define INOTIFY_ST_CHANGED(last_st, prev_st) \
777 814 ((last_st).st_mtime != (prev_st).st_mtime || \
815 ST_MTIME_NSEC(last_st) != ST_MTIME_NSEC(prev_st) || \
816 (last_st).st_size != (prev_st).st_size || \
817 (last_st).st_ino != (prev_st).st_ino)
818 struct stat list_prev_st = inotify->list_last_st;
819 struct stat inbox_prev_st = inotify->inbox_last_st;
820
778 notify_update_stat(inotify); 821 notify_update_stat(inotify);
779 if (inotify->last_st.st_mtime != prev_st.st_mtime || 822 if (INOTIFY_ST_CHANGED(inotify->inbox_last_st, inbox_prev_st))
780 ST_MTIME_NSEC(inotify->last_st) != ST_MTIME_NSEC(prev_st) || 823 inotify->inbox_event_pending = TRUE;
781 inotify->last_st.st_size != prev_st.st_size || 824 if (inotify->inbox_event_pending ||
782 inotify->last_st.st_ino != prev_st.st_ino) { 825 INOTIFY_ST_CHANGED(inotify->list_last_st, list_prev_st)) {
783 /* log has changed. call the callback with a small delay 826 /* log has changed. call the callback with a small delay
784 to allow bundling multiple changes together */ 827 to allow bundling multiple changes together */
785 if (inotify->to_notify != NULL) { 828 if (inotify->to_notify != NULL) {
786 /* already doing this */ 829 /* already doing this */
787 return; 830 return;
796 void (*callback)(void *context), 839 void (*callback)(void *context),
797 void *context) 840 void *context)
798 { 841 {
799 struct mailbox_list_notify_index *inotify = 842 struct mailbox_list_notify_index *inotify =
800 (struct mailbox_list_notify_index *)notify; 843 (struct mailbox_list_notify_index *)notify;
801 const char *path;
802 unsigned int check_interval; 844 unsigned int check_interval;
803 845
804 inotify->wait_callback = callback; 846 inotify->wait_callback = callback;
805 inotify->wait_context = context; 847 inotify->wait_context = context;
806 848
807 if (callback == NULL) { 849 if (callback == NULL) {
808 if (inotify->io_wait != NULL) 850 if (inotify->io_wait != NULL)
809 io_remove(&inotify->io_wait); 851 io_remove(&inotify->io_wait);
852 if (inotify->io_wait_inbox != NULL)
853 io_remove(&inotify->io_wait_inbox);
810 if (inotify->to_wait != NULL) 854 if (inotify->to_wait != NULL)
811 timeout_remove(&inotify->to_wait); 855 timeout_remove(&inotify->to_wait);
812 if (inotify->to_notify != NULL) 856 if (inotify->to_notify != NULL)
813 timeout_remove(&inotify->to_notify); 857 timeout_remove(&inotify->to_notify);
814 } else if (inotify->to_wait == NULL) { 858 } else if (inotify->to_wait == NULL) {
815 path = inotify->view->index->log->filepath; 859 (void)io_add_notify(inotify->list_log_path, notify_callback,
816 (void)io_add_notify(path, notify_callback, inotify, 860 inotify, &inotify->io_wait);
817 &inotify->io_wait); 861 /* we need to check for INBOX explicitly, because INBOX changes
862 don't get added to mailbox.list.index.log */
863 if (inotify->inbox_log_path != NULL) {
864 (void)io_add_notify(inotify->inbox_log_path,
865 notify_callback, inotify,
866 &inotify->io_wait_inbox);
867 }
818 /* check with timeout as well, in case io_add_notify() 868 /* check with timeout as well, in case io_add_notify()
819 doesn't work (e.g. NFS) */ 869 doesn't work (e.g. NFS) */
820 check_interval = notify->list->mail_set->mailbox_idle_check_interval; 870 check_interval = notify->list->mail_set->mailbox_idle_check_interval;
821 i_assert(check_interval > 0); 871 i_assert(check_interval > 0);
822 inotify->to_wait = timeout_add(check_interval * 1000, 872 inotify->to_wait = timeout_add(check_interval * 1000,