1 #include "config.h"
2
3 #include <iostream>
4 #include <string>
5 #include <fstream>
6 #include <vector>
7 #include <map>
8 #include <algorithm>
9 #include <cctype>
10
11 #include "asserts.h"
12 #include "error.h"
13 #include "estring.h"
14 #include "fs.h"
15 #include "tstamp.h"
16
17 #include "rconfig.h"
18
19 //----------------------------------------------------------------------------
20
21 /** Reset to a default value */
clear(void)22 void archive_path_element::clear(void)
23 {
24 m_type = jobname;
25 m_literal.erase();
26 }
27
28 /** C'tor */
archive_path_element()29 archive_path_element::archive_path_element()
30 {
31 clear();
32 }
33
34 /** C'tor */
archive_path_element(const archive_path_element & a_class)35 archive_path_element::archive_path_element(
36 const archive_path_element& a_class)
37 {
38 clear();
39 assign(a_class);
40 }
41
42 /** C'tor */
archive_path_element(const archive_path_element::element_type & a_enum)43 archive_path_element::archive_path_element(
44 const archive_path_element::element_type& a_enum
45 )
46 {
47 clear();
48 assign(a_enum);
49 }
50
51 /** C'tor */
archive_path_element(const std::string & a_str)52 archive_path_element::archive_path_element(const std::string& a_str)
53 {
54 clear();
55 assign(a_str);
56 }
57
58 /** Assign the value from another archive_path_element instance */
assign(const archive_path_element & a_class)59 void archive_path_element::assign(const archive_path_element& a_class)
60 {
61 if (this == &a_class)
62 return;
63
64 if (a_class.type() == literal)
65 assign(a_class.str());
66 else
67 assign(a_class.type());
68 }
69
70 /** Assign any type except literal */
assign(const archive_path_element::element_type & a_enum)71 void archive_path_element::assign(
72 const archive_path_element::element_type& a_enum
73 )
74 {
75 m_type = a_enum;
76 if (m_type != literal) {
77 m_literal.erase();
78 }
79 }
80
81 /** Assign a literal */
assign(const std::string & a_str)82 void archive_path_element::assign(const std::string& a_str)
83 {
84 std::string es;
85 std::string str;
86 std::string::size_type idx;
87
88 TRY_nomem(es = "Invalid archive-path keyword: \"");
89 TRY_nomem(es += a_str);
90 TRY_nomem(es += "\"");
91
92 if (a_str.size() == 0) {
93 throw(ERROR(0,es));
94 }
95 if (a_str[0] == '"') {
96 if (a_str.size() < 3) {
97 throw(ERROR(0,es));
98 }
99 if (a_str[a_str.size()-1] != '"') {
100 throw(ERROR(0,es));
101 }
102 TRY_nomem(str = a_str.substr(1,a_str.size()-2));
103
104 idx = str.find('"');
105 while (idx != std::string::npos) {
106 if ((idx > 0) && (str[idx-1] != '\\'))
107 throw(ERROR(0,es));
108 idx = str.find('"',idx+1);
109 }
110
111 idx = str.find("\\\"");
112 while (idx != std::string::npos) {
113 str.erase(idx,1);
114 idx = str.find("\\\"");
115 }
116
117 TRY_nomem(m_literal = str);
118 m_type = literal;
119 }
120 else {
121 str = estring(a_str).lower();
122 if (str == "jobname") {
123 assign(jobname);
124 }
125 else if (str == "groupname") {
126 assign(groupname);
127 }
128 else if (str == "hostname") {
129 assign(hostname);
130 }
131 else if (str == "pathname") {
132 assign(pathname);
133 }
134 else if (str == "permutation") {
135 assign(permutation);
136 }
137 else {
138 throw(ERROR(0,es));
139 }
140 }
141 }
142
143 /** Retrieve the current type */
144 const archive_path_element::element_type&
type(void) const145 archive_path_element::type(void) const
146 {
147 return(m_type);
148 }
149
150 /** Retrieve the current literal value */
value(void) const151 const std::string& archive_path_element::value(void) const
152 {
153 return(m_literal);
154 }
155
156 /** Construct a string of the current value */
str(void) const157 const std::string archive_path_element::str(void) const
158 {
159 std::string::size_type idx;
160 std::string value;
161
162 if (m_type == jobname) {
163 TRY_nomem(value = "jobname");
164 }
165 else if (m_type == groupname) {
166 TRY_nomem(value = "groupname");
167 }
168 else if (m_type == hostname) {
169 TRY_nomem(value = "hostname");
170 }
171 else if (m_type == pathname) {
172 TRY_nomem(value = "pathname");
173 }
174 else if (m_type == permutation) {
175 TRY_nomem(value = "permutation");
176 }
177 else {
178 TRY_nomem(value = "\"");
179 for (idx = 0; idx != m_literal.size(); idx++) {
180 if (m_literal[idx] == '"') {
181 TRY_nomem(value += '\\');
182 }
183 TRY_nomem(value += m_literal[idx]);
184 }
185 TRY_nomem(value += '"');
186 }
187
188 return(value);
189 }
190
191 /** Assignment */
192 archive_path_element&
operator =(const archive_path_element & a_class)193 archive_path_element::operator=(const archive_path_element& a_class)
194 {
195 clear();
196 assign(a_class);
197
198 return(*this);
199 }
200
201 /** Assignment */
202 archive_path_element&
operator =(const archive_path_element::element_type a_enum)203 archive_path_element::operator=(
204 const archive_path_element::element_type a_enum)
205 {
206 clear();
207 assign(a_enum);
208
209 return(*this);
210 }
211
212 /** Assignment */
213 archive_path_element&
operator =(const std::string & a_str)214 archive_path_element::operator=(const std::string& a_str)
215 {
216 clear();
217 assign(a_str);
218
219 return(*this);
220 }
221
222 //----------------------------------------------------------------------------
223
224 /** Reset to a default value */
reset(void)225 void archive_path::reset(void)
226 {
227 clear();
228 push_back("hostname/pathname");
229 }
230
231 /** C'tor */
archive_path()232 archive_path::archive_path()
233 {
234 reset();
235 }
236
237 /** C'tor */
archive_path(const archive_path & a_class)238 archive_path::archive_path(const archive_path& a_class)
239 {
240 push_back(a_class);
241 }
242
243 /** C'tor */
archive_path(const archive_path_element & a_class)244 archive_path::archive_path(const archive_path_element& a_class)
245 {
246 push_back(a_class);
247 }
248
249 /** C'tor */
archive_path(const std::string & a_str)250 archive_path::archive_path(const std::string& a_str)
251 {
252 push_back(a_str);
253 }
254
255 /** Add elements to the path list from another archive_path instance */
push_back(const archive_path & a_class)256 void archive_path::push_back(const archive_path& a_class)
257 {
258 const_iterator li;
259
260 for (li = a_class.begin(); li != a_class.end(); li++) {
261 TRY_nomem(push_back(*li));
262 }
263 }
264
265 /** Add an element to the path list from an archive_path_element instance */
push_back(const archive_path_element & a_class)266 void archive_path::push_back(const archive_path_element& a_class)
267 {
268 TRY_nomem(type::push_back(a_class));
269 }
270
271 /** Parse a string into an archive-path list */
push_back(const std::string & a_str)272 void archive_path::push_back(const std::string& a_str)
273 {
274 std::string es;
275 std::string keyword;
276 std::string str;
277 std::string::size_type idx;
278 archive_path_element ape;
279
280 if (a_str.size() == 0)
281 return;
282
283 TRY_nomem(str = a_str);
284 if (str[0] == '/')
285 str.erase(0,1);
286 if (str[str.size()-1] == '/')
287 str.erase(str.size()-1,1);
288
289 while (str.size() > 0) {
290 idx = str.find('/');
291 if (idx == std::string::npos) {
292 TRY_nomem(keyword = str);
293 str.erase();
294 }
295 else {
296 TRY_nomem(keyword = str.substr(0,idx));
297 str.erase(0,idx+1);
298 }
299
300 ape.clear();
301 TRY_nomem(es = "At: \"");
302 TRY_nomem(es += keyword);
303 TRY_nomem(es += "\"");
304 TRY(ape.assign(keyword),es);
305 TRY_nomem(push_back(ape));
306 }
307 }
308
309 /** Reconstruct a string from an archive-path list */
str(void) const310 const std::string archive_path::str(void) const
311 {
312 std::string str;
313 const_iterator li;
314
315 for (li = begin(); li != end(); li++) {
316 if (li != begin()) {
317 TRY_nomem(str += '/');
318 }
319 TRY_nomem(str += (*li).str());
320 }
321
322 return(str);
323 }
324
325 /** Assign an archive-path list from another archive_path instance */
assign(const archive_path & a_class)326 void archive_path::assign(const archive_path& a_class)
327 {
328 if (this != &a_class) {
329 clear();
330 push_back(a_class);
331 }
332 }
333
334 /** Assign an archive-path list from a single archive_path_element instance */
assign(const archive_path_element & a_class)335 void archive_path::assign(const archive_path_element& a_class)
336 {
337 clear();
338 push_back(a_class);
339 }
340
341 /** Assign an archive-path list from a string (parse the string) */
assign(const std::string & a_str)342 void archive_path::assign(const std::string& a_str)
343 {
344 clear();
345 push_back(a_str);
346 }
347
348 /** Assignment */
operator =(const archive_path & a_class)349 archive_path& archive_path::operator=(const archive_path& a_class)
350 {
351 assign(a_class);
352
353 return(*this);
354 }
355
356 /** Assignment */
operator =(const archive_path_element & a_class)357 archive_path& archive_path::operator=(const archive_path_element& a_class)
358 {
359 assign(a_class);
360
361 return(*this);
362 }
363
364 /** Assignment */
operator =(const std::string & a_str)365 archive_path& archive_path::operator=(const std::string& a_str)
366 {
367 assign(a_str);
368
369 return(*this);
370 }
371
372 //----------------------------------------------------------------------------
373
374 /** Default behavior */
375 const uint16 rsync_behavior::default_behavior;
376
377 /** Clear all values and set the default to "retry" */
clear(void)378 void rsync_behavior::clear(void)
379 {
380 m_map.clear();
381 m_default = retry;
382 }
383
384 /** Clear all values and set some sane default actions */
reset(void)385 void rsync_behavior::reset(void)
386 {
387 clear();
388 m_default = retry;
389 TRY_nomem(
390 m_map[0] = ok;
391 m_map[1] = fail;
392 m_map[2] = fail;
393 m_map[4] = fail;
394 m_map[23] = retry_without_hardlinks;
395 m_map[124] = fail;
396 m_map[125] = fail;
397 m_map[126] = fail;
398 m_map[127] = fail;
399 );
400 }
401
402 /** C'tor */
rsync_behavior()403 rsync_behavior::rsync_behavior()
404 {
405 reset();
406 }
407
408 /** C'tor */
rsync_behavior(const rsync_behavior & a_class)409 rsync_behavior::rsync_behavior(const rsync_behavior& a_class)
410 {
411 assign(a_class);
412 }
413
414 /** Assign an action to be taken for a specific exit code */
assign(const uint16 a_code,const value_type a_action)415 void rsync_behavior::assign(const uint16 a_code, const value_type a_action)
416 {
417 if (a_code == default_behavior)
418 m_default = a_action;
419 else {
420 TRY_nomem(m_map[a_code] = a_action);
421 }
422 }
423
424 /** Assign actions to be taken from another rsync_behavior instance */
assign(const rsync_behavior & a_class)425 void rsync_behavior::assign(const rsync_behavior& a_class)
426 {
427 if (this == &a_class)
428 return;
429
430 clear();
431 m_default = a_class.default_value();
432 TRY_nomem(m_map = a_class.map_value());
433 }
434
435 /**
436 Assign actions to be taken from a string (parse a string in the form of
437 exit-code '=' action)
438 */
assign(const std::string & a_str)439 void rsync_behavior::assign(const std::string& a_str)
440 {
441 std::string es;
442 std::string code_str;
443 std::string action_str;
444 uint16 code;
445 rsync_behavior::behavior_type action;
446 std::string::size_type idx;
447
448 TRY_nomem(es = "Invalid rsync-behavior value: \"");
449 TRY_nomem(es += a_str);
450 TRY_nomem(es += "\"");
451
452 TRY_nomem(action_str = a_str);
453 idx = 0;
454 while (isdigit(action_str[idx]) || (action_str[idx] == '*'))
455 ++idx;
456 TRY_nomem(code_str = action_str.substr(0,idx));
457 action_str.erase(0,idx);
458 if (code_str.size() == 0)
459 throw(ERROR(0,es));
460
461 idx = 0;
462 while ((action_str[idx] == ' ') || (action_str[idx] == '\t'))
463 idx++;
464 action_str.erase(0,idx);
465
466 if (action_str[0] != '=')
467 throw(ERROR(0,es));
468 action_str.erase(0,1);
469
470 idx = 0;
471 while ((action_str[idx] == ' ') || (action_str[idx] == '\t'))
472 idx++;
473 action_str.erase(0,idx);
474
475 if (code_str == "*")
476 code = rsync_behavior::default_behavior;
477 else
478 code = estring(code_str);
479 if (estring(action_str).lower() == "ok")
480 action = rsync_behavior::ok;
481 else if (estring(action_str).lower() == "fail")
482 action = rsync_behavior::fail;
483 else if (estring(action_str).lower() == "retry")
484 action = rsync_behavior::retry;
485 else if (estring(action_str).lower() == "retry-without-hardlinks")
486 action = rsync_behavior::retry_without_hardlinks;
487 else if (estring(action_str).lower() == "ok")
488 action = rsync_behavior::quit;
489 else
490 throw(ERROR(0,es));
491 assign(code, action);
492 }
493
494 /** Return the action to be taken for a given exit code */
495 const rsync_behavior::value_type
operator [](const uint16 a_code) const496 rsync_behavior::operator[](const uint16 a_code) const
497 {
498 if (a_code == default_behavior) {
499 return(m_default);
500 }
501 if (m_map.find(a_code) != m_map.end()) {
502 return(m_map.find(a_code)->second);
503 }
504 return(m_default);
505 }
506
507 /** Return the action to be taken for a given exit code */
508 rsync_behavior::value_type&
operator [](const uint16 a_code)509 rsync_behavior::operator[](const uint16 a_code)
510 {
511 if (a_code == default_behavior) {
512 return(m_default);
513 }
514 return(m_map[a_code]);
515 }
516
517 /** Assignment */
operator =(const rsync_behavior & a_class)518 rsync_behavior& rsync_behavior::operator=(const rsync_behavior& a_class)
519 {
520 assign(a_class);
521
522 return(*this);
523 }
524
525 /** Assignment */
526 rsync_behavior&
operator =(const rsync_behavior::behavior_type a_enum)527 rsync_behavior::operator=(const rsync_behavior::behavior_type a_enum)
528 {
529 assign(default_behavior, a_enum);
530
531 return(*this);
532 }
533
534 /** Assignment */
operator =(const std::string & a_str)535 rsync_behavior& rsync_behavior::operator=(const std::string& a_str)
536 {
537 assign(a_str);
538
539 return(*this);
540 }
541
542 /** Return the default action */
default_value(void) const543 const rsync_behavior::behavior_type rsync_behavior::default_value(void) const
544 {
545 return(m_default);
546 }
547
548 /** Return an std::map of exit codes to actions */
map_value(void) const549 const rsync_behavior::map_type& rsync_behavior::map_value(void) const
550 {
551 return(m_map);
552 }
553
554 //----------------------------------------------------------------------------
555
556 /** C'tor */
job()557 job::job()
558 {
559 clear();
560 }
561
562 /** C'tor */
job(const job & a_job)563 job::job(const job& a_job)
564 {
565 clear();
566 assign(a_job);
567 }
568
569 /** Clear values */
clear(void)570 void job::clear(void)
571 {
572 default_config_path.erase();
573 default_config_line = 0;
574 config_path.erase();
575 config_line = 0;
576 archive_path = "hostname/pathname";
577 excludes.clear();
578 includes.clear();
579 groupname.erase();
580 hostname.erase();
581 jobname.erase();
582 paths.clear();
583 rsync_behavior.reset();
584 rsync_connection = connection_remote;
585 rsync_hardlink = true;
586 rsync_multi_hardlink = false;
587 rsync_multi_hardlink_max = 20;
588 rsync_options.erase();
589 ssh_options.erase();
590 rsync_remote_user.erase();
591 rsync_remote_path.erase();
592 rsync_remote_port = 0;
593 rsync_remote_module.erase();
594 rsync_retry_count = 3;
595 rsync_retry_delay = 0;
596 rsync_timeout = 60 * 60 * 4; // 4 hours;
597 }
598
599 /** Assign values from another job instance */
assign(const job & a_job)600 void job::assign(const job& a_job)
601 {
602 clear();
603
604 default_config_path = a_job.default_config_path;
605 default_config_line = a_job.default_config_line;
606 config_path = a_job.config_path;
607 config_line = a_job.config_line;
608 archive_path = a_job.archive_path;
609 excludes = a_job.excludes;
610 includes = a_job.includes;
611 groupname = a_job.groupname;
612 hostname = a_job.hostname;
613 jobname = a_job.jobname;
614 paths = a_job.paths;
615 rsync_behavior.clear();
616 rsync_behavior = a_job.rsync_behavior;
617 rsync_connection = a_job.rsync_connection;
618 rsync_hardlink = a_job.rsync_hardlink;
619 rsync_multi_hardlink = a_job.rsync_multi_hardlink;
620 rsync_multi_hardlink_max = a_job.rsync_multi_hardlink_max;
621 rsync_options = a_job.rsync_options;
622 ssh_options = a_job.ssh_options;
623 rsync_remote_user = a_job.rsync_remote_user;
624 rsync_remote_path = a_job.rsync_remote_path;
625 rsync_remote_port = a_job.rsync_remote_port;
626 rsync_remote_module = a_job.rsync_remote_module;
627 rsync_retry_count = a_job.rsync_retry_count;
628 rsync_retry_delay = a_job.rsync_retry_delay;
629 rsync_timeout = a_job.rsync_timeout;
630 }
631
632 /** Assignment */
operator =(const job & a_job)633 job& job::operator=(const job& a_job)
634 {
635 assign(a_job);
636
637 return(*this);
638 }
639
640 /** Generate the archive-path subdirectory for this job */
generate_archive_path(const std::string & a_path) const641 const std::string job::generate_archive_path(const std::string& a_path) const
642 {
643 std::string es;
644 std::string path;
645 archive_path::const_iterator capi;
646
647 for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) {
648 if (capi->type() == archive_path_element::jobname) {
649 if (jobname.size() == 0) {
650 TRY_nomem(es = "archive-path references jobname, ");
651 TRY_nomem(es += "but jobname is empty");
652 throw(ERROR(0,es));
653 }
654 if (path.size() != 0) {
655 TRY_nomem(path += '/');
656 }
657 TRY_nomem(path += jobname);
658 }
659 else if (capi->type() == archive_path_element::groupname) {
660 if (groupname.size() == 0) {
661 TRY_nomem(es = "archive-path references groupname, ");
662 TRY_nomem(es += "but groupname is empty");
663 throw(ERROR(0,es));
664 }
665 if (path.size() != 0) {
666 TRY_nomem(path += '/');
667 }
668 TRY_nomem(path += groupname);
669 }
670 else if (capi->type() == archive_path_element::hostname) {
671 if (hostname.size() == 0) {
672 TRY_nomem(es = "archive-path references hostname, ");
673 TRY_nomem(es += "but hostname is empty");
674 throw(ERROR(0,es));
675 }
676 if (path.size() != 0) {
677 TRY_nomem(path += '/');
678 }
679 TRY_nomem(path += hostname);
680 }
681 else if (capi->type() == archive_path_element::pathname) {
682 if (a_path.size() == 0) {
683 TRY_nomem(es = "archive-path references path name, ");
684 TRY_nomem(es += "but path name is empty");
685 throw(ERROR(0,es));
686 }
687 if (path.size() != 0) {
688 TRY_nomem(path += '/');
689 }
690 TRY_nomem(path += a_path);
691 }
692 else if (capi->type() == archive_path_element::permutation) {
693 if (a_path.size() == 0) {
694 TRY_nomem(es = "archive-path references path name, ");
695 TRY_nomem(es += "but path name is empty");
696 throw(ERROR(0,es));
697 }
698 if (path.size() != 0) {
699 TRY_nomem(path += '/');
700 }
701 TRY_nomem(path += permute_path(a_path));
702 }
703 else if (capi->type() == archive_path_element::literal) {
704 if (capi->value().size() == 0) {
705 TRY_nomem(es = "archive-path references literal string, ");
706 TRY_nomem(es += "but literal string value is empty");
707 throw(INTERNAL_ERROR(0,es));
708 }
709 if (path.size() != 0) {
710 TRY_nomem(path += '/');
711 }
712 TRY_nomem(path += capi->value());
713 }
714 else
715 throw(INTERNAL_ERROR(0,"Unknown archive path element type"));
716 }
717
718 // If path does not end with a '/', strip off characters until it does.
719 while ((path.size() > 0) && (path[path.size()-1] != '/'))
720 path.erase(path.size()-1);
721
722 path = reform_path(path);
723
724 return(path);
725 }
726
727 /** Generate the source path to be passed to rsync on the command line */
generate_source_path(const std::string & a_path) const728 const std::string job::generate_source_path(const std::string& a_path) const
729 {
730 estring path;
731
732 if (rsync_connection == connection_server) {
733 path += "rsync://";
734 if (rsync_remote_user.size() != 0) {
735 path += rsync_remote_user;
736 path += "@";
737 }
738 TRY_nomem(path += hostname);
739 if (rsync_remote_port != 0) {
740 path += ":";
741 path += estring(rsync_remote_port);
742 }
743 if (rsync_remote_module.size() != 0) {
744 path += "/";
745 path += rsync_remote_module;
746 if ((a_path.size() > 0) && (a_path[0] != '/'))
747 path += "/";
748 }
749 }
750 else if (rsync_connection == connection_remote) {
751 if (rsync_remote_user.size() != 0) {
752 path += rsync_remote_user;
753 path += "@";
754 }
755 path += hostname;
756 path += ":";
757 }
758 path += a_path;
759
760 return(path);
761 }
762
763 /** Find the common pathname among all job paths (may be an empty string) */
common_pathname(void) const764 const std::string job::common_pathname(void) const
765 {
766 std::string common_path;
767 paths_type::size_type pidx;
768 std::string::size_type sidx;
769 std::string::size_type max_sidx;
770 bool same;
771
772 TRY_nomem(common_path = "");
773 sidx = 0;
774 max_sidx = paths[0].size();
775 for (pidx = 0; pidx < paths.size(); pidx++) {
776 if (max_sidx > paths[pidx].size()) {
777 max_sidx = paths[pidx].size();
778 }
779 }
780 same = true;
781 for (sidx = 0; sidx < max_sidx; sidx++) {
782 for (pidx = 0; (same && (pidx < paths.size())); pidx++) {
783 if (pidx == 0)
784 continue;
785 if (paths[pidx-1][sidx] != paths[pidx][sidx])
786 same = false;
787 }
788 if (same)
789 TRY_nomem(common_path += paths[0][sidx]);
790 }
791 if (common_path[0] == '/')
792 common_path.erase(0,1);
793 if ((common_path.size() > 0) && (common_path[common_path.size()-1] == '/'))
794 common_path.erase(common_path.size()-1);
795
796 return(common_path);
797 }
798
799 /** Generate a unique ID string for this job */
generate_job_id(void) const800 const std::string job::generate_job_id(void) const
801 {
802 std::string es;
803 std::string path;
804 archive_path::const_iterator capi;
805
806 for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) {
807 if (capi->type() == archive_path_element::jobname) {
808 if (jobname.size() == 0) {
809 TRY_nomem(es = "archive-path references jobname, ");
810 TRY_nomem(es += "but jobname is empty");
811 throw(ERROR(0,es));
812 }
813 if (path.size() != 0) {
814 TRY_nomem(path += '/');
815 }
816 TRY_nomem(path += jobname);
817 }
818 else if (capi->type() == archive_path_element::groupname) {
819 if (groupname.size() == 0) {
820 TRY_nomem(es = "archive-path references groupname, ");
821 TRY_nomem(es += "but groupname is empty");
822 throw(ERROR(0,es));
823 }
824 if (path.size() != 0) {
825 TRY_nomem(path += '/');
826 }
827 TRY_nomem(path += groupname);
828 }
829 else if (capi->type() == archive_path_element::hostname) {
830 if (hostname.size() == 0) {
831 TRY_nomem(es = "archive-path references hostname, ");
832 TRY_nomem(es += "but hostname is empty");
833 throw(ERROR(0,es));
834 }
835 if (path.size() != 0) {
836 TRY_nomem(path += '/');
837 }
838 TRY_nomem(path += hostname);
839 }
840 else if (capi->type() == archive_path_element::pathname) {
841 if (path.size() == 0) {
842 TRY_nomem(path += common_pathname());
843 }
844 }
845 else if (capi->type() == archive_path_element::permutation) {
846 if (path.size() == 0) {
847 TRY_nomem(path += permute_path(common_pathname()));
848 }
849 }
850 else if (capi->type() == archive_path_element::literal) {
851 if (capi->value().size() == 0) {
852 TRY_nomem(es = "archive-path references literal string, ");
853 TRY_nomem(es += "but literal string value is empty");
854 throw(INTERNAL_ERROR(0,es));
855 }
856 if (path.size() != 0) {
857 TRY_nomem(path += '/');
858 }
859 TRY_nomem(path += capi->value());
860 }
861 }
862
863 path = reform_path(path);
864
865 if (path.size() == 0) {
866 TRY_nomem(path = jobname);
867 }
868 return(path);
869 }
870
871 /** Generate rsync command line options */
generate_rsync_options_vector(void) const872 const std::vector<std::string> job::generate_rsync_options_vector(void) const
873 {
874 std::string opts = rsync_options;
875 std::vector<std::string> argv;
876 std::string str;
877
878 while (opts.size() != 0) {
879
880 str.clear();
881 while ((opts.size() != 0) && (opts[0] != ' ') && (opts[0] != '\t')) {
882 switch (opts[0]) {
883 case '\0':
884 break;
885 case '\'':
886 opts.erase(0,1);
887 while ((opts.size() != 0) && (opts[0] != '\'')) {
888 str += opts[0];
889 opts.erase(0,1);
890 }
891 if (opts[0] == '\'') {
892 opts.erase(0,1);
893 }
894 break;
895 case '"':
896 opts.erase(0,1);
897 while ((opts.size() != 0) && (opts[0] != '"')) {
898 if ((opts.size() >= 2) && (opts[0] == '\\') && (opts[1] == '"')) {
899 str += '"';
900 opts.erase(0,2);
901 }
902 else {
903 str += opts[0];
904 opts.erase(0,1);
905 }
906 }
907 if (opts[0] == '"') {
908 opts.erase(0,1);
909 }
910 break;
911 case '\\':
912 if ((opts.size() >= 2) && (opts[1] == ' ')) {
913 str += ' ';
914 opts.erase(0,2);
915 }
916 else if ((opts.size() >= 2) && (opts[1] == 't')) {
917 str += '\t';
918 opts.erase(0,2);
919 }
920 else {
921 str += '\\';
922 opts.erase(0,1);
923 }
924 break;
925 default:
926 str += opts[0];
927 opts.erase(0,1);
928 break;
929 }
930 }
931
932 if (str.size()) {
933 argv.push_back(str);
934 }
935 while ((opts.size() > 0) && ((opts[0] == ' ') || (opts[0] == '\t'))) {
936 opts.erase(0,1);
937 }
938 }
939
940 return(argv);
941 }
942
943 /** Generate ssh command line options */
generate_ssh_options_vector(void) const944 const std::vector<std::string> job::generate_ssh_options_vector(void) const
945 {
946 std::string opts = ssh_options;
947 std::vector<std::string> argv;
948 std::string str;
949
950 while (opts.size() != 0) {
951
952 str.clear();
953 while ((opts.size() != 0) && (opts[0] != ' ') && (opts[0] != '\t')) {
954 switch (opts[0]) {
955 case '\0':
956 break;
957 case '\'':
958 opts.erase(0,1);
959 while ((opts.size() != 0) && (opts[0] != '\'')) {
960 str += opts[0];
961 opts.erase(0,1);
962 }
963 if (opts[0] == '\'') {
964 opts.erase(0,1);
965 }
966 break;
967 case '"':
968 opts.erase(0,1);
969 while ((opts.size() != 0) && (opts[0] != '"')) {
970 if ((opts.size() >= 2) && (opts[0] == '\\') && (opts[1] == '"')) {
971 str += '"';
972 opts.erase(0,2);
973 }
974 else {
975 str += opts[0];
976 opts.erase(0,1);
977 }
978 }
979 if (opts[0] == '"') {
980 opts.erase(0,1);
981 }
982 break;
983 case '\\':
984 if ((opts.size() >= 2) && (opts[1] == ' ')) {
985 str += ' ';
986 opts.erase(0,2);
987 }
988 else if ((opts.size() >= 2) && (opts[1] == 't')) {
989 str += '\t';
990 opts.erase(0,2);
991 }
992 else {
993 str += '\\';
994 opts.erase(0,1);
995 }
996 break;
997 default:
998 str += opts[0];
999 opts.erase(0,1);
1000 break;
1001 }
1002 }
1003
1004 if (str.size()) {
1005 argv.push_back(str);
1006 }
1007 while ((opts.size() > 0) && ((opts[0] == ' ') || (opts[0] == '\t'))) {
1008 opts.erase(0,1);
1009 }
1010 }
1011
1012 return(argv);
1013 }
1014
1015 /** Perform sanity checks for the configuration settings of this job */
check(void)1016 void job::check(void)
1017 {
1018 std::string es;
1019 std::string this_path;
1020 std::string that_path;
1021 paths_type::const_iterator cpi;
1022 configuration_manager::jobs_type::const_iterator cji;
1023 paths_type::const_iterator cjapi;
1024
1025 if (
1026 (
1027 (rsync_connection == connection_remote)
1028 || (rsync_connection == connection_server)
1029 || (rsync_connection == connection_ssh_local)
1030 )
1031 && (hostname.size() == 0)
1032 )
1033 {
1034 TRY_nomem(es = "rsync-connection-type references hostname, ");
1035 TRY_nomem(es += "but hostname is empty");
1036 throw(ERROR(0,es));
1037 }
1038
1039 if (rsync_connection == connection_ssh_local) {
1040 /*
1041 if (config.ssh_local_path.size() == 0) {
1042 TRY_nomem(es = "rsync-connection-type references remote-rsync-local-dir, ");
1043 TRY_nomem(es += "but ssh-local-path is empty");
1044 throw(ERROR(0,es));
1045 }
1046 */
1047
1048 if (excludes.size() != 0) {
1049 TRY_nomem(es = "rsync-connection-type is remote-rsync-local-dir, ");
1050 TRY_nomem(es += "but exclude files have been specified");
1051 throw(ERROR(0,es));
1052 }
1053
1054 if (includes.size() != 0) {
1055 TRY_nomem(es = "rsync-connection-type is remote-rsync-local-dir, ");
1056 TRY_nomem(es += "but include files have been specified");
1057 throw(ERROR(0,es));
1058 }
1059 }
1060
1061 if ((rsync_remote_module.size() != 0)
1062 && (rsync_connection != connection_server))
1063 {
1064 TRY_nomem(es = "rsync-remote-module specifies a module, but ");
1065 TRY_nomem(es = "rsync-connection-type is not server");
1066 throw(ERROR(0,es));
1067 }
1068
1069 if (paths.size() == 0) {
1070 throw(ERROR(0,"No paths defined for this job"));
1071 }
1072
1073 for (cpi = paths.begin() ; cpi != paths.end(); cpi++) {
1074 TRY_nomem(this_path = generate_archive_path(*cpi));
1075
1076 for (
1077 cji = config.jobs().begin();
1078 cji != config.jobs().end();
1079 cji++
1080 )
1081 {
1082 for (
1083 cjapi = cji->paths.begin();
1084 cjapi != cji->paths.end();
1085 cjapi++
1086 )
1087 {
1088 TRY_nomem(that_path = cji->generate_archive_path(*cjapi));
1089
1090 if (this_path == that_path) {
1091 error e(0);
1092
1093 TRY_nomem(es = "Duplicate archive-path values detected");
1094 e.push_back(ERROR_INSTANCE(es));
1095 TRY_nomem(es = "Archive path: \"");
1096 TRY_nomem(es += this_path);
1097 TRY_nomem(es += "\"");
1098 e.push_back(ERROR_INSTANCE(es));
1099 TRY_nomem(es = "Previously defined at ");
1100 TRY_nomem(es += cji->config_path);
1101 TRY_nomem(es += "[");
1102 TRY_nomem(es += estring(cji->config_line));
1103 TRY_nomem(es += "]");
1104 e.push_back(ERROR_INSTANCE(es));
1105 throw(e);
1106 }
1107
1108 if (
1109 (this_path.size() < that_path.size())
1110 && (this_path == that_path.substr(0,this_path.size()))
1111 && (
1112 (that_path[this_path.size()] == '/')
1113 || (this_path.size() == 0)
1114 )
1115 )
1116 {
1117 error e(0);
1118
1119 TRY_nomem(es = "Overlapping archive-path values detected");
1120 e.push_back(ERROR_INSTANCE(es));
1121 TRY_nomem(es = "Defined archive-path: \"");
1122 TRY_nomem(es += this_path);
1123 TRY_nomem(es += "\"");
1124 e.push_back(ERROR_INSTANCE(es));
1125 TRY_nomem(es = "Is in a parent directory of another job's previously defined archive-path");
1126 e.push_back(ERROR_INSTANCE(es));
1127 TRY_nomem(es = "At ");
1128 TRY_nomem(es += cji->config_path);
1129 TRY_nomem(es += "[");
1130 TRY_nomem(es += estring(cji->config_line));
1131 TRY_nomem(es += "]");
1132 e.push_back(ERROR_INSTANCE(es));
1133 throw(e);
1134 }
1135
1136 if (
1137 (this_path.size() > that_path.size())
1138 && (this_path.substr(0,that_path.size()) == that_path)
1139 && (
1140 (this_path[that_path.size()] == '/')
1141 || (that_path.size() == 0)
1142 )
1143 )
1144 {
1145 error e(0);
1146
1147 TRY_nomem(es = "Overlapping archive-path values detected");
1148 e.push_back(ERROR_INSTANCE(es));
1149 TRY_nomem(es = "Defined archive-path: \"");
1150 TRY_nomem(es += this_path);
1151 TRY_nomem(es += "\"");
1152 e.push_back(ERROR_INSTANCE(es));
1153 TRY_nomem(es = "Is in a subdirectory of another job's previously defined archive-path");
1154 e.push_back(ERROR_INSTANCE(es));
1155 TRY_nomem(es = "At ");
1156 TRY_nomem(es += cji->config_path);
1157 TRY_nomem(es += "[");
1158 TRY_nomem(es += estring(cji->config_line));
1159 TRY_nomem(es += "]");
1160 e.push_back(ERROR_INSTANCE(es));
1161 throw(e);
1162 }
1163 }
1164 }
1165 }
1166 }
1167
1168 //----------------------------------------------------------------------------
1169
1170 /** Reset configuration to default settings */
clear(void)1171 void configuration_manager::clear(void)
1172 {
1173 m_initialized = false;
1174 m_configs_read = 0;
1175 m_default = true;
1176 TRY_nomem(m_default_file = CONFIGFILE);
1177 m_action = action_help;
1178 m_timestamp.set();
1179 m_cfgfiles.clear();
1180 m_link_catalog_dir.erase();
1181 TRY_nomem(m_log_dir = LOGDIR);
1182 m_delete_old_log_files = false;
1183 m_delete_old_report_files = false;
1184 m_rsync_local_path.erase();
1185 m_delete_command_path.erase();
1186 if (strlen(LOCAL_RSYNC) > 0) {
1187 TRY_nomem(m_rsync_local_path = LOCAL_RSYNC);
1188 }
1189 m_ssh_local_path.erase();
1190 if (strlen(LOCAL_SSH) > 0) {
1191 TRY_nomem(m_ssh_local_path = LOCAL_SSH);
1192 }
1193 m_rsync_parallel = 1;
1194 m_io_poll_interval = 1;
1195 m_vaults.clear();
1196 m_vault_overflow_behavior = overflow_quit;
1197 m_vault_overflow_blocks = 10;
1198 m_vault_overflow_inodes = 10;
1199 m_vault_selection_behavior = selection_round_robin;
1200 m_vault_locking = true;
1201 m_default_job.clear();
1202 m_logging_level = logging_child;
1203 m_error_logging_level = logging_rsync;
1204 m_jobs.clear();
1205 }
1206
1207 /** C'tor */
configuration_manager()1208 configuration_manager::configuration_manager()
1209 {
1210 if (this != &config)
1211 throw(
1212 INTERNAL_ERROR(0,"Attempt to allocate multiple configuration managers")
1213 );
1214 clear();
1215 }
1216
1217 /** Initialize the configuration manager from rvm's command line options */
init(int argc,char const * argv[])1218 void configuration_manager::init(int argc, char const * argv[])
1219 {
1220 int c;
1221 estring opt;
1222 estring arg;
1223 std::string es;
1224 std::string tes;
1225 cfgfiles_type::const_iterator cfi;
1226 bool use_custom_timestamp = false;
1227 class timestamp custom_timestamp;
1228
1229 for (c = 1; c < argc; c++) {
1230 TRY_nomem(opt = argv[c]);
1231 if (c+1 == argc) {
1232 TRY_nomem(arg = "");
1233 }
1234 else {
1235 TRY_nomem(arg = argv[c+1]);
1236 }
1237
1238 if (opt == "--archive") {
1239 m_action = action_archive;
1240 }
1241 else if (opt == "--relink") {
1242 m_action = action_relink;
1243 }
1244 else if (opt == "--help") {
1245 m_action = action_help;
1246 }
1247 else if (opt == "--version") {
1248 m_action = action_version;
1249 }
1250 else if (opt == "--check-config") {
1251 m_action = action_check_config;
1252 }
1253 else if (opt == "--no-default-config") {
1254 m_default = false;
1255 }
1256 else if (opt == "--config") {
1257 directory dir;
1258 directory::const_iterator cdi;
1259
1260 TRY_nomem(es = "Error finding configuration file(s) ");
1261 TRY_nomem(es += "matching command line argument [");
1262 TRY_nomem(es += estring(c+1));
1263 TRY_nomem(es += "]: \"");
1264 TRY_nomem(es += arg);
1265 TRY_nomem(es += "\"");
1266
1267 try {
1268 dir.path(arg);
1269 }
1270 catch(error e) {
1271 e.push_back(ERROR_INSTANCE(es));
1272 throw(e);
1273 }
1274 catch(...) {
1275 error e = err_unknown;
1276
1277 e.push_back(ERROR_INSTANCE(es));
1278 throw(e);
1279 }
1280 if (dir.size() == 0) {
1281 TRY_nomem(es = "No configuration file(s) found matching ");
1282 TRY_nomem(es += "command line argument [");
1283 TRY_nomem(es += estring(c+1));
1284 TRY_nomem(es += "]: \"");
1285 TRY_nomem(es += arg);
1286 TRY_nomem(es += "\"");
1287
1288 throw(ERROR(0,es));
1289 }
1290 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
1291 TRY_nomem(m_cfgfiles.push_back(cfgfile_element(config_file, arg)));
1292 }
1293
1294 c++;
1295 }
1296 else if (opt == "--job") {
1297 directory dir;
1298 directory::const_iterator cdi;
1299
1300 TRY_nomem(es = "Error finding job file(s) ");
1301 TRY_nomem(es += "matching command line argument [");
1302 TRY_nomem(es += estring(c+1));
1303 TRY_nomem(es += "]: \"");
1304 TRY_nomem(es += arg);
1305 TRY_nomem(es += "\"");
1306
1307 try {
1308 dir.path(arg);
1309 }
1310 catch(error e) {
1311 e.push_back(ERROR_INSTANCE(es));
1312 throw(e);
1313 }
1314 catch(...) {
1315 error e = err_unknown;
1316 throw(e);
1317 }
1318 if (dir.size() == 0) {
1319 TRY_nomem(es = "No job file(s) found matching ");
1320 TRY_nomem(es += "command line argument [");
1321 TRY_nomem(es += estring(c+1));
1322 TRY_nomem(es += "]: \"");
1323 TRY_nomem(es += arg);
1324 TRY_nomem(es += "\"");
1325
1326 throw(ERROR(0,es));
1327 }
1328 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
1329 TRY_nomem(m_cfgfiles.push_back(cfgfile_element(job_file, arg)));
1330 }
1331
1332 c++;
1333 }
1334 else if (opt == "--timestamp") {
1335 TRY_nomem(es = "From command line argument ");
1336 TRY_nomem(es += estring(c+1));
1337 TRY(custom_timestamp.assign(arg),es);
1338 use_custom_timestamp = true;
1339 c++;
1340 TRY_nomem(tes = es);
1341 }
1342 else {
1343 TRY_nomem(es = "Unknown command line option: \"");
1344 TRY_nomem(es += opt);
1345 TRY_nomem(es += "\"");
1346 throw(ERROR(0,es));
1347 }
1348 }
1349
1350 m_initialized = true;
1351
1352 if ((m_action == action_help) || (m_action == action_version))
1353 return;
1354
1355 if (use_default())
1356 read_config(m_default_file);
1357
1358 for (cfi = m_cfgfiles.begin(); cfi != m_cfgfiles.end(); cfi++) {
1359 if (cfi->first == config_file) {
1360 read_config(cfi->second);
1361 }
1362 else {
1363 read_job(cfi->second);
1364 }
1365 }
1366
1367 if (m_configs_read == 0) {
1368 throw(ERROR(0,"No configuration file(s) read"));
1369 }
1370
1371 if (use_custom_timestamp) {
1372 TRY(m_timestamp = custom_timestamp,tes);
1373 }
1374
1375 check();
1376 }
1377
1378 /** Perform sanity checks on configuration settings */
check(void) const1379 void configuration_manager::check(void) const
1380 {
1381 std::string es;
1382 subdirectory subdir;
1383 filestatus fstat;
1384
1385 if (!initialized())
1386 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1387
1388 // log-dir -- If no log-dir is set then we use a default. What if that
1389 // default doesn't exist?
1390 if (m_log_dir.size() == 0) {
1391 throw(ERROR(0,"log-dir not set"));
1392 }
1393 TRY_nomem(es = "Invalid log-dir value: \"");
1394 TRY_nomem(es += m_log_dir);
1395 TRY_nomem(es += "\"");
1396 TRY_instead(subdir.path(m_log_dir,"*"),es);
1397
1398 // rsync-local-path -- If (a) rsync-local-path is not set from config, and
1399 // (b) a default was determined at compile time, then (c) does the default
1400 // exist?
1401 TRY_nomem(es = "Invalid rsync-local-path value: \"");
1402 TRY_nomem(es += m_rsync_local_path);
1403 TRY_nomem(es += "\"");
1404 TRY(fstat.path(m_rsync_local_path),es);
1405
1406 // delete-command-path -- If delete-command-path is set, check to see if the
1407 // command exists
1408 if (m_delete_command_path.size() > 0) {
1409 TRY_nomem(es = "Invalid delete-command-path value: \"");
1410 TRY_nomem(es += m_delete_command_path);
1411 TRY_nomem(es += "\"");
1412 TRY(fstat.path(m_delete_command_path),es);
1413 }
1414
1415 // ssh-local-path -- If (a) ssh-local-path is not set from config, and
1416 // (b) a default was determined at compile time, then (c) does the default
1417 // exist?
1418 //
1419 // TODO: NOT SURE WE SHOULD CARE ABOUT THIS HERE. What if the end user
1420 // doesn't want or need to have ssh installed on their machine?
1421 /*
1422 TRY_nomem(es = "Invalid ssh-local-path value: \"");
1423 TRY_nomem(es += m_ssh_local_path);
1424 TRY_nomem(es += "\"");
1425 TRY(fstat.path(m_ssh_local_path),es);
1426 */
1427
1428 // vault -- Are there any?
1429 if (m_vaults.size() == 0) {
1430 throw(ERROR(0,"No vaults defined"));
1431 }
1432
1433 // Do all jobs generate a valid job ID?
1434 // Note: This is purely cosmetic, generated job ID strings are only used in
1435 // status reports and report logs.
1436 if (jobs().size() > 0) {
1437 jobs_type::const_iterator cji;
1438
1439 for (cji = jobs().begin(); cji != jobs().end(); cji++) {
1440 if (cji->generate_job_id().size() == 0) {
1441 error e(0);
1442
1443 TRY_nomem(es = "Empty ID generated by job at ");
1444 TRY_nomem(es += cji->config_path);
1445 TRY_nomem(es += "[");
1446 TRY_nomem(es += estring(cji->config_line));
1447 TRY_nomem(es += "]");
1448 e.push_back(es);
1449 TRY_nomem(es =
1450 "Use 'jobname' to assign a descriptive name to this job");
1451 e.push_back(es);
1452 throw(e);
1453 }
1454 }
1455 }
1456 }
1457
1458 /** Return the initialized state of the configuration manager */
initialized(void) const1459 const bool configuration_manager::initialized(void) const
1460 {
1461 return(m_initialized);
1462 }
1463
1464 /** Return the action rvm is to take */
1465 const configuration_manager::action_type
action(void) const1466 configuration_manager::action(void) const
1467 {
1468 if (!initialized())
1469 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1470
1471 return(m_action);
1472 }
1473
1474 /** Return whether or not rvm is to try to read it's default configuration file */
use_default(void) const1475 const bool configuration_manager::use_default(void) const
1476 {
1477 if (!initialized())
1478 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1479
1480 return(m_default);
1481 }
1482
1483 /** Set the default configuration filename */
default_file(const std::string & a_path)1484 void configuration_manager::default_file(const std::string& a_path)
1485 {
1486 TRY_nomem(m_default_file = a_path);
1487 }
1488
1489 /** Return the default configuration filename */
default_file(void) const1490 const std::string& configuration_manager::default_file(void) const
1491 {
1492 if (!initialized())
1493 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1494
1495 return(m_default_file);
1496 }
1497
1498 /** Return the default log-dir */
default_logdir(const std::string & a_path)1499 void configuration_manager::default_logdir(const std::string& a_path)
1500 {
1501 TRY_nomem(m_log_dir = a_path);
1502 }
1503
1504 /** Return the timestamp of this instance of rvm */
timestamp(void) const1505 const class timestamp& configuration_manager::timestamp(void) const
1506 {
1507 if (!initialized())
1508 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1509
1510 return(m_timestamp);
1511 }
1512
1513 /** Return the link-catalog-dir path */
link_catalog_dir(void) const1514 const std::string& configuration_manager::link_catalog_dir(void) const
1515 {
1516 if (!initialized())
1517 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1518
1519 return(m_link_catalog_dir);
1520 }
1521
1522 /** Return the log-dir path */
log_dir(void) const1523 const std::string& configuration_manager::log_dir(void) const
1524 {
1525 if (!initialized())
1526 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1527
1528 return(m_log_dir);
1529 }
1530
1531 /** Return the value of delete-old-log-files */
delete_old_log_files(void) const1532 const bool configuration_manager::delete_old_log_files(void) const
1533 {
1534 if (!initialized())
1535 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1536
1537 return(m_delete_old_log_files);
1538 }
1539
1540 /** Return the value of delete-old-report-files */
delete_old_report_files(void) const1541 const bool configuration_manager::delete_old_report_files(void) const
1542 {
1543 if (!initialized())
1544 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1545
1546 return(m_delete_old_report_files);
1547 }
1548
1549 /** Return the rsync-local-path */
rsync_local_path(void) const1550 const std::string& configuration_manager::rsync_local_path(void) const
1551 {
1552 if (!initialized())
1553 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1554
1555 return(m_rsync_local_path);
1556 }
1557
1558 /** Return the delete-command-path */
delete_command_path(void) const1559 const std::string& configuration_manager::delete_command_path(void) const
1560 {
1561 if (!initialized())
1562 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1563
1564 return(m_delete_command_path);
1565 }
1566
1567 /** Return the ssh-local-path */
ssh_local_path(void) const1568 const std::string& configuration_manager::ssh_local_path(void) const
1569 {
1570 if (!initialized())
1571 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1572
1573 return(m_ssh_local_path);
1574 }
1575
1576 /** Return the rsync-parallel */
rsync_parallel(void) const1577 const uint16& configuration_manager::rsync_parallel(void) const
1578 {
1579 if (!initialized())
1580 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1581
1582 return(m_rsync_parallel);
1583 }
1584
1585 /** Return the number of seconds to sleep between polling for I/O */
io_poll_interval(void) const1586 const uint16& configuration_manager::io_poll_interval(void) const
1587 {
1588 if (!initialized())
1589 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1590
1591 return(m_io_poll_interval);
1592 }
1593
1594 /** Return the timestamp-resolution */
timestamp_resolution(void) const1595 const timestamp::resolution_type configuration_manager::timestamp_resolution(void) const
1596 {
1597 if (!initialized())
1598 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1599
1600 return(m_timestamp.resolution());
1601 }
1602
1603 /** Return the vaults */
vaults(void) const1604 const configuration_manager::vaults_type& configuration_manager::vaults(void) const
1605 {
1606 if (!initialized())
1607 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1608
1609 return(m_vaults);
1610 }
1611
1612 /** Return the vault-overflow-behavior */
vault_overflow_behavior(void) const1613 const configuration_manager::overflow_type& configuration_manager::vault_overflow_behavior(void) const
1614 {
1615 if (!initialized())
1616 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1617
1618 return(m_vault_overflow_behavior);
1619 }
1620
1621 /** Return the vault-overflow-blocks */
vault_overflow_blocks(void) const1622 const uint16& configuration_manager::vault_overflow_blocks(void) const
1623 {
1624 if (!initialized())
1625 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1626
1627 return(m_vault_overflow_blocks);
1628 }
1629
1630 /** Return the vault-overflow-inodes */
vault_overflow_inodes(void) const1631 const uint16& configuration_manager::vault_overflow_inodes(void) const
1632 {
1633 if (!initialized())
1634 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1635
1636 return(m_vault_overflow_inodes);
1637 }
1638
1639 /** Return the vault-selection-behavior */
vault_selection_behavior(void) const1640 const configuration_manager::selection_type& configuration_manager::vault_selection_behavior(void) const
1641 {
1642 if (!initialized())
1643 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1644
1645 return(m_vault_selection_behavior);
1646 }
1647
1648 /** Return the vault-locking selection */
vault_locking(void) const1649 const bool configuration_manager::vault_locking(void) const
1650 {
1651 if (!initialized())
1652 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1653
1654 return(m_vault_locking);
1655 }
1656
1657 /** Return the default job configuration */
default_job(void) const1658 const job& configuration_manager::default_job(void) const
1659 {
1660 if (!initialized())
1661 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1662
1663 return(m_default_job);
1664 }
1665
1666 /** Return a list of jobs */
jobs(void) const1667 const configuration_manager::jobs_type& configuration_manager::jobs(void) const
1668 {
1669 if (!initialized())
1670 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1671
1672 return(m_jobs);
1673 }
1674
1675 /** Return the logging-level */
logging_level(void) const1676 const configuration_manager::logging_type& configuration_manager::logging_level(void) const
1677 {
1678 if (!initialized())
1679 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1680
1681 return(m_logging_level);
1682 }
1683
1684 /** Return the error-logging-level */
error_logging_level(void) const1685 const configuration_manager::logging_type& configuration_manager::error_logging_level(void) const
1686 {
1687 if (!initialized())
1688 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
1689
1690 return(m_error_logging_level);
1691 }
1692
1693 /** Read a configuration file */
read_config(const std::string & a_path)1694 void configuration_manager::read_config(const std::string& a_path)
1695 {
1696 std::string es;
1697 std::ifstream in;
1698 uint16 line = 0;
1699
1700 in.open(a_path.c_str());
1701 if (!in.is_open()) {
1702 TRY_nomem(es = "Could not open configuration file: \"");
1703 TRY_nomem(es += a_path);
1704 TRY_nomem(es += "\"");
1705 throw(ERROR(errno,es));
1706 }
1707
1708 global_parser(a_path, line, in);
1709
1710 in.close();
1711 m_configs_read++;
1712 }
1713
1714 /** Read a job configuration file */
read_job(const std::string & a_path)1715 void configuration_manager::read_job(const std::string& a_path)
1716 {
1717 std::string es;
1718 std::ifstream in;
1719 job njob;
1720 uint16 line = 0;
1721
1722 in.open(a_path.c_str());
1723 if (!in.is_open()) {
1724 TRY_nomem(es = "Could not open job file: \"");
1725 TRY_nomem(es += a_path);
1726 TRY_nomem(es += "\"");
1727 throw(ERROR(errno,es));
1728 }
1729
1730 njob = m_default_job;
1731
1732 job_parser(&njob, a_path, line, in, "", false);
1733
1734 njob.check();
1735
1736 in.close();
1737 }
1738
1739 //----------------------------------------------------------------------------
1740
1741 /** Parse a keyword/value pair read from a configuration file */
parse_line(std::istream & a_in,std::string & a_keyword,std::string & a_value)1742 void parse_line(
1743 std::istream& a_in,
1744 std::string& a_keyword,
1745 std::string& a_value
1746 )
1747 {
1748 std::string es;
1749 char ch;
1750
1751 a_keyword.erase();
1752 a_value.erase();
1753
1754 while (true) {
1755 a_in.get(ch);
1756 // Skip whitespace before keyword.
1757 while ((a_in) && ((ch == ' ') || (ch == '\t'))) {
1758 a_in.get(ch);
1759 }
1760 // Read keyword
1761 while ((a_in) && ((ch != ' ') && (ch != '\t') && (ch != '\n'))) {
1762 TRY_nomem(a_keyword += ch);
1763 a_in.get(ch);
1764 }
1765 if (!a_in)
1766 return;
1767
1768 // If keyword is empty then this is a blank line, skip it.
1769 if (a_keyword.size() == 0)
1770 return;
1771
1772 // If keyword starts with a '#' then this is a comment line, skip it.
1773 if (a_keyword[0] == '#') {
1774 // Discard the rest of the line.
1775 while ((a_in) && (ch != '\n')) {
1776 a_in.get(ch);
1777 }
1778 return;
1779 }
1780
1781 a_keyword = estring(a_keyword).lower();
1782
1783 // If there is not value, return.
1784 if (ch == '\n')
1785 return;
1786
1787 // Skip whitespace between keyword and value.
1788 a_in.get(ch);
1789 while ((a_in) && ((ch == ' ') || (ch == '\t'))) {
1790 a_in.get(ch);
1791 }
1792 if (!a_in)
1793 return;
1794 a_in.putback(ch);
1795 if (!a_in)
1796 return;
1797
1798 // Read value
1799 a_in.get(ch);
1800 while ((a_in) && (ch != '\n')) {
1801 TRY_nomem(a_value += ch);
1802 a_in.get(ch);
1803 }
1804 return;
1805 }
1806 }
1807
1808 /** Given a path, strip off the basename -- like the dirname UNIX command */
parse_dirname(const std::string & a_path)1809 const std::string parse_dirname(const std::string& a_path)
1810 {
1811 std::string rpath;
1812
1813 TRY_nomem(rpath = a_path);
1814
1815 if (rpath.find_last_of('/') != std::string::npos)
1816 rpath.erase(rpath.find_last_of('/'));
1817
1818 if (rpath.size() == 0) {
1819 TRY_nomem(rpath = "./");
1820 return(rpath);
1821 }
1822
1823 if (rpath[rpath.size()-1] != '/') {
1824 TRY_nomem(rpath += '/');
1825 }
1826
1827 return(rpath);
1828 }
1829
1830 /** Given a path, strip off the directory -- like the basename UNIX commane */
parse_basename(const std::string & a_path)1831 const std::string parse_basename(const std::string& a_path)
1832 {
1833 std::string es;
1834 std::string rpath;
1835
1836 TRY_nomem(rpath = a_path);
1837
1838 if (rpath.find_last_of('/') != std::string::npos)
1839 rpath.erase(0,rpath.find_last_of('/'));
1840
1841 return(rpath);
1842 }
1843
1844 //----------------------------------------------------------------------------
1845
1846 /** C'tor */
global_parser(const std::string & a_path,uint16 & a_line,std::istream & a_in)1847 global_parser::global_parser(
1848 const std::string& a_path,
1849 uint16& a_line,
1850 std::istream& a_in
1851 )
1852 {
1853 m_in = &a_in;
1854 m_path = &a_path;
1855 m_line = &a_line;
1856
1857 parse();
1858 }
1859
1860 /** Generate a string showing the current location within a configuration file
1861 * of the parser */
location(void)1862 const std::string global_parser::location(void)
1863 {
1864 std::string es;
1865
1866 TRY_nomem(es = "At ");
1867 TRY_nomem(es += (*m_path));
1868 TRY_nomem(es += "[");
1869 TRY_nomem(es += estring((*m_line)));
1870 TRY_nomem(es += "]");
1871
1872 return(es);
1873 }
1874
1875 /** Read a configuration file, used by the "include" command */
read_config(const std::string & a_path)1876 void global_parser::read_config(const std::string& a_path)
1877 {
1878 std::string es;
1879 std::ifstream in;
1880 uint16 line = 0;
1881
1882 in.open(a_path.c_str());
1883 if (!in.is_open()) {
1884 TRY_nomem(es = "Could not open configuraiton file: \"");
1885 TRY_nomem(es += a_path);
1886 TRY_nomem(es += "\"");
1887 throw(ERROR(errno,es));
1888 }
1889
1890 global_parser(a_path, line, in);
1891
1892 in.close();
1893 config.m_configs_read++;
1894 }
1895
1896 /** Read a job configuration file, used by the "include-job" command */
read_job(const std::string & a_path,job & a_job)1897 void global_parser::read_job(const std::string& a_path, job& a_job)
1898 {
1899 std::string es;
1900 std::ifstream in;
1901 uint16 line = 0;
1902
1903 in.open(a_path.c_str());
1904 if (!in.is_open()) {
1905 TRY_nomem(es = "Could not open job file: \"");
1906 TRY_nomem(es += a_path);
1907 TRY_nomem(es += "\"");
1908 throw(ERROR(errno,es));
1909 }
1910
1911 job_parser(&a_job, a_path, line, in, "", false);
1912
1913 TRY_nomem(a_job.config_path = *m_path);
1914 TRY_nomem(a_job.config_line = *m_line);
1915 a_job.check();
1916
1917 in.close();
1918 }
1919
1920 /** Global context parser */
parse(void)1921 void global_parser::parse(void)
1922 {
1923 std::string es;
1924 std::string keyword;
1925 std::string value;
1926
1927 while ((*m_in)) {
1928 (*m_line)++;
1929
1930 try {
1931 parse_line((*m_in), keyword, value);
1932 }
1933 catch(error e) {
1934 e.push_back(ERROR_INSTANCE(location()));
1935 throw(e);
1936 }
1937 catch(...) {
1938 error e = err_unknown;
1939
1940 e.push_back(ERROR_INSTANCE(location()));
1941 throw(e);
1942 }
1943
1944 if (keyword.size() == 0)
1945 continue;
1946 if (keyword[0] == '#')
1947 continue;
1948
1949 try {
1950 if (keyword == "<default>") {
1951 parse_default(value);
1952 }
1953 else if (keyword == "include") {
1954 parse_include(value);
1955 }
1956 else if (keyword == "include-job") {
1957 parse_include_job(value);
1958 }
1959 else if (keyword == "<job>") {
1960 parse_job(value);
1961 }
1962 else if (keyword == "link-catalog-dir") {
1963 parse_link_catalog_dir(value);
1964 }
1965 else if (keyword == "log-dir") {
1966 parse_log_dir(value);
1967 }
1968 else if (keyword == "delete-old-log-files") {
1969 parse_delete_old_log_files(value);
1970 }
1971 else if (keyword == "delete-old-report-files") {
1972 parse_delete_old_report_files(value);
1973 }
1974 else if (keyword == "logging-level") {
1975 parse_logging_level(value);
1976 }
1977 else if (keyword == "error-logging-level") {
1978 parse_error_logging_level(value);
1979 }
1980 else if (keyword == "rsync-local-path") {
1981 parse_rsync_local_path(value);
1982 }
1983 else if (keyword == "delete-command-path") {
1984 parse_delete_command_path(value);
1985 }
1986 else if (keyword == "ssh-local-path") {
1987 parse_ssh_local_path(value);
1988 }
1989 else if (keyword == "rsync-parallel") {
1990 parse_rsync_parallel(value);
1991 }
1992 else if (keyword == "io-poll-interval") {
1993 parse_io_poll_interval(value);
1994 }
1995 else if (keyword == "timestamp-resolution") {
1996 parse_timestamp_resolution(value);
1997 }
1998 else if (keyword == "vault") {
1999 try {
2000 parse_vault(value);
2001 }
2002 catch(error e) {
2003 std::cerr << e;
2004 }
2005 catch(...) {
2006 throw(err_unknown);
2007 }
2008 }
2009 else if (keyword == "vault-overflow-behavior") {
2010 parse_vault_overflow_behavior(value);
2011 }
2012 else if (keyword == "vault-overflow-blocks") {
2013 parse_vault_overflow_blocks(value);
2014 }
2015 else if (keyword == "vault-overflow-inodes") {
2016 parse_vault_overflow_inodes(value);
2017 }
2018 else if (keyword == "vault-selection-behavior") {
2019 parse_vault_selection_behavior(value);
2020 }
2021 else if (keyword == "vault-locking") {
2022 parse_vault_locking(value);
2023 }
2024 else {
2025 error e(0);
2026
2027 TRY_nomem(es = "Unknown command in global context: \"");
2028 TRY_nomem(es += keyword);
2029 TRY_nomem(es += "\"");
2030 throw(ERROR(0,es));
2031 }
2032 }
2033 catch(error e) {
2034 e.push_back(ERROR_INSTANCE(location()));
2035 throw(e);
2036 }
2037 catch(...) {
2038 error e = err_unknown;
2039
2040 e.push_back(ERROR_INSTANCE(location()));
2041 throw(e);
2042 }
2043 }
2044 }
2045
2046 /** Parse a default job context */
parse_default(const std::string & a_value)2047 void global_parser::parse_default(const std::string& a_value)
2048 {
2049 std::string delimiter;
2050 std::string default_config_path;
2051 uint16 default_config_line;
2052 job* jobPtr = &config.m_default_job;
2053
2054 config.m_default_job.clear();
2055
2056 TRY_nomem(default_config_path = *m_path);
2057 TRY_nomem(default_config_line = *m_line);
2058 TRY_nomem(delimiter = "</default>");
2059
2060 job_parser(jobPtr, *m_path, *m_line, *m_in, delimiter, true);
2061
2062 TRY_nomem(jobPtr->default_config_path = default_config_path);
2063 jobPtr->default_config_line = default_config_line;
2064 }
2065
2066 /** Parse an "include" command */
parse_include(const std::string & a_value)2067 void global_parser::parse_include(const std::string& a_value)
2068 {
2069 std::string es;
2070 directory dir;
2071 directory::const_iterator cdi;
2072 std::string rpath;
2073 std::string ipath;
2074
2075 if (a_value.size() == 0) {
2076 TRY_nomem(es = "Invalid include path: \"");
2077 TRY_nomem(es += a_value);
2078 TRY_nomem(es += "\"");
2079 throw(ERROR(0,es));
2080 }
2081
2082 if (a_value[0] != '/') {
2083 TRY_nomem(rpath = parse_dirname(*m_path));
2084 }
2085
2086 TRY_nomem(ipath = rpath + a_value);
2087
2088 TRY_nomem(es = "No configuration file(s) found: \"");
2089 TRY_nomem(es += a_value);
2090 TRY_nomem(es += "\"");
2091 TRY_instead(dir.path(ipath),es);
2092 if (dir.size() == 0)
2093 throw(ERROR(0,es));
2094
2095 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
2096 if (dir.size() > 1) {
2097 TRY_nomem(es = "For files found matching: \"");
2098 TRY_nomem(es += a_value);
2099 TRY_nomem(es += "\"");
2100 try {
2101 read_config(*cdi);
2102 }
2103 catch(error e) {
2104 e.push_back(ERROR_INSTANCE(es));
2105 throw(e);
2106 }
2107 catch(...) {
2108 error e = err_unknown;
2109
2110 e.push_back(ERROR_INSTANCE(es));
2111 throw(e);
2112 }
2113 }
2114 else
2115 read_config(*cdi);
2116 }
2117 }
2118
2119 /** Parse an "include-job" command */
parse_include_job(const std::string & a_value)2120 void global_parser::parse_include_job(const std::string& a_value)
2121 {
2122 std::string es;
2123 directory dir;
2124 directory::const_iterator cdi;
2125 std::string rpath;
2126 std::string ipath;
2127
2128 if (a_value.size() == 0) {
2129 TRY_nomem(es = "Invalid include-job path: \"");
2130 TRY_nomem(es += a_value);
2131 TRY_nomem(es += "\"");
2132 throw(ERROR(0,es));
2133 }
2134
2135 if (a_value[0] != '/') {
2136 TRY_nomem(rpath = parse_dirname(*m_path));
2137 }
2138
2139 TRY_nomem(ipath = rpath + a_value);
2140
2141 TRY_nomem(es = "No job file(s) found: \"");
2142 TRY_nomem(es += a_value);
2143 TRY_nomem(es += "\"");
2144 TRY(dir.path(ipath),es);
2145 if (dir.size() == 0)
2146 throw(ERROR(0,es));
2147
2148 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
2149 job new_job;
2150
2151 new_job = config.m_default_job;
2152
2153 if (dir.size() > 1) {
2154 TRY_nomem(es = "For files found matching: \"");
2155 TRY_nomem(es += a_value);
2156 TRY_nomem(es += "\"");
2157 try {
2158 read_job(*cdi, new_job);
2159 }
2160 catch(error e) {
2161 e.push_back(ERROR_INSTANCE(es));
2162 throw(e);
2163 }
2164 catch(...) {
2165 error e = err_unknown;
2166
2167 e.push_back(ERROR_INSTANCE(es));
2168 throw(e);
2169 }
2170 }
2171 else
2172 read_job(*cdi, new_job);
2173 TRY_nomem(config.m_jobs.push_back(new_job));
2174 }
2175 }
2176
2177 /** Parse a job context */
parse_job(const std::string & a_value)2178 void global_parser::parse_job(const std::string& a_value)
2179 {
2180 std::string delimiter;
2181 std::string config_path;
2182 uint16 config_line;
2183 job new_job;
2184
2185 TRY_nomem(config_path = *m_path);
2186 config_line = *m_line;
2187 new_job = config.m_default_job;
2188
2189 TRY_nomem(new_job.config_path = config_path);
2190 new_job.config_line = config_line;
2191 TRY_nomem(delimiter = "</job>");
2192
2193 job_parser(&new_job, *m_path, *m_line, *m_in, delimiter, false);
2194
2195 new_job.check();
2196
2197 TRY_nomem(config.m_jobs.push_back(new_job));
2198 }
2199
2200 /** Parse a "link-catalog-dir" command */
parse_link_catalog_dir(const std::string & a_value)2201 void global_parser::parse_link_catalog_dir(const std::string& a_value)
2202 {
2203 std::string es;
2204 subdirectory subdir;
2205
2206 TRY_nomem(es = "Invalid link-catalog-dir path: \"");
2207 TRY_nomem(es += a_value);
2208 TRY_nomem(es += "\"");
2209 TRY_instead(subdir.path(a_value,"*"),es);
2210 TRY_nomem(config.m_link_catalog_dir = a_value);
2211 }
2212
2213 /** Parse a "log-dir" command */
parse_log_dir(const std::string & a_value)2214 void global_parser::parse_log_dir(const std::string& a_value)
2215 {
2216 std::string es;
2217 subdirectory subdir;
2218
2219 TRY_nomem(es = "Invalid log-dir path: \"");
2220 TRY_nomem(es += a_value);
2221 TRY_nomem(es += "\"");
2222 TRY_instead(subdir.path(a_value,"*"),es);
2223 TRY_nomem(config.m_log_dir = a_value);
2224 }
2225
2226 /** Parse a "delete-old-log-files" command */
parse_delete_old_log_files(const std::string & a_value)2227 void global_parser::parse_delete_old_log_files(const std::string& a_value)
2228 {
2229 std::string es;
2230 estring str;
2231
2232 TRY_nomem(es = "Invalid delete-old-log-files value: \"");
2233 TRY_nomem(es += a_value);
2234 TRY_nomem(es += "\"");
2235 TRY(str = estring(a_value).lower(),es);
2236 if (
2237 (str == "y")
2238 || (str == "yes")
2239 || (str == "t")
2240 || (str == "true")
2241 || (str == "1")
2242 || (str == "on")
2243 ) {
2244 config.m_delete_old_log_files = true;
2245 }
2246 else if (
2247 (str == "n")
2248 || (str == "no")
2249 || (str == "f")
2250 || (str == "false")
2251 || (str == "0")
2252 || (str == "off")
2253 ) {
2254 config.m_delete_old_log_files = false;
2255 }
2256 else {
2257 throw(ERROR(0,es));
2258 }
2259 }
2260
2261 /** Parse a "delete-old-report-files" command */
parse_delete_old_report_files(const std::string & a_value)2262 void global_parser::parse_delete_old_report_files(const std::string& a_value)
2263 {
2264 std::string es;
2265 estring str;
2266
2267 TRY_nomem(es = "Invalid delete-old-report-files value: \"");
2268 TRY_nomem(es += a_value);
2269 TRY_nomem(es += "\"");
2270 TRY(str = estring(a_value).lower(),es);
2271 if (
2272 (str == "y")
2273 || (str == "yes")
2274 || (str == "t")
2275 || (str == "true")
2276 || (str == "1")
2277 || (str == "on")
2278 ) {
2279 config.m_delete_old_report_files = true;
2280 }
2281 else if (
2282 (str == "n")
2283 || (str == "no")
2284 || (str == "f")
2285 || (str == "false")
2286 || (str == "0")
2287 || (str == "off")
2288 ) {
2289 config.m_delete_old_report_files = false;
2290 }
2291 else {
2292 throw(ERROR(0,es));
2293 }
2294 }
2295
2296 /** Parse a "loging-level" command */
parse_logging_level(const std::string & a_value)2297 void global_parser::parse_logging_level(const std::string& a_value)
2298 {
2299 std::string es;
2300 estring str;
2301
2302 TRY_nomem(es = "Invalid logging-level value: \"");
2303 TRY_nomem(es += a_value);
2304 TRY_nomem(es += "\"");
2305 TRY_nomem(str = estring(a_value).lower());
2306 if (str == "manager") {
2307 config.m_logging_level = configuration_manager::logging_manager;
2308 }
2309 else if (str == "child") {
2310 config.m_logging_level = configuration_manager::logging_child;
2311 }
2312 else if (str == "rsync") {
2313 config.m_logging_level = configuration_manager::logging_rsync;
2314 }
2315 else {
2316 throw(ERROR(0,es));
2317 }
2318 }
2319
2320 /** Parse a "error-loging-level" command */
parse_error_logging_level(const std::string & a_value)2321 void global_parser::parse_error_logging_level(const std::string& a_value)
2322 {
2323 std::string es;
2324 estring str;
2325
2326 TRY_nomem(es = "Invalid error-logging-level value: \"");
2327 TRY_nomem(es += a_value);
2328 TRY_nomem(es += "\"");
2329 TRY_nomem(str = estring(a_value).lower());
2330 if (str == "manager") {
2331 config.m_error_logging_level = configuration_manager::logging_manager;
2332 }
2333 else if (str == "child") {
2334 config.m_error_logging_level = configuration_manager::logging_child;
2335 }
2336 else if (str == "rsync") {
2337 config.m_error_logging_level = configuration_manager::logging_rsync;
2338 }
2339 else {
2340 throw(ERROR(0,es));
2341 }
2342 }
2343
2344 /** Parse an "rsync-local-path" command */
parse_rsync_local_path(const std::string & a_value)2345 void global_parser::parse_rsync_local_path(const std::string& a_value)
2346 {
2347 std::string es;
2348 filestatus fstat;
2349
2350 TRY_nomem(es = "Invalid rsync-local-path value: \"");
2351 TRY_nomem(es += a_value);
2352 TRY_nomem(es += "\"");
2353 TRY_instead(fstat.path(a_value),es);
2354 TRY_nomem(config.m_rsync_local_path = a_value);
2355 }
2356
2357 /** Parse an "delete-command-path" command */
parse_delete_command_path(const std::string & a_value)2358 void global_parser::parse_delete_command_path(const std::string& a_value)
2359 {
2360 std::string es;
2361 filestatus fstat;
2362
2363 TRY_nomem(es = "Invalid delete-command-path value: \"");
2364 TRY_nomem(es += a_value);
2365 TRY_nomem(es += "\"");
2366 TRY_instead(fstat.path(a_value),es);
2367 TRY_nomem(config.m_delete_command_path = a_value);
2368 }
2369
2370 /** Parse an "ssh-local-path" command */
parse_ssh_local_path(const std::string & a_value)2371 void global_parser::parse_ssh_local_path(const std::string& a_value)
2372 {
2373 std::string es;
2374 filestatus fstat;
2375
2376 TRY_nomem(es = "Invalid ssh-local-path value: \"");
2377 TRY_nomem(es += a_value);
2378 TRY_nomem(es += "\"");
2379 TRY_instead(fstat.path(a_value),es);
2380 TRY_nomem(config.m_ssh_local_path = a_value);
2381 }
2382
2383 /** Parse an "rsync-parallel" command */
parse_rsync_parallel(const std::string & a_value)2384 void global_parser::parse_rsync_parallel(const std::string& a_value)
2385 {
2386 std::string es;
2387 uint16 num;
2388
2389 TRY_nomem(es = "Invalid rsync-parallel value: \"");
2390 TRY_nomem(es += a_value);
2391 TRY_nomem(es += "\"");
2392 TRY_instead(num = estring(a_value),es);
2393 if (num == 0)
2394 throw(ERROR(0,es));
2395 config.m_rsync_parallel = num;
2396 }
2397
2398 /** Parse "io-poll-interval" command */
parse_io_poll_interval(const std::string & a_value)2399 void global_parser::parse_io_poll_interval(const std::string& a_value)
2400 {
2401 std::string es;
2402 uint16 num;
2403
2404 TRY_nomem(es = "Invalid io-poll-interval value: \"");
2405 TRY_nomem(es += a_value);
2406 TRY_nomem(es += "\"");
2407 TRY_instead(num = estring(a_value),es);
2408 if (num == 0)
2409 throw(ERROR(0,es));
2410 config.m_io_poll_interval = num;
2411 }
2412
2413 /** Parse a "timestamp-resolution" command */
parse_timestamp_resolution(const std::string & a_value)2414 void global_parser::parse_timestamp_resolution(const std::string& a_value)
2415 {
2416 std::string es;
2417 estring str;
2418
2419 TRY_nomem(es = "Invalid timestamp-resolution: \"");
2420 TRY_nomem(es += a_value);
2421 TRY_nomem(es += "\"");
2422 TRY(str = estring(a_value).lower(),es);
2423 if (str == "year") {
2424 config.m_timestamp.resolution(timestamp::resolution_year);
2425 }
2426 else if (str == "month") {
2427 config.m_timestamp.resolution(timestamp::resolution_month);
2428 }
2429 else if (str == "day") {
2430 config.m_timestamp.resolution(timestamp::resolution_day);
2431 }
2432 else if (str == "hour") {
2433 config.m_timestamp.resolution(timestamp::resolution_hour);
2434 }
2435 else if (str == "minute") {
2436 config.m_timestamp.resolution(timestamp::resolution_minute);
2437 }
2438 else if (str == "second") {
2439 config.m_timestamp.resolution(timestamp::resolution_second);
2440 }
2441 else {
2442 throw(ERROR(0,es));
2443 }
2444 }
2445
2446 /** Parse a "vault" command */
parse_vault(const std::string & a_value)2447 void global_parser::parse_vault(const std::string& a_value)
2448 {
2449 std::string es;
2450 directory dir;
2451 directory::const_iterator cdi;
2452 subdirectory subdir;
2453 filestatus fstat;
2454 error partial_vault_error = ERROR(0,"One or more vault paths were found to be invalid:");
2455
2456 TRY_nomem(es = "Invalid vault path: \"");
2457 TRY_nomem(es += a_value);
2458 TRY_nomem(es += "\"");
2459
2460 if (a_value.size() == 0)
2461 throw(ERROR(0,es));
2462
2463 TRY(dir.path(a_value),es);
2464
2465 if (dir.size() == 0) {
2466 TRY_instead(fstat.path(a_value),es);
2467 }
2468
2469 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
2470 if (!is_dir(*cdi)) {
2471 TRY_nomem(es = get_error_str(ENOTDIR));
2472 TRY_nomem(es += ": \"");
2473 TRY_nomem(es += *cdi);
2474 TRY_nomem(es += "\"");
2475 partial_vault_error.push_back(es);
2476 continue;
2477 }
2478 if (!readable(*cdi)) {
2479 TRY_nomem(es = "No read access to path: \"");
2480 TRY_nomem(es += *cdi);
2481 TRY_nomem(es += "\"");
2482 partial_vault_error.push_back(es);
2483 continue;
2484 }
2485 if (!writable(*cdi)) {
2486 TRY_nomem(es = "No write access to path: \"");
2487 TRY_nomem(es += *cdi);
2488 TRY_nomem(es += "\"");
2489 partial_vault_error.push_back(es);
2490 continue;
2491 }
2492 if (!executable(*cdi)) {
2493 TRY_nomem(es = "No execution access to path: \"");
2494 TRY_nomem(es += *cdi);
2495 TRY_nomem(es += "\"");
2496 partial_vault_error.push_back(es);
2497 continue;
2498 }
2499 TRY_nomem(config.m_vaults.push_back(*cdi));
2500 }
2501
2502 if (partial_vault_error.size() > 1) {
2503 TRY_nomem(es = "For paths found matching: \"");
2504 TRY_nomem(es += a_value);
2505 TRY_nomem(es += "\"");
2506 partial_vault_error.push_back(es);
2507 throw(partial_vault_error);
2508 }
2509 }
2510
2511 /** Parse a "vault-overflow-behavior" command */
parse_vault_overflow_behavior(const std::string & a_value)2512 void global_parser::parse_vault_overflow_behavior(const std::string& a_value)
2513 {
2514 std::string es;
2515 estring str;
2516
2517 TRY_nomem(es = "Invalid vault-overflow-behavior value: \"");
2518 TRY_nomem(es += a_value);
2519 TRY_nomem(es += "\"");
2520 TRY(str = estring(a_value).lower(),es);
2521 if (str == "quit") {
2522 config.m_vault_overflow_behavior = configuration_manager::overflow_quit;
2523 }
2524 else if (str == "delete-oldest") {
2525 config.m_vault_overflow_behavior = configuration_manager::overflow_delete_oldest;
2526 }
2527 else if (str == "delete-until-free") {
2528 config.m_vault_overflow_behavior = configuration_manager::overflow_delete_until_free;
2529 }
2530 else {
2531 throw(ERROR(0,es));
2532 }
2533 }
2534
2535 /** Parse a "vault-overflow-blocks" command */
parse_vault_overflow_blocks(const std::string & a_value)2536 void global_parser::parse_vault_overflow_blocks(const std::string& a_value)
2537 {
2538 std::string es;
2539 uint16 num;
2540
2541 TRY_nomem(es = "Invalid vault-overflow-blocks value: \"");
2542 TRY_nomem(es += a_value);
2543 TRY_nomem(es += "\"");
2544 TRY(num = estring(a_value),es);
2545 if (num > 50)
2546 throw(ERROR(0,es));
2547 config.m_vault_overflow_blocks = num;
2548 }
2549
2550 /** Parse a "vault-overflow-inodes" command */
parse_vault_overflow_inodes(const std::string & a_value)2551 void global_parser::parse_vault_overflow_inodes(const std::string& a_value)
2552 {
2553 std::string es;
2554 uint16 num;
2555
2556 TRY_nomem(es = "Invalid vault-overflow-inodes value: \"");
2557 TRY_nomem(es += a_value);
2558 TRY_nomem(es += "\"");
2559 TRY(num = estring(a_value),es);
2560 if (num > 50)
2561 throw(ERROR(0,es));
2562 config.m_vault_overflow_inodes = num;
2563 }
2564
2565 /** Parse a "vault-selection-behavior" command */
parse_vault_selection_behavior(const std::string & a_value)2566 void global_parser::parse_vault_selection_behavior(const std::string& a_value)
2567 {
2568 std::string es;
2569 estring str;
2570
2571 TRY_nomem(es = "Invalid vault-selection-behavior value: \"");
2572 TRY_nomem(es += a_value);
2573 TRY_nomem(es += "\"");
2574 TRY(str = estring(a_value).lower(),es);
2575 if (str == "max-free") {
2576 config.m_vault_selection_behavior = configuration_manager::selection_max_free;
2577 }
2578 else if (str == "round-robin") {
2579 config.m_vault_selection_behavior = configuration_manager::selection_round_robin;
2580 }
2581 else {
2582 throw(ERROR(0,es));
2583 }
2584 }
2585
2586 /** Parse a "vault-locking" command */
parse_vault_locking(const std::string & a_value)2587 void global_parser::parse_vault_locking(const std::string& a_value)
2588 {
2589 std::string es;
2590 estring str;
2591
2592 TRY_nomem(es = "Invalid vault-locking value: \"");
2593 TRY_nomem(es += a_value);
2594 TRY_nomem(es += "\"");
2595 TRY(str = estring(a_value).lower(),es);
2596 if (
2597 (str == "y")
2598 || (str == "yes")
2599 || (str == "t")
2600 || (str == "true")
2601 || (str == "1")
2602 || (str == "on")
2603 ) {
2604 config.m_vault_locking = true;
2605 }
2606 else if (
2607 (str == "n")
2608 || (str == "no")
2609 || (str == "f")
2610 || (str == "false")
2611 || (str == "0")
2612 || (str == "off")
2613 ) {
2614 config.m_vault_locking = false;
2615 }
2616 else {
2617 throw(ERROR(0,es));
2618 }
2619 }
2620
2621 //----------------------------------------------------------------------------
2622
2623 /** C'tor */
job_parser(job * a_job,const std::string & a_path,uint16 & a_line,std::istream & a_in,const std::string & a_block_delimiter,const bool a_default_context=false)2624 job_parser::job_parser(
2625 job * a_job,
2626 const std::string& a_path,
2627 uint16& a_line,
2628 std::istream& a_in,
2629 const std::string& a_block_delimiter,
2630 const bool a_default_context = false
2631 )
2632 {
2633 m_job = a_job;
2634 m_in = &a_in;
2635 m_path = &a_path;
2636 m_line = &a_line;
2637 m_delimiter = &a_block_delimiter;
2638 m_default_context = a_default_context;
2639
2640 parse();
2641 }
2642
2643 /** Construct a string with the current location of the parser in a
2644 * configuration file */
location(void)2645 const std::string job_parser::location(void)
2646 {
2647 std::string es;
2648
2649 TRY_nomem(es = "At ");
2650 TRY_nomem(es += (*m_path));
2651 TRY_nomem(es += "[");
2652 TRY_nomem(es += estring((*m_line)));
2653 TRY_nomem(es += "]");
2654
2655 return(es);
2656 }
2657
2658 /** Read a job configuration file, used by the "include" command */
read_job(const std::string & a_path)2659 void job_parser::read_job(const std::string& a_path)
2660 {
2661 std::string es;
2662 std::ifstream in;
2663 uint16 line = 0;
2664
2665 in.open(a_path.c_str());
2666 if (!in.is_open()) {
2667 TRY_nomem(es = "Could not open job file: \"");
2668 TRY_nomem(es += a_path);
2669 TRY_nomem(es += "\"");
2670 throw(ERROR(errno,es));
2671 }
2672
2673 job_parser(m_job, a_path, line, in, "", m_default_context);
2674
2675 in.close();
2676 }
2677
2678 /** Job context parser */
parse(void)2679 void job_parser::parse(void)
2680 {
2681 std::string es;
2682 std::string keyword;
2683 std::string value;
2684
2685 while ((*m_in)) {
2686 (*m_line)++;
2687
2688 try {
2689 parse_line((*m_in), keyword, value);
2690 }
2691 catch(error e) {
2692 e.push_back(ERROR_INSTANCE(location()));
2693 throw(e);
2694 }
2695 catch(...) {
2696 error e = err_unknown;
2697
2698 e.push_back(ERROR_INSTANCE(location()));
2699 throw(e);
2700 }
2701
2702 if (!(*m_in) && (m_delimiter->size() != 0)) {
2703 TRY_nomem(es = "Unexpected end of file, expected \"");
2704 TRY_nomem(es += *m_delimiter);
2705 TRY_nomem(es += "\" here");
2706 throw(ERROR(errno,es));
2707 }
2708
2709 if (keyword.size() == 0)
2710 continue;
2711 if (keyword[0] == '#')
2712 continue;
2713
2714 try {
2715 if (keyword == *m_delimiter) {
2716 return;
2717 }
2718 else if (keyword == "archive-path") {
2719 parse_archive_path(value);
2720 }
2721 else if (keyword == "clear") {
2722 parse_clear(value);
2723 }
2724 else if (keyword == "exclude-from") {
2725 parse_exclude_from(value);
2726 }
2727 else if (keyword == "include-from") {
2728 parse_include_from(value);
2729 }
2730 else if (keyword == "groupname") {
2731 parse_groupname(value);
2732 }
2733 else if (keyword == "hostname") {
2734 parse_hostname(value);
2735 }
2736 else if (keyword == "include") {
2737 parse_include(value);
2738 }
2739 else if ((keyword == "jobname") && (*m_delimiter == "</job>")) {
2740 parse_jobname(value);
2741 }
2742 else if (keyword == "path") {
2743 parse_path(value);
2744 }
2745 else if (keyword == "rsync-behavior") {
2746 parse_rsync_behavior(value);
2747 }
2748 else if (keyword == "rsync-connection-type") {
2749 parse_rsync_connection_type(value);
2750 }
2751 else if (keyword == "rsync-hardlink") {
2752 parse_rsync_hardlink(value);
2753 }
2754 else if (keyword == "rsync-multi-hardlink") {
2755 parse_rsync_multi_hardlink(value);
2756 }
2757 else if (keyword == "rsync-multi-hardlink-max") {
2758 parse_rsync_multi_hardlink_max(value);
2759 }
2760 else if (keyword == "rsync-options") {
2761 parse_rsync_options(value);
2762 }
2763 else if (keyword == "ssh-options") {
2764 parse_ssh_options(value);
2765 }
2766 else if (keyword == "<rsync-options>") {
2767 parse_rsync_options_context(value);
2768 }
2769 else if (keyword == "<ssh-options>") {
2770 parse_ssh_options_context(value);
2771 }
2772 else if (keyword == "rsync-remote-user") {
2773 parse_rsync_remote_user(value);
2774 }
2775 else if (keyword == "rsync-remote-path") {
2776 parse_rsync_remote_path(value);
2777 }
2778 else if (keyword == "rsync-remote-port") {
2779 parse_rsync_remote_port(value);
2780 }
2781 else if (keyword == "rsync-remote-module") {
2782 parse_rsync_remote_module(value);
2783 }
2784 else if (keyword == "rsync-retry-count") {
2785 parse_rsync_retry_count(value);
2786 }
2787 else if (keyword == "rsync-retry-delay") {
2788 parse_rsync_retry_delay(value);
2789 }
2790 else if (keyword == "rsync-timeout") {
2791 parse_rsync_timeout(value);
2792 }
2793 else {
2794 error e(0);
2795
2796 TRY_nomem(es = "Unknown command in ");
2797 if (m_default_context) {
2798 TRY_nomem(es += "default");
2799 }
2800 else {
2801 TRY_nomem(es += "job");
2802 }
2803 TRY_nomem(es += " context: \"");
2804 TRY_nomem(es += keyword);
2805 TRY_nomem(es += "\"");
2806 throw(ERROR(0,es));
2807 }
2808 }
2809 catch(error e) {
2810 if ((*m_delimiter).size() == 0)
2811 e.push_back(ERROR_INSTANCE(location()));
2812 throw(e);
2813 }
2814 catch(...) {
2815 error e = err_unknown;
2816
2817 e.push_back(ERROR_INSTANCE(location()));
2818 throw(e);
2819 }
2820 }
2821 }
2822
2823 /** Parse an "archive-path" command */
parse_archive_path(const std::string & a_value)2824 void job_parser::parse_archive_path(const std::string& a_value)
2825 {
2826 std::string es;
2827
2828 TRY_nomem(es = "Invalid archive-path value");
2829 TRY((*m_job).archive_path = a_value,es);
2830 }
2831
2832 /** Parse a "clear" command */
parse_clear(const std::string & a_value)2833 void job_parser::parse_clear(const std::string& a_value)
2834 {
2835 std::string es;
2836 estring str;
2837
2838 TRY_nomem(es = "Invalid clear value: \"");
2839 TRY_nomem(es += a_value);
2840 TRY_nomem(es += "\"");
2841 TRY(str = estring(a_value).lower(),es);
2842 if (str == "archive-path") {
2843 m_job->archive_path.clear();
2844 }
2845 else if (str == "exclude-from") {
2846 m_job->excludes.clear();
2847 }
2848 else if (str == "groupname") {
2849 m_job->groupname.erase();
2850 }
2851 else if (str == "hostname") {
2852 m_job->hostname.erase();
2853 }
2854 else if (str == "include-from") {
2855 m_job->includes.clear();
2856 }
2857 else if ((str == "jobname") && (*m_delimiter == "</job>")) {
2858 m_job->jobname.erase();
2859 }
2860 else if (str == "paths") {
2861 m_job->paths.clear();
2862 }
2863 else if (str == "rsync-behavior") {
2864 m_job->rsync_behavior.clear();
2865 }
2866 else if (str == "rsync-options") {
2867 m_job->rsync_options.erase();
2868 }
2869 else if (str == "ssh-options") {
2870 m_job->ssh_options.erase();
2871 }
2872 else if (str == "rsync-remote-user") {
2873 m_job->rsync_remote_user.erase();
2874 }
2875 else if (str == "rsync-remotr-port") {
2876 m_job->rsync_remote_port = 0;
2877 }
2878 else if (str == "rsync-remote-module") {
2879 m_job->rsync_remote_module.erase();
2880 }
2881 else if (str == "rsync-remote-path") {
2882 m_job->rsync_remote_path.erase();
2883 }
2884 else {
2885 throw(ERROR(0,es));
2886 }
2887 }
2888
2889 /** Parse an "exclude-from" command */
parse_exclude_from(const std::string & a_value)2890 void job_parser::parse_exclude_from(const std::string& a_value)
2891 {
2892 std::string es;
2893 directory dir;
2894 directory::const_iterator cdi;
2895 std::string rpath;
2896 std::string ipath;
2897
2898 if (a_value.size() == 0) {
2899 TRY_nomem(es = "Invalid exclude-from path: \"");
2900 TRY_nomem(es += a_value);
2901 TRY_nomem(es += "\"");
2902 throw(ERROR(0,es));
2903 }
2904
2905 if (a_value[0] != '/') {
2906 TRY_nomem(rpath = parse_dirname(*m_path));
2907 }
2908
2909 TRY_nomem(ipath = rpath + a_value);
2910
2911 TRY_nomem(es = "No exclude-from file(s) found: \"");
2912 TRY_nomem(es += a_value);
2913 TRY_nomem(es += "\"");
2914 TRY(dir.path(ipath),es);
2915 if (dir.size() == 0)
2916 throw(ERROR(0,es));
2917
2918 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
2919 TRY_nomem((*m_job).excludes.push_back(*cdi));
2920 }
2921 }
2922
2923 /** Parse an "include-from" command */
parse_include_from(const std::string & a_value)2924 void job_parser::parse_include_from(const std::string& a_value)
2925 {
2926 std::string es;
2927 directory dir;
2928 directory::const_iterator cdi;
2929 std::string rpath;
2930 std::string ipath;
2931
2932 if (a_value.size() == 0) {
2933 TRY_nomem(es = "Invalid include-from path: \"");
2934 TRY_nomem(es += a_value);
2935 TRY_nomem(es += "\"");
2936 throw(ERROR(0,es));
2937 }
2938
2939 if (a_value[0] != '/') {
2940 TRY_nomem(rpath = parse_dirname(*m_path));
2941 }
2942
2943 TRY_nomem(ipath = rpath + a_value);
2944
2945 TRY_nomem(es = "No include-from file(s) found: \"");
2946 TRY_nomem(es += a_value);
2947 TRY_nomem(es += "\"");
2948 TRY(dir.path(ipath),es);
2949 if (dir.size() == 0)
2950 throw(ERROR(0,es));
2951
2952 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
2953 TRY_nomem((*m_job).includes.push_back(*cdi));
2954 }
2955 }
2956
2957 /** Parse a "groupname" command */
parse_groupname(const std::string & a_value)2958 void job_parser::parse_groupname(const std::string& a_value)
2959 {
2960 TRY_nomem((*m_job).groupname = a_value);
2961 }
2962
2963 /** Parse a "hostname" command */
parse_hostname(const std::string & a_value)2964 void job_parser::parse_hostname(const std::string& a_value)
2965 {
2966 TRY_nomem((*m_job).hostname = a_value);
2967 }
2968
2969 /** Parse an "include" command */
parse_include(const std::string & a_value)2970 void job_parser::parse_include(const std::string& a_value)
2971 {
2972 std::string es;
2973 directory dir;
2974 directory::const_iterator cdi;
2975 std::string rpath;
2976 std::string ipath;
2977
2978 if (a_value.size() == 0) {
2979 TRY_nomem(es = "Invalid include path: \"");
2980 TRY_nomem(es += a_value);
2981 TRY_nomem(es += "\"");
2982 throw(ERROR(0,es));
2983 }
2984
2985 if (a_value[0] != '/') {
2986 TRY_nomem(rpath = parse_dirname(*m_path));
2987 }
2988
2989 TRY_nomem(ipath = rpath + a_value);
2990
2991 TRY_nomem(es = "No configuration file(s) found: \"");
2992 TRY_nomem(es += a_value);
2993 TRY_nomem(es += "\"");
2994 TRY_instead(dir.path(ipath),es);
2995 if (dir.size() == 0)
2996 throw(ERROR(0,es));
2997
2998 for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
2999 if (dir.size() > 1) {
3000 TRY_nomem(es = "For files found matching: \"");
3001 TRY_nomem(es += a_value);
3002 TRY_nomem(es += "\"");
3003 try {
3004 read_job(*cdi);
3005 }
3006 catch(error e) {
3007 e.push_back(ERROR_INSTANCE(es));
3008 throw(e);
3009 }
3010 catch(...) {
3011 error e = err_unknown;
3012
3013 e.push_back(ERROR_INSTANCE(es));
3014 throw(e);
3015 }
3016 }
3017 else
3018 read_job(*cdi);
3019 }
3020 }
3021
3022 /** Parse a "jobname" command */
parse_jobname(const std::string & a_value)3023 void job_parser::parse_jobname(const std::string& a_value)
3024 {
3025 TRY_nomem((*m_job).jobname = a_value);
3026 }
3027
3028 /** Parse a "path" command */
parse_path(const std::string & a_value)3029 void job_parser::parse_path(const std::string& a_value)
3030 {
3031 TRY_nomem((*m_job).paths.push_back(a_value));
3032 }
3033
3034 /** Parse an "rsync-behavior" command */
parse_rsync_behavior(const std::string & a_value)3035 void job_parser::parse_rsync_behavior(const std::string& a_value)
3036 {
3037 std::string es;
3038 estring str;
3039
3040 TRY_nomem(es = "Invalid rsync-behavior value: \"");
3041 TRY_nomem(es += a_value);
3042 TRY_nomem(es += "\"");
3043 TRY(str = estring(a_value).lower(),es);
3044 if (str == "clear") {
3045 (*m_job).rsync_behavior.clear();
3046 }
3047 else if (str == "reset") {
3048 (*m_job).rsync_behavior.reset();
3049 }
3050 else
3051 (*m_job).rsync_behavior.assign(a_value);
3052 }
3053
3054 /** Parse an "rsync-connection-type" command */
parse_rsync_connection_type(const std::string & a_value)3055 void job_parser::parse_rsync_connection_type(const std::string& a_value)
3056 {
3057 std::string es;
3058 estring str;
3059
3060 TRY_nomem(es = "Invalid rsync-connection-type value: \"");
3061 TRY_nomem(es += a_value);
3062 TRY_nomem(es += "\"");
3063 TRY(str = estring(a_value).lower(),es);
3064 if (str == "local") {
3065 (*m_job).rsync_connection = job::connection_local;
3066 }
3067 else if (str == "remote") {
3068 (*m_job).rsync_connection = job::connection_remote;
3069 }
3070 else if (str == "server") {
3071 (*m_job).rsync_connection = job::connection_server;
3072 }
3073 else if (str == "remote-rsync-local-dir") {
3074 (*m_job).rsync_connection = job::connection_ssh_local;
3075 }
3076 else {
3077 throw(ERROR(0,es));
3078 }
3079 }
3080
3081 /** Parse an "rsync-hardlink" command */
parse_rsync_hardlink(const std::string & a_value)3082 void job_parser::parse_rsync_hardlink(const std::string& a_value)
3083 {
3084 std::string es;
3085 estring str;
3086
3087 TRY_nomem(es = "Invalid rsync-hardlink value: \"");
3088 TRY_nomem(es += a_value);
3089 TRY_nomem(es += "\"");
3090 TRY(str = estring(a_value).lower(),es);
3091 if (
3092 (str == "y")
3093 || (str == "yes")
3094 || (str == "t")
3095 || (str == "true")
3096 || (str == "1")
3097 || (str == "on")
3098 ) {
3099 (*m_job).rsync_hardlink = true;
3100 }
3101 else if (
3102 (str == "n")
3103 || (str == "no")
3104 || (str == "f")
3105 || (str == "false")
3106 || (str == "0")
3107 || (str == "off")
3108 ) {
3109 (*m_job).rsync_hardlink = false;
3110 }
3111 else {
3112 throw(ERROR(0,es));
3113 }
3114 }
3115
3116 /** Parse an "rsync-multi-hardlink" command */
parse_rsync_multi_hardlink(const std::string & a_value)3117 void job_parser::parse_rsync_multi_hardlink(const std::string& a_value)
3118 {
3119 std::string es;
3120 estring str;
3121
3122 TRY_nomem(es = "Invalid rsync-multi-hardlink value: \"");
3123 TRY_nomem(es += a_value);
3124 TRY_nomem(es += "\"");
3125 TRY(str = estring(a_value).lower(),es);
3126 if (
3127 (str == "y")
3128 || (str == "yes")
3129 || (str == "t")
3130 || (str == "true")
3131 || (str == "1")
3132 || (str == "on")
3133 ) {
3134 (*m_job).rsync_multi_hardlink = true;
3135 (*m_job).rsync_hardlink = true;
3136 }
3137 else if (
3138 (str == "n")
3139 || (str == "no")
3140 || (str == "f")
3141 || (str == "false")
3142 || (str == "0")
3143 || (str == "off")
3144 ) {
3145 (*m_job).rsync_multi_hardlink = false;
3146 }
3147 else {
3148 throw(ERROR(0,es));
3149 }
3150 }
3151
3152 /** Parse an "rsync-multi-hardlink-max" command */
parse_rsync_multi_hardlink_max(const std::string & a_value)3153 void job_parser::parse_rsync_multi_hardlink_max(const std::string& a_value)
3154 {
3155 std::string es;
3156 uint16 num;
3157
3158 TRY_nomem(es = "Invalid rsync-multi-hardlink-max value: \"");
3159 TRY_nomem(es += a_value);
3160 TRY_nomem(es += "\"");
3161 TRY_instead(num = estring(a_value),es);
3162 if (num == 0)
3163 throw(ERROR(0,es));
3164 (*m_job).rsync_multi_hardlink_max = num;
3165 }
3166
3167 /** Parse an "rsync-options" command */
parse_rsync_options(const std::string & a_value)3168 void job_parser::parse_rsync_options(const std::string& a_value)
3169 {
3170 if (*m_delimiter == "</default>") {
3171 TRY_nomem((*m_job).rsync_options = a_value);
3172 }
3173 else {
3174 if ((*m_job).rsync_options.size() != 0) {
3175 TRY_nomem((*m_job).rsync_options += " ");
3176 }
3177 TRY_nomem((*m_job).rsync_options += a_value);
3178 }
3179 }
3180
3181 /** Parse an "ssh-options" command */
parse_ssh_options(const std::string & a_value)3182 void job_parser::parse_ssh_options(const std::string& a_value)
3183 {
3184 if (*m_delimiter == "</default>") {
3185 TRY_nomem((*m_job).ssh_options = a_value);
3186 }
3187 else {
3188 if ((*m_job).ssh_options.size() != 0) {
3189 TRY_nomem((*m_job).ssh_options += " ");
3190 }
3191 TRY_nomem((*m_job).ssh_options += a_value);
3192 }
3193 }
3194
3195 /** Parse an rsync-options context */
parse_rsync_options_context(const std::string & a_value)3196 void job_parser::parse_rsync_options_context(const std::string& a_value)
3197 {
3198 std::string es;
3199 std::string value;
3200 std::string rsync_options;
3201 char ch;
3202
3203 while ((*m_in)) {
3204 (*m_line)++;
3205
3206 value.erase();
3207
3208 (*m_in).get(ch);
3209 while ((ch == ' ') || (ch == '\t'))
3210 (*m_in).get(ch);
3211 while (((*m_in)) && (ch != '\n')) {
3212 TRY_nomem(value += ch);
3213 (*m_in).get(ch);
3214 }
3215
3216 if ((!(*m_in)) && (value != "</rsync-options>")) {
3217 TRY_nomem(es = "Unexpected end of file, expected ");
3218 TRY_nomem(es += "\"</rsync-options>\" here");
3219 throw(ERROR(errno,es));
3220 }
3221
3222 if (value.size() == 0)
3223 continue;
3224 if (value[0] == '#')
3225 continue;
3226
3227 if (value == "</rsync-options>") {
3228 parse_rsync_options(rsync_options);
3229 return;
3230 }
3231
3232 if (rsync_options.size() != 0) {
3233 TRY_nomem(rsync_options += ' ');
3234 }
3235 TRY_nomem(rsync_options += value);
3236 }
3237 }
3238
3239 /** Parse an ssh-options context */
parse_ssh_options_context(const std::string & a_value)3240 void job_parser::parse_ssh_options_context(const std::string& a_value)
3241 {
3242 std::string es;
3243 std::string value;
3244 std::string ssh_options;
3245 char ch;
3246
3247 while ((*m_in)) {
3248 (*m_line)++;
3249
3250 value.erase();
3251
3252 (*m_in).get(ch);
3253 while ((ch == ' ') || (ch == '\t'))
3254 (*m_in).get(ch);
3255 while (((*m_in)) && (ch != '\n')) {
3256 TRY_nomem(value += ch);
3257 (*m_in).get(ch);
3258 }
3259
3260 if ((!(*m_in)) && (value != "</ssh-options>")) {
3261 TRY_nomem(es = "Unexpected end of file, expected ");
3262 TRY_nomem(es += "\"</ssh-options>\" here");
3263 throw(ERROR(errno,es));
3264 }
3265
3266 if (value.size() == 0)
3267 continue;
3268 if (value[0] == '#')
3269 continue;
3270
3271 if (value == "</ssh-options>") {
3272 parse_ssh_options(ssh_options);
3273 return;
3274 }
3275
3276 if (ssh_options.size() != 0) {
3277 TRY_nomem(ssh_options += ' ');
3278 }
3279 TRY_nomem(ssh_options += value);
3280 }
3281 }
3282
3283 /** Parse a "remote-user" command */
parse_rsync_remote_user(const std::string & a_value)3284 void job_parser::parse_rsync_remote_user(const std::string& a_value)
3285 {
3286 TRY_nomem((*m_job).rsync_remote_user = a_value);
3287 }
3288
3289 /** Parse an "rsync-remote-path" command */
parse_rsync_remote_path(const std::string & a_value)3290 void job_parser::parse_rsync_remote_path(const std::string& a_value)
3291 {
3292 TRY_nomem((*m_job).rsync_remote_path = a_value);
3293 }
3294
3295 /** Parse an "rsync-remote-port" command */
parse_rsync_remote_port(const std::string & a_value)3296 void job_parser::parse_rsync_remote_port(const std::string& a_value)
3297 {
3298 std::string es;
3299 estring str;
3300 uint16 num;
3301
3302 TRY_nomem(es = "Invalid rsync-remote-port value: \"");
3303 TRY_nomem(es += a_value);
3304 TRY_nomem(es += "\"");
3305 TRY_nomem(str = a_value);
3306 TRY(num = str,es);
3307 (*m_job).rsync_remote_port = num;
3308 }
3309
3310 /** Parse an "rsync-remote-module" command */
parse_rsync_remote_module(const std::string & a_value)3311 void job_parser::parse_rsync_remote_module(const std::string& a_value)
3312 {
3313 TRY_nomem((*m_job).rsync_remote_module = a_value);
3314 }
3315
3316 /** Parse an "rsync-retry-count" command */
parse_rsync_retry_count(const std::string & a_value)3317 void job_parser::parse_rsync_retry_count(const std::string& a_value)
3318 {
3319 std::string es;
3320 estring str;
3321 uint16 num;
3322
3323 TRY_nomem(es = "Invalid rsync-retry-count value: \"");
3324 TRY_nomem(es += a_value);
3325 TRY_nomem(es += "\"");
3326 TRY_nomem(str = a_value);
3327 TRY(num = str,es);
3328 (*m_job).rsync_retry_count = num;
3329 }
3330
3331 /** Parse an "rsync-retry-delay" command */
parse_rsync_retry_delay(const std::string & a_value)3332 void job_parser::parse_rsync_retry_delay(const std::string& a_value)
3333 {
3334 std::string es;
3335 estring str;
3336 uint16 num;
3337
3338 TRY_nomem(es = "Invalid rsync-retry-delay value: \"");
3339 TRY_nomem(es += a_value);
3340 TRY_nomem(es += "\"");
3341 TRY_nomem(str = a_value);
3342 TRY(num = str,es);
3343 (*m_job).rsync_retry_delay = num;
3344 }
3345
3346 /** Parse an "rsync-timeout" command */
parse_rsync_timeout(const std::string & a_value)3347 void job_parser::parse_rsync_timeout(const std::string& a_value)
3348 {
3349 std::string es;
3350 estring str;
3351 uint16 num;
3352
3353 TRY_nomem(es = "Invalid rsync-timeout value: \"");
3354 TRY_nomem(es += a_value);
3355 TRY_nomem(es += "\"");
3356 TRY_nomem(str = a_value);
3357 TRY(num = str,es);
3358 (*m_job).rsync_timeout = num;
3359 }
3360
3361 //----------------------------------------------------------------------------
3362
3363 /** The global configuration manager instance */
3364 configuration_manager config;
3365