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