1 /*
2 * This file Copyright (C) 2015-2016 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include <errno.h>
10 #include <limits.h> /* NAME_MAX */
11 #include <stdlib.h> /* realloc() */
12
13 #include <unistd.h> /* close() */
14
15 #include <sys/inotify.h>
16
17 #include <event2/bufferevent.h>
18 #include <event2/event.h>
19
20 #define __LIBTRANSMISSION_WATCHDIR_MODULE__
21
22 #include "transmission.h"
23 #include "log.h"
24 #include "tr-assert.h"
25 #include "utils.h"
26 #include "watchdir.h"
27 #include "watchdir-common.h"
28
29 /***
30 ****
31 ***/
32
33 #define log_error(...) (!tr_logLevelIsActive(TR_LOG_ERROR) ? (void)0 : \
34 tr_logAddMessage(__FILE__, __LINE__, TR_LOG_ERROR, "watchdir:inotify", __VA_ARGS__))
35
36 /***
37 ****
38 ***/
39
40 typedef struct tr_watchdir_inotify
41 {
42 tr_watchdir_backend base;
43
44 int infd;
45 int inwd;
46 struct bufferevent* event;
47 }
48 tr_watchdir_inotify;
49
50 #define BACKEND_UPCAST(b) ((tr_watchdir_inotify*)(b))
51
52 #define INOTIFY_WATCH_MASK (IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE)
53
54 /***
55 ****
56 ***/
57
tr_watchdir_inotify_on_first_scan(evutil_socket_t fd UNUSED,short type UNUSED,void * context)58 static void tr_watchdir_inotify_on_first_scan(evutil_socket_t fd UNUSED, short type UNUSED, void* context)
59 {
60 tr_watchdir_t const handle = context;
61
62 tr_watchdir_scan(handle, NULL);
63 }
64
tr_watchdir_inotify_on_event(struct bufferevent * event,void * context)65 static void tr_watchdir_inotify_on_event(struct bufferevent* event, void* context)
66 {
67 TR_ASSERT(context != NULL);
68
69 tr_watchdir_t const handle = context;
70 #ifdef TR_ENABLE_ASSERTS
71 tr_watchdir_inotify* const backend = BACKEND_UPCAST(tr_watchdir_get_backend(handle));
72 #endif
73 struct inotify_event ev;
74 size_t nread;
75 size_t name_size = NAME_MAX + 1;
76 char* name = tr_new(char, name_size);
77
78 /* Read the size of the struct excluding name into buf. Guaranteed to have at
79 least sizeof(ev) available */
80 while ((nread = bufferevent_read(event, &ev, sizeof(ev))) != 0)
81 {
82 if (nread == (size_t)-1)
83 {
84 log_error("Failed to read inotify event: %s", tr_strerror(errno));
85 break;
86 }
87
88 if (nread != sizeof(ev))
89 {
90 log_error("Failed to read inotify event: expected %zu, got %zu bytes.", sizeof(ev), nread);
91 break;
92 }
93
94 TR_ASSERT(ev.wd == backend->inwd);
95 TR_ASSERT((ev.mask & INOTIFY_WATCH_MASK) != 0);
96 TR_ASSERT(ev.len > 0);
97
98 if (ev.len > name_size)
99 {
100 name_size = ev.len;
101 name = tr_renew(char, name, name_size);
102 }
103
104 /* Consume entire name into buffer */
105 if ((nread = bufferevent_read(event, name, ev.len)) == (size_t)-1)
106 {
107 log_error("Failed to read inotify name: %s", tr_strerror(errno));
108 break;
109 }
110
111 if (nread != ev.len)
112 {
113 log_error("Failed to read inotify name: expected %" PRIu32 ", got %zu bytes.", ev.len, nread);
114 break;
115 }
116
117 tr_watchdir_process(handle, name);
118 }
119
120 tr_free(name);
121 }
122
tr_watchdir_inotify_free(tr_watchdir_backend * backend_base)123 static void tr_watchdir_inotify_free(tr_watchdir_backend* backend_base)
124 {
125 tr_watchdir_inotify* const backend = BACKEND_UPCAST(backend_base);
126
127 if (backend == NULL)
128 {
129 return;
130 }
131
132 TR_ASSERT(backend->base.free_func == &tr_watchdir_inotify_free);
133
134 if (backend->event != NULL)
135 {
136 bufferevent_disable(backend->event, EV_READ);
137 bufferevent_free(backend->event);
138 }
139
140 if (backend->infd != -1)
141 {
142 if (backend->inwd != -1)
143 {
144 inotify_rm_watch(backend->infd, backend->inwd);
145 }
146
147 close(backend->infd);
148 }
149
150 tr_free(backend);
151 }
152
tr_watchdir_inotify_new(tr_watchdir_t handle)153 tr_watchdir_backend* tr_watchdir_inotify_new(tr_watchdir_t handle)
154 {
155 char const* const path = tr_watchdir_get_path(handle);
156 tr_watchdir_inotify* backend;
157
158 backend = tr_new0(tr_watchdir_inotify, 1);
159 backend->base.free_func = &tr_watchdir_inotify_free;
160 backend->infd = -1;
161 backend->inwd = -1;
162
163 if ((backend->infd = inotify_init()) == -1)
164 {
165 log_error("Unable to inotify_init: %s", tr_strerror(errno));
166 goto fail;
167 }
168
169 if ((backend->inwd = inotify_add_watch(backend->infd, path, INOTIFY_WATCH_MASK | IN_ONLYDIR)) == -1)
170 {
171 log_error("Failed to setup watchdir \"%s\": %s (%d)", path, tr_strerror(errno), errno);
172 goto fail;
173 }
174
175 if ((backend->event = bufferevent_socket_new(tr_watchdir_get_event_base(handle), backend->infd, 0)) == NULL)
176 {
177 log_error("Failed to create event buffer: %s", tr_strerror(errno));
178 goto fail;
179 }
180
181 /* Guarantees at least the sizeof an inotify event will be available in the
182 event buffer */
183 bufferevent_setwatermark(backend->event, EV_READ, sizeof(struct inotify_event), 0);
184 bufferevent_setcb(backend->event, &tr_watchdir_inotify_on_event, NULL, NULL, handle);
185 bufferevent_enable(backend->event, EV_READ);
186
187 /* Perform an initial scan on the directory */
188 if (event_base_once(tr_watchdir_get_event_base(handle), -1, EV_TIMEOUT, &tr_watchdir_inotify_on_first_scan, handle,
189 NULL) == -1)
190 {
191 log_error("Failed to perform initial scan: %s", tr_strerror(errno));
192 }
193
194 return BACKEND_DOWNCAST(backend);
195
196 fail:
197 tr_watchdir_inotify_free(BACKEND_DOWNCAST(backend));
198 return NULL;
199 }
200