Blame view

mon_inotify.c 5.33 KB
redmine authored
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
    clsync - file tree sync utility based on inotify/kqueue
    
    Copyright (C) 2013-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"
#include "error.h"
#include "sync.h"
#include "indexes.h"
redmine authored
24
#include "privileged.h"
redmine authored
25 26 27 28 29 30 31 32 33
#include "mon_inotify.h"

enum event_bits {
	UEM_DIR		= 0x01,
	UEM_CREATED	= 0x02,
	UEM_DELETED	= 0x04,
};

struct recognize_event_return {
redmine authored
34 35
	eventobjtype_t objtype_old;
	eventobjtype_t objtype_new;
redmine authored
36 37
};

redmine authored
38
static inline void recognize_event(struct recognize_event_return *r, uint32_t event) {
redmine authored
39 40 41 42 43 44 45 46 47 48
	eventobjtype_t type;
	int is_created;
	int is_deleted;

	type = (event & IN_ISDIR ? EOT_DIR : EOT_FILE);
	is_created = event & (IN_CREATE|IN_MOVED_TO);
	is_deleted = event & (IN_DELETE_SELF|IN_DELETE|IN_MOVED_FROM);

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

redmine authored
49 50
	r->objtype_old = (is_created ? EOT_DOESNTEXIST : type);
	r->objtype_new = (is_deleted ? EOT_DOESNTEXIST : type);
redmine authored
51

redmine authored
52
	return;
redmine authored
53
}
redmine authored
54

redmine authored
55 56
int inotify_add_watch_dir(ctx_t *ctx_p, indexes_t *indexes_p, const char *const accpath) {
	int inotify_d = (int)(long)ctx_p->fsmondata;
redmine authored
57
	return privileged_inotify_add_watch(inotify_d, accpath, INOTIFY_MARKMASK, PC_INOTIFY_ADD_WATCH_DIR);
redmine authored
58
}
redmine authored
59

redmine authored
60
int inotify_wait(ctx_t *ctx_p, struct indexes *indexes_p, struct timeval *tv_p) {
redmine authored
61
	int inotify_d = (int)(long)ctx_p->fsmondata;
redmine authored
62

redmine authored
63
	debug(3, "select with timeout %li secs (fd == %u).", tv_p->tv_sec, inotify_d);
redmine authored
64 65 66
	fd_set rfds;
	FD_ZERO(&rfds);
	FD_SET(inotify_d, &rfds);
redmine authored
67
	return select(inotify_d+1, &rfds, NULL, NULL, tv_p);
redmine authored
68 69 70 71 72 73 74 75
}

#define INOTIFY_HANDLE_CONTINUE {\
	ptr += sizeof(struct inotify_event) + event->len;\
	count++;\
	continue;\
}

redmine authored
76
int inotify_handle(ctx_t *ctx_p, indexes_t *indexes_p) {
redmine authored
77
	static struct timeval tv={0};
redmine authored
78
	int inotify_d = (int)(long)ctx_p->fsmondata;
redmine authored
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

	int count = 0;

	fd_set rfds;
	FD_ZERO(&rfds);
	FD_SET(inotify_d, &rfds);

	char   *path_rel	= NULL;
	size_t  path_rel_len	= 0;
	char   *path_full	= NULL;
	size_t  path_full_size	= 0;
	while (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {

		char buf[BUFSIZ + 1];
		size_t r = read(inotify_d, buf, BUFSIZ);
		if (r <= 0) {
			error("Got error while reading events from inotify with read().");
			count = -1;
			goto l_inotify_handle_end;
		}

#ifdef PARANOID
		g_hash_table_remove_all(indexes_p->fpath2ei_ht);
#endif

		char *ptr =  buf;
		char *end = &buf[r];
		while (ptr < end) {
			struct inotify_event *event = (struct inotify_event *)ptr;

			// Removing stale wd-s

redmine authored
111
			if (event->mask & IN_IGNORED) {
redmine authored
112 113 114 115 116 117 118 119 120
				debug(2, "Cleaning up info about watch descriptor %i.", event->wd);
				indexes_remove_bywd(indexes_p, event->wd);
				INOTIFY_HANDLE_CONTINUE;
			}

			// Getting path

			char *fpath = indexes_wd2fpath(indexes_p, event->wd);

redmine authored
121
			if (fpath == NULL) {
redmine authored
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
				debug(2, "Event %p on stale watch (wd: %i).", (void *)(long)event->mask, event->wd);
				INOTIFY_HANDLE_CONTINUE;
			}
			debug(2, "Event %p on \"%s\" (wd: %i; fpath: \"%s\").", (void *)(long)event->mask, event->len>0?event->name:"", event->wd, fpath);

			// Getting full path

			size_t path_full_memreq = strlen(fpath) + event->len + 2;
			if (path_full_size < path_full_memreq) {
				path_full      = xrealloc(path_full, path_full_memreq);
				path_full_size = path_full_memreq;
			}

			if (event->len>0)
				sprintf(path_full, "%s/%s", fpath, event->name);
			else
				sprintf(path_full, "%s", fpath);

			// Getting infomation about file/dir/etc

redmine authored
142 143 144
			struct  recognize_event_return r = {0};
			recognize_event(&r, event->mask);

redmine authored
145
			stat64_t lstat, *lstat_p;
redmine authored
146 147
			mode_t st_mode;
			size_t st_size;
redmine authored
148 149
			if ((r.objtype_new == EOT_DOESNTEXIST) || (ctx_p->flags[CANCEL_SYSCALLS]&CSC_MON_STAT) || lstat64(path_full, &lstat)) {
				debug(2, "Cannot lstat64(\"%s\", lstat). Seems, that the object had been deleted (%i) or option \"--cancel-syscalls mon_stat\" (%i) is set.", path_full, r.objtype_new == EOT_DOESNTEXIST, ctx_p->flags[CANCEL_SYSCALLS]&CSC_MON_STAT);
redmine authored
150
				st_mode = (event->mask & IN_ISDIR ? S_IFDIR : S_IFREG);
redmine authored
151
				st_size = 0;
redmine authored
152
				lstat_p = NULL;
redmine authored
153 154 155
			} else {
				st_mode = lstat.st_mode;
				st_size = lstat.st_size;
redmine authored
156
				lstat_p = &lstat;
redmine authored
157 158
			}

redmine authored
159
			if (sync_prequeue_loadmark(1, ctx_p, indexes_p, path_full, NULL, lstat_p, r.objtype_old, r.objtype_new, event->mask, event->wd, st_mode, st_size, &path_rel, &path_rel_len, NULL)) {
redmine authored
160 161 162 163 164 165 166 167 168 169 170 171 172
				count = -1;
				goto l_inotify_handle_end;
			}

			INOTIFY_HANDLE_CONTINUE;
		}

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

l_inotify_handle_end:
redmine authored
173
	if (path_full != NULL)
redmine authored
174 175
		free(path_full);

redmine authored
176
	if (path_rel != NULL)
redmine authored
177 178 179 180
		free(path_rel);

	return count;
}
redmine authored
181 182 183 184 185 186 187

int inotify_deinit(ctx_t *ctx_p) {
	int inotify_d = (int)(long)ctx_p->fsmondata;
	debug(3, "Closing inotify_d");
	return close(inotify_d);
}