rvm 1.08
|
00001 #include "config.h" 00002 00003 #include <iostream> 00004 #include <string> 00005 #include <fstream> 00006 #include <vector> 00007 #include <map> 00008 #include <algorithm> 00009 #include <cctype> 00010 00011 #include "asserts.h" 00012 #include "error.h" 00013 #include "estring.h" 00014 #include "fs.h" 00015 #include "tstamp.h" 00016 00017 #include "rconfig.h" 00018 00019 //---------------------------------------------------------------------------- 00020 00021 /** Reset to a default value */ 00022 void archive_path_element::clear(void) 00023 { 00024 m_type = jobname; 00025 m_literal.erase(); 00026 } 00027 00028 /** C'tor */ 00029 archive_path_element::archive_path_element() 00030 { 00031 clear(); 00032 } 00033 00034 /** C'tor */ 00035 archive_path_element::archive_path_element( 00036 const archive_path_element& a_class) 00037 { 00038 clear(); 00039 assign(a_class); 00040 } 00041 00042 /** C'tor */ 00043 archive_path_element::archive_path_element( 00044 const archive_path_element::element_type& a_enum 00045 ) 00046 { 00047 clear(); 00048 assign(a_enum); 00049 } 00050 00051 /** C'tor */ 00052 archive_path_element::archive_path_element(const std::string& a_str) 00053 { 00054 clear(); 00055 assign(a_str); 00056 } 00057 00058 /** Assign the value from another archive_path_element instance */ 00059 void archive_path_element::assign(const archive_path_element& a_class) 00060 { 00061 if (this == &a_class) 00062 return; 00063 00064 if (a_class.type() == literal) 00065 assign(a_class.str()); 00066 else 00067 assign(a_class.type()); 00068 } 00069 00070 /** Assign any type except literal */ 00071 void archive_path_element::assign( 00072 const archive_path_element::element_type& a_enum 00073 ) 00074 { 00075 m_type = a_enum; 00076 if (m_type != literal) { 00077 m_literal.erase(); 00078 } 00079 } 00080 00081 /** Assign a literal */ 00082 void archive_path_element::assign(const std::string& a_str) 00083 { 00084 std::string es; 00085 std::string str; 00086 std::string::size_type idx; 00087 00088 TRY_nomem(es = "Invalid archive-path keyword: \""); 00089 TRY_nomem(es += a_str); 00090 TRY_nomem(es += "\""); 00091 00092 if (a_str.size() == 0) { 00093 throw(ERROR(0,es)); 00094 } 00095 if (a_str[0] == '"') { 00096 if (a_str.size() < 3) { 00097 throw(ERROR(0,es)); 00098 } 00099 if (a_str[a_str.size()-1] != '"') { 00100 throw(ERROR(0,es)); 00101 } 00102 TRY_nomem(str = a_str.substr(1,a_str.size()-2)); 00103 00104 idx = str.find('"'); 00105 while (idx != std::string::npos) { 00106 if ((idx > 0) && (str[idx-1] != '\\')) 00107 throw(ERROR(0,es)); 00108 idx = str.find('"',idx+1); 00109 } 00110 00111 idx = str.find("\\\""); 00112 while (idx != std::string::npos) { 00113 str.erase(idx,1); 00114 idx = str.find("\\\""); 00115 } 00116 00117 TRY_nomem(m_literal = str); 00118 m_type = literal; 00119 } 00120 else { 00121 str = estring(a_str).lower(); 00122 if (str == "jobname") { 00123 assign(jobname); 00124 } 00125 else if (str == "groupname") { 00126 assign(groupname); 00127 } 00128 else if (str == "hostname") { 00129 assign(hostname); 00130 } 00131 else if (str == "pathname") { 00132 assign(pathname); 00133 } 00134 else if (str == "permutation") { 00135 assign(permutation); 00136 } 00137 else { 00138 throw(ERROR(0,es)); 00139 } 00140 } 00141 } 00142 00143 /** Retrieve the current type */ 00144 const archive_path_element::element_type& 00145 archive_path_element::type(void) const 00146 { 00147 return(m_type); 00148 } 00149 00150 /** Retrieve the current literal value */ 00151 const std::string& archive_path_element::value(void) const 00152 { 00153 return(m_literal); 00154 } 00155 00156 /** Construct a string of the current value */ 00157 const std::string archive_path_element::str(void) const 00158 { 00159 std::string::size_type idx; 00160 std::string value; 00161 00162 if (m_type == jobname) { 00163 TRY_nomem(value = "jobname"); 00164 } 00165 else if (m_type == groupname) { 00166 TRY_nomem(value = "groupname"); 00167 } 00168 else if (m_type == hostname) { 00169 TRY_nomem(value = "hostname"); 00170 } 00171 else if (m_type == pathname) { 00172 TRY_nomem(value = "pathname"); 00173 } 00174 else if (m_type == permutation) { 00175 TRY_nomem(value = "permutation"); 00176 } 00177 else { 00178 TRY_nomem(value = "\""); 00179 for (idx = 0; idx != m_literal.size(); idx++) { 00180 if (m_literal[idx] == '"') { 00181 TRY_nomem(value += '\\'); 00182 } 00183 TRY_nomem(value += m_literal[idx]); 00184 } 00185 TRY_nomem(value += '"'); 00186 } 00187 00188 return(value); 00189 } 00190 00191 /** Assignment */ 00192 archive_path_element& 00193 archive_path_element::operator=(const archive_path_element& a_class) 00194 { 00195 clear(); 00196 assign(a_class); 00197 00198 return(*this); 00199 } 00200 00201 /** Assignment */ 00202 archive_path_element& 00203 archive_path_element::operator=( 00204 const archive_path_element::element_type a_enum) 00205 { 00206 clear(); 00207 assign(a_enum); 00208 00209 return(*this); 00210 } 00211 00212 /** Assignment */ 00213 archive_path_element& 00214 archive_path_element::operator=(const std::string& a_str) 00215 { 00216 clear(); 00217 assign(a_str); 00218 00219 return(*this); 00220 } 00221 00222 //---------------------------------------------------------------------------- 00223 00224 /** Reset to a default value */ 00225 void archive_path::reset(void) 00226 { 00227 clear(); 00228 push_back("hostname/pathname"); 00229 } 00230 00231 /** C'tor */ 00232 archive_path::archive_path() 00233 { 00234 reset(); 00235 } 00236 00237 /** C'tor */ 00238 archive_path::archive_path(const archive_path& a_class) 00239 { 00240 push_back(a_class); 00241 } 00242 00243 /** C'tor */ 00244 archive_path::archive_path(const archive_path_element& a_class) 00245 { 00246 push_back(a_class); 00247 } 00248 00249 /** C'tor */ 00250 archive_path::archive_path(const std::string& a_str) 00251 { 00252 push_back(a_str); 00253 } 00254 00255 /** Add elements to the path list from another archive_path instance */ 00256 void archive_path::push_back(const archive_path& a_class) 00257 { 00258 const_iterator li; 00259 00260 for (li = a_class.begin(); li != a_class.end(); li++) { 00261 TRY_nomem(push_back(*li)); 00262 } 00263 } 00264 00265 /** Add an element to the path list from an archive_path_element instance */ 00266 void archive_path::push_back(const archive_path_element& a_class) 00267 { 00268 TRY_nomem(type::push_back(a_class)); 00269 } 00270 00271 /** Parse a string into an archive-path list */ 00272 void archive_path::push_back(const std::string& a_str) 00273 { 00274 std::string es; 00275 std::string keyword; 00276 std::string str; 00277 std::string::size_type idx; 00278 archive_path_element ape; 00279 00280 if (a_str.size() == 0) 00281 return; 00282 00283 TRY_nomem(str = a_str); 00284 if (str[0] == '/') 00285 str.erase(0,1); 00286 if (str[str.size()-1] == '/') 00287 str.erase(str.size()-1,1); 00288 00289 while (str.size() > 0) { 00290 idx = str.find('/'); 00291 if (idx == std::string::npos) { 00292 TRY_nomem(keyword = str); 00293 str.erase(); 00294 } 00295 else { 00296 TRY_nomem(keyword = str.substr(0,idx)); 00297 str.erase(0,idx+1); 00298 } 00299 00300 ape.clear(); 00301 TRY_nomem(es = "At: \""); 00302 TRY_nomem(es += keyword); 00303 TRY_nomem(es += "\""); 00304 TRY(ape.assign(keyword),es); 00305 TRY_nomem(push_back(ape)); 00306 } 00307 } 00308 00309 /** Reconstruct a string from an archive-path list */ 00310 const std::string archive_path::str(void) const 00311 { 00312 std::string str; 00313 const_iterator li; 00314 00315 for (li = begin(); li != end(); li++) { 00316 if (li != begin()) { 00317 TRY_nomem(str += '/'); 00318 } 00319 TRY_nomem(str += (*li).str()); 00320 } 00321 00322 return(str); 00323 } 00324 00325 /** Assign an archive-path list from another archive_path instance */ 00326 void archive_path::assign(const archive_path& a_class) 00327 { 00328 if (this != &a_class) { 00329 clear(); 00330 push_back(a_class); 00331 } 00332 } 00333 00334 /** Assign an archive-path list from a single archive_path_element instance */ 00335 void archive_path::assign(const archive_path_element& a_class) 00336 { 00337 clear(); 00338 push_back(a_class); 00339 } 00340 00341 /** Assign an archive-path list from a string (parse the string) */ 00342 void archive_path::assign(const std::string& a_str) 00343 { 00344 clear(); 00345 push_back(a_str); 00346 } 00347 00348 /** Assignment */ 00349 archive_path& archive_path::operator=(const archive_path& a_class) 00350 { 00351 assign(a_class); 00352 00353 return(*this); 00354 } 00355 00356 /** Assignment */ 00357 archive_path& archive_path::operator=(const archive_path_element& a_class) 00358 { 00359 assign(a_class); 00360 00361 return(*this); 00362 } 00363 00364 /** Assignment */ 00365 archive_path& archive_path::operator=(const std::string& a_str) 00366 { 00367 assign(a_str); 00368 00369 return(*this); 00370 } 00371 00372 //---------------------------------------------------------------------------- 00373 00374 /** Default behavior */ 00375 const uint16 rsync_behavior::default_behavior; 00376 00377 /** Clear all values and set the default to "retry" */ 00378 void rsync_behavior::clear(void) 00379 { 00380 m_map.clear(); 00381 m_default = retry; 00382 } 00383 00384 /** Clear all values and set some sane default actions */ 00385 void rsync_behavior::reset(void) 00386 { 00387 clear(); 00388 m_default = retry; 00389 TRY_nomem( 00390 m_map[0] = ok; 00391 m_map[1] = fail; 00392 m_map[2] = fail; 00393 m_map[4] = fail; 00394 m_map[23] = retry_without_hardlinks; 00395 m_map[124] = fail; 00396 m_map[125] = fail; 00397 m_map[126] = fail; 00398 m_map[127] = fail; 00399 ); 00400 } 00401 00402 /** C'tor */ 00403 rsync_behavior::rsync_behavior() 00404 { 00405 reset(); 00406 } 00407 00408 /** C'tor */ 00409 rsync_behavior::rsync_behavior(const rsync_behavior& a_class) 00410 { 00411 assign(a_class); 00412 } 00413 00414 /** Assign an action to be taken for a specific exit code */ 00415 void rsync_behavior::assign(const uint16 a_code, const value_type a_action) 00416 { 00417 if (a_code == default_behavior) 00418 m_default = a_action; 00419 else { 00420 TRY_nomem(m_map[a_code] = a_action); 00421 } 00422 } 00423 00424 /** Assign actions to be taken from another rsync_behavior instance */ 00425 void rsync_behavior::assign(const rsync_behavior& a_class) 00426 { 00427 if (this == &a_class) 00428 return; 00429 00430 clear(); 00431 m_default = a_class.default_value(); 00432 TRY_nomem(m_map = a_class.map_value()); 00433 } 00434 00435 /** 00436 Assign actions to be taken from a string (parse a string in the form of 00437 exit-code '=' action) 00438 */ 00439 void rsync_behavior::assign(const std::string& a_str) 00440 { 00441 std::string es; 00442 std::string code_str; 00443 std::string action_str; 00444 uint16 code; 00445 rsync_behavior::behavior_type action; 00446 std::string::size_type idx; 00447 00448 TRY_nomem(es = "Invalid rsync-behavior value: \""); 00449 TRY_nomem(es += a_str); 00450 TRY_nomem(es += "\""); 00451 00452 TRY_nomem(action_str = a_str); 00453 idx = 0; 00454 while (isdigit(action_str[idx]) || (action_str[idx] == '*')) 00455 ++idx; 00456 TRY_nomem(code_str = action_str.substr(0,idx)); 00457 action_str.erase(0,idx); 00458 if (code_str.size() == 0) 00459 throw(ERROR(0,es)); 00460 00461 idx = 0; 00462 while ((action_str[idx] == ' ') || (action_str[idx] == '\t')) 00463 idx++; 00464 action_str.erase(0,idx); 00465 00466 if (action_str[0] != '=') 00467 throw(ERROR(0,es)); 00468 action_str.erase(0,1); 00469 00470 idx = 0; 00471 while ((action_str[idx] == ' ') || (action_str[idx] == '\t')) 00472 idx++; 00473 action_str.erase(0,idx); 00474 00475 if (code_str == "*") 00476 code = rsync_behavior::default_behavior; 00477 else 00478 code = estring(code_str); 00479 if (estring(action_str).lower() == "ok") 00480 action = rsync_behavior::ok; 00481 else if (estring(action_str).lower() == "fail") 00482 action = rsync_behavior::fail; 00483 else if (estring(action_str).lower() == "retry") 00484 action = rsync_behavior::retry; 00485 else if (estring(action_str).lower() == "retry-without-hardlinks") 00486 action = rsync_behavior::retry_without_hardlinks; 00487 else if (estring(action_str).lower() == "ok") 00488 action = rsync_behavior::quit; 00489 else 00490 throw(ERROR(0,es)); 00491 assign(code, action); 00492 } 00493 00494 /** Return the action to be taken for a given exit code */ 00495 const rsync_behavior::value_type 00496 rsync_behavior::operator[](const uint16 a_code) const 00497 { 00498 if (a_code == default_behavior) { 00499 return(m_default); 00500 } 00501 if (m_map.find(a_code) != m_map.end()) { 00502 return(m_map.find(a_code)->second); 00503 } 00504 return(m_default); 00505 } 00506 00507 /** Return the action to be taken for a given exit code */ 00508 rsync_behavior::value_type& 00509 rsync_behavior::operator[](const uint16 a_code) 00510 { 00511 if (a_code == default_behavior) { 00512 return(m_default); 00513 } 00514 return(m_map[a_code]); 00515 } 00516 00517 /** Assignment */ 00518 rsync_behavior& rsync_behavior::operator=(const rsync_behavior& a_class) 00519 { 00520 assign(a_class); 00521 00522 return(*this); 00523 } 00524 00525 /** Assignment */ 00526 rsync_behavior& 00527 rsync_behavior::operator=(const rsync_behavior::behavior_type a_enum) 00528 { 00529 assign(default_behavior, a_enum); 00530 00531 return(*this); 00532 } 00533 00534 /** Assignment */ 00535 rsync_behavior& rsync_behavior::operator=(const std::string& a_str) 00536 { 00537 assign(a_str); 00538 00539 return(*this); 00540 } 00541 00542 /** Return the default action */ 00543 const rsync_behavior::behavior_type rsync_behavior::default_value(void) const 00544 { 00545 return(m_default); 00546 } 00547 00548 /** Return an std::map of exit codes to actions */ 00549 const rsync_behavior::map_type& rsync_behavior::map_value(void) const 00550 { 00551 return(m_map); 00552 } 00553 00554 //---------------------------------------------------------------------------- 00555 00556 /** C'tor */ 00557 job::job() 00558 { 00559 clear(); 00560 } 00561 00562 /** C'tor */ 00563 job::job(const job& a_job) 00564 { 00565 clear(); 00566 assign(a_job); 00567 } 00568 00569 /** Clear values */ 00570 void job::clear(void) 00571 { 00572 default_config_path.erase(); 00573 default_config_line = 0; 00574 config_path.erase(); 00575 config_line = 0; 00576 archive_path = "hostname/pathname"; 00577 excludes.clear(); 00578 includes.clear(); 00579 groupname.erase(); 00580 hostname.erase(); 00581 jobname.erase(); 00582 paths.clear(); 00583 rsync_behavior.reset(); 00584 rsync_connection = connection_remote; 00585 rsync_hardlink = true; 00586 rsync_multi_hardlink = false; 00587 rsync_multi_hardlink_max = 20; 00588 rsync_options.erase(); 00589 rsync_remote_user.erase(); 00590 rsync_remote_path.erase(); 00591 rsync_remote_port = 0; 00592 rsync_remote_module.erase(); 00593 rsync_retry_count = 3; 00594 rsync_retry_delay = 0; 00595 rsync_timeout = 60 * 60 * 4; // 4 hours; 00596 } 00597 00598 /** Assign values from another job instance */ 00599 void job::assign(const job& a_job) 00600 { 00601 clear(); 00602 00603 default_config_path = a_job.default_config_path; 00604 default_config_line = a_job.default_config_line; 00605 config_path = a_job.config_path; 00606 config_line = a_job.config_line; 00607 archive_path = a_job.archive_path; 00608 excludes = a_job.excludes; 00609 includes = a_job.includes; 00610 groupname = a_job.groupname; 00611 hostname = a_job.hostname; 00612 jobname = a_job.jobname; 00613 paths = a_job.paths; 00614 rsync_behavior.clear(); 00615 rsync_behavior = a_job.rsync_behavior; 00616 rsync_connection = a_job.rsync_connection; 00617 rsync_hardlink = a_job.rsync_hardlink; 00618 rsync_multi_hardlink = a_job.rsync_multi_hardlink; 00619 rsync_multi_hardlink_max = a_job.rsync_multi_hardlink_max; 00620 rsync_options = a_job.rsync_options; 00621 rsync_remote_user = a_job.rsync_remote_user; 00622 rsync_remote_path = a_job.rsync_remote_path; 00623 rsync_remote_port = a_job.rsync_remote_port; 00624 rsync_remote_module = a_job.rsync_remote_module; 00625 rsync_retry_count = a_job.rsync_retry_count; 00626 rsync_retry_delay = a_job.rsync_retry_delay; 00627 rsync_timeout = a_job.rsync_timeout; 00628 } 00629 00630 /** Assignment */ 00631 job& job::operator=(const job& a_job) 00632 { 00633 assign(a_job); 00634 00635 return(*this); 00636 } 00637 00638 /** Generate the archive-path subdirectory for this job */ 00639 const std::string job::generate_archive_path(const std::string& a_path) const 00640 { 00641 std::string es; 00642 std::string path; 00643 archive_path::const_iterator capi; 00644 00645 for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) { 00646 if (capi->type() == archive_path_element::jobname) { 00647 if (jobname.size() == 0) { 00648 TRY_nomem(es = "archive-path references jobname, "); 00649 TRY_nomem(es += "but jobname is empty"); 00650 throw(ERROR(0,es)); 00651 } 00652 if (path.size() != 0) { 00653 TRY_nomem(path += '/'); 00654 } 00655 TRY_nomem(path += jobname); 00656 } 00657 else if (capi->type() == archive_path_element::groupname) { 00658 if (groupname.size() == 0) { 00659 TRY_nomem(es = "archive-path references groupname, "); 00660 TRY_nomem(es += "but groupname is empty"); 00661 throw(ERROR(0,es)); 00662 } 00663 if (path.size() != 0) { 00664 TRY_nomem(path += '/'); 00665 } 00666 TRY_nomem(path += groupname); 00667 } 00668 else if (capi->type() == archive_path_element::hostname) { 00669 if (hostname.size() == 0) { 00670 TRY_nomem(es = "archive-path references hostname, "); 00671 TRY_nomem(es += "but hostname is empty"); 00672 throw(ERROR(0,es)); 00673 } 00674 if (path.size() != 0) { 00675 TRY_nomem(path += '/'); 00676 } 00677 TRY_nomem(path += hostname); 00678 } 00679 else if (capi->type() == archive_path_element::pathname) { 00680 if (a_path.size() == 0) { 00681 TRY_nomem(es = "archive-path references path name, "); 00682 TRY_nomem(es += "but path name is empty"); 00683 throw(ERROR(0,es)); 00684 } 00685 if (path.size() != 0) { 00686 TRY_nomem(path += '/'); 00687 } 00688 TRY_nomem(path += a_path); 00689 } 00690 else if (capi->type() == archive_path_element::permutation) { 00691 if (a_path.size() == 0) { 00692 TRY_nomem(es = "archive-path references path name, "); 00693 TRY_nomem(es += "but path name is empty"); 00694 throw(ERROR(0,es)); 00695 } 00696 if (path.size() != 0) { 00697 TRY_nomem(path += '/'); 00698 } 00699 TRY_nomem(path += permute_path(a_path)); 00700 } 00701 else if (capi->type() == archive_path_element::literal) { 00702 if (capi->value().size() == 0) { 00703 TRY_nomem(es = "archive-path references literal string, "); 00704 TRY_nomem(es += "but literal string value is empty"); 00705 throw(INTERNAL_ERROR(0,es)); 00706 } 00707 if (path.size() != 0) { 00708 TRY_nomem(path += '/'); 00709 } 00710 TRY_nomem(path += capi->value()); 00711 } 00712 else 00713 throw(INTERNAL_ERROR(0,"Unknown archive path element type")); 00714 } 00715 00716 // If path does not end with a '/', strip off characters until it does. 00717 while ((path.size() > 0) && (path[path.size()-1] != '/')) 00718 path.erase(path.size()-1); 00719 00720 path = reform_path(path); 00721 00722 return(path); 00723 } 00724 00725 /** Generate the source path to be passed to rsync on the command line */ 00726 const std::string job::generate_source_path(const std::string& a_path) const 00727 { 00728 estring path; 00729 00730 if (rsync_connection == connection_server) { 00731 path += "rsync://"; 00732 if (rsync_remote_user.size() != 0) { 00733 path += rsync_remote_user; 00734 path += "@"; 00735 } 00736 TRY_nomem(path += hostname); 00737 if (rsync_remote_port != 0) { 00738 path += ":"; 00739 path += estring(rsync_remote_port); 00740 } 00741 if (rsync_remote_module.size() != 0) { 00742 path += "/"; 00743 path += rsync_remote_module; 00744 if ((a_path.size() > 0) && (a_path[0] != '/')) 00745 path += "/"; 00746 } 00747 } 00748 else if (rsync_connection == connection_remote) { 00749 if (rsync_remote_user.size() != 0) { 00750 path += rsync_remote_user; 00751 path += "@"; 00752 } 00753 path += hostname; 00754 path += ":"; 00755 } 00756 path += a_path; 00757 00758 return(path); 00759 } 00760 00761 /** Find the common pathname among all job paths (may be an empty string) */ 00762 const std::string job::common_pathname(void) const 00763 { 00764 std::string common_path; 00765 paths_type::size_type pidx; 00766 std::string::size_type sidx; 00767 std::string::size_type max_sidx; 00768 bool same; 00769 00770 TRY_nomem(common_path = ""); 00771 sidx = 0; 00772 max_sidx = paths[0].size(); 00773 for (pidx = 0; pidx < paths.size(); pidx++) { 00774 if (max_sidx > paths[pidx].size()) { 00775 max_sidx = paths[pidx].size(); 00776 } 00777 } 00778 same = true; 00779 for (sidx = 0; sidx < max_sidx; sidx++) { 00780 for (pidx = 0; (same && (pidx < paths.size())); pidx++) { 00781 if (pidx == 0) 00782 continue; 00783 if (paths[pidx-1][sidx] != paths[pidx][sidx]) 00784 same = false; 00785 } 00786 if (same) 00787 TRY_nomem(common_path += paths[0][sidx]); 00788 } 00789 if (common_path[0] == '/') 00790 common_path.erase(0,1); 00791 if ((common_path.size() > 0) && (common_path[common_path.size()-1] == '/')) 00792 common_path.erase(common_path.size()-1); 00793 00794 return(common_path); 00795 } 00796 00797 /** Generate a unique ID string for this job */ 00798 const std::string job::generate_job_id(void) const 00799 { 00800 std::string es; 00801 std::string path; 00802 archive_path::const_iterator capi; 00803 00804 for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) { 00805 if (capi->type() == archive_path_element::jobname) { 00806 if (jobname.size() == 0) { 00807 TRY_nomem(es = "archive-path references jobname, "); 00808 TRY_nomem(es += "but jobname is empty"); 00809 throw(ERROR(0,es)); 00810 } 00811 if (path.size() != 0) { 00812 TRY_nomem(path += '/'); 00813 } 00814 TRY_nomem(path += jobname); 00815 } 00816 else if (capi->type() == archive_path_element::groupname) { 00817 if (groupname.size() == 0) { 00818 TRY_nomem(es = "archive-path references groupname, "); 00819 TRY_nomem(es += "but groupname is empty"); 00820 throw(ERROR(0,es)); 00821 } 00822 if (path.size() != 0) { 00823 TRY_nomem(path += '/'); 00824 } 00825 TRY_nomem(path += groupname); 00826 } 00827 else if (capi->type() == archive_path_element::hostname) { 00828 if (hostname.size() == 0) { 00829 TRY_nomem(es = "archive-path references hostname, "); 00830 TRY_nomem(es += "but hostname is empty"); 00831 throw(ERROR(0,es)); 00832 } 00833 if (path.size() != 0) { 00834 TRY_nomem(path += '/'); 00835 } 00836 TRY_nomem(path += hostname); 00837 } 00838 else if (capi->type() == archive_path_element::pathname) { 00839 if (path.size() == 0) { 00840 TRY_nomem(path += common_pathname()); 00841 } 00842 } 00843 else if (capi->type() == archive_path_element::permutation) { 00844 if (path.size() == 0) { 00845 TRY_nomem(path += permute_path(common_pathname())); 00846 } 00847 } 00848 else if (capi->type() == archive_path_element::literal) { 00849 if (capi->value().size() == 0) { 00850 TRY_nomem(es = "archive-path references literal string, "); 00851 TRY_nomem(es += "but literal string value is empty"); 00852 throw(INTERNAL_ERROR(0,es)); 00853 } 00854 if (path.size() != 0) { 00855 TRY_nomem(path += '/'); 00856 } 00857 TRY_nomem(path += capi->value()); 00858 } 00859 } 00860 00861 path = reform_path(path); 00862 00863 if (path.size() == 0) { 00864 TRY_nomem(path = jobname); 00865 } 00866 return(path); 00867 } 00868 00869 const std::vector<std::string> job::generate_rsync_options_vector(void) const 00870 { 00871 std::string opts = rsync_options; 00872 std::vector<std::string> argv; 00873 std::string str; 00874 00875 while (opts.size() != 0) { 00876 00877 str.clear(); 00878 while ((opts.size() != 0) && (opts[0] != ' ') && (opts[0] != '\t')) { 00879 switch (opts[0]) { 00880 case '\0': 00881 break; 00882 case '\'': 00883 opts.erase(0,1); 00884 while ((opts.size() != 0) && (opts[0] != '\'')) { 00885 str += opts[0]; 00886 opts.erase(0,1); 00887 } 00888 if (opts[0] == '\'') { 00889 opts.erase(0,1); 00890 } 00891 break; 00892 case '"': 00893 opts.erase(0,1); 00894 while ((opts.size() != 0) && (opts[0] != '"')) { 00895 if ((opts.size() >= 2) && (opts[0] == '\\') && (opts[1] == '"')) { 00896 str += '"'; 00897 opts.erase(0,2); 00898 } 00899 else { 00900 str += opts[0]; 00901 opts.erase(0,1); 00902 } 00903 } 00904 if (opts[0] == '"') { 00905 opts.erase(0,1); 00906 } 00907 break; 00908 case '\\': 00909 if ((opts.size() >= 2) && (opts[1] == ' ')) { 00910 str += ' '; 00911 opts.erase(0,2); 00912 } 00913 else if ((opts.size() >= 2) && (opts[1] == 't')) { 00914 str += '\t'; 00915 opts.erase(0,2); 00916 } 00917 else { 00918 str += '\\'; 00919 opts.erase(0,1); 00920 } 00921 break; 00922 default: 00923 str += opts[0]; 00924 opts.erase(0,1); 00925 break; 00926 } 00927 } 00928 00929 if (str.size()) { 00930 argv.push_back(str); 00931 } 00932 while ((opts.size() > 0) && ((opts[0] == ' ') || (opts[0] == '\t'))) { 00933 opts.erase(0,1); 00934 } 00935 } 00936 00937 return(argv); 00938 } 00939 00940 /** Perform sanity checks for the configuration settings of this job */ 00941 void job::check(void) 00942 { 00943 std::string es; 00944 std::string this_path; 00945 std::string that_path; 00946 paths_type::const_iterator cpi; 00947 configuration_manager::jobs_type::const_iterator cji; 00948 paths_type::const_iterator cjapi; 00949 00950 if ( 00951 ( 00952 (rsync_connection == connection_remote) 00953 || (rsync_connection == connection_server) 00954 ) 00955 && (hostname.size() == 0) 00956 ) 00957 { 00958 TRY_nomem(es = "rsync-connection-type references hostname, "); 00959 TRY_nomem(es += "but hostname is empty"); 00960 throw(ERROR(0,es)); 00961 } 00962 00963 if ((rsync_remote_module.size() != 0) 00964 && (rsync_connection != connection_server)) 00965 { 00966 TRY_nomem(es = "rsync-remote-module specifies a module, but "); 00967 TRY_nomem(es = "rsync-connection-type is not server"); 00968 throw(ERROR(0,es)); 00969 } 00970 00971 if (paths.size() == 0) { 00972 throw(ERROR(0,"No paths defined for this job")); 00973 } 00974 00975 for (cpi = paths.begin() ; cpi != paths.end(); cpi++) { 00976 TRY_nomem(this_path = generate_archive_path(*cpi)); 00977 00978 for ( 00979 cji = config.jobs().begin(); 00980 cji != config.jobs().end(); 00981 cji++ 00982 ) 00983 { 00984 for ( 00985 cjapi = cji->paths.begin(); 00986 cjapi != cji->paths.end(); 00987 cjapi++ 00988 ) 00989 { 00990 TRY_nomem(that_path = cji->generate_archive_path(*cjapi)); 00991 00992 if (this_path == that_path) { 00993 error e(0); 00994 00995 TRY_nomem(es = "Duplicate archive-path values detected"); 00996 e.push_back(ERROR_INSTANCE(es)); 00997 TRY_nomem(es = "Archive path: \""); 00998 TRY_nomem(es += this_path); 00999 TRY_nomem(es += "\""); 01000 e.push_back(ERROR_INSTANCE(es)); 01001 TRY_nomem(es = "Previously defined at "); 01002 TRY_nomem(es += cji->config_path); 01003 TRY_nomem(es += "["); 01004 TRY_nomem(es += estring(cji->config_line)); 01005 TRY_nomem(es += "]"); 01006 e.push_back(ERROR_INSTANCE(es)); 01007 throw(e); 01008 } 01009 01010 if ( 01011 (this_path.size() < that_path.size()) 01012 && (this_path == that_path.substr(0,this_path.size())) 01013 && ( 01014 (that_path[this_path.size()] == '/') 01015 || (this_path.size() == 0) 01016 ) 01017 ) 01018 { 01019 error e(0); 01020 01021 TRY_nomem(es = "Overlapping archive-path values detected"); 01022 e.push_back(ERROR_INSTANCE(es)); 01023 TRY_nomem(es = "Defined archive-path: \""); 01024 TRY_nomem(es += this_path); 01025 TRY_nomem(es += "\""); 01026 e.push_back(ERROR_INSTANCE(es)); 01027 TRY_nomem(es = "Is in a parent directory of another job's previously defined archive-path"); 01028 e.push_back(ERROR_INSTANCE(es)); 01029 TRY_nomem(es = "At "); 01030 TRY_nomem(es += cji->config_path); 01031 TRY_nomem(es += "["); 01032 TRY_nomem(es += estring(cji->config_line)); 01033 TRY_nomem(es += "]"); 01034 e.push_back(ERROR_INSTANCE(es)); 01035 throw(e); 01036 } 01037 01038 if ( 01039 (this_path.size() > that_path.size()) 01040 && (this_path.substr(0,that_path.size()) == that_path) 01041 && ( 01042 (this_path[that_path.size()] == '/') 01043 || (that_path.size() == 0) 01044 ) 01045 ) 01046 { 01047 error e(0); 01048 01049 TRY_nomem(es = "Overlapping archive-path values detected"); 01050 e.push_back(ERROR_INSTANCE(es)); 01051 TRY_nomem(es = "Defined archive-path: \""); 01052 TRY_nomem(es += this_path); 01053 TRY_nomem(es += "\""); 01054 e.push_back(ERROR_INSTANCE(es)); 01055 TRY_nomem(es = "Is in a subdirectory of another job's previously defined archive-path"); 01056 e.push_back(ERROR_INSTANCE(es)); 01057 TRY_nomem(es = "At "); 01058 TRY_nomem(es += cji->config_path); 01059 TRY_nomem(es += "["); 01060 TRY_nomem(es += estring(cji->config_line)); 01061 TRY_nomem(es += "]"); 01062 e.push_back(ERROR_INSTANCE(es)); 01063 throw(e); 01064 } 01065 } 01066 } 01067 } 01068 } 01069 01070 //---------------------------------------------------------------------------- 01071 01072 /** Reset configuration to default settings */ 01073 void configuration_manager::clear(void) 01074 { 01075 m_initialized = false; 01076 m_configs_read = 0; 01077 m_default = true; 01078 TRY_nomem(m_default_file = CONFIGFILE); 01079 m_action = action_help; 01080 m_timestamp.set(); 01081 m_cfgfiles.clear(); 01082 m_link_catalog_dir.erase(); 01083 TRY_nomem(m_log_dir = LOGDIR); 01084 m_delete_old_log_files = false; 01085 m_delete_old_report_files = false; 01086 m_rsync_local_path.erase(); 01087 if (strlen(LOCAL_RSYNC) > 0) { 01088 TRY_nomem(m_rsync_local_path = LOCAL_RSYNC); 01089 } 01090 m_rsync_parallel = 1; 01091 m_io_poll_interval = 1; 01092 m_vaults.clear(); 01093 m_vault_overflow_behavior = overflow_quit; 01094 m_vault_overflow_blocks = 10; 01095 m_vault_overflow_inodes = 10; 01096 m_vault_selection_behavior = selection_round_robin; 01097 m_vault_locking = true; 01098 m_default_job.clear(); 01099 m_logging_level = logging_child; 01100 m_error_logging_level = logging_rsync; 01101 m_jobs.clear(); 01102 } 01103 01104 /** C'tor */ 01105 configuration_manager::configuration_manager() 01106 { 01107 if (this != &config) 01108 throw( 01109 INTERNAL_ERROR(0,"Attempt to allocate multiple configuration managers") 01110 ); 01111 clear(); 01112 } 01113 01114 /** Initialize the configuration manager from rvm's command line options */ 01115 void configuration_manager::init(int argc, char const * argv[]) 01116 { 01117 int c; 01118 estring opt; 01119 estring arg; 01120 std::string es; 01121 std::string tes; 01122 cfgfiles_type::const_iterator cfi; 01123 bool use_custom_timestamp = false; 01124 class timestamp custom_timestamp; 01125 01126 for (c = 1; c < argc; c++) { 01127 TRY_nomem(opt = argv[c]); 01128 if (c+1 == argc) { 01129 TRY_nomem(arg = ""); 01130 } 01131 else { 01132 TRY_nomem(arg = argv[c+1]); 01133 } 01134 01135 if (opt == "--archive") { 01136 m_action = action_archive; 01137 } 01138 else if (opt == "--relink") { 01139 m_action = action_relink; 01140 } 01141 else if (opt == "--help") { 01142 m_action = action_help; 01143 } 01144 else if (opt == "--version") { 01145 m_action = action_version; 01146 } 01147 else if (opt == "--check-config") { 01148 m_action = action_check_config; 01149 } 01150 else if (opt == "--no-default-config") { 01151 m_default = false; 01152 } 01153 else if (opt == "--config") { 01154 directory dir; 01155 directory::const_iterator cdi; 01156 01157 TRY_nomem(es = "Error finding configuration file(s) "); 01158 TRY_nomem(es += "matching command line argument ["); 01159 TRY_nomem(es += estring(c+1)); 01160 TRY_nomem(es += "]: \""); 01161 TRY_nomem(es += arg); 01162 TRY_nomem(es += "\""); 01163 01164 try { 01165 dir.path(arg); 01166 } 01167 catch(error e) { 01168 e.push_back(ERROR_INSTANCE(es)); 01169 throw(e); 01170 } 01171 catch(...) { 01172 error e = err_unknown; 01173 01174 e.push_back(ERROR_INSTANCE(es)); 01175 throw(e); 01176 } 01177 if (dir.size() == 0) { 01178 TRY_nomem(es = "No configuration file(s) found matching "); 01179 TRY_nomem(es += "command line argument ["); 01180 TRY_nomem(es += estring(c+1)); 01181 TRY_nomem(es += "]: \""); 01182 TRY_nomem(es += arg); 01183 TRY_nomem(es += "\""); 01184 01185 throw(ERROR(0,es)); 01186 } 01187 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 01188 TRY_nomem(m_cfgfiles.push_back(cfgfile_element(config_file, arg))); 01189 } 01190 01191 c++; 01192 } 01193 else if (opt == "--job") { 01194 directory dir; 01195 directory::const_iterator cdi; 01196 01197 TRY_nomem(es = "Error finding job file(s) "); 01198 TRY_nomem(es += "matching command line argument ["); 01199 TRY_nomem(es += estring(c+1)); 01200 TRY_nomem(es += "]: \""); 01201 TRY_nomem(es += arg); 01202 TRY_nomem(es += "\""); 01203 01204 try { 01205 dir.path(arg); 01206 } 01207 catch(error e) { 01208 e.push_back(ERROR_INSTANCE(es)); 01209 throw(e); 01210 } 01211 catch(...) { 01212 error e = err_unknown; 01213 throw(e); 01214 } 01215 if (dir.size() == 0) { 01216 TRY_nomem(es = "No job file(s) found matching "); 01217 TRY_nomem(es += "command line argument ["); 01218 TRY_nomem(es += estring(c+1)); 01219 TRY_nomem(es += "]: \""); 01220 TRY_nomem(es += arg); 01221 TRY_nomem(es += "\""); 01222 01223 throw(ERROR(0,es)); 01224 } 01225 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 01226 TRY_nomem(m_cfgfiles.push_back(cfgfile_element(job_file, arg))); 01227 } 01228 01229 c++; 01230 } 01231 else if (opt == "--timestamp") { 01232 TRY_nomem(es = "From command line argument "); 01233 TRY_nomem(es += estring(c+1)); 01234 TRY(custom_timestamp.assign(arg),es); 01235 use_custom_timestamp = true; 01236 c++; 01237 TRY_nomem(tes = es); 01238 } 01239 else { 01240 TRY_nomem(es = "Unknown command line option: \""); 01241 TRY_nomem(es += opt); 01242 TRY_nomem(es += "\""); 01243 throw(ERROR(0,es)); 01244 } 01245 } 01246 01247 m_initialized = true; 01248 01249 if ((m_action == action_help) || (m_action == action_version)) 01250 return; 01251 01252 if (use_default()) 01253 read_config(m_default_file); 01254 01255 for (cfi = m_cfgfiles.begin(); cfi != m_cfgfiles.end(); cfi++) { 01256 if (cfi->first == config_file) { 01257 read_config(cfi->second); 01258 } 01259 else { 01260 read_job(cfi->second); 01261 } 01262 } 01263 01264 if (m_configs_read == 0) { 01265 throw(ERROR(0,"No configuration file(s) read")); 01266 } 01267 01268 if (use_custom_timestamp) { 01269 TRY(m_timestamp = custom_timestamp,tes); 01270 } 01271 01272 check(); 01273 } 01274 01275 /** Perform sanity checks on configuration settings */ 01276 void configuration_manager::check(void) const 01277 { 01278 std::string es; 01279 subdirectory subdir; 01280 filestatus fstat; 01281 01282 if (!initialized()) 01283 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01284 01285 // log-dir -- If no log-dir is set then we use a default. What if that 01286 // default doesn't exist? 01287 if (m_log_dir.size() == 0) { 01288 throw(ERROR(0,"log-dir not set")); 01289 } 01290 TRY_nomem(es = "Invalid log-dir value: \""); 01291 TRY_nomem(es += m_log_dir); 01292 TRY_nomem(es += "\""); 01293 TRY_instead(subdir.path(m_log_dir,"*"),es); 01294 01295 // rsync-local-path -- If (a) rsync-local-path is not set from config, and 01296 // (b) a default was determined at compile time, then (c) does the default 01297 // exist? 01298 TRY_nomem(es = "Invalid rsync-local-path value: \""); 01299 TRY_nomem(es += m_rsync_local_path); 01300 TRY_nomem(es += "\""); 01301 TRY(fstat.path(m_rsync_local_path),es); 01302 01303 // vault -- Are there any? 01304 if (m_vaults.size() == 0) { 01305 throw(ERROR(0,"No vaults defined")); 01306 } 01307 01308 // Do all jobs generate a valid job ID? 01309 // Note: This is purely cosmetic, generated job ID strings are only used in 01310 // status reports and report logs. 01311 if (jobs().size() > 0) { 01312 jobs_type::const_iterator cji; 01313 01314 for (cji = jobs().begin(); cji != jobs().end(); cji++) { 01315 if (cji->generate_job_id().size() == 0) { 01316 error e(0); 01317 01318 TRY_nomem(es = "Empty ID generated by job at "); 01319 TRY_nomem(es += cji->config_path); 01320 TRY_nomem(es += "["); 01321 TRY_nomem(es += estring(cji->config_line)); 01322 TRY_nomem(es += "]"); 01323 e.push_back(es); 01324 TRY_nomem(es = 01325 "Use 'jobname' to assign a descriptive name to this job"); 01326 e.push_back(es); 01327 throw(e); 01328 } 01329 } 01330 } 01331 } 01332 01333 /** Return the initialized state of the configuration manager */ 01334 const bool configuration_manager::initialized(void) const 01335 { 01336 return(m_initialized); 01337 } 01338 01339 /** Return the action rvm is to take */ 01340 const configuration_manager::action_type 01341 configuration_manager::action(void) const 01342 { 01343 if (!initialized()) 01344 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01345 01346 return(m_action); 01347 } 01348 01349 /** Return whether or not rvm is to try to read it's default configuration file */ 01350 const bool configuration_manager::use_default(void) const 01351 { 01352 if (!initialized()) 01353 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01354 01355 return(m_default); 01356 } 01357 01358 /** Set the default configuration filename */ 01359 void configuration_manager::default_file(const std::string& a_path) 01360 { 01361 TRY_nomem(m_default_file = a_path); 01362 } 01363 01364 /** Return the default configuration filename */ 01365 const std::string& configuration_manager::default_file(void) const 01366 { 01367 if (!initialized()) 01368 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01369 01370 return(m_default_file); 01371 } 01372 01373 /** Return the default log-dir */ 01374 void configuration_manager::default_logdir(const std::string& a_path) 01375 { 01376 TRY_nomem(m_log_dir = a_path); 01377 } 01378 01379 /** Return the timestamp of this instance of rvm */ 01380 const class timestamp& configuration_manager::timestamp(void) const 01381 { 01382 if (!initialized()) 01383 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01384 01385 return(m_timestamp); 01386 } 01387 01388 /** Return the link-catalog-dir path */ 01389 const std::string& configuration_manager::link_catalog_dir(void) const 01390 { 01391 if (!initialized()) 01392 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01393 01394 return(m_link_catalog_dir); 01395 } 01396 01397 /** Return the log-dir path */ 01398 const std::string& configuration_manager::log_dir(void) const 01399 { 01400 if (!initialized()) 01401 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01402 01403 return(m_log_dir); 01404 } 01405 01406 /** Return the value of delete-old-log-files */ 01407 const bool configuration_manager::delete_old_log_files(void) const 01408 { 01409 if (!initialized()) 01410 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01411 01412 return(m_delete_old_log_files); 01413 } 01414 01415 /** Return the value of delete-old-report-files */ 01416 const bool configuration_manager::delete_old_report_files(void) const 01417 { 01418 if (!initialized()) 01419 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01420 01421 return(m_delete_old_report_files); 01422 } 01423 01424 /** Return the rsync-local-path */ 01425 const std::string& configuration_manager::rsync_local_path(void) const 01426 { 01427 if (!initialized()) 01428 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01429 01430 return(m_rsync_local_path); 01431 } 01432 01433 /** Return the rsync-parallel */ 01434 const uint16& configuration_manager::rsync_parallel(void) const 01435 { 01436 if (!initialized()) 01437 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01438 01439 return(m_rsync_parallel); 01440 } 01441 01442 /** Return the number of seconds to sleep between polling for I/O */ 01443 const uint16& configuration_manager::io_poll_interval(void) const 01444 { 01445 if (!initialized()) 01446 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01447 01448 return(m_io_poll_interval); 01449 } 01450 01451 /** Return the timestamp-resolution */ 01452 const timestamp::resolution_type configuration_manager::timestamp_resolution(void) const 01453 { 01454 if (!initialized()) 01455 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01456 01457 return(m_timestamp.resolution()); 01458 } 01459 01460 /** Return the vaults */ 01461 const configuration_manager::vaults_type& configuration_manager::vaults(void) const 01462 { 01463 if (!initialized()) 01464 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01465 01466 return(m_vaults); 01467 } 01468 01469 /** Return the vault-overflow-behavior */ 01470 const configuration_manager::overflow_type& configuration_manager::vault_overflow_behavior(void) const 01471 { 01472 if (!initialized()) 01473 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01474 01475 return(m_vault_overflow_behavior); 01476 } 01477 01478 /** Return the vault-overflow-blocks */ 01479 const uint16& configuration_manager::vault_overflow_blocks(void) const 01480 { 01481 if (!initialized()) 01482 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01483 01484 return(m_vault_overflow_blocks); 01485 } 01486 01487 /** Return the vault-overflow-inodes */ 01488 const uint16& configuration_manager::vault_overflow_inodes(void) const 01489 { 01490 if (!initialized()) 01491 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01492 01493 return(m_vault_overflow_inodes); 01494 } 01495 01496 /** Return the vault-selection-behavior */ 01497 const configuration_manager::selection_type& configuration_manager::vault_selection_behavior(void) const 01498 { 01499 if (!initialized()) 01500 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01501 01502 return(m_vault_selection_behavior); 01503 } 01504 01505 /** Return the vault-locking selection */ 01506 const bool configuration_manager::vault_locking(void) const 01507 { 01508 if (!initialized()) 01509 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01510 01511 return(m_vault_locking); 01512 } 01513 01514 /** Return the default job configuration */ 01515 const job& configuration_manager::default_job(void) const 01516 { 01517 if (!initialized()) 01518 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01519 01520 return(m_default_job); 01521 } 01522 01523 /** Return a list of jobs */ 01524 const configuration_manager::jobs_type& configuration_manager::jobs(void) const 01525 { 01526 if (!initialized()) 01527 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01528 01529 return(m_jobs); 01530 } 01531 01532 /** Return the logging-level */ 01533 const configuration_manager::logging_type& configuration_manager::logging_level(void) const 01534 { 01535 if (!initialized()) 01536 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01537 01538 return(m_logging_level); 01539 } 01540 01541 /** Return the error-logging-level */ 01542 const configuration_manager::logging_type& configuration_manager::error_logging_level(void) const 01543 { 01544 if (!initialized()) 01545 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); 01546 01547 return(m_error_logging_level); 01548 } 01549 01550 /** Read a configuration file */ 01551 void configuration_manager::read_config(const std::string& a_path) 01552 { 01553 std::string es; 01554 std::ifstream in; 01555 uint16 line = 0; 01556 01557 in.open(a_path.c_str()); 01558 if (!in.is_open()) { 01559 TRY_nomem(es = "Could not open configuration file: \""); 01560 TRY_nomem(es += a_path); 01561 TRY_nomem(es += "\""); 01562 throw(ERROR(errno,es)); 01563 } 01564 01565 global_parser(a_path, line, in); 01566 01567 in.close(); 01568 m_configs_read++; 01569 } 01570 01571 /** Read a job configuration file */ 01572 void configuration_manager::read_job(const std::string& a_path) 01573 { 01574 std::string es; 01575 std::ifstream in; 01576 job njob; 01577 uint16 line = 0; 01578 01579 in.open(a_path.c_str()); 01580 if (!in.is_open()) { 01581 TRY_nomem(es = "Could not open job file: \""); 01582 TRY_nomem(es += a_path); 01583 TRY_nomem(es += "\""); 01584 throw(ERROR(errno,es)); 01585 } 01586 01587 njob = m_default_job; 01588 01589 job_parser(&njob, a_path, line, in, "", false); 01590 01591 njob.check(); 01592 01593 in.close(); 01594 } 01595 01596 //---------------------------------------------------------------------------- 01597 01598 /** Parse a keyword/value pair read from a configuration file */ 01599 void parse_line( 01600 std::istream& a_in, 01601 std::string& a_keyword, 01602 std::string& a_value 01603 ) 01604 { 01605 std::string es; 01606 char ch; 01607 01608 a_keyword.erase(); 01609 a_value.erase(); 01610 01611 while (true) { 01612 a_in.get(ch); 01613 // Skip whitespace before keyword. 01614 while ((a_in) && ((ch == ' ') || (ch == '\t'))) { 01615 a_in.get(ch); 01616 } 01617 // Read keyword 01618 while ((a_in) && ((ch != ' ') && (ch != '\t') && (ch != '\n'))) { 01619 TRY_nomem(a_keyword += ch); 01620 a_in.get(ch); 01621 } 01622 if (!a_in) 01623 return; 01624 01625 // If keyword is empty then this is a blank line, skip it. 01626 if (a_keyword.size() == 0) 01627 return; 01628 01629 // If keyword starts with a '#' then this is a comment line, skip it. 01630 if (a_keyword[0] == '#') { 01631 // Discard the rest of the line. 01632 while ((a_in) && (ch != '\n')) { 01633 a_in.get(ch); 01634 } 01635 return; 01636 } 01637 01638 a_keyword = estring(a_keyword).lower(); 01639 01640 // If there is not value, return. 01641 if (ch == '\n') 01642 return; 01643 01644 // Skip whitespace between keyword and value. 01645 a_in.get(ch); 01646 while ((a_in) && ((ch == ' ') || (ch == '\t'))) { 01647 a_in.get(ch); 01648 } 01649 if (!a_in) 01650 return; 01651 a_in.putback(ch); 01652 if (!a_in) 01653 return; 01654 01655 // Read value 01656 a_in.get(ch); 01657 while ((a_in) && (ch != '\n')) { 01658 TRY_nomem(a_value += ch); 01659 a_in.get(ch); 01660 } 01661 return; 01662 } 01663 } 01664 01665 /** Given a path, strip off the basename -- like the dirname UNIX command */ 01666 const std::string parse_dirname(const std::string& a_path) 01667 { 01668 std::string rpath; 01669 01670 TRY_nomem(rpath = a_path); 01671 01672 if (rpath.find_last_of('/') != std::string::npos) 01673 rpath.erase(rpath.find_last_of('/')); 01674 01675 if (rpath.size() == 0) { 01676 TRY_nomem(rpath = "./"); 01677 return(rpath); 01678 } 01679 01680 if (rpath[rpath.size()-1] != '/') { 01681 TRY_nomem(rpath += '/'); 01682 } 01683 01684 return(rpath); 01685 } 01686 01687 /** Given a path, strip off the directory -- like the basename UNIX commane */ 01688 const std::string parse_basename(const std::string& a_path) 01689 { 01690 std::string es; 01691 std::string rpath; 01692 01693 TRY_nomem(rpath = a_path); 01694 01695 if (rpath.find_last_of('/') != std::string::npos) 01696 rpath.erase(0,rpath.find_last_of('/')); 01697 01698 return(rpath); 01699 } 01700 01701 //---------------------------------------------------------------------------- 01702 01703 /** C'tor */ 01704 global_parser::global_parser( 01705 const std::string& a_path, 01706 uint16& a_line, 01707 std::istream& a_in 01708 ) 01709 { 01710 m_in = &a_in; 01711 m_path = &a_path; 01712 m_line = &a_line; 01713 01714 parse(); 01715 } 01716 01717 /** Generate a string showing the current location within a configuration file 01718 * of the parser */ 01719 const std::string global_parser::location(void) 01720 { 01721 std::string es; 01722 01723 TRY_nomem(es = "At "); 01724 TRY_nomem(es += (*m_path)); 01725 TRY_nomem(es += "["); 01726 TRY_nomem(es += estring((*m_line))); 01727 TRY_nomem(es += "]"); 01728 01729 return(es); 01730 } 01731 01732 /** Read a configuration file, used by the "include" command */ 01733 void global_parser::read_config(const std::string& a_path) 01734 { 01735 std::string es; 01736 std::ifstream in; 01737 uint16 line = 0; 01738 01739 in.open(a_path.c_str()); 01740 if (!in.is_open()) { 01741 TRY_nomem(es = "Could not open configuraiton file: \""); 01742 TRY_nomem(es += a_path); 01743 TRY_nomem(es += "\""); 01744 throw(ERROR(errno,es)); 01745 } 01746 01747 global_parser(a_path, line, in); 01748 01749 in.close(); 01750 config.m_configs_read++; 01751 } 01752 01753 /** Read a job configuration file, used by the "include-job" command */ 01754 void global_parser::read_job(const std::string& a_path, job& a_job) 01755 { 01756 std::string es; 01757 std::ifstream in; 01758 uint16 line = 0; 01759 01760 in.open(a_path.c_str()); 01761 if (!in.is_open()) { 01762 TRY_nomem(es = "Could not open job file: \""); 01763 TRY_nomem(es += a_path); 01764 TRY_nomem(es += "\""); 01765 throw(ERROR(errno,es)); 01766 } 01767 01768 job_parser(&a_job, a_path, line, in, "", false); 01769 01770 TRY_nomem(a_job.config_path = *m_path); 01771 TRY_nomem(a_job.config_line = *m_line); 01772 a_job.check(); 01773 01774 in.close(); 01775 } 01776 01777 /** Global context parser */ 01778 void global_parser::parse(void) 01779 { 01780 std::string es; 01781 std::string keyword; 01782 std::string value; 01783 01784 while ((*m_in)) { 01785 (*m_line)++; 01786 01787 try { 01788 parse_line((*m_in), keyword, value); 01789 } 01790 catch(error e) { 01791 e.push_back(ERROR_INSTANCE(location())); 01792 throw(e); 01793 } 01794 catch(...) { 01795 error e = err_unknown; 01796 01797 e.push_back(ERROR_INSTANCE(location())); 01798 throw(e); 01799 } 01800 01801 if (keyword.size() == 0) 01802 continue; 01803 if (keyword[0] == '#') 01804 continue; 01805 01806 try { 01807 if (keyword == "<default>") { 01808 parse_default(value); 01809 } 01810 else if (keyword == "include") { 01811 parse_include(value); 01812 } 01813 else if (keyword == "include-job") { 01814 parse_include_job(value); 01815 } 01816 else if (keyword == "<job>") { 01817 parse_job(value); 01818 } 01819 else if (keyword == "link-catalog-dir") { 01820 parse_link_catalog_dir(value); 01821 } 01822 else if (keyword == "log-dir") { 01823 parse_log_dir(value); 01824 } 01825 else if (keyword == "delete-old-log-files") { 01826 parse_delete_old_log_files(value); 01827 } 01828 else if (keyword == "delete-old-report-files") { 01829 parse_delete_old_report_files(value); 01830 } 01831 else if (keyword == "logging-level") { 01832 parse_logging_level(value); 01833 } 01834 else if (keyword == "error-logging-level") { 01835 parse_error_logging_level(value); 01836 } 01837 else if (keyword == "rsync-local-path") { 01838 parse_rsync_local_path(value); 01839 } 01840 else if (keyword == "rsync-parallel") { 01841 parse_rsync_parallel(value); 01842 } 01843 else if (keyword == "io-poll-interval") { 01844 parse_io_poll_interval(value); 01845 } 01846 else if (keyword == "timestamp-resolution") { 01847 parse_timestamp_resolution(value); 01848 } 01849 else if (keyword == "vault") { 01850 try { 01851 parse_vault(value); 01852 } 01853 catch(error e) { 01854 std::cerr << e; 01855 } 01856 catch(...) { 01857 throw(err_unknown); 01858 } 01859 } 01860 else if (keyword == "vault-overflow-behavior") { 01861 parse_vault_overflow_behavior(value); 01862 } 01863 else if (keyword == "vault-overflow-blocks") { 01864 parse_vault_overflow_blocks(value); 01865 } 01866 else if (keyword == "vault-overflow-inodes") { 01867 parse_vault_overflow_inodes(value); 01868 } 01869 else if (keyword == "vault-selection-behavior") { 01870 parse_vault_selection_behavior(value); 01871 } 01872 else if (keyword == "vault-locking") { 01873 parse_vault_locking(value); 01874 } 01875 else { 01876 error e(0); 01877 01878 TRY_nomem(es = "Unknown command in global context: \""); 01879 TRY_nomem(es += keyword); 01880 TRY_nomem(es += "\""); 01881 throw(ERROR(0,es)); 01882 } 01883 } 01884 catch(error e) { 01885 e.push_back(ERROR_INSTANCE(location())); 01886 throw(e); 01887 } 01888 catch(...) { 01889 error e = err_unknown; 01890 01891 e.push_back(ERROR_INSTANCE(location())); 01892 throw(e); 01893 } 01894 } 01895 } 01896 01897 /** Parse a default job context */ 01898 void global_parser::parse_default(const std::string& a_value) 01899 { 01900 std::string delimiter; 01901 std::string default_config_path; 01902 uint16 default_config_line; 01903 job* jobPtr = &config.m_default_job; 01904 01905 config.m_default_job.clear(); 01906 01907 TRY_nomem(default_config_path = *m_path); 01908 TRY_nomem(default_config_line = *m_line); 01909 TRY_nomem(delimiter = "</default>"); 01910 01911 job_parser(jobPtr, *m_path, *m_line, *m_in, delimiter, true); 01912 01913 TRY_nomem(jobPtr->default_config_path = default_config_path); 01914 jobPtr->default_config_line = default_config_line; 01915 } 01916 01917 /** Parse an "include" command */ 01918 void global_parser::parse_include(const std::string& a_value) 01919 { 01920 std::string es; 01921 directory dir; 01922 directory::const_iterator cdi; 01923 std::string rpath; 01924 std::string ipath; 01925 01926 if (a_value.size() == 0) { 01927 TRY_nomem(es = "Invalid include path: \""); 01928 TRY_nomem(es += a_value); 01929 TRY_nomem(es += "\""); 01930 throw(ERROR(0,es)); 01931 } 01932 01933 if (a_value[0] != '/') { 01934 TRY_nomem(rpath = parse_dirname(*m_path)); 01935 } 01936 01937 TRY_nomem(ipath = rpath + a_value); 01938 01939 TRY_nomem(es = "No configuration file(s) found: \""); 01940 TRY_nomem(es += a_value); 01941 TRY_nomem(es += "\""); 01942 TRY_instead(dir.path(ipath),es); 01943 if (dir.size() == 0) 01944 throw(ERROR(0,es)); 01945 01946 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 01947 if (dir.size() > 1) { 01948 TRY_nomem(es = "For files found matching: \""); 01949 TRY_nomem(es += a_value); 01950 TRY_nomem(es += "\""); 01951 try { 01952 read_config(*cdi); 01953 } 01954 catch(error e) { 01955 e.push_back(ERROR_INSTANCE(es)); 01956 throw(e); 01957 } 01958 catch(...) { 01959 error e = err_unknown; 01960 01961 e.push_back(ERROR_INSTANCE(es)); 01962 throw(e); 01963 } 01964 } 01965 else 01966 read_config(*cdi); 01967 } 01968 } 01969 01970 /** Parse an "include-job" command */ 01971 void global_parser::parse_include_job(const std::string& a_value) 01972 { 01973 std::string es; 01974 directory dir; 01975 directory::const_iterator cdi; 01976 std::string rpath; 01977 std::string ipath; 01978 01979 if (a_value.size() == 0) { 01980 TRY_nomem(es = "Invalid include-job path: \""); 01981 TRY_nomem(es += a_value); 01982 TRY_nomem(es += "\""); 01983 throw(ERROR(0,es)); 01984 } 01985 01986 if (a_value[0] != '/') { 01987 TRY_nomem(rpath = parse_dirname(*m_path)); 01988 } 01989 01990 TRY_nomem(ipath = rpath + a_value); 01991 01992 TRY_nomem(es = "No job file(s) found: \""); 01993 TRY_nomem(es += a_value); 01994 TRY_nomem(es += "\""); 01995 TRY(dir.path(ipath),es); 01996 if (dir.size() == 0) 01997 throw(ERROR(0,es)); 01998 01999 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 02000 job new_job; 02001 02002 new_job = config.m_default_job; 02003 02004 if (dir.size() > 1) { 02005 TRY_nomem(es = "For files found matching: \""); 02006 TRY_nomem(es += a_value); 02007 TRY_nomem(es += "\""); 02008 try { 02009 read_job(*cdi, new_job); 02010 } 02011 catch(error e) { 02012 e.push_back(ERROR_INSTANCE(es)); 02013 throw(e); 02014 } 02015 catch(...) { 02016 error e = err_unknown; 02017 02018 e.push_back(ERROR_INSTANCE(es)); 02019 throw(e); 02020 } 02021 } 02022 else 02023 read_job(*cdi, new_job); 02024 TRY_nomem(config.m_jobs.push_back(new_job)); 02025 } 02026 } 02027 02028 /** Parse a job context */ 02029 void global_parser::parse_job(const std::string& a_value) 02030 { 02031 std::string delimiter; 02032 std::string config_path; 02033 uint16 config_line; 02034 job new_job; 02035 02036 TRY_nomem(config_path = *m_path); 02037 config_line = *m_line; 02038 new_job = config.m_default_job; 02039 02040 TRY_nomem(new_job.config_path = config_path); 02041 new_job.config_line = config_line; 02042 TRY_nomem(delimiter = "</job>"); 02043 02044 job_parser(&new_job, *m_path, *m_line, *m_in, delimiter, false); 02045 02046 new_job.check(); 02047 02048 TRY_nomem(config.m_jobs.push_back(new_job)); 02049 } 02050 02051 /** Parse a "link-catalog-dir" command */ 02052 void global_parser::parse_link_catalog_dir(const std::string& a_value) 02053 { 02054 std::string es; 02055 subdirectory subdir; 02056 02057 TRY_nomem(es = "Invalid link-catalog-dir path: \""); 02058 TRY_nomem(es += a_value); 02059 TRY_nomem(es += "\""); 02060 TRY_instead(subdir.path(a_value,"*"),es); 02061 TRY_nomem(config.m_link_catalog_dir = a_value); 02062 } 02063 02064 /** Parse a "log-dir" command */ 02065 void global_parser::parse_log_dir(const std::string& a_value) 02066 { 02067 std::string es; 02068 subdirectory subdir; 02069 02070 TRY_nomem(es = "Invalid log-dir path: \""); 02071 TRY_nomem(es += a_value); 02072 TRY_nomem(es += "\""); 02073 TRY_instead(subdir.path(a_value,"*"),es); 02074 TRY_nomem(config.m_log_dir = a_value); 02075 } 02076 02077 /** Parse a "delete-old-log-files" command */ 02078 void global_parser::parse_delete_old_log_files(const std::string& a_value) 02079 { 02080 std::string es; 02081 estring str; 02082 02083 TRY_nomem(es = "Invalid delete-old-log-files value: \""); 02084 TRY_nomem(es += a_value); 02085 TRY_nomem(es += "\""); 02086 TRY(str = estring(a_value).lower(),es); 02087 if ( 02088 (str == "y") 02089 || (str == "yes") 02090 || (str == "t") 02091 || (str == "true") 02092 || (str == "1") 02093 || (str == "on") 02094 ) { 02095 config.m_delete_old_log_files = true; 02096 } 02097 else if ( 02098 (str == "n") 02099 || (str == "no") 02100 || (str == "f") 02101 || (str == "false") 02102 || (str == "0") 02103 || (str == "off") 02104 ) { 02105 config.m_delete_old_log_files = false; 02106 } 02107 else { 02108 throw(ERROR(0,es)); 02109 } 02110 } 02111 02112 /** Parse a "delete-old-report-files" command */ 02113 void global_parser::parse_delete_old_report_files(const std::string& a_value) 02114 { 02115 std::string es; 02116 estring str; 02117 02118 TRY_nomem(es = "Invalid delete-old-report-files value: \""); 02119 TRY_nomem(es += a_value); 02120 TRY_nomem(es += "\""); 02121 TRY(str = estring(a_value).lower(),es); 02122 if ( 02123 (str == "y") 02124 || (str == "yes") 02125 || (str == "t") 02126 || (str == "true") 02127 || (str == "1") 02128 || (str == "on") 02129 ) { 02130 config.m_delete_old_report_files = true; 02131 } 02132 else if ( 02133 (str == "n") 02134 || (str == "no") 02135 || (str == "f") 02136 || (str == "false") 02137 || (str == "0") 02138 || (str == "off") 02139 ) { 02140 config.m_delete_old_report_files = false; 02141 } 02142 else { 02143 throw(ERROR(0,es)); 02144 } 02145 } 02146 02147 /** Parse a "loging-level" command */ 02148 void global_parser::parse_logging_level(const std::string& a_value) 02149 { 02150 std::string es; 02151 estring str; 02152 02153 TRY_nomem(es = "Invalid logging-level value: \""); 02154 TRY_nomem(es += a_value); 02155 TRY_nomem(es += "\""); 02156 TRY_nomem(str = estring(a_value).lower()); 02157 if (str == "manager") { 02158 config.m_logging_level = configuration_manager::logging_manager; 02159 } 02160 else if (str == "child") { 02161 config.m_logging_level = configuration_manager::logging_child; 02162 } 02163 else if (str == "rsync") { 02164 config.m_logging_level = configuration_manager::logging_rsync; 02165 } 02166 else { 02167 throw(ERROR(0,es)); 02168 } 02169 } 02170 02171 /** Parse a "error-loging-level" command */ 02172 void global_parser::parse_error_logging_level(const std::string& a_value) 02173 { 02174 std::string es; 02175 estring str; 02176 02177 TRY_nomem(es = "Invalid error-logging-level value: \""); 02178 TRY_nomem(es += a_value); 02179 TRY_nomem(es += "\""); 02180 TRY_nomem(str = estring(a_value).lower()); 02181 if (str == "manager") { 02182 config.m_error_logging_level = configuration_manager::logging_manager; 02183 } 02184 else if (str == "child") { 02185 config.m_error_logging_level = configuration_manager::logging_child; 02186 } 02187 else if (str == "rsync") { 02188 config.m_error_logging_level = configuration_manager::logging_rsync; 02189 } 02190 else { 02191 throw(ERROR(0,es)); 02192 } 02193 } 02194 02195 /** Parse an "rsync-local-path" command */ 02196 void global_parser::parse_rsync_local_path(const std::string& a_value) 02197 { 02198 std::string es; 02199 filestatus fstat; 02200 02201 TRY_nomem(es = "Invalid rsync-local-path value: \""); 02202 TRY_nomem(es += a_value); 02203 TRY_nomem(es += "\""); 02204 TRY_instead(fstat.path(a_value),es); 02205 TRY_nomem(config.m_rsync_local_path = a_value); 02206 } 02207 02208 /** Parse an "rsync-parallel" command */ 02209 void global_parser::parse_rsync_parallel(const std::string& a_value) 02210 { 02211 std::string es; 02212 uint16 num; 02213 02214 TRY_nomem(es = "Invalid rsync-parallel value: \""); 02215 TRY_nomem(es += a_value); 02216 TRY_nomem(es += "\""); 02217 TRY_instead(num = estring(a_value),es); 02218 if (num == 0) 02219 throw(ERROR(0,es)); 02220 config.m_rsync_parallel = num; 02221 } 02222 02223 /** Parse "io-poll-interval" command */ 02224 void global_parser::parse_io_poll_interval(const std::string& a_value) 02225 { 02226 std::string es; 02227 uint16 num; 02228 02229 TRY_nomem(es = "Invalid io-poll-interval value: \""); 02230 TRY_nomem(es += a_value); 02231 TRY_nomem(es += "\""); 02232 TRY_instead(num = estring(a_value),es); 02233 if (num == 0) 02234 throw(ERROR(0,es)); 02235 config.m_io_poll_interval = num; 02236 } 02237 02238 /** Parse a "timestamp-resolution" command */ 02239 void global_parser::parse_timestamp_resolution(const std::string& a_value) 02240 { 02241 std::string es; 02242 estring str; 02243 02244 TRY_nomem(es = "Invalid timestamp-resolution: \""); 02245 TRY_nomem(es += a_value); 02246 TRY_nomem(es += "\""); 02247 TRY(str = estring(a_value).lower(),es); 02248 if (str == "year") { 02249 config.m_timestamp.resolution(timestamp::resolution_year); 02250 } 02251 else if (str == "month") { 02252 config.m_timestamp.resolution(timestamp::resolution_month); 02253 } 02254 else if (str == "day") { 02255 config.m_timestamp.resolution(timestamp::resolution_day); 02256 } 02257 else if (str == "hour") { 02258 config.m_timestamp.resolution(timestamp::resolution_hour); 02259 } 02260 else if (str == "minute") { 02261 config.m_timestamp.resolution(timestamp::resolution_minute); 02262 } 02263 else if (str == "second") { 02264 config.m_timestamp.resolution(timestamp::resolution_second); 02265 } 02266 else { 02267 throw(ERROR(0,es)); 02268 } 02269 } 02270 02271 /** Parse a "vault" command */ 02272 void global_parser::parse_vault(const std::string& a_value) 02273 { 02274 std::string es; 02275 directory dir; 02276 directory::const_iterator cdi; 02277 subdirectory subdir; 02278 filestatus fstat; 02279 error partial_vault_error = ERROR(0,"One or more vault paths were found to be invalid:"); 02280 02281 TRY_nomem(es = "Invalid vault path: \""); 02282 TRY_nomem(es += a_value); 02283 TRY_nomem(es += "\""); 02284 02285 if (a_value.size() == 0) 02286 throw(ERROR(0,es)); 02287 02288 TRY(dir.path(a_value),es); 02289 02290 if (dir.size() == 0) { 02291 TRY_instead(fstat.path(a_value),es); 02292 } 02293 02294 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 02295 if (!is_dir(*cdi)) { 02296 TRY_nomem(es = get_error_str(ENOTDIR)); 02297 TRY_nomem(es += ": \""); 02298 TRY_nomem(es += *cdi); 02299 TRY_nomem(es += "\""); 02300 partial_vault_error.push_back(es); 02301 continue; 02302 } 02303 if (!readable(*cdi)) { 02304 TRY_nomem(es = "No read access to path: \""); 02305 TRY_nomem(es += *cdi); 02306 TRY_nomem(es += "\""); 02307 partial_vault_error.push_back(es); 02308 continue; 02309 } 02310 if (!writable(*cdi)) { 02311 TRY_nomem(es = "No write access to path: \""); 02312 TRY_nomem(es += *cdi); 02313 TRY_nomem(es += "\""); 02314 partial_vault_error.push_back(es); 02315 continue; 02316 } 02317 if (!executable(*cdi)) { 02318 TRY_nomem(es = "No execution access to path: \""); 02319 TRY_nomem(es += *cdi); 02320 TRY_nomem(es += "\""); 02321 partial_vault_error.push_back(es); 02322 continue; 02323 } 02324 TRY_nomem(config.m_vaults.push_back(*cdi)); 02325 } 02326 02327 if (partial_vault_error.size() > 1) { 02328 TRY_nomem(es = "For paths found matching: \""); 02329 TRY_nomem(es += a_value); 02330 TRY_nomem(es += "\""); 02331 partial_vault_error.push_back(es); 02332 throw(partial_vault_error); 02333 } 02334 } 02335 02336 /** Parse a "vault-overflow-behavior" command */ 02337 void global_parser::parse_vault_overflow_behavior(const std::string& a_value) 02338 { 02339 std::string es; 02340 estring str; 02341 02342 TRY_nomem(es = "Invalid vault-overflow-behavior value: \""); 02343 TRY_nomem(es += a_value); 02344 TRY_nomem(es += "\""); 02345 TRY(str = estring(a_value).lower(),es); 02346 if (str == "quit") { 02347 config.m_vault_overflow_behavior = configuration_manager::overflow_quit; 02348 } 02349 else if (str == "delete-oldest") { 02350 config.m_vault_overflow_behavior = configuration_manager::overflow_delete_oldest; 02351 } 02352 else if (str == "delete-until-free") { 02353 config.m_vault_overflow_behavior = configuration_manager::overflow_delete_until_free; 02354 } 02355 else { 02356 throw(ERROR(0,es)); 02357 } 02358 } 02359 02360 /** Parse a "vault-overflow-blocks" command */ 02361 void global_parser::parse_vault_overflow_blocks(const std::string& a_value) 02362 { 02363 std::string es; 02364 uint16 num; 02365 02366 TRY_nomem(es = "Invalid vault-overflow-blocks value: \""); 02367 TRY_nomem(es += a_value); 02368 TRY_nomem(es += "\""); 02369 TRY(num = estring(a_value),es); 02370 if (num > 50) 02371 throw(ERROR(0,es)); 02372 config.m_vault_overflow_blocks = num; 02373 } 02374 02375 /** Parse a "vault-overflow-inodes" command */ 02376 void global_parser::parse_vault_overflow_inodes(const std::string& a_value) 02377 { 02378 std::string es; 02379 uint16 num; 02380 02381 TRY_nomem(es = "Invalid vault-overflow-inodes value: \""); 02382 TRY_nomem(es += a_value); 02383 TRY_nomem(es += "\""); 02384 TRY(num = estring(a_value),es); 02385 if (num > 50) 02386 throw(ERROR(0,es)); 02387 config.m_vault_overflow_inodes = num; 02388 } 02389 02390 /** Parse a "vault-selection-behavior" command */ 02391 void global_parser::parse_vault_selection_behavior(const std::string& a_value) 02392 { 02393 std::string es; 02394 estring str; 02395 02396 TRY_nomem(es = "Invalid vault-selection-behavior value: \""); 02397 TRY_nomem(es += a_value); 02398 TRY_nomem(es += "\""); 02399 TRY(str = estring(a_value).lower(),es); 02400 if (str == "max-free") { 02401 config.m_vault_selection_behavior = configuration_manager::selection_max_free; 02402 } 02403 else if (str == "round-robin") { 02404 config.m_vault_selection_behavior = configuration_manager::selection_round_robin; 02405 } 02406 else { 02407 throw(ERROR(0,es)); 02408 } 02409 } 02410 02411 /** Parse a "vault-locking" command */ 02412 void global_parser::parse_vault_locking(const std::string& a_value) 02413 { 02414 std::string es; 02415 estring str; 02416 02417 TRY_nomem(es = "Invalid vault-locking value: \""); 02418 TRY_nomem(es += a_value); 02419 TRY_nomem(es += "\""); 02420 TRY(str = estring(a_value).lower(),es); 02421 if ( 02422 (str == "y") 02423 || (str == "yes") 02424 || (str == "t") 02425 || (str == "true") 02426 || (str == "1") 02427 || (str == "on") 02428 ) { 02429 config.m_vault_locking = true; 02430 } 02431 else if ( 02432 (str == "n") 02433 || (str == "no") 02434 || (str == "f") 02435 || (str == "false") 02436 || (str == "0") 02437 || (str == "off") 02438 ) { 02439 config.m_vault_locking = false; 02440 } 02441 else { 02442 throw(ERROR(0,es)); 02443 } 02444 } 02445 02446 //---------------------------------------------------------------------------- 02447 02448 /** C'tor */ 02449 job_parser::job_parser( 02450 job * a_job, 02451 const std::string& a_path, 02452 uint16& a_line, 02453 std::istream& a_in, 02454 const std::string& a_block_delimiter, 02455 const bool a_default_context = false 02456 ) 02457 { 02458 m_job = a_job; 02459 m_in = &a_in; 02460 m_path = &a_path; 02461 m_line = &a_line; 02462 m_delimiter = &a_block_delimiter; 02463 m_default_context = a_default_context; 02464 02465 parse(); 02466 } 02467 02468 /** Construct a string with the current location of the parser in a 02469 * configuration file */ 02470 const std::string job_parser::location(void) 02471 { 02472 std::string es; 02473 02474 TRY_nomem(es = "At "); 02475 TRY_nomem(es += (*m_path)); 02476 TRY_nomem(es += "["); 02477 TRY_nomem(es += estring((*m_line))); 02478 TRY_nomem(es += "]"); 02479 02480 return(es); 02481 } 02482 02483 /** Read a job configuration file, used by the "include" command */ 02484 void job_parser::read_job(const std::string& a_path) 02485 { 02486 std::string es; 02487 std::ifstream in; 02488 uint16 line = 0; 02489 02490 in.open(a_path.c_str()); 02491 if (!in.is_open()) { 02492 TRY_nomem(es = "Could not open job file: \""); 02493 TRY_nomem(es += a_path); 02494 TRY_nomem(es += "\""); 02495 throw(ERROR(errno,es)); 02496 } 02497 02498 job_parser(m_job, a_path, line, in, "", m_default_context); 02499 02500 in.close(); 02501 } 02502 02503 /** Job context parser */ 02504 void job_parser::parse(void) 02505 { 02506 std::string es; 02507 std::string keyword; 02508 std::string value; 02509 02510 while ((*m_in)) { 02511 (*m_line)++; 02512 02513 try { 02514 parse_line((*m_in), keyword, value); 02515 } 02516 catch(error e) { 02517 e.push_back(ERROR_INSTANCE(location())); 02518 throw(e); 02519 } 02520 catch(...) { 02521 error e = err_unknown; 02522 02523 e.push_back(ERROR_INSTANCE(location())); 02524 throw(e); 02525 } 02526 02527 if (!(*m_in) && (m_delimiter->size() != 0)) { 02528 TRY_nomem(es = "Unexpected end of file, expected \""); 02529 TRY_nomem(es += *m_delimiter); 02530 TRY_nomem(es += "\" here"); 02531 throw(ERROR(errno,es)); 02532 } 02533 02534 if (keyword.size() == 0) 02535 continue; 02536 if (keyword[0] == '#') 02537 continue; 02538 02539 try { 02540 if (keyword == *m_delimiter) { 02541 return; 02542 } 02543 else if (keyword == "archive-path") { 02544 parse_archive_path(value); 02545 } 02546 else if (keyword == "clear") { 02547 parse_clear(value); 02548 } 02549 else if (keyword == "exclude-from") { 02550 parse_exclude_from(value); 02551 } 02552 else if (keyword == "include-from") { 02553 parse_include_from(value); 02554 } 02555 else if (keyword == "groupname") { 02556 parse_groupname(value); 02557 } 02558 else if (keyword == "hostname") { 02559 parse_hostname(value); 02560 } 02561 else if (keyword == "include") { 02562 parse_include(value); 02563 } 02564 else if ((keyword == "jobname") && (*m_delimiter == "</job>")) { 02565 parse_jobname(value); 02566 } 02567 else if (keyword == "path") { 02568 parse_path(value); 02569 } 02570 else if (keyword == "rsync-behavior") { 02571 parse_rsync_behavior(value); 02572 } 02573 else if (keyword == "rsync-connection-type") { 02574 parse_rsync_connection_type(value); 02575 } 02576 else if (keyword == "rsync-hardlink") { 02577 parse_rsync_hardlink(value); 02578 } 02579 else if (keyword == "rsync-multi-hardlink") { 02580 parse_rsync_multi_hardlink(value); 02581 } 02582 else if (keyword == "rsync-multi-hardlink-max") { 02583 parse_rsync_multi_hardlink_max(value); 02584 } 02585 else if (keyword == "rsync-options") { 02586 parse_rsync_options(value); 02587 } 02588 else if (keyword == "<rsync-options>") { 02589 parse_rsync_options_context(value); 02590 } 02591 else if (keyword == "rsync-remote-user") { 02592 parse_rsync_remote_user(value); 02593 } 02594 else if (keyword == "rsync-remote-path") { 02595 parse_rsync_remote_path(value); 02596 } 02597 else if (keyword == "rsync-remote-port") { 02598 parse_rsync_remote_port(value); 02599 } 02600 else if (keyword == "rsync-remote-module") { 02601 parse_rsync_remote_module(value); 02602 } 02603 else if (keyword == "rsync-retry-count") { 02604 parse_rsync_retry_count(value); 02605 } 02606 else if (keyword == "rsync-retry-delay") { 02607 parse_rsync_retry_delay(value); 02608 } 02609 else if (keyword == "rsync-timeout") { 02610 parse_rsync_timeout(value); 02611 } 02612 else { 02613 error e(0); 02614 02615 TRY_nomem(es = "Unknown command in "); 02616 if (m_default_context) { 02617 TRY_nomem(es += "default"); 02618 } 02619 else { 02620 TRY_nomem(es += "job"); 02621 } 02622 TRY_nomem(es += " context: \""); 02623 TRY_nomem(es += keyword); 02624 TRY_nomem(es += "\""); 02625 throw(ERROR(0,es)); 02626 } 02627 } 02628 catch(error e) { 02629 if ((*m_delimiter).size() == 0) 02630 e.push_back(ERROR_INSTANCE(location())); 02631 throw(e); 02632 } 02633 catch(...) { 02634 error e = err_unknown; 02635 02636 e.push_back(ERROR_INSTANCE(location())); 02637 throw(e); 02638 } 02639 } 02640 } 02641 02642 /** Parse an "archive-path" command */ 02643 void job_parser::parse_archive_path(const std::string& a_value) 02644 { 02645 std::string es; 02646 02647 TRY_nomem(es = "Invalid archive-path value"); 02648 TRY((*m_job).archive_path = a_value,es); 02649 } 02650 02651 /** Parse a "clear" command */ 02652 void job_parser::parse_clear(const std::string& a_value) 02653 { 02654 std::string es; 02655 estring str; 02656 02657 TRY_nomem(es = "Invalid clear value: \""); 02658 TRY_nomem(es += a_value); 02659 TRY_nomem(es += "\""); 02660 TRY(str = estring(a_value).lower(),es); 02661 if (str == "archive-path") { 02662 m_job->archive_path.clear(); 02663 } 02664 else if (str == "exclude-from") { 02665 m_job->excludes.clear(); 02666 } 02667 else if (str == "groupname") { 02668 m_job->groupname.erase(); 02669 } 02670 else if (str == "hostname") { 02671 m_job->hostname.erase(); 02672 } 02673 else if (str == "include-from") { 02674 m_job->includes.clear(); 02675 } 02676 else if ((str == "jobname") && (*m_delimiter == "</job>")) { 02677 m_job->jobname.erase(); 02678 } 02679 else if (str == "paths") { 02680 m_job->paths.clear(); 02681 } 02682 else if (str == "rsync-behavior") { 02683 m_job->rsync_behavior.clear(); 02684 } 02685 else if (str == "rsync-options") { 02686 m_job->rsync_options.erase(); 02687 } 02688 else if (str == "rsync-remote-user") { 02689 m_job->rsync_remote_user.erase(); 02690 } 02691 else if (str == "rsync-remotr-port") { 02692 m_job->rsync_remote_port = 0; 02693 } 02694 else if (str == "rsync-remote-module") { 02695 m_job->rsync_remote_module.erase(); 02696 } 02697 else if (str == "rsync-remote-path") { 02698 m_job->rsync_remote_path.erase(); 02699 } 02700 else { 02701 throw(ERROR(0,es)); 02702 } 02703 } 02704 02705 /** Parse an "exclude-from" command */ 02706 void job_parser::parse_exclude_from(const std::string& a_value) 02707 { 02708 std::string es; 02709 directory dir; 02710 directory::const_iterator cdi; 02711 std::string rpath; 02712 std::string ipath; 02713 02714 if (a_value.size() == 0) { 02715 TRY_nomem(es = "Invalid exclude-from path: \""); 02716 TRY_nomem(es += a_value); 02717 TRY_nomem(es += "\""); 02718 throw(ERROR(0,es)); 02719 } 02720 02721 if (a_value[0] != '/') { 02722 TRY_nomem(rpath = parse_dirname(*m_path)); 02723 } 02724 02725 TRY_nomem(ipath = rpath + a_value); 02726 02727 TRY_nomem(es = "No exclude-from file(s) found: \""); 02728 TRY_nomem(es += a_value); 02729 TRY_nomem(es += "\""); 02730 TRY(dir.path(ipath),es); 02731 if (dir.size() == 0) 02732 throw(ERROR(0,es)); 02733 02734 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 02735 TRY_nomem((*m_job).excludes.push_back(*cdi)); 02736 } 02737 } 02738 02739 /** Parse an "include-from" command */ 02740 void job_parser::parse_include_from(const std::string& a_value) 02741 { 02742 std::string es; 02743 directory dir; 02744 directory::const_iterator cdi; 02745 std::string rpath; 02746 std::string ipath; 02747 02748 if (a_value.size() == 0) { 02749 TRY_nomem(es = "Invalid include-from path: \""); 02750 TRY_nomem(es += a_value); 02751 TRY_nomem(es += "\""); 02752 throw(ERROR(0,es)); 02753 } 02754 02755 if (a_value[0] != '/') { 02756 TRY_nomem(rpath = parse_dirname(*m_path)); 02757 } 02758 02759 TRY_nomem(ipath = rpath + a_value); 02760 02761 TRY_nomem(es = "No include-from file(s) found: \""); 02762 TRY_nomem(es += a_value); 02763 TRY_nomem(es += "\""); 02764 TRY(dir.path(ipath),es); 02765 if (dir.size() == 0) 02766 throw(ERROR(0,es)); 02767 02768 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 02769 TRY_nomem((*m_job).includes.push_back(*cdi)); 02770 } 02771 } 02772 02773 /** Parse a "groupname" command */ 02774 void job_parser::parse_groupname(const std::string& a_value) 02775 { 02776 TRY_nomem((*m_job).groupname = a_value); 02777 } 02778 02779 /** Parse a "hostname" command */ 02780 void job_parser::parse_hostname(const std::string& a_value) 02781 { 02782 TRY_nomem((*m_job).hostname = a_value); 02783 } 02784 02785 /** Parse an "include" command */ 02786 void job_parser::parse_include(const std::string& a_value) 02787 { 02788 std::string es; 02789 directory dir; 02790 directory::const_iterator cdi; 02791 std::string rpath; 02792 std::string ipath; 02793 02794 if (a_value.size() == 0) { 02795 TRY_nomem(es = "Invalid include path: \""); 02796 TRY_nomem(es += a_value); 02797 TRY_nomem(es += "\""); 02798 throw(ERROR(0,es)); 02799 } 02800 02801 if (a_value[0] != '/') { 02802 TRY_nomem(rpath = parse_dirname(*m_path)); 02803 } 02804 02805 TRY_nomem(ipath = rpath + a_value); 02806 02807 TRY_nomem(es = "No configuration file(s) found: \""); 02808 TRY_nomem(es += a_value); 02809 TRY_nomem(es += "\""); 02810 TRY_instead(dir.path(ipath),es); 02811 if (dir.size() == 0) 02812 throw(ERROR(0,es)); 02813 02814 for (cdi = dir.begin(); cdi != dir.end(); cdi++) { 02815 if (dir.size() > 1) { 02816 TRY_nomem(es = "For files found matching: \""); 02817 TRY_nomem(es += a_value); 02818 TRY_nomem(es += "\""); 02819 try { 02820 read_job(*cdi); 02821 } 02822 catch(error e) { 02823 e.push_back(ERROR_INSTANCE(es)); 02824 throw(e); 02825 } 02826 catch(...) { 02827 error e = err_unknown; 02828 02829 e.push_back(ERROR_INSTANCE(es)); 02830 throw(e); 02831 } 02832 } 02833 else 02834 read_job(*cdi); 02835 } 02836 } 02837 02838 /** Parse a "jobname" command */ 02839 void job_parser::parse_jobname(const std::string& a_value) 02840 { 02841 TRY_nomem((*m_job).jobname = a_value); 02842 } 02843 02844 /** Parse a "path" command */ 02845 void job_parser::parse_path(const std::string& a_value) 02846 { 02847 TRY_nomem((*m_job).paths.push_back(a_value)); 02848 } 02849 02850 /** Parse an "rsync-behavior" command */ 02851 void job_parser::parse_rsync_behavior(const std::string& a_value) 02852 { 02853 std::string es; 02854 estring str; 02855 02856 TRY_nomem(es = "Invalid rsync-behavior value: \""); 02857 TRY_nomem(es += a_value); 02858 TRY_nomem(es += "\""); 02859 TRY(str = estring(a_value).lower(),es); 02860 if (str == "clear") { 02861 (*m_job).rsync_behavior.clear(); 02862 } 02863 else if (str == "reset") { 02864 (*m_job).rsync_behavior.reset(); 02865 } 02866 else 02867 (*m_job).rsync_behavior.assign(a_value); 02868 } 02869 02870 /** Parse an "rsync-connection-type" command */ 02871 void job_parser::parse_rsync_connection_type(const std::string& a_value) 02872 { 02873 std::string es; 02874 estring str; 02875 02876 TRY_nomem(es = "Invalid rsync-connection-type value: \""); 02877 TRY_nomem(es += a_value); 02878 TRY_nomem(es += "\""); 02879 TRY(str = estring(a_value).lower(),es); 02880 if (str == "local") { 02881 (*m_job).rsync_connection = job::connection_local; 02882 } 02883 else if (str == "remote") { 02884 (*m_job).rsync_connection = job::connection_remote; 02885 } 02886 else if (str == "server") { 02887 (*m_job).rsync_connection = job::connection_server; 02888 } 02889 else { 02890 throw(ERROR(0,es)); 02891 } 02892 } 02893 02894 /** Parse an "rsync-hardlink" command */ 02895 void job_parser::parse_rsync_hardlink(const std::string& a_value) 02896 { 02897 std::string es; 02898 estring str; 02899 02900 TRY_nomem(es = "Invalid rsync-hardlink value: \""); 02901 TRY_nomem(es += a_value); 02902 TRY_nomem(es += "\""); 02903 TRY(str = estring(a_value).lower(),es); 02904 if ( 02905 (str == "y") 02906 || (str == "yes") 02907 || (str == "t") 02908 || (str == "true") 02909 || (str == "1") 02910 || (str == "on") 02911 ) { 02912 (*m_job).rsync_hardlink = true; 02913 } 02914 else if ( 02915 (str == "n") 02916 || (str == "no") 02917 || (str == "f") 02918 || (str == "false") 02919 || (str == "0") 02920 || (str == "off") 02921 ) { 02922 (*m_job).rsync_hardlink = false; 02923 } 02924 else { 02925 throw(ERROR(0,es)); 02926 } 02927 } 02928 02929 /** Parse an "rsync-multi-hardlink" command */ 02930 void job_parser::parse_rsync_multi_hardlink(const std::string& a_value) 02931 { 02932 std::string es; 02933 estring str; 02934 02935 TRY_nomem(es = "Invalid rsync-multi-hardlink value: \""); 02936 TRY_nomem(es += a_value); 02937 TRY_nomem(es += "\""); 02938 TRY(str = estring(a_value).lower(),es); 02939 if ( 02940 (str == "y") 02941 || (str == "yes") 02942 || (str == "t") 02943 || (str == "true") 02944 || (str == "1") 02945 || (str == "on") 02946 ) { 02947 (*m_job).rsync_multi_hardlink = true; 02948 (*m_job).rsync_hardlink = true; 02949 } 02950 else if ( 02951 (str == "n") 02952 || (str == "no") 02953 || (str == "f") 02954 || (str == "false") 02955 || (str == "0") 02956 || (str == "off") 02957 ) { 02958 (*m_job).rsync_multi_hardlink = false; 02959 } 02960 else { 02961 throw(ERROR(0,es)); 02962 } 02963 } 02964 02965 /** Parse an "rsync-multi-hardlink-max" command */ 02966 void job_parser::parse_rsync_multi_hardlink_max(const std::string& a_value) 02967 { 02968 std::string es; 02969 uint16 num; 02970 02971 TRY_nomem(es = "Invalid rsync-multi-hardlink-max value: \""); 02972 TRY_nomem(es += a_value); 02973 TRY_nomem(es += "\""); 02974 TRY_instead(num = estring(a_value),es); 02975 if (num == 0) 02976 throw(ERROR(0,es)); 02977 (*m_job).rsync_multi_hardlink_max = num; 02978 } 02979 02980 /** Parse an "rsync-options" command */ 02981 void job_parser::parse_rsync_options(const std::string& a_value) 02982 { 02983 if (*m_delimiter == "</default>") { 02984 TRY_nomem((*m_job).rsync_options = a_value); 02985 } 02986 else { 02987 if ((*m_job).rsync_options.size() != 0) { 02988 TRY_nomem((*m_job).rsync_options += " "); 02989 } 02990 TRY_nomem((*m_job).rsync_options += a_value); 02991 } 02992 } 02993 02994 /** Parse an rsync-options context */ 02995 void job_parser::parse_rsync_options_context(const std::string& a_value) 02996 { 02997 std::string es; 02998 std::string value; 02999 std::string rsync_options; 03000 char ch; 03001 03002 while ((*m_in)) { 03003 (*m_line)++; 03004 03005 value.erase(); 03006 03007 (*m_in).get(ch); 03008 while ((ch == ' ') || (ch == '\t')) 03009 (*m_in).get(ch); 03010 while (((*m_in)) && (ch != '\n')) { 03011 TRY_nomem(value += ch); 03012 (*m_in).get(ch); 03013 } 03014 03015 if ((!(*m_in)) && (value != "</rsync-options>")) { 03016 TRY_nomem(es = "Unexpected end of file, expected "); 03017 TRY_nomem(es += "\"</rsync-options>\" here"); 03018 throw(ERROR(errno,es)); 03019 } 03020 03021 if (value.size() == 0) 03022 continue; 03023 if (value[0] == '#') 03024 continue; 03025 03026 if (value == "</rsync-options>") { 03027 parse_rsync_options(rsync_options); 03028 return; 03029 } 03030 03031 if (rsync_options.size() != 0) { 03032 TRY_nomem(rsync_options += ' '); 03033 } 03034 TRY_nomem(rsync_options += value); 03035 } 03036 } 03037 03038 /** Parse a "remote-user" command */ 03039 void job_parser::parse_rsync_remote_user(const std::string& a_value) 03040 { 03041 TRY_nomem((*m_job).rsync_remote_user = a_value); 03042 } 03043 03044 /** Parse an "rsync-remote-path" command */ 03045 void job_parser::parse_rsync_remote_path(const std::string& a_value) 03046 { 03047 TRY_nomem((*m_job).rsync_remote_path = a_value); 03048 } 03049 03050 /** Parse an "rsync-remote-port" command */ 03051 void job_parser::parse_rsync_remote_port(const std::string& a_value) 03052 { 03053 std::string es; 03054 estring str; 03055 uint16 num; 03056 03057 TRY_nomem(es = "Invalid rsync-remote-port value: \""); 03058 TRY_nomem(es += a_value); 03059 TRY_nomem(es += "\""); 03060 TRY_nomem(str = a_value); 03061 TRY(num = str,es); 03062 (*m_job).rsync_remote_port = num; 03063 } 03064 03065 /** Parse an "rsync-remote-module" command */ 03066 void job_parser::parse_rsync_remote_module(const std::string& a_value) 03067 { 03068 TRY_nomem((*m_job).rsync_remote_module = a_value); 03069 } 03070 03071 /** Parse an "rsync-retry-count" command */ 03072 void job_parser::parse_rsync_retry_count(const std::string& a_value) 03073 { 03074 std::string es; 03075 estring str; 03076 uint16 num; 03077 03078 TRY_nomem(es = "Invalid rsync-retry-count value: \""); 03079 TRY_nomem(es += a_value); 03080 TRY_nomem(es += "\""); 03081 TRY_nomem(str = a_value); 03082 TRY(num = str,es); 03083 (*m_job).rsync_retry_count = num; 03084 } 03085 03086 /** Parse an "rsync-retry-delay" command */ 03087 void job_parser::parse_rsync_retry_delay(const std::string& a_value) 03088 { 03089 std::string es; 03090 estring str; 03091 uint16 num; 03092 03093 TRY_nomem(es = "Invalid rsync-retry-delay value: \""); 03094 TRY_nomem(es += a_value); 03095 TRY_nomem(es += "\""); 03096 TRY_nomem(str = a_value); 03097 TRY(num = str,es); 03098 (*m_job).rsync_retry_delay = num; 03099 } 03100 03101 /** Parse an "rsync-timeout" command */ 03102 void job_parser::parse_rsync_timeout(const std::string& a_value) 03103 { 03104 std::string es; 03105 estring str; 03106 uint16 num; 03107 03108 TRY_nomem(es = "Invalid rsync-timeout value: \""); 03109 TRY_nomem(es += a_value); 03110 TRY_nomem(es += "\""); 03111 TRY_nomem(str = a_value); 03112 TRY(num = str,es); 03113 (*m_job).rsync_timeout = num; 03114 } 03115 03116 //---------------------------------------------------------------------------- 03117 03118 /** The global configuration manager instance */ 03119 configuration_manager config;