summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorAlastair Poole <netstar@gmail.com>2021-01-31 14:45:46 +0000
committerAlastair Poole <netstar@gmail.com>2021-01-31 14:45:46 +0000
commit44425e748997a7ed3e34537dc396accfd5ce8981 (patch)
tree46a6d5e353275e863f5fb1c2e1a7e83d2ac1abd5 /src/lib
parent39c305cf866ca65658f270a11d7fc76cabcc613f (diff)
ecore_file_monitor: add kevent backend.
Summary: This is a very simple kevent backend, very similar to the eio_monitor implementation. On BSD systems, some core features within in E and other applications are always using the poll engine, which is not ideal. This is better, and simpler. Reviewers: devilhorns, raster, bu5hm4n, vtorri Reviewed By: vtorri Subscribers: vtorri, cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12239
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ecore_file/ecore_file_monitor_kevent.c290
-rw-r--r--src/lib/ecore_file/meson.build2
2 files changed, 292 insertions, 0 deletions
diff --git a/src/lib/ecore_file/ecore_file_monitor_kevent.c b/src/lib/ecore_file/ecore_file_monitor_kevent.c
new file mode 100644
index 0000000000..e8c4c1e238
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_monitor_kevent.c
@@ -0,0 +1,290 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <sys/types.h>
6#include <sys/event.h>
7#include <sys/stat.h>
8#include <fcntl.h>
9#include "ecore_file_private.h"
10
11#define KEVENT_NUM_EVENTS 5
12
13typedef struct _Ecore_File_Monitor_Kevent Ecore_File_Monitor_Kevent;
14
15#define ECORE_FILE_MONITOR_KEVENT(x) ((Ecore_File_Monitor_Kevent *)(x))
16
17struct _Ecore_File_Monitor_Kevent
18{
19 Ecore_File_Monitor monitor;
20 Eina_List *prev;
21 int fd;
22};
23
24typedef struct _File_Info File_Info;
25struct _File_Info
26{
27 const char *path;
28 Eina_Stat st;
29};
30
31static Ecore_Fd_Handler *_kevent_fdh = NULL;
32static Eina_Hash *_kevent_monitors = NULL;
33
34static Eina_Bool _ecore_file_monitor_kevent_handler(void *data, Ecore_Fd_Handler *fdh);
35static int _ecore_file_monitor_kevent_monitor(Ecore_File_Monitor *em, const char *path);
36static void _ecore_file_monitor_kevent_find(Ecore_File_Monitor *em);
37static void _ecore_file_monitor_kevent_hash_del_cb(void *data);
38static Eina_List * _ecore_file_monitor_kevent_ls(const char *directory);
39static void _ecore_file_monitor_kevent_ls_free(Eina_List *);
40
41int
42ecore_file_monitor_backend_init(void)
43{
44 int fd;
45
46 if (_kevent_fdh != NULL) return 0;
47
48 fd = kqueue();
49 if (fd < 0)
50 return 0;
51
52 _kevent_fdh = ecore_main_fd_handler_add(fd, ECORE_FD_READ, _ecore_file_monitor_kevent_handler,
53 NULL, NULL, NULL);
54 if (!_kevent_fdh)
55 {
56 close(fd);
57 return 0;
58 }
59
60 _kevent_monitors = eina_hash_int32_new(_ecore_file_monitor_kevent_hash_del_cb);
61 return 1;
62}
63
64int
65ecore_file_monitor_backend_shutdown(void)
66{
67 int fd;
68
69 if (!_kevent_fdh) return 1;
70
71 eina_hash_free(_kevent_monitors);
72
73 fd = ecore_main_fd_handler_fd_get(_kevent_fdh);
74 ecore_main_fd_handler_del(_kevent_fdh);
75 _kevent_fdh = NULL;
76
77 if (fd != -1)
78 close(fd);
79
80 return 1;
81}
82
83Ecore_File_Monitor *
84ecore_file_monitor_backend_add(const char *path,
85 void (*func) (void *data, Ecore_File_Monitor *em,
86 Ecore_File_Event event,
87 const char *path),
88 void *data)
89{
90 Ecore_File_Monitor *em;
91 char *path2;
92 size_t len;
93
94 if (!path) return NULL;
95 if (!func) return NULL;
96
97 em = (Ecore_File_Monitor *)calloc(1, sizeof(Ecore_File_Monitor_Kevent));
98 if (!em) return NULL;
99
100 em->func = func;
101 em->data = data;
102
103 len = strlen(path);
104 path2 = alloca(len + 1);
105 strcpy(path2, path);
106 if (path2[len - 1] == '/' && strcmp(path2, "/")) path2[len - 1] = 0;
107 em->path = eina_stringshare_add(path2);
108
109 if (!_ecore_file_monitor_kevent_monitor(em, em->path))
110 return NULL;
111
112 return em;
113}
114
115static Eina_List *
116_ecore_file_monitor_kevent_ls(const char *directory)
117{
118 Eina_Iterator *it;
119 Eina_File_Direct_Info *info;
120 Eina_List *files = NULL;
121
122 it = eina_file_direct_ls(directory);
123 if (!it) return NULL;
124
125 EINA_ITERATOR_FOREACH(it, info)
126 {
127 File_Info *file = malloc(sizeof(File_Info));
128 if (eina_file_statat(eina_iterator_container_get(it), info, &file->st))
129 {
130 free(file);
131 continue;
132 }
133 file->path = eina_stringshare_add(info->path);
134 files = eina_list_append(files, file);
135 }
136
137 eina_iterator_free(it);
138
139 return files;
140}
141
142static void
143_ecore_file_monitor_kevent_ls_free(Eina_List *list)
144{
145 File_Info *file;
146
147 EINA_LIST_FREE(list, file)
148 {
149 eina_stringshare_del(file->path);
150 free(file);
151 }
152}
153
154static void
155_ecore_file_monitor_kevent_hash_del_cb(void *data)
156{
157 Ecore_File_Monitor *em = data;
158
159 if (ECORE_FILE_MONITOR_KEVENT(em)->fd >= 0)
160 close(ECORE_FILE_MONITOR_KEVENT(em)->fd);
161 eina_stringshare_del(em->path);
162 _ecore_file_monitor_kevent_ls_free(ECORE_FILE_MONITOR_KEVENT(em)->prev);
163
164 free(em);
165}
166
167void
168ecore_file_monitor_backend_del(Ecore_File_Monitor *em)
169{
170 eina_hash_del(_kevent_monitors, &(ECORE_FILE_MONITOR_KEVENT(em)->fd), em);
171}
172
173static Eina_Bool
174_ecore_file_monitor_kevent_handler(void *data EINA_UNUSED, Ecore_Fd_Handler *fdh)
175{
176 Ecore_File_Monitor *em;
177 struct kevent evs[KEVENT_NUM_EVENTS];
178 int fd;
179 const struct timespec timeout = { 0, 0 };
180
181 fd = ecore_main_fd_handler_fd_get(fdh);
182 if (fd < 0) return ECORE_CALLBACK_RENEW;
183
184 int res = kevent(fd, 0, 0, evs, KEVENT_NUM_EVENTS, &timeout);
185 for (int i = 0; i < res; i++)
186 {
187 em = eina_hash_find(_kevent_monitors, &evs[i].ident);
188 if (evs[i].fflags & NOTE_DELETE)
189 {
190 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
191 }
192 if ((evs[i].fflags & NOTE_WRITE) || (evs[i].fflags & NOTE_ATTRIB))
193 {
194 _ecore_file_monitor_kevent_find(em);
195 }
196 }
197
198 return ECORE_CALLBACK_RENEW;
199}
200
201static void
202_ecore_file_monitor_kevent_find(Ecore_File_Monitor *em)
203{
204 Eina_List *l, *l2;
205 File_Info *file, *file2;
206 Eina_List *files;
207
208 files = _ecore_file_monitor_kevent_ls(em->path);
209
210 EINA_LIST_FOREACH(ECORE_FILE_MONITOR_KEVENT(em)->prev, l, file)
211 {
212 Eina_Bool exists = EINA_FALSE;
213 EINA_LIST_FOREACH(files, l2, file2)
214 {
215 if (file->st.ino == file2->st.ino)
216 {
217 if (file->path == file2->path)
218 exists = EINA_TRUE;
219
220 if (file->st.mtime != file2->st.mtime)
221 em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, file->path);
222 }
223 }
224
225 if (!exists)
226 {
227 if (S_ISDIR(file->st.mode))
228 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_DIRECTORY, file->path);
229 else
230 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_FILE, file->path);
231 }
232 }
233
234 EINA_LIST_FOREACH(files, l, file)
235 {
236 Eina_Bool exists = EINA_FALSE;
237 EINA_LIST_FOREACH(ECORE_FILE_MONITOR_KEVENT(em)->prev, l2, file2)
238 {
239 if ((file->path == file2->path) && (file->st.ino == file2->st.ino))
240 {
241 exists = EINA_TRUE;
242 break;
243 }
244 }
245
246 if (!exists)
247 {
248 if (S_ISDIR(file->st.mode))
249 em->func(em->data, em, ECORE_FILE_EVENT_CREATED_DIRECTORY, file->path);
250 else
251 em->func(em->data, em, ECORE_FILE_EVENT_CREATED_FILE, file->path);
252 }
253 }
254
255 _ecore_file_monitor_kevent_ls_free(ECORE_FILE_MONITOR_KEVENT(em)->prev);
256 ECORE_FILE_MONITOR_KEVENT(em)->prev = files;
257}
258
259static int
260_ecore_file_monitor_kevent_monitor(Ecore_File_Monitor *em, const char *path)
261{
262 struct kevent ev;
263 int fd, res = 0;
264
265 if ((!ecore_file_exists(path)) || (!ecore_file_is_dir(path)))
266 return 0;
267
268 fd = open(path, O_RDONLY);
269 if (fd < 0)
270 {
271 INF("open failed, %s", strerror(errno));
272 ecore_file_monitor_backend_del(em);
273 return 0;
274 }
275
276 eina_file_close_on_exec(fd, EINA_TRUE);
277
278 ECORE_FILE_MONITOR_KEVENT(em)->fd = fd;
279 ECORE_FILE_MONITOR_KEVENT(em)->prev = _ecore_file_monitor_kevent_ls(em->path);
280
281 eina_hash_direct_add(_kevent_monitors, &(ECORE_FILE_MONITOR_KEVENT(em)->fd), em);
282
283 EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR,
284 NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB, 0, NULL);
285 res = kevent(ecore_main_fd_handler_fd_get(_kevent_fdh), &ev, 1, 0, 0, 0);
286 if (res)
287 eina_hash_del(_kevent_monitors, &(ECORE_FILE_MONITOR_KEVENT(em)->fd), em);
288
289 return 1;
290}
diff --git a/src/lib/ecore_file/meson.build b/src/lib/ecore_file/meson.build
index 9e0212d6c0..a25d98dffb 100644
--- a/src/lib/ecore_file/meson.build
+++ b/src/lib/ecore_file/meson.build
@@ -14,6 +14,8 @@ if sys_windows == true
14 ecore_file_src += files([ 'ecore_file_monitor_win32.c']) 14 ecore_file_src += files([ 'ecore_file_monitor_win32.c'])
15elif sys_linux == true 15elif sys_linux == true
16 ecore_file_src += files([ 'ecore_file_monitor_inotify.c']) 16 ecore_file_src += files([ 'ecore_file_monitor_inotify.c'])
17elif sys_bsd == true
18 ecore_file_src += files([ 'ecore_file_monitor_kevent.c'])
17else 19else
18 ecore_file_src += files([ 'ecore_file_monitor_poll.c']) 20 ecore_file_src += files([ 'ecore_file_monitor_poll.c'])
19endif 21endif