summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mailbox-watch.c
blob: 659cab3810f1ba9c2f8fc2642b8c79434bdd633d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "mail-storage-private.h"
#include "mailbox-watch.h"

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define NOTIFY_DELAY_MSECS 500

struct mailbox_notify_file {
	struct mailbox_notify_file *next;

	char *path;
	struct stat last_st;
	struct io *io_notify;
};

static void notify_delay_callback(struct mailbox *box)
{
	timeout_remove(&box->to_notify_delay);
	box->notify_callback(box, box->notify_context);
}

static void notify_timeout(struct mailbox *box)
{
	struct mailbox_notify_file *file;
	struct stat st;
	bool notify = FALSE;

	for (file = box->notify_files; file != NULL; file = file->next) {
		if (stat(file->path, &st) == 0 &&
		    ST_CHANGED(file->last_st, st)) {
			file->last_st = st;
			notify = TRUE;
		}
	}

	if (notify)
		notify_delay_callback(box);
}

static void notify_callback(struct mailbox *box)
{
	timeout_reset(box->to_notify);

	if (box->to_notify_delay == NULL) {
		box->to_notify_delay =
			timeout_add_short(NOTIFY_DELAY_MSECS,
					  notify_delay_callback, box);
	}
}

void mailbox_watch_add(struct mailbox *box, const char *path)
{
	const struct mail_storage_settings *set = box->storage->set;
	struct mailbox_notify_file *file;
	struct stat st;
	struct io *io = NULL;

	i_assert(set->mailbox_idle_check_interval > 0);

	(void)io_add_notify(path, notify_callback, box, &io);

	file = i_new(struct mailbox_notify_file, 1);
	file->path = i_strdup(path);
	if (stat(path, &st) == 0)
		file->last_st = st;
	file->io_notify = io;

	file->next = box->notify_files;
	box->notify_files = file;

	/* we still add a timeout if we don't have one already,
	 * because we don't know what happens with [di]notify
	 * when the filesystem is remote (NFS, ...) */
	if (box->to_notify == NULL) {
		box->to_notify =
			timeout_add(set->mailbox_idle_check_interval * 1000,
				    notify_timeout, box);
	}
}

void mailbox_watch_remove_all(struct mailbox *box)
{
	struct mailbox_notify_file *file;

	while (box->notify_files != NULL) {
		file = box->notify_files;
		box->notify_files = file->next;

		io_remove(&file->io_notify);
                i_free(file->path);
		i_free(file);
	}

	timeout_remove(&box->to_notify_delay);
	timeout_remove(&box->to_notify);
}

static void notify_extract_callback(struct mailbox *box ATTR_UNUSED)
{
	i_unreached();
}

int mailbox_watch_extract_notify_fd(struct mailbox *box, const char **reason_r)
{
	struct ioloop *ioloop;
	struct mailbox_notify_file *file;
	struct io *io;
	ARRAY(struct io *) temp_ios;
	int ret;
	bool failed = FALSE;

	/* add all the notify IOs to a new ioloop. */
	ioloop = io_loop_create();

	t_array_init(&temp_ios, 8);
	for (file = box->notify_files; file != NULL && !failed; file = file->next) {
		switch (io_add_notify(file->path, notify_extract_callback, box, &io)) {
		case IO_NOTIFY_ADDED:
			array_push_back(&temp_ios, &io);
			break;
		case IO_NOTIFY_NOTFOUND:
			*reason_r = t_strdup_printf(
				"%s not found - can't watch it", file->path);
			failed = TRUE;
			break;
		case IO_NOTIFY_NOSUPPORT:
			*reason_r = "Filesystem notifications not supported";
			failed = TRUE;
			break;
		}
	}
	if (failed)
		ret = -1;
	else if (array_count(&temp_ios) == 0) {
		*reason_r = "Mailbox has no IO notifications";
		ret = -1;
	} else {
		ret = io_loop_extract_notify_fd(ioloop);
		if (ret == -1)
			*reason_r = "Couldn't extra notify fd";
	}
	array_foreach_elem(&temp_ios, io)
		io_remove(&io);
	io_loop_destroy(&ioloop);
	return ret;
}