12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082 |
- /*
- clsync - file tree sync utility based on fanotify and inotify
- Copyright (C) 2013 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "common.h"
- #include "output.h"
- #include "sync.h"
- #include "malloc.h"
- #include "cluster.h"
- #include "fileutils.h"
- #include "revision.h"
- static struct option long_options[] =
- {
- {"background", optional_argument, NULL, BACKGROUND},
- {"config-path", required_argument, NULL, CONFIGPATH},
- {"config-block", required_argument, NULL, CONFIGBLOCK},
- {"pid-file", required_argument, NULL, PIDFILE},
- {"uid", required_argument, NULL, UID},
- {"gid", required_argument, NULL, GID},
- #ifdef HAVE_CAPABILITIES
- {"preserve-file-access",optional_argument, NULL, CAP_PRESERVE_FILEACCESS},
- #endif
- {"pthread", optional_argument, NULL, PTHREAD},
- {"syslog", optional_argument, NULL, SYSLOG},
- #ifdef CLUSTER_SUPPORT
- {"cluster-iface", required_argument, NULL, CLUSTERIFACE}, // Not implemented, yet
- {"cluster-ip", required_argument, NULL, CLUSTERMCASTIPADDR}, // Not implemented, yet
- {"cluster-port", required_argument, NULL, CLUSTERMCASTIPPORT}, // Not implemented, yet
- {"cluster-timeout", required_argument, NULL, CLUSTERTIMEOUT}, // Not implemented, yet
- {"cluster-node-name", required_argument, NULL, CLUSTERNODENAME}, // Not implemented, yet
- {"cluster-hash-dl-min", required_argument, NULL, CLUSTERHDLMIN},
- {"cluster-hash-dl-max", required_argument, NULL, CLUSTERHDLMAX},
- {"cluster-scan-dl-max", required_argument, NULL, CLUSTERSDLMAX},
- #endif
- {"timeout-sync", required_argument, NULL, SYNCTIMEOUT},
- {"delay-sync", required_argument, NULL, SYNCDELAY},
- {"delay-collect", required_argument, NULL, DELAY},
- {"delay-collect-bigfile",required_argument, NULL, BFILEDELAY},
- {"threshold-bigfile", required_argument, NULL, BFILETHRESHOLD},
- {"dir-lists", required_argument, NULL, OUTLISTSDIR},
- {"initialsync-enable", optional_argument, NULL, ENABLEINITIALSYNC},
- {"synclist-simplify", optional_argument, NULL, SYNCLISTSIMPLIFY},
- {"auto-add-rules-w", optional_argument, NULL, AUTORULESW},
- {"synchandler-so-module",optional_argument, NULL, SYNCHANDLERSO},
- {"rsync", optional_argument, NULL, RSYNC},
- {"rsync-inclimit", required_argument, NULL, RSYNCINCLIMIT},
- {"rsync-prefer-include",optional_argument, NULL, RSYNC_PREFERINCLUDE},
- {"ignore-exitcode", required_argument, NULL, IGNOREEXITCODE},
- {"dont-unlink-lists", optional_argument, NULL, DONTUNLINK},
- {"full-initialsync", optional_argument, NULL, INITFULL},
- {"verbose", optional_argument, NULL, VERBOSE},
- {"debug", optional_argument, NULL, DEBUG},
- {"quiet", optional_argument, NULL, QUIET},
- #ifdef FANOTIFY_SUPPORT
- {"fanotify", optional_argument, NULL, FANOTIFY},
- {"inotify", optional_argument, NULL, INOTIFY},
- #endif
- {"label", required_argument, NULL, LABEL},
- {"help", optional_argument, NULL, HELP},
- {"version", optional_argument, NULL, SHOW_VERSION},
- {"watch-dir", required_argument, NULL, WATCHDIR},
- {"sync-handler", required_argument, NULL, SYNCHANDLER},
- {"rules-path", required_argument, NULL, RULESPATH},
- {"dir-destination", required_argument, NULL, DESTDIR},
- {NULL, 0, NULL, 0}
- };
- int syntax() {
- printf("syntax: clsync [flags] <watch dir> <action script> [file with rules regexps] [destination directory]\npossible options:\n");
- int i=0;
- while(long_options[i].name != NULL) {
- if(!(long_options[i].val & OPTION_CONFIGONLY))
- printf("\t--%-24s-%c%s\n", long_options[i].name, long_options[i].val, (long_options[i].has_arg == required_argument ? " argument" : ""));
- i++;
- }
- exit(EINVAL);
- }
- int version() {
- printf(PROGRAM" v%i.%i"REVISION"\n\t"AUTHOR"\n", VERSION_MAJ, VERSION_MIN);
- exit(0);
- }
- static inline int parse_parameter(options_t *options_p, uint16_t param_id, char *arg, paramsource_t paramsource) {
- #ifdef _DEBUG
- fprintf(stderr, "Force-Debug: parse_parameter(): %i: %i = \"%s\"\n", paramsource, param_id, arg);
- #endif
- switch(paramsource) {
- case PS_ARGUMENT:
- if(param_id & OPTION_CONFIGONLY) {
- syntax();
- return 0;
- }
- options_p->flags_set[param_id] = 1;
- break;
- case PS_CONFIG:
- if(options_p->flags_set[param_id])
- return 0;
- break;
- default:
- printf_e("Warning: Unknown parameter #%i source (value \"%s\").\n", param_id, arg!=NULL ? arg : "");
- break;
- }
- switch(param_id) {
- case '?':
- case HELP:
- syntax();
- break;
- case CONFIGPATH:
- options_p->config_path = arg;
- break;
- case CONFIGBLOCK:
- options_p->config_block = arg;
- break;
- case GID:
- options_p->gid = (unsigned int)atol(arg);
- options_p->flags[param_id]++;
- break;
- case UID:
- options_p->uid = (unsigned int)atol(arg);
- options_p->flags[param_id]++;
- break;
- case PIDFILE:
- options_p->pidfile = arg;
- break;
- #ifdef CLUSTER_SUPPORT
- case CLUSTERIFACE:
- options_p->cluster_iface = arg;
- break;
- case CLUSTERMCASTIPADDR:
- options_p->cluster_mcastipaddr = arg;
- break;
- case CLUSTERMCASTIPPORT:
- options_p->cluster_mcastipport = (uint16_t)atoi(arg);
- break;
- case CLUSTERTIMEOUT:
- options_p->cluster_timeout = (unsigned int)atol(arg);
- break;
- case CLUSTERNODENAME:
- options_p->cluster_nodename = arg;
- break;
- case CLUSTERHDLMIN:
- options_p->cluster_hash_dl_min = (uint16_t)atoi(arg);
- break;
- case CLUSTERHDLMAX:
- options_p->cluster_hash_dl_max = (uint16_t)atoi(arg);
- break;
- case CLUSTERSDLMAX:
- options_p->cluster_scan_dl_max = (uint16_t)atoi(arg);
- break;
- #endif
- case OUTLISTSDIR:
- options_p->listoutdir = arg;
- break;
- case LABEL:
- options_p->label = arg;
- break;
- case SYNCDELAY:
- options_p->syncdelay = (unsigned int)atol(arg);
- break;
- case DELAY:
- options_p->_queues[QUEUE_NORMAL].collectdelay = (unsigned int)atol(arg);
- break;
- case BFILEDELAY:
- options_p->_queues[QUEUE_BIGFILE].collectdelay = (unsigned int)atol(arg);
- break;
- case BFILETHRESHOLD:
- options_p->bfilethreshold = (unsigned long)atol(arg);
- break;
- #ifdef FANOTIFY_SUPPORT
- case FANOTIFY:
- options_p->notifyengine = NE_FANOTIFY;
- break;
- #endif
- case INOTIFY:
- options_p->notifyengine = NE_INOTIFY;
- break;
- case RSYNCINCLIMIT:
- options_p->rsyncinclimit = (unsigned int)atol(arg);
- break;
- case SYNCTIMEOUT:
- options_p->synctimeout = (unsigned int)atol(arg);
- break;
- case IGNOREEXITCODE:
- options_p->isignoredexitcode[(unsigned char)atoi(arg)] = 1;
- break;
- case SHOW_VERSION:
- version();
- break;
- case WATCHDIR:
- options_p->watchdir = arg;
- break;
- case SYNCHANDLER:
- options_p->handlerfpath = arg;
- break;
- case RULESPATH:
- options_p->rulfpath = arg;
- break;
- case DESTDIR:
- options_p->destdir = arg;
- break;
- default:
- if(arg == NULL)
- options_p->flags[param_id]++;
- else
- options_p->flags[param_id] = atoi(arg);
- #ifdef _DEBUG
- fprintf(stderr, "Force-Debug: flag %i is set to %i\n", param_id&0xff, options_p->flags[param_id]);
- #endif
- break;
- }
- return 0;
- }
- int arguments_parse(int argc, char *argv[], struct options *options_p) {
- int c;
- int option_index = 0;
- // Generating "optstring" (man 3 getopt_long) with using information from struct array "long_options"
- char *optstring = alloca((('z'-'a'+1)*3 + '9'-'0'+1)*3 + 1);
- char *optstring_ptr = optstring;
- struct option *lo_ptr = long_options;
- while(lo_ptr->name != NULL) {
- if(!(lo_ptr->val & OPTION_CONFIGONLY)) {
- *(optstring_ptr++) = lo_ptr->val & 0xff;
- if(lo_ptr->has_arg == required_argument)
- *(optstring_ptr++) = ':';
- if(lo_ptr->has_arg == optional_argument) {
- *(optstring_ptr++) = ':';
- *(optstring_ptr++) = ':';
- }
- }
- lo_ptr++;
- }
- *optstring_ptr = 0;
- #ifdef _DEBUG
- fprintf(stderr, "Force-Debug: %s\n", optstring);
- #endif
- // Parsing arguments
- while(1) {
- c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
- if (c == -1) break;
- parse_parameter(options_p, c, optarg, PS_ARGUMENT);
- }
- /*
- if(optind+1 >= argc)
- syntax();
- options_p->handlerfpath = argv[optind+1];
- if(optind+2 < argc) {
- options_p->rulfpath = argv[optind+2];
- if(!strcmp(options_p->rulfpath, ""))
- options_p->rulfpath = NULL;
- }
- if(optind+3 < argc) {
- options_p->destdir = argv[optind+3];
- options_p->destdirlen = strlen(options_p->destdir);
- }
- options_p->watchdir = argv[optind];
- options_p->watchdirlen = strlen(options_p->watchdir);*/
- if(optind+0 < argc) {
- options_p->watchdir = argv[optind];
- options_p->watchdirlen = strlen(options_p->watchdir);
- } else {
- options_p->watchdir = NULL;
- options_p->watchdirlen = 0;
- }
- if(optind+1 < argc) {
- options_p->handlerfpath = argv[optind+1];
- } else {
- options_p->handlerfpath = NULL;
- }
- if(optind+2 < argc) {
- options_p->rulfpath = argv[optind+2];
- if(!strcmp(options_p->rulfpath, ""))
- options_p->rulfpath = NULL;
- } else {
- options_p->rulfpath = NULL;
- }
- if(optind+3 < argc) {
- options_p->destdir = argv[optind+3];
- options_p->destdirlen = strlen(options_p->destdir);
- } else {
- options_p->destdir = NULL;
- options_p->destdirlen = 0;
- }
- return 0;
- }
- char *configs_parse_str[1<<8] = {0};
- void gkf_parse(options_t *options_p, GKeyFile *gkf) {
- struct option *lo_ptr = long_options;
- while(lo_ptr->name != NULL) {
- gchar *value = g_key_file_get_value(gkf, options_p->config_block, lo_ptr->name, NULL);
- if(value != NULL) {
- unsigned char val_char = lo_ptr->val&0xff;
- if(configs_parse_str[val_char])
- free(configs_parse_str[val_char]);
- configs_parse_str[val_char] = value;
- parse_parameter(options_p, lo_ptr->val, value, PS_CONFIG);
- }
- lo_ptr++;
- }
- return;
- }
- int configs_parse(options_t *options_p) {
- GKeyFile *gkf;
- gkf = g_key_file_new();
- if(options_p->config_path) {
- printf_d("Debug: configs_parse(): Trying config-file \"%s\"\n", options_p->config_path);
- if(!g_key_file_load_from_file(gkf, options_p->config_path, G_KEY_FILE_NONE, NULL)) {
- printf_d("Debug: configs_parse(): Cannot open/parse file \"%s\"\n", options_p->config_path);
- g_key_file_free(gkf);
- return -1;
- } else
- gkf_parse(options_p, gkf);
- } else {
- char *config_paths[] = CONFIG_PATHS;
- char **config_path_p = config_paths, *config_path_real = xmalloc(PATH_MAX);
- size_t config_path_real_size=PATH_MAX;
- char *homedir = getenv("HOME");
- size_t homedir_len = strlen(homedir);
- while(*config_path_p != NULL) {
- size_t config_path_len = strlen(*config_path_p);
- if(config_path_len+homedir_len+3 > config_path_real_size) {
- config_path_real_size = config_path_len+homedir_len+3;
- config_path_real = xmalloc(config_path_real_size);
- }
- if(*config_path_p[0] != '/') {
- memcpy(config_path_real, homedir, homedir_len);
- config_path_real[homedir_len] = '/';
- memcpy(&config_path_real[homedir_len+1], *config_path_p, config_path_len+1);
- } else
- memcpy(config_path_real, *config_path_p, config_path_len+1);
- printf_d("Debug: configs_parse(): Trying config-file \"%s\"\n", config_path_real);
- if(!g_key_file_load_from_file(gkf, config_path_real, G_KEY_FILE_NONE, NULL)) {
- printf_d("Debug: configs_parse(): Cannot open/parse file \"%s\"\n", config_path_real);
- config_path_p++;
- continue;
- }
- gkf_parse(options_p, gkf);
- break;
- }
- free(config_path_real);
- }
- g_key_file_free(gkf);
- return 0;
- }
- int configs_cleanup() {
- int i=0;
- while(i < (1<<8)) {
- if(configs_parse_str[i] != NULL) {
- free(configs_parse_str[i]);
- configs_parse_str[i] = NULL;
- }
- i++;
- }
- return 0;
- }
- int rule_complete(rule_t *rule_p, const char *expr) {
- printf_ddd("Debug3: rule_complete(): <%s>.\n", expr);
- #ifdef VERYPARANOID
- if(rule_p->mask == RA_NONE) {
- printf_e("Error: rule_complete(): Received a rule with rule_p->mask == 0x00. Exit.\n");
- return EINVAL;
- }
- #endif
- char buf[BUFSIZ];
- int ret = 0;
- if(rule_p->num >= MAXRULES) {
- printf_e("Error: Too many rules (%i >= %i).\n", rule_p->num, MAXRULES);
- return ENOMEM;
- }
- if((ret = regcomp(&rule_p->expr, expr, REG_EXTENDED | REG_NOSUB))) {
- regerror(ret, &rule_p->expr, buf, BUFSIZ);
- printf_e("Error: Invalid regexp pattern <%s>: %s (regex-errno: %i).\n", expr, buf, ret);
- return ret;
- }
- return ret;
- }
- int parse_rules_fromfile(options_t *options_p) {
- int ret = 0;
- char *rulfpath = options_p->rulfpath;
- rule_t *rules = options_p->rules;
- char *line_buf=NULL;
- FILE *f = fopen(rulfpath, "r");
-
- if(f == NULL) {
- rules->mask = RA_NONE; // Terminator. End of rules' chain.
- rules->perm = DEFAULT_RULES_PERM;
- printf_e("Error: Cannot open \"%s\" for reading: %s (errno: %i).\n", rulfpath, strerror(errno), errno);
- return errno;
- }
- GHashTable *autowrules_ht = g_hash_table_new_full(g_str_hash, g_str_equal, free, 0);
- int i=0;
- size_t linelen, size=0;
- while((linelen = getline(&line_buf, &size, f)) != -1) {
- if(linelen>1) {
- uint8_t sign = 0;
- char *line = line_buf;
- rule_t *rule;
- rule = &rules[i];
- #ifdef VERYPARANOID
- memset(rule, 0, sizeof(*rule));
- #endif
- rule->num = i++;
- line[--linelen] = 0;
- // Parsing the first character of the line
- switch(*line) {
- case '+':
- sign = RS_PERMIT;
- break;
- case '-':
- sign = RS_REJECT;
- break;
- case '#': // Comment?
- continue;
- default:
- printf_e("Error: Wrong rule action <%c>.\n", *line);
- return EINVAL;
- }
- line++;
- linelen--;
- // Parsing the second character of the line
- *line |= 0x20; // lower-casing
- // Default rule->mask and rule->perm
- // rule->mask - sets bitmask of operations that are affected by the rule
- // rule->perm - sets bitmask of permit/reject for every operation. Effect have only bits specified by the rule->mask.
- rule->mask = RA_ALL;
- switch(sign) {
- case RS_REJECT:
- rule->perm = RA_NONE;
- break;
- case RS_PERMIT:
- rule->perm = RA_ALL;
- break;
- }
- switch(*line) {
- case '*':
- rule->objtype = 0; // "0" - means "of any type"
- break;
- #ifdef DETAILED_FTYPE
- case 's':
- rule->objtype = S_IFSOCK;
- break;
- case 'l':
- rule->objtype = S_IFLNK;
- break;
- case 'b':
- rule->objtype = S_IFBLK;
- break;
- case 'c':
- rule->objtype = S_IFCHR;
- break;
- case 'p':
- rule->objtype = S_IFIFO;
- break;
- #endif
- case 'f':
- rule->objtype = S_IFREG;
- break;
- case 'd':
- rule->objtype = S_IFDIR;
- break;
- case 'w': // accept or reject walking to directory
- if(options_p->flags[RSYNC]) {
- printf_e("parse_rules_fromfile(): Warning: Used \"w\" rule in \"--rsync\" case."
- " This may cause unexpected problems.\n");
- }
- rule->objtype = S_IFDIR;
- rule->mask = RA_WALK;
- break;
- default:
- printf_e("parse_rules_fromfile(): Warning: Cannot parse the rule <%s>\n", &line[-1]);
- i--; // Don't adding the rule
- continue;
- }
- line++;
- linelen--;
- // Parsing the rest part of the line
- printf_d("Debug2: parse_rules_fromfile(): Rule #%i <%c> <%c> pattern <%s> (length: %i).\n", rule->num, line[-2], line[-1], line, linelen);
- if((ret=rule_complete(rule, line)))
- goto l_parse_rules_fromfile_end;
- // Post-processing:
- line--;
- linelen++;
- if(*line != 'w') {
- // processing --auto-add-rules-w
- if(options_p->flags[AUTORULESW] && (sign == RS_PERMIT)) {
- // Preparing to add appropriate w-rules
- char skip = 0;
- char *expr = alloca(linelen+2);
- memcpy(expr, line, linelen+1);
- size_t exprlen = linelen;
- // Making expr to be starting with '^'
- if(line[1] == '^') {
- expr++;
- exprlen--;
- } else
- *expr = '^';
- char *end;
- if(*line == 'd' || *line == '*') {
- // "d" rule already doing what we need, so we can skip the last level
- end = &expr[exprlen];
- if(end[-1] != '$')
- *(end++) = '$';
- *end = 0;
- // printf_ddd("Debug3: parse_rules_fromfile(): Don't adding w-rule for \"%s\" due to [*d]-rule for \"%s\"\n",
- // expr, &line[1]);
- g_hash_table_insert(autowrules_ht, strdup(expr), GINT_TO_POINTER(1));
- }
- if(!skip) {
- do {
- // Decreasing directory level and make the '$' ending
- end = strrchr(expr, '/');
- if(end != NULL) {
- if(end[-1] != '$')
- *(end++) = '$';
- *end = 0;
- exprlen = (size_t)(end - expr);
- } else {
- expr[1] = '$';
- expr[2] = 0;
- exprlen = 2;
- }
- // Checking if it not already set
- if(!g_hash_table_lookup(autowrules_ht, expr)) {
- // Switching to next rule:
- rule = &rules[i];
- rule->num = i++;
- // Adding the rule
- rule->objtype = S_IFDIR;
- rule->mask = RA_WALK;
- rule->perm = RA_WALK;
- printf_d("Debug2: parse_rules_fromfile(): Rule #%i <+> <w> pattern <%s> (length: %i) [auto].\n",
- rule->num, expr, exprlen);
- if((ret=rule_complete(rule, expr)))
- goto l_parse_rules_fromfile_end;
- g_hash_table_insert(autowrules_ht, strdup(expr), GINT_TO_POINTER(1));
- }
- } while(end != NULL);
- }
- }
- }
- }
- }
- l_parse_rules_fromfile_end:
- if(size)
- free(line_buf);
- fclose(f);
- rules[i].mask = RA_NONE; // Terminator. End of rules' chain.
- rules[i].perm = DEFAULT_RULES_PERM;
- g_hash_table_destroy(autowrules_ht);
- #ifdef _DEBUG
- printf_ddd("Debug3: parse_rules_fromfile(): Total:\n");
- i=0;
- do {
- printf_ddd("\t%i\t%i\t%p/%p\n", i, rules[i].objtype, (void *)(long)rules[i].perm, (void *)(long)rules[i].mask);
- i++;
- } while(rules[i].mask != RA_NONE);
- #endif
- return ret;
- }
- int becomedaemon() {
- int pid;
- signal(SIGPIPE, SIG_IGN);
- switch((pid = fork())) {
- case -1:
- printf_e("Error: Cannot fork(): %s (errno: %i).\n", strerror(errno), errno);
- return(errno);
- case 0:
- setsid();
- break;
- default:
- printf_d("Debug: fork()-ed, pid is %i.\n", pid);
- exit(0);
- }
- return 0;
- }
- int main_cleanup(options_t *options_p) {
- int i=0;
- while((i < MAXRULES) && (options_p->rules[i].mask != RA_NONE))
- regfree(&options_p->rules[i++].expr);
- printf_ddd("Debug3: main_cleanup(): %i %i %i %i\n", options_p->watchdirsize, options_p->watchdirwslashsize, options_p->destdirsize, options_p->destdirwslashsize);
- return 0;
- }
- int main_rehash(options_t *options_p) {
- printf_ddd("Debug3: main_rehash()\n");
- int ret=0;
- main_cleanup(options_p);
- if(options_p->rulfpath != NULL) {
- ret = parse_rules_fromfile(options_p);
- if(ret)
- printf_e("Error: main_rehash(): Got error from parse_rules_fromfile(): %s (errno: %i).\n", strerror(ret), ret);
- } else {
- options_p->rules[0].perm = DEFAULT_RULES_PERM;
- options_p->rules[0].mask = RA_NONE; // Terminator. End of rules.
- }
- return ret;
- }
- int main(int argc, char *argv[]) {
- struct options options;
- #ifdef CLUSTER_SUPPORT
- struct utsname utsname;
- #endif
- memset(&options, 0, sizeof(options));
- int ret = 0, nret;
- options.notifyengine = DEFAULT_NOTIFYENGINE;
- options.syncdelay = DEFAULT_SYNCDELAY;
- options._queues[QUEUE_NORMAL].collectdelay = DEFAULT_COLLECTDELAY;
- options._queues[QUEUE_BIGFILE].collectdelay= DEFAULT_BFILECOLLECTDELAY;
- options._queues[QUEUE_INSTANT].collectdelay= COLLECTDELAY_INSTANT;
- options.bfilethreshold = DEFAULT_BFILETHRESHOLD;
- options.label = DEFAULT_LABEL;
- options.rsyncinclimit = DEFAULT_RSYNC_INCLUDELINESLIMIT;
- options.synctimeout = DEFAULT_SYNCTIMEOUT;
- #ifdef CLUSTER_SUPPORT
- options.cluster_hash_dl_min = DEFAULT_CLUSTERHDLMIN;
- options.cluster_hash_dl_max = DEFAULT_CLUSTERHDLMAX;
- options.cluster_scan_dl_max = DEFAULT_CLUSTERSDLMAX;
- #endif
- options.config_block = DEFAULT_CONFIG_BLOCK;
- arguments_parse(argc, argv, &options);
- out_init(options.flags);
- nret = configs_parse(&options);
- if(!ret) ret = nret;
- out_init(options.flags);
- if(options.watchdir == NULL) {
- printf_e("Error: watchdir is not set.\n");
- ret = EINVAL;
- }
- if(options.handlerfpath == NULL) {
- printf_e("Error: sync-handler path is not set.\n");
- ret = EINVAL;
- }
- if(options.flags[SYNCHANDLER] && options.flags[RSYNC]) {
- printf_e("Error: Option \"--rsync\" cannot be used in conjunction with \"--synchandler-so-module\".\n");
- ret = EINVAL;
- }
- if(options.flags[SYNCHANDLER] && (options.listoutdir != NULL))
- printf_e("Warning: Option \"--dir-lists\" has no effect conjunction with \"--synchandler-so-module\".\n");
- if(options.flags[SYNCHANDLER] && (options.destdir != NULL))
- printf_e("Warning: Destination directory argument has no effect conjunction with \"--synchandler-so-module\".\n");
- if((options.flags[RSYNC]>1) && (options.destdir == NULL)) {
- printf_e("Error: Option \"-R2\" cannot be used without specifying \"destination directory\".\n");
- ret = EINVAL;
- }
- #ifdef CLUSTER_SUPPORT
- if((options.flags[RSYNC]>1) && (options.cluster_iface != NULL)) {
- printf_e("Error: Option \"-R2\" cannot be used in conjunction with \"--cluster-iface\".\n");
- ret = EINVAL;
- }
- if((options.cluster_iface == NULL) && ((options.cluster_mcastipaddr != NULL) || (options.cluster_nodename != NULL) || (options.cluster_timeout) || (options.cluster_mcastipport))) {
- printf_e("Error: Options \"--cluster-ip\", \"--cluster-node-name\", \"--cluster_timeout\" and/or \"cluster_ipport\" cannot be used without \"--cluster-iface\".\n");
- ret = EINVAL;
- }
- if(options.cluster_hash_dl_min > options.cluster_hash_dl_max) {
- printf_e("Error: \"--cluster-hash-dl-min\" cannot be greater than \"--cluster-hash-dl-max\".\n");
- ret = EINVAL;
- }
- if(options.cluster_hash_dl_max > options.cluster_scan_dl_max) {
- printf_e("Error: \"--cluster-hash-dl-max\" cannot be greater than \"--cluster-scan-dl-max\".\n");
- ret = EINVAL;
- }
- if(!options.cluster_timeout)
- options.cluster_timeout = DEFAULT_CLUSTERTIMEOUT;
- if(!options.cluster_mcastipport)
- options.cluster_mcastipport = DEFAULT_CLUSTERIPPORT;
- if(!options.cluster_mcastipaddr)
- options.cluster_mcastipaddr = DEFAULT_CLUSTERIPADDR;
- if(options.cluster_iface != NULL) {
- #ifndef _DEBUG
- printf_e("Error: Cluster subsystem is not implemented, yet. Sorry.\n");
- ret = EINVAL;
- #endif
- if(options.cluster_nodename == NULL) {
- if(!uname(&utsname))
- options.cluster_nodename = utsname.nodename;
- printf_d("Debug: cluster node name is: %s\n", options.cluster_nodename);
- }
- if(options.cluster_nodename == NULL) {
- printf_e("Error: Option \"--cluster-iface\" is set, but \"--cluster-node-name\" is not set and cannot get the nodename with uname().\n");
- ret = EINVAL;
- } else {
- options.cluster_nodename_len = strlen(options.cluster_nodename);
- }
- }
- #endif // CLUSTER_SUPPORT
- {
- char *rwatchdir = realpath(options.watchdir, NULL);
- if(rwatchdir == NULL) {
- printf_e("Error: main(): Got error while realpath() on \"%s\": %s (errno: %i) [#0].\n", options.watchdir, strerror(errno), errno);
- ret = errno;
- }
- if(!ret) {
- options.watchdir = rwatchdir;
- options.watchdirlen = strlen(options.watchdir);
- options.watchdirsize = options.watchdirlen;
- if(options.watchdirlen == 1) {
- printf_e("Error: watchdir is supposed to be not \"/\".\n");
- ret = EINVAL;
- }
- }
- if(!ret) {
- size_t size = options.watchdirlen + 2;
- char *newwatchdir = xmalloc(size);
- memcpy( newwatchdir, options.watchdir, options.watchdirlen);
- options.watchdirwslash = newwatchdir;
- options.watchdirwslashsize = size;
- memcpy(&options.watchdirwslash[options.watchdirlen], "/", 2);
- options.watchdir_dirlevel = fileutils_calcdirlevel(options.watchdirwslash);
- }
- }
- if(options.destdir != NULL) {
- char *rdestdir = realpath(options.destdir, NULL);
- if(rdestdir == NULL) {
- printf_e("Error: main(): Got error while realpath() on \"%s\": %s (errno: %i) [#1].\n", options.destdir, strerror(errno), errno);
- ret = errno;
- }
- if(!ret) {
- options.destdir = rdestdir;
- options.destdirlen = strlen(options.destdir);
- options.destdirsize = options.destdirlen;
- if(options.destdirlen == 1) {
- printf_e("Error: destdir is supposed to be not \"/\".\n");
- ret = EINVAL;
- }
- }
- if(!ret) {
- size_t size = options.destdirlen + 2;
- char *newdestdir = xmalloc(size);
- memcpy( newdestdir, options.destdir, options.destdirlen);
- options.destdirwslash = newdestdir;
- options.destdirwslashsize = size;
- memcpy(&options.destdirwslash[options.destdirlen], "/", 2);
- }
- }
- printf_d("Debug: %s [%s] (%p) -> %s [%s]\n", options.watchdir, options.watchdirwslash, options.watchdirwslash, options.destdir?options.destdir:"", options.destdirwslash?options.destdirwslash:"");
- if(options.flags[RSYNC] && (options.listoutdir == NULL)) {
- printf_e("Error: Option \"--rsync\" cannot be used without \"--outlistsdir\".\n");
- ret = EINVAL;
- }
- if(options.flags[RSYNC_PREFERINCLUDE] && (!options.flags[RSYNC]))
- printf_e("Warning: Option \"--rsyncpreferinclude\" is useless without \"--rsync\".\n");
- if(options.flags[RSYNC] && options.flags[AUTORULESW])
- printf_e("Warning: Option \"--auto-add-rules-w\" in conjunction with \"--rsync\" may cause unexpected problems.\n");
- if(options.flags[DEBUG])
- debug_print_flags();
- if(options.listoutdir != NULL) {
- struct stat st={0};
- errno = 0;
- if(stat(options.listoutdir, &st)) {
- if(errno == ENOENT) {
- printf_e("Warning: Directory \"%s\" doesn't exist. Creating it.\n", options.listoutdir);
- errno = 0;
- if(mkdir(options.listoutdir, S_IRWXU)) {
- printf_e("Error: main(): Cannot create directory \"%s\": %s (%i)", options.listoutdir, strerror(errno), errno);
- ret = errno;
- }
- } else {
- printf_e("Error: main(): Got error while stat() on \"%s\": %s (errno: %i).\n", options.listoutdir, strerror(errno), errno);
- ret = errno;
- }
- }
- if(!errno)
- if(st.st_mode & (S_IRWXG|S_IRWXO)) {
- #ifdef PARANOID
- printf_e("Error: Insecure: Others have access to directory \"%s\". Exit.\n", options.listoutdir);
- ret = EACCES;
- #else
- printf_e("Warning: Insecure: Others have access to directory \"%s\".\n", options.listoutdir);
- #endif
- }
- }
- if(options.flags[ENABLEINITIALSYNC] && (options.listoutdir == NULL)) {
- printf_e("Error: main(): Option \"--dir-lists\" should be set to use option \"--initialsync-enable\".\n");
- ret = EINVAL;
- }
- if(options.flags[ENABLEINITIALSYNC] && options.flags[RSYNC]) {
- printf_e("Error: main(): Options \"--initialsync-enable\" and \"--rsync\" are incompatable.\n");
- ret = EINVAL;
- }
- if(options.flags[SYNCLISTSIMPLIFY] && (options.listoutdir == NULL)) {
- printf_e("Error: main(): Option \"--dir-lists\" should be set to use option \"--synclist-simplify\".\n");
- ret = EINVAL;
- }
- if(options.flags[SYNCLISTSIMPLIFY] && options.flags[RSYNC]) {
- printf_e("Error: main(): Options \"--synclist-simplify\" and \"--rsync\" are incompatable.\n");
- ret = EINVAL;
- }
- #ifdef FANOTIFY_SUPPORT
- if(options.notifyengine != NE_INOTIFY) {
- printf_e("Warning: fanotify is not fully supported, yet!\n");
- }
- #endif
- if(access(options.handlerfpath, X_OK) == -1) {
- printf_e("Error: \"%s\" is not executable: %s (errno: %i).\n", options.handlerfpath, strerror(errno), errno);
- ret = errno;
- }
- #ifdef VERYPARANOID
- {
- struct stat64 stat64={0};
- if(lstat64(options.watchdir, &stat64)) {
- printf_e("Error: main(): Cannot lstat64() on \"%s\": %s (errno: %i)\n", options.watchdir, strerror(errno), errno);
- ret = errno;
- } else {
- if((stat64.st_mode & S_IFMT) == S_IFLNK) {
- // The proplems may be due to FTS_PHYSICAL option of ftp_open() in sync_initialsync_rsync_walk(),
- // so if the "watch dir" is just a symlink it doesn't walk recursivly. For example, in "-R" case
- // it disables filters, because exclude-list will be empty.
- printf_e("Error: Watch dir cannot be symlink, but \"%s\" is a symlink.\n", options.watchdir);
- ret = EINVAL;
- }
- }
- }
- #endif
- nret=main_rehash(&options);
- if(nret)
- ret = nret;
- if(options.flags[BACKGROUND]) {
- nret = becomedaemon();
- if(nret)
- ret = nret;
- }
- #ifdef HAVE_CAPABILITIES
- if(options.flags[CAP_PRESERVE_FILEACCESS]) {
- // Doesn't work, yet :(
- //
- // Error: Cannot inotify_add_watch() on "/home/xaionaro/clsync/examples/testdir/from": Permission denied (errno: 13).
- printf_d("Debug: main(): Preserving access to files with using linux capabilites\n");
- struct __user_cap_header_struct cap_hdr = {0};
- struct __user_cap_data_struct cap_dat = {0};
- cap_hdr.version = _LINUX_CAPABILITY_VERSION;
- if(capget(&cap_hdr, &cap_dat) < 0) {
- printf_e("Error: main() cannot get capabilites with capget(): %s (errno: %i)\n", strerror(errno), errno);
- ret = errno;
- goto preserve_fileaccess_end;
- }
- // From "man 7 capabilities":
- // CAP_DAC_OVERRIDE - Bypass file read, write, and execute permission checks.
- // CAP_DAC_READ_SEARCH - Bypass file read permission checks and directory read and execute permission checks.
- 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));
- 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));
- cap_dat.inheritable = 0;
- printf_ddd("Debug3: main(): cap.eff == %p; cap.prm == %p.\n",
- (void *)(long)cap_dat.effective, (void *)(long)cap_dat.permitted);
- if(capset(&cap_hdr, &cap_dat) < 0) {
- printf_e("Error: main(): Cannot set capabilities with capset(): %s (errno: %i).\n", strerror(errno), errno);
- ret = errno;
- goto preserve_fileaccess_end;
- }
- // Tell kernel not clear capabilities when dropping root
- if(prctl(PR_SET_KEEPCAPS, 1) < 0) {
- printf_e("Error: main(): Cannot prctl(PR_SET_KEEPCAPS, 1) to preserve capabilities: %s (errno: %i)\n",
- strerror(errno), errno);
- ret = errno;
- goto preserve_fileaccess_end;
- }
- }
- preserve_fileaccess_end:
- #endif
- if(options.flags[UID]) {
- if(setuid(options.uid)) {
- printf_e("Error: main(): Cannot setuid(%u): %s (errno: %i)\n", options.uid, strerror(errno), errno);
- ret = errno;
- }
- }
- if(options.flags[GID]) {
- if(setuid(options.gid)) {
- printf_e("Error: main(): Cannot setgid(%u): %s (errno: %i)\n", options.gid, strerror(errno), errno);
- ret = errno;
- }
- }
- if(options.pidfile != NULL) {
- pid_t pid = getpid();
- FILE *pidfile = fopen(options.pidfile, "w");
- if(pidfile == NULL) {
- printf_e("Error: main(): Cannot open file \"%s\" to write a pid there: %s (errno: %i)\n",
- options.pidfile, strerror(errno), errno);
- ret = errno;
- } else {
- if(fprintf(pidfile, "%u\n", pid) < 0) {
- printf_e("Error: main(): Cannot write pid into file \"%s\": %s (errno: %i)\n",
- options.pidfile, strerror(errno), errno);
- ret = errno;
- }
- fclose(pidfile);
- }
- }
- printf_ddd("Debug3: main(): Current errno is %i.\n", ret);
- if(ret == 0)
- ret = sync_run(&options);
- if(options.pidfile != NULL) {
- if(unlink(options.pidfile)) {
- printf_e("Error: main(): Cannot unlink pidfile \"%s\": %s (errno: %i)\n",
- options.pidfile, strerror(errno), errno);
- ret = errno;
- }
- }
- main_cleanup(&options);
- if(options.watchdirsize)
- free(options.watchdir);
- if(options.watchdirwslashsize)
- free(options.watchdirwslash);
- if(options.destdirsize)
- free(options.destdir);
- if(options.destdirwslashsize)
- free(options.destdirwslash);
- configs_cleanup();
- out_flush();
- printf_d("Debug: finished, exitcode: %i: %s.\n", ret, strerror(ret));
- out_flush();
- out_deinit();
- return ret;
- }
|