Blame view

redmine authored
1
/*
redmine authored
2
    clsync - file tree sync utility based on inotify/kqueue
redmine authored
3
    
redmine authored
4
    Copyright (C) 2013-2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
redmine authored
5
    
redmine authored
6 7 8 9
    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.
redmine authored
10
    
redmine authored
11 12 13 14
    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.
redmine authored
15
    
redmine authored
16 17 18 19 20
    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
#if KQUEUE_SUPPORT
redmine authored
23
#	include "mon_kqueue.h"
redmine authored
24 25
#endif
#if INOTIFY_SUPPORT
redmine authored
26
#	include "mon_inotify.h"
redmine authored
27 28
#endif
#if FANOTIFY_SUPPORT
redmine authored
29 30 31 32
#	include "mon_fanotify.h"
#endif
#if BSM_SUPPORT
#	include "mon_bsm.h"
redmine authored
33
#	include <bsm/audit_kevents.h>
redmine authored
34
#endif
redmine authored
35 36 37 38
#if GIO_SUPPORT
#	include <gio/gio.h>
#	include "mon_gio.h"
#endif
redmine authored
39

redmine authored
40
#include "main.h"
redmine authored
41
#include "error.h"
redmine authored
42
#include "fileutils.h"
redmine authored
43
#include "malloc.h"
redmine authored
44
#include "cluster.h"
redmine authored
45
#include "sync.h"
redmine authored
46
#include "glibex.h"
redmine authored
47
#include "control.h"
redmine authored
48
#include "indexes.h"
redmine authored
49
#include "privileged.h"
redmine authored
50
#include "rules.h"
redmine authored
51 52 53
#if CGROUP_SUPPORT
#	include "cgroup.h"
#endif
redmine authored
54

Artyom A Anikeev authored
55
#include <stdio.h>
redmine authored
56 57
#include <dlfcn.h>

redmine authored
58

redmine authored
59
pthread_t pthread_sighandler;
redmine authored
60

redmine authored
61 62 63 64 65 66 67 68 69 70 71 72 73 74
// seqid - is a counter of main loop. But it may overflow and it's required to compare
// seqid-values anyway.
// So if (a-b) is too big, let's assume, that "b<a".
#define SEQID_WINDOW (((unsigned int)~0)>>1)
#define SEQID_EQ(a, b) ((a)==(b))
#define SEQID_GE(a, b) ((a)-(b) < SEQID_WINDOW)
#define SEQID_LE(a, b) ((b)-(a) < SEQID_WINDOW)
#define SEQID_GT(a, b) ((!SEQID_EQ(a, b)) && (SEQID_GE(a, b)))
#define SEQID_LT(a, b) ((!SEQID_EQ(a, b)) && (SEQID_LE(a, b)))
static unsigned int _sync_seqid_value=0;
static inline unsigned int sync_seqid() {
	return _sync_seqid_value++;
}

redmine authored
75 76 77 78 79 80 81 82 83 84 85 86 87
static inline void setenv_iteration(uint32_t iteration_num)
{
	char iterations[sizeof("4294967296")];	// 4294967296 == 2**32
	sprintf(iterations, "%i", iteration_num);
	setenv("CLSYNC_ITERATION", iterations, 1);

	return;
}

static inline void finish_iteration(ctx_t *ctx_p) {
	if (ctx_p->iteration_num < ~0) // ~0 is the max value for unsigned variables
		ctx_p->iteration_num++;

redmine authored
88
#ifdef THREADING_SUPPORT
redmine authored
89
	if (!ctx_p->flags[THREADING])
redmine authored
90
#endif
redmine authored
91 92 93 94 95 96 97
		setenv_iteration(ctx_p->iteration_num); 

	debug(3, "next iteration: %u/%u", 
		ctx_p->iteration_num, ctx_p->flags[MAXITERATIONS]);

	return;
}
redmine authored
98

redmine authored
99 100 101 102 103 104 105 106 107
gpointer eidup(gpointer ei_gp) {
	eventinfo_t *ei = (eventinfo_t *)ei_gp;

	eventinfo_t *ei_dup = (eventinfo_t *)xmalloc(sizeof(*ei));
	memcpy(ei_dup, ei, sizeof(*ei));

	return (gpointer)ei_dup;
}

redmine authored
108
static inline void evinfo_merge(ctx_t *ctx_p, eventinfo_t *evinfo_dst, eventinfo_t *evinfo_src) {
redmine authored
109 110
	debug(3, "evinfo_dst: seqid_min == %u; seqid_max == %u; objtype_old == %i; objtype_new == %i; \t"
			"evinfo_src: seqid_min == %u; seqid_max == %u; objtype_old == %i; objtype_new == %i",
redmine authored
111 112 113 114
			evinfo_dst->seqid_min, evinfo_dst->seqid_max, evinfo_dst->objtype_old, evinfo_dst->objtype_new,
			evinfo_src->seqid_min, evinfo_src->seqid_max, evinfo_src->objtype_old, evinfo_src->objtype_new
		);

redmine authored
115 116 117 118 119 120 121 122 123 124 125 126 127
#if KQUEUE_SUPPORT | INOTIFY_SUPPORT
	switch(ctx_p->flags[MONITOR]) {
#ifdef KQUEUE_SUPPORT
		case NE_KQUEUE:
#endif
#ifdef INOTIFY_SUPPORT
		case NE_INOTIFY:
#endif
			evinfo_dst->evmask |= evinfo_src->evmask;
			break;
	}
#endif

redmine authored
128 129 130 131 132 133 134 135 136 137
	evinfo_dst->flags  |= evinfo_src->flags;

	if(SEQID_LE(evinfo_src->seqid_min, evinfo_dst->seqid_min)) {
		evinfo_dst->objtype_old = evinfo_src->objtype_old;
		evinfo_dst->seqid_min   = evinfo_src->seqid_min;
	}

	if(SEQID_GE(evinfo_src->seqid_max,  evinfo_dst->seqid_max))  {
		evinfo_dst->objtype_new = evinfo_src->objtype_new;
		evinfo_dst->seqid_max   = evinfo_src->seqid_max;
redmine authored
138
		switch(ctx_p->flags[MONITOR]) {
redmine authored
139 140 141 142 143 144
#ifdef GIO_SUPPORT
			case NE_GIO:
				evinfo_dst->evmask = evinfo_src->evmask;
				break;
#endif
#ifdef BSM_SUPPORT
redmine authored
145
			case NE_BSM:
redmine authored
146
			case NE_BSM_PREFETCH:
redmine authored
147 148 149
				evinfo_dst->evmask = evinfo_src->evmask;
				break;
#endif
redmine authored
150 151 152
			default:
				break;
		}
redmine authored
153 154 155 156 157
	}

	return;
}

redmine authored
158 159
static inline int _exitcode_process(ctx_t *ctx_p, int exitcode) {
	if (ctx_p->isignoredexitcode[(unsigned char)exitcode])
redmine authored
160 161
		return 0;

redmine authored
162
	if (exitcode && !((ctx_p->flags[MODE]==MODE_RSYNCDIRECT) && (exitcode == 24))) {
redmine authored
163
		error("Got non-zero exitcode %i from __sync_exec().", exitcode);
redmine authored
164 165 166 167 168 169
		return exitcode;
	}

	return 0;
}

redmine authored
170 171
int exitcode_process(ctx_t *ctx_p, int exitcode) {
	int err = _exitcode_process(ctx_p, exitcode);
redmine authored
172

redmine authored
173
	if(err) error("Got error-report from exitcode_process().\nExitcode is %i, strerror(%i) returns \"%s\". However strerror() is not ensures compliance "
redmine authored
174
			"between exitcode and error description for every utility. So, e.g if you're using rsync, you should look for the error description "
redmine authored
175
			"into rsync's manpage (\"man 1 rsync\"). Also some advices about diagnostics can be found in clsync's manpage (\"man 1 clsync\", see DIAGNOSTICS)", 
redmine authored
176 177 178 179 180
			exitcode, exitcode, strerror(exitcode));

	return err;
}

redmine authored
181

redmine authored
182
threadsinfo_t *thread_info() {	// TODO: optimize this
redmine authored
183
	static threadsinfo_t threadsinfo={{{{0}}},{{{0}}},0};
redmine authored
184
	if (!threadsinfo.mutex_init) {
redmine authored
185
		int i=0;
redmine authored
186 187
		while (i < PTHREAD_MUTEX_MAX) {
			if (pthread_mutex_init(&threadsinfo.mutex[i], NULL)) {
redmine authored
188
				error("Cannot pthread_mutex_init().");
redmine authored
189 190
				return NULL;
			}
redmine authored
191
			if (pthread_cond_init (&threadsinfo.cond [i], NULL)) {
redmine authored
192
				error("Cannot pthread_cond_init().");
redmine authored
193 194
				return NULL;
			}
redmine authored
195
			i++;
redmine authored
196
		}
redmine authored
197
		threadsinfo.mutex_init++;
redmine authored
198
	}
redmine authored
199

redmine authored
200 201 202
	return &threadsinfo;
}

redmine authored
203
#ifdef THREADING_SUPPORT
redmine authored
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
#define thread_info_lock() _thread_info_lock(__FUNCTION__)
static inline threadsinfo_t *_thread_info_lock(const char *const function_name) {
	threadsinfo_t *threadsinfo_p = thread_info();

	debug(4, "used by %s()", function_name);
	pthread_mutex_lock  (&threadsinfo_p->mutex[PTHREAD_MUTEX_THREADSINFO]);

	return threadsinfo_p;
}

#define thread_info_unlock(...) _thread_info_unlock(__FUNCTION__, __VA_ARGS__)
static inline int _thread_info_unlock(const char *const function_name, int rc) {
	threadsinfo_t *threadsinfo_p = thread_info();

	debug(4, "used by %s()", function_name);
	pthread_mutex_unlock(&threadsinfo_p->mutex[PTHREAD_MUTEX_THREADSINFO]);

	return rc;
}

redmine authored
224 225
int threads_foreach(int (*funct)(threadinfo_t *, void *), state_t state, void *arg) {
	int i, rc;
redmine authored
226 227 228
	threadsinfo_t *threadsinfo_p = thread_info_lock();
#ifdef PARANOID
	if(threadsinfo_p == NULL)
redmine authored
229
		return thread_info_unlock(EINVAL);
redmine authored
230
#endif
redmine authored
231 232 233 234 235 236 237 238 239 240 241

	rc = 0;
	i  = 0;
	while (i < threadsinfo_p->used) {
		threadinfo_t *threadinfo_p = &threadsinfo_p->threads[i++];
		if ((state == STATE_UNKNOWN) || (threadinfo_p->state == state)) {
			if((rc=funct(threadinfo_p, arg)))
				break;
		}
	}

redmine authored
242
	return thread_info_unlock(rc);
redmine authored
243 244
}

redmine authored
245 246
time_t thread_nextexpiretime() {
	time_t nextexpiretime = 0;
redmine authored
247 248
	threadsinfo_t *threadsinfo_p = thread_info_lock();
#ifdef PARANOID
redmine authored
249
	if(threadsinfo_p == NULL)
redmine authored
250
		return thread_info_unlock(0);
redmine authored
251
#endif
redmine authored
252 253 254 255 256

	int thread_num = threadsinfo_p->used;

	while(thread_num--) {
		threadinfo_t *threadinfo_p = &threadsinfo_p->threads[thread_num];
redmine authored
257
		debug(3, "threadsinfo_p->threads[%i].state == %i;\tthreadsinfo_p->threads[%i].pthread == %p;\tthreadsinfo_p->threads[%i].expiretime == %i", 
redmine authored
258 259 260 261 262
			thread_num, threadinfo_p->state,thread_num, threadinfo_p->pthread, thread_num, threadinfo_p->expiretime);

		if(threadinfo_p->state == STATE_EXIT)
			continue;

redmine authored
263 264 265 266 267 268 269 270
		if(threadinfo_p->expiretime) {
			if(nextexpiretime)
				nextexpiretime = MIN(nextexpiretime, threadinfo_p->expiretime);
			else
				nextexpiretime = threadinfo_p->expiretime;
		}
	}

redmine authored
271
	thread_info_unlock(0);
redmine authored
272
	debug(3, "nextexpiretime == %i", nextexpiretime);
redmine authored
273 274 275
	return nextexpiretime;
}

redmine authored
276
threadinfo_t *thread_new() {
redmine authored
277 278
	threadsinfo_t *threadsinfo_p = thread_info_lock();
#ifdef PARANOID
redmine authored
279 280
	if(threadsinfo_p == NULL) {
		thread_info_unlock(0);
redmine authored
281
		return NULL;
redmine authored
282
	}
redmine authored
283
#endif
redmine authored
284

redmine authored
285 286 287 288 289
	int thread_num;
	threadinfo_t *threadinfo_p;

	if(threadsinfo_p->stacklen) {
		threadinfo_p = threadsinfo_p->threadsstack[--threadsinfo_p->stacklen];
redmine authored
290
		thread_num   =  threadinfo_p->thread_num;
redmine authored
291 292 293
	} else {
		if(threadsinfo_p->used >= threadsinfo_p->allocated) {
			threadsinfo_p->allocated += ALLOC_PORTION;
redmine authored
294
			debug(2, "Reallocated memory for threadsinfo -> %i.", threadsinfo_p->allocated);
redmine authored
295 296 297 298 299
			threadsinfo_p->threads      = (threadinfo_t *) xrealloc((char *)threadsinfo_p->threads, 
											sizeof(*threadsinfo_p->threads)     *(threadsinfo_p->allocated+2));
			threadsinfo_p->threadsstack = (threadinfo_t **)xrealloc((char *)threadsinfo_p->threadsstack,
											sizeof(*threadsinfo_p->threadsstack)*(threadsinfo_p->allocated+2));
		}
redmine authored
300

redmine authored
301 302 303
		thread_num = threadsinfo_p->used++;
		threadinfo_p = &threadsinfo_p->threads[thread_num];
	}
redmine authored
304 305

#ifdef PARANOID
redmine authored
306 307 308 309 310
	memset(threadinfo_p, 0, sizeof(*threadinfo_p));
#else
	threadinfo_p->expiretime = 0;
	threadinfo_p->errcode    = 0;
	threadinfo_p->exitcode   = 0;
redmine authored
311 312 313 314
#endif
	threadinfo_p->thread_num = thread_num;
	threadinfo_p->state	 = STATE_RUNNING;

redmine authored
315

redmine authored
316
	debug(2, "thread_new -> thread_num: %i; used: %i", thread_num, threadsinfo_p->used);
redmine authored
317
	thread_info_unlock(0);
redmine authored
318
	return threadinfo_p;
redmine authored
319 320
}

redmine authored
321
int thread_del_bynum(int thread_num) {
redmine authored
322
	debug(2, "thread_del_bynum(%i)", thread_num);
redmine authored
323 324
	threadsinfo_t *threadsinfo_p = thread_info_lock();
#ifdef PARANOID
redmine authored
325
	if(threadsinfo_p == NULL)
redmine authored
326
		return thread_info_unlock(errno);
redmine authored
327
#endif
redmine authored
328 329

	if(thread_num >= threadsinfo_p->used)
redmine authored
330
		return thread_info_unlock(EINVAL);
redmine authored
331

redmine authored
332
	threadinfo_t *threadinfo_p = &threadsinfo_p->threads[thread_num];
redmine authored
333
	threadinfo_p->state = STATE_EXIT;
redmine authored
334 335 336 337 338 339 340 341

	char **ptr = threadinfo_p->argv;
	if(ptr != NULL) {
		while(*ptr)
			free(*(ptr++));
		free(threadinfo_p->argv);
	}

redmine authored
342 343
	if(thread_num == (threadsinfo_p->used-1)) {
		threadsinfo_p->used--;
redmine authored
344
		debug(3, "thread_del_bynum(%i): there're %i threads left (#0).", thread_num, threadsinfo_p->used - threadsinfo_p->stacklen);
redmine authored
345
		return thread_info_unlock(0);
redmine authored
346 347 348 349 350
	}
	
	threadinfo_t *t = &threadsinfo_p->threads[threadsinfo_p->used-1];
	if(t->state == STATE_EXIT) {
		threadsinfo_p->used--;
redmine authored
351
		debug(3, "%i [%p] -> %i [%p]; left: %i", 
redmine authored
352 353 354 355 356
			threadsinfo_p->used, t->pthread, thread_num, threadinfo_p->pthread, threadsinfo_p->used - threadsinfo_p->stacklen);
		memcpy(threadinfo_p, t, sizeof(*threadinfo_p));
	} else {
#ifdef PARANOID
		if(threadsinfo_p->stacklen >= threadsinfo_p->allocated) {
redmine authored
357
			error("Threads metadata structures pointers stack overflowed!");
redmine authored
358
			return thread_info_unlock(EINVAL);
redmine authored
359 360 361
		}
#endif
		threadsinfo_p->threadsstack[threadsinfo_p->stacklen++] = threadinfo_p;
redmine authored
362
	}
redmine authored
363

redmine authored
364
	debug(3, "thread_del_bynum(%i): there're %i threads left (#1).", thread_num, threadsinfo_p->used - threadsinfo_p->stacklen);
redmine authored
365
	return thread_info_unlock(0);
redmine authored
366 367
}

redmine authored
368
int thread_gc(ctx_t *ctx_p) {
redmine authored
369
	int thread_num;
redmine authored
370
	time_t tm = time(NULL);
redmine authored
371
	debug(3, "tm == %i; thread %p", tm, pthread_self());
redmine authored
372
	if (!ctx_p->flags[THREADING])
redmine authored
373 374
		return 0;

redmine authored
375 376
	threadsinfo_t *threadsinfo_p = thread_info_lock();
#ifdef PARANOID
redmine authored
377
	if (threadsinfo_p == NULL)
redmine authored
378
		return thread_info_unlock(errno);
redmine authored
379
#endif
redmine authored
380

redmine authored
381
	debug(2, "There're %i threads.", threadsinfo_p->used);
redmine authored
382
	thread_num=-1;
redmine authored
383
	while (++thread_num < threadsinfo_p->used) {
redmine authored
384
		int err;
redmine authored
385
		threadinfo_t *threadinfo_p = &threadsinfo_p->threads[thread_num];
redmine authored
386

redmine authored
387
		debug(3, "Trying thread #%i (==%i) (state: %i; expire at: %i, now: %i, exitcode: %i, errcode: %i; i_p: %p; p: %p).", 
redmine authored
388 389 390
			thread_num, threadinfo_p->thread_num, threadinfo_p->state, threadinfo_p->expiretime, tm, threadinfo_p->exitcode, 
			threadinfo_p->errcode, threadinfo_p, threadinfo_p->pthread);

redmine authored
391
		if (threadinfo_p->state == STATE_EXIT)
redmine authored
392
			continue;
redmine authored
393

redmine authored
394
		if (threadinfo_p->expiretime && (threadinfo_p->expiretime <= tm)) {
redmine authored
395
			if(pthread_tryjoin_np(threadinfo_p->pthread, NULL)) {	// TODO: check this pthread_tryjoin_np() on error returnings
redmine authored
396
				error("Debug3: thread_gc(): Thread #%i is alive too long: %lu <= %lu (started at %lu)", thread_num, threadinfo_p->expiretime, tm, threadinfo_p->starttime);
redmine authored
397
				return thread_info_unlock(ETIME);
redmine authored
398
			}
redmine authored
399
		}
redmine authored
400

redmine authored
401
#ifndef VERYPARANOID
redmine authored
402
		if (threadinfo_p->state != STATE_TERM) {
redmine authored
403
			debug(3, "Thread #%i is busy, skipping (#0).", thread_num);
redmine authored
404 405
			continue;
		}
redmine authored
406
#endif
redmine authored
407 408


redmine authored
409
		debug(3, "Trying to join thread #%i: %p", thread_num, threadinfo_p->pthread);
redmine authored
410

redmine authored
411
#ifndef VERYPARANOID
redmine authored
412
		switch ((err=pthread_join(threadinfo_p->pthread, NULL))) {
redmine authored
413
#else
redmine authored
414
		switch ((err=pthread_tryjoin_np(threadinfo_p->pthread, NULL))) {
redmine authored
415
			case EBUSY:
redmine authored
416
				debug(3, "Thread #%i is busy, skipping (#1).", thread_num);
redmine authored
417
				continue;
redmine authored
418
#endif
redmine authored
419 420
			case EDEADLK:
			case EINVAL:
redmine authored
421
			case 0:
redmine authored
422
				debug(3, "Thread #%i is finished with exitcode %i (errcode %i), deleting. threadinfo_p == %p",
redmine authored
423
					thread_num, threadinfo_p->exitcode, threadinfo_p->errcode, threadinfo_p);
redmine authored
424
				break;
redmine authored
425
			default:
redmine authored
426
				error("Got error while pthread_join() or pthread_tryjoin_np().", strerror(err), err);
redmine authored
427
				return thread_info_unlock(errno);
redmine authored
428

redmine authored
429 430
		}

redmine authored
431
		if (threadinfo_p->errcode) {
redmine authored
432
			error("Got error from thread #%i: errcode %i.", thread_num, threadinfo_p->errcode);
redmine authored
433
			thread_info_unlock(0);
redmine authored
434
			thread_del_bynum(thread_num);
redmine authored
435
			return threadinfo_p->errcode;
redmine authored
436 437
		}

redmine authored
438
		thread_info_unlock(0);
redmine authored
439
		if (thread_del_bynum(thread_num))
redmine authored
440
			return errno;
redmine authored
441
		thread_info_lock();
redmine authored
442 443
	}

redmine authored
444
	debug(3, "There're %i threads left.", threadsinfo_p->used - threadsinfo_p->stacklen);
redmine authored
445
	return thread_info_unlock(0);
redmine authored
446 447
}

redmine authored
448 449
int thread_cleanup(ctx_t *ctx_p) {
	debug(3, "");
redmine authored
450 451 452
	threadsinfo_t *threadsinfo_p = thread_info_lock();

#ifdef PARANOID
redmine authored
453
	if (threadsinfo_p == NULL)
redmine authored
454
		return thread_info_unlock(errno);
redmine authored
455
#endif
redmine authored
456 457

	// Waiting for threads:
redmine authored
458
	debug(1, "There're %i opened threads. Waiting.", threadsinfo_p->used);
redmine authored
459
	while (threadsinfo_p->used) {
redmine authored
460
//		int err;
redmine authored
461
		threadinfo_t *threadinfo_p = &threadsinfo_p->threads[--threadsinfo_p->used];
redmine authored
462
		if (threadinfo_p->state == STATE_EXIT)
redmine authored
463 464
			continue;
		//pthread_kill(threadinfo_p->pthread, SIGTERM);
redmine authored
465
		debug(1, "killing pid %i with SIGTERM", threadinfo_p->child_pid);
redmine authored
466 467
		kill(threadinfo_p->child_pid, SIGTERM);
		pthread_join(threadinfo_p->pthread, NULL);
redmine authored
468
		debug(2, "thread #%i exitcode: %i", threadsinfo_p->used, threadinfo_p->exitcode);
redmine authored
469
/*
redmine authored
470
		if(threadinfo_p->callback)
redmine authored
471
			if((err=threadinfo_p->callback(ctx_p, threadinfo_p->argv)))
redmine authored
472
				warning("Got error from callback function.", strerror(err), err);
redmine authored
473
*/
redmine authored
474
		char **ptr = threadinfo_p->argv;
redmine authored
475
		while (*ptr)
redmine authored
476 477
			free(*(ptr++));
		free(threadinfo_p->argv);
redmine authored
478
	}
redmine authored
479
	debug(3, "All threads are closed.");
redmine authored
480 481

	// Freeing
redmine authored
482
	if (threadsinfo_p->allocated) {
redmine authored
483
		free(threadsinfo_p->threads);
redmine authored
484 485
		free(threadsinfo_p->threadsstack);
	}
redmine authored
486

redmine authored
487
	if (threadsinfo_p->mutex_init) {
redmine authored
488
		int i=0;
redmine authored
489 490 491 492 493
		while(i < PTHREAD_MUTEX_MAX) {
			pthread_mutex_destroy(&threadsinfo_p->mutex[i]);
			pthread_cond_destroy (&threadsinfo_p->cond [i]);
			i++;
		}
redmine authored
494
	}
redmine authored
495

redmine authored
496
#ifdef PARANOID
redmine authored
497 498
	// Reseting
	memset(threadsinfo_p, 0, sizeof(*threadsinfo_p));	// Just in case;
redmine authored
499
#endif
redmine authored
500

redmine authored
501
	debug(3, "done.");
redmine authored
502
	return thread_info_unlock(0);
redmine authored
503
}
redmine authored
504
#endif
redmine authored
505

redmine authored
506 507
volatile state_t *state_p = NULL;
volatile int exitcode = 0;
redmine authored
508
#define SHOULD_THREAD(ctx_p) ((ctx_p->flags[THREADING] != PM_OFF) && (ctx_p->flags[THREADING] != PM_SAFE || ctx_p->iteration_num))
redmine authored
509

redmine authored
510
int exec_argv(char **argv, int *child_pid) {
redmine authored
511
	debug(3, "Thread %p.", pthread_self());
redmine authored
512 513 514
	pid_t pid;
	int status;

redmine authored
515
	// Forking
redmine authored
516
	pid = privileged_fork_execvp(argv[0], (char *const *)argv);
redmine authored
517
//	debug(3, "After fork thread %p"")".", pthread_self() );
redmine authored
518
	debug(3, "Child pid is %u", pid);
redmine authored
519 520

	// Setting *child_pid value
redmine authored
521
	if (child_pid)
redmine authored
522
		*child_pid = pid;
redmine authored
523

redmine authored
524 525 526 527 528 529 530 531
	// Waiting for process end
#ifdef VERYPARANOID
	sigset_t sigset_exec, sigset_old;
	sigemptyset(&sigset_exec);
	sigaddset(&sigset_exec, SIGUSR_BLOPINT);
	pthread_sigmask(SIG_BLOCK, &sigset_exec, &sigset_old);
#endif

redmine authored
532
//	debug(3, "Pre-wait thread %p"")".", pthread_self() );
redmine authored
533
	if (privileged_waitpid(pid, &status, 0) != pid) {
redmine authored
534 535 536 537 538 539 540 541
		switch (errno) {
			case ECHILD:
				debug(2, "Child %u is already dead.", pid);
				break;
			default:
				error("Cannot waitid().");
				return errno;
		}
redmine authored
542
	}
redmine authored
543
//	debug(3, "After-wait thread %p"")".", pthread_self() );
redmine authored
544

redmine authored
545 546 547 548 549
#ifdef VERYPARANOID
	pthread_sigmask(SIG_SETMASK, &sigset_old, NULL);
#endif

	// Return
redmine authored
550
	int exitcode = WEXITSTATUS(status);
redmine authored
551
	debug(3, "execution completed with exitcode %i", exitcode);
redmine authored
552

redmine authored
553
	return exitcode;
redmine authored
554 555
}

redmine authored
556
#ifdef THREADING_SUPPORT
redmine authored
557
static inline int thread_exit(threadinfo_t *threadinfo_p, int exitcode ) {
redmine authored
558
	int err=0;
redmine authored
559 560
	threadinfo_p->exitcode = exitcode;

redmine authored
561
#if _DEBUG_FORCE | VERYPARANOID
redmine authored
562
	if (threadinfo_p->pthread != pthread_self()) {
redmine authored
563
		error("pthread id mismatch! (i_p->p) %p != (p) %p""", threadinfo_p->pthread, pthread_self() );
redmine authored
564 565 566 567
		return EINVAL;
	}
#endif

redmine authored
568 569
	if (threadinfo_p->callback) {
		if (threadinfo_p->ctx_p->flags[DEBUG]>2) {
redmine authored
570
			debug(3, "thread %p, argv: ", threadinfo_p->pthread);
redmine authored
571 572
			char **argv = threadinfo_p->argv;
			while(*argv) {
redmine authored
573
				debug(3, "\t%p == %s", *argv, *argv);
redmine authored
574 575 576
				argv++;
			}
		}
redmine authored
577
		if ((err=threadinfo_p->callback(threadinfo_p->ctx_p, threadinfo_p->callback_arg))) {
redmine authored
578
			error("Got error from callback function.", strerror(err), err);
redmine authored
579 580 581 582 583 584
			threadinfo_p->errcode = err;
		}
	}

	// Notifying the parent-thread, that it's time to collect garbage threads
	threadinfo_p->state    = STATE_TERM;
redmine authored
585
	debug(3, "thread %p is sending signal to sighandler to call GC", threadinfo_p->pthread);
redmine authored
586
	return pthread_kill(pthread_sighandler, SIGUSR_THREAD_GC);
redmine authored
587
}
redmine authored
588
#endif
redmine authored
589

redmine authored
590 591 592
static inline void so_call_sync_finished(int n, api_eventinfo_t *ei) {
	int i = 0;
	api_eventinfo_t *ei_i = ei;
redmine authored
593
	while (i < n) {
redmine authored
594
#ifdef PARANOID
redmine authored
595 596
		if (ei_i->path == NULL) {
			warning("ei_i->path == NULL");
redmine authored
597 598 599 600 601 602 603 604
			i++;
			continue;
		}
#endif
		free((char *)ei_i->path);
		ei_i++;
		i++;
	}
redmine authored
605
	if (ei != NULL)
redmine authored
606 607 608 609 610
		free(ei);

	return;
}

redmine authored
611
#ifdef THREADING_SUPPORT
redmine authored
612
int so_call_sync_thread(threadinfo_t *threadinfo_p) {
redmine authored
613
	debug(3, "thread_num == %i; threadinfo_p == %p; i_p->pthread %p; thread %p", 
redmine authored
614 615
			threadinfo_p->thread_num, threadinfo_p, threadinfo_p->pthread, pthread_self());

redmine authored
616
	ctx_t *ctx_p	= threadinfo_p->ctx_p;
redmine authored
617 618 619
	int n			= threadinfo_p->n;
	api_eventinfo_t *ei	= threadinfo_p->ei;

redmine authored
620
	int err=0, rc=0, try_again = 0;
redmine authored
621
	do {
redmine authored
622
		try_again = 0;
redmine authored
623 624
		threadinfo_p->try_n++;

redmine authored
625
		rc = ctx_p->handler_funct.sync(n, ei);
redmine authored
626

redmine authored
627
		if ((err=exitcode_process(threadinfo_p->ctx_p, rc))) {
redmine authored
628
			try_again = ((!ctx_p->retries) || (threadinfo_p->try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT);
redmine authored
629 630
			warning("Bad exitcode %i (errcode %i). %s.", rc, err, try_again?"Retrying":"Give up");
			if (try_again) {
redmine authored
631 632
				debug(2, "Sleeping for %u seconds before the retry.", ctx_p->syncdelay);
				sleep(ctx_p->syncdelay);
redmine authored
633 634
			}
		}
redmine authored
635

redmine authored
636
	} while (err && ((!ctx_p->retries) || (threadinfo_p->try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT));
redmine authored
637

redmine authored
638
	if (err && !ctx_p->flags[IGNOREFAILURES]) {
redmine authored
639
		error("Bad exitcode %i (errcode %i)", rc, err);
redmine authored
640 641 642
		threadinfo_p->errcode = err;
	}

redmine authored
643 644
	so_call_sync_finished(n, ei);

redmine authored
645
	if ((err=thread_exit(threadinfo_p, rc))) {
redmine authored
646 647 648
		exitcode = err;	// This's global variable "exitcode"
		pthread_kill(pthread_sighandler, SIGTERM);
	}
redmine authored
649

redmine authored
650
	return rc;
redmine authored
651
}
redmine authored
652
#endif
redmine authored
653

redmine authored
654
static inline int so_call_sync(ctx_t *ctx_p, indexes_t *indexes_p, int n, api_eventinfo_t *ei) {
redmine authored
655
	debug(2, "n == %i", n);
redmine authored
656

redmine authored
657
#ifdef THREADING_SUPPORT
redmine authored
658
	if (!SHOULD_THREAD(ctx_p)) {
redmine authored
659
#endif
redmine authored
660
		int rc=0, ret=0, err=0;
redmine authored
661
		int try_n=0, try_again;
redmine authored
662
		state_t status = STATE_UNKNOWN;
redmine authored
663

redmine authored
664 665
//		indexes_p->nonthreaded_syncing_fpath2ei_ht = g_hash_table_dup(indexes_p->fpath2ei_ht, g_str_hash, g_str_equal, free, free, (gpointer(*)(gpointer))strdup, eidup);
		indexes_p->nonthreaded_syncing_fpath2ei_ht = indexes_p->fpath2ei_ht;
redmine authored
666

redmine authored
667
		do {
redmine authored
668
			try_again = 0;
redmine authored
669 670
			try_n++;

redmine authored
671 672
			alarm(ctx_p->synctimeout);
			rc = ctx_p->handler_funct.sync(n, ei);
redmine authored
673 674
			alarm(0);

redmine authored
675
			if ((err=exitcode_process(ctx_p, rc))) {
redmine authored
676
				if ((try_n == 1) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT)) {
redmine authored
677 678 679 680 681
					status = ctx_p->state;
					ctx_p->state = STATE_SYNCHANDLER_ERR;
					main_status_update(ctx_p);
				}

redmine authored
682
				try_again = ((!ctx_p->retries) || (try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT);
redmine authored
683 684
				warning("Bad exitcode %i (errcode %i). %s.", rc, err, try_again?"Retrying":"Give up");
				if (try_again) {
redmine authored
685 686
					debug(2, "Sleeping for %u seconds before the retry.", ctx_p->syncdelay);
					sleep(ctx_p->syncdelay);
redmine authored
687 688
				}
			}
redmine authored
689
		} while (err && ((!ctx_p->retries) || (try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT));
redmine authored
690
		if (err && !ctx_p->flags[IGNOREFAILURES]) {
redmine authored
691
			error("Bad exitcode %i (errcode %i)", rc, err);
redmine authored
692
			ret = err;
redmine authored
693 694 695 696
		} else
		if (status != STATE_UNKNOWN) {
			ctx_p->state = status;
			main_status_update(ctx_p);
redmine authored
697
		}
redmine authored
698

redmine authored
699 700
//		g_hash_table_destroy(indexes_p->nonthreaded_syncing_fpath2ei_ht);
		indexes_p->nonthreaded_syncing_fpath2ei_ht = NULL;
redmine authored
701

redmine authored
702 703
		so_call_sync_finished(n, ei);
		return ret;
redmine authored
704
#ifdef THREADING_SUPPORT
redmine authored
705
	}
redmine authored
706 707

	threadinfo_t *threadinfo_p = thread_new();
redmine authored
708
	if (threadinfo_p == NULL)
redmine authored
709 710
		return errno;

redmine authored
711
	threadinfo_p->try_n       = 0;
redmine authored
712 713
	threadinfo_p->callback    = NULL;
	threadinfo_p->argv        = NULL;
redmine authored
714
	threadinfo_p->ctx_p       = ctx_p;
redmine authored
715
	threadinfo_p->starttime	  = time(NULL);
redmine authored
716
	threadinfo_p->fpath2ei_ht = g_hash_table_dup(indexes_p->fpath2ei_ht, g_str_hash, g_str_equal, free, free, (gpointer(*)(gpointer))strdup, eidup);
redmine authored
717 718
	threadinfo_p->n           = n;
	threadinfo_p->ei          = ei;
redmine authored
719
	threadinfo_p->iteration   = ctx_p->iteration_num;
redmine authored
720

redmine authored
721
	if (ctx_p->synctimeout)
redmine authored
722
		threadinfo_p->expiretime = threadinfo_p->starttime + ctx_p->synctimeout;
redmine authored
723

redmine authored
724
	if (pthread_create(&threadinfo_p->pthread, NULL, (void *(*)(void *))so_call_sync_thread, threadinfo_p)) {
redmine authored
725
		error("Cannot pthread_create().");
redmine authored
726 727
		return errno;
	}
redmine authored
728
	debug(3, "thread %p", threadinfo_p->pthread);
redmine authored
729
	return 0;
redmine authored
730
#endif
redmine authored
731 732
}

redmine authored
733
static inline int so_call_rsync_finished(ctx_t *ctx_p, const char *inclistfile, const char *exclistfile) {
redmine authored
734
	int ret0, ret1;
redmine authored
735
	debug(5, "");
redmine authored
736
	if (ctx_p->flags[DONTUNLINK]) 
redmine authored
737 738
		return 0;

redmine authored
739
	if (inclistfile == NULL) {
redmine authored
740
		error("inclistfile == NULL.");
redmine authored
741 742
		return EINVAL;
	}
redmine authored
743

redmine authored
744
	debug(3, "unlink()-ing \"%s\"", inclistfile);
redmine authored
745 746
	ret0 = unlink(inclistfile);

redmine authored
747
	if (ctx_p->flags[RSYNCPREFERINCLUDE])
redmine authored
748 749
		return ret0;

redmine authored
750
	if (exclistfile == NULL) {
redmine authored
751
		error("exclistfile == NULL.");
redmine authored
752 753 754
		return EINVAL;
	}

redmine authored
755
	debug(3, "unlink()-ing \"%s\"", exclistfile);
redmine authored
756 757 758
	ret1 = unlink(exclistfile);

	return ret0 == 0 ? ret1 : ret0;
redmine authored
759 760
}

redmine authored
761
#ifdef THREADING_SUPPORT
redmine authored
762
int so_call_rsync_thread(threadinfo_t *threadinfo_p) {
redmine authored
763
	debug(3, "thread_num == %i; threadinfo_p == %p; i_p->pthread %p; thread %p", 
redmine authored
764 765
			threadinfo_p->thread_num, threadinfo_p, threadinfo_p->pthread, pthread_self());

redmine authored
766
	ctx_t *ctx_p	= threadinfo_p->ctx_p;
redmine authored
767 768
	char **argv		= threadinfo_p->argv;

redmine authored
769
	int err=0, rc=0, try_again;
redmine authored
770
	do {
redmine authored
771
		try_again=0;
redmine authored
772 773
		threadinfo_p->try_n++;

redmine authored
774
		rc = ctx_p->handler_funct.rsync(argv[0], argv[1]);
redmine authored
775
		if ((err=exitcode_process(threadinfo_p->ctx_p, rc))) {
redmine authored
776
			try_again = ((!ctx_p->retries) || (threadinfo_p->try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT);
redmine authored
777 778
			warning("Bad exitcode %i (errcode %i). %s.", rc, err, try_again?"Retrying":"Give up");
			if (try_again) {
redmine authored
779 780
				debug(2, "Sleeping for %u seconds before the retry.", ctx_p->syncdelay);
				sleep(ctx_p->syncdelay);
redmine authored
781 782
			}
		}
redmine authored
783
	} while (try_again);
redmine authored
784

redmine authored
785
	if (err && !ctx_p->flags[IGNOREFAILURES]) {
redmine authored
786
		error("Bad exitcode %i (errcode %i)", rc, err);
redmine authored
787 788
		threadinfo_p->errcode = err;
	}
redmine authored
789

redmine authored
790
	if ((err=so_call_rsync_finished(ctx_p, argv[0], argv[1]))) {
redmine authored
791 792 793
		exitcode = err;	// This's global variable "exitcode"
		pthread_kill(pthread_sighandler, SIGTERM);
	}
redmine authored
794 795 796 797 798

	free(argv[0]);
	free(argv[1]);
	free(argv);

redmine authored
799
	if ((err=thread_exit(threadinfo_p, rc))) {
redmine authored
800 801 802 803
		exitcode = err;	// This's global variable "exitcode"
		pthread_kill(pthread_sighandler, SIGTERM);
	}

redmine authored
804
	return rc;
redmine authored
805
}
redmine authored
806
#endif
redmine authored
807

redmine authored
808
static inline int so_call_rsync(ctx_t *ctx_p, indexes_t *indexes_p, const char *inclistfile, const char *exclistfile) {
redmine authored
809
	debug(2, "inclistfile == \"%s\"; exclistfile == \"%s\"", inclistfile, exclistfile);
redmine authored
810

redmine authored
811
#ifdef THREADING_SUPPORT
redmine authored
812
	if (!SHOULD_THREAD(ctx_p)) {
redmine authored
813
#endif
redmine authored
814
		debug(3, "ctx_p->handler_funct.rsync == %p", ctx_p->handler_funct.rsync);
redmine authored
815

redmine authored
816 817
//		indexes_p->nonthreaded_syncing_fpath2ei_ht = g_hash_table_dup(indexes_p->fpath2ei_ht, g_str_hash, g_str_equal, free, free, (gpointer(*)(gpointer))strdup, eidup);
		indexes_p->nonthreaded_syncing_fpath2ei_ht = indexes_p->fpath2ei_ht;
redmine authored
818

redmine authored
819
		int rc=0, err=0;
redmine authored
820
		int try_n=0, try_again;
redmine authored
821
		state_t status = STATE_UNKNOWN;
redmine authored
822
		do {
redmine authored
823
			try_again = 0;
redmine authored
824 825
			try_n++;

redmine authored
826 827
			alarm(ctx_p->synctimeout);
			rc = ctx_p->handler_funct.rsync(inclistfile, exclistfile);
redmine authored
828 829
			alarm(0);

redmine authored
830
			if ((err=exitcode_process(ctx_p, rc))) {
redmine authored
831
				if ((try_n == 1) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT)) {
redmine authored
832 833 834 835
					status = ctx_p->state;
					ctx_p->state = STATE_SYNCHANDLER_ERR;
					main_status_update(ctx_p);
				}
redmine authored
836
				try_again = ((!ctx_p->retries) || (try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT);
redmine authored
837 838
				warning("Bad exitcode %i (errcode %i). %s.", rc, err, try_again?"Retrying":"Give up");
				if (try_again) {
redmine authored
839 840
					debug(2, "Sleeping for %u seconds before the retry.", ctx_p->syncdelay);
					sleep(ctx_p->syncdelay);
redmine authored
841 842
				}
			}
redmine authored
843 844
		} while (try_again);
		if (err && !ctx_p->flags[IGNOREFAILURES]) {
redmine authored
845
			error("Bad exitcode %i (errcode %i)", rc, err);
redmine authored
846
			rc = err;
redmine authored
847 848 849 850
		} else
		if (status != STATE_UNKNOWN) {
			ctx_p->state = status;
			main_status_update(ctx_p);
redmine authored
851 852
		}

redmine authored
853 854
//		g_hash_table_destroy(indexes_p->nonthreaded_syncing_fpath2ei_ht);
		indexes_p->nonthreaded_syncing_fpath2ei_ht = NULL;
redmine authored
855

redmine authored
856
		int ret_cleanup;
redmine authored
857
		if ((ret_cleanup=so_call_rsync_finished(ctx_p, inclistfile, exclistfile)))
redmine authored
858 859
			return rc ? rc : ret_cleanup;
		return rc;
redmine authored
860
#ifdef THREADING_SUPPORT
redmine authored
861 862 863 864 865 866
	}

	threadinfo_t *threadinfo_p = thread_new();
	if(threadinfo_p == NULL)
		return errno;

redmine authored
867
	threadinfo_p->try_n       = 0;
redmine authored
868 869
	threadinfo_p->callback    = NULL;
	threadinfo_p->argv        = xmalloc(sizeof(char *) * 3);
redmine authored
870
	threadinfo_p->ctx_p       = ctx_p;
redmine authored
871 872
	threadinfo_p->starttime	  = time(NULL);
	threadinfo_p->fpath2ei_ht = g_hash_table_dup(indexes_p->fpath2ei_ht, g_str_hash, g_str_equal, free, free, (gpointer(*)(gpointer))strdup, eidup);
redmine authored
873
	threadinfo_p->iteration   = ctx_p->iteration_num;
redmine authored
874 875 876 877

	threadinfo_p->argv[0]	  = strdup(inclistfile);
	threadinfo_p->argv[1]	  = strdup(exclistfile);

redmine authored
878 879
	if(ctx_p->synctimeout)
		threadinfo_p->expiretime = threadinfo_p->starttime + ctx_p->synctimeout;
redmine authored
880 881

	if(pthread_create(&threadinfo_p->pthread, NULL, (void *(*)(void *))so_call_rsync_thread, threadinfo_p)) {
redmine authored
882
		error("Cannot pthread_create().");
redmine authored
883 884
		return errno;
	}
redmine authored
885
	debug(3, "thread %p", threadinfo_p->pthread);
redmine authored
886
	return 0;
redmine authored
887
#endif
redmine authored
888 889
}

redmine authored
890 891
// === SYNC_EXEC() === {

redmine authored
892
//#define SYNC_EXEC(...)      (SHOULD_THREAD(ctx_p) ? sync_exec_thread      : sync_exec     )(__VA_ARGS__)
redmine authored
893
#ifdef THREADING_SUPPORT
redmine authored
894
#define SYNC_EXEC_ARGV(...) (SHOULD_THREAD(ctx_p) ? sync_exec_argv_thread : sync_exec_argv)(__VA_ARGS__)
redmine authored
895 896 897
#else
#define SYNC_EXEC_ARGV(...) sync_exec_argv(__VA_ARGS__)
#endif
redmine authored
898 899 900 901 902 903

#define debug_argv_dump(level, argv)\
	if (unlikely(ctx_p->flags[DEBUG] >= level))\
		argv_dump(level, argv)

static inline void argv_dump(int debug_level, char **argv) {
redmine authored
904
#ifdef _DEBUG_FORCE
redmine authored
905 906
	debug(19, "(%u, %p)", debug_level, argv);
#endif
redmine authored
907 908
	char **argv_p = argv;
	while (*argv_p != NULL) {
redmine authored
909
		debug(debug_level, "%p: \"%s\"", *argv_p, *argv_p);
redmine authored
910 911 912 913 914
		argv_p++;
	}

	return;
}
redmine authored
915

redmine authored
916
#define _sync_exec_getargv(argv, firstarg, COPYARG) {\
redmine authored
917
	va_list arglist;\
redmine authored
918
	va_start(arglist, firstarg);\
redmine authored
919 920 921 922 923
\
	int i = 0;\
	do {\
		char *arg;\
		if(i >= MAXARGUMENTS) {\
redmine authored
924
			error("Too many arguments (%i >= %i).", i, MAXARGUMENTS);\
redmine authored
925 926 927
			return ENOMEM;\
		}\
		arg = (char *)va_arg(arglist, const char *const);\
redmine authored
928
		argv[i] = arg!=NULL ? COPYARG : NULL;\
redmine authored
929 930 931 932
	} while(argv[i++] != NULL);\
	va_end(arglist);\
}

redmine authored
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
char *sync_path_rel2abs(ctx_t *ctx_p, const char *path_rel, size_t path_rel_len, size_t *path_abs_len_p, char *path_abs_oldptr) {
	if (path_rel == NULL)
		return NULL;

	if (path_rel_len == -1)
		path_rel_len = strlen(path_rel);

	char  *path_abs;
	size_t watchdirlen = 
		(ctx_p->watchdir == ctx_p->watchdirwslash) ? 0 : ctx_p->watchdirlen;
		// if [watchdir == "/"] ? 0 : watchdir.length()

	size_t path_abs_len = path_rel_len + watchdirlen + 1;

	path_abs = (path_abs_len_p == NULL || path_abs_len >= *path_abs_len_p) ?
			xrealloc(path_abs_oldptr, path_abs_len+1) :
			path_abs_oldptr;

	if (path_abs_oldptr == NULL) {
		memcpy(path_abs, ctx_p->watchdir, watchdirlen);
		path_abs[watchdirlen] = '/';
	}
	memcpy(&path_abs[watchdirlen+1], path_rel, path_rel_len+1);

	if (path_abs_len_p != NULL)
		*path_abs_len_p = path_abs_len;

	return path_abs;
}

redmine authored
963
char *sync_path_abs2rel(ctx_t *ctx_p, const char *path_abs, size_t path_abs_len, size_t *path_rel_len_p, char *path_rel_oldptr) {
redmine authored
964
	if (path_abs == NULL)
redmine authored
965 966
		return NULL;

redmine authored
967
	if (path_abs_len == -1)
redmine authored
968 969
		path_abs_len = strlen(path_abs);

redmine authored
970
	size_t path_rel_len;
redmine authored
971
	char  *path_rel;
redmine authored
972 973
	size_t watchdirlen = 
		(ctx_p->watchdir == ctx_p->watchdirwslash) ? 0 : ctx_p->watchdirlen;
redmine authored
974

redmine authored
975
	signed long path_rel_len_signed = path_abs_len - (watchdirlen+1);
redmine authored
976 977 978

	path_rel_len = (path_rel_len_signed > 0) ? path_rel_len_signed : 0;

redmine authored
979 980 981
	path_rel = (path_rel_len_p == NULL || path_rel_len >= *path_rel_len_p) ? 
			xrealloc(path_rel_oldptr, path_rel_len+1) : 
			path_rel_oldptr;
redmine authored
982

redmine authored
983
	if (!path_rel_len) {
redmine authored
984 985 986 987
		path_rel[0] = 0;
		return path_rel;
	}

redmine authored
988
	memcpy(path_rel, &path_abs[watchdirlen+1], path_rel_len+1);
redmine authored
989 990 991

#ifdef VERYPARANOID
	// Removing "/" on the end
redmine authored
992
	debug(3, "\"%s\" (len: %i) --%i--> \"%s\" (len: %i) + ", 
redmine authored
993
		path_abs, path_abs_len, path_rel[path_rel_len - 1] == '/',
redmine authored
994
		ctx_p->watchdirwslash, watchdirlen+1);
redmine authored
995
	if (path_rel[path_rel_len - 1] == '/')
redmine authored
996
		path_rel[--path_rel_len] = 0x00;
redmine authored
997
	debug(3, "\"%s\" (len: %i)", path_rel, path_rel_len);
redmine authored
998 999
#endif

redmine authored
1000
	if (path_rel_len_p != NULL)
redmine authored
1001 1002 1003 1004 1005
		*path_rel_len_p = path_rel_len;

	return path_rel;
}

redmine authored
1006
pid_t clsyncapi_fork(ctx_t *ctx_p) {
redmine authored
1007
//	if(ctx_p->flags[THREADING])
redmine authored
1008
//		return fork();
redmine authored
1009 1010 1011

	// Cleaning stale pids. TODO: Optimize this. Remove this GC.
	int i=0;
redmine authored
1012
	while (i < ctx_p->children) {
redmine authored
1013
		if (privileged_waitpid(ctx_p->child_pid[i], NULL, WNOHANG)<0)
redmine authored
1014
			if(errno==ECHILD)
redmine authored
1015
				ctx_p->child_pid[i] = ctx_p->child_pid[--ctx_p->children];
redmine authored
1016 1017 1018 1019
		i++;
	}

	// Too many children
redmine authored
1020
	if (ctx_p->children >= MAXCHILDREN) {
redmine authored
1021 1022
		errno = ECANCELED;
		return -1;
redmine authored
1023
	}
redmine authored
1024 1025 1026

	// Forking
	pid_t pid = fork();
redmine authored
1027
	ctx_p->child_pid[ctx_p->children++] = pid;
redmine authored
1028 1029 1030
	return pid;
}

redmine authored
1031
int sync_exec_argv(ctx_t *ctx_p, indexes_t *indexes_p, thread_callbackfunct_t callback, thread_callbackfunct_arg_t *callback_arg_p, char **argv) {
redmine authored
1032
	debug(2, "");
redmine authored
1033

redmine authored
1034
	debug_argv_dump(2, argv);
redmine authored
1035

redmine authored
1036 1037
//	indexes_p->nonthreaded_syncing_fpath2ei_ht = g_hash_table_dup(indexes_p->fpath2ei_ht, g_str_hash, g_str_equal, free, free, (gpointer(*)(gpointer))strdup, eidup);
	indexes_p->nonthreaded_syncing_fpath2ei_ht = indexes_p->fpath2ei_ht;
redmine authored
1038

redmine authored
1039
	int exitcode=0, ret=0, err=0;
redmine authored
1040
	int try_n=0, try_again;
redmine authored
1041
	state_t status = STATE_UNKNOWN;
redmine authored
1042
	do {
redmine authored
1043
		try_again = 0;
redmine authored
1044
		try_n++;
redmine authored
1045
		debug(2, "try_n == %u (retries == %u)", try_n, ctx_p->retries);
redmine authored
1046

redmine authored
1047 1048 1049 1050
		alarm(ctx_p->synctimeout);
		ctx_p->children = 1;
		exitcode = exec_argv(argv, ctx_p->child_pid );
		ctx_p->children = 0;
redmine authored
1051 1052
		alarm(0);

redmine authored
1053
		if ((err=exitcode_process(ctx_p, exitcode))) {
redmine authored
1054
			if ((try_n == 1) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT)) {
redmine authored
1055 1056 1057 1058
				status = ctx_p->state;
				ctx_p->state = STATE_SYNCHANDLER_ERR;
				main_status_update(ctx_p);
			}
redmine authored
1059
			try_again = ((!ctx_p->retries) || (try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT);
redmine authored
1060
			warning("Bad exitcode %i (errcode %i). %s.", exitcode, err, try_again?"Retrying":"Give up");
redmine authored
1061
			if (try_again) {
redmine authored
1062 1063
				debug(2, "Sleeping for %u seconds before the retry.", ctx_p->syncdelay);
				sleep(ctx_p->syncdelay);
redmine authored
1064 1065 1066
			}
		}
	} while(try_again);
redmine authored
1067

redmine authored
1068
	if (err && !ctx_p->flags[IGNOREFAILURES]) {
redmine authored
1069
		error("Bad exitcode %i (errcode %i)", exitcode, err);
redmine authored
1070
		ret = err;
redmine authored
1071 1072 1073 1074
	} else
	if (status != STATE_UNKNOWN) {
		ctx_p->state = status;
		main_status_update(ctx_p);
redmine authored
1075 1076
	}

redmine authored
1077
	if (callback != NULL) {
redmine authored
1078
		int nret = callback(ctx_p, callback_arg_p);
redmine authored
1079
		if (nret) {
redmine authored
1080
			error("Got error while callback().");
redmine authored
1081
			if (!ret) ret=nret;
redmine authored
1082 1083 1084
		}
	}

redmine authored
1085 1086
//	g_hash_table_destroy(indexes_p->nonthreaded_syncing_fpath2ei_ht);
	indexes_p->nonthreaded_syncing_fpath2ei_ht = NULL;
redmine authored
1087
	return ret;
redmine authored
1088 1089
}

redmine authored
1090
/*
redmine authored
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
static inline int sync_exec(ctx_t *ctx_p, indexes_t *indexes_p, thread_callbackfunct_t callback, thread_callbackfunct_arg_t *callback_arg_p, ...) {
	int rc;
	debug(2, "");

	char **argv = (char **)xcalloc(sizeof(char *), MAXARGUMENTS);
	memset(argv, 0, sizeof(char *)*MAXARGUMENTS);

	_sync_exec_getargv(argv, callback_arg_p, arg);

	rc = sync_exec_argv(ctx_p, indexes_p, callback, callback_arg_p, argv);
	free(argv);
	return rc;
}
redmine authored
1104
*/
redmine authored
1105

redmine authored
1106
#ifdef THREADING_SUPPORT
redmine authored
1107
int __sync_exec_thread(threadinfo_t *threadinfo_p) {
redmine authored
1108
	char **argv		= threadinfo_p->argv;
redmine authored
1109
	ctx_t *ctx_p		= threadinfo_p->ctx_p;
redmine authored
1110

redmine authored
1111 1112
	debug(3, "thread_num == %i; threadinfo_p == %p; i_p->pthread %p; thread %p""", 
			threadinfo_p->thread_num, threadinfo_p, threadinfo_p->pthread, pthread_self() );
redmine authored
1113

redmine authored
1114
	int err=0, exec_exitcode=0, try_again;
redmine authored
1115
	do {
redmine authored
1116
		try_again = 0;
redmine authored
1117 1118
		threadinfo_p->try_n++;

redmine authored
1119
		exec_exitcode = exec_argv(argv, &threadinfo_p->child_pid );
redmine authored
1120

redmine authored
1121
		if ((err=exitcode_process(threadinfo_p->ctx_p, exec_exitcode))) {
redmine authored
1122
			try_again = ((!ctx_p->retries) || (threadinfo_p->try_n < ctx_p->retries)) && (ctx_p->state != STATE_TERM) && (ctx_p->state != STATE_EXIT);
redmine authored
1123 1124
			warning("__sync_exec_thread(): Bad exitcode %i (errcode %i). %s.", exec_exitcode, err, try_again?"Retrying":"Give up");
			if (try_again) {
redmine authored
1125 1126
				debug(2, "Sleeping for %u seconds before the retry.", ctx_p->syncdelay);
				sleep(ctx_p->syncdelay);
redmine authored
1127 1128
			}
		}
redmine authored
1129

redmine authored
1130
	} while (try_again);
redmine authored
1131

redmine authored
1132
	if (err && !ctx_p->flags[IGNOREFAILURES]) {
redmine authored
1133
		error("Bad exitcode %i (errcode %i)", exec_exitcode, err);
redmine authored
1134 1135
		threadinfo_p->errcode = err;
	}
redmine authored
1136

redmine authored
1137 1138
	g_hash_table_destroy(threadinfo_p->fpath2ei_ht);

redmine authored
1139
	if ((err=thread_exit(threadinfo_p, exec_exitcode))) {
redmine authored
1140 1141 1142 1143
		exitcode = err;	// This's global variable "exitcode"
		pthread_kill(pthread_sighandler, SIGTERM);
	}

redmine authored
1144 1145
	debug(3, "thread_num == %i; threadinfo_p == %p; i_p->pthread %p; thread %p""; errcode %i", 
			threadinfo_p->thread_num, threadinfo_p, threadinfo_p->pthread, pthread_self(),  threadinfo_p->errcode);
redmine authored
1146
	return exec_exitcode;
redmine authored
1147 1148
}

redmine authored
1149
static inline int sync_exec_argv_thread(ctx_t *ctx_p, indexes_t *indexes_p, thread_callbackfunct_t callback, thread_callbackfunct_arg_t *callback_arg_p, char **argv) {
redmine authored
1150
	debug(2, "");
redmine authored
1151

redmine authored
1152
	debug_argv_dump(2, argv);
redmine authored
1153

redmine authored
1154
	threadinfo_t *threadinfo_p = thread_new();
redmine authored
1155
	if (threadinfo_p == NULL)
redmine authored
1156 1157
		return errno;

redmine authored
1158 1159 1160 1161 1162 1163 1164 1165
	threadinfo_p->try_n        = 0;
	threadinfo_p->callback     = callback;
	threadinfo_p->callback_arg = callback_arg_p;
	threadinfo_p->argv         = argv;
	threadinfo_p->ctx_p        = ctx_p;
	threadinfo_p->starttime	   = time(NULL);
	threadinfo_p->fpath2ei_ht  = g_hash_table_dup(indexes_p->fpath2ei_ht, g_str_hash, g_str_equal, free, free, (gpointer(*)(gpointer))strdup, eidup);
	threadinfo_p->iteration    = ctx_p->iteration_num;
redmine authored
1166

redmine authored
1167
	if (ctx_p->synctimeout)
redmine authored
1168
		threadinfo_p->expiretime = threadinfo_p->starttime + ctx_p->synctimeout;
redmine authored
1169

redmine authored
1170
	if (pthread_create(&threadinfo_p->pthread, NULL, (void *(*)(void *))__sync_exec_thread, threadinfo_p)) {
redmine authored
1171
		error("Cannot pthread_create().");
redmine authored
1172 1173
		return errno;
	}
redmine authored
1174
	debug(3, "thread %p", threadinfo_p->pthread);
redmine authored
1175 1176 1177
	return 0;
}

redmine authored
1178
/*
redmine authored
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
static inline int sync_exec_thread(ctx_t *ctx_p, indexes_t *indexes_p, thread_callbackfunct_t callback, thread_callbackfunct_arg_t *callback_arg_p, ...) {
	debug(2, "");

	char **argv = (char **)xcalloc(sizeof(char *), MAXARGUMENTS);
	memset(argv, 0, sizeof(char *)*MAXARGUMENTS);

	_sync_exec_getargv(argv, callback_arg_p, strdup(arg));

	return sync_exec_argv_thread(ctx_p, indexes_p, callback, callback_arg_p, argv);
}
redmine authored
1189
*/
redmine authored
1190
#endif
redmine authored
1191

redmine authored
1192 1193
// } === SYNC_EXEC() ===

redmine authored
1194
static int sync_queuesync(const char *fpath_rel, eventinfo_t *evinfo, ctx_t *ctx_p, indexes_t *indexes_p, queue_id_t queue_id) {
redmine authored
1195

redmine authored
1196
	debug(3, "sync_queuesync(\"%s\", ...): fsize == %lu; tres == %lu, queue_id == %u", fpath_rel, evinfo->fsize, ctx_p->bfilethreshold, queue_id);
redmine authored
1197
	if(queue_id == QUEUE_AUTO)
redmine authored
1198
		queue_id = (evinfo->fsize > ctx_p->bfilethreshold) ? QUEUE_BIGFILE : QUEUE_NORMAL;
redmine authored
1199

redmine authored
1200
	queueinfo_t *queueinfo = &ctx_p->_queues[queue_id];
redmine authored
1201 1202 1203 1204

	if(!queueinfo->stime)
		queueinfo->stime = time(NULL);

redmine authored
1205
//	char *fpath_rel = sync_path_abs2rel(ctx_p, fpath, -1, NULL, NULL);
redmine authored
1206

redmine authored
1207
	// Filename can contain "" character that conflicts with event-row separator of list-files.
redmine authored
1208
	if(strchr(fpath_rel, '\n')) {
redmine authored
1209
		// At the moment, we will just ignore events of such files :(
redmine authored
1210
		debug(3, "There's \"\\n\" character in path \"%s\". Ignoring it :(. Feedback to: https://github.com/xaionaro/clsync/issues/12", fpath_rel);
redmine authored
1211 1212 1213
		return 0;
	}

redmine authored
1214
#ifdef CLUSTER_SUPPORT
redmine authored
1215
	if(ctx_p->cluster_iface)
redmine authored
1216
		cluster_capture(fpath_rel);
redmine authored
1217
#endif
redmine authored
1218

redmine authored
1219 1220 1221 1222 1223 1224
	eventinfo_t *evinfo_q   = indexes_lookupinqueue(indexes_p, fpath_rel, queue_id);
	if(evinfo_q == NULL) {
		eventinfo_t *evinfo_dup = (eventinfo_t *)xmalloc(sizeof(*evinfo_dup));
		memcpy(evinfo_dup, evinfo, sizeof(*evinfo_dup));
		return indexes_queueevent(indexes_p, strdup(fpath_rel), evinfo_dup, queue_id);
	} else {
redmine authored
1225
		evinfo_merge(ctx_p, evinfo_q, evinfo);
redmine authored
1226 1227 1228
	}

	return 0;
redmine authored
1229 1230
}

redmine authored
1231 1232 1233 1234
static inline void evinfo_initialevmask(ctx_t *ctx_p, eventinfo_t *evinfo_p, int isdir) {
	switch(ctx_p->flags[MONITOR]) {
#ifdef FANOTIFY_SUPPORT
		case NE_FANOTIFY:
redmine authored
1235
			critical("fanotify is not supported");
redmine authored
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
			break;
#endif
#if INOTIFY_SUPPORT | KQUEUE_SUPPORT
#ifdef INOTIFY_SUPPORT
		case NE_INOTIFY:
#endif
#ifdef KQUEUE_SUPPORT
		case NE_KQUEUE:
#endif
			evinfo_p->evmask = IN_CREATE_SELF;
			if (isdir)
				evinfo_p->evmask |= IN_ISDIR;
			break;
#endif
#ifdef BSM_SUPPORT
		case NE_BSM:
redmine authored
1252
		case NE_BSM_PREFETCH:
redmine authored
1253 1254 1255
			evinfo_p->evmask = (isdir ? AUE_MKDIR : AUE_OPEN_RWC);
			break;
#endif
redmine authored
1256 1257 1258 1259 1260
#ifdef GIO_SUPPORT
		case NE_GIO:
			evinfo_p->evmask = G_FILE_MONITOR_EVENT_CREATED;
			break;
#endif
redmine authored
1261 1262 1263 1264 1265 1266 1267 1268 1269
#ifdef VERYPARANOID
		default:
			critical("Unknown monitor subsystem: %u", ctx_p->flags[MONITOR]);
#endif
	}
	return;
}

static inline void api_evinfo_initialevmask(ctx_t *ctx_p, api_eventinfo_t *evinfo_p, int isdir) {
redmine authored
1270
	eventinfo_t evinfo = {0};
redmine authored
1271 1272 1273 1274 1275
	evinfo_initialevmask(ctx_p, &evinfo, isdir);
	evinfo_p->evmask = evinfo.evmask;
	return;
}

redmine authored
1276
int sync_dosync(const char *fpath, uint32_t evmask, ctx_t *ctx_p, indexes_t *indexes_p);
redmine authored
1277
int sync_initialsync_walk(ctx_t *ctx_p, const char *dirpath, indexes_t *indexes_p, queue_id_t queue_id, initsync_t initsync) {
redmine authored
1278
	int ret = 0;
redmine authored
1279 1280 1281
	const char *rootpaths[] = {dirpath, NULL};
	eventinfo_t evinfo;
	FTS *tree;
redmine authored
1282
	rule_t *rules_p = ctx_p->rules;
redmine authored
1283
	debug(2, "(ctx_p, \"%s\", indexes_p, %i, %i).", dirpath, queue_id, initsync);
redmine authored
1284

redmine authored
1285
	char skip_rules = (initsync==INITSYNC_FULL) && ctx_p->flags[INITFULL];
redmine authored
1286

redmine authored
1287 1288
	char rsync_and_prefer_excludes =
			(
redmine authored
1289 1290 1291
				(ctx_p->flags[MODE]==MODE_RSYNCDIRECT) ||
				(ctx_p->flags[MODE]==MODE_RSYNCSHELL)  ||
				(ctx_p->flags[MODE]==MODE_RSYNCSO)
redmine authored
1292
			) && 
redmine authored
1293
			!ctx_p->flags[RSYNCPREFERINCLUDE];
redmine authored
1294

redmine authored
1295
	if ((!ctx_p->flags[RSYNCPREFERINCLUDE]) && skip_rules)
redmine authored
1296 1297
		return 0;

redmine authored
1298 1299
	skip_rules |= (ctx_p->rules_count == 0);

redmine authored
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312
	char fts_no_stat =
		(
			(
				initsync == INITSYNC_FULL
			) ||
			(
				ctx_p->_queues[QUEUE_NORMAL ].collectdelay ==
				ctx_p->_queues[QUEUE_BIGFILE].collectdelay
			) ||
			(
				ctx_p->bfilethreshold == 0
			)
		) && !(ctx_p->flags[EXCLUDEMOUNTPOINTS]);
redmine authored
1313

redmine authored
1314
	int fts_opts =  FTS_NOCHDIR | FTS_PHYSICAL | 
redmine authored
1315
			(fts_no_stat			? FTS_NOSTAT	: 0) | 
redmine authored
1316
			(ctx_p->flags[ONEFILESYSTEM] 	? FTS_XDEV	: 0); 
redmine authored
1317

redmine authored
1318
        debug(3, "fts_opts == %p", (void *)(long)fts_opts);
redmine authored
1319

redmine authored
1320
	tree = privileged_fts_open((char *const *)&rootpaths, fts_opts, NULL, PC_SYNC_INIIALSYNC_WALK_FTS_OPEN);
redmine authored
1321

redmine authored
1322 1323
	if (tree == NULL) {
		error("Cannot privileged_fts_open() on \"%s\".", dirpath);
redmine authored
1324 1325 1326 1327 1328 1329
		return errno;
	}

	memset(&evinfo, 0, sizeof(evinfo));

	FTSENT *node;
redmine authored
1330 1331 1332
	char  *path_rel		= NULL;
	size_t path_rel_len	= 0;

redmine authored
1333 1334 1335
#ifdef VERYPARANOID
	errno = 0;
#endif
redmine authored
1336
	while ((node = privileged_fts_read(tree, PC_SYNC_INIIALSYNC_WALK_FTS_READ))) {
redmine authored
1337
		switch (node->fts_info) {
redmine authored
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347
			// Duplicates:
			case FTS_DP:
				continue;
			// To sync:
			case FTS_DEFAULT:
			case FTS_SL:
			case FTS_SLNONE:
			case FTS_F:
			case FTS_D:
			case FTS_DOT:
redmine authored
1348 1349
                        case FTS_DC:    // TODO: think about case of FTS_DC
                        case FTS_NSOK:
redmine authored
1350 1351 1352 1353
				break;
			// Error cases:
			case FTS_ERR:
			case FTS_NS:
redmine authored
1354 1355 1356 1357 1358
			case FTS_DNR: {
				int fts_errno = node->fts_errno;

				if (fts_errno == ENOENT) {
					debug(1, "Got error while privileged_fts_read(): %s (errno: %i; fts_info: %i).", strerror(fts_errno), fts_errno, node->fts_info);
redmine authored
1359 1360
					continue;
				} else {
redmine authored
1361
					error("Got error while privileged_fts_read(): %s (errno: %i; fts_info: %i).", strerror(fts_errno), fts_errno, node->fts_info);
redmine authored
1362
					ret = node->fts_errno;
redmine authored
1363
					goto l_sync_initialsync_walk_end;
redmine authored
1364
				}
redmine authored
1365
			}
redmine authored
1366 1367
			default:

redmine authored
1368
				error("Got unknown fts_info vlaue while privileged_fts_read(): %i.", node->fts_info);
redmine authored
1369 1370
				ret = EINVAL;
				goto l_sync_initialsync_walk_end;
redmine authored
1371
		}
redmine authored
1372
		path_rel = sync_path_abs2rel(ctx_p, node->fts_path, -1, &path_rel_len, path_rel);
redmine authored
1373

redmine authored
1374
		debug(3, "Pointing to \"%s\" (node->fts_info == %i)", path_rel, node->fts_info);
redmine authored
1375

redmine authored
1376 1377 1378
		if (ctx_p->flags[EXCLUDEMOUNTPOINTS] && node->fts_info==FTS_D) {
			if (rsync_and_prefer_excludes) {
				if (node->fts_statp->st_dev != ctx_p->st_dev) {
redmine authored
1379
					debug(3, "Excluding \"%s\" due to location on other device: node->fts_statp->st_dev [0x%o] != ctx_p->st_dev [0x%o]", path_rel, node->fts_statp->st_dev, ctx_p->st_dev);
redmine authored
1380
					if (queue_id == QUEUE_AUTO) {
redmine authored
1381
						int i=0;
redmine authored
1382
						while (i<QUEUE_MAX)
redmine authored
1383 1384 1385
							indexes_addexclude(indexes_p, strdup(path_rel), EVIF_CONTENTRECURSIVELY, i++);
					} else
						indexes_addexclude(indexes_p, strdup(path_rel), EVIF_CONTENTRECURSIVELY, queue_id);
redmine authored
1386 1387

					fts_set(tree, node, FTS_SKIP);
redmine authored
1388
				}
redmine authored
1389 1390
			} else
			if (!ctx_p->flags[RSYNCPREFERINCLUDE])
redmine authored
1391
				error("Excluding mount points is not implentemted for non \"rsync*\" modes.");
redmine authored
1392 1393 1394 1395
		}

		mode_t st_mode = fts_no_stat ? (node->fts_info==FTS_D ? S_IFDIR : S_IFREG) : node->fts_statp->st_mode;

redmine authored
1396
		if (!skip_rules<