main.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  1. /*
  2. clsync - file tree sync utility based on fanotify and inotify
  3. Copyright (C) 2013 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include "common.h"
  16. #include "output.h"
  17. #include "sync.h"
  18. #include "malloc.h"
  19. #include "cluster.h"
  20. #include "fileutils.h"
  21. #include "revision.h"
  22. static struct option long_options[] =
  23. {
  24. {"background", optional_argument, NULL, BACKGROUND},
  25. {"config-path", required_argument, NULL, CONFIGPATH},
  26. {"config-block", required_argument, NULL, CONFIGBLOCK},
  27. {"pid-file", required_argument, NULL, PIDFILE},
  28. {"uid", required_argument, NULL, UID},
  29. {"gid", required_argument, NULL, GID},
  30. #ifdef HAVE_CAPABILITIES
  31. {"preserve-file-access",optional_argument, NULL, CAP_PRESERVE_FILEACCESS},
  32. #endif
  33. {"pthread", optional_argument, NULL, PTHREAD},
  34. {"syslog", optional_argument, NULL, SYSLOG},
  35. #ifdef CLUSTER_SUPPORT
  36. {"cluster-iface", required_argument, NULL, CLUSTERIFACE}, // Not implemented, yet
  37. {"cluster-ip", required_argument, NULL, CLUSTERMCASTIPADDR}, // Not implemented, yet
  38. {"cluster-port", required_argument, NULL, CLUSTERMCASTIPPORT}, // Not implemented, yet
  39. {"cluster-timeout", required_argument, NULL, CLUSTERTIMEOUT}, // Not implemented, yet
  40. {"cluster-node-name", required_argument, NULL, CLUSTERNODENAME}, // Not implemented, yet
  41. {"cluster-hash-dl-min", required_argument, NULL, CLUSTERHDLMIN},
  42. {"cluster-hash-dl-max", required_argument, NULL, CLUSTERHDLMAX},
  43. {"cluster-scan-dl-max", required_argument, NULL, CLUSTERSDLMAX},
  44. #endif
  45. {"timeout-sync", required_argument, NULL, SYNCTIMEOUT},
  46. {"delay-sync", required_argument, NULL, SYNCDELAY},
  47. {"delay-collect", required_argument, NULL, DELAY},
  48. {"delay-collect-bigfile",required_argument, NULL, BFILEDELAY},
  49. {"threshold-bigfile", required_argument, NULL, BFILETHRESHOLD},
  50. {"dir-lists", required_argument, NULL, OUTLISTSDIR},
  51. {"initialsync-enable", optional_argument, NULL, ENABLEINITIALSYNC},
  52. {"synclist-simplify", optional_argument, NULL, SYNCLISTSIMPLIFY},
  53. {"auto-add-rules-w", optional_argument, NULL, AUTORULESW},
  54. {"synchandler-so-module",optional_argument, NULL, SYNCHANDLERSO},
  55. {"rsync", optional_argument, NULL, RSYNC},
  56. {"rsync-inclimit", required_argument, NULL, RSYNCINCLIMIT},
  57. {"rsync-prefer-include",optional_argument, NULL, RSYNC_PREFERINCLUDE},
  58. {"ignore-exitcode", required_argument, NULL, IGNOREEXITCODE},
  59. {"dont-unlink-lists", optional_argument, NULL, DONTUNLINK},
  60. {"full-initialsync", optional_argument, NULL, INITFULL},
  61. {"verbose", optional_argument, NULL, VERBOSE},
  62. {"debug", optional_argument, NULL, DEBUG},
  63. {"quiet", optional_argument, NULL, QUIET},
  64. #ifdef FANOTIFY_SUPPORT
  65. {"fanotify", optional_argument, NULL, FANOTIFY},
  66. {"inotify", optional_argument, NULL, INOTIFY},
  67. #endif
  68. {"label", required_argument, NULL, LABEL},
  69. {"help", optional_argument, NULL, HELP},
  70. {"version", optional_argument, NULL, SHOW_VERSION},
  71. {"watch-dir", required_argument, NULL, WATCHDIR},
  72. {"sync-handler", required_argument, NULL, SYNCHANDLER},
  73. {"rules-path", required_argument, NULL, RULESPATH},
  74. {"dir-destination", required_argument, NULL, DESTDIR},
  75. {NULL, 0, NULL, 0}
  76. };
  77. int syntax() {
  78. printf("syntax: clsync [flags] <watch dir> <action script> [file with rules regexps] [destination directory]\npossible options:\n");
  79. int i=0;
  80. while(long_options[i].name != NULL) {
  81. if(!(long_options[i].val & OPTION_CONFIGONLY))
  82. printf("\t--%-24s-%c%s\n", long_options[i].name, long_options[i].val, (long_options[i].has_arg == required_argument ? " argument" : ""));
  83. i++;
  84. }
  85. exit(EINVAL);
  86. }
  87. int version() {
  88. printf(PROGRAM" v%i.%i"REVISION"\n\t"AUTHOR"\n", VERSION_MAJ, VERSION_MIN);
  89. exit(0);
  90. }
  91. static inline int parse_parameter(options_t *options_p, uint16_t param_id, char *arg, paramsource_t paramsource) {
  92. #ifdef _DEBUG
  93. fprintf(stderr, "Force-Debug: parse_parameter(): %i: %i = \"%s\"\n", paramsource, param_id, arg);
  94. #endif
  95. switch(paramsource) {
  96. case PS_ARGUMENT:
  97. if(param_id & OPTION_CONFIGONLY) {
  98. syntax();
  99. return 0;
  100. }
  101. options_p->flags_set[param_id] = 1;
  102. break;
  103. case PS_CONFIG:
  104. if(options_p->flags_set[param_id])
  105. return 0;
  106. break;
  107. default:
  108. printf_e("Warning: Unknown parameter #%i source (value \"%s\").\n", param_id, arg!=NULL ? arg : "");
  109. break;
  110. }
  111. switch(param_id) {
  112. case '?':
  113. case HELP:
  114. syntax();
  115. break;
  116. case CONFIGPATH:
  117. options_p->config_path = arg;
  118. break;
  119. case CONFIGBLOCK:
  120. options_p->config_block = arg;
  121. break;
  122. case GID:
  123. options_p->gid = (unsigned int)atol(arg);
  124. options_p->flags[param_id]++;
  125. break;
  126. case UID:
  127. options_p->uid = (unsigned int)atol(arg);
  128. options_p->flags[param_id]++;
  129. break;
  130. case PIDFILE:
  131. options_p->pidfile = arg;
  132. break;
  133. #ifdef CLUSTER_SUPPORT
  134. case CLUSTERIFACE:
  135. options_p->cluster_iface = arg;
  136. break;
  137. case CLUSTERMCASTIPADDR:
  138. options_p->cluster_mcastipaddr = arg;
  139. break;
  140. case CLUSTERMCASTIPPORT:
  141. options_p->cluster_mcastipport = (uint16_t)atoi(arg);
  142. break;
  143. case CLUSTERTIMEOUT:
  144. options_p->cluster_timeout = (unsigned int)atol(arg);
  145. break;
  146. case CLUSTERNODENAME:
  147. options_p->cluster_nodename = arg;
  148. break;
  149. case CLUSTERHDLMIN:
  150. options_p->cluster_hash_dl_min = (uint16_t)atoi(arg);
  151. break;
  152. case CLUSTERHDLMAX:
  153. options_p->cluster_hash_dl_max = (uint16_t)atoi(arg);
  154. break;
  155. case CLUSTERSDLMAX:
  156. options_p->cluster_scan_dl_max = (uint16_t)atoi(arg);
  157. break;
  158. #endif
  159. case OUTLISTSDIR:
  160. options_p->listoutdir = arg;
  161. break;
  162. case LABEL:
  163. options_p->label = arg;
  164. break;
  165. case SYNCDELAY:
  166. options_p->syncdelay = (unsigned int)atol(arg);
  167. break;
  168. case DELAY:
  169. options_p->_queues[QUEUE_NORMAL].collectdelay = (unsigned int)atol(arg);
  170. break;
  171. case BFILEDELAY:
  172. options_p->_queues[QUEUE_BIGFILE].collectdelay = (unsigned int)atol(arg);
  173. break;
  174. case BFILETHRESHOLD:
  175. options_p->bfilethreshold = (unsigned long)atol(arg);
  176. break;
  177. #ifdef FANOTIFY_SUPPORT
  178. case FANOTIFY:
  179. options_p->notifyengine = NE_FANOTIFY;
  180. break;
  181. #endif
  182. case INOTIFY:
  183. options_p->notifyengine = NE_INOTIFY;
  184. break;
  185. case RSYNCINCLIMIT:
  186. options_p->rsyncinclimit = (unsigned int)atol(arg);
  187. break;
  188. case SYNCTIMEOUT:
  189. options_p->synctimeout = (unsigned int)atol(arg);
  190. break;
  191. case IGNOREEXITCODE:
  192. options_p->isignoredexitcode[(unsigned char)atoi(arg)] = 1;
  193. break;
  194. case SHOW_VERSION:
  195. version();
  196. break;
  197. case WATCHDIR:
  198. options_p->watchdir = arg;
  199. break;
  200. case SYNCHANDLER:
  201. options_p->handlerfpath = arg;
  202. break;
  203. case RULESPATH:
  204. options_p->rulfpath = arg;
  205. break;
  206. case DESTDIR:
  207. options_p->destdir = arg;
  208. break;
  209. default:
  210. if(arg == NULL)
  211. options_p->flags[param_id]++;
  212. else
  213. options_p->flags[param_id] = atoi(arg);
  214. #ifdef _DEBUG
  215. fprintf(stderr, "Force-Debug: flag %i is set to %i\n", param_id&0xff, options_p->flags[param_id]);
  216. #endif
  217. break;
  218. }
  219. return 0;
  220. }
  221. int arguments_parse(int argc, char *argv[], struct options *options_p) {
  222. int c;
  223. int option_index = 0;
  224. // Generating "optstring" (man 3 getopt_long) with using information from struct array "long_options"
  225. char *optstring = alloca((('z'-'a'+1)*3 + '9'-'0'+1)*3 + 1);
  226. char *optstring_ptr = optstring;
  227. struct option *lo_ptr = long_options;
  228. while(lo_ptr->name != NULL) {
  229. if(!(lo_ptr->val & OPTION_CONFIGONLY)) {
  230. *(optstring_ptr++) = lo_ptr->val & 0xff;
  231. if(lo_ptr->has_arg == required_argument)
  232. *(optstring_ptr++) = ':';
  233. if(lo_ptr->has_arg == optional_argument) {
  234. *(optstring_ptr++) = ':';
  235. *(optstring_ptr++) = ':';
  236. }
  237. }
  238. lo_ptr++;
  239. }
  240. *optstring_ptr = 0;
  241. #ifdef _DEBUG
  242. fprintf(stderr, "Force-Debug: %s\n", optstring);
  243. #endif
  244. // Parsing arguments
  245. while(1) {
  246. c = getopt_long(argc, argv, optstring, long_options, &option_index);
  247. if (c == -1) break;
  248. parse_parameter(options_p, c, optarg, PS_ARGUMENT);
  249. }
  250. /*
  251. if(optind+1 >= argc)
  252. syntax();
  253. options_p->handlerfpath = argv[optind+1];
  254. if(optind+2 < argc) {
  255. options_p->rulfpath = argv[optind+2];
  256. if(!strcmp(options_p->rulfpath, ""))
  257. options_p->rulfpath = NULL;
  258. }
  259. if(optind+3 < argc) {
  260. options_p->destdir = argv[optind+3];
  261. options_p->destdirlen = strlen(options_p->destdir);
  262. }
  263. options_p->watchdir = argv[optind];
  264. options_p->watchdirlen = strlen(options_p->watchdir);*/
  265. if(optind+0 < argc) {
  266. options_p->watchdir = argv[optind];
  267. options_p->watchdirlen = strlen(options_p->watchdir);
  268. } else {
  269. options_p->watchdir = NULL;
  270. options_p->watchdirlen = 0;
  271. }
  272. if(optind+1 < argc) {
  273. options_p->handlerfpath = argv[optind+1];
  274. } else {
  275. options_p->handlerfpath = NULL;
  276. }
  277. if(optind+2 < argc) {
  278. options_p->rulfpath = argv[optind+2];
  279. if(!strcmp(options_p->rulfpath, ""))
  280. options_p->rulfpath = NULL;
  281. } else {
  282. options_p->rulfpath = NULL;
  283. }
  284. if(optind+3 < argc) {
  285. options_p->destdir = argv[optind+3];
  286. options_p->destdirlen = strlen(options_p->destdir);
  287. } else {
  288. options_p->destdir = NULL;
  289. options_p->destdirlen = 0;
  290. }
  291. return 0;
  292. }
  293. char *configs_parse_str[1<<8] = {0};
  294. void gkf_parse(options_t *options_p, GKeyFile *gkf) {
  295. struct option *lo_ptr = long_options;
  296. while(lo_ptr->name != NULL) {
  297. gchar *value = g_key_file_get_value(gkf, options_p->config_block, lo_ptr->name, NULL);
  298. if(value != NULL) {
  299. unsigned char val_char = lo_ptr->val&0xff;
  300. if(configs_parse_str[val_char])
  301. free(configs_parse_str[val_char]);
  302. configs_parse_str[val_char] = value;
  303. parse_parameter(options_p, lo_ptr->val, value, PS_CONFIG);
  304. }
  305. lo_ptr++;
  306. }
  307. return;
  308. }
  309. int configs_parse(options_t *options_p) {
  310. GKeyFile *gkf;
  311. gkf = g_key_file_new();
  312. if(options_p->config_path) {
  313. printf_d("Debug: configs_parse(): Trying config-file \"%s\"\n", options_p->config_path);
  314. if(!g_key_file_load_from_file(gkf, options_p->config_path, G_KEY_FILE_NONE, NULL)) {
  315. printf_d("Debug: configs_parse(): Cannot open/parse file \"%s\"\n", options_p->config_path);
  316. g_key_file_free(gkf);
  317. return -1;
  318. } else
  319. gkf_parse(options_p, gkf);
  320. } else {
  321. char *config_paths[] = CONFIG_PATHS;
  322. char **config_path_p = config_paths, *config_path_real = xmalloc(PATH_MAX);
  323. size_t config_path_real_size=PATH_MAX;
  324. char *homedir = getenv("HOME");
  325. size_t homedir_len = strlen(homedir);
  326. while(*config_path_p != NULL) {
  327. size_t config_path_len = strlen(*config_path_p);
  328. if(config_path_len+homedir_len+3 > config_path_real_size) {
  329. config_path_real_size = config_path_len+homedir_len+3;
  330. config_path_real = xmalloc(config_path_real_size);
  331. }
  332. if(*config_path_p[0] != '/') {
  333. memcpy(config_path_real, homedir, homedir_len);
  334. config_path_real[homedir_len] = '/';
  335. memcpy(&config_path_real[homedir_len+1], *config_path_p, config_path_len+1);
  336. } else
  337. memcpy(config_path_real, *config_path_p, config_path_len+1);
  338. printf_d("Debug: configs_parse(): Trying config-file \"%s\"\n", config_path_real);
  339. if(!g_key_file_load_from_file(gkf, config_path_real, G_KEY_FILE_NONE, NULL)) {
  340. printf_d("Debug: configs_parse(): Cannot open/parse file \"%s\"\n", config_path_real);
  341. config_path_p++;
  342. continue;
  343. }
  344. gkf_parse(options_p, gkf);
  345. break;
  346. }
  347. free(config_path_real);
  348. }
  349. g_key_file_free(gkf);
  350. return 0;
  351. }
  352. int configs_cleanup() {
  353. int i=0;
  354. while(i < (1<<8)) {
  355. if(configs_parse_str[i] != NULL) {
  356. free(configs_parse_str[i]);
  357. configs_parse_str[i] = NULL;
  358. }
  359. i++;
  360. }
  361. return 0;
  362. }
  363. int rule_complete(rule_t *rule_p, const char *expr) {
  364. printf_ddd("Debug3: rule_complete(): <%s>.\n", expr);
  365. #ifdef VERYPARANOID
  366. if(rule_p->mask == RA_NONE) {
  367. printf_e("Error: rule_complete(): Received a rule with rule_p->mask == 0x00. Exit.\n");
  368. return EINVAL;
  369. }
  370. #endif
  371. char buf[BUFSIZ];
  372. int ret = 0;
  373. if(rule_p->num >= MAXRULES) {
  374. printf_e("Error: Too many rules (%i >= %i).\n", rule_p->num, MAXRULES);
  375. return ENOMEM;
  376. }
  377. if((ret = regcomp(&rule_p->expr, expr, REG_EXTENDED | REG_NOSUB))) {
  378. regerror(ret, &rule_p->expr, buf, BUFSIZ);
  379. printf_e("Error: Invalid regexp pattern <%s>: %s (regex-errno: %i).\n", expr, buf, ret);
  380. return ret;
  381. }
  382. return ret;
  383. }
  384. int parse_rules_fromfile(options_t *options_p) {
  385. int ret = 0;
  386. char *rulfpath = options_p->rulfpath;
  387. rule_t *rules = options_p->rules;
  388. char *line_buf=NULL;
  389. FILE *f = fopen(rulfpath, "r");
  390. if(f == NULL) {
  391. rules->mask = RA_NONE; // Terminator. End of rules' chain.
  392. rules->perm = DEFAULT_RULES_PERM;
  393. printf_e("Error: Cannot open \"%s\" for reading: %s (errno: %i).\n", rulfpath, strerror(errno), errno);
  394. return errno;
  395. }
  396. GHashTable *autowrules_ht = g_hash_table_new_full(g_str_hash, g_str_equal, free, 0);
  397. int i=0;
  398. size_t linelen, size=0;
  399. while((linelen = getline(&line_buf, &size, f)) != -1) {
  400. if(linelen>1) {
  401. uint8_t sign = 0;
  402. char *line = line_buf;
  403. rule_t *rule;
  404. rule = &rules[i];
  405. #ifdef VERYPARANOID
  406. memset(rule, 0, sizeof(*rule));
  407. #endif
  408. rule->num = i++;
  409. line[--linelen] = 0;
  410. // Parsing the first character of the line
  411. switch(*line) {
  412. case '+':
  413. sign = RS_PERMIT;
  414. break;
  415. case '-':
  416. sign = RS_REJECT;
  417. break;
  418. case '#': // Comment?
  419. continue;
  420. default:
  421. printf_e("Error: Wrong rule action <%c>.\n", *line);
  422. return EINVAL;
  423. }
  424. line++;
  425. linelen--;
  426. // Parsing the second character of the line
  427. *line |= 0x20; // lower-casing
  428. // Default rule->mask and rule->perm
  429. // rule->mask - sets bitmask of operations that are affected by the rule
  430. // rule->perm - sets bitmask of permit/reject for every operation. Effect have only bits specified by the rule->mask.
  431. rule->mask = RA_ALL;
  432. switch(sign) {
  433. case RS_REJECT:
  434. rule->perm = RA_NONE;
  435. break;
  436. case RS_PERMIT:
  437. rule->perm = RA_ALL;
  438. break;
  439. }
  440. switch(*line) {
  441. case '*':
  442. rule->objtype = 0; // "0" - means "of any type"
  443. break;
  444. #ifdef DETAILED_FTYPE
  445. case 's':
  446. rule->objtype = S_IFSOCK;
  447. break;
  448. case 'l':
  449. rule->objtype = S_IFLNK;
  450. break;
  451. case 'b':
  452. rule->objtype = S_IFBLK;
  453. break;
  454. case 'c':
  455. rule->objtype = S_IFCHR;
  456. break;
  457. case 'p':
  458. rule->objtype = S_IFIFO;
  459. break;
  460. #endif
  461. case 'f':
  462. rule->objtype = S_IFREG;
  463. break;
  464. case 'd':
  465. rule->objtype = S_IFDIR;
  466. break;
  467. case 'w': // accept or reject walking to directory
  468. if(options_p->flags[RSYNC]) {
  469. printf_e("parse_rules_fromfile(): Warning: Used \"w\" rule in \"--rsync\" case."
  470. " This may cause unexpected problems.\n");
  471. }
  472. rule->objtype = S_IFDIR;
  473. rule->mask = RA_WALK;
  474. break;
  475. default:
  476. printf_e("parse_rules_fromfile(): Warning: Cannot parse the rule <%s>\n", &line[-1]);
  477. i--; // Don't adding the rule
  478. continue;
  479. }
  480. line++;
  481. linelen--;
  482. // Parsing the rest part of the line
  483. printf_d("Debug2: parse_rules_fromfile(): Rule #%i <%c> <%c> pattern <%s> (length: %i).\n", rule->num, line[-2], line[-1], line, linelen);
  484. if((ret=rule_complete(rule, line)))
  485. goto l_parse_rules_fromfile_end;
  486. // Post-processing:
  487. line--;
  488. linelen++;
  489. if(*line != 'w') {
  490. // processing --auto-add-rules-w
  491. if(options_p->flags[AUTORULESW] && (sign == RS_PERMIT)) {
  492. // Preparing to add appropriate w-rules
  493. char skip = 0;
  494. char *expr = alloca(linelen+2);
  495. memcpy(expr, line, linelen+1);
  496. size_t exprlen = linelen;
  497. // Making expr to be starting with '^'
  498. if(line[1] == '^') {
  499. expr++;
  500. exprlen--;
  501. } else
  502. *expr = '^';
  503. char *end;
  504. if(*line == 'd' || *line == '*') {
  505. // "d" rule already doing what we need, so we can skip the last level
  506. end = &expr[exprlen];
  507. if(end[-1] != '$')
  508. *(end++) = '$';
  509. *end = 0;
  510. // printf_ddd("Debug3: parse_rules_fromfile(): Don't adding w-rule for \"%s\" due to [*d]-rule for \"%s\"\n",
  511. // expr, &line[1]);
  512. g_hash_table_insert(autowrules_ht, strdup(expr), GINT_TO_POINTER(1));
  513. }
  514. if(!skip) {
  515. do {
  516. // Decreasing directory level and make the '$' ending
  517. end = strrchr(expr, '/');
  518. if(end != NULL) {
  519. if(end[-1] != '$')
  520. *(end++) = '$';
  521. *end = 0;
  522. exprlen = (size_t)(end - expr);
  523. } else {
  524. expr[1] = '$';
  525. expr[2] = 0;
  526. exprlen = 2;
  527. }
  528. // Checking if it not already set
  529. if(!g_hash_table_lookup(autowrules_ht, expr)) {
  530. // Switching to next rule:
  531. rule = &rules[i];
  532. rule->num = i++;
  533. // Adding the rule
  534. rule->objtype = S_IFDIR;
  535. rule->mask = RA_WALK;
  536. rule->perm = RA_WALK;
  537. printf_d("Debug2: parse_rules_fromfile(): Rule #%i <+> <w> pattern <%s> (length: %i) [auto].\n",
  538. rule->num, expr, exprlen);
  539. if((ret=rule_complete(rule, expr)))
  540. goto l_parse_rules_fromfile_end;
  541. g_hash_table_insert(autowrules_ht, strdup(expr), GINT_TO_POINTER(1));
  542. }
  543. } while(end != NULL);
  544. }
  545. }
  546. }
  547. }
  548. }
  549. l_parse_rules_fromfile_end:
  550. if(size)
  551. free(line_buf);
  552. fclose(f);
  553. rules[i].mask = RA_NONE; // Terminator. End of rules' chain.
  554. rules[i].perm = DEFAULT_RULES_PERM;
  555. g_hash_table_destroy(autowrules_ht);
  556. #ifdef _DEBUG
  557. printf_ddd("Debug3: parse_rules_fromfile(): Total:\n");
  558. i=0;
  559. do {
  560. printf_ddd("\t%i\t%i\t%p/%p\n", i, rules[i].objtype, (void *)(long)rules[i].perm, (void *)(long)rules[i].mask);
  561. i++;
  562. } while(rules[i].mask != RA_NONE);
  563. #endif
  564. return ret;
  565. }
  566. int becomedaemon() {
  567. int pid;
  568. signal(SIGPIPE, SIG_IGN);
  569. switch((pid = fork())) {
  570. case -1:
  571. printf_e("Error: Cannot fork(): %s (errno: %i).\n", strerror(errno), errno);
  572. return(errno);
  573. case 0:
  574. setsid();
  575. break;
  576. default:
  577. printf_d("Debug: fork()-ed, pid is %i.\n", pid);
  578. exit(0);
  579. }
  580. return 0;
  581. }
  582. int main_cleanup(options_t *options_p) {
  583. int i=0;
  584. while((i < MAXRULES) && (options_p->rules[i].mask != RA_NONE))
  585. regfree(&options_p->rules[i++].expr);
  586. printf_ddd("Debug3: main_cleanup(): %i %i %i %i\n", options_p->watchdirsize, options_p->watchdirwslashsize, options_p->destdirsize, options_p->destdirwslashsize);
  587. return 0;
  588. }
  589. int main_rehash(options_t *options_p) {
  590. printf_ddd("Debug3: main_rehash()\n");
  591. int ret=0;
  592. main_cleanup(options_p);
  593. if(options_p->rulfpath != NULL) {
  594. ret = parse_rules_fromfile(options_p);
  595. if(ret)
  596. printf_e("Error: main_rehash(): Got error from parse_rules_fromfile(): %s (errno: %i).\n", strerror(ret), ret);
  597. } else {
  598. options_p->rules[0].perm = DEFAULT_RULES_PERM;
  599. options_p->rules[0].mask = RA_NONE; // Terminator. End of rules.
  600. }
  601. return ret;
  602. }
  603. int main(int argc, char *argv[]) {
  604. struct options options;
  605. #ifdef CLUSTER_SUPPORT
  606. struct utsname utsname;
  607. #endif
  608. memset(&options, 0, sizeof(options));
  609. int ret = 0, nret;
  610. options.notifyengine = DEFAULT_NOTIFYENGINE;
  611. options.syncdelay = DEFAULT_SYNCDELAY;
  612. options._queues[QUEUE_NORMAL].collectdelay = DEFAULT_COLLECTDELAY;
  613. options._queues[QUEUE_BIGFILE].collectdelay= DEFAULT_BFILECOLLECTDELAY;
  614. options._queues[QUEUE_INSTANT].collectdelay= COLLECTDELAY_INSTANT;
  615. options.bfilethreshold = DEFAULT_BFILETHRESHOLD;
  616. options.label = DEFAULT_LABEL;
  617. options.rsyncinclimit = DEFAULT_RSYNC_INCLUDELINESLIMIT;
  618. options.synctimeout = DEFAULT_SYNCTIMEOUT;
  619. #ifdef CLUSTER_SUPPORT
  620. options.cluster_hash_dl_min = DEFAULT_CLUSTERHDLMIN;
  621. options.cluster_hash_dl_max = DEFAULT_CLUSTERHDLMAX;
  622. options.cluster_scan_dl_max = DEFAULT_CLUSTERSDLMAX;
  623. #endif
  624. options.config_block = DEFAULT_CONFIG_BLOCK;
  625. arguments_parse(argc, argv, &options);
  626. out_init(options.flags);
  627. nret = configs_parse(&options);
  628. if(!ret) ret = nret;
  629. out_init(options.flags);
  630. if(options.watchdir == NULL) {
  631. printf_e("Error: watchdir is not set.\n");
  632. ret = EINVAL;
  633. }
  634. if(options.handlerfpath == NULL) {
  635. printf_e("Error: sync-handler path is not set.\n");
  636. ret = EINVAL;
  637. }
  638. if(options.flags[SYNCHANDLER] && options.flags[RSYNC]) {
  639. printf_e("Error: Option \"--rsync\" cannot be used in conjunction with \"--synchandler-so-module\".\n");
  640. ret = EINVAL;
  641. }
  642. if(options.flags[SYNCHANDLER] && (options.listoutdir != NULL))
  643. printf_e("Warning: Option \"--dir-lists\" has no effect conjunction with \"--synchandler-so-module\".\n");
  644. if(options.flags[SYNCHANDLER] && (options.destdir != NULL))
  645. printf_e("Warning: Destination directory argument has no effect conjunction with \"--synchandler-so-module\".\n");
  646. if((options.flags[RSYNC]>1) && (options.destdir == NULL)) {
  647. printf_e("Error: Option \"-R2\" cannot be used without specifying \"destination directory\".\n");
  648. ret = EINVAL;
  649. }
  650. #ifdef CLUSTER_SUPPORT
  651. if((options.flags[RSYNC]>1) && (options.cluster_iface != NULL)) {
  652. printf_e("Error: Option \"-R2\" cannot be used in conjunction with \"--cluster-iface\".\n");
  653. ret = EINVAL;
  654. }
  655. if((options.cluster_iface == NULL) && ((options.cluster_mcastipaddr != NULL) || (options.cluster_nodename != NULL) || (options.cluster_timeout) || (options.cluster_mcastipport))) {
  656. printf_e("Error: Options \"--cluster-ip\", \"--cluster-node-name\", \"--cluster_timeout\" and/or \"cluster_ipport\" cannot be used without \"--cluster-iface\".\n");
  657. ret = EINVAL;
  658. }
  659. if(options.cluster_hash_dl_min > options.cluster_hash_dl_max) {
  660. printf_e("Error: \"--cluster-hash-dl-min\" cannot be greater than \"--cluster-hash-dl-max\".\n");
  661. ret = EINVAL;
  662. }
  663. if(options.cluster_hash_dl_max > options.cluster_scan_dl_max) {
  664. printf_e("Error: \"--cluster-hash-dl-max\" cannot be greater than \"--cluster-scan-dl-max\".\n");
  665. ret = EINVAL;
  666. }
  667. if(!options.cluster_timeout)
  668. options.cluster_timeout = DEFAULT_CLUSTERTIMEOUT;
  669. if(!options.cluster_mcastipport)
  670. options.cluster_mcastipport = DEFAULT_CLUSTERIPPORT;
  671. if(!options.cluster_mcastipaddr)
  672. options.cluster_mcastipaddr = DEFAULT_CLUSTERIPADDR;
  673. if(options.cluster_iface != NULL) {
  674. #ifndef _DEBUG
  675. printf_e("Error: Cluster subsystem is not implemented, yet. Sorry.\n");
  676. ret = EINVAL;
  677. #endif
  678. if(options.cluster_nodename == NULL) {
  679. if(!uname(&utsname))
  680. options.cluster_nodename = utsname.nodename;
  681. printf_d("Debug: cluster node name is: %s\n", options.cluster_nodename);
  682. }
  683. if(options.cluster_nodename == NULL) {
  684. printf_e("Error: Option \"--cluster-iface\" is set, but \"--cluster-node-name\" is not set and cannot get the nodename with uname().\n");
  685. ret = EINVAL;
  686. } else {
  687. options.cluster_nodename_len = strlen(options.cluster_nodename);
  688. }
  689. }
  690. #endif // CLUSTER_SUPPORT
  691. {
  692. char *rwatchdir = realpath(options.watchdir, NULL);
  693. if(rwatchdir == NULL) {
  694. printf_e("Error: main(): Got error while realpath() on \"%s\": %s (errno: %i) [#0].\n", options.watchdir, strerror(errno), errno);
  695. ret = errno;
  696. }
  697. if(!ret) {
  698. options.watchdir = rwatchdir;
  699. options.watchdirlen = strlen(options.watchdir);
  700. options.watchdirsize = options.watchdirlen;
  701. if(options.watchdirlen == 1) {
  702. printf_e("Error: watchdir is supposed to be not \"/\".\n");
  703. ret = EINVAL;
  704. }
  705. }
  706. if(!ret) {
  707. size_t size = options.watchdirlen + 2;
  708. char *newwatchdir = xmalloc(size);
  709. memcpy( newwatchdir, options.watchdir, options.watchdirlen);
  710. options.watchdirwslash = newwatchdir;
  711. options.watchdirwslashsize = size;
  712. memcpy(&options.watchdirwslash[options.watchdirlen], "/", 2);
  713. options.watchdir_dirlevel = fileutils_calcdirlevel(options.watchdirwslash);
  714. }
  715. }
  716. if(options.destdir != NULL) {
  717. char *rdestdir = realpath(options.destdir, NULL);
  718. if(rdestdir == NULL) {
  719. printf_e("Error: main(): Got error while realpath() on \"%s\": %s (errno: %i) [#1].\n", options.destdir, strerror(errno), errno);
  720. ret = errno;
  721. }
  722. if(!ret) {
  723. options.destdir = rdestdir;
  724. options.destdirlen = strlen(options.destdir);
  725. options.destdirsize = options.destdirlen;
  726. if(options.destdirlen == 1) {
  727. printf_e("Error: destdir is supposed to be not \"/\".\n");
  728. ret = EINVAL;
  729. }
  730. }
  731. if(!ret) {
  732. size_t size = options.destdirlen + 2;
  733. char *newdestdir = xmalloc(size);
  734. memcpy( newdestdir, options.destdir, options.destdirlen);
  735. options.destdirwslash = newdestdir;
  736. options.destdirwslashsize = size;
  737. memcpy(&options.destdirwslash[options.destdirlen], "/", 2);
  738. }
  739. }
  740. printf_d("Debug: %s [%s] (%p) -> %s [%s]\n", options.watchdir, options.watchdirwslash, options.watchdirwslash, options.destdir?options.destdir:"", options.destdirwslash?options.destdirwslash:"");
  741. if(options.flags[RSYNC] && (options.listoutdir == NULL)) {
  742. printf_e("Error: Option \"--rsync\" cannot be used without \"--outlistsdir\".\n");
  743. ret = EINVAL;
  744. }
  745. if(options.flags[RSYNC_PREFERINCLUDE] && (!options.flags[RSYNC]))
  746. printf_e("Warning: Option \"--rsyncpreferinclude\" is useless without \"--rsync\".\n");
  747. if(options.flags[RSYNC] && options.flags[AUTORULESW])
  748. printf_e("Warning: Option \"--auto-add-rules-w\" in conjunction with \"--rsync\" may cause unexpected problems.\n");
  749. if(options.flags[DEBUG])
  750. debug_print_flags();
  751. if(options.listoutdir != NULL) {
  752. struct stat st={0};
  753. errno = 0;
  754. if(stat(options.listoutdir, &st)) {
  755. if(errno == ENOENT) {
  756. printf_e("Warning: Directory \"%s\" doesn't exist. Creating it.\n", options.listoutdir);
  757. errno = 0;
  758. if(mkdir(options.listoutdir, S_IRWXU)) {
  759. printf_e("Error: main(): Cannot create directory \"%s\": %s (%i)", options.listoutdir, strerror(errno), errno);
  760. ret = errno;
  761. }
  762. } else {
  763. printf_e("Error: main(): Got error while stat() on \"%s\": %s (errno: %i).\n", options.listoutdir, strerror(errno), errno);
  764. ret = errno;
  765. }
  766. }
  767. if(!errno)
  768. if(st.st_mode & (S_IRWXG|S_IRWXO)) {
  769. #ifdef PARANOID
  770. printf_e("Error: Insecure: Others have access to directory \"%s\". Exit.\n", options.listoutdir);
  771. ret = EACCES;
  772. #else
  773. printf_e("Warning: Insecure: Others have access to directory \"%s\".\n", options.listoutdir);
  774. #endif
  775. }
  776. }
  777. if(options.flags[ENABLEINITIALSYNC] && (options.listoutdir == NULL)) {
  778. printf_e("Error: main(): Option \"--dir-lists\" should be set to use option \"--initialsync-enable\".\n");
  779. ret = EINVAL;
  780. }
  781. if(options.flags[ENABLEINITIALSYNC] && options.flags[RSYNC]) {
  782. printf_e("Error: main(): Options \"--initialsync-enable\" and \"--rsync\" are incompatable.\n");
  783. ret = EINVAL;
  784. }
  785. if(options.flags[SYNCLISTSIMPLIFY] && (options.listoutdir == NULL)) {
  786. printf_e("Error: main(): Option \"--dir-lists\" should be set to use option \"--synclist-simplify\".\n");
  787. ret = EINVAL;
  788. }
  789. if(options.flags[SYNCLISTSIMPLIFY] && options.flags[RSYNC]) {
  790. printf_e("Error: main(): Options \"--synclist-simplify\" and \"--rsync\" are incompatable.\n");
  791. ret = EINVAL;
  792. }
  793. #ifdef FANOTIFY_SUPPORT
  794. if(options.notifyengine != NE_INOTIFY) {
  795. printf_e("Warning: fanotify is not fully supported, yet!\n");
  796. }
  797. #endif
  798. if(access(options.handlerfpath, X_OK) == -1) {
  799. printf_e("Error: \"%s\" is not executable: %s (errno: %i).\n", options.handlerfpath, strerror(errno), errno);
  800. ret = errno;
  801. }
  802. #ifdef VERYPARANOID
  803. {
  804. struct stat64 stat64={0};
  805. if(lstat64(options.watchdir, &stat64)) {
  806. printf_e("Error: main(): Cannot lstat64() on \"%s\": %s (errno: %i)\n", options.watchdir, strerror(errno), errno);
  807. ret = errno;
  808. } else {
  809. if((stat64.st_mode & S_IFMT) == S_IFLNK) {
  810. // The proplems may be due to FTS_PHYSICAL option of ftp_open() in sync_initialsync_rsync_walk(),
  811. // so if the "watch dir" is just a symlink it doesn't walk recursivly. For example, in "-R" case
  812. // it disables filters, because exclude-list will be empty.
  813. printf_e("Error: Watch dir cannot be symlink, but \"%s\" is a symlink.\n", options.watchdir);
  814. ret = EINVAL;
  815. }
  816. }
  817. }
  818. #endif
  819. nret=main_rehash(&options);
  820. if(nret)
  821. ret = nret;
  822. if(options.flags[BACKGROUND]) {
  823. nret = becomedaemon();
  824. if(nret)
  825. ret = nret;
  826. }
  827. #ifdef HAVE_CAPABILITIES
  828. if(options.flags[CAP_PRESERVE_FILEACCESS]) {
  829. // Doesn't work, yet :(
  830. //
  831. // Error: Cannot inotify_add_watch() on "/home/xaionaro/clsync/examples/testdir/from": Permission denied (errno: 13).
  832. printf_d("Debug: main(): Preserving access to files with using linux capabilites\n");
  833. struct __user_cap_header_struct cap_hdr = {0};
  834. struct __user_cap_data_struct cap_dat = {0};
  835. cap_hdr.version = _LINUX_CAPABILITY_VERSION;
  836. if(capget(&cap_hdr, &cap_dat) < 0) {
  837. printf_e("Error: main() cannot get capabilites with capget(): %s (errno: %i)\n", strerror(errno), errno);
  838. ret = errno;
  839. goto preserve_fileaccess_end;
  840. }
  841. // From "man 7 capabilities":
  842. // CAP_DAC_OVERRIDE - Bypass file read, write, and execute permission checks.
  843. // CAP_DAC_READ_SEARCH - Bypass file read permission checks and directory read and execute permission checks.
  844. 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));
  845. 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));
  846. cap_dat.inheritable = 0;
  847. printf_ddd("Debug3: main(): cap.eff == %p; cap.prm == %p.\n",
  848. (void *)(long)cap_dat.effective, (void *)(long)cap_dat.permitted);
  849. if(capset(&cap_hdr, &cap_dat) < 0) {
  850. printf_e("Error: main(): Cannot set capabilities with capset(): %s (errno: %i).\n", strerror(errno), errno);
  851. ret = errno;
  852. goto preserve_fileaccess_end;
  853. }
  854. // Tell kernel not clear capabilities when dropping root
  855. if(prctl(PR_SET_KEEPCAPS, 1) < 0) {
  856. printf_e("Error: main(): Cannot prctl(PR_SET_KEEPCAPS, 1) to preserve capabilities: %s (errno: %i)\n",
  857. strerror(errno), errno);
  858. ret = errno;
  859. goto preserve_fileaccess_end;
  860. }
  861. }
  862. preserve_fileaccess_end:
  863. #endif
  864. if(options.flags[UID]) {
  865. if(setuid(options.uid)) {
  866. printf_e("Error: main(): Cannot setuid(%u): %s (errno: %i)\n", options.uid, strerror(errno), errno);
  867. ret = errno;
  868. }
  869. }
  870. if(options.flags[GID]) {
  871. if(setuid(options.gid)) {
  872. printf_e("Error: main(): Cannot setgid(%u): %s (errno: %i)\n", options.gid, strerror(errno), errno);
  873. ret = errno;
  874. }
  875. }
  876. if(options.pidfile != NULL) {
  877. pid_t pid = getpid();
  878. FILE *pidfile = fopen(options.pidfile, "w");
  879. if(pidfile == NULL) {
  880. printf_e("Error: main(): Cannot open file \"%s\" to write a pid there: %s (errno: %i)\n",
  881. options.pidfile, strerror(errno), errno);
  882. ret = errno;
  883. } else {
  884. if(fprintf(pidfile, "%u\n", pid) < 0) {
  885. printf_e("Error: main(): Cannot write pid into file \"%s\": %s (errno: %i)\n",
  886. options.pidfile, strerror(errno), errno);
  887. ret = errno;
  888. }
  889. fclose(pidfile);
  890. }
  891. }
  892. printf_ddd("Debug3: main(): Current errno is %i.\n", ret);
  893. if(ret == 0)
  894. ret = sync_run(&options);
  895. if(options.pidfile != NULL) {
  896. if(unlink(options.pidfile)) {
  897. printf_e("Error: main(): Cannot unlink pidfile \"%s\": %s (errno: %i)\n",
  898. options.pidfile, strerror(errno), errno);
  899. ret = errno;
  900. }
  901. }
  902. main_cleanup(&options);
  903. if(options.watchdirsize)
  904. free(options.watchdir);
  905. if(options.watchdirwslashsize)
  906. free(options.watchdirwslash);
  907. if(options.destdirsize)
  908. free(options.destdir);
  909. if(options.destdirwslashsize)
  910. free(options.destdirwslash);
  911. configs_cleanup();
  912. out_flush();
  913. printf_d("Debug: finished, exitcode: %i: %s.\n", ret, strerror(ret));
  914. out_flush();
  915. out_deinit();
  916. return ret;
  917. }