View | Details | Raw Unified | Return to bug 214338 | Differences between
and this patch

Collapse All | Expand All

(-)devel/glib20/Makefile (-1 / +12 lines)
Lines 48-54 Link Here
48
		glib-compile-resources.1 gresource.1 gdbus-codegen.1
48
		glib-compile-resources.1 gresource.1 gdbus-codegen.1
49
gobject_MAN=	glib-genmarshal.1 glib-mkenums.1 gobject-query.1
49
gobject_MAN=	glib-genmarshal.1 glib-mkenums.1 gobject-query.1
50
50
51
OPTIONS_DEFINE=	COLLATION_FIX DEBUG NLS
51
OPTIONS_DEFINE=	COLLATION_FIX DEBUG FAM_ALTBACKEND NLS
52
OPTIONS_SUB=	yes
52
OPTIONS_SUB=	yes
53
# libc collation was fixed by https://svnweb.freebsd.org/changeset/base/290494
53
# libc collation was fixed by https://svnweb.freebsd.org/changeset/base/290494
54
COLLATION_FIX_DESC=	Use ICU for UTF-8 string collation (if libc is broken)
54
COLLATION_FIX_DESC=	Use ICU for UTF-8 string collation (if libc is broken)
Lines 65-70 Link Here
65
# http://www.freebsd.org/cgi/query-pr.cgi?pr=175930 see comment by ed@
65
# http://www.freebsd.org/cgi/query-pr.cgi?pr=175930 see comment by ed@
66
#COLLATION_FIX_CONFIGURE_ENV+=	CFLAGS="-D__STDC_ISO_10646__"
66
#COLLATION_FIX_CONFIGURE_ENV+=	CFLAGS="-D__STDC_ISO_10646__"
67
67
68
FAM_ALTBACKEND_DESC=		Alternate file monitor backend
69
FAM_ALTBACKEND_USES+=		autoreconf:build
70
FAM_ALTBACKEND_EXTRA_PATCHES=	${FILESDIR}/extra-patch-gio_kqueue_Makefile.am
71
72
68
.include <bsd.port.pre.mk>
73
.include <bsd.port.pre.mk>
69
74
70
# doesn't build yet
75
# doesn't build yet
Lines 98-103 Link Here
98
		s|-Werror|| ; \
103
		s|-Werror|| ; \
99
		s|#define HAVE_SYS_INOTIFY_H 1||' ${WRKSRC}/configure
104
		s|#define HAVE_SYS_INOTIFY_H 1||' ${WRKSRC}/configure
100
105
106
post-patch-FAM_ALTBACKEND-on:
107
	@${CP} -f ${FILESDIR}/gkqueuefilemonitor.c.in ${WRKSRC}/gio/kqueue/gkqueuefilemonitor.c
108
	@${CP} ${FILESDIR}/kqueue_fnm.c.in ${WRKSRC}/gio/kqueue/kqueue_fnm.c
109
	@${CP} ${FILESDIR}/kqueue_fnm.h.in ${WRKSRC}/gio/kqueue/kqueue_fnm.h
110
	@(cd ${WRKSRC} && ${AUTORECONF} -fvi)
111
101
post-install:
112
post-install:
102
	@${MKDIR} ${STAGEDIR}${PREFIX}/share/GConf/gsettings
113
	@${MKDIR} ${STAGEDIR}${PREFIX}/share/GConf/gsettings
103
	@${MKDIR} ${STAGEDIR}${PREFIX}/lib/gio/modules
114
	@${MKDIR} ${STAGEDIR}${PREFIX}/lib/gio/modules
(-)devel/glib20/files/extra-patch-gio_kqueue_Makefile.am (+26 lines)
Line 0 Link Here
1
--- gio/kqueue/Makefile.am.orig	2015-10-14 14:41:16.000000000 +0300
2
+++ gio/kqueue/Makefile.am	2016-11-06 05:08:37.646089000 +0300
3
@@ -4,21 +4,8 @@
4
 
5
 libkqueue_la_SOURCES = \
6
        gkqueuefilemonitor.c \
7
-       gkqueuefilemonitor.h \
8
-       kqueue-helper.c \
9
-       kqueue-helper.h \
10
-       kqueue-thread.c \
11
-       kqueue-thread.h \
12
-       kqueue-sub.c \
13
-       kqueue-sub.h \
14
-       kqueue-missing.c \
15
-       kqueue-missing.h \
16
-       kqueue-utils.c \
17
-       kqueue-utils.h \
18
-       kqueue-exclusions.c \
19
-       kqueue-exclusions.h \
20
-       dep-list.c \
21
-       dep-list.h \
22
+       kqueue_fnm.c \
23
+       kqueue_fnm.h \
24
        $(NULL)
25
 
26
 libkqueue_la_CFLAGS = \
(-)devel/glib20/files/gkqueuefilemonitor.c.in (+188 lines)
Line 0 Link Here
1
/*-
2
 * Copyright (c) 2016 - 2017 Rozhuk Ivan <rozhuk.im@gmail.com>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 *
26
 * Author: Rozhuk Ivan <rozhuk.im@gmail.com>
27
 *
28
 */
29
30
#include "config.h"
31
32
#include <glib-object.h>
33
#include <string.h>
34
#include <gio/gfilemonitor.h>
35
#include <gio/glocalfilemonitor.h>
36
#include <gio/giomodule.h>
37
#include "glib-private.h"
38
#include <glib-unix.h>
39
#include "kqueue_fnm.h"
40
41
42
static GMutex			kqueue_lock;
43
static volatile kqueue_fnm_p	kqueue_fnm = NULL;
44
45
#define G_TYPE_KQUEUE_FILE_MONITOR      (g_kqueue_file_monitor_get_type())
46
#define G_KQUEUE_FILE_MONITOR(inst)     (G_TYPE_CHECK_INSTANCE_CAST((inst), \
47
					 G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor))
48
49
typedef GLocalFileMonitorClass	GKqueueFileMonitorClass;
50
51
typedef struct {
52
	GLocalFileMonitor	parent_instance;
53
	kqueue_file_mon_data_p	fmd;
54
} GKqueueFileMonitor;
55
56
GType g_kqueue_file_monitor_get_type(void);
57
G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
58
       g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
59
               g_define_type_id,
60
               "kqueue",
61
               10))
62
63
64
static void
65
kqueue_event_handler(kqueue_fnm_p kfnm,
66
    kqueue_file_mon_data_p fmd, void *udata, uint32_t event,
67
    const char *base, const char *filename, const char *new_filename) {
68
	static const uint32_t kfnm_to_glib_map[] = {
69
		0,				/* KF_EVENT_NOT_CHANGED */
70
		G_FILE_MONITOR_EVENT_CREATED,	/* KF_EVENT_CREATED */
71
		G_FILE_MONITOR_EVENT_DELETED,	/* KF_EVENT_DELETED */
72
		G_FILE_MONITOR_EVENT_RENAMED,	/* KF_EVENT_RENAMED */
73
		G_FILE_MONITOR_EVENT_CHANGED	/* KF_EVENT_CHANGED */
74
	};
75
76
	if (NULL == kfnm || NULL == filename ||
77
	    KF_EVENT_CREATED > event ||
78
	    KF_EVENT_CHANGED < event)
79
		return;
80
	g_file_monitor_source_handle_event(udata,
81
	    kfnm_to_glib_map[event],
82
	    filename, new_filename, NULL,
83
	    g_get_monotonic_time());
84
}
85
86
static gboolean
87
g_kqueue_file_monitor_is_supported(void) {
88
89
	if (NULL != kqueue_fnm)
90
		return (TRUE);
91
	/* Init only once. */
92
	g_mutex_lock(&kqueue_lock);
93
	if (NULL != kqueue_fnm) {
94
		g_mutex_unlock(&kqueue_lock);
95
		return (TRUE); /* Initialized while wait lock. */
96
	}
97
	kqueue_fnm = kqueue_fnm_create(1, kqueue_event_handler);
98
	if (NULL == kqueue_fnm) {
99
		g_mutex_unlock(&kqueue_lock);
100
		return (FALSE); /* Init fail. */
101
	}
102
	g_mutex_unlock(&kqueue_lock);
103
104
	return (TRUE);
105
}
106
107
static gboolean
108
g_kqueue_file_monitor_cancel(GFileMonitor *monitor) {
109
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(monitor);
110
111
	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
112
	gffm->fmd = NULL;
113
114
	return (TRUE);
115
}
116
117
static void
118
g_kqueue_file_monitor_finalize(GObject *object) {
119
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(object);
120
121
	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
122
	gffm->fmd = NULL;
123
}
124
125
static void
126
g_kqueue_file_monitor_start(GLocalFileMonitor *local_monitor,
127
    const gchar *dirname, const gchar *basename,
128
    const gchar *filename, GFileMonitorSource *source) {
129
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR (local_monitor);
130
131
	g_assert(NULL != kqueue_fnm);
132
	g_source_ref((GSource*)source);
133
134
	if (NULL == filename) {
135
		filename = dirname;
136
	}
137
	gffm->fmd = kqueue_fnm_add(kqueue_fnm, filename, source);
138
}
139
140
static void
141
g_kqueue_file_monitor_init(GKqueueFileMonitor *monitor) {
142
	
143
}
144
145
static void
146
g_kqueue_file_monitor_class_init(GKqueueFileMonitorClass *class) {
147
	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
148
	GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS(class);
149
150
	class->is_supported = g_kqueue_file_monitor_is_supported;
151
	class->start = g_kqueue_file_monitor_start;
152
	class->mount_notify = TRUE; /* TODO: ??? */
153
	file_monitor_class->cancel = g_kqueue_file_monitor_cancel;
154
	gobject_class->finalize = g_kqueue_file_monitor_finalize;
155
}
156
157
static void
158
g_kqueue_file_monitor_class_finalize(GKqueueFileMonitorClass *class) {
159
	
160
}
161
162
void
163
g_io_module_load(GIOModule *module) {
164
165
	g_type_module_use(G_TYPE_MODULE(module));
166
167
	g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
168
	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
169
	g_io_extension_point_implement(G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
170
	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
171
}
172
173
void
174
g_io_module_unload(GIOModule *module) {
175
	
176
	g_assert_not_reached();
177
}
178
179
char **
180
g_io_module_query(void) {
181
	char *eps[] = {
182
		G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
183
		G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
184
		NULL
185
	};
186
187
	return (g_strdupv(eps));
188
}
(-)devel/glib20/files/kqueue_fnm.c.in (+751 lines)
Line 0 Link Here
1
/*-
2
 * Copyright (c) 2016 - 2017 Rozhuk Ivan <rozhuk.im@gmail.com>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 *
26
 * Author: Rozhuk Ivan <rozhuk.im@gmail.com>
27
 *
28
 */
29
30
#include <sys/param.h>
31
#include <sys/types.h>
32
#include <sys/event.h>
33
#include <sys/stat.h>
34
#include <sys/fcntl.h> /* open, fcntl */
35
36
#include <inttypes.h>
37
#include <stdlib.h> /* malloc, exit */
38
#include <unistd.h> /* close, write, sysconf */
39
#include <string.h> /* bcopy, bzero, memcpy, memmove, memset, strerror... */
40
#include <dirent.h> /* opendir, readdir */
41
#include <errno.h>
42
#include <pthread.h>
43
44
#include "kqueue_fnm.h"
45
46
47
/* Preallocate items count. */
48
#ifndef FILES_ALLOC_BLK_SIZE
49
#	define FILES_ALLOC_BLK_SIZE	32
50
#endif
51
52
typedef struct file_info_s { /* Directory file. */
53
	struct dirent 	de;		/* d_reclen used for action. */
54
	struct stat	sb;
55
} file_info_t, *file_info_p;
56
57
58
typedef struct readdir_data_s {
59
	int		fd;
60
	uint8_t		*buf;
61
	size_t		buf_size;
62
	size_t		buf_used;
63
	size_t		buf_pos;
64
} readdir_data_t, *readdir_data_p;
65
66
67
typedef struct kqueue_file_mon_data_s {
68
	int		fd;		/* fd for notify kqueue(). */
69
	int		is_dir;
70
	char		path[(PATH_MAX + 2)];
71
	size_t		path_size;
72
	size_t		name_offset;	/* Parent path size. */
73
	size_t		st_blksize;
74
	void		*udata;
75
	kqueue_fnm_p	kfnm;
76
	/* For dir. */
77
	file_info_p	files;
78
	volatile size_t	files_count;
79
	size_t		files_allocated;
80
} kqueue_file_mon_data_t;
81
82
83
typedef struct kqueue_file_nonify_monitor_s {
84
	int		fd;		/* kqueue() fd. */
85
	int		pfd[2];		/* pipe queue specific. */
86
	uint8_t		*tmpbuf;
87
	size_t		tmpbuf_size;
88
	kfnm_event_handler_cb cb_func;	/* Callback on dir/file change. */
89
	pthread_t	tid;
90
} kqueue_fnm_t;
91
92
93
typedef void (*kq_msg_cb)(kqueue_file_mon_data_p fmd);
94
95
typedef struct kqueue_file_mon_msg_pkt_s {
96
	size_t		magic;
97
	kq_msg_cb	msg_cb;
98
	kqueue_file_mon_data_p fmd;
99
	size_t		chk_sum;
100
} kqueue_fnm_msg_pkt_t, *kqueue_fnm_msg_pkt_p;
101
102
#define KF_MSG_PKT_MAGIC	0xffddaa00
103
104
105
#ifndef O_NOATIME
106
#	define O_NOATIME	0
107
#endif
108
#ifndef O_EVTONLY
109
#	define O_EVTONLY	O_RDONLY
110
#endif
111
#define OPEN_FILE_FLAGS		(O_EVTONLY | O_NONBLOCK | O_NOFOLLOW | O_NOATIME | O_CLOEXEC)
112
113
#define EVFILT_VNODE_FLAGS_ALL	(NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE)
114
115
116
#ifndef _GENERIC_DIRSIZ
117
#	define _GENERIC_DIRSIZ(__de)	MIN((__de)->d_reclen, sizeof(struct dirent))
118
#endif
119
120
#define IS_NAME_DOTS(__name)	('.' == (__name)[0] &&			\
121
				 ('\0' == (__name)[1] || 		\
122
				  ('.' == (__name)[1] && '\0' == (__name)[2])))
123
#define IS_DE_NAME_EQ(__de1, __de2)  (0 == mem_cmpn((__de1)->d_name,	\
124
						    (__de1)->d_namlen,	\
125
						    (__de2)->d_name,	\
126
						    (__de2)->d_namlen))
127
#define zalloc(__size)		calloc(1, (__size))
128
129
void 	*kqueue_fnm_proccess_events_proc(void *data);
130
131
static inline int
132
mem_cmpn(const void *buf1, const size_t buf1_size,
133
    const void *buf2, const size_t buf2_size) {
134
135
	if (buf1_size != buf2_size)
136
		return (((buf1_size > buf2_size) ? 127 : -127));
137
	if (0 == buf1_size || buf1 == buf2)
138
		return (0);
139
	if (NULL == buf1)
140
		return (-127);
141
	if (NULL == buf2)
142
		return (127);
143
	return (memcmp(buf1, buf2, buf1_size));
144
}
145
146
static inline int
147
realloc_items(void **items, const size_t item_size,
148
    size_t *allocated, const size_t alloc_blk_cnt, const size_t count) {
149
	size_t allocated_prev, allocated_new;
150
	uint8_t *items_new;
151
152
	if (NULL == items || 0 == item_size || NULL == allocated ||
153
	    0 == alloc_blk_cnt)
154
		return (EINVAL);
155
	allocated_prev = (*allocated);
156
	if (NULL != (*items) &&
157
	    allocated_prev > count &&
158
	    allocated_prev <= (count + alloc_blk_cnt))
159
		return (0);
160
	allocated_new = (((count / alloc_blk_cnt) + 1) * alloc_blk_cnt);
161
#if (defined(__FreeBSD_version) && __FreeBSD_version >= 1100000) || defined(HAVE_REALLOCARRAY)
162
	items_new = (uint8_t*)reallocarray((*items), item_size, allocated_new);
163
#else /* For old BSD systems. */
164
	items_new = (uint8_t*)realloc((*items), (item_size * allocated_new));
165
#endif
166
	if (NULL == items_new) /* Realloc fail! */
167
		return (ENOMEM);
168
169
	if (allocated_new > allocated_prev) { /* Init new mem. */
170
		bzero((items_new + (allocated_prev * item_size)),
171
		    ((allocated_new - allocated_prev) * item_size));
172
	}
173
	(*items) = items_new;
174
	(*allocated) = allocated_new;
175
176
	return (0);
177
}
178
179
180
static int
181
readdir_data_start(int fd, uint8_t *buf, size_t buf_size, readdir_data_p rdd) {
182
183
	if (-1 == fd || NULL == buf || 0 == buf_size || NULL == rdd)
184
		return (EINVAL);
185
	if (-1 == lseek(fd, 0, SEEK_SET))
186
		return (errno);
187
	bzero(rdd, sizeof(readdir_data_t));
188
	rdd->fd = fd;
189
	rdd->buf = buf;
190
	rdd->buf_size = buf_size;
191
192
	return (0);
193
}
194
195
static int
196
readdir_data_next(readdir_data_p rdd, struct dirent *de) {
197
	int ios;
198
	uint8_t *ptr;
199
200
	if (NULL == rdd || NULL == de)
201
		return (EINVAL);
202
203
retry:
204
	if (rdd->buf_used <= rdd->buf_pos) {
205
		ios = getdents(rdd->fd, (char*)rdd->buf, rdd->buf_size);
206
		if (-1 == ios)
207
			return (errno);
208
		rdd->buf_used = (size_t)ios;
209
		rdd->buf_pos = 0;
210
		if (0 == ios)
211
			return (ESPIPE); /* EOF. */
212
	}
213
	/* Keep data aligned. */
214
	ptr = (rdd->buf + rdd->buf_pos);
215
	memcpy(de, ptr, (sizeof(struct dirent) - sizeof(de->d_name)));
216
	if (0 == de->d_reclen)
217
		return (ESPIPE); /* EOF. */
218
	rdd->buf_pos += de->d_reclen;
219
#ifdef DT_WHT
220
	if (DT_WHT == de->d_type)
221
		goto retry;
222
#endif
223
	memcpy(de, ptr, _GENERIC_DIRSIZ(de));
224
	if (IS_NAME_DOTS(de->d_name))
225
		goto retry;
226
	return (0);
227
}
228
229
230
static int
231
file_info_find_de_fileno(file_info_p files, size_t files_count,
232
    struct dirent *de, size_t *idx) {
233
	size_t i;
234
235
	if (NULL == files || NULL == de || NULL == idx)
236
		return (0);
237
	for (i = 0; i < files_count; i ++) {
238
		if (de->d_type != files[i].de.d_type ||
239
		    de->d_fileno != files[i].de.d_fileno)
240
			continue;
241
		(*idx) = i;
242
		return (1);
243
	}
244
	(*idx) = files_count;
245
	return (0);
246
}
247
248
static int
249
file_info_find_de_name(file_info_p files, size_t files_count,
250
    const size_t skip_idx, struct dirent *de, size_t *idx) {
251
	size_t i;
252
253
	if (NULL == files || NULL == de || NULL == idx)
254
		return (0);
255
	for (i = 0; i < files_count; i ++) {
256
		if (i == skip_idx ||
257
		    de->d_type != files[i].de.d_type ||
258
		    0 == IS_DE_NAME_EQ(de, &files[i].de))
259
			continue;
260
		(*idx) = i;
261
		return (1);
262
	}
263
	(*idx) = files_count;
264
	return (0);
265
}
266
267
268
static void
269
kqueue_file_mon_data_clean(kqueue_file_mon_data_p fmd) {
270
271
	if (NULL == fmd)
272
		return;
273
	close(fmd->fd);
274
	fmd->fd = -1;
275
	fmd->files_count = 0;
276
	fmd->files_allocated = 0;
277
	free(fmd->files);
278
	fmd->files = NULL;
279
}
280
281
static void
282
kqueue_file_mon_data_free(kqueue_file_mon_data_p fmd) {
283
284
	if (NULL == fmd)
285
		return;
286
	close(fmd->fd);
287
	free(fmd->files);
288
	free(fmd);
289
}
290
291
static kqueue_file_mon_data_p
292
kqueue_file_mon_data_alloc(kqueue_fnm_p kfnm, const char *path, void *udata) {
293
	kqueue_file_mon_data_p fmd;
294
295
	if (NULL == kfnm || NULL == path)
296
		return (NULL);
297
	fmd = zalloc(sizeof(kqueue_file_mon_data_t));
298
	if (NULL == fmd)
299
		return (NULL);
300
	/* Remember args. */
301
	fmd->path_size = strlcpy(fmd->path, path, PATH_MAX);
302
	fmd->name_offset = fmd->path_size;
303
	fmd->udata = udata;
304
	fmd->kfnm = kfnm;
305
306
	return (fmd);
307
}
308
309
static int
310
kqueue_file_mon_data_readdir(kqueue_file_mon_data_p fmd) {
311
	int error;
312
	struct dirent *de;
313
	uint8_t *tmpbuf;
314
	readdir_data_t rdd;
315
316
	if (NULL == fmd || 0 == fmd->is_dir)
317
		return (EINVAL);
318
319
	/* Get temp buf. */
320
	if (fmd->st_blksize > fmd->kfnm->tmpbuf_size) {
321
		tmpbuf = realloc(fmd->kfnm->tmpbuf, fmd->st_blksize);
322
		if (NULL == tmpbuf)
323
			return (ENOMEM);
324
		fmd->kfnm->tmpbuf = tmpbuf;
325
		fmd->kfnm->tmpbuf_size = fmd->st_blksize;
326
	}
327
328
	fmd->files_count = 0;
329
	fmd->files_allocated = 0;
330
	free(fmd->files);
331
	fmd->files = NULL;
332
333
	error = readdir_data_start(fmd->fd, fmd->kfnm->tmpbuf, fmd->st_blksize, &rdd);
334
	if (0 != error)
335
		return (error);
336
	for (;;) {
337
		if (0 != realloc_items((void**)&fmd->files,
338
		    sizeof(file_info_t), &fmd->files_allocated,
339
		    FILES_ALLOC_BLK_SIZE, fmd->files_count)) {
340
			fmd->files_count = 0;
341
			fmd->files_allocated = 0;
342
			free(fmd->files);
343
			fmd->files = NULL;
344
			return (ENOMEM);
345
		}
346
		de = &fmd->files[fmd->files_count].de; /* Use short name. */
347
		/* Get file name from folder. */
348
		if (0 != readdir_data_next(&rdd, de))
349
			break;
350
		/* Get file attrs. */
351
		if (0 != fstatat(fmd->fd, de->d_name,
352
		    &fmd->files[fmd->files_count].sb,
353
		    AT_SYMLINK_NOFOLLOW)) {
354
			bzero(&fmd->files[fmd->files_count].sb,
355
			    sizeof(struct stat));
356
		}
357
		fmd->files_count ++;
358
	}
359
360
	return (0); /* OK. */
361
}
362
363
364
static void
365
kqueue_file_mon_data_init(kqueue_file_mon_data_p fmd) {
366
	struct stat sb;
367
	struct kevent kev;
368
	struct timespec ke_timeout;
369
370
	if (NULL == fmd)
371
		return;
372
	fmd->fd = open(fmd->path, OPEN_FILE_FLAGS);
373
	if (-1 == fmd->fd)
374
		return;
375
	if (0 != fstat(fmd->fd, &sb))
376
		goto err_out;
377
	fmd->st_blksize = (size_t)sb.st_blksize;
378
	/* Get parent folder name. */
379
	if (S_ISDIR(sb.st_mode)) {
380
		fmd->is_dir = 1;
381
		/* Be sure that folder contain trailing '/'. */
382
		if ('/' != fmd->path[(fmd->path_size - 1)]) {
383
			fmd->path[fmd->path_size] = '/';
384
			fmd->path_size ++;
385
			fmd->path[fmd->path_size] = 0;
386
		}
387
		/* Skip last '/' for parent dir search. */
388
		fmd->name_offset = (fmd->path_size - 1);
389
	}
390
	/* Find parent dir path size. */
391
	while (0 < fmd->name_offset && '/' != fmd->path[(fmd->name_offset - 1)]) {
392
		fmd->name_offset --;
393
	}
394
395
	/* Dir special processing. */
396
	if (0 != fmd->is_dir) {
397
		/* Read and remember dir content. */
398
		if (0 != kqueue_file_mon_data_readdir(fmd))
399
			goto err_out;
400
	}
401
	/* Add to kqueue. */
402
	bzero(&kev, sizeof(kev));
403
	kev.ident = (uintptr_t)fmd->fd;
404
	kev.filter = EVFILT_VNODE;
405
	kev.flags = (EV_ADD | EV_CLEAR);
406
	kev.fflags = EVFILT_VNODE_FLAGS_ALL;
407
	kev.udata = (void*)fmd;
408
409
	bzero(&ke_timeout, sizeof(ke_timeout));
410
411
	if (-1 == kevent(fmd->kfnm->fd, &kev, 1, NULL, 0, &ke_timeout))
412
		goto err_out;
413
	return; /* OK. */
414
415
err_out:
416
	kqueue_file_mon_data_clean(fmd);
417
}
418
419
420
static void
421
kqueue_handle_changes(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
422
	struct stat sb;
423
	size_t i, k, files_count;
424
	file_info_p files;
425
426
	if (NULL == kfnm || NULL == fmd)
427
		return;
428
	if (0 != fstat(fmd->fd, &sb) ||
429
	    0 == sb.st_nlink) {
430
		kqueue_file_mon_data_clean(fmd);
431
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
432
		    fmd->path, "", NULL);
433
		return;
434
	}
435
	if (0 == fmd->is_dir) {
436
		fmd->path[fmd->name_offset] = 0;
437
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED, fmd->path,
438
		    (fmd->path + fmd->name_offset), NULL);
439
		fmd->path[fmd->name_offset] = '/';
440
		return;
441
	}
442
443
	/* Dir processing. */
444
445
	/* Save prev. */
446
	files = fmd->files;
447
	files_count = fmd->files_count;
448
	fmd->files = NULL;
449
	fmd->files_count = 0;
450
	/* Update dir. */
451
	if (0 != kqueue_file_mon_data_readdir(fmd))
452
		goto err_out;
453
	/* Notify removed first. */
454
	for (i = 0; i < files_count; i ++) {
455
		if (0 != file_info_find_de_fileno(fmd->files, fmd->files_count, &files[i].de, &k) ||
456
		    0 != file_info_find_de_name(fmd->files, fmd->files_count, ~0, &files[i].de, &k)) /* Deleted. */
457
			continue;
458
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
459
			    fmd->path, files[i].de.d_name, NULL);
460
	}
461
	/* Notify. */
462
	for (i = 0; i < fmd->files_count; i ++) {
463
		/* Is new file/folder? */
464
		if (0 == file_info_find_de_fileno(files, files_count, &fmd->files[i].de, &k) &&
465
		    0 == file_info_find_de_name(files, files_count, ~0, &fmd->files[i].de, &k)) { /* Add new. */
466
			kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CREATED,
467
			    fmd->path, fmd->files[i].de.d_name, NULL);
468
			continue;
469
		}
470
		/* Is renamed? */
471
		if (0 == IS_DE_NAME_EQ(&files[k].de, &fmd->files[i].de)) {
472
			kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
473
			    fmd->path, files[k].de.d_name, fmd->files[i].de.d_name);
474
			continue;
475
		}
476
		/* Is modified? */
477
		if (0 != memcmp(&fmd->files[i].sb, &files[k].sb, sizeof(struct stat))) {
478
			kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED,
479
			    fmd->path, fmd->files[i].de.d_name, NULL);
480
			continue;
481
		}
482
		/* Not changed. */
483
	}
484
485
err_out:
486
	free(files);
487
}
488
489
static void
490
kqueue_handle_rename(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
491
	int up_dir_fd, found = 0;
492
	readdir_data_t rdd;
493
	struct dirent de;
494
	struct stat sb;
495
	char old_filename[(MAXNAMLEN + 2)];
496
	size_t old_filename_size;
497
498
	if (NULL == kfnm || NULL == fmd)
499
		return;
500
	if (0 != fstat(fmd->fd, &sb) ||
501
	    0 == sb.st_nlink) {
502
notify_removed:
503
		kqueue_file_mon_data_clean(fmd);
504
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
505
		    fmd->path, "", NULL);
506
		return;
507
	}
508
	/* Save old file name. */
509
	old_filename_size = (fmd->path_size - fmd->name_offset - fmd->is_dir);
510
	memcpy(old_filename,
511
	    (fmd->path + fmd->name_offset),
512
	    old_filename_size);
513
	old_filename[old_filename_size] = 0;
514
515
	/* Get parent folder name. */
516
	fmd->path[fmd->name_offset] = 0;
517
	/* Try to open. */
518
	up_dir_fd = open(fmd->path, (OPEN_FILE_FLAGS | O_DIRECTORY));
519
	/* Restore '/' after parent folder. */
520
	fmd->path[fmd->name_offset] = '/';
521
	if (-1 == up_dir_fd ||
522
	    0 != fstat(up_dir_fd, &sb) ||
523
	    0 != readdir_data_start(up_dir_fd, fmd->kfnm->tmpbuf, sb.st_blksize, &rdd)) {
524
		close(up_dir_fd);
525
		return;
526
	}
527
	/* Find new name by inode. */
528
	while (0 == readdir_data_next(&rdd, &de)) {
529
		if (de.d_fileno == sb.st_ino) {
530
			found ++;
531
			break;
532
		}
533
	}
534
	close(up_dir_fd);
535
	if (0 == found)
536
		goto notify_removed; /* Not found. */
537
	/* Update name. */
538
	if (PATH_MAX <= (fmd->name_offset + de.d_namlen))
539
		return; /* Too long. */
540
	memcpy((fmd->path + fmd->name_offset), de.d_name, de.d_namlen);
541
	fmd->path_size = (fmd->name_offset + de.d_namlen);
542
	/* Add last '/' for dir. */
543
	fmd->path[fmd->path_size] = '/';
544
	fmd->path_size += fmd->is_dir;
545
	fmd->path[fmd->path_size] = 0;
546
	/* Notify. */
547
	kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
548
	    fmd->path, old_filename, de.d_name);
549
}
550
551
552
static void
553
kqueue_fnm_delay_call_process(kqueue_fnm_p kfnm, kq_msg_cb forced_msg_cb) {
554
	ssize_t ios;
555
	kqueue_fnm_msg_pkt_t msg;
556
557
	for (;;) {
558
		ios = read(kfnm->pfd[0], &msg, sizeof(msg));
559
		if (0 >= ios)
560
			return;
561
		if (sizeof(msg) != ios ||
562
		    KF_MSG_PKT_MAGIC != msg.magic ||
563
		    (((size_t)msg.msg_cb) ^ ((size_t)msg.fmd)) != msg.chk_sum)
564
			continue;
565
		if (NULL != forced_msg_cb) {
566
			forced_msg_cb(msg.fmd);
567
			continue;
568
		}
569
		if (NULL == msg.msg_cb)
570
			continue;
571
		msg.msg_cb(msg.fmd);
572
	}
573
}
574
575
static int
576
kqueue_fnm_delay_call(kqueue_fnm_p kfnm, kq_msg_cb msg_cb,
577
    kqueue_file_mon_data_p fmd) {
578
	kqueue_fnm_msg_pkt_t msg;
579
580
	if (NULL == kfnm || NULL == fmd)
581
		return (EINVAL);
582
	msg.magic = KF_MSG_PKT_MAGIC;
583
	msg.msg_cb = msg_cb;
584
	msg.fmd = fmd;
585
	msg.chk_sum = (((size_t)msg.msg_cb) ^ ((size_t)msg.fmd));
586
	if (sizeof(msg) == write(kfnm->pfd[1], &msg, sizeof(msg)))
587
		return (0);
588
	return (errno);
589
}
590
591
592
void
593
kqueue_fnm_free(kqueue_fnm_p kfnm) {
594
595
	if (NULL == kfnm)
596
		return;
597
	close(kfnm->fd);
598
	pthread_join(kfnm->tid, NULL);
599
	/* Free all in delay calls queue. */
600
	kqueue_fnm_delay_call_process(kfnm, kqueue_file_mon_data_free);
601
	close(kfnm->pfd[0]);
602
	close(kfnm->pfd[1]);
603
	free(kfnm->tmpbuf);
604
	free(kfnm);
605
}
606
607
kqueue_fnm_p
608
kqueue_fnm_create(int use_thread, kfnm_event_handler_cb cb_func) {
609
	kqueue_fnm_p kfnm;
610
	struct kevent kev;
611
	struct timespec ke_timeout;
612
613
	if (NULL == cb_func)
614
		return (NULL);
615
	kfnm = zalloc(sizeof(kqueue_fnm_t));
616
	if (NULL == kfnm)
617
		return (NULL);
618
	kfnm->fd = kqueue();
619
	if (-1 == kfnm->fd)
620
		goto err_out;
621
	if (-1 == pipe2(kfnm->pfd, O_NONBLOCK))
622
		goto err_out;
623
	kfnm->cb_func = cb_func;
624
625
	bzero(&kev, sizeof(kev));
626
	kev.ident = kfnm->pfd[0];
627
	kev.filter = EVFILT_READ;
628
	kev.flags = (EV_ADD | EV_ENABLE);
629
630
	bzero(&ke_timeout, sizeof(ke_timeout));
631
632
	if (-1 == kevent(kfnm->fd, &kev, 1, NULL, 0, &ke_timeout))
633
		goto err_out;
634
	if (0 != use_thread) {
635
		if (0 != pthread_create(&kfnm->tid, NULL,
636
		    kqueue_fnm_proccess_events_proc, kfnm))
637
			goto err_out;
638
	}
639
	return (kfnm);
640
641
err_out:
642
	kqueue_fnm_free(kfnm);
643
	return (NULL);
644
}
645
646
kqueue_file_mon_data_p
647
kqueue_fnm_add(kqueue_fnm_p kfnm, const char *path, void *udata) {
648
	int error;
649
	kqueue_file_mon_data_p fmd;
650
	
651
	if (NULL == kfnm || NULL == path)
652
		return (NULL);
653
	fmd = kqueue_file_mon_data_alloc(kfnm, path, udata);
654
	if (NULL == fmd)
655
		return (NULL);
656
	/* Shedule delay call to init. */
657
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_init, fmd);
658
	if (0 != error) { /* Error, do no directly init to avoid freezes. */
659
		kqueue_file_mon_data_free(fmd);
660
		return (NULL);
661
	}
662
	return (fmd);
663
}
664
665
void
666
kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
667
	int error;
668
669
	if (NULL == kfnm || NULL == fmd)
670
		return;
671
	/* Cancel notifications. */
672
	close(fmd->fd);
673
	fmd->fd = -1;
674
	/* Shedule delay call to free. */
675
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_free, fmd);
676
	if (0 == error)
677
		return;
678
	/* Error, free directly. */
679
	kqueue_file_mon_data_free(fmd);
680
}
681
682
683
static void
684
kqueue_fnm_proccess_event(kqueue_fnm_p kfnm, struct kevent *kev) {
685
	kqueue_file_mon_data_p fmd;
686
687
	if (NULL == kfnm || NULL == kev)
688
		return;
689
	if (kev->ident == (uintptr_t)kfnm->pfd[0] &&
690
	    kev->filter == EVFILT_READ) { /* Handle delay calls. */
691
		kqueue_fnm_delay_call_process(kfnm, NULL);
692
		return;
693
	}
694
	if (EVFILT_VNODE != kev->filter ||
695
	    0 == kev->udata)
696
		return; /* Unknown event or no associated data, skip. */
697
	fmd = (kqueue_file_mon_data_p)kev->udata;
698
699
	if (EV_ERROR & kev->flags) {
700
		kev->flags |= NOTE_REVOKE; /* Treat error as unmount. */
701
	}
702
	if (NOTE_RENAME & kev->fflags) {
703
		kqueue_handle_rename(kfnm, fmd);
704
	}
705
	if ((NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB) & kev->fflags) {
706
		kqueue_handle_changes(kfnm, fmd);
707
	}
708
	if ((NOTE_DELETE | NOTE_REVOKE) & kev->fflags) {
709
		kqueue_file_mon_data_clean(fmd);
710
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
711
		    fmd->path, "", NULL);
712
	}
713
}
714
715
void *
716
kqueue_fnm_proccess_events_proc(void *data) {
717
	struct kevent kev;
718
	kqueue_fnm_p kfnm = data;
719
720
	if (NULL == kfnm)
721
		return (NULL);
722
	/* Get and proccess events, no wait. */
723
	while (0 < kevent(kfnm->fd, NULL, 0, &kev, 1, NULL)) {
724
		kqueue_fnm_proccess_event(kfnm, &kev);
725
	}
726
727
	return (NULL);
728
}
729
730
731
int
732
kqueue_fnm_get_ev_recv_fd(kqueue_fnm_p kfnm) {
733
734
	if (NULL == kfnm || NULL != kfnm->tid)
735
		return (-1);
736
	return (kfnm->fd);
737
}
738
739
void
740
kqueue_fnm_proccess_events(kqueue_fnm_p kfnm) {
741
	struct kevent kev;
742
	struct timespec ke_timeout;
743
744
	if (NULL == kfnm || NULL != kfnm->tid)
745
		return;
746
	/* Get and proccess events, no wait. */
747
	bzero(&ke_timeout, sizeof(ke_timeout));
748
	while (0 < kevent(kfnm->fd, NULL, 0, &kev, 1, &ke_timeout)) {
749
		kqueue_fnm_proccess_event(kfnm, &kev);
750
	}
751
}
(-)devel/glib20/files/kqueue_fnm.h.in (+67 lines)
Line 0 Link Here
1
/*-
2
 * Copyright (c) 2016 - 2017 Rozhuk Ivan <rozhuk.im@gmail.com>
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 *
26
 * Author: Rozhuk Ivan <rozhuk.im@gmail.com>
27
 *
28
 */
29
30
31
32
#ifndef __KQUEUE_FILE_NOTIFY_MONITOR_H__
33
#define __KQUEUE_FILE_NOTIFY_MONITOR_H__
34
35
#include <sys/param.h>
36
#include <sys/types.h>
37
#include <inttypes.h>
38
39
40
typedef struct kqueue_file_nonify_monitor_s	*kqueue_fnm_p;
41
typedef struct kqueue_file_mon_data_s		*kqueue_file_mon_data_p;
42
43
typedef void (*kfnm_event_handler_cb)(kqueue_fnm_p kfnm,
44
			       kqueue_file_mon_data_p fmd, void *udata,
45
			       uint32_t event,
46
			       const char *base,
47
			       const char *filename,
48
			       const char *new_filename);
49
#define KF_EVENT_NOT_CHANGED	0 /* Internal use. */
50
#define KF_EVENT_CREATED	1
51
#define KF_EVENT_DELETED	2
52
#define KF_EVENT_RENAMED	3
53
#define KF_EVENT_CHANGED	4
54
55
56
kqueue_fnm_p	kqueue_fnm_create(int use_thread, kfnm_event_handler_cb cb_func);
57
void		kqueue_fnm_free(kqueue_fnm_p kfnm);
58
59
kqueue_file_mon_data_p	kqueue_fnm_add(kqueue_fnm_p kfnm,
60
			    const char *path, void *udata);
61
void		kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd);
62
63
int		kqueue_fnm_get_ev_recv_fd(kqueue_fnm_p kfnm);
64
void		kqueue_fnm_proccess_events(kqueue_fnm_p kfnm);
65
66
67
#endif /* __KQUEUE_FILE_NOTIFY_MONITOR_H__ */

Return to bug 214338