1 /*
2     clsync - file tree sync utility based on inotify
3 
4     Copyright (C) 2013  Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
5 
6     This program is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 
21 #include "common.h"
22 
23 #include "port-hacks.h"
24 
25 #include <pwd.h>	// For getpwnam()
26 #include <grp.h>	// For getgrnam()
27 
28 #include "error.h"
29 #include "sync.h"
30 #include "malloc.h"
31 #include "cluster.h"
32 #include "fileutils.h"
33 #include "socket.h"
34 
35 //#include "revision.h"
36 
37 static const struct option long_options[] =
38 {
39 	{"watch-dir",		required_argument,	NULL,	WATCHDIR},
40 	{"sync-handler",	required_argument,	NULL,	SYNCHANDLER},
41 	{"rules-file",		required_argument,	NULL,	RULESFILE},
42 	{"destination-dir",	required_argument,	NULL,	DESTDIR},
43 	{"mode",		required_argument,	NULL,	MODE},
44 	{"socket",		required_argument,	NULL,	SOCKETPATH},
45 	{"socket-auth",		required_argument,	NULL,	SOCKETAUTH},
46 	{"socket-mod",		required_argument,	NULL,	SOCKETMOD},
47 	{"socket-own",		required_argument,	NULL,	SOCKETOWN},
48 	{"status-file",		required_argument,	NULL,	STATUSFILE},
49 
50 	{"background",		optional_argument,	NULL,	BACKGROUND},
51 	{"config-file",		required_argument,	NULL,	CONFIGFILE},
52 	{"config-block",	required_argument,	NULL,	CONFIGBLOCK},
53 	{"config-block-inherits",required_argument,	NULL,	CONFIGBLOCKINHERITS},
54 	{"pid-file",		required_argument,	NULL,	PIDFILE},
55 	{"uid",			required_argument,	NULL,	UID},
56 	{"gid",			required_argument,	NULL,	GID},
57 #ifdef HAVE_CAPABILITIES
58 	{"preserve-file-access",optional_argument,	NULL,	CAP_PRESERVE_FILEACCESS},
59 #endif
60 	{"threading",		required_argument,	NULL,	THREADING},
61 	{"retries",		optional_argument,	NULL,	RETRIES},
62 	{"ignore-failures",	optional_argument,	NULL,	IGNOREFAILURES},
63 	{"output",		required_argument,	NULL,	OUTPUT_METHOD},
64 	{"one-file-system",	optional_argument,	NULL,	ONEFILESYSTEM},
65 	{"exclude-mount-points",optional_argument,	NULL,	EXCLUDEMOUNTPOINTS},
66 #ifdef CLUSTER_SUPPORT
67 	{"cluster-iface",	required_argument,	NULL,	CLUSTERIFACE},		// Not implemented, yet
68 	{"cluster-ip",		required_argument,	NULL,	CLUSTERMCASTIPADDR},	// Not implemented, yet
69 	{"cluster-port",	required_argument,	NULL,	CLUSTERMCASTIPPORT},	// Not implemented, yet
70 	{"cluster-timeout",	required_argument,	NULL,	CLUSTERTIMEOUT},	// Not implemented, yet
71 	{"cluster-node-name",	required_argument,	NULL,	CLUSTERNODENAME},	// Not implemented, yet
72 	{"cluster-hash-dl-min",	required_argument,	NULL,	CLUSTERHDLMIN},
73 	{"cluster-hash-dl-max",	required_argument,	NULL,	CLUSTERHDLMAX},
74 	{"cluster-scan-dl-max",	required_argument,	NULL,	CLUSTERSDLMAX},
75 #endif
76 	{"max-iterations",	required_argument,	NULL,	MAXITERATIONS},
77 	{"standby-file",	required_argument,	NULL,	STANDBYFILE},
78 	{"timeout-sync",	required_argument,	NULL,	SYNCTIMEOUT},
79 	{"delay-sync",		required_argument,	NULL,	SYNCDELAY},
80 	{"delay-collect",	required_argument,	NULL,	DELAY},
81 	{"delay-collect-bigfile",required_argument,	NULL,	BFILEDELAY},
82 	{"threshold-bigfile",	required_argument,	NULL,	BFILETHRESHOLD},
83 	{"lists-dir",		required_argument,	NULL,	OUTLISTSDIR},
84 	{"have-recursive-sync",	optional_argument,	NULL,	HAVERECURSIVESYNC},
85 	{"synclist-simplify",	optional_argument,	NULL,	SYNCLISTSIMPLIFY},
86 	{"auto-add-rules-w",	optional_argument,	NULL,	AUTORULESW},
87 	{"rsync-inclimit",	required_argument,	NULL,	RSYNCINCLIMIT},
88 	{"rsync-prefer-include",optional_argument,	NULL,	RSYNCPREFERINCLUDE},
89 	{"ignore-exitcode",	required_argument,	NULL,	IGNOREEXITCODE},
90 	{"dont-unlink-lists",	optional_argument,	NULL,	DONTUNLINK},
91 	{"full-initialsync",	optional_argument,	NULL,	INITFULL},
92 	{"only-initialsync",	optional_argument,	NULL,	ONLYINITSYNC},
93 	{"skip-initialsync",	optional_argument,	NULL,	SKIPINITSYNC},
94 	{"exit-on-no-events",	optional_argument,	NULL,	EXITONNOEVENTS},
95 	{"exit-hook",		required_argument,	NULL,	EXITHOOK},
96 	{"verbose",		optional_argument,	NULL,	VERBOSE},
97 	{"debug",		optional_argument,	NULL,	DEBUG},
98 	{"dump-dir",		required_argument,	NULL,	DUMPDIR},
99 	{"quiet",		optional_argument,	NULL,	QUIET},
100 	{"monitor",		required_argument,	NULL,	MONITOR},
101 	{"label",		required_argument,	NULL,	LABEL},
102 	{"help",		optional_argument,	NULL,	HELP},
103 	{"version",		optional_argument,	NULL,	SHOW_VERSION},
104 
105 	{NULL,			0,			NULL,	0}
106 };
107 
108 static char *const socketauth[] = {
109 	[SOCKAUTH_UNSET]	= "",
110 	[SOCKAUTH_NULL]		= "null",
111 //	[SOCKAUTH_PAM]		= "pam",
112 	NULL
113 };
114 
115 static char *const threading_modes[] = {
116 	[PM_OFF]		= "off",
117 	[PM_SAFE]		= "safe",
118 	[PM_FULL]		= "full",
119 	NULL
120 };
121 
122 static char *const notify_engines[] = {
123 	[NE_UNDEFINED]		= "",
124 	[NE_INOTIFY]		= "inotify",
125 	[NE_KQUEUE]		= "kqueue",
126 	[NE_FANOTIFY]		= "fanotify",
127 	[NE_BSM]		= "bsm",
128 	[NE_DTRACEPIPE]		= "dtracepipe",
129 	NULL
130 };
131 
132 static char *const output_methods[] = {
133 	[OM_STDERR]		= "stderr",
134 	[OM_STDOUT]		= "stdout",
135 	[OM_SYSLOG]		= "syslog",
136 	NULL
137 };
138 
139 static char *const modes[] = {
140 	[MODE_UNSET]		= "",
141 	[MODE_SIMPLE]		= "simple",
142 	[MODE_SHELL]		= "shell",
143 	[MODE_RSYNCSHELL]	= "rsyncshell",
144 	[MODE_RSYNCDIRECT]	= "rsyncdirect",
145 	[MODE_RSYNCSO]		= "rsyncso",
146 	[MODE_SO]		= "so",
147 	NULL
148 };
149 
150 static char *const status_descr[] = {
151 	[STATE_EXIT]		= "exiting",
152 	[STATE_STARTING]	= "starting",
153 	[STATE_RUNNING]		= "running",
154 	[STATE_REHASH]		= "rehashing",
155 	[STATE_TERM]		= "terminating",
156 	[STATE_THREAD_GC]	= "thread gc",
157 	[STATE_INITSYNC]	= "initsync",
158 	NULL
159 };
160 
syntax()161 int syntax() {
162 	info("possible options:");
163 	int i=0;
164 	while(long_options[i].name != NULL) {
165 		if(!(long_options[i].val & OPTION_CONFIGONLY))
166 			info("\t--%-24s%c%c%s", long_options[i].name,
167 				 long_options[i].val & OPTION_LONGOPTONLY ? ' ' : '-',
168 				 long_options[i].val & OPTION_LONGOPTONLY ? ' ' : long_options[i].val,
169 				(long_options[i].has_arg == required_argument ? " argument" : ""));
170 		i++;
171 	}
172 	exit(EINVAL);
173 }
174 
version()175 int version() {
176 	info(PROGRAM" v%i.%i"REVISION"\n\t"AUTHOR"", VERSION_MAJ, VERSION_MIN);
177 	exit(0);
178 }
179 
clsyncapi_getapiversion()180 int clsyncapi_getapiversion() {
181 	return CLSYNC_API_VERSION;
182 }
183 
184 /**
185  * @brief 			Gets raw (string) an option value by an option name
186  *
187  * @param[in]	ctx_p		Context
188  @ @param[in]	variable_name	The name of the option
189  *
190  * @retval	char *		Pointer to newly allocated string, if successful
191  * @retval	NULL		On error
192  *
193  */
parameter_get(ctx_t * ctx_p,char * variable_name)194 char *parameter_get(ctx_t *ctx_p, char *variable_name) {
195 	const struct option *long_option_p = long_options;
196 	int param_id = -1;
197 
198 	while (long_option_p->name != NULL) {
199 		if (!strcmp(long_option_p->name, variable_name)) {
200 			param_id = long_option_p->val;
201 			break;
202 		}
203 
204 		long_option_p++;
205 	}
206 
207 	if (param_id == -1) {
208 		errno = ENOENT;
209 		return NULL;
210 	}
211 
212 	return ctx_p->flags_values_raw[param_id];
213 }
214 
215 /**
216  * @brief 			Expands option values, e. g. "/var/log/clsync-%label%.pid" -> "/var/log/clsync-clone.pid"
217  *
218  * @param[in]	ctx_p		Context
219  @ @param[in]	arg		Allocated string with unexpanded value. Will be free'd
220  *
221  * @retval	char *		Pointer to newly allocated string, if successful
222  * @retval	NULL		On error
223  *
224  */
parameter_expand(ctx_t * ctx_p,char * arg,int ignorewarnings)225 char *parameter_expand(ctx_t *ctx_p, char *arg, int ignorewarnings) {
226 	char *ret = NULL;
227 	size_t ret_size = 0, ret_len = 0;
228 
229 #ifdef PARANOID
230 	if (arg == NULL) {
231 		errno = EINVAL;
232 		return NULL;
233 	}
234 #endif
235 
236 	char *ptr = &arg[-1];
237 	while (1) {
238 		ptr++;
239 
240 		switch (*ptr) {
241 			case 0:
242 				ret[ret_len] = 0;
243 				debug(3, "Expanding value \"%s\" to \"%s\"", arg, ret);
244 				free(arg);
245 				return ret;
246 			case '%': {
247 				if (ptr[1] == '%') {
248 					ret[ret_len++] = *(ptr++);
249 					break;
250 				}
251 
252 				char nest_searching = 1;
253 				char *ptr_nest = ptr;
254 				while (nest_searching) {
255 					ptr_nest++;
256 
257 					switch (*ptr_nest) {
258 						case 0:
259 							ret[ret_len] = 0;
260 							if (!(ignorewarnings&1))
261 								warning("Unexpected end of macro-substitution \"%%%s\" in value \"%s\"; result value is \"%s\"", ptr_nest, arg, ret);
262 							free(arg);
263 							return ret;
264 						case '%': {
265 							nest_searching = 0;
266 							*ptr_nest = 0;
267 							char *variable_name  = &ptr[1];
268 							char *variable_value = parameter_get(ctx_p, variable_name);
269 							if (variable_value == NULL) {
270 								if (!(ignorewarnings&2))
271 									warning("Variable \"%s\" is not set (%s)", variable_name, strerror(errno));
272 								*ptr_nest = '%';
273 								errno = 0;
274 								break;
275 							}
276 							*ptr_nest = '%';
277 							size_t variable_value_len = strlen(variable_value);
278 							if (ret_len+variable_value_len+1 >= ret_size) {
279 								ret_size = ret_len+variable_value_len+1 + ALLOC_PORTION;
280 								ret      = xrealloc(ret, ret_size);
281 							}
282 							memcpy(&ret[ret_len], variable_value, variable_value_len);
283 							ret_len += variable_value_len;
284 							break;
285 						}
286 					}
287 				}
288 				ptr = ptr_nest;
289 				break;
290 			}
291 			default: {
292 				if (ret_len+2 >= ret_size) {
293 					ret_size = ret_len+2 + ALLOC_PORTION;
294 					ret      = xrealloc(ret, ret_size);
295 				}
296 				ret[ret_len++] = *ptr;
297 				break;
298 			}
299 		}
300 	}
301 	error("Unknown internal error");
302 	return arg;
303 }
304 
parse_parameter(ctx_t * ctx_p,uint16_t param_id,char * arg,paramsource_t paramsource)305 int parse_parameter(ctx_t *ctx_p, uint16_t param_id, char *arg, paramsource_t paramsource) {
306 #ifdef _DEBUG
307 	fprintf(stderr, "Force-Debug: parse_parameter(): %i: %i = \"%s\"\n", paramsource, param_id, arg);
308 #endif
309 	switch(paramsource) {
310 		case PS_ARGUMENT:
311 			if(param_id & OPTION_CONFIGONLY) {
312 				syntax();
313 				return 0;
314 			}
315 			ctx_p->flags_set[param_id] = 1;
316 			break;
317 		case PS_CONFIG:
318 			if(ctx_p->flags_set[param_id])
319 				return 0;
320 			ctx_p->flags_set[param_id] = 1;
321 			break;
322 		default:
323 			error("Warning: Unknown parameter #%i source (value \"%s\").", param_id, arg!=NULL ? arg : "");
324 			break;
325 	}
326 
327 	if (arg != NULL) {
328 		arg = parameter_expand(ctx_p, arg, 0);
329 
330 		if (ctx_p->flags_values_raw[param_id] != NULL)
331 			free(ctx_p->flags_values_raw[param_id]);
332 		ctx_p->flags_values_raw[param_id] = arg;
333 	}
334 
335 	switch(param_id) {
336 		case '?':
337 		case HELP:
338 			syntax();
339 			break;
340 		case CONFIGFILE:
341 			ctx_p->config_path    = *arg ? arg : NULL;
342 			break;
343 		case CONFIGBLOCK:
344 			ctx_p->config_block   = *arg ? arg : NULL;
345 			break;
346 		case CONFIGBLOCKINHERITS:
347 			break;
348 		case GID:
349 			ctx_p->gid = (unsigned int)atol(arg);
350 			ctx_p->flags[param_id]++;
351 			break;
352 		case UID:
353 			ctx_p->uid = (unsigned int)atol(arg);
354 			ctx_p->flags[param_id]++;
355 			break;
356 		case PIDFILE:
357 			ctx_p->pidfile		= arg;
358 			break;
359 		case RETRIES:
360 			ctx_p->retries		= (unsigned int)atol(arg);
361 			break;
362 		case THREADING: {
363 			char *value, *arg_orig = arg;
364 
365 			if (!*arg) {
366 				ctx_p->flags_set[param_id] = 0;
367 				return 0;
368 			}
369 
370 			threadingmode_t threadingmode = getsubopt(&arg, threading_modes, &value);
371 			if((int)threadingmode == -1) {
372 				errno = EINVAL;
373 				error("Invalid threading mode entered: \"%s\"", arg_orig);
374 				return EINVAL;
375 			}
376 			ctx_p->flags[THREADING] = threadingmode;
377 
378 			break;
379 		}
380 		case OUTPUT_METHOD: {
381 			char *value, *arg_orig = arg;
382 
383 			if (!*arg) {
384 				ctx_p->flags_set[param_id] = 0;
385 				return 0;
386 			}
387 
388 			outputmethod_t outputmethod = getsubopt(&arg, output_methods, &value);
389 			if((int)outputmethod == -1) {
390 				errno = EINVAL;
391 				error("Invalid log writing destination entered: \"%s\"", arg_orig);
392 				return EINVAL;
393 			}
394 			ctx_p->flags[OUTPUT_METHOD] = outputmethod;
395 
396 			break;
397 		}
398 #ifdef CLUSTER_SUPPORT
399 		case CLUSTERIFACE:
400 			ctx_p->cluster_iface	= arg;
401 			break;
402 		case CLUSTERMCASTIPADDR:
403 			ctx_p->cluster_mcastipaddr	= arg;
404 			break;
405 		case CLUSTERMCASTIPPORT:
406 			ctx_p->cluster_mcastipport	= (uint16_t)atoi(arg);
407 			break;
408 		case CLUSTERTIMEOUT:
409 			ctx_p->cluster_timeout	= (unsigned int)atol(arg);
410 			break;
411 		case CLUSTERNODENAME:
412 			ctx_p->cluster_nodename	= arg;
413 			break;
414 		case CLUSTERHDLMIN:
415 			ctx_p->cluster_hash_dl_min	= (uint16_t)atoi(arg);
416 			break;
417 		case CLUSTERHDLMAX:
418 			ctx_p->cluster_hash_dl_max	= (uint16_t)atoi(arg);
419 			break;
420 		case CLUSTERSDLMAX:
421 			ctx_p->cluster_scan_dl_max	= (uint16_t)atoi(arg);
422 			break;
423 #endif
424 		case OUTLISTSDIR:
425 			ctx_p->listoutdir		= arg;
426 			break;
427 		case LABEL:
428 			ctx_p->label		= arg;
429 			break;
430 		case STANDBYFILE:
431 			if(strlen(arg)) {
432 				ctx_p->standbyfile		= arg;
433 				ctx_p->flags[STANDBYFILE]	= 1;
434 			} else {
435 				ctx_p->standbyfile		= NULL;
436 				ctx_p->flags[STANDBYFILE]	= 0;
437 			}
438 			break;
439 		case SYNCDELAY:
440 			ctx_p->syncdelay		= (unsigned int)atol(arg);
441 			break;
442 		case DELAY:
443 			ctx_p->_queues[QUEUE_NORMAL].collectdelay = (unsigned int)atol(arg);
444 			break;
445 		case BFILEDELAY:
446 			ctx_p->_queues[QUEUE_BIGFILE].collectdelay = (unsigned int)atol(arg);
447 			break;
448 		case BFILETHRESHOLD:
449 			ctx_p->bfilethreshold = (unsigned long)atol(arg);
450 			break;
451 		case MONITOR: {
452 			char *value, *arg_orig = arg;
453 
454 			if (!*arg) {
455 				ctx_p->flags_set[param_id] = 0;
456 				return 0;
457 			}
458 
459 			notifyengine_t notifyengine = getsubopt(&arg, notify_engines, &value);
460 			if((int)notifyengine == -1) {
461 				errno = EINVAL;
462 				error("Invalid FS monitor subsystem entered: \"%s\"", arg_orig);
463 				return EINVAL;
464 			}
465 
466 			switch (notifyengine) {
467 #ifdef FANOTIFY_SUPPORT
468 				case NE_FANOTIFY:
469 #endif
470 #ifdef INOTIFY_SUPPORT
471 				case NE_INOTIFY:
472 #endif
473 #ifdef KQUEUE_SUPPORT
474 				case NE_KQUEUE:
475 #endif
476 #ifdef BSM_SUPPORT
477 				case NE_BSM:
478 #endif
479 #ifdef DTRACEPIPE_SUPPORT
480 				case NE_DTRACEPIPE:
481 #endif
482 					break;
483 				default:
484 					error(PROGRAM" is compiled without %s subsystem support. Recompile with option \"--with-%s\" if you're planning to use it.", arg_orig, arg_orig);
485 					return EINVAL;
486 			}
487 
488 			ctx_p->flags[MONITOR] = notifyengine;
489 
490 			break;
491 		}
492 		case RSYNCINCLIMIT:
493 			ctx_p->rsyncinclimit = (unsigned int)atol(arg);
494 			break;
495 		case SYNCTIMEOUT:
496 			ctx_p->synctimeout   = (unsigned int)atol(arg);
497 			break;
498 		case EXITHOOK:
499 			if(strlen(arg)) {
500 				ctx_p->exithookfile		= arg;
501 				ctx_p->flags[EXITHOOK]	= 1;
502 			} else {
503 				ctx_p->exithookfile		= NULL;
504 				ctx_p->flags[EXITHOOK]	= 0;
505 			}
506 			break;
507 		case IGNOREEXITCODE: {
508 			char *ptr = arg, *start = arg;
509 			unsigned char exitcode;
510 			do {
511 				switch(*ptr) {
512 					case 0:
513 					case ',':
514 //						*ptr=0;
515 						exitcode = (unsigned char)atoi(start);
516 						if(exitcode == 0) {
517 							// flushing the setting
518 							int i = 0;
519 							while(i < 256)
520 								ctx_p->isignoredexitcode[i++] = 0;
521 #ifdef _DEBUG
522 							fprintf(stderr, "Force-Debug: parse_parameter(): Reset ignored exitcodes.\n");
523 #endif
524 						} else {
525 							ctx_p->isignoredexitcode[exitcode] = 1;
526 #ifdef _DEBUG
527 							fprintf(stderr, "Force-Debug: parse_parameter(): Adding ignored exitcode %u.\n", exitcode);
528 #endif
529 						}
530 						start = ptr+1;
531 						break;
532 				}
533 			} while(*(ptr++));
534 			break;
535 		}
536 		case SHOW_VERSION:
537 			version();
538 			break;
539 		case WATCHDIR:
540 			ctx_p->watchdir		= arg;
541 			break;
542 		case SYNCHANDLER:
543 			ctx_p->handlerfpath	= arg;
544 			break;
545 		case RULESFILE:
546 			ctx_p->rulfpath		= arg;
547 			break;
548 		case DESTDIR: {
549 			char *sep = strstr(arg, "://");
550 
551 			if (ctx_p->destproto != NULL) {
552 				free(ctx_p->destproto);
553 				ctx_p->destproto = NULL;
554 			}
555 
556 			if (sep != NULL) {
557 				char *ptr = arg;
558 				while (ptr < sep) {
559 					if (*ptr<'a' || *ptr>'z')
560 						break;
561 					ptr++;
562 				}
563 				if (ptr == sep) {
564 					size_t len = (ptr-arg)+1;
565 					ctx_p->destproto = xmalloc(len+1);
566 					memcpy(ctx_p->destproto, arg, len);
567 					ctx_p->destproto[len] = 0;
568 				}
569 			}
570 
571 			ctx_p->destdir	 = arg;
572 			break;
573 		}
574 		case SOCKETPATH:
575 			ctx_p->socketpath	= arg;
576 			break;
577 		case SOCKETAUTH: {
578 			char *value;
579 
580 			ctx_p->flags[SOCKETAUTH] = getsubopt(&arg, socketauth, &value);
581 			if(ctx_p->flags[SOCKETAUTH] == -1) {
582 				error("Wrong socket auth mech entered: \"%s\"", arg);
583 				return EINVAL;
584 			}
585 		}
586 		case SOCKETMOD:
587 			if(!sscanf(arg, "%o", (unsigned int *)&ctx_p->socketmod)) {
588 				error("Non octal value passed to --socket-mod: \"%s\"", arg);
589 				return EINVAL;
590 			}
591 			ctx_p->flags[param_id]++;
592 			break;
593 		case SOCKETOWN: {
594 			char *colon = strchr(arg, ':');
595 			uid_t uid;
596 			gid_t gid;
597 
598 			if(colon == NULL) {
599 				struct passwd *pwent = getpwnam(arg);
600 
601 				if(pwent == NULL) {
602 					error("Cannot find username \"%s\" (case #0)",
603 						arg);
604 					return EINVAL;
605 				}
606 
607 				uid = pwent->pw_uid;
608 				gid = pwent->pw_gid;
609 
610 			} else {
611 
612 				char user[USER_LEN+2], group[GROUP_LEN+2];
613 
614 				memcpy(user, arg, MIN(USER_LEN, colon-arg));
615 				user[colon-arg] = 0;
616 
617 				strncpy(group, &colon[1], GROUP_LEN);
618 
619 				errno=0;
620 				struct passwd *pwent = getpwnam(user);
621 				if(pwent == NULL) {
622 					error("Cannot find username \"%s\" (case #1)",
623 						user);
624 					return EINVAL;
625 				}
626 
627 				errno=0;
628 				struct group  *grent = getgrnam(group);
629 				if(grent == NULL) {
630 					error("Cannot find group \"%s\"",
631 						group);
632 					return EINVAL;
633 				}
634 
635 				uid = pwent->pw_uid;
636 				gid = grent->gr_gid;
637 			}
638 
639 			ctx_p->socketuid = uid;
640 			ctx_p->socketgid = gid;
641 			ctx_p->flags[param_id]++;
642 
643 			debug(2, "socket: uid == %u; gid == %u", uid, gid);
644 
645 			break;
646 		}
647 		case STATUSFILE:
648 			ctx_p->statusfile	= arg;
649 			break;
650 		case DUMPDIR:
651 			ctx_p->dump_path	= arg;
652 			break;
653 		case MODE: {
654 			char *value;
655 
656 			ctx_p->flags[MODE]  = getsubopt(&arg, modes, &value);
657 			if(ctx_p->flags[MODE] == -1) {
658 				error("Wrong mode name entered: \"%s\"", arg);
659 				return EINVAL;
660 			}
661 			break;
662 		}
663 		default:
664 			if(arg == NULL)
665 				ctx_p->flags[param_id]++;
666 			else
667 				ctx_p->flags[param_id] = atoi(arg);
668 #ifdef _DEBUG
669 			fprintf(stderr, "Force-Debug: flag %i is set to %i\n", param_id&0xff, ctx_p->flags[param_id]);
670 #endif
671 			break;
672 	}
673 	return 0;
674 }
675 
arguments_parse(int argc,char * argv[],struct ctx * ctx_p)676 int arguments_parse(int argc, char *argv[], struct ctx *ctx_p) {
677 	int c;
678 	int option_index = 0;
679 
680 	// Generating "optstring" (man 3 getopt_long) with using information from struct array "long_options"
681 	char *optstring     = alloca((('z'-'a'+1)*3 + '9'-'0'+1)*3 + 1);
682 	char *optstring_ptr = optstring;
683 
684 	const struct option *lo_ptr = long_options;
685 	while(lo_ptr->name != NULL) {
686 		if(!(lo_ptr->val & (OPTION_CONFIGONLY|OPTION_LONGOPTONLY))) {
687 			*(optstring_ptr++) = lo_ptr->val & 0xff;
688 
689 			if(lo_ptr->has_arg == required_argument)
690 				*(optstring_ptr++) = ':';
691 
692 			if(lo_ptr->has_arg == optional_argument) {
693 				*(optstring_ptr++) = ':';
694 				*(optstring_ptr++) = ':';
695 			}
696 		}
697 		lo_ptr++;
698 	}
699 	*optstring_ptr = 0;
700 #ifdef _DEBUG
701 	fprintf(stderr, "Force-Debug: %s\n", optstring);
702 #endif
703 
704 	// Parsing arguments
705 	while(1) {
706 		c = getopt_long(argc, argv, optstring, long_options, &option_index);
707 
708 		if (c == -1) break;
709 		int ret = parse_parameter(ctx_p, c, optarg == NULL ? NULL : strdup(optarg), PS_ARGUMENT);
710 		if(ret) return ret;
711 	}
712 	if(optind+1 < argc)
713 		syntax();
714 
715 	return 0;
716 }
717 
gkf_parse(ctx_t * ctx_p,GKeyFile * gkf)718 void gkf_parse(ctx_t *ctx_p, GKeyFile *gkf) {
719 	char *config_block = ctx_p->config_block;
720 	do {
721 		const struct option *lo_ptr = long_options;
722 
723 		if (config_block != ctx_p->config_block) {
724 			ctx_p->flags_values_raw[CONFIGBLOCKINHERITS] = NULL;
725 			ctx_p->flags_set[CONFIGBLOCKINHERITS] = 0;
726 		}
727 		while(lo_ptr->name != NULL) {
728 			gchar *value = g_key_file_get_value(gkf, config_block, lo_ptr->name, NULL);
729 			if(value != NULL) {
730 				int ret = parse_parameter(ctx_p, lo_ptr->val, value, PS_CONFIG);
731 				if(ret) exit(ret);
732 			}
733 			lo_ptr++;
734 		}
735 
736 		if (config_block != ctx_p->config_block)
737 			free(config_block);
738 
739 		config_block = ctx_p->flags_values_raw[CONFIGBLOCKINHERITS];
740 
741 		if (config_block != NULL)
742 			debug(2, "Next block is: %s", config_block);
743 	} while (config_block != NULL);
744 
745 	return;
746 }
747 
configs_parse(ctx_t * ctx_p)748 int configs_parse(ctx_t *ctx_p) {
749 	GKeyFile *gkf;
750 
751 	gkf = g_key_file_new();
752 
753 	if (ctx_p->config_path) {
754 		GError *g_error = NULL;
755 
756 		if (!strcmp(ctx_p->config_path, "/NULL/")) {
757 			debug(2, "Empty path to config file. Don't read any of config files.");
758 			return 0;
759 		}
760 
761 		debug(1, "Trying config-file \"%s\"", ctx_p->config_path);
762 		if (!g_key_file_load_from_file(gkf, ctx_p->config_path, G_KEY_FILE_NONE, &g_error)) {
763 			error("Cannot open/parse file \"%s\" (g_error #%u.%u: %s)", ctx_p->config_path, g_error->domain, g_error->code, g_error->message);
764 			g_key_file_free(gkf);
765 			return -1;
766 		} else
767 			gkf_parse(ctx_p, gkf);
768 
769 	} else {
770 		char *config_paths[] = CONFIG_PATHS;
771 		char **config_path_p = config_paths, *config_path_real = xmalloc(PATH_MAX);
772 		size_t config_path_real_size=PATH_MAX;
773 
774 		char *homedir = getenv("HOME");
775 		size_t homedir_len = strlen(homedir);
776 
777 		while(*config_path_p != NULL) {
778 			size_t config_path_len = strlen(*config_path_p);
779 
780 			if(config_path_len+homedir_len+3 > config_path_real_size) {
781 				config_path_real_size = config_path_len+homedir_len+3;
782 				config_path_real      = xmalloc(config_path_real_size);
783 			}
784 
785 			if(*config_path_p[0] != '/') {
786 				memcpy(config_path_real, homedir, homedir_len);
787 				config_path_real[homedir_len] = '/';
788 				memcpy(&config_path_real[homedir_len+1], *config_path_p, config_path_len+1);
789 			} else
790 				memcpy(config_path_real, *config_path_p, config_path_len+1);
791 
792 			debug(1, "Trying config-file \"%s\"", config_path_real);
793 			if(!g_key_file_load_from_file(gkf, config_path_real, G_KEY_FILE_NONE, NULL)) {
794 				debug(1, "Cannot open/parse file \"%s\"", config_path_real);
795 				config_path_p++;
796 				continue;
797 			}
798 
799 			gkf_parse(ctx_p, gkf);
800 
801 			break;
802 		}
803 		free(config_path_real);
804 	}
805 
806 	g_key_file_free(gkf);
807 
808 	return 0;
809 }
810 
options_cleanup(ctx_t * ctx_p)811 void options_cleanup(ctx_t *ctx_p) {
812 	int i=0;
813 
814 	while (i < OPTION_FLAGS) {
815 		if (ctx_p->flags_values_raw[i] != NULL) {
816 			free(ctx_p->flags_values_raw[i]);
817 			ctx_p->flags_values_raw[i] = NULL;
818 		}
819 		i++;
820 	}
821 
822 	return;
823 }
824 
rule_complete(rule_t * rule_p,const char * expr)825 int rule_complete(rule_t *rule_p, const char *expr) {
826 	debug(3, "<%s>.", expr);
827 #ifdef VERYPARANOID
828 	if(rule_p->mask == RA_NONE) {
829 		error("Received a rule with rule_p->mask == 0x00. Exit.");
830 		return EINVAL;
831 	}
832 #endif
833 
834 	char buf[BUFSIZ];
835 	int ret = 0;
836 	if(rule_p->num >= MAXRULES) {
837 		error("Too many rules (%i >= %i).", rule_p->num, MAXRULES);
838 		return ENOMEM;
839 	}
840 	if((ret = regcomp(&rule_p->expr, expr, REG_EXTENDED | REG_NOSUB))) {
841 		regerror(ret, &rule_p->expr, buf, BUFSIZ);
842 		error("Invalid regexp pattern <%s>: %s (regex-errno: %i).", expr, buf, ret);
843 		return ret;
844 	}
845 
846 	return ret;
847 }
848 
parse_rules_fromfile(ctx_t * ctx_p)849 int parse_rules_fromfile(ctx_t *ctx_p) {
850 	int ret = 0;
851 	char *rulfpath = ctx_p->rulfpath;
852 	rule_t *rules  = ctx_p->rules;
853 
854 	char *line_buf=NULL;
855 	FILE *f = fopen(rulfpath, "r");
856 
857 	if(f == NULL) {
858 		rules->mask   = RA_NONE;		// Terminator. End of rules' chain.
859 		rules->perm   = DEFAULT_RULES_PERM;
860 		error("Cannot open \"%s\" for reading.", rulfpath);
861 		return errno;
862 	}
863 
864 	GHashTable *autowrules_ht = g_hash_table_new_full(g_str_hash,	g_str_equal,	free,    0);
865 
866 	int i=0;
867 	size_t linelen, size=0;
868 	while((linelen = getline(&line_buf, &size, f)) != -1) {
869 		if(linelen>1) {
870 			uint8_t sign = 0;
871 			char *line = line_buf;
872 			rule_t *rule;
873 
874 			rule = &rules[i];
875 #ifdef VERYPARANOID
876 			memset(rule, 0, sizeof(*rule));
877 #endif
878 			rule->num = i++;
879 			line[--linelen] = 0;
880 
881 
882 			// Parsing the first character of the line
883 			switch(*line) {
884 				case '+':
885 					sign = RS_PERMIT;
886 					break;
887 				case '-':
888 					sign = RS_REJECT;
889 					break;
890 				case '#':	// Comment?
891 					i--;	// Canceling new rule
892 					continue;
893 				default:
894 					error("Wrong rule action <%c>.", *line);
895 					return EINVAL;
896 			}
897 
898 			line++;
899 			linelen--;
900 
901 			// Parsing the second character of the line
902 			*line |= 0x20;	// lower-casing
903 			// Default rule->mask and rule->perm
904 
905 			// rule->mask - sets bitmask of operations that are affected by the rule
906 			// rule->perm - sets bitmask of permit/reject for every operation. Effect have only bits specified by the rule->mask.
907 
908 			rule->mask = RA_ALL;
909 			switch(sign) {
910 				case RS_REJECT:
911 					rule->perm = RA_NONE;
912 					break;
913 				case RS_PERMIT:
914 					rule->perm = RA_ALL;
915 					break;
916 			}
917 
918 			switch(*line) {
919 				case '*':
920 					rule->objtype = 0;	// "0" - means "of any type"
921 					break;
922 #ifdef DETAILED_FTYPE
923 				case 's':
924 					rule->objtype = S_IFSOCK;
925 					break;
926 				case 'l':
927 					rule->objtype = S_IFLNK;
928 					break;
929 				case 'b':
930 					rule->objtype = S_IFBLK;
931 					break;
932 				case 'c':
933 					rule->objtype = S_IFCHR;
934 					break;
935 				case 'p':
936 					rule->objtype = S_IFIFO;
937 					break;
938 #endif
939 				case 'f':
940 					rule->objtype = S_IFREG;
941 					break;
942 				case 'd':
943 					rule->objtype = S_IFDIR;
944 					break;
945 				case 'w':	// accept or reject walking to directory
946 					if(
947 						(ctx_p->flags[MODE] == MODE_RSYNCDIRECT) ||
948 						(ctx_p->flags[MODE] == MODE_RSYNCSHELL)  ||
949 						(ctx_p->flags[MODE] == MODE_RSYNCSO)
950 					) {
951 						error("Warning: Used \"w\" rule in \"--rsync\" case."
952 							" This may cause unexpected problems.");
953 					}
954 					rule->objtype = S_IFDIR;
955 					rule->mask    = RA_WALK;
956 					break;
957 				default:
958 					error("Warning: Cannot parse the rule <%s>", &line[-1]);
959 					i--;	// Canceling new rule
960 					continue;
961 			}
962 
963 
964 			line++;
965 			linelen--;
966 
967 			// Parsing the rest part of the line
968 
969 			debug(1, "Rule #%i <%c>[0x%02x 0x%02x] <%c>[0x%04x] pattern <%s> (length: %i).", rule->num, line[-2], rule->perm, rule->mask, line[-1], rule->objtype, line, linelen);
970 			if((ret=rule_complete(rule, line)))
971 				goto l_parse_rules_fromfile_end;
972 
973 			// Post-processing:
974 
975 			line--;
976 			linelen++;
977 
978 			if(*line != 'w') {
979 				// processing --auto-add-rules-w
980 				if(ctx_p->flags[AUTORULESW] && (sign == RS_PERMIT)) {
981 					// Preparing to add appropriate w-rules
982 					char skip = 0;
983 					char *expr = alloca(linelen+2);
984 					memcpy(expr, line, linelen+1);
985 					size_t exprlen = linelen;
986 
987 					// Making expr to be starting with '^'
988 					if(line[1] == '^') {
989 						expr++;
990 						exprlen--;
991 					} else
992 						*expr = '^';
993 
994 					char *end;
995 
996 					if(*line == 'd' || *line == '*') {
997 						// "d" rule already doing what we need, so we can skip the last level
998 
999 						end = &expr[exprlen];
1000 						if(end[-1] != '$')
1001 							*(end++) = '$';
1002 						*end = 0;
1003 
1004 //						debug(3, "Don't adding w-rule for \"%s\" due to [*d]-rule for \"%s\"",
1005 //							expr, &line[1]);
1006 						g_hash_table_insert(autowrules_ht, strdup(expr), GINT_TO_POINTER(1));
1007 
1008 					}
1009 
1010 					if(!skip) {
1011 
1012 						do {
1013 							// Decreasing directory level and make the '$' ending
1014 							end = strrchr(expr, '/');
1015 							if(end != NULL) {
1016 								if(end[-1] != '$')
1017 									*(end++) = '$';
1018 								*end = 0;
1019 								exprlen = (size_t)(end - expr);
1020 							} else {
1021 								expr[1] = '$';
1022 								expr[2] = 0;
1023 								exprlen = 2;
1024 							}
1025 
1026 							// Checking if it not already set
1027 							if(!g_hash_table_lookup(autowrules_ht, expr)) {
1028 
1029 								// Switching to next rule:
1030 
1031 								rule = &rules[i];
1032 								rule->num = i++;
1033 
1034 								// Adding the rule
1035 
1036 								rule->objtype = S_IFDIR;
1037 								rule->mask    = RA_WALK;
1038 								rule->perm    = RA_WALK;
1039 
1040 								debug(1, "Rule #%i <+> <w> pattern <%s> (length: %i) [auto].",
1041 									rule->num, expr, exprlen);
1042 								if((ret=rule_complete(rule, expr)))
1043 									goto l_parse_rules_fromfile_end;
1044 								g_hash_table_insert(autowrules_ht, strdup(expr), GINT_TO_POINTER(1));
1045 
1046 							}
1047 						} while(end != NULL);
1048 					}
1049 				}
1050 			}
1051 		}
1052 	}
1053 
1054 l_parse_rules_fromfile_end:
1055 	if(size)
1056 		free(line_buf);
1057 
1058 	fclose(f);
1059 
1060 	debug(3, "Adding tail-rule #%u (effective #%u).", -1, i);
1061 
1062 	rules[i].mask   = RA_NONE;		// Terminator. End of rules' chain.
1063 	rules[i].perm   = DEFAULT_RULES_PERM;
1064 
1065 	g_hash_table_destroy(autowrules_ht);
1066 #ifdef _DEBUG
1067 	debug(3, "Total (p == %p):", rules);
1068 	i=0;
1069 	do {
1070 		debug(4, "\t%i\t%i\t%p/%p", i, rules[i].objtype, (void *)(long)rules[i].perm, (void *)(long)rules[i].mask);
1071 		i++;
1072 	} while(rules[i].mask != RA_NONE);
1073 #endif
1074 	return ret;
1075 }
1076 
becomedaemon()1077 int becomedaemon() {
1078 	int pid;
1079 	signal(SIGPIPE, SIG_IGN);
1080 	switch((pid = fork())) {
1081 		case -1:
1082 			error("Cannot fork().");
1083 			return(errno);
1084 		case 0:
1085 			setsid();
1086 			break;
1087 		default:
1088 			debug(1, "fork()-ed, pid is %i.", pid);
1089 			errno=0;
1090 			exit(0);
1091 	}
1092 	return 0;
1093 }
1094 
main_cleanup(ctx_t * ctx_p)1095 int main_cleanup(ctx_t *ctx_p) {
1096 	int i=0;
1097 	while((i < MAXRULES) && (ctx_p->rules[i].mask != RA_NONE))
1098 		regfree(&ctx_p->rules[i++].expr);
1099 
1100 	debug(3, "%i %i %i %i", ctx_p->watchdirsize, ctx_p->watchdirwslashsize, ctx_p->destdirsize, ctx_p->destdirwslashsize);
1101 
1102 	return 0;
1103 }
1104 
main_rehash(ctx_t * ctx_p)1105 int main_rehash(ctx_t *ctx_p) {
1106 	debug(3, "");
1107 	int ret=0;
1108 
1109 	main_cleanup(ctx_p);
1110 
1111 	if(ctx_p->rulfpath != NULL) {
1112 		ret = parse_rules_fromfile(ctx_p);
1113 		if(ret)
1114 			error("Got error from parse_rules_fromfile().");
1115 	} else {
1116 		ctx_p->rules[0].perm = DEFAULT_RULES_PERM;
1117 		ctx_p->rules[0].mask = RA_NONE;		// Terminator. End of rules.
1118 	}
1119 
1120 	return ret;
1121 }
1122 
main_status_update(ctx_t * ctx_p,state_t state)1123 int main_status_update(ctx_t *ctx_p, state_t state) {
1124 	static state_t state_old = STATE_UNKNOWN;
1125 	debug(4, "%u", state);
1126 
1127 	if(state == state_old) {
1128 		debug(3, "State unchanged: %u == %u", state, state_old);
1129 		return 0;
1130 	}
1131 
1132 #ifdef VERYPARANOID
1133 	if(status_descr[state] == NULL) {
1134 		error("status_descr[%u] == NULL.", state);
1135 		return EINVAL;
1136 	}
1137 #endif
1138 
1139 	setenv("CLSYNC_STATUS", status_descr[state], 1);
1140 
1141 	if(ctx_p->statusfile == NULL)
1142 		return 0;
1143 
1144 	FILE *f = fopen(ctx_p->statusfile, "w");
1145 	if(f == NULL) {
1146 		error("Cannot open file \"%s\" for writing.",
1147 			ctx_p->statusfile);
1148 		return errno;
1149 	}
1150 
1151 	debug(3, "Setting status to %i: %s.", state, status_descr[state]);
1152 	state_old=state;
1153 
1154 	int ret = 0;
1155 
1156 	if(fprintf(f, "%s", status_descr[state]) <= 0) {	// TODO: check output length
1157 		error("Cannot write to file \"%s\".",
1158 			ctx_p->statusfile);
1159 		ret = errno;
1160 	}
1161 
1162 	if(fclose(f)) {
1163 		error("Cannot close file \"%s\".",
1164 			ctx_p->statusfile);
1165 		ret = errno;
1166 	}
1167 
1168 	return ret;
1169 }
1170 
main(int argc,char * argv[])1171 int main(int argc, char *argv[]) {
1172 	struct ctx *ctx_p = xcalloc(1, sizeof(*ctx_p));
1173 #ifdef CLUSTER_SUPPORT
1174 	struct utsname utsname;
1175 #endif
1176 
1177 	int ret = 0, nret;
1178 	ctx_p->flags[MONITOR]			 = DEFAULT_NOTIFYENGINE;
1179 	ctx_p->syncdelay 			 = DEFAULT_SYNCDELAY;
1180 	ctx_p->_queues[QUEUE_NORMAL].collectdelay   = DEFAULT_COLLECTDELAY;
1181 	ctx_p->_queues[QUEUE_BIGFILE].collectdelay  = DEFAULT_BFILECOLLECTDELAY;
1182 	ctx_p->_queues[QUEUE_INSTANT].collectdelay  = COLLECTDELAY_INSTANT;
1183 	ctx_p->_queues[QUEUE_LOCKWAIT].collectdelay = COLLECTDELAY_INSTANT;
1184 	ctx_p->bfilethreshold			 = DEFAULT_BFILETHRESHOLD;
1185 	ctx_p->label				 = DEFAULT_LABEL;
1186 	ctx_p->rsyncinclimit			 = DEFAULT_RSYNCINCLUDELINESLIMIT;
1187 	ctx_p->synctimeout			 = DEFAULT_SYNCTIMEOUT;
1188 #ifdef CLUSTER_SUPPORT
1189 	ctx_p->cluster_hash_dl_min		 = DEFAULT_CLUSTERHDLMIN;
1190 	ctx_p->cluster_hash_dl_max		 = DEFAULT_CLUSTERHDLMAX;
1191 	ctx_p->cluster_scan_dl_max		 = DEFAULT_CLUSTERSDLMAX;
1192 #endif
1193 	ctx_p->config_block			 = DEFAULT_CONFIG_BLOCK;
1194 	ctx_p->retries				 = DEFAULT_RETRIES;
1195 	ctx_p->flags[VERBOSE]			 = DEFAULT_VERBOSE;
1196 
1197 	error_init(&ctx_p->flags[OUTPUT_METHOD], &ctx_p->flags[QUIET], &ctx_p->flags[VERBOSE], &ctx_p->flags[DEBUG]);
1198 
1199 	nret = arguments_parse(argc, argv, ctx_p);
1200 	if (nret) ret = nret;
1201 
1202 	if (!ret) {
1203 		nret = configs_parse(ctx_p);
1204 		if(nret) ret = nret;
1205 	}
1206 
1207 	if (ctx_p->dump_path == NULL) {
1208 		ctx_p->dump_path = parameter_expand(ctx_p, strdup(DEFAULT_DUMPDIR), 2);
1209 		ctx_p->flags_values_raw[DUMPDIR] = ctx_p->dump_path;
1210 	}
1211 
1212 	debug(4, "debugging flags: %u %u %u %u", ctx_p->flags[OUTPUT_METHOD], ctx_p->flags[QUIET], ctx_p->flags[VERBOSE], ctx_p->flags[DEBUG]);
1213 
1214 	main_status_update(ctx_p, STATE_STARTING);
1215 
1216 	if(ctx_p->socketpath != NULL) {
1217 #ifndef ENABLE_SOCKET
1218 		ret = EINVAL;
1219 		error("clsync is compiled without control socket support, option \"--socket\" cannot be used.");
1220 #endif
1221 		if(ctx_p->flags[SOCKETAUTH] == SOCKAUTH_UNSET)
1222 			ctx_p->flags[SOCKETAUTH] = SOCKAUTH_NULL;
1223 	}
1224 
1225 	if((ctx_p->flags[SOCKETOWN]) && (ctx_p->socketpath == NULL)) {
1226 		ret = errno = EINVAL;
1227 		error("\"--socket-own\" is useless without \"--socket\"");
1228 	}
1229 
1230 	if((ctx_p->flags[SOCKETMOD]) && (ctx_p->socketpath == NULL)) {
1231 		ret = errno = EINVAL;
1232 		error("\"--socket-mod\" is useless without \"--socket\"");
1233 	}
1234 
1235 	if((ctx_p->flags[SOCKETAUTH]) && (ctx_p->socketpath == NULL)) {
1236 		ret = errno = EINVAL;
1237 		error("\"--socket-auth\" is useless without \"--socket\"");
1238 	}
1239 
1240 #ifdef VERYPARANOID
1241 	if((ctx_p->retries != 1) && ctx_p->flags[THREADING]) {
1242 		ret = errno = EINVAL;
1243 		error("\"--retries\" values should be equal to \"1\" for this \"--threading\" value.");
1244 	}
1245 #endif
1246 
1247 	if(ctx_p->flags[STANDBYFILE] && (ctx_p->flags[MODE] == MODE_SIMPLE)) {
1248 		ret = errno = EINVAL;
1249 		error("Sorry but option \"--standby-file\" cannot be used in mode \"simple\", yet.");
1250 	}
1251 
1252 	if(ctx_p->flags[THREADING] && ctx_p->flags[ONLYINITSYNC]) {
1253 		ret = errno = EINVAL;
1254 		error("Conflicting options: This value of \"--threading\" cannot be used in conjunction with \"--only-initialsync\".");
1255 	}
1256 
1257 	if(ctx_p->flags[THREADING] && ctx_p->flags[EXITONNOEVENTS]) {
1258 		ret = errno = EINVAL;
1259 		error("Conflicting options: This value of \"--threading\" cannot be used in conjunction with \"--exit-on-no-events\".");
1260 	}
1261 	if(ctx_p->flags[THREADING] && ctx_p->flags[MAXITERATIONS]) {
1262 		ret = errno = EINVAL;
1263 		error("Conflicting options: This value of \"--threading\" cannot be used in conjunction with \"--max-iterations\".");
1264 	}
1265 	if(ctx_p->flags[SKIPINITSYNC] && ctx_p->flags[EXITONNOEVENTS]) {
1266 		ret = errno = EINVAL;
1267 		error("Conflicting options: \"--skip-initialsync\" and \"--exit-on-no-events\" cannot be used together.");
1268 	}
1269 	if(ctx_p->flags[ONLYINITSYNC] && ctx_p->flags[EXITONNOEVENTS]) {
1270 		ret = errno = EINVAL;
1271 		error("Conflicting options: \"--only-initialsync\" and \"--exit-on-no-events\" cannot be used together.");
1272 	}
1273 
1274 	if(ctx_p->flags[SKIPINITSYNC] && ctx_p->flags[ONLYINITSYNC]) {
1275 		ret = errno = EINVAL;
1276 		error("Conflicting options: \"--skip-initialsync\" and \"--only-initialsync\" cannot be used together.");
1277 	}
1278 
1279 	if(ctx_p->flags[INITFULL] && ctx_p->flags[SKIPINITSYNC]) {
1280 		ret = errno = EINVAL;
1281 		error("Conflicting options: \"--full-initialsync\" and \"--skip-initialsync\" cannot be used together.");
1282 	}
1283 
1284 	if(ctx_p->flags[EXCLUDEMOUNTPOINTS])
1285 		ctx_p->flags[ONEFILESYSTEM]=1;
1286 
1287 	if(ctx_p->flags[MODE] == MODE_UNSET) {
1288 		ret = errno = EINVAL;
1289 		error("\"--mode\" is not set.");
1290 	}
1291 
1292 	if(ctx_p->watchdir == NULL) {
1293 		ret = errno = EINVAL;
1294 		error("\"--watchdir\" is not set.");
1295 	}
1296 
1297 	if(ctx_p->handlerfpath == NULL) {
1298 		ret = errno = EINVAL;
1299 		error("\"--sync-handler\" path is not set.");
1300 	}
1301 /*
1302 	if(ctx_p->flags[SYNCHANDLERSO] && ctx_p->flags[RSYNC]) {
1303 		ret = EINVAL;
1304 		ret = errno = EINVAL;
1305 		error("Option \"--rsync\" cannot be used in conjunction with \"--synchandler-so-module\".");
1306 	}
1307 */
1308 //	if(ctx_p->flags[SYNCHANDLERSO] && (ctx_p->listoutdir != NULL))
1309 //		error("Warning: Option \"--dir-lists\" has no effect conjunction with \"--synchandler-so-module\".");
1310 
1311 //	if(ctx_p->flags[SYNCHANDLERSO] && (ctx_p->destdir != NULL))
1312 //		error("Warning: Destination directory argument has no effect conjunction with \"--synchandler-so-module\".");
1313 
1314 	if((ctx_p->flags[MODE] == MODE_RSYNCDIRECT) && (ctx_p->destdir == NULL)) {
1315 		ret = errno = EINVAL;
1316 		error("Mode \"rsyncdirect\" cannot be used without specifying \"destination directory\".");
1317 	}
1318 
1319 #ifdef CLUSTER_SUPPORT
1320 	if((ctx_p->flags[MODE] == MODE_RSYNCDIRECT ) && (ctx_p->cluster_iface != NULL)) {
1321 		ret = errno = EINVAL;
1322 		error("Mode \"rsyncdirect\" cannot be used in conjunction with \"--cluster-iface\".");
1323 	}
1324 
1325 	if((ctx_p->cluster_iface == NULL) && ((ctx_p->cluster_mcastipaddr != NULL) || (ctx_p->cluster_nodename != NULL) || (ctx_p->cluster_timeout) || (ctx_p->cluster_mcastipport))) {
1326 		ret = errno = EINVAL;
1327 		error("ctx \"--cluster-ip\", \"--cluster-node-name\", \"--cluster_timeout\" and/or \"cluster_ipport\" cannot be used without \"--cluster-iface\".");
1328 	}
1329 
1330 	if(ctx_p->cluster_hash_dl_min > ctx_p->cluster_hash_dl_max) {
1331 		ret = errno = EINVAL;
1332 		error("\"--cluster-hash-dl-min\" cannot be greater than \"--cluster-hash-dl-max\".");
1333 	}
1334 
1335 	if(ctx_p->cluster_hash_dl_max > ctx_p->cluster_scan_dl_max) {
1336 		ret = errno = EINVAL;
1337 		error("\"--cluster-hash-dl-max\" cannot be greater than \"--cluster-scan-dl-max\".");
1338 	}
1339 
1340 	if(!ctx_p->cluster_timeout)
1341 		ctx_p->cluster_timeout	    = DEFAULT_CLUSTERTIMEOUT;
1342 	if(!ctx_p->cluster_mcastipport)
1343 		ctx_p->cluster_mcastipport = DEFAULT_CLUSTERIPPORT;
1344 	if(!ctx_p->cluster_mcastipaddr)
1345 		ctx_p->cluster_mcastipaddr = DEFAULT_CLUSTERIPADDR;
1346 
1347 	if(ctx_p->cluster_iface != NULL) {
1348 #ifndef _DEBUG
1349 		ret = errno = EINVAL;
1350 		error("Cluster subsystem is not implemented, yet. Sorry.");
1351 #endif
1352 		if(ctx_p->cluster_nodename == NULL) {
1353 
1354 			if(!uname(&utsname))
1355 				ctx_p->cluster_nodename = utsname.nodename;
1356 
1357 			debug(1, "cluster node name is: %s", ctx_p->cluster_nodename);
1358 		}
1359 		if(ctx_p->cluster_nodename == NULL) {
1360 			ret = errno = EINVAL;
1361 			error("Option \"--cluster-iface\" is set, but \"--cluster-node-name\" is not set and cannot get the nodename with uname().");
1362 		} else {
1363 			ctx_p->cluster_nodename_len = strlen(ctx_p->cluster_nodename);
1364 		}
1365 	}
1366 #endif // CLUSTER_SUPPORT
1367 
1368 	if (ctx_p->watchdir != NULL) {
1369 		char *rwatchdir = realpath(ctx_p->watchdir, NULL);
1370 		if(rwatchdir == NULL) {
1371 			error("Got error while realpath() on \"%s\" [#0].", ctx_p->watchdir);
1372 			ret = errno;
1373 		}
1374 
1375 		stat64_t stat64={0};
1376 		if(lstat64(ctx_p->watchdir, &stat64)) {
1377 			error("Cannot lstat64() on \"%s\"", ctx_p->watchdir);
1378 			if(!ret)
1379 				ret = errno;
1380 		} else {
1381 			if(ctx_p->flags[EXCLUDEMOUNTPOINTS])
1382 				ctx_p->st_dev = stat64.st_dev;
1383 			if((stat64.st_mode & S_IFMT) == S_IFLNK) {
1384 				// The proplems may be due to FTS_PHYSICAL option of ftp_open() in sync_initialsync_rsync_walk(),
1385 				// so if the "watch dir" is just a symlink it doesn't walk recursivly. For example, in "-R" case
1386 				// it disables filters, because exclude-list will be empty.
1387 #ifdef VERYPARANOID
1388 				error("Watch dir cannot be symlink, but \"%s\" is a symlink.", ctx_p->watchdir);
1389 				ret = EINVAL;
1390 #else
1391 				char *watchdir_resolved_part = xcalloc(1, PATH_MAX+2);
1392 				ssize_t r = readlink(ctx_p->watchdir, watchdir_resolved_part, PATH_MAX+1);
1393 
1394 				if(r>=PATH_MAX) {	// TODO: check if it's possible
1395 					ret = errno = EINVAL;
1396 					error("Too long file path resolved from symbolic link \"%s\"", ctx_p->watchdir);
1397 				} else
1398 				if(r<0) {
1399 					error("Cannot resolve symbolic link \"%s\": readlink() error", ctx_p->watchdir);
1400 					ret = EINVAL;
1401 				} else {
1402 					char *watchdir_resolved;
1403 #ifdef VERYPARANOID
1404 					if(ctx_p->watchdirsize)
1405 						if(ctx_p->watchdir != NULL)
1406 							free(ctx_p->watchdir);
1407 #endif
1408 
1409 					size_t watchdir_resolved_part_len = strlen(watchdir_resolved_part);
1410 					ctx_p->watchdirsize = watchdir_resolved_part_len+1;	// Not true for case of relative symlink
1411 					if(*watchdir_resolved_part == '/') {
1412 						// Absolute symlink
1413 						watchdir_resolved = malloc(ctx_p->watchdirsize);
1414 						memcpy(watchdir_resolved, watchdir_resolved_part, ctx_p->watchdirsize);
1415 					} else {
1416 						// Relative symlink :(
1417 						char *rslash = strrchr(ctx_p->watchdir, '/');
1418 
1419 						char *watchdir_resolved_rel  = xmalloc(PATH_MAX+2);
1420 						size_t watchdir_resolved_rel_len = rslash-ctx_p->watchdir + 1;
1421 						memcpy(watchdir_resolved_rel, ctx_p->watchdir, watchdir_resolved_rel_len);
1422 						memcpy(&watchdir_resolved_rel[watchdir_resolved_rel_len], watchdir_resolved_part, watchdir_resolved_part_len+1);
1423 
1424 						watchdir_resolved = realpath(watchdir_resolved_rel, NULL);
1425 
1426 						free(watchdir_resolved_rel);
1427 					}
1428 
1429 
1430 					debug(1, "Symlink resolved: watchdir \"%s\" -> \"%s\"", ctx_p->watchdir, watchdir_resolved);
1431 					ctx_p->watchdir = watchdir_resolved;
1432 				}
1433 				free(watchdir_resolved_part);
1434 #endif // VERYPARANOID else
1435 			}
1436 		}
1437 
1438 		if(!ret) {
1439 			ctx_p->watchdir     = rwatchdir;
1440 			ctx_p->watchdirlen  = strlen(ctx_p->watchdir);
1441 			ctx_p->watchdirsize = ctx_p->watchdirlen;
1442 
1443 #ifdef VERYPARANOID
1444 			if(ctx_p->watchdirlen == 1) {
1445 				ret = errno = EINVAL;
1446 				error("Very-Paranoid: --watch-dir is supposed to be not \"/\".");
1447 			}
1448 #endif
1449 		}
1450 
1451 		if(!ret) {
1452 			if(ctx_p->watchdirlen == 1) {
1453 				ctx_p->watchdirwslash     = ctx_p->watchdir;
1454 				ctx_p->watchdirwslashsize = 0;
1455 				ctx_p->watchdir_dirlevel  = 0;
1456 			} else {
1457 				size_t size = ctx_p->watchdirlen + 2;
1458 				char *newwatchdir = xmalloc(size);
1459 				memcpy( newwatchdir, ctx_p->watchdir, ctx_p->watchdirlen);
1460 				ctx_p->watchdirwslash     = newwatchdir;
1461 				ctx_p->watchdirwslashsize = size;
1462 				memcpy(&ctx_p->watchdirwslash[ctx_p->watchdirlen], "/", 2);
1463 
1464 				ctx_p->watchdir_dirlevel  = fileutils_calcdirlevel(ctx_p->watchdirwslash);
1465 			}
1466 		}
1467 	}
1468 
1469 	if((ctx_p->destdir != NULL) && (ctx_p->destproto == NULL)) {	// "ctx_p->destproto == NULL" means "no protocol"/"local directory"
1470 		char *rdestdir = realpath(ctx_p->destdir, NULL);
1471 		if(rdestdir == NULL) {
1472 			error("Got error while realpath() on \"%s\" [#1].", ctx_p->destdir);
1473 			ret = errno;
1474 		}
1475 
1476 		if(!ret) {
1477 			ctx_p->destdir     = rdestdir;
1478 			ctx_p->destdirlen  = strlen(ctx_p->destdir);
1479 			ctx_p->destdirsize = ctx_p->destdirlen;
1480 
1481 			if(ctx_p->destdirlen == 1) {
1482 				ret = errno = EINVAL;
1483 				error("destdir is supposed to be not \"/\".");
1484 			}
1485 		}
1486 
1487 		if(!ret) {
1488 			size_t size = ctx_p->destdirlen  + 2;
1489 			char *newdestdir  = xmalloc(size);
1490 			memcpy( newdestdir,  ctx_p->destdir,  ctx_p->destdirlen);
1491 			ctx_p->destdirwslash     = newdestdir;
1492 			ctx_p->destdirwslashsize = size;
1493 			memcpy(&ctx_p->destdirwslash[ctx_p->destdirlen], "/", 2);
1494 		}
1495 	} else
1496 	if (ctx_p->destproto != NULL)
1497 		ctx_p->destdirwslash = ctx_p->destdir;
1498 
1499 	debug(1, "%s [%s] (%p) -> %s [%s]", ctx_p->watchdir, ctx_p->watchdirwslash, ctx_p->watchdirwslash, ctx_p->destdir?ctx_p->destdir:"", ctx_p->destdirwslash?ctx_p->destdirwslash:"");
1500 
1501 	if(
1502 		(
1503 			(ctx_p->flags[MODE]==MODE_RSYNCDIRECT) ||
1504 			(ctx_p->flags[MODE]==MODE_RSYNCSHELL)  ||
1505 			(ctx_p->flags[MODE]==MODE_RSYNCSO)
1506 		) && (ctx_p->listoutdir == NULL)
1507 	) {
1508 		ret = errno = EINVAL;
1509 		error("Modes \"rsyncdirect\", \"rsyncshell\" and \"rsyncso\" cannot be used without \"--lists-dir\".");
1510 	}
1511 
1512 	if(
1513 		ctx_p->flags[RSYNCPREFERINCLUDE] &&
1514 		!(
1515 			ctx_p->flags[MODE] == MODE_RSYNCDIRECT ||
1516 			ctx_p->flags[MODE] == MODE_RSYNCSHELL  ||
1517 			ctx_p->flags[MODE] == MODE_RSYNCSO
1518 		)
1519 	)
1520 		warning("Option \"--rsyncpreferinclude\" is useless if mode is not \"rsyncdirect\", \"rsyncshell\" or \"rsyncso\".");
1521 
1522 	if(
1523 		(
1524 			ctx_p->flags[MODE] == MODE_RSYNCDIRECT ||
1525 			ctx_p->flags[MODE] == MODE_RSYNCSHELL  ||
1526 			ctx_p->flags[MODE] == MODE_RSYNCSO
1527 		)
1528 		&& ctx_p->flags[AUTORULESW]
1529 	)
1530 		warning("Option \"--auto-add-rules-w\" in modes \"rsyncdirect\", \"rsyncshell\" and \"rsyncso\" may cause unexpected problems.");
1531 
1532 	if(ctx_p->listoutdir != NULL) {
1533 		struct stat st={0};
1534 		errno = 0;
1535 		if(stat(ctx_p->listoutdir, &st)) {
1536 			if(errno == ENOENT) {
1537 				warning("Directory \"%s\" doesn't exist. Creating it.", ctx_p->listoutdir);
1538 				errno = 0;
1539 				if(mkdir(ctx_p->listoutdir, S_IRWXU)) {
1540 					error("Cannot create directory \"%s\".", ctx_p->listoutdir);
1541 					ret = errno;
1542 				}
1543 			} else {
1544 				error("Got error while stat() on \"%s\".", ctx_p->listoutdir);
1545 				ret = errno;
1546 			}
1547 		}
1548 		if(!errno)
1549 			if(st.st_mode & (S_IRWXG|S_IRWXO)) {
1550 #ifdef PARANOID
1551 				ret = errno = EACCES;
1552 				error("Insecure: Others have access to directory \"%s\". Exit.", ctx_p->listoutdir);
1553 #else
1554 				warning("Insecure: Others have access to directory \"%s\".", ctx_p->listoutdir);
1555 #endif
1556 			}
1557 	}
1558 
1559 /*
1560 	if(ctx_p->flags[HAVERECURSIVESYNC] && (ctx_p->listoutdir == NULL)) {
1561 		error("Option \"--dir-lists\" should be set to use option \"--have-recursive-sync\".");
1562 		ret = EINVAL;
1563 	}
1564 */
1565 
1566 	if(
1567 		ctx_p->flags[HAVERECURSIVESYNC] &&
1568 		(
1569 			ctx_p->flags[MODE] == MODE_RSYNCDIRECT ||
1570 			ctx_p->flags[MODE] == MODE_RSYNCSHELL  ||
1571 			ctx_p->flags[MODE] == MODE_RSYNCSO
1572 		)
1573 	) {
1574 		ret = errno = EINVAL;
1575 		error("Option \"--have-recursive-sync\" with nodes \"rsyncdirect\", \"rsyncshell\" and \"rsyncso\" are incompatible.");
1576 	}
1577 
1578 	if(ctx_p->flags[SYNCLISTSIMPLIFY] && (ctx_p->listoutdir == NULL)) {
1579 		ret = errno = EINVAL;
1580 		error("Option \"--dir-lists\" should be set to use option \"--synclist-simplify\".");
1581 	}
1582 
1583 	if(
1584 		ctx_p->flags[SYNCLISTSIMPLIFY] &&
1585 		(
1586 			ctx_p->flags[MODE] == MODE_RSYNCDIRECT ||
1587 			ctx_p->flags[MODE] == MODE_RSYNCSHELL  ||
1588 			ctx_p->flags[MODE] == MODE_RSYNCSO
1589 		)
1590 	) {
1591 		ret = errno = EINVAL;
1592 		error("Option \"--synclist-simplify\" with nodes \"rsyncdirect\" and \"rsyncshell\" are incompatible.");
1593 	}
1594 
1595 #ifdef FANOTIFY_SUPPORT
1596 	if (ctx_p->flags[MONITOR] == NE_FANOTIFY)
1597 		critical("fanotify is not supported, now!");
1598 	else
1599 #endif
1600 	switch (ctx_p->flags[MONITOR]) {
1601 #ifdef INOTIFY_SUPPORT
1602 		case NE_INOTIFY:
1603 #endif
1604 #ifdef FANOTIFY_SUPPORT
1605 		case NE_FANOTIFY:
1606 #endif
1607 #ifdef KQUEUE_SUPPORT
1608 		case NE_KQUEUE:
1609 #endif
1610 #ifdef BSM_SUPPORT
1611 		case NE_BSM:
1612 #endif
1613 #ifdef DTRACEPIPE_SUPPORT
1614 		case NE_DTRACEPIPE:
1615 #endif
1616 			break;
1617 		default:
1618 			ret = errno = EINVAL;
1619 			error("Required one of next options:"
1620 #ifdef INOTIFY_SUPPORT
1621 				" \"--monitor=inotify\""
1622 #endif
1623 #ifdef FANOTIFY_SUPPORT
1624 				" \"--monitor=fanotify\""
1625 #endif
1626 #ifdef KQUEUE_SUPPORT
1627 				" \"--monitor=kqueue\""
1628 #endif
1629 #ifdef BSM_SUPPORT
1630 				" \"--monitor=bsm\""
1631 #endif
1632 #ifdef DTRACEPIPE_SUPPORT
1633 				" \"--monitor=dtracepipe\""
1634 #endif
1635 			);
1636 	}
1637 
1638 	if (ctx_p->flags[EXITHOOK]) {
1639 #ifdef VERYPARANOID
1640 		if(ctx_p->exithookfile == NULL) {
1641 			ret = errno = EINVAL;
1642 			error("ctx_p->exithookfile == NULL");
1643 		} else
1644 #endif
1645 		{
1646 			if(access(ctx_p->exithookfile, X_OK) == -1) {
1647 				error("\"%s\" is not executable.", ctx_p->exithookfile);
1648 				if(!ret)
1649 					ret = errno;
1650 			}
1651 		}
1652 	}
1653 
1654 	if (ctx_p->handlerfpath != NULL)
1655 		if (access(ctx_p->handlerfpath, X_OK) == -1) {
1656 			error("\"%s\" is not executable.", ctx_p->handlerfpath);
1657 			if (!ret)
1658 				ret = errno;
1659 		}
1660 
1661 	nret=main_rehash(ctx_p);
1662 	if(nret)
1663 		ret = nret;
1664 
1665 	if(ctx_p->flags[BACKGROUND]) {
1666 		nret = becomedaemon();
1667 		if(nret)
1668 			ret = nret;
1669 	}
1670 
1671 #ifdef HAVE_CAPABILITIES
1672 	if(ctx_p->flags[CAP_PRESERVE_FILEACCESS]) {
1673 		// Doesn't work, yet :(
1674 		//
1675 		// Error: Cannot inotify_add_watch() on "/home/xaionaro/clsync/examples/testdir/from": Permission denied (errno: 13).
1676 
1677 		debug(1, "Preserving access to files with using linux capabilites");
1678 
1679 		struct __user_cap_header_struct	cap_hdr = {0};
1680 		struct __user_cap_data_struct	cap_dat = {0};
1681 
1682 		cap_hdr.version = _LINUX_CAPABILITY_VERSION;
1683 		if(capget(&cap_hdr, &cap_dat) < 0) {
1684 			error("main() cannot get capabilites with capget()");
1685 			ret = errno;
1686 
1687 			goto preserve_fileaccess_end;
1688 		}
1689 
1690 		// From "man 7 capabilities":
1691 		// CAP_DAC_OVERRIDE    - Bypass file read, write, and execute permission checks.
1692 		// CAP_DAC_READ_SEARCH - Bypass file read permission checks and directory read and execute permission checks.
1693 
1694 		cap_dat.effective    =  (CAP_TO_MASK(CAP_DAC_OVERRIDE)|CAP_TO_MASK(CAP_DAC_READ_SEARCH)|CAP_TO_MASK(CAP_FOWNER)|CAP_TO_MASK(CAP_SYS_ADMIN)|CAP_TO_MASK(CAP_SETUID));
1695 		cap_dat.permitted    =  (CAP_TO_MASK(CAP_DAC_OVERRIDE)|CAP_TO_MASK(CAP_DAC_READ_SEARCH)|CAP_TO_MASK(CAP_FOWNER)|CAP_TO_MASK(CAP_SYS_ADMIN)|CAP_TO_MASK(CAP_SETUID));
1696 		cap_dat.inheritable  = 0;
1697 
1698 		debug(3, "cap.eff == %p; cap.prm == %p.",
1699 			(void *)(long)cap_dat.effective, (void *)(long)cap_dat.permitted);
1700 
1701 		if(capset(&cap_hdr, &cap_dat) < 0) {
1702 			error("Cannot set capabilities with capset().");
1703 			ret = errno;
1704 
1705 			goto preserve_fileaccess_end;
1706 		}
1707 
1708 		// Tell kernel not clear capabilities when dropping root
1709 		if(prctl(PR_SET_KEEPCAPS, 1) < 0) {
1710 			error("Cannot prctl(PR_SET_KEEPCAPS, 1) to preserve capabilities");
1711 			ret = errno;
1712 
1713 			goto preserve_fileaccess_end;
1714 		}
1715 	}
1716 preserve_fileaccess_end:
1717 #endif
1718 
1719 	if(ctx_p->flags[UID]) {
1720 		if(setuid(ctx_p->uid)) {
1721 			error("Cannot setuid(%u)", ctx_p->uid);
1722 			ret = errno;
1723 		}
1724 	}
1725 
1726 	if(ctx_p->flags[GID]) {
1727 		if(setuid(ctx_p->gid)) {
1728 			error("Cannot setgid(%u)", ctx_p->gid);
1729 			ret = errno;
1730 		}
1731 	}
1732 
1733 	if(ctx_p->pidfile != NULL) {
1734 		pid_t pid = getpid();
1735 		FILE *pidfile = fopen(ctx_p->pidfile, "w");
1736 		if(pidfile == NULL) {
1737 			error("Cannot open file \"%s\" to write a pid there",
1738 				ctx_p->pidfile);
1739 			ret = errno;
1740 		} else {
1741 			if(fprintf(pidfile, "%u", pid) < 0) {
1742 				error("Cannot write pid into file \"%s\"",
1743 					ctx_p->pidfile);
1744 				ret = errno;
1745 			}
1746 			fclose(pidfile);
1747 		}
1748 	}
1749 
1750 	debug(3, "Current errno is %i.", ret);
1751 
1752 	// == RUNNING ==
1753 	if(ret == 0)
1754 		ret = sync_run(ctx_p);
1755 	// == RUNNING ==
1756 
1757 	if(ctx_p->pidfile != NULL) {
1758 		if(unlink(ctx_p->pidfile)) {
1759 			error("Cannot unlink pidfile \"%s\"",
1760 				ctx_p->pidfile);
1761 			ret = errno;
1762 		}
1763 	}
1764 
1765 	if(ctx_p->statusfile != NULL) {
1766 		if(unlink(ctx_p->statusfile)) {
1767 			error("Cannot unlink status file \"%s\"",
1768 				ctx_p->statusfile);
1769 			ret = errno;
1770 		}
1771 	}
1772 
1773 	main_cleanup(ctx_p);
1774 
1775 	if(ctx_p->watchdirsize)
1776 		free(ctx_p->watchdir);
1777 
1778 	if(ctx_p->watchdirwslashsize)
1779 		free(ctx_p->watchdirwslash);
1780 
1781 	if(ctx_p->destdirsize)
1782 		free(ctx_p->destdir);
1783 
1784 	if(ctx_p->destdirwslashsize)
1785 		free(ctx_p->destdirwslash);
1786 
1787 	options_cleanup(ctx_p);
1788 	free(ctx_p);
1789 	debug(1, "finished, exitcode: %i: %s.", ret, strerror(ret));
1790 	return ret;
1791 }
1792 
1793 
1794