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
pre-configure-FAM_ALTBACKEND-on:
107
	@${CP} -f ${FILESDIR}/gkqueuefilemonitor.c ${WRKSRC}/gio/kqueue/gkqueuefilemonitor.c
108
	@${CP} ${FILESDIR}/kqueue_fnm.c ${WRKSRC}/gio/kqueue/kqueue_fnm.c
109
	@${CP} ${FILESDIR}/kqueue_fnm.h ${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 (+194 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
/* Defaults. */
42
#ifndef KQUEUE_MON_LOCAL_SUBFILES
43
#define KQUEUE_MON_LOCAL_SUBFILES	1
44
#endif
45
46
47
static GMutex			kqueue_lock;
48
static volatile kqueue_fnm_p	kqueue_fnm = NULL;
49
/* Exclude from file changes monitoring, watch only for dirs. */
50
static const char *non_local_fs[] = {
51
	"fusefs.sshfs",
52
	NULL
53
};
54
55
#define G_TYPE_KQUEUE_FILE_MONITOR      (g_kqueue_file_monitor_get_type())
56
#define G_KQUEUE_FILE_MONITOR(inst)     (G_TYPE_CHECK_INSTANCE_CAST((inst), \
57
					 G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor))
58
59
typedef GLocalFileMonitorClass	GKqueueFileMonitorClass;
60
61
typedef struct {
62
	GLocalFileMonitor	parent_instance;
63
	kqueue_file_mon_data_p	fmd;
64
} GKqueueFileMonitor;
65
66
GType g_kqueue_file_monitor_get_type(void);
67
G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
68
       g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
69
               g_define_type_id,
70
               "kqueue",
71
               10))
72
73
74
static void
75
kqueue_event_handler(kqueue_fnm_p kfnm,
76
    kqueue_file_mon_data_p fmd, void *udata, uint32_t event,
77
    const char *base, const char *filename, const char *new_filename) {
78
	static const uint32_t kfnm_to_glib_map[] = {
79
		0,				/* KF_EVENT_NOT_CHANGED */
80
		G_FILE_MONITOR_EVENT_CREATED,	/* KF_EVENT_CREATED */
81
		G_FILE_MONITOR_EVENT_DELETED,	/* KF_EVENT_DELETED */
82
		G_FILE_MONITOR_EVENT_RENAMED,	/* KF_EVENT_RENAMED */
83
		G_FILE_MONITOR_EVENT_CHANGED	/* KF_EVENT_CHANGED */
84
	};
85
86
	if (NULL == kfnm || NULL == filename ||
87
	    KF_EVENT_CREATED > event ||
88
	    KF_EVENT_CHANGED < event)
89
		return;
90
	g_file_monitor_source_handle_event(udata,
91
	    kfnm_to_glib_map[event],
92
	    filename, new_filename, NULL,
93
	    g_get_monotonic_time());
94
}
95
96
static gboolean
97
g_kqueue_file_monitor_is_supported(void) {
98
	kqueue_file_mon_settings_t kfms;
99
100
	if (NULL != kqueue_fnm)
101
		return (TRUE);
102
	/* Init only once. */
103
	g_mutex_lock(&kqueue_lock);
104
	if (NULL != kqueue_fnm) {
105
		g_mutex_unlock(&kqueue_lock);
106
		return (TRUE); /* Initialized while wait lock. */
107
	}
108
109
	memset(&kfms, 0x00, sizeof(kqueue_file_mon_settings_t));
110
	kfms.mon_local_subfiles = KQUEUE_MON_LOCAL_SUBFILES;
111
	kfms.local_fs = NULL;
112
	kfms.non_local_fs = non_local_fs;
113
114
	kqueue_fnm = kqueue_fnm_create(&kfms, kqueue_event_handler);
115
	if (NULL == kqueue_fnm) {
116
		g_mutex_unlock(&kqueue_lock);
117
		return (FALSE); /* Init fail. */
118
	}
119
	g_mutex_unlock(&kqueue_lock);
120
121
	return (TRUE);
122
}
123
124
static gboolean
125
g_kqueue_file_monitor_cancel(GFileMonitor *monitor) {
126
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(monitor);
127
128
	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
129
	gffm->fmd = NULL;
130
131
	return (TRUE);
132
}
133
134
static void
135
g_kqueue_file_monitor_finalize(GObject *object) {
136
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(object);
137
138
	kqueue_fnm_del(kqueue_fnm, gffm->fmd);
139
	gffm->fmd = NULL;
140
}
141
142
static void
143
g_kqueue_file_monitor_start(GLocalFileMonitor *local_monitor,
144
    const gchar *dirname, const gchar *basename,
145
    const gchar *filename, GFileMonitorSource *source) {
146
	GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(local_monitor);
147
148
	g_assert(NULL != kqueue_fnm);
149
	g_source_ref((GSource*)source);
150
151
	if (NULL == filename) {
152
		filename = dirname;
153
	}
154
	gffm->fmd = kqueue_fnm_add(kqueue_fnm, filename, source);
155
}
156
157
static void
158
g_kqueue_file_monitor_init(GKqueueFileMonitor *monitor) {
159
160
}
161
162
static void
163
g_kqueue_file_monitor_class_init(GKqueueFileMonitorClass *class) {
164
	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
165
	GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS(class);
166
167
	class->is_supported = g_kqueue_file_monitor_is_supported;
168
	class->start = g_kqueue_file_monitor_start;
169
	class->mount_notify = TRUE; /* TODO: ??? */
170
	file_monitor_class->cancel = g_kqueue_file_monitor_cancel;
171
	gobject_class->finalize = g_kqueue_file_monitor_finalize;
172
}
173
174
static void
175
g_kqueue_file_monitor_class_finalize(GKqueueFileMonitorClass *class) {
176
177
}
178
179
void
180
g_io_module_load(GIOModule *module) {
181
182
	g_type_module_use(G_TYPE_MODULE(module));
183
184
	g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
185
	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
186
	g_io_extension_point_implement(G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
187
	    G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10);
188
}
189
190
void
191
g_io_module_unload(GIOModule *module) {
192
193
	g_assert_not_reached();
194
}
(-)devel/glib20/files/kqueue_fnm.c (+885 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/mount.h>
35
#include <sys/fcntl.h> /* open, fcntl */
36
37
#include <inttypes.h>
38
#include <stdlib.h> /* malloc, exit */
39
#include <unistd.h> /* close, write, sysconf */
40
#include <string.h> /* bcopy, bzero, memcpy, memmove, memset, strerror... */
41
#include <dirent.h> /* opendir, readdir */
42
#include <errno.h>
43
#include <pthread.h>
44
45
#include "kqueue_fnm.h"
46
47
48
/* Preallocate items count. */
49
#ifndef FILES_ALLOC_BLK_SIZE
50
#	define FILES_ALLOC_BLK_SIZE	32
51
#endif
52
53
54
typedef struct file_info_s { /* Directory file. */
55
	int		fd;		/* For notify kqueue(). */
56
	struct dirent 	de;		/* d_reclen used for action. */
57
	struct stat	sb;
58
} file_info_t, *file_info_p;
59
60
61
typedef struct readdir_data_s {
62
	int		fd;
63
	uint8_t		*buf;
64
	size_t		buf_size;
65
	size_t		buf_used;
66
	size_t		buf_pos;
67
} readdir_data_t, *readdir_data_p;
68
69
70
typedef struct kqueue_file_mon_data_s {
71
	int		fd;		/* For notify kqueue(). */
72
	int		is_dir;
73
	int		is_local;	/* Is file system local. */
74
	char		path[(PATH_MAX + 2)];
75
	size_t		path_size;
76
	size_t		name_offset;	/* Parent path size. */
77
	size_t		st_blksize;
78
	void		*udata;
79
	kqueue_fnm_p	kfnm;
80
	/* For dir. */
81
	file_info_p	files;
82
	volatile size_t	files_count;
83
	size_t		files_allocated;
84
} kqueue_file_mon_data_t;
85
86
87
typedef struct kqueue_file_nonify_monitor_s {
88
	int		fd;		/* kqueue() fd. */
89
	int		pfd[2];		/* pipe queue specific. */
90
	uint8_t		*tmpbuf;
91
	size_t		tmpbuf_size;
92
	kfnm_event_handler_cb cb_func;	/* Callback on dir/file change. */
93
	kqueue_file_mon_settings_t s;
94
	pthread_t	tid;
95
} kqueue_fnm_t;
96
97
98
typedef void (*kq_msg_cb)(void *arg);
99
100
typedef struct kqueue_file_mon_msg_pkt_s {
101
	size_t		magic;
102
	kq_msg_cb	msg_cb;
103
	void		*arg;
104
	size_t		chk_sum;
105
} kqueue_fnm_msg_pkt_t, *kqueue_fnm_msg_pkt_p;
106
107
#define KF_MSG_PKT_MAGIC	0xffddaa00
108
109
110
#ifndef O_NOATIME
111
#	define O_NOATIME	0
112
#endif
113
#ifndef O_EVTONLY
114
#	define O_EVTONLY	O_RDONLY
115
#endif
116
#define OPEN_FILE_FLAGS		(O_EVTONLY | O_NONBLOCK | O_NOFOLLOW | O_NOATIME | O_CLOEXEC)
117
118
#define EVFILT_VNODE_SUB_FLAGS	(NOTE_WRITE |				\
119
				NOTE_EXTEND |				\
120
				NOTE_ATTRIB |				\
121
				NOTE_LINK |				\
122
				NOTE_CLOSE_WRITE)
123
124
#define EVFILT_VNODE_FLAGS_ALL	(NOTE_DELETE |				\
125
				EVFILT_VNODE_SUB_FLAGS |		\
126
				NOTE_RENAME |				\
127
				NOTE_REVOKE)
128
129
#ifndef _GENERIC_DIRSIZ
130
#	define _GENERIC_DIRSIZ(__de)	MIN((__de)->d_reclen, sizeof(struct dirent))
131
#endif
132
133
#define IS_NAME_DOTS(__name)	('.' == (__name)[0] &&			\
134
				 ('\0' == (__name)[1] || 		\
135
				  ('.' == (__name)[1] && '\0' == (__name)[2])))
136
#define IS_DE_NAME_EQ(__de1, __de2)  (0 == mem_cmpn((__de1)->d_name,	\
137
						    (__de1)->d_namlen,	\
138
						    (__de2)->d_name,	\
139
						    (__de2)->d_namlen))
140
#define zalloc(__size)		calloc(1, (__size))
141
142
#if (defined(__FreeBSD_version) && __FreeBSD_version >= 1100000) || defined(HAVE_REALLOCARRAY)
143
#	define REALLOCARRAY(__mem, __size, __count)	reallocarray((__mem), (__size), (__count))
144
#else /* For old BSD systems. */
145
#	define REALLOCARRAY(__mem, __size, __count)	realloc((__mem), ((__size) * (__count)))
146
#endif
147
148
149
void 	*kqueue_fnm_proccess_events_proc(void *data);
150
151
static inline int
152
mem_cmpn(const void *buf1, const size_t buf1_size,
153
    const void *buf2, const size_t buf2_size) {
154
155
	if (buf1_size != buf2_size)
156
		return (((buf1_size > buf2_size) ? 127 : -127));
157
	if (0 == buf1_size || buf1 == buf2)
158
		return (0);
159
	if (NULL == buf1)
160
		return (-127);
161
	if (NULL == buf2)
162
		return (127);
163
	return (memcmp(buf1, buf2, buf1_size));
164
}
165
166
static int
167
realloc_items(void **items, const size_t item_size,
168
    size_t *allocated, const size_t alloc_blk_cnt, const size_t count) {
169
	size_t allocated_prev, allocated_new;
170
	uint8_t *items_new;
171
172
	if (NULL == items || 0 == item_size || NULL == allocated ||
173
	    0 == alloc_blk_cnt)
174
		return (EINVAL);
175
	allocated_prev = (*allocated);
176
	if (NULL != (*items) &&
177
	    allocated_prev > count &&
178
	    allocated_prev <= (count + alloc_blk_cnt))
179
		return (0);
180
	allocated_new = (((count / alloc_blk_cnt) + 1) * alloc_blk_cnt);
181
	items_new = (uint8_t*)REALLOCARRAY((*items), item_size, allocated_new);
182
	if (NULL == items_new) /* Realloc fail! */
183
		return (ENOMEM);
184
185
	if (allocated_new > allocated_prev) { /* Init new mem. */
186
		memset((items_new + (allocated_prev * item_size)), 0x00,
187
		    ((allocated_new - allocated_prev) * item_size));
188
	}
189
	(*items) = items_new;
190
	(*allocated) = allocated_new;
191
192
	return (0);
193
}
194
195
196
static int
197
readdir_data_start(int fd, uint8_t *buf, size_t buf_size, readdir_data_p rdd) {
198
199
	if (-1 == fd || NULL == buf || 0 == buf_size || NULL == rdd)
200
		return (EINVAL);
201
	if (-1 == lseek(fd, 0, SEEK_SET))
202
		return (errno);
203
	memset(rdd, 0x00, sizeof(readdir_data_t));
204
	rdd->fd = fd;
205
	rdd->buf = buf;
206
	rdd->buf_size = buf_size;
207
208
	return (0);
209
}
210
211
static int
212
readdir_data_next(readdir_data_p rdd, struct dirent *de) {
213
	int ios;
214
	uint8_t *ptr;
215
216
	if (NULL == rdd || NULL == de)
217
		return (EINVAL);
218
219
retry:
220
	if (rdd->buf_used <= rdd->buf_pos) {
221
		ios = getdents(rdd->fd, (char*)rdd->buf, (int)rdd->buf_size);
222
		if (-1 == ios)
223
			return (errno);
224
		rdd->buf_used = (size_t)ios;
225
		rdd->buf_pos = 0;
226
		if (0 == ios)
227
			return (ESPIPE); /* EOF. */
228
	}
229
	/* Keep data aligned. */
230
	ptr = (rdd->buf + rdd->buf_pos);
231
	memcpy(de, ptr, (sizeof(struct dirent) - sizeof(de->d_name)));
232
	if (0 == de->d_reclen)
233
		return (ESPIPE); /* EOF. */
234
	rdd->buf_pos += de->d_reclen;
235
#ifdef DT_WHT
236
	if (DT_WHT == de->d_type)
237
		goto retry;
238
#endif
239
	memcpy(de, ptr, _GENERIC_DIRSIZ(de));
240
	if (IS_NAME_DOTS(de->d_name))
241
		goto retry;
242
	return (0);
243
}
244
245
246
static int
247
file_info_find_de(file_info_p files, size_t files_count,
248
    struct dirent *de, size_t *idx) {
249
	size_t i;
250
251
	if (NULL == files || NULL == de || NULL == idx)
252
		return (0);
253
	for (i = 0; i < files_count; i ++) {
254
		if (de->d_type != files[i].de.d_type)
255
			continue;
256
		if (de->d_fileno != files[i].de.d_fileno &&
257
		    0 == IS_DE_NAME_EQ(de, &files[i].de))
258
			continue;
259
		(*idx) = i;
260
		return (1);
261
	}
262
	(*idx) = files_count;
263
	return (0);
264
}
265
266
static int
267
file_info_find_de_fileno(file_info_p files, size_t files_count,
268
    struct dirent *de, size_t *idx) {
269
	size_t i;
270
271
	if (NULL == files || NULL == de || NULL == idx)
272
		return (0);
273
	for (i = 0; i < files_count; i ++) {
274
		if (de->d_type != files[i].de.d_type ||
275
		    de->d_fileno != files[i].de.d_fileno)
276
			continue;
277
		(*idx) = i;
278
		return (1);
279
	}
280
	(*idx) = files_count;
281
	return (0);
282
}
283
284
static int
285
file_info_find_de_name(file_info_p files, size_t files_count,
286
    struct dirent *de, size_t *idx) {
287
	size_t i;
288
289
	if (NULL == files || NULL == de || NULL == idx)
290
		return (0);
291
	for (i = 0; i < files_count; i ++) {
292
		if (de->d_type != files[i].de.d_type ||
293
		    0 == IS_DE_NAME_EQ(de, &files[i].de))
294
			continue;
295
		(*idx) = i;
296
		return (1);
297
	}
298
	(*idx) = files_count;
299
	return (0);
300
}
301
302
303
static int
304
is_fs_local(struct statfs *stfs, const char **local_fs, const char **non_local_fs) {
305
	size_t i;
306
307
	if (NULL == stfs)
308
		return (0);
309
	if (NULL != local_fs) {
310
		for (i = 0; NULL != local_fs[i]; i ++) {
311
			if (0 == strncmp(stfs->f_fstypename, local_fs[i],
312
			    sizeof(stfs->f_fstypename)))
313
				return (1);
314
		}
315
	}
316
	if (0 == (MNT_LOCAL & stfs->f_flags))
317
		return (0);
318
	if (NULL != non_local_fs) {
319
		for (i = 0; NULL != non_local_fs[i]; i ++) {
320
			if (0 == strncmp(stfs->f_fstypename, non_local_fs[i],
321
			    sizeof(stfs->f_fstypename)))
322
				return (0);
323
		}
324
	}
325
	return (1);
326
}
327
328
329
static void
330
kqueue_file_mon_data_clean(kqueue_file_mon_data_p fmd) {
331
	size_t i;
332
333
	if (NULL == fmd)
334
		return;
335
	if (-1 != fmd->fd) {
336
		close(fmd->fd);
337
		fmd->fd = -1;
338
	}
339
	if (0 != fmd->is_local) { /* Stop monitoring files/dirs. */
340
		for (i = 0; i < fmd->files_count; i ++) {
341
			if (-1 == fmd->files[i].fd)
342
				continue;
343
			close(fmd->files[i].fd);
344
		}
345
	}
346
	free(fmd->files);
347
	fmd->files = NULL;
348
	fmd->files_count = 0;
349
	fmd->files_allocated = 0;
350
}
351
352
static void
353
kqueue_file_mon_data_free(void *arg) {
354
	kqueue_file_mon_data_p fmd = arg;
355
356
	if (NULL == fmd)
357
		return;
358
	kqueue_file_mon_data_clean(fmd);
359
	free(fmd);
360
}
361
362
static kqueue_file_mon_data_p
363
kqueue_file_mon_data_alloc(kqueue_fnm_p kfnm, const char *path, void *udata) {
364
	kqueue_file_mon_data_p fmd;
365
366
	if (NULL == kfnm || NULL == path)
367
		return (NULL);
368
	fmd = zalloc(sizeof(kqueue_file_mon_data_t));
369
	if (NULL == fmd)
370
		return (NULL);
371
	/* Remember args. */
372
	fmd->path_size = strlcpy(fmd->path, path, PATH_MAX);
373
	fmd->name_offset = fmd->path_size;
374
	fmd->udata = udata;
375
	fmd->kfnm = kfnm;
376
377
	return (fmd);
378
}
379
380
static int
381
kqueue_file_mon_data_readdir(kqueue_file_mon_data_p fmd, size_t expected_count) {
382
	int error;
383
	struct dirent *de;
384
	uint8_t *tmpbuf;
385
	file_info_p tmfi;
386
	readdir_data_t rdd;
387
388
	if (NULL == fmd || 0 == fmd->is_dir)
389
		return (EINVAL);
390
391
	/* Get temp buf. */
392
	if (fmd->st_blksize > fmd->kfnm->tmpbuf_size) {
393
		tmpbuf = realloc(fmd->kfnm->tmpbuf, fmd->st_blksize);
394
		if (NULL == tmpbuf)
395
			return (ENOMEM);
396
		fmd->kfnm->tmpbuf = tmpbuf;
397
		fmd->kfnm->tmpbuf_size = fmd->st_blksize;
398
	}
399
400
	free(fmd->files);
401
	fmd->files = NULL;
402
	fmd->files_count = 0;
403
	fmd->files_allocated = 0;
404
	/* Pre allocate. */
405
	if (0 != realloc_items((void**)&fmd->files,
406
	    sizeof(file_info_t), &fmd->files_allocated,
407
	    FILES_ALLOC_BLK_SIZE, (expected_count + 1)))
408
		return (ENOMEM);
409
410
	error = readdir_data_start(fmd->fd, fmd->kfnm->tmpbuf, fmd->st_blksize, &rdd);
411
	if (0 != error)
412
		return (error);
413
	for (;;) {
414
		if (0 != realloc_items((void**)&fmd->files,
415
		    sizeof(file_info_t), &fmd->files_allocated,
416
		    FILES_ALLOC_BLK_SIZE, fmd->files_count)) {
417
			free(fmd->files);
418
			fmd->files = NULL;
419
			fmd->files_count = 0;
420
			fmd->files_allocated = 0;
421
			return (ENOMEM);
422
		}
423
		de = &fmd->files[fmd->files_count].de; /* Use short name. */
424
		/* Get file name from folder. */
425
		if (0 != readdir_data_next(&rdd, de))
426
			break;
427
		/* Get file attrs. */
428
		if (0 != fstatat(fmd->fd, de->d_name,
429
		    &fmd->files[fmd->files_count].sb,
430
		    AT_SYMLINK_NOFOLLOW)) {
431
			memset(&fmd->files[fmd->files_count].sb, 0x00,
432
			    sizeof(struct stat));
433
		}
434
		fmd->files[fmd->files_count].fd = -1;
435
		fmd->files_count ++;
436
	}
437
	/* Mem compact. */
438
	tmfi = REALLOCARRAY(fmd->files, sizeof(file_info_t), (fmd->files_count + 1));
439
	if (NULL != tmfi) { /* realloc ok. */
440
		fmd->files = tmfi;
441
		fmd->files_allocated = (fmd->files_count + 1);
442
	}
443
444
	return (0); /* OK. */
445
}
446
447
448
static void
449
kqueue_file_mon_data_init(void *arg) {
450
	kqueue_file_mon_data_p fmd = arg;
451
	size_t i;
452
	struct stat sb;
453
	struct statfs stfs;
454
	struct kevent kev;
455
456
	if (NULL == fmd)
457
		return;
458
	fmd->fd = open(fmd->path, OPEN_FILE_FLAGS);
459
	if (-1 == fmd->fd)
460
		return;
461
	if (0 != fstat(fmd->fd, &sb))
462
		goto err_out;
463
	fmd->st_blksize = (size_t)sb.st_blksize;
464
	
465
	/* Get parent folder name. */
466
	if (S_ISDIR(sb.st_mode)) {
467
		fmd->is_dir = 1;
468
		/* Be sure that folder contain trailing '/'. */
469
		if ('/' != fmd->path[(fmd->path_size - 1)]) {
470
			fmd->path[fmd->path_size] = '/';
471
			fmd->path_size ++;
472
			fmd->path[fmd->path_size] = 0;
473
		}
474
		/* Skip last '/' for parent dir search. */
475
		fmd->name_offset = (fmd->path_size - 1);
476
	}
477
478
	/* Is file system local? */
479
	if (0 != fmd->is_dir &&
480
	    0 != fmd->kfnm->s.mon_local_subfiles &&
481
	    0 == fstatfs(fmd->fd, &stfs)) {
482
		fmd->is_local = is_fs_local(&stfs, fmd->kfnm->s.local_fs,
483
		    fmd->kfnm->s.non_local_fs);
484
	}
485
486
	/* Find parent dir path size. */
487
	while (0 < fmd->name_offset && '/' != fmd->path[(fmd->name_offset - 1)]) {
488
		fmd->name_offset --;
489
	}
490
491
	/* Dir special processing. */
492
	if (0 != fmd->is_dir) {
493
		/* Read and remember dir content. */
494
		if (0 != kqueue_file_mon_data_readdir(fmd, FILES_ALLOC_BLK_SIZE))
495
			goto err_out;
496
	}
497
	/* Add to kqueue. */
498
	EV_SET(&kev, fmd->fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR),
499
	    EVFILT_VNODE_FLAGS_ALL, 0, fmd);
500
	if (-1 == kevent(fmd->kfnm->fd, &kev, 1, NULL, 0, NULL))
501
		goto err_out;
502
	if (0 != fmd->is_local) { /* Add monitor sub files/dirs, ignory errors. */
503
		for (i = 0; i < fmd->files_count; i ++) {
504
			fmd->files[i].fd = openat(fmd->fd,
505
			    fmd->files[i].de.d_name, OPEN_FILE_FLAGS);
506
			if (-1 == fmd->files[i].fd)
507
				continue;
508
			EV_SET(&kev, fmd->files[i].fd, EVFILT_VNODE,
509
			    (EV_ADD | EV_CLEAR),
510
			    EVFILT_VNODE_SUB_FLAGS, 0, fmd);
511
			kevent(fmd->kfnm->fd, &kev, 1, NULL, 0, NULL);
512
		}
513
	}
514
515
	return; /* OK. */
516
517
err_out:
518
	kqueue_file_mon_data_clean(fmd);
519
}
520
521
522
static void
523
kqueue_handle_changes(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
524
	struct stat sb;
525
	struct kevent kev;
526
	size_t i, k, files_count;
527
	file_info_p files;
528
529
	if (NULL == kfnm || NULL == fmd)
530
		return;
531
	if (0 != fstat(fmd->fd, &sb) ||
532
	    0 == sb.st_nlink) {
533
		kqueue_file_mon_data_clean(fmd);
534
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
535
		    fmd->path, "", NULL);
536
		return;
537
	}
538
	if (0 == fmd->is_dir) {
539
		fmd->path[fmd->name_offset] = 0;
540
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED,
541
		    fmd->path, (fmd->path + fmd->name_offset), NULL);
542
		fmd->path[fmd->name_offset] = '/';
543
		return;
544
	}
545
546
	/* Dir processing. */
547
548
	/* Save prev. */
549
	files = fmd->files;
550
	files_count = fmd->files_count;
551
	fmd->files = NULL;
552
	fmd->files_count = 0;
553
	/* Update dir. */
554
	if (0 != kqueue_file_mon_data_readdir(fmd, files_count)) {
555
		/* Restore prev state on fail. */
556
		fmd->files = files;
557
		fmd->files_count = files_count;
558
		return;
559
	}
560
	/* Notify removed first. */
561
	for (i = 0; i < files_count; i ++) {
562
		if (0 != file_info_find_de(fmd->files, fmd->files_count, &files[i].de, &k)) /* Deleted? */
563
			continue;
564
		if (-1 != files[i].fd) {
565
			close(files[i].fd);
566
		}
567
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
568
			    fmd->path, files[i].de.d_name, NULL);
569
	}
570
	/* Notify. */
571
	for (i = 0; i < fmd->files_count; i ++) {
572
		/* Is new file/folder? */
573
		if (0 == file_info_find_de_fileno(files, files_count, &fmd->files[i].de, &k) &&
574
		    0 == file_info_find_de_name(files, files_count, &fmd->files[i].de, &k)) { /* Add new. */
575
			if (0 != fmd->is_local) { /* Add monitor sub files/dirs, ignory errors. */
576
				fmd->files[i].fd = openat(fmd->fd,
577
				    fmd->files[i].de.d_name, OPEN_FILE_FLAGS);
578
				if (-1 != fmd->files[i].fd) {
579
					EV_SET(&kev, fmd->files[i].fd,
580
					    EVFILT_VNODE,
581
					    (EV_ADD | EV_CLEAR),
582
					    EVFILT_VNODE_SUB_FLAGS, 0, fmd);
583
					kevent(kfnm->fd, &kev, 1, NULL, 0, NULL);
584
				}
585
			}
586
			kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CREATED,
587
			    fmd->path, fmd->files[i].de.d_name, NULL);
588
			continue;
589
		}
590
		fmd->files[i].fd = files[k].fd; /* Keep file fd. */
591
		/* Is renamed? */
592
		if (0 == IS_DE_NAME_EQ(&files[k].de, &fmd->files[i].de)) {
593
			kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
594
			    fmd->path, files[k].de.d_name, fmd->files[i].de.d_name);
595
			continue;
596
		}
597
		/* Is modified? */
598
		if (0 != memcmp(&fmd->files[i].sb, &files[k].sb, sizeof(struct stat))) {
599
			kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED,
600
			    fmd->path, fmd->files[i].de.d_name, NULL);
601
			continue;
602
		}
603
		/* Not changed. */
604
	}
605
606
	free(files);
607
}
608
609
static void
610
kqueue_handle_rename(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
611
	int up_dir_fd, found = 0;
612
	readdir_data_t rdd;
613
	struct dirent de;
614
	struct stat sb;
615
	char old_filename[(MAXNAMLEN + 2)];
616
	size_t old_filename_size;
617
618
	if (NULL == kfnm || NULL == fmd)
619
		return;
620
	if (0 != fstat(fmd->fd, &sb) ||
621
	    0 == sb.st_nlink) {
622
notify_removed:
623
		kqueue_file_mon_data_clean(fmd);
624
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
625
		    fmd->path, "", NULL);
626
		return;
627
	}
628
	/* Save old file name. */
629
	old_filename_size = (fmd->path_size - fmd->name_offset - (size_t)fmd->is_dir);
630
	memcpy(old_filename,
631
	    (fmd->path + fmd->name_offset),
632
	    old_filename_size);
633
	old_filename[old_filename_size] = 0;
634
635
	/* Get parent folder name. */
636
	fmd->path[fmd->name_offset] = 0;
637
	/* Try to open. */
638
	up_dir_fd = open(fmd->path, (OPEN_FILE_FLAGS | O_DIRECTORY));
639
	/* Restore '/' after parent folder. */
640
	fmd->path[fmd->name_offset] = '/';
641
	if (-1 == up_dir_fd ||
642
	    0 != fstat(up_dir_fd, &sb) ||
643
	    0 != readdir_data_start(up_dir_fd, fmd->kfnm->tmpbuf, (size_t)sb.st_blksize, &rdd)) {
644
		close(up_dir_fd);
645
		return;
646
	}
647
	/* Find new name by inode. */
648
	while (0 == readdir_data_next(&rdd, &de)) {
649
		if (de.d_fileno == sb.st_ino) {
650
			found ++;
651
			break;
652
		}
653
	}
654
	close(up_dir_fd);
655
	if (0 == found)
656
		goto notify_removed; /* Not found. */
657
	/* Update name. */
658
	if (PATH_MAX <= (fmd->name_offset + de.d_namlen))
659
		return; /* Too long. */
660
	memcpy((fmd->path + fmd->name_offset), de.d_name, de.d_namlen);
661
	fmd->path_size = (fmd->name_offset + de.d_namlen);
662
	/* Add last '/' for dir. */
663
	fmd->path[fmd->path_size] = '/';
664
	fmd->path_size += (size_t)fmd->is_dir;
665
	fmd->path[fmd->path_size] = 0;
666
	/* Notify. */
667
	kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED,
668
	    fmd->path, old_filename, de.d_name);
669
}
670
671
672
static void
673
kqueue_fnm_delay_call_process(kqueue_fnm_p kfnm, kq_msg_cb forced_msg_cb) {
674
	ssize_t ios;
675
	kqueue_fnm_msg_pkt_t msg;
676
677
	for (;;) {
678
		ios = read(kfnm->pfd[0], &msg, sizeof(msg));
679
		if (0 >= ios)
680
			return;
681
		if (sizeof(msg) != ios ||
682
		    KF_MSG_PKT_MAGIC != msg.magic ||
683
		    (((size_t)msg.msg_cb) ^ ((size_t)msg.arg)) != msg.chk_sum)
684
			continue;
685
		if (NULL != forced_msg_cb) {
686
			forced_msg_cb(msg.arg);
687
			continue;
688
		}
689
		if (NULL == msg.msg_cb)
690
			continue;
691
		msg.msg_cb(msg.arg);
692
	}
693
}
694
695
static int
696
kqueue_fnm_delay_call(kqueue_fnm_p kfnm, kq_msg_cb msg_cb,
697
    void *arg) {
698
	kqueue_fnm_msg_pkt_t msg;
699
700
	if (NULL == kfnm || NULL == arg)
701
		return (EINVAL);
702
	msg.magic = KF_MSG_PKT_MAGIC;
703
	msg.msg_cb = msg_cb;
704
	msg.arg = arg;
705
	msg.chk_sum = (((size_t)msg.msg_cb) ^ ((size_t)msg.arg));
706
	if (sizeof(msg) == write(kfnm->pfd[1], &msg, sizeof(msg)))
707
		return (0);
708
	return (errno);
709
}
710
711
712
static void
713
kqueue_fnm_free_cb(void *arg) {
714
	kqueue_fnm_p kfnm = arg;
715
716
	if (NULL == kfnm)
717
		return;
718
	close(kfnm->fd);
719
	kfnm->fd = -1;
720
}
721
void
722
kqueue_fnm_free(kqueue_fnm_p kfnm) {
723
724
	if (NULL == kfnm)
725
		return;
726
	kqueue_fnm_delay_call(kfnm, kqueue_fnm_free_cb, kfnm);
727
	pthread_join(kfnm->tid, NULL);
728
	/* Free all in delay calls queue. */
729
	close(kfnm->pfd[1]);
730
	kqueue_fnm_delay_call_process(kfnm, kqueue_file_mon_data_free);
731
	close(kfnm->pfd[0]);
732
	free(kfnm->tmpbuf);
733
	free(kfnm);
734
}
735
736
kqueue_fnm_p
737
kqueue_fnm_create(kqueue_file_mon_settings_p s, kfnm_event_handler_cb cb_func) {
738
	kqueue_fnm_p kfnm;
739
	struct kevent kev;
740
741
	if (NULL == s || NULL == cb_func)
742
		return (NULL);
743
	kfnm = zalloc(sizeof(kqueue_fnm_t));
744
	if (NULL == kfnm)
745
		return (NULL);
746
	kfnm->fd = kqueue();
747
	if (-1 == kfnm->fd)
748
		goto err_out;
749
	if (-1 == pipe2(kfnm->pfd, O_NONBLOCK))
750
		goto err_out;
751
	kfnm->cb_func = cb_func;
752
	memcpy(&kfnm->s, s, sizeof(kqueue_file_mon_settings_t));
753
754
	EV_SET(&kev, kfnm->pfd[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
755
	if (-1 == kevent(kfnm->fd, &kev, 1, NULL, 0, NULL))
756
		goto err_out;
757
	if (0 != pthread_create(&kfnm->tid, NULL,
758
	    kqueue_fnm_proccess_events_proc, kfnm))
759
		goto err_out;
760
761
	return (kfnm);
762
763
err_out:
764
	kqueue_fnm_free(kfnm);
765
	return (NULL);
766
}
767
768
kqueue_file_mon_data_p
769
kqueue_fnm_add(kqueue_fnm_p kfnm, const char *path, void *udata) {
770
	int error;
771
	kqueue_file_mon_data_p fmd;
772
773
	if (NULL == kfnm || NULL == path)
774
		return (NULL);
775
	fmd = kqueue_file_mon_data_alloc(kfnm, path, udata);
776
	if (NULL == fmd)
777
		return (NULL);
778
	/* Shedule delay call to init. */
779
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_init, fmd);
780
	if (0 != error) { /* Error, do no directly init to avoid freezes. */
781
		kqueue_file_mon_data_free(fmd);
782
		return (NULL);
783
	}
784
	return (fmd);
785
}
786
787
void
788
kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) {
789
	int error;
790
791
	if (NULL == kfnm || NULL == fmd)
792
		return;
793
	/* Cancel notifications. */
794
	close(fmd->fd);
795
	fmd->fd = -1;
796
	/* Shedule delay call to free. */
797
	error = kqueue_fnm_delay_call(kfnm, kqueue_file_mon_data_free, fmd);
798
	if (0 == error)
799
		return;
800
	/* Error, free directly. */
801
	kqueue_file_mon_data_free(fmd);
802
}
803
804
805
static void
806
kqueue_fnm_proccess_event(kqueue_fnm_p kfnm, struct kevent *kev) {
807
	kqueue_file_mon_data_p fmd;
808
	file_info_p fi;
809
	size_t i;
810
	struct stat sb;
811
812
	if (NULL == kfnm || NULL == kev)
813
		return;
814
815
	/* Handle delay calls. */
816
	if (kev->ident == (uintptr_t)kfnm->pfd[0] &&
817
	    kev->filter == EVFILT_READ) {
818
		kqueue_fnm_delay_call_process(kfnm, NULL);
819
		return;
820
	}
821
822
	if (0 == kev->udata)
823
		return; /* No associated data, skip. */
824
825
	/* FS notifications. */
826
	if (EVFILT_VNODE != kev->filter)
827
		return; /* Unknown event, skip. */
828
	fmd = (kqueue_file_mon_data_p)kev->udata;
829
	/* Subdir/file */
830
	if (kev->ident != (uintptr_t)fmd->fd) {
831
		fi = NULL;
832
		for (i = 0; i < fmd->files_count; i ++) {
833
			if (kev->ident != (uintptr_t)fmd->files[i].fd)
834
				continue;
835
			fi = &fmd->files[i];
836
			break;
837
		}
838
		if (NULL != fi) {
839
			/* Get file attrs. */
840
			if (0 != fstat(fi->fd, &sb)) {
841
				memset(&sb, 0x00, sizeof(struct stat));
842
			}
843
			/* Is modified? */
844
			if (0 != memcmp(&fi->sb, &sb, sizeof(struct stat))) {
845
				memcpy(&fi->sb, &sb, sizeof(struct stat));
846
				kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED,
847
				    fmd->path, fi->de.d_name, NULL);
848
				return;
849
			}
850
		}
851
		/* fd not found, rescan dir. */
852
		kev->fflags = NOTE_WRITE;
853
	}
854
	/* Monitored object. */
855
	/* All flags from EVFILT_VNODE_FLAGS_ALL must be handled here. */
856
	if (EV_ERROR & kev->flags) {
857
		kev->fflags |= NOTE_REVOKE; /* Treat error as unmount. */
858
	}
859
	if (NOTE_RENAME & kev->fflags) {
860
		kqueue_handle_rename(kfnm, fmd);
861
	}
862
	if ((NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_CLOSE_WRITE) & kev->fflags) {
863
		kqueue_handle_changes(kfnm, fmd);
864
	}
865
	if ((NOTE_DELETE | NOTE_REVOKE) & kev->fflags) {
866
		kqueue_file_mon_data_clean(fmd);
867
		kfnm->cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED,
868
		    fmd->path, "", NULL);
869
	}
870
}
871
872
void *
873
kqueue_fnm_proccess_events_proc(void *data) {
874
	struct kevent kev;
875
	kqueue_fnm_p kfnm = data;
876
877
	if (NULL == kfnm)
878
		return (NULL);
879
	/* Get and proccess events, no wait. */
880
	while (0 < kevent(kfnm->fd, NULL, 0, &kev, 1, NULL)) {
881
		kqueue_fnm_proccess_event(kfnm, &kev);
882
	}
883
884
	return (NULL);
885
}
(-)devel/glib20/files/kqueue_fnm.h (+69 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
#ifndef __KQUEUE_FILE_NOTIFY_MONITOR_H__
31
#define __KQUEUE_FILE_NOTIFY_MONITOR_H__
32
33
#include <sys/param.h>
34
#include <sys/types.h>
35
#include <inttypes.h>
36
37
38
typedef struct kqueue_file_nonify_monitor_s	*kqueue_fnm_p;
39
typedef struct kqueue_file_mon_data_s		*kqueue_file_mon_data_p;
40
41
typedef void (*kfnm_event_handler_cb)(kqueue_fnm_p kfnm,
42
			       kqueue_file_mon_data_p fmd, void *udata,
43
			       uint32_t event,
44
			       const char *base,
45
			       const char *filename,
46
			       const char *new_filename);
47
#define KF_EVENT_NOT_CHANGED	0 /* Internal use. */
48
#define KF_EVENT_CREATED	1
49
#define KF_EVENT_DELETED	2
50
#define KF_EVENT_RENAMED	3
51
#define KF_EVENT_CHANGED	4
52
53
54
typedef struct kqueue_file_mon_settings_s {
55
	int		mon_local_subfiles;	/* Enable monitoring files/subdirs changes on local file systems. */
56
	const char 	**local_fs;		/* NULL terminated fs names list that threat as local. Keep utill kqueue_fnm_free() return. */
57
	const char	**non_local_fs;		/* NULL terminated fs names list that threat as not local. Keep utill kqueue_fnm_free() return. */
58
} kqueue_file_mon_settings_t, *kqueue_file_mon_settings_p;
59
60
kqueue_fnm_p	kqueue_fnm_create(kqueue_file_mon_settings_p s,
61
		    kfnm_event_handler_cb cb_func);
62
void		kqueue_fnm_free(kqueue_fnm_p kfnm);
63
64
kqueue_file_mon_data_p	kqueue_fnm_add(kqueue_fnm_p kfnm,
65
			    const char *path, void *udata);
66
void		kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd);
67
68
69
#endif /* __KQUEUE_FILE_NOTIFY_MONITOR_H__ */

Return to bug 214338