Blame view

mon_kqueue.c 22.8 KB
redmine authored
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
    clsync - file tree sync utility based on inotify/kqueue
    
    Copyright (C) 2014  Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "common.h"
redmine authored
21

redmine authored
22
#include <glib.h>
redmine authored
23

redmine authored
24 25 26
#include "error.h"
#include "sync.h"
#include "indexes.h"
redmine authored
27 28
#include "fileutils.h"
#include "calc.h"
redmine authored
29
#include "glibex.h"
redmine authored
30
#include "mon_kqueue.h"
redmine authored
31

redmine authored
32 33 34 35 36 37 38 39
enum kqueue_status {
	KQUEUE_STATUS_UNKNOWN,
	KQUEUE_STATUS_RUNNING,
	KQUEUE_STATUS_DEINIT,
	KQUEUE_STATUS_DEAD,
};
enum kqueue_status kqueue_status = KQUEUE_STATUS_UNKNOWN;

redmine authored
40 41 42 43 44 45 46 47 48 49
struct monobj {
	ino_t          inode;
	dev_t          device;
	int            fd;
	int            dir_fd;
	char          *name;
	size_t         name_len;
	uint32_t       name_hash;
	unsigned char  type;
	size_t         changelist_id;
redmine authored
50 51
	struct monobj *parent;

redmine authored
52 53 54 55 56 57 58
	// Case specific stuff
	union {
		// For directories only, for original (not dupes) obj_p only
		GTree *children_tree;
		// For duplicates only, see _kqueue_handle_oneevent_dircontent()
		struct monobj *origin;
	};
redmine authored
59 60 61 62 63 64 65 66 67 68 69
};
typedef struct monobj monobj_t;

struct kqueue_data {
	int kqueue_d;

	struct kevent *changelist;
	size_t         changelist_alloced;
	size_t         changelist_used;
	struct kevent  eventlist[KQUEUE_EVENTLISTSIZE];
	size_t         eventlist_count;
redmine authored
70
	monobj_t     **obj_p_by_clid;			// An associative array to get the monobj pointer by an changelist_id
redmine authored
71 72
	GTree   *file_btree;
	GTree     *fd_btree;
redmine authored
73 74
};

redmine authored
75 76 77 78 79 80 81 82 83 84
struct recognize_event_return {
	union {
		struct {
			eventobjtype_t objtype_old:16;
			eventobjtype_t objtype_new:16;
		} v;
		uint32_t i;
	} u;
};

redmine authored
85 86 87
ctx_t     *ctx_p;
indexes_t *indexes_p;

redmine authored
88
static inline uint32_t recognize_event(uint32_t event, int is_dir) {
redmine authored
89 90 91 92 93 94
	struct recognize_event_return r = {{{0}}};

	eventobjtype_t type;
	int is_created;
	int is_deleted;

redmine authored
95 96 97
	type       = (is_dir ? EOT_DIR : EOT_FILE);
	is_created =  event & (NOTE_LINK);
	is_deleted =  event & (NOTE_DELETE);
redmine authored
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

	debug(4, "type == %x; is_created == %x; is_deleted == %x", type, is_created, is_deleted);

	r.u.v.objtype_old = type;
	r.u.v.objtype_new = type;

	if (is_created)
		r.u.v.objtype_old = EOT_DOESNTEXIST;

	if (is_deleted)
		r.u.v.objtype_new = EOT_DOESNTEXIST;

	return r.u.i;
}

redmine authored
113 114
extern int kqueue_sync(ctx_t *ctx_p, indexes_t *indexes_p, struct kevent *ev_p, monobj_t *obj_p);
extern int kqueue_unmark(ctx_t *ctx_p, monobj_t *obj_p);
redmine authored
115

redmine authored
116 117 118 119 120 121 122
void unmarkchild(gpointer _obj_p) {
	monobj_t *obj_p = _obj_p;
	static struct kevent ev = {0};
	debug(10, "Calling kqueue_sync() on \"%s\" (obj_p: %p; dir_fd: %i; fd: %i)", obj_p->name, obj_p, obj_p->dir_fd, obj_p->fd);
	ev.ident  = obj_p->fd;
	ev.fflags = NOTE_DELETE;
	critical_on (kqueue_sync(ctx_p, indexes_p, &ev, obj_p));
redmine authored
123

redmine authored
124 125 126 127 128 129 130 131
	debug(4, "Unmarking the child \"%s\" (dir_fd: %i; fd: %i)", obj_p->name, obj_p->dir_fd, obj_p->fd);
	kqueue_unmark(ctx_p, obj_p);
	return;
}
gboolean unmarkchild_for_foreach(gpointer _obj_p, gpointer _value, gpointer _ctx_p) {
	unmarkchild(_obj_p);
	return FALSE;
}
redmine authored
132

redmine authored
133 134
void monobj_free(void *monobj_p) {
	monobj_t *obj_p = monobj_p;
redmine authored
135
	debug(20, "obj_p == %p; obj_p->fd == %i; obj_p->name == \"%s\"", obj_p, obj_p->fd, obj_p->name);
redmine authored
136

redmine authored
137 138 139 140 141 142 143 144 145 146 147 148
	if (kqueue_status != KQUEUE_STATUS_DEINIT) {
		if (obj_p->children_tree != NULL) {
			debug(20, "Removing children");
			if (g_tree_nnodes(obj_p->children_tree)) {
				g_tree_foreach(obj_p->children_tree, unmarkchild_for_foreach, ctx_p);
				g_tree_destroy(obj_p->children_tree);
			}
		}
		if (obj_p->parent != NULL) {
			monobj_t *parent = obj_p->parent;
			debug(20, "Removing the obj from parent->children_tree (obj_p == %p; parent == %p; parent->children_tree == %p)", obj_p, parent, parent->children_tree);
			g_tree_remove(parent->children_tree, obj_p);
redmine authored
149 150 151 152 153 154 155 156 157
		}
	}

	debug(20, "free()-s");

	free(obj_p->name);
	free(obj_p);

	return;
redmine authored
158 159
}

redmine authored
160 161 162 163 164 165
static gint monobj_filecmp(gconstpointer _a, gconstpointer _b, gpointer _ctx_p) {
#ifdef VERYPARANOID
	critical_on (_a == NULL);
	critical_on (_b == NULL);
#endif

redmine authored
166
	const monobj_t *a=_a, *b=_b;
redmine authored
167
	debug(95, "a == %p; b == %p", a, b);
redmine authored
168 169

	int diff_inode  = a->inode  - b->inode;
redmine authored
170
	debug(90, "diff_inode = %i", diff_inode);
redmine authored
171 172 173 174
	if (diff_inode)
		return diff_inode;

	int diff_device = a->device - b->device;
redmine authored
175
	debug(50, "diff_device = %i", diff_device);
redmine authored
176 177 178 179
	if (diff_device)
		return diff_device;

	int diff_dir_fd = a->dir_fd - b->dir_fd;
redmine authored
180
	debug(50, "diff_dir_fd = %i (%i - %i)", diff_dir_fd, a->dir_fd, b->dir_fd);
redmine authored
181 182 183 184
	if (diff_dir_fd)
		return diff_dir_fd;

	int diff_name_hash = a->name_hash - b->name_hash;
redmine authored
185
	debug(50, "diff_name_hash = %i", diff_name_hash);
redmine authored
186 187 188
	if (diff_name_hash)
		return diff_name_hash;

redmine authored
189 190
	
	debug(10, "strcmp(\"%s\", \"%s\") = %i", a->name, b->name, strcmp(a->name, b->name));
redmine authored
191 192 193
	return strcmp(a->name, b->name);
}

redmine authored
194
static int monobj_fdcmp(gconstpointer a, gconstpointer b, gpointer _ctx_p) {
redmine authored
195
	return ((monobj_t *)a)->fd - ((monobj_t *)b)->fd;
redmine authored
196 197
}

redmine authored
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
int kqueue_init(ctx_t *_ctx_p) {
	struct kqueue_data *mondata;
	debug(9, "_ctx_p == %p", _ctx_p);
	ctx_p     = _ctx_p;
	indexes_p =  ctx_p->indexes_p;

	ctx_p->fsmondata = xcalloc(1, sizeof(struct kqueue_data));
	if (ctx_p->fsmondata == NULL)
		return -1;

	struct kqueue_data *dat = ctx_p->fsmondata;

	dat->kqueue_d = kqueue();

	mondata = ctx_p->fsmondata;

	mondata->file_btree = g_tree_new_full(monobj_filecmp, _ctx_p, monobj_free, NULL);
	mondata->fd_btree   = g_tree_new_full(monobj_fdcmp,   _ctx_p, NULL,        NULL);

redmine authored
217
	kqueue_status = KQUEUE_STATUS_RUNNING;
redmine authored
218 219 220
	return 0;
}

redmine authored
221 222 223 224 225 226 227 228
int kqueue_mark(ctx_t *ctx_p, monobj_t *obj_p) {
	struct kqueue_data *dat = ctx_p->fsmondata;
#ifdef VERYPARANOID
	if (obj_p == NULL) {
		errno = EINVAL;
		return -1;
	}
#endif
redmine authored
229
	debug(9, "");
redmine authored
230 231

	if (obj_p->dir_fd == -1)
Artyom A Anikeev authored
232
		obj_p->fd = open(ctx_p->watchdir, O_RDONLY|O_NOFOLLOW);
redmine authored
233
	else
Artyom A Anikeev authored
234
		obj_p->fd = openat(obj_p->dir_fd, obj_p->name, O_RDONLY|O_NOFOLLOW);
redmine authored
235

redmine authored
236
	debug(4, "obj_p-> (%p): dir_fd == %i; name == \"%s\"; fd == %i; type == %i (isdir == %i)", obj_p, obj_p->dir_fd, obj_p->name, obj_p->fd, obj_p->type, obj_p->type == DT_DIR);
redmine authored
237

redmine authored
238 239 240
	if (obj_p->fd == -1) {
		debug(2, "File/dir \"%s\" disappeared. Skipping", obj_p->name);
		return 0;
redmine authored
241 242 243 244
	}

	if (dat->changelist_used >= dat->changelist_alloced) {
		dat->changelist_alloced += ALLOC_PORTION;
redmine authored
245 246
		dat->changelist          = xrealloc(dat->changelist,    dat->changelist_alloced*sizeof(*dat->changelist));
		dat->obj_p_by_clid       = xrealloc(dat->obj_p_by_clid, dat->changelist_alloced*sizeof(*dat->obj_p_by_clid));
redmine authored
247 248 249 250 251 252 253
	}

	switch (obj_p->type) {
		case DT_DIR:
			EV_SET(&dat->changelist[dat->changelist_used], obj_p->fd,
				EVFILT_VNODE,
				EV_ADD | EV_CLEAR,
redmine authored
254
				NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_DELETE,
redmine authored
255 256 257 258 259 260 261 262 263 264 265 266 267
				0, 0);
			break;
		default:
			EV_SET(&dat->changelist[dat->changelist_used], obj_p->fd,
				EVFILT_VNODE,
				EV_ADD | EV_CLEAR,
				NOTE_WRITE | NOTE_ATTRIB | NOTE_DELETE,
				0, 0);
			break;
	}

	obj_p->changelist_id = dat->changelist_used++;

redmine authored
268 269
	dat->obj_p_by_clid[obj_p->changelist_id] = obj_p;

redmine authored
270
	return 0;
redmine authored
271 272
}

redmine authored
273

redmine authored
274 275 276
void child_free(monobj_t *node) {
	critical_on (kqueue_unmark(ctx_p, node));
}
redmine authored
277
int kqueue_unmark(ctx_t *ctx_p, monobj_t *obj_p) {
redmine authored
278
	int changelist_id_last;
redmine authored
279
	debug(20, "obj_p == %p", obj_p);
redmine authored
280 281 282 283
	struct kqueue_data *dat = ctx_p->fsmondata;
#ifdef VERYPARANOID
	if (obj_p == NULL) {
		errno = EINVAL;
redmine authored
284
		return -1;
redmine authored
285 286 287
	}
#endif

redmine authored
288 289
	debug(30, "dat->changelist_used == %i", dat->changelist_used);
	if (dat->changelist_used) {
redmine authored
290
		dat->changelist_used--;
redmine authored
291 292
		changelist_id_last = dat->changelist_used;
		debug(30, "Checking: (obj_p->changelist_id [%i] < changelist_id_last [%i]) == %i", obj_p->changelist_id, changelist_id_last, (obj_p->changelist_id < changelist_id_last));
redmine authored
293 294 295
#ifdef PARANOID
		critical_on (obj_p->changelist_id > changelist_id_last);
#endif
redmine authored
296 297
		if (obj_p->changelist_id < changelist_id_last) {
			debug(20, "dat->changelist: moving %i -> %i", changelist_id_last, obj_p->changelist_id);
redmine authored
298

redmine authored
299 300
			dat->obj_p_by_clid[ obj_p->changelist_id ]                = dat->obj_p_by_clid[ changelist_id_last ];
			dat->obj_p_by_clid[ obj_p->changelist_id ]->changelist_id = obj_p->changelist_id;
redmine authored
301

redmine authored
302
			memcpy(&dat->changelist[obj_p->changelist_id], &dat->changelist[changelist_id_last], sizeof(*dat->changelist));
redmine authored
303

redmine authored
304 305 306 307 308 309 310 311
			debug(30,
					"dat->obj_p_by_clid[ obj_p->changelist_id ] == %p; "
					"dat->obj_p_by_clid[ obj_p->changelist_id ]->fd == %i; "
					"dat->obj_p_by_clid[ obj_p->changelist_id ]->name == \"%s\"",
					dat->obj_p_by_clid[ obj_p->changelist_id ],
					dat->obj_p_by_clid[ obj_p->changelist_id ]->fd,
					dat->obj_p_by_clid[ obj_p->changelist_id ]->name
				);
redmine authored
312
		}
redmine authored
313 314
	}

redmine authored
315 316
	close(obj_p->fd);

redmine authored
317
	debug(20, "Removing the obj itself");
redmine authored
318 319
	g_tree_remove(dat->fd_btree,   obj_p);
	g_tree_remove(dat->file_btree, obj_p);
redmine authored
320 321 322 323

	return 0;
}

redmine authored
324
monobj_t *kqueue_start_watch(ctx_t *ctx_p, ino_t inode, dev_t device, int dir_fd, const char *const fname, size_t name_len, unsigned char type) {
redmine authored
325
	monobj_t *obj_p, *parent, parent_pattern;
redmine authored
326
	struct kqueue_data *dat = ctx_p->fsmondata;
redmine authored
327 328
	debug(3, "(ctx_p, %i, \"%s\", %u, %u)", dir_fd, fname, name_len, type);

redmine authored
329 330 331
#ifdef VERYPARANOID
	obj_p = xcalloc(sizeof(*obj_p), 1);
#else
redmine authored
332
	obj_p = xmalloc(sizeof(*obj_p));
redmine authored
333
#endif
redmine authored
334 335
	obj_p->inode	 = inode;
	obj_p->device	 = device;
redmine authored
336
	obj_p->dir_fd	 = dir_fd;
redmine authored
337 338 339 340
	obj_p->name_len	 = name_len;
	obj_p->name	 = xmalloc(obj_p->name_len+1);
	obj_p->type	 = type;
	memcpy(obj_p->name, fname, obj_p->name_len+1);
redmine authored
341
	obj_p->name_hash = adler32_calc((const unsigned char *)fname, name_len);
redmine authored
342

redmine authored
343
	parent = NULL;
redmine authored
344
	parent_pattern.fd = dir_fd;
redmine authored
345
	parent = g_tree_lookup(dat->fd_btree, &parent_pattern);
redmine authored
346

redmine authored
347
	if (parent != NULL) {
redmine authored
348 349
		obj_p->parent = parent;
		debug(20, "Adding a child for dir_fd == %i", dir_fd);
redmine authored
350 351 352

		g_tree_replace(parent->children_tree, obj_p, obj_p);

redmine authored
353 354 355 356
		debug(25, "parent->children_tree == %p", parent->children_tree);
	}

	debug(20, "parent == %p; obj_p == %p", parent, obj_p);
redmine authored
357 358 359

	switch (type) {
		case DT_DIR:
redmine authored
360 361
			obj_p->children_tree = g_tree_new_full(monobj_filecmp, ctx_p, NULL, NULL);
			debug(25, "dir_p->children_tree == %p", obj_p->children_tree);
redmine authored
362 363 364 365 366 367 368 369 370 371 372 373
		case DT_UNKNOWN:
		case DT_REG:
			if (kqueue_mark(ctx_p, obj_p)) {
				error("Got error while kqueue_mark()");
				free(obj_p->name);
				free(obj_p);
				return NULL;
			}
			break;
		default:
			debug(4, "I won't open() this object due to it's type == %u.", type);
			break;
redmine authored
374 375
	}

redmine authored
376
	debug(8, "storing: inode == %u; device == %u; dir_fd == %i; fd == %i; parent == %p", obj_p->inode, obj_p->device, obj_p->dir_fd, obj_p->fd, parent);
redmine authored
377 378
	g_tree_replace(dat->file_btree, obj_p, obj_p);
	g_tree_replace(  dat->fd_btree, obj_p, obj_p);
redmine authored
379 380 381 382 383 384 385 386 387

	return obj_p;
}

monobj_t *kqueue_add_watch_direntry(ctx_t *ctx_p, indexes_t *indexes_p, struct dirent *entry, monobj_t *dir_obj_p) {
	struct kqueue_data *dat = ctx_p->fsmondata;
	monobj_t *obj_p;
	uint32_t name_hash;
#ifdef VERYPARANOID
redmine authored
388
	critical_on (entry == NULL);
redmine authored
389 390 391 392 393
#endif
	size_t name_len = strlen(entry->d_name);

	name_hash = adler32_calc((unsigned char *)entry->d_name, name_len);
	{
redmine authored
394
		monobj_t obj;
redmine authored
395 396 397 398
		obj.inode     = entry->d_ino;
		obj.device    = dir_obj_p->device;
		obj.dir_fd    = dir_obj_p->fd;
		obj.name_hash = name_hash;
redmine authored
399 400
		if ((obj_p = g_tree_lookup(dat->file_btree, &obj)) != NULL)
			return obj_p;
redmine authored
401 402
	}

redmine authored
403
	if ((obj_p = kqueue_start_watch(ctx_p, entry->d_ino, dir_obj_p->device, dir_obj_p->fd, entry->d_name, name_len, entry->d_type)) == NULL)
redmine authored
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
		error("Got error while kqueue_start_watch()");

	obj_p->inode     = entry->d_ino;
	obj_p->device    = dir_obj_p->device;
	obj_p->dir_fd    = dir_obj_p->fd;
	obj_p->name_hash = name_hash;

	return obj_p;
}

monobj_t *kqueue_add_watch_path(ctx_t *ctx_p, indexes_t *indexes_p, const char *const path) {
	struct stat st;
	struct kqueue_data *dat = ctx_p->fsmondata;
	monobj_t *obj_p = NULL;
	uint32_t name_hash;
	const char *file_name;
	int dir_fd;
	size_t name_len;

#ifdef VERYPARANOID
	if (path == NULL) {
		errno = EINVAL;
redmine authored
426
		return NULL;
redmine authored
427 428
	}
#endif
redmine authored
429 430
	debug(6, "(ctx_p, indexes_p, \"%s\")", path);

redmine authored
431 432 433 434 435 436 437 438 439 440 441 442
	{
		char *dir_path, *ptr;

		ptr = strrchr(path, '/');
		if (ptr == NULL) {
			file_name = path;
			dir_fd = indexes_fpath2wd(indexes_p, "");
		} else {
			dir_path = strdup(path);
			dir_path[ptr - path] = 0;
			dir_fd = indexes_fpath2wd(indexes_p, dir_path);
			if (dir_fd == -1) {
redmine authored
443 444 445 446 447
				if (strcmp(ctx_p->watchdir, path)) {
					errno = ENOENT;
					error("Cannot find file descriptor of directory \"%s\"", dir_path);
					return NULL;
				}
redmine authored
448 449 450 451 452 453 454 455 456 457 458 459
			}
			free(dir_path);
			file_name = &ptr[1];
		}

		name_len  = strlen(file_name);
		name_hash = adler32_calc((unsigned char *)file_name, name_len);
	}

	lstat(path, &st);

	{
redmine authored
460
		monobj_t obj;
redmine authored
461 462 463 464
		obj.inode     = st.st_ino;
		obj.device    = st.st_dev;
		obj.dir_fd    = dir_fd;
		obj.name_hash = name_hash;
redmine authored
465
		obj.name      = (char *)file_name;
redmine authored
466 467
		if ((obj_p = g_tree_lookup(dat->file_btree, &obj)) != NULL)
			return obj_p;
redmine authored
468 469
	}

redmine authored
470 471
	debug(9, "not monitored file/dir \"%s\", yet.", file_name);

redmine authored
472 473 474 475 476 477 478 479 480
	{
		const char *name_start;

		name_start = strrchr(path, '/');
		if (name_start == NULL)
			name_start = path;
		else
			name_start++;

redmine authored
481
		if ((obj_p = kqueue_start_watch(ctx_p, st.st_ino, st.st_dev, dir_fd, file_name, name_len, (st.st_mode&S_IFMT) == S_IFDIR ? DT_DIR : DT_REG)) == NULL)
redmine authored
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
			error("Got error while kqueue_start_watch()");
	}

	obj_p->inode     = st.st_ino;
	obj_p->device    = st.st_dev;
	obj_p->dir_fd    = dir_fd;
	obj_p->name_hash = name_hash;
	return obj_p;
}

int kqueue_add_watch_dir(ctx_t *ctx_p, indexes_t *indexes_p, const char *const accpath) {
	DIR      *dir;
	monobj_t *dir_obj_p = NULL;
	struct dirent *entry;

#ifdef VERYPARANOID
redmine authored
498
	if (accpath == NULL) {
redmine authored
499 500 501 502 503
		errno = EINVAL;
		return -1;
	}
#endif

redmine authored
504 505
	debug(5, "(ctx_p, indexes_p, \"%s\")", accpath);

redmine authored
506 507 508 509 510 511 512 513 514
	if ((dir_obj_p = kqueue_add_watch_path(ctx_p, indexes_p, accpath)) == NULL) {
		error("Got error while kqueue_add_watch_path(ctx_p, \"%s\")", accpath);
		return -1;
	}

	dir = fdopendir(dir_obj_p->fd);
	if (dir == NULL)
		return -1;

redmine authored
515 516 517 518 519
	while ((entry = readdir(dir))) {
		if (!memcmp(entry->d_name, ".",  2))
			continue;
		if (!memcmp(entry->d_name, "..", 3))
			continue;
redmine authored
520 521 522 523
		if (kqueue_add_watch_direntry(ctx_p, indexes_p, entry, dir_obj_p) == NULL) {
			error("Got error while kqueue_add_watch_direntry(ctx_p, indexes_p, entry {->d_name == \"%s\"}, %u)", entry->d_name, dir_obj_p->fd);
			return -1;
		}
redmine authored
524
	}
redmine authored
525 526 527 528

	return dir_obj_p->fd;
}

redmine authored
529
int kqueue_wait(ctx_t *ctx_p, struct indexes *indexes_p, struct timeval *tv_p) {
redmine authored
530 531
	struct kqueue_data *dat = ctx_p->fsmondata;
	struct timespec ts;
redmine authored
532
	debug(3, "tv_p->: tv_sec == %li; tv_usec == %li", tv_p->tv_sec, tv_p->tv_usec);
redmine authored
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555

#ifdef PARANOID
	if (tv_p == NULL)
		return dat->eventlist_count = kevent(dat->kqueue_d, dat->changelist, dat->changelist_used, dat->eventlist, KQUEUE_EVENTLISTSIZE, NULL);
#endif

	ts.tv_sec  = tv_p->tv_sec;
	ts.tv_nsec = tv_p->tv_usec * 1000;

	return dat->eventlist_count = kevent(dat->kqueue_d, dat->changelist, dat->changelist_used, dat->eventlist, KQUEUE_EVENTLISTSIZE, &ts);
}

// Not a thread-safe function!
char *kqueue_getpath(ctx_t *ctx_p, indexes_t *indexes_p, monobj_t *obj_p) {
	char  *dirpath;
	size_t dirpath_len;
	static char   filepath[PATH_MAX+2];
	size_t filepath_len;

	dirpath = indexes_wd2fpath(indexes_p, obj_p->fd);
	if (dirpath != NULL)
		return strdup(dirpath);

redmine authored
556 557 558 559 560 561
	if (obj_p->dir_fd == -1) {
		errno = ENOENT;
		error("Cannot find fd of parent directory of \"%s\"", obj_p->name);
		return NULL;
	}

redmine authored
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
	dirpath = indexes_wd2fpath(indexes_p, obj_p->dir_fd);
	if (dirpath == NULL) {
		errno = ENOENT;
		error("Cannot find directory with fd == %u", obj_p->dir_fd);
		return NULL;
	}

	dirpath_len  = strlen(dirpath);
	filepath_len = dirpath_len + obj_p->name_len + 1;

#ifdef PARANOID
	if (filepath_len + 1 >= PATH_MAX) {
		errno = ENAMETOOLONG;
		error("Too long file path: \"%s/%s\"", dirpath, obj_p->name);
		return NULL;
	}
#endif

	memcpy(filepath, dirpath, dirpath_len);
	filepath[dirpath_len] = '/';
	memcpy(&filepath[dirpath_len+1], obj_p->name, obj_p->name_len+1);

	return filepath;
}

int kqueue_sync(ctx_t *ctx_p, indexes_t *indexes_p, struct kevent *ev_p, monobj_t *obj_p) {
redmine authored
588
	stat64_t lstat, *lstat_p;
redmine authored
589 590 591 592 593 594 595 596
	char *path_full = kqueue_getpath(ctx_p, indexes_p, obj_p);

#ifdef PARANOID
	if (path_full == NULL) {
		error("Cannot get full path for \"%s\" (fd: %u)", obj_p->name, obj_p->fd);
		return -1;
	}
#endif
redmine authored
597
	debug(8, "path_full = \"%s\"", path_full);
redmine authored
598 599 600

	mode_t st_mode;
	size_t st_size;
redmine authored
601 602 603
	if ((ev_p->fflags == NOTE_DELETE) || (ctx_p->flags[CANCEL_SYSCALLS]&CSC_MON_STAT) || lstat64(path_full, &lstat)) {
		debug(2, "Cannot or cancelled lstat64(\"%s\", lstat). The object had been deleted (%i) or option \"--cancel-syscalls=mon_stat\" (%i) is set.", path_full, ev_p->fflags == NOTE_DELETE, ctx_p->flags[CANCEL_SYSCALLS]&CSC_MON_STAT);
		st_mode = (obj_p->type == DT_DIR ? S_IFDIR : S_IFREG);
redmine authored
604
		st_size = 0;
redmine authored
605
		lstat_p = NULL;
redmine authored
606
	} else {
redmine authored
607 608 609
		st_mode =  lstat.st_mode;
		st_size =  lstat.st_size;
		lstat_p = &lstat;
redmine authored
610 611 612 613 614
	}

	{
		char   *path_rel	= NULL;
		size_t  path_rel_len	= 0;
redmine authored
615
		struct  recognize_event_return r;
redmine authored
616
		r.u.i = recognize_event(ev_p->fflags, obj_p->type == DT_DIR);
redmine authored
617

redmine authored
618
		int ret = sync_prequeue_loadmark(1, ctx_p, indexes_p, path_full, NULL, lstat_p, r.u.v.objtype_old, r.u.v.objtype_new, ev_p->fflags, ev_p->ident, st_mode, st_size, &path_rel, &path_rel_len, NULL);
redmine authored
619 620 621 622 623 624 625 626 627 628

		if (path_rel != NULL)
			free(path_rel);

		return ret;
	}

	return 0;
}

redmine authored
629
static inline int _kqueue_handle_oneevent_dircontent_item(struct kqueue_data *dat, ctx_t *ctx_p, indexes_t *indexes_p, monobj_t *dir_obj_p, struct dirent *entry, void *children_notfound) {
redmine authored
630 631
	static monobj_t obj, *obj_p;
	struct kevent ev = {0};
redmine authored
632

redmine authored
633
	debug(9, "\"%s\"", entry->d_name);
redmine authored
634

redmine authored
635
	obj.type      = entry->d_type;
redmine authored
636 637 638 639 640 641 642
	obj.inode     = entry->d_ino;
	obj.device    = dir_obj_p->device;
	obj.dir_fd    = dir_obj_p->fd;
	obj.name      = entry->d_name;
	obj.name_len  = strlen(entry->d_name);
	obj.name_hash = adler32_calc((unsigned char *)entry->d_name, obj.name_len);

redmine authored
643 644
	debug(20, "Checking if the object is already monitored (obj_p == %p)", obj_p);
	if ((obj_p = g_tree_lookup(dat->file_btree, &obj)) != NULL) {
redmine authored
645
		debug(20, "Marking the the object is found");
redmine authored
646
		g_tree_remove(children_notfound, obj_p);
redmine authored
647
		return 0;
redmine authored
648
	}
redmine authored
649

redmine authored
650
	debug(20, "Calling kqueue_start_watch() on the object");
redmine authored
651 652 653 654
	if ((obj_p = kqueue_start_watch(ctx_p, entry->d_ino, dir_obj_p->device, dir_obj_p->fd, obj.name, obj.name_len, obj.type)) == NULL) {
		error("Got error while kqueue_start_watch()");
		return -1;
	}
redmine authored
655

redmine authored
656
	debug(20, "Calling kqueue_sync() for the object");
redmine authored
657 658 659 660 661 662 663 664
	ev.ident  = obj_p->fd;
	ev.fflags = NOTE_LINK;
	if (kqueue_sync(ctx_p, indexes_p, &ev, obj_p)) {
		error("Got error while kqueue_sync()");
		return -1;
	}

	return 0;
redmine authored
665 666
}

redmine authored
667 668 669 670 671 672 673 674 675
void monobj_freedup(gpointer _obj_p) {
	monobj_t *obj_p = _obj_p;

	free(obj_p->name);
	free(obj_p);

	return;
}

redmine authored
676 677
gpointer monobj_dup(gpointer _obj_p) {
	monobj_t *src = _obj_p, *dst;
redmine authored
678

redmine authored
679 680
	dst = xmalloc(sizeof(*src));
	memcpy(dst, src, sizeof(*src));
redmine authored
681 682

	dst->name   = xmalloc(src->name_len+2);
redmine authored
683 684
	memcpy(dst->name, src->name, src->name_len+1);

redmine authored
685 686
	dst->origin = src;

redmine authored
687
	return dst;
redmine authored
688
}
redmine authored
689

redmine authored
690 691 692 693 694 695 696 697 698 699
gboolean unmarkdupchild(gpointer _obj_p, gpointer value, gpointer _ctx_p) {
	monobj_t *dupobj_p = _obj_p;
	monobj_t *obj_p    = dupobj_p->origin;
//	ctx_t    *ctx_p    = _ctx_p;

	unmarkchild(obj_p);

	return FALSE;
}

redmine authored
700
int _kqueue_handle_oneevent_dircontent(ctx_t *ctx_p, indexes_t *indexes_p, monobj_t *obj_p) {
redmine authored
701
	DIR *dir;
redmine authored
702
	GTree *children_tree_dup;
redmine authored
703 704
	struct dirent *entry;
	struct kqueue_data *dat = ctx_p->fsmondata;
redmine authored
705
	debug(8, "obj_p == %p; obj_p->dir_fd == %i", obj_p, obj_p->dir_fd);
redmine authored
706

redmine authored
707

redmine authored
708
	debug(20, "open*()-ing the directory");
redmine authored
709
	int fd;
Artyom authored
710 711 712 713
	if (obj_p->dir_fd == -1)
		fd = open(ctx_p->watchdir, O_RDONLY|O_PATH);
	else
		fd = openat(obj_p->dir_fd, obj_p->name, O_RDONLY|O_PATH);
Artyom A Anikeev authored
714

redmine authored
715
	debug(20, "fdopendir()-ing the directory");
redmine authored
716
	dir = fdopendir(fd);
redmine authored
717 718 719 720 721 722
	if (dir == NULL) {
		debug(20, "dir == NULL. return 0");
		return 0;
	}

	debug(20, "tdup()-ing the children_tree == %p", obj_p->children_tree);
redmine authored
723
	children_tree_dup = g_tree_dup(obj_p->children_tree, monobj_filecmp, ctx_p, monobj_freedup, NULL, monobj_dup, NULL);
redmine authored
724 725 726
	debug(8, "children_count == %i", g_tree_nnodes(children_tree_dup));

	debug(20, "reading the directory");
redmine authored
727 728 729 730 731 732
	while ((entry = readdir(dir))) {
		debug(10, "file/dir: \"%s\"", entry->d_name);
		if (!memcmp(entry->d_name, ".",  2))
			continue;
		if (!memcmp(entry->d_name, "..", 3))
			continue;
redmine authored
733
		if (_kqueue_handle_oneevent_dircontent_item(dat, ctx_p, indexes_p, obj_p, entry, children_tree_dup)) {
redmine authored
734 735 736
			error("Got error while _kqueue_handle_oneevent_dircontent_item(ctx_p, obj_p, entry {->d_name == \"%s\"})", entry->d_name);
			return -1;
		}
redmine authored
737 738
	}

redmine authored
739
	debug(20, "searching for deleted objects from the directory");
redmine authored
740

redmine authored
741
	g_tree_foreach(children_tree_dup, unmarkdupchild, ctx_p);
redmine authored
742
	g_tree_destroy(children_tree_dup);
redmine authored
743

redmine authored
744
	debug(20, "end");
redmine authored
745
	closedir(dir);
redmine authored
746 747 748 749 750 751 752 753 754 755 756 757 758 759
	return 0;
}

int kqueue_handle_oneevent(ctx_t *ctx_p, indexes_t *indexes_p, struct kevent *ev_p, monobj_t *obj_p) {
#ifdef VERYPARANOID
	if (ev_p == NULL) {
		errno = EINVAL;
		return -1;
	}
	if (obj_p == NULL) {
		errno = EINVAL;
		return -1;
	}
#endif
redmine authored
760
	debug(9, "obj_p->: name == \"%s\"; dir_fd == %i; type == 0x%x (isdir == %i); fd = %i. ev_p->fflags == 0x%x", obj_p->name, obj_p->dir_fd, obj_p->type, obj_p->type == DT_DIR, obj_p->fd, ev_p->fflags);
redmine authored
761 762
	int ret    = 0;

redmine authored
763
	if (obj_p->type == DT_DIR && (ev_p->fflags & (NOTE_EXTEND|NOTE_WRITE)))
redmine authored
764 765
		ret |= _kqueue_handle_oneevent_dircontent(ctx_p, indexes_p, obj_p);

redmine authored
766
	if (ev_p->fflags & (NOTE_EXTEND|NOTE_WRITE|NOTE_ATTRIB|NOTE_DELETE|NOTE_RENAME))
redmine authored
767 768 769 770 771 772 773 774 775 776 777
		ret |= kqueue_sync(ctx_p, indexes_p, ev_p, obj_p);

	if (ev_p->fflags & NOTE_DELETE)
		ret |= kqueue_unmark(ctx_p, obj_p);

	return ret;
}

int kqueue_handle(ctx_t *ctx_p, indexes_t *indexes_p) {
	static struct timeval tv={0};
	struct kqueue_data *dat = ctx_p->fsmondata;
redmine authored
778
	debug(3, "dat->eventlist_count == %i", dat->eventlist_count);
redmine authored
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795

	if (dat->eventlist_count == 0)
		return 0;

	int count = 0;

	do {
		int i = 0;
#ifdef PARANOID
		g_hash_table_remove_all(indexes_p->fpath2ei_ht);
#endif

		while (i < dat->eventlist_count) {
			struct kevent *ev_p = &dat->eventlist[i++];

			monobj_t obj;
			obj.fd = ev_p->ident;
redmine authored
796 797
			monobj_t *obj_p = g_tree_lookup(dat->fd_btree, &obj);
			if (obj_p == NULL) {
redmine authored
798
				debug(3, "Cannot find internal structure for fd == %u. Skipping the event.", ev_p->ident);
redmine authored
799 800 801
				continue;
			}

redmine authored
802 803 804 805 806
			if (kqueue_handle_oneevent(ctx_p, indexes_p, ev_p, obj_p)) {
				error("Got error from kqueue_handle_oneevent()");
				return -1;
			}
			count++;
redmine authored
807 808 809 810 811 812 813
		}

		// Globally queueing captured events:
		// Moving events from local queue to global ones
		sync_prequeue_unload(ctx_p, indexes_p);

		dat->eventlist_count = 0;
redmine authored
814
	} while (kqueue_wait(ctx_p, indexes_p, &tv));
redmine authored
815 816 817 818 819

	return count;
}

int kqueue_deinit(ctx_t *ctx_p) {
redmine authored
820
	kqueue_status = KQUEUE_STATUS_DEINIT;
redmine authored
821 822
	struct kqueue_data *dat = ctx_p->fsmondata;
	debug(3, "dat->eventlist_count == %i", dat->eventlist_count);
redmine authored
823

redmine authored
824 825
	g_tree_destroy(dat->file_btree);
	g_tree_destroy(dat->fd_btree);
redmine authored
826
	kqueue_status = KQUEUE_STATUS_DEAD;
redmine authored
827
	return 0;
redmine authored
828 829
}