#include "config.h" #include #include #include #include #include #include #include #include "asserts.h" #include "error.h" #include "estring.h" #include "fs.h" #include "tstamp.h" #include "rconfig.h" //---------------------------------------------------------------------------- /** Reset to a default value */ void archive_path_element::clear(void) { m_type = jobname; m_literal.erase(); } /** C'tor */ archive_path_element::archive_path_element() { clear(); } /** C'tor */ archive_path_element::archive_path_element( const archive_path_element& a_class) { clear(); assign(a_class); } /** C'tor */ archive_path_element::archive_path_element( const archive_path_element::element_type& a_enum ) { clear(); assign(a_enum); } /** C'tor */ archive_path_element::archive_path_element(const std::string& a_str) { clear(); assign(a_str); } /** Assign the value from another archive_path_element instance */ void archive_path_element::assign(const archive_path_element& a_class) { if (this == &a_class) return; if (a_class.type() == literal) assign(a_class.str()); else assign(a_class.type()); } /** Assign any type except literal */ void archive_path_element::assign( const archive_path_element::element_type& a_enum ) { m_type = a_enum; if (m_type != literal) { m_literal.erase(); } } /** Assign a literal */ void archive_path_element::assign(const std::string& a_str) { std::string es; std::string str; std::string::size_type idx; TRY_nomem(es = "Invalid archive-path keyword: \""); TRY_nomem(es += a_str); TRY_nomem(es += "\""); if (a_str.size() == 0) { throw(ERROR(0,es)); } if (a_str[0] == '"') { if (a_str.size() < 3) { throw(ERROR(0,es)); } if (a_str[a_str.size()-1] != '"') { throw(ERROR(0,es)); } TRY_nomem(str = a_str.substr(1,a_str.size()-2)); idx = str.find('"'); while (idx != std::string::npos) { if ((idx > 0) && (str[idx-1] != '\\')) throw(ERROR(0,es)); idx = str.find('"',idx+1); } idx = str.find("\\\""); while (idx != std::string::npos) { str.erase(idx,1); idx = str.find("\\\""); } TRY_nomem(m_literal = str); m_type = literal; } else { str = estring(a_str).lower(); if (str == "jobname") { assign(jobname); } else if (str == "groupname") { assign(groupname); } else if (str == "hostname") { assign(hostname); } else if (str == "pathname") { assign(pathname); } else if (str == "permutation") { assign(permutation); } else { throw(ERROR(0,es)); } } } /** Retrieve the current type */ const archive_path_element::element_type& archive_path_element::type(void) const { return(m_type); } /** Retrieve the current literal value */ const std::string& archive_path_element::value(void) const { return(m_literal); } /** Construct a string of the current value */ const std::string archive_path_element::str(void) const { std::string::size_type idx; std::string value; if (m_type == jobname) { TRY_nomem(value = "jobname"); } else if (m_type == groupname) { TRY_nomem(value = "groupname"); } else if (m_type == hostname) { TRY_nomem(value = "hostname"); } else if (m_type == pathname) { TRY_nomem(value = "pathname"); } else if (m_type == permutation) { TRY_nomem(value = "permutation"); } else { TRY_nomem(value = "\""); for (idx = 0; idx != m_literal.size(); idx++) { if (m_literal[idx] == '"') { TRY_nomem(value += '\\'); } TRY_nomem(value += m_literal[idx]); } TRY_nomem(value += '"'); } return(value); } /** Assignment */ archive_path_element& archive_path_element::operator=(const archive_path_element& a_class) { clear(); assign(a_class); return(*this); } /** Assignment */ archive_path_element& archive_path_element::operator=( const archive_path_element::element_type a_enum) { clear(); assign(a_enum); return(*this); } /** Assignment */ archive_path_element& archive_path_element::operator=(const std::string& a_str) { clear(); assign(a_str); return(*this); } //---------------------------------------------------------------------------- /** Reset to a default value */ void archive_path::reset(void) { clear(); push_back("hostname/pathname"); } /** C'tor */ archive_path::archive_path() { reset(); } /** C'tor */ archive_path::archive_path(const archive_path& a_class) { push_back(a_class); } /** C'tor */ archive_path::archive_path(const archive_path_element& a_class) { push_back(a_class); } /** C'tor */ archive_path::archive_path(const std::string& a_str) { push_back(a_str); } /** Add elements to the path list from another archive_path instance */ void archive_path::push_back(const archive_path& a_class) { const_iterator li; for (li = a_class.begin(); li != a_class.end(); li++) { TRY_nomem(push_back(*li)); } } /** Add an element to the path list from an archive_path_element instance */ void archive_path::push_back(const archive_path_element& a_class) { TRY_nomem(type::push_back(a_class)); } /** Parse a string into an archive-path list */ void archive_path::push_back(const std::string& a_str) { std::string es; std::string keyword; std::string str; std::string::size_type idx; archive_path_element ape; if (a_str.size() == 0) return; TRY_nomem(str = a_str); if (str[0] == '/') str.erase(0,1); if (str[str.size()-1] == '/') str.erase(str.size()-1,1); while (str.size() > 0) { idx = str.find('/'); if (idx == std::string::npos) { TRY_nomem(keyword = str); str.erase(); } else { TRY_nomem(keyword = str.substr(0,idx)); str.erase(0,idx+1); } ape.clear(); TRY_nomem(es = "At: \""); TRY_nomem(es += keyword); TRY_nomem(es += "\""); TRY(ape.assign(keyword),es); TRY_nomem(push_back(ape)); } } /** Reconstruct a string from an archive-path list */ const std::string archive_path::str(void) const { std::string str; const_iterator li; for (li = begin(); li != end(); li++) { if (li != begin()) { TRY_nomem(str += '/'); } TRY_nomem(str += (*li).str()); } return(str); } /** Assign an archive-path list from another archive_path instance */ void archive_path::assign(const archive_path& a_class) { if (this != &a_class) { clear(); push_back(a_class); } } /** Assign an archive-path list from a single archive_path_element instance */ void archive_path::assign(const archive_path_element& a_class) { clear(); push_back(a_class); } /** Assign an archive-path list from a string (parse the string) */ void archive_path::assign(const std::string& a_str) { clear(); push_back(a_str); } /** Assignment */ archive_path& archive_path::operator=(const archive_path& a_class) { assign(a_class); return(*this); } /** Assignment */ archive_path& archive_path::operator=(const archive_path_element& a_class) { assign(a_class); return(*this); } /** Assignment */ archive_path& archive_path::operator=(const std::string& a_str) { assign(a_str); return(*this); } //---------------------------------------------------------------------------- /** Default behavior */ const uint16 rsync_behavior::default_behavior; /** Clear all values and set the default to "retry" */ void rsync_behavior::clear(void) { m_map.clear(); m_default = retry; } /** Clear all values and set some sane default actions */ void rsync_behavior::reset(void) { clear(); m_default = retry; TRY_nomem( m_map[0] = ok; m_map[1] = fail; m_map[2] = fail; m_map[4] = fail; m_map[23] = retry_without_hardlinks; m_map[124] = fail; m_map[125] = fail; m_map[126] = fail; m_map[127] = fail; ); } /** C'tor */ rsync_behavior::rsync_behavior() { reset(); } /** C'tor */ rsync_behavior::rsync_behavior(const rsync_behavior& a_class) { assign(a_class); } /** Assign an action to be taken for a specific exit code */ void rsync_behavior::assign(const uint16 a_code, const value_type a_action) { if (a_code == default_behavior) m_default = a_action; else { TRY_nomem(m_map[a_code] = a_action); } } /** Assign actions to be taken from another rsync_behavior instance */ void rsync_behavior::assign(const rsync_behavior& a_class) { if (this == &a_class) return; clear(); m_default = a_class.default_value(); TRY_nomem(m_map = a_class.map_value()); } /** Assign actions to be taken from a string (parse a string in the form of exit-code '=' action) */ void rsync_behavior::assign(const std::string& a_str) { std::string es; std::string code_str; std::string action_str; uint16 code; rsync_behavior::behavior_type action; std::string::size_type idx; TRY_nomem(es = "Invalid rsync-behavior value: \""); TRY_nomem(es += a_str); TRY_nomem(es += "\""); TRY_nomem(action_str = a_str); idx = 0; while (isdigit(action_str[idx]) || (action_str[idx] == '*')) ++idx; TRY_nomem(code_str = action_str.substr(0,idx)); action_str.erase(0,idx); if (code_str.size() == 0) throw(ERROR(0,es)); idx = 0; while ((action_str[idx] == ' ') || (action_str[idx] == '\t')) idx++; action_str.erase(0,idx); if (action_str[0] != '=') throw(ERROR(0,es)); action_str.erase(0,1); idx = 0; while ((action_str[idx] == ' ') || (action_str[idx] == '\t')) idx++; action_str.erase(0,idx); if (code_str == "*") code = rsync_behavior::default_behavior; else code = estring(code_str); if (estring(action_str).lower() == "ok") action = rsync_behavior::ok; else if (estring(action_str).lower() == "fail") action = rsync_behavior::fail; else if (estring(action_str).lower() == "retry") action = rsync_behavior::retry; else if (estring(action_str).lower() == "retry-without-hardlinks") action = rsync_behavior::retry_without_hardlinks; else if (estring(action_str).lower() == "ok") action = rsync_behavior::quit; else throw(ERROR(0,es)); assign(code, action); } /** Return the action to be taken for a given exit code */ const rsync_behavior::value_type rsync_behavior::operator[](const uint16 a_code) const { if (a_code == default_behavior) { return(m_default); } if (m_map.find(a_code) != m_map.end()) { return(m_map.find(a_code)->second); } return(m_default); } /** Return the action to be taken for a given exit code */ rsync_behavior::value_type& rsync_behavior::operator[](const uint16 a_code) { if (a_code == default_behavior) { return(m_default); } return(m_map[a_code]); } /** Assignment */ rsync_behavior& rsync_behavior::operator=(const rsync_behavior& a_class) { assign(a_class); return(*this); } /** Assignment */ rsync_behavior& rsync_behavior::operator=(const rsync_behavior::behavior_type a_enum) { assign(default_behavior, a_enum); return(*this); } /** Assignment */ rsync_behavior& rsync_behavior::operator=(const std::string& a_str) { assign(a_str); return(*this); } /** Return the default action */ const rsync_behavior::behavior_type rsync_behavior::default_value(void) const { return(m_default); } /** Return an std::map of exit codes to actions */ const rsync_behavior::map_type& rsync_behavior::map_value(void) const { return(m_map); } //---------------------------------------------------------------------------- /** C'tor */ job::job() { clear(); } /** C'tor */ job::job(const job& a_job) { clear(); assign(a_job); } /** Clear values */ void job::clear(void) { default_config_path.erase(); default_config_line = 0; config_path.erase(); config_line = 0; archive_path = "hostname/pathname"; excludes.clear(); includes.clear(); groupname.erase(); hostname.erase(); jobname.erase(); paths.clear(); rsync_behavior.reset(); rsync_connection = connection_remote; rsync_hardlink = true; rsync_options.erase(); rsync_remote_user.erase(); rsync_remote_path.erase(); rsync_remote_port = 0; rsync_remote_module.erase(); rsync_retry_count = 3; rsync_timeout = 60 * 60 * 4; // 4 hours; } /** Assign values from another job instance */ void job::assign(const job& a_job) { clear(); default_config_path = a_job.default_config_path; default_config_line = a_job.default_config_line; config_path = a_job.config_path; config_line = a_job.config_line; archive_path = a_job.archive_path; excludes = a_job.excludes; includes = a_job.includes; groupname = a_job.groupname; hostname = a_job.hostname; jobname = a_job.jobname; paths = a_job.paths; rsync_behavior.clear(); rsync_behavior = a_job.rsync_behavior; rsync_connection = a_job.rsync_connection; rsync_hardlink = a_job.rsync_hardlink; rsync_options = a_job.rsync_options; rsync_remote_user = a_job.rsync_remote_user; rsync_remote_path = a_job.rsync_remote_path; rsync_remote_port = a_job.rsync_remote_port; rsync_remote_module = a_job.rsync_remote_module; rsync_retry_count = a_job.rsync_retry_count; rsync_timeout = a_job.rsync_timeout; } /** Assignment */ job& job::operator=(const job& a_job) { assign(a_job); return(*this); } /** Generate the archive-path subdirectory for this job */ const std::string job::generate_archive_path(const std::string& a_path) const { std::string es; std::string path; archive_path::const_iterator capi; for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) { if (capi->type() == archive_path_element::jobname) { if (jobname.size() == 0) { TRY_nomem(es = "archive-path references jobname, "); TRY_nomem(es += "but jobname is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += jobname); } else if (capi->type() == archive_path_element::groupname) { if (groupname.size() == 0) { TRY_nomem(es = "archive-path references groupname, "); TRY_nomem(es += "but groupname is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += groupname); } else if (capi->type() == archive_path_element::hostname) { if (hostname.size() == 0) { TRY_nomem(es = "archive-path references hostname, "); TRY_nomem(es += "but hostname is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += hostname); } else if (capi->type() == archive_path_element::pathname) { if (a_path.size() == 0) { TRY_nomem(es = "archive-path references path name, "); TRY_nomem(es += "but path name is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += a_path); } else if (capi->type() == archive_path_element::permutation) { if (a_path.size() == 0) { TRY_nomem(es = "archive-path references path name, "); TRY_nomem(es += "but path name is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += permute_path(a_path)); } else if (capi->type() == archive_path_element::literal) { if (capi->value().size() == 0) { TRY_nomem(es = "archive-path references literal string, "); TRY_nomem(es += "but literal string value is empty"); throw(INTERNAL_ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += capi->value()); } else throw(INTERNAL_ERROR(0,"Unknown archive path element type")); } // If path does not end with a '/', strip off characters until it does. while ((path.size() > 0) && (path[path.size()-1] != '/')) path.erase(path.size()-1); path = reform_path(path); return(path); } /** Generate the source path to be passed to rsync on the command line */ const std::string job::generate_source_path(const std::string& a_path) const { estring path; if (rsync_connection == connection_server) { path += "rsync://"; if (rsync_remote_user.size() != 0) { path += rsync_remote_user; path += "@"; } TRY_nomem(path += hostname); if (rsync_remote_port != 0) { path += ":"; path += estring(rsync_remote_port); } if (rsync_remote_module.size() != 0) { path += "/"; path += rsync_remote_module; if ((a_path.size() > 0) && (a_path[0] != '/')) path += "/"; } } else if (rsync_connection == connection_remote) { if (rsync_remote_user.size() != 0) { path += rsync_remote_user; path += "@"; } path += hostname; path += ":"; } path += a_path; return(path); } /** Find the common pathname among all job paths (may be an empty string) */ const std::string job::common_pathname(void) const { std::string common_path; paths_type::size_type pidx; std::string::size_type sidx; std::string::size_type max_sidx; bool same; TRY_nomem(common_path = ""); sidx = 0; max_sidx = paths[0].size(); for (pidx = 0; pidx < paths.size(); pidx++) { if (max_sidx > paths[pidx].size()) { max_sidx = paths[pidx].size(); } } same = true; for (sidx = 0; sidx < max_sidx; sidx++) { for (pidx = 0; (same && (pidx < paths.size())); pidx++) { if (pidx == 0) continue; if (paths[pidx-1][sidx] != paths[pidx][sidx]) same = false; } if (same) TRY_nomem(common_path += paths[0][sidx]); } if (common_path[0] == '/') common_path.erase(0,1); if ((common_path.size() > 0) && (common_path[common_path.size()-1] == '/')) common_path.erase(common_path.size()-1); return(common_path); } /** Generate a unique ID string for this job */ const std::string job::generate_job_id(void) const { std::string es; std::string path; archive_path::const_iterator capi; for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) { if (capi->type() == archive_path_element::jobname) { if (jobname.size() == 0) { TRY_nomem(es = "archive-path references jobname, "); TRY_nomem(es += "but jobname is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += jobname); } else if (capi->type() == archive_path_element::groupname) { if (groupname.size() == 0) { TRY_nomem(es = "archive-path references groupname, "); TRY_nomem(es += "but groupname is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += groupname); } else if (capi->type() == archive_path_element::hostname) { if (hostname.size() == 0) { TRY_nomem(es = "archive-path references hostname, "); TRY_nomem(es += "but hostname is empty"); throw(ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += hostname); } else if (capi->type() == archive_path_element::pathname) { if (path.size() == 0) { TRY_nomem(path += common_pathname()); } } else if (capi->type() == archive_path_element::permutation) { if (path.size() == 0) { TRY_nomem(path += permute_path(common_pathname())); } } else if (capi->type() == archive_path_element::literal) { if (capi->value().size() == 0) { TRY_nomem(es = "archive-path references literal string, "); TRY_nomem(es += "but literal string value is empty"); throw(INTERNAL_ERROR(0,es)); } if (path.size() != 0) { TRY_nomem(path += '/'); } TRY_nomem(path += capi->value()); } } path = reform_path(path); if (path.size() == 0) { TRY_nomem(path = jobname); } return(path); } /** Perform sanity checks for the configuration settings of this job */ void job::check(void) { std::string es; std::string this_path; std::string that_path; paths_type::const_iterator cpi; configuration_manager::jobs_type::const_iterator cji; paths_type::const_iterator cjapi; if ( ( (rsync_connection == connection_remote) || (rsync_connection == connection_server) ) && (hostname.size() == 0) ) { TRY_nomem(es = "rsync-connection-type references hostname, "); TRY_nomem(es += "but hostname is empty"); throw(ERROR(0,es)); } if ((rsync_remote_module.size() != 0) && (rsync_connection != connection_server)) { TRY_nomem(es = "rsync-remote-module specifies a module, but "); TRY_nomem(es = "rsync-connection-type is not server"); throw(ERROR(0,es)); } if (paths.size() == 0) { throw(ERROR(0,"No paths defined for this job")); } for (cpi = paths.begin() ; cpi != paths.end(); cpi++) { TRY_nomem(this_path = generate_archive_path(*cpi)); for ( cji = config.jobs().begin(); cji != config.jobs().end(); cji++ ) { for ( cjapi = cji->paths.begin(); cjapi != cji->paths.end(); cjapi++ ) { TRY_nomem(that_path = cji->generate_archive_path(*cjapi)); if (this_path == that_path) { error e(0); TRY_nomem(es = "Duplicate archive-path values detected"); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "Archive path: \""); TRY_nomem(es += this_path); TRY_nomem(es += "\""); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "Previously defined at "); TRY_nomem(es += cji->config_path); TRY_nomem(es += "["); TRY_nomem(es += estring(cji->config_line)); TRY_nomem(es += "]"); e.push_back(ERROR_INSTANCE(es)); throw(e); } if ( (this_path.size() < that_path.size()) && (this_path == that_path.substr(0,this_path.size())) && ( (that_path[this_path.size()] == '/') || (this_path.size() == 0) ) ) { error e(0); TRY_nomem(es = "Overlapping archive-path values detected"); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "Defined archive-path: \""); TRY_nomem(es += this_path); TRY_nomem(es += "\""); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "Is in a parent directory of another job's previously defined archive-path"); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "At "); TRY_nomem(es += cji->config_path); TRY_nomem(es += "["); TRY_nomem(es += estring(cji->config_line)); TRY_nomem(es += "]"); e.push_back(ERROR_INSTANCE(es)); throw(e); } if ( (this_path.size() > that_path.size()) && (this_path.substr(0,that_path.size()) == that_path) && ( (this_path[that_path.size()] == '/') || (that_path.size() == 0) ) ) { error e(0); TRY_nomem(es = "Overlapping archive-path values detected"); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "Defined archive-path: \""); TRY_nomem(es += this_path); TRY_nomem(es += "\""); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "Is in a subdirectory of another job's previously defined archive-path"); e.push_back(ERROR_INSTANCE(es)); TRY_nomem(es = "At "); TRY_nomem(es += cji->config_path); TRY_nomem(es += "["); TRY_nomem(es += estring(cji->config_line)); TRY_nomem(es += "]"); e.push_back(ERROR_INSTANCE(es)); throw(e); } } } } } //---------------------------------------------------------------------------- /** Reset configuration to default settings */ void configuration_manager::clear(void) { m_initialized = false; m_configs_read = 0; m_default = true; TRY_nomem(m_default_file = CONFIGFILE); m_action = action_help; m_timestamp.set(); m_cfgfiles.clear(); m_link_catalog_dir.erase(); TRY_nomem(m_log_dir = LOGDIR); m_delete_old_log_files = false; m_delete_old_report_files = false; m_rsync_local_path.erase(); if (strlen(LOCAL_RSYNC) > 0) { TRY_nomem(m_rsync_local_path = LOCAL_RSYNC); } m_rsync_parallel = 1; m_io_poll_interval = 1; m_vaults.clear(); m_vault_overflow_behavior = overflow_quit; m_vault_overflow_blocks = 10; m_vault_overflow_inodes = 10; m_vault_selection_behavior = selection_round_robin; m_vault_locking = true; m_default_job.clear(); m_logging_level = logging_child; m_error_logging_level = logging_rsync; m_jobs.clear(); } /** C'tor */ configuration_manager::configuration_manager() { if (this != &config) throw( INTERNAL_ERROR(0,"Attempt to allocate multiple configuration managers") ); clear(); } /** Initialize the configuration manager from rvm's command line options */ void configuration_manager::init(int argc, char *argv[]) { int c; estring opt; estring arg; std::string es; std::string tes; cfgfiles_type::const_iterator cfi; bool use_custom_timestamp = false; class timestamp custom_timestamp; for (c = 1; c < argc; c++) { TRY_nomem(opt = argv[c]); if (c+1 == argc) { TRY_nomem(arg = ""); } else { TRY_nomem(arg = argv[c+1]); } if (opt == "--archive") { m_action = action_archive; } else if (opt == "--relink") { m_action = action_relink; } else if (opt == "--help") { m_action = action_help; } else if (opt == "--version") { m_action = action_version; } else if (opt == "--check-config") { m_action = action_check_config; } else if (opt == "--no-default-config") { m_default = false; } else if (opt == "--config") { directory dir; directory::const_iterator cdi; TRY_nomem(es = "Error finding configuration file(s) "); TRY_nomem(es += "matching command line argument ["); TRY_nomem(es += estring(c+1)); TRY_nomem(es += "]: \""); TRY_nomem(es += arg); TRY_nomem(es += "\""); try { dir.path(arg); } catch(error e) { e.push_back(ERROR_INSTANCE(es)); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(es)); throw(e); } if (dir.size() == 0) { TRY_nomem(es = "No configuration file(s) found matching "); TRY_nomem(es += "command line argument ["); TRY_nomem(es += estring(c+1)); TRY_nomem(es += "]: \""); TRY_nomem(es += arg); TRY_nomem(es += "\""); throw(ERROR(0,es)); } for (cdi = dir.begin(); cdi != dir.end(); cdi++) { TRY_nomem(m_cfgfiles.push_back(cfgfile_element(config_file, arg))); } c++; } else if (opt == "--job") { directory dir; directory::const_iterator cdi; TRY_nomem(es = "Error finding job file(s) "); TRY_nomem(es += "matching command line argument ["); TRY_nomem(es += estring(c+1)); TRY_nomem(es += "]: \""); TRY_nomem(es += arg); TRY_nomem(es += "\""); try { dir.path(arg); } catch(error e) { e.push_back(ERROR_INSTANCE(es)); throw(e); } catch(...) { error e = err_unknown; throw(e); } if (dir.size() == 0) { TRY_nomem(es = "No job file(s) found matching "); TRY_nomem(es += "command line argument ["); TRY_nomem(es += estring(c+1)); TRY_nomem(es += "]: \""); TRY_nomem(es += arg); TRY_nomem(es += "\""); throw(ERROR(0,es)); } for (cdi = dir.begin(); cdi != dir.end(); cdi++) { TRY_nomem(m_cfgfiles.push_back(cfgfile_element(job_file, arg))); } c++; } else if (opt == "--timestamp") { TRY_nomem(es = "From command line argument "); TRY_nomem(es += estring(c+1)); TRY(custom_timestamp.assign(arg),es); use_custom_timestamp = true; c++; TRY_nomem(tes = es); } else { TRY_nomem(es = "Unknown command line option: \""); TRY_nomem(es += opt); TRY_nomem(es += "\""); throw(ERROR(0,es)); } } m_initialized = true; if ((m_action == action_help) || (m_action == action_version)) return; if (use_default()) read_config(m_default_file); for (cfi = m_cfgfiles.begin(); cfi != m_cfgfiles.end(); cfi++) { if (cfi->first == config_file) { read_config(cfi->second); } else { read_job(cfi->second); } } if (m_configs_read == 0) { throw(ERROR(0,"No configuration file(s) read")); } if (use_custom_timestamp) { TRY(m_timestamp = custom_timestamp,tes); } check(); } /** Perform sanity checks on configuration settings */ void configuration_manager::check(void) const { std::string es; subdirectory subdir; filestatus fstat; if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); // log-dir -- If no log-dir is set then we use a default. What if that // default doesn't exist? if (m_log_dir.size() == 0) { throw(ERROR(0,"log-dir not set")); } TRY_nomem(es = "Invalid log-dir value: \""); TRY_nomem(es += m_log_dir); TRY_nomem(es += "\""); TRY_instead(subdir.path(m_log_dir,"*"),es); // rsync-local-path -- If (a) rsync-local-path is not set from config, and // (b) a default was determined at compile time, then (c) does the default // exist? TRY_nomem(es = "Invalid rsync-local-path value: \""); TRY_nomem(es += m_rsync_local_path); TRY_nomem(es += "\""); TRY(fstat.path(m_rsync_local_path),es); // vault -- Are there any? if (m_vaults.size() == 0) { throw(ERROR(0,"No vaults defined")); } // Do all jobs generate a valid job ID? // Note: This is purely cosmetic, generated job ID strings are only used in // status reports and report logs. if (jobs().size() > 0) { jobs_type::const_iterator cji; for (cji = jobs().begin(); cji != jobs().end(); cji++) { if (cji->generate_job_id().size() == 0) { error e(0); TRY_nomem(es = "Empty ID generated by job at "); TRY_nomem(es += cji->config_path); TRY_nomem(es += "["); TRY_nomem(es += estring(cji->config_line)); TRY_nomem(es += "]"); e.push_back(es); TRY_nomem(es = "Use 'jobname' to assign a descriptive name to this job"); e.push_back(es); throw(e); } } } } /** Return the initialized state of the configuration manager */ const bool configuration_manager::initialized(void) const { return(m_initialized); } /** Return the action rvm is to take */ const configuration_manager::action_type configuration_manager::action(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_action); } /** Return whether or not rvm is to try to read it's default configuration file */ const bool configuration_manager::use_default(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_default); } /** Set the default configuration filename */ void configuration_manager::default_file(const std::string& a_path) { TRY_nomem(m_default_file = a_path); } /** Return the default configuration filename */ const std::string& configuration_manager::default_file(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_default_file); } /** Return the default log-dir */ void configuration_manager::default_logdir(const std::string& a_path) { TRY_nomem(m_log_dir = a_path); } /** Return the timestamp of this instance of rvm */ const class timestamp& configuration_manager::timestamp(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_timestamp); } /** Return the link-catalog-dir path */ const std::string& configuration_manager::link_catalog_dir(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_link_catalog_dir); } /** Return the log-dir path */ const std::string& configuration_manager::log_dir(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_log_dir); } /** Return the value of delete-old-log-files */ const bool configuration_manager::delete_old_log_files(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_delete_old_log_files); } /** Return the value of delete-old-report-files */ const bool configuration_manager::delete_old_report_files(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_delete_old_report_files); } /** Return the rsync-local-path */ const std::string& configuration_manager::rsync_local_path(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_rsync_local_path); } /** Return the rsync-parallel */ const uint16& configuration_manager::rsync_parallel(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_rsync_parallel); } /** Return the number of seconds to sleep between polling for I/O */ const uint16& configuration_manager::io_poll_interval(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_io_poll_interval); } /** Return the timestamp-resolution */ const timestamp::resolution_type configuration_manager::timestamp_resolution(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_timestamp.resolution()); } /** Return the vaults */ const configuration_manager::vaults_type& configuration_manager::vaults(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_vaults); } /** Return the vault-overflow-behavior */ const configuration_manager::overflow_type& configuration_manager::vault_overflow_behavior(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_vault_overflow_behavior); } /** Return the vault-overflow-blocks */ const uint16& configuration_manager::vault_overflow_blocks(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_vault_overflow_blocks); } /** Return the vault-overflow-inodes */ const uint16& configuration_manager::vault_overflow_inodes(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_vault_overflow_inodes); } /** Return the vault-selection-behavior */ const configuration_manager::selection_type& configuration_manager::vault_selection_behavior(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_vault_selection_behavior); } /** Return the vault-locking selection */ const bool configuration_manager::vault_locking(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_vault_locking); } /** Return the default job configuration */ const job& configuration_manager::default_job(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_default_job); } /** Return a list of jobs */ const configuration_manager::jobs_type& configuration_manager::jobs(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_jobs); } /** Return the logging-level */ const configuration_manager::logging_type& configuration_manager::logging_level(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_logging_level); } /** Return the error-logging-level */ const configuration_manager::logging_type& configuration_manager::error_logging_level(void) const { if (!initialized()) throw(INTERNAL_ERROR(0,"Configuration manager is not initialized")); return(m_error_logging_level); } /** Read a configuration file */ void configuration_manager::read_config(const std::string& a_path) { std::string es; std::ifstream in; uint16 line = 0; in.open(a_path.c_str()); if (!in.is_open()) { TRY_nomem(es = "Could not open configuration file: \""); TRY_nomem(es += a_path); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } global_parser(a_path, line, in); in.close(); m_configs_read++; } /** Read a job configuration file */ void configuration_manager::read_job(const std::string& a_path) { std::string es; std::ifstream in; job njob; uint16 line = 0; in.open(a_path.c_str()); if (!in.is_open()) { TRY_nomem(es = "Could not open job file: \""); TRY_nomem(es += a_path); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } njob = m_default_job; job_parser(&njob, a_path, line, in, "", false); njob.check(); in.close(); } //---------------------------------------------------------------------------- /** Parse a keyword/value pair read from a configuration file */ void parse_line( std::istream& a_in, std::string& a_keyword, std::string& a_value ) { std::string es; char ch; a_keyword.erase(); a_value.erase(); while (true) { a_in.get(ch); // Skip whitespace before keyword. while ((a_in) && ((ch == ' ') || (ch == '\t'))) { a_in.get(ch); } // Read keyword while ((a_in) && ((ch != ' ') && (ch != '\t') && (ch != '\n'))) { TRY_nomem(a_keyword += ch); a_in.get(ch); } if (!a_in) return; // If keyword is empty then this is a blank line, skip it. if (a_keyword.size() == 0) return; // If keyword starts with a '#' then this is a comment line, skip it. if (a_keyword[0] == '#') { // Discard the rest of the line. while ((a_in) && (ch != '\n')) { a_in.get(ch); } return; } a_keyword = estring(a_keyword).lower(); // If there is not value, return. if (ch == '\n') return; // Skip whitespace between keyword and value. a_in.get(ch); while ((a_in) && ((ch == ' ') || (ch == '\t'))) { a_in.get(ch); } if (!a_in) return; a_in.putback(ch); if (!a_in) return; // Read value a_in.get(ch); while ((a_in) && (ch != '\n')) { TRY_nomem(a_value += ch); a_in.get(ch); } return; } } /** Given a path, strip off the basename -- like the dirname UNIX command */ const std::string parse_dirname(const std::string& a_path) { std::string rpath; TRY_nomem(rpath = a_path); if (rpath.find_last_of('/') != std::string::npos) rpath.erase(rpath.find_last_of('/')); if (rpath.size() == 0) { TRY_nomem(rpath = "./"); return(rpath); } if (rpath[rpath.size()-1] != '/') { TRY_nomem(rpath += '/'); } return(rpath); } /** Given a path, strip off the directory -- like the basename UNIX commane */ const std::string parse_basename(const std::string& a_path) { std::string es; std::string rpath; TRY_nomem(rpath = a_path); if (rpath.find_last_of('/') != std::string::npos) rpath.erase(0,rpath.find_last_of('/')); return(rpath); } //---------------------------------------------------------------------------- /** C'tor */ global_parser::global_parser( const std::string& a_path, uint16& a_line, std::istream& a_in ) { m_in = &a_in; m_path = &a_path; m_line = &a_line; parse(); } /** Generate a string showing the current location within a configuration file * of the parser */ const std::string global_parser::location(void) { std::string es; TRY_nomem(es = "At "); TRY_nomem(es += (*m_path)); TRY_nomem(es += "["); TRY_nomem(es += estring((*m_line))); TRY_nomem(es += "]"); return(es); } /** Read a configuration file, used by the "include" command */ void global_parser::read_config(const std::string& a_path) { std::string es; std::ifstream in; uint16 line = 0; in.open(a_path.c_str()); if (!in.is_open()) { TRY_nomem(es = "Could not open configuraiton file: \""); TRY_nomem(es += a_path); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } global_parser(a_path, line, in); in.close(); config.m_configs_read++; } /** Read a job configuration file, used by the "include-job" command */ void global_parser::read_job(const std::string& a_path, job& a_job) { std::string es; std::ifstream in; uint16 line = 0; in.open(a_path.c_str()); if (!in.is_open()) { TRY_nomem(es = "Could not open job file: \""); TRY_nomem(es += a_path); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } job_parser(&a_job, a_path, line, in, "", false); TRY_nomem(a_job.config_path = *m_path); TRY_nomem(a_job.config_line = *m_line); a_job.check(); in.close(); } /** Global context parser */ void global_parser::parse(void) { std::string es; std::string keyword; std::string value; while ((*m_in)) { (*m_line)++; try { parse_line((*m_in), keyword, value); } catch(error e) { e.push_back(ERROR_INSTANCE(location())); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(location())); throw(e); } if (keyword.size() == 0) continue; if (keyword[0] == '#') continue; try { if (keyword == "") { parse_default(value); } else if (keyword == "include") { parse_include(value); } else if (keyword == "include-job") { parse_include_job(value); } else if (keyword == "") { parse_job(value); } else if (keyword == "link-catalog-dir") { parse_link_catalog_dir(value); } else if (keyword == "log-dir") { parse_log_dir(value); } else if (keyword == "delete-old-log-files") { parse_delete_old_log_files(value); } else if (keyword == "delete-old-report-files") { parse_delete_old_report_files(value); } else if (keyword == "logging-level") { parse_logging_level(value); } else if (keyword == "error-logging-level") { parse_error_logging_level(value); } else if (keyword == "rsync-local-path") { parse_rsync_local_path(value); } else if (keyword == "rsync-parallel") { parse_rsync_parallel(value); } else if (keyword == "io-poll-interval") { parse_io_poll_interval(value); } else if (keyword == "timestamp-resolution") { parse_timestamp_resolution(value); } else if (keyword == "vault") { try { parse_vault(value); } catch(error e) { std::cerr << e; } catch(...) { throw(err_unknown); } } else if (keyword == "vault-overflow-behavior") { parse_vault_overflow_behavior(value); } else if (keyword == "vault-overflow-blocks") { parse_vault_overflow_blocks(value); } else if (keyword == "vault-overflow-inodes") { parse_vault_overflow_inodes(value); } else if (keyword == "vault-selection-behavior") { parse_vault_selection_behavior(value); } else if (keyword == "vault-locking") { parse_vault_locking(value); } else { error e(0); TRY_nomem(es = "Unknown command in global context: \""); TRY_nomem(es += keyword); TRY_nomem(es += "\""); throw(ERROR(0,es)); } } catch(error e) { e.push_back(ERROR_INSTANCE(location())); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(location())); throw(e); } } } /** Parse a default job context */ void global_parser::parse_default(const std::string& a_value) { std::string delimiter; std::string default_config_path; uint16 default_config_line; job* jobPtr = &config.m_default_job; config.m_default_job.clear(); TRY_nomem(default_config_path = *m_path); TRY_nomem(default_config_line = *m_line); TRY_nomem(delimiter = ""); job_parser(jobPtr, *m_path, *m_line, *m_in, delimiter, true); TRY_nomem(jobPtr->default_config_path = default_config_path); jobPtr->default_config_line = default_config_line; } /** Parse an "include" command */ void global_parser::parse_include(const std::string& a_value) { std::string es; directory dir; directory::const_iterator cdi; std::string rpath; std::string ipath; if (a_value.size() == 0) { TRY_nomem(es = "Invalid include path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); throw(ERROR(0,es)); } if (a_value[0] != '/') { TRY_nomem(rpath = parse_dirname(*m_path)); } TRY_nomem(ipath = rpath + a_value); TRY_nomem(es = "No configuration file(s) found: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_instead(dir.path(ipath),es); if (dir.size() == 0) throw(ERROR(0,es)); for (cdi = dir.begin(); cdi != dir.end(); cdi++) { if (dir.size() > 1) { TRY_nomem(es = "For files found matching: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); try { read_config(*cdi); } catch(error e) { e.push_back(ERROR_INSTANCE(es)); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(es)); throw(e); } } else read_config(*cdi); } } /** Parse an "include-job" command */ void global_parser::parse_include_job(const std::string& a_value) { std::string es; directory dir; directory::const_iterator cdi; std::string rpath; std::string ipath; if (a_value.size() == 0) { TRY_nomem(es = "Invalid include-job path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); throw(ERROR(0,es)); } if (a_value[0] != '/') { TRY_nomem(rpath = parse_dirname(*m_path)); } TRY_nomem(ipath = rpath + a_value); TRY_nomem(es = "No job file(s) found: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(dir.path(ipath),es); if (dir.size() == 0) throw(ERROR(0,es)); for (cdi = dir.begin(); cdi != dir.end(); cdi++) { job new_job; new_job = config.m_default_job; if (dir.size() > 1) { TRY_nomem(es = "For files found matching: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); try { read_job(*cdi, new_job); } catch(error e) { e.push_back(ERROR_INSTANCE(es)); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(es)); throw(e); } } else read_job(*cdi, new_job); TRY_nomem(config.m_jobs.push_back(new_job)); } } /** Parse a job context */ void global_parser::parse_job(const std::string& a_value) { std::string delimiter; std::string config_path; uint16 config_line; job new_job; TRY_nomem(config_path = *m_path); config_line = *m_line; new_job = config.m_default_job; TRY_nomem(new_job.config_path = config_path); new_job.config_line = config_line; TRY_nomem(delimiter = ""); job_parser(&new_job, *m_path, *m_line, *m_in, delimiter, false); new_job.check(); TRY_nomem(config.m_jobs.push_back(new_job)); } /** Parse a "link-catalog-dir" command */ void global_parser::parse_link_catalog_dir(const std::string& a_value) { std::string es; subdirectory subdir; TRY_nomem(es = "Invalid link-catalog-dir path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_instead(subdir.path(a_value,"*"),es); TRY_nomem(config.m_link_catalog_dir = a_value); } /** Parse a "log-dir" command */ void global_parser::parse_log_dir(const std::string& a_value) { std::string es; subdirectory subdir; TRY_nomem(es = "Invalid log-dir path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_instead(subdir.path(a_value,"*"),es); TRY_nomem(config.m_log_dir = a_value); } /** Parse a "delete-old-log-files" command */ void global_parser::parse_delete_old_log_files(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid delete-old-log-files value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if ( (str == "y") || (str == "yes") || (str == "t") || (str == "true") || (str == "1") || (str == "on") ) { config.m_delete_old_log_files = true; } else if ( (str == "n") || (str == "no") || (str == "f") || (str == "false") || (str == "0") || (str == "off") ) { config.m_delete_old_log_files = false; } else { throw(ERROR(0,es)); } } /** Parse a "delete-old-report-files" command */ void global_parser::parse_delete_old_report_files(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid delete-old-report-files value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if ( (str == "y") || (str == "yes") || (str == "t") || (str == "true") || (str == "1") || (str == "on") ) { config.m_delete_old_report_files = true; } else if ( (str == "n") || (str == "no") || (str == "f") || (str == "false") || (str == "0") || (str == "off") ) { config.m_delete_old_report_files = false; } else { throw(ERROR(0,es)); } } /** Parse a "loging-level" command */ void global_parser::parse_logging_level(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid logging-level value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_nomem(str = estring(a_value).lower()); if (str == "manager") { config.m_logging_level = configuration_manager::logging_manager; } else if (str == "child") { config.m_logging_level = configuration_manager::logging_child; } else if (str == "rsync") { config.m_logging_level = configuration_manager::logging_rsync; } else { throw(ERROR(0,es)); } } /** Parse a "error-loging-level" command */ void global_parser::parse_error_logging_level(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid error-logging-level value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_nomem(str = estring(a_value).lower()); if (str == "manager") { config.m_error_logging_level = configuration_manager::logging_manager; } else if (str == "child") { config.m_error_logging_level = configuration_manager::logging_child; } else if (str == "rsync") { config.m_error_logging_level = configuration_manager::logging_rsync; } else { throw(ERROR(0,es)); } } /** Parse an "rsync-local-path" command */ void global_parser::parse_rsync_local_path(const std::string& a_value) { std::string es; filestatus fstat; TRY_nomem(es = "Invalid rsync-local-path value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_instead(fstat.path(a_value),es); TRY_nomem(config.m_rsync_local_path = a_value); } /** Parse an "rsync-parallel" command */ void global_parser::parse_rsync_parallel(const std::string& a_value) { std::string es; uint16 num; TRY_nomem(es = "Invalid rsync-parallel value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_instead(num = estring(a_value),es); if (num == 0) throw(ERROR(0,es)); config.m_rsync_parallel = num; } /** Parse "io-poll-interval" command */ void global_parser::parse_io_poll_interval(const std::string& a_value) { std::string es; uint16 num; TRY_nomem(es = "Invalid io-poll-interval value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_instead(num = estring(a_value),es); if (num == 0) throw(ERROR(0,es)); config.m_io_poll_interval = num; } /** Parse a "timestamp-resolution" command */ void global_parser::parse_timestamp_resolution(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid timestamp-resolution: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if (str == "year") { config.m_timestamp.resolution(timestamp::resolution_year); } else if (str == "month") { config.m_timestamp.resolution(timestamp::resolution_month); } else if (str == "day") { config.m_timestamp.resolution(timestamp::resolution_day); } else if (str == "hour") { config.m_timestamp.resolution(timestamp::resolution_hour); } else if (str == "minute") { config.m_timestamp.resolution(timestamp::resolution_minute); } else if (str == "second") { config.m_timestamp.resolution(timestamp::resolution_second); } else { throw(ERROR(0,es)); } } /** Parse a "vault" command */ void global_parser::parse_vault(const std::string& a_value) { std::string es; directory dir; directory::const_iterator cdi; subdirectory subdir; filestatus fstat; error partial_vault_error = ERROR(0,"One or more vault paths were found to be invalid:"); TRY_nomem(es = "Invalid vault path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); if (a_value.size() == 0) throw(ERROR(0,es)); TRY(dir.path(a_value),es); if (dir.size() == 0) { TRY_instead(fstat.path(a_value),es); } for (cdi = dir.begin(); cdi != dir.end(); cdi++) { if (!is_dir(*cdi)) { TRY_nomem(es = get_error_str(ENOTDIR)); TRY_nomem(es += ": \""); TRY_nomem(es += *cdi); TRY_nomem(es += "\""); partial_vault_error.push_back(es); continue; } if (!readable(*cdi)) { TRY_nomem(es = "No read access to path: \""); TRY_nomem(es += *cdi); TRY_nomem(es += "\""); partial_vault_error.push_back(es); continue; } if (!writable(*cdi)) { TRY_nomem(es = "No write access to path: \""); TRY_nomem(es += *cdi); TRY_nomem(es += "\""); partial_vault_error.push_back(es); continue; } if (!executable(*cdi)) { TRY_nomem(es = "No execution access to path: \""); TRY_nomem(es += *cdi); TRY_nomem(es += "\""); partial_vault_error.push_back(es); continue; } TRY_nomem(config.m_vaults.push_back(*cdi)); } if (partial_vault_error.size() > 1) { TRY_nomem(es = "For paths found matching: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); partial_vault_error.push_back(es); throw(partial_vault_error); } } /** Parse a "vault-overflow-behavior" command */ void global_parser::parse_vault_overflow_behavior(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid vault-overflow-behavior value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if (str == "quit") { config.m_vault_overflow_behavior = configuration_manager::overflow_quit; } else if (str == "delete-oldest") { config.m_vault_overflow_behavior = configuration_manager::overflow_delete_oldest; } else if (str == "delete-until-free") { config.m_vault_overflow_behavior = configuration_manager::overflow_delete_until_free; } else { throw(ERROR(0,es)); } } /** Parse a "vault-overflow-blocks" command */ void global_parser::parse_vault_overflow_blocks(const std::string& a_value) { std::string es; uint16 num; TRY_nomem(es = "Invalid vault-overflow-blocks value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(num = estring(a_value),es); if (num > 50) throw(ERROR(0,es)); config.m_vault_overflow_blocks = num; } /** Parse a "vault-overflow-inodes" command */ void global_parser::parse_vault_overflow_inodes(const std::string& a_value) { std::string es; uint16 num; TRY_nomem(es = "Invalid vault-overflow-inodes value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(num = estring(a_value),es); if (num > 50) throw(ERROR(0,es)); config.m_vault_overflow_inodes = num; } /** Parse a "vault-selection-behavior" command */ void global_parser::parse_vault_selection_behavior(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid vault-selection-behavior value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if (str == "max-free") { config.m_vault_selection_behavior = configuration_manager::selection_max_free; } else if (str == "round-robin") { config.m_vault_selection_behavior = configuration_manager::selection_round_robin; } else { throw(ERROR(0,es)); } } /** Parse a "vault-locking" command */ void global_parser::parse_vault_locking(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid vault-locking value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if ( (str == "y") || (str == "yes") || (str == "t") || (str == "true") || (str == "1") || (str == "on") ) { config.m_vault_locking = true; } else if ( (str == "n") || (str == "no") || (str == "f") || (str == "false") || (str == "0") || (str == "off") ) { config.m_vault_locking = false; } else { throw(ERROR(0,es)); } } //---------------------------------------------------------------------------- /** C'tor */ job_parser::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 ) { m_job = a_job; m_in = &a_in; m_path = &a_path; m_line = &a_line; m_delimiter = &a_block_delimiter; m_default_context = a_default_context; parse(); } /** Construct a string with the current location of the parser in a * configuration file */ const std::string job_parser::location(void) { std::string es; TRY_nomem(es = "At "); TRY_nomem(es += (*m_path)); TRY_nomem(es += "["); TRY_nomem(es += estring((*m_line))); TRY_nomem(es += "]"); return(es); } /** Read a job configuration file, used by the "include" command */ void job_parser::read_job(const std::string& a_path) { std::string es; std::ifstream in; uint16 line = 0; in.open(a_path.c_str()); if (!in.is_open()) { TRY_nomem(es = "Could not open job file: \""); TRY_nomem(es += a_path); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } job_parser(m_job, a_path, line, in, "", m_default_context); in.close(); } /** Job context parser */ void job_parser::parse(void) { std::string es; std::string keyword; std::string value; while ((*m_in)) { (*m_line)++; try { parse_line((*m_in), keyword, value); } catch(error e) { e.push_back(ERROR_INSTANCE(location())); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(location())); throw(e); } if (!(*m_in) && (m_delimiter->size() != 0)) { TRY_nomem(es = "Unexpected end of file, expected \""); TRY_nomem(es += *m_delimiter); TRY_nomem(es += "\" here"); throw(ERROR(errno,es)); } if (keyword.size() == 0) continue; if (keyword[0] == '#') continue; try { if (keyword == *m_delimiter) { return; } else if (keyword == "archive-path") { parse_archive_path(value); } else if (keyword == "clear") { parse_clear(value); } else if (keyword == "exclude-from") { parse_exclude_from(value); } else if (keyword == "include-from") { parse_include_from(value); } else if (keyword == "groupname") { parse_groupname(value); } else if (keyword == "hostname") { parse_hostname(value); } else if (keyword == "include") { parse_include(value); } else if ((keyword == "jobname") && (*m_delimiter == "")) { parse_jobname(value); } else if (keyword == "path") { parse_path(value); } else if (keyword == "rsync-behavior") { parse_rsync_behavior(value); } else if (keyword == "rsync-connection-type") { parse_rsync_connection_type(value); } else if (keyword == "rsync-hardlink") { parse_rsync_hardlink(value); } else if (keyword == "rsync-options") { parse_rsync_options(value); } else if (keyword == "") { parse_rsync_options_context(value); } else if (keyword == "rsync-remote-user") { parse_rsync_remote_user(value); } else if (keyword == "rsync-remote-path") { parse_rsync_remote_path(value); } else if (keyword == "rsync-remote-port") { parse_rsync_remote_port(value); } else if (keyword == "rsync-remote-module") { parse_rsync_remote_module(value); } else if (keyword == "rsync-retry-count") { parse_rsync_retry_count(value); } else if (keyword == "rsync-timeout") { parse_rsync_timeout(value); } else { error e(0); TRY_nomem(es = "Unknown command in "); if (m_default_context) { TRY_nomem(es += "default"); } else { TRY_nomem(es += "job"); } TRY_nomem(es += " context: \""); TRY_nomem(es += keyword); TRY_nomem(es += "\""); throw(ERROR(0,es)); } } catch(error e) { if ((*m_delimiter).size() == 0) e.push_back(ERROR_INSTANCE(location())); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(location())); throw(e); } } } /** Parse an "archive-path" command */ void job_parser::parse_archive_path(const std::string& a_value) { std::string es; TRY_nomem(es = "Invalid archive-path value"); TRY((*m_job).archive_path = a_value,es); } /** Parse a "clear" command */ void job_parser::parse_clear(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid clear value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if (str == "archive-path") { m_job->archive_path.clear(); } else if (str == "exclude-from") { m_job->excludes.clear(); } else if (str == "groupname") { m_job->groupname.erase(); } else if (str == "hostname") { m_job->hostname.erase(); } else if (str == "include-from") { m_job->includes.clear(); } else if ((str == "jobname") && (*m_delimiter == "")) { m_job->jobname.erase(); } else if (str == "paths") { m_job->paths.clear(); } else if (str == "rsync-behavior") { m_job->rsync_behavior.clear(); } else if (str == "rsync-options") { m_job->rsync_options.erase(); } else if (str == "rsync-remote-user") { m_job->rsync_remote_user.erase(); } else if (str == "rsync-remotr-port") { m_job->rsync_remote_port = 0; } else if (str == "rsync-remote-module") { m_job->rsync_remote_module.erase(); } else if (str == "rsync-remote-path") { m_job->rsync_remote_path.erase(); } else { throw(ERROR(0,es)); } } /** Parse an "exclude-from" command */ void job_parser::parse_exclude_from(const std::string& a_value) { std::string es; directory dir; directory::const_iterator cdi; std::string rpath; std::string ipath; if (a_value.size() == 0) { TRY_nomem(es = "Invalid exclude-from path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); throw(ERROR(0,es)); } if (a_value[0] != '/') { TRY_nomem(rpath = parse_dirname(*m_path)); } TRY_nomem(ipath = rpath + a_value); TRY_nomem(es = "No exclude-from file(s) found: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(dir.path(ipath),es); if (dir.size() == 0) throw(ERROR(0,es)); for (cdi = dir.begin(); cdi != dir.end(); cdi++) { TRY_nomem((*m_job).excludes.push_back(*cdi)); } } /** Parse an "include-from" command */ void job_parser::parse_include_from(const std::string& a_value) { std::string es; directory dir; directory::const_iterator cdi; std::string rpath; std::string ipath; if (a_value.size() == 0) { TRY_nomem(es = "Invalid include-from path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); throw(ERROR(0,es)); } if (a_value[0] != '/') { TRY_nomem(rpath = parse_dirname(*m_path)); } TRY_nomem(ipath = rpath + a_value); TRY_nomem(es = "No include-from file(s) found: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(dir.path(ipath),es); if (dir.size() == 0) throw(ERROR(0,es)); for (cdi = dir.begin(); cdi != dir.end(); cdi++) { TRY_nomem((*m_job).includes.push_back(*cdi)); } } /** Parse a "groupname" command */ void job_parser::parse_groupname(const std::string& a_value) { TRY_nomem((*m_job).groupname = a_value); } /** Parse a "hostname" command */ void job_parser::parse_hostname(const std::string& a_value) { TRY_nomem((*m_job).hostname = a_value); } /** Parse an "include" command */ void job_parser::parse_include(const std::string& a_value) { std::string es; directory dir; directory::const_iterator cdi; std::string rpath; std::string ipath; if (a_value.size() == 0) { TRY_nomem(es = "Invalid include path: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); throw(ERROR(0,es)); } if (a_value[0] != '/') { TRY_nomem(rpath = parse_dirname(*m_path)); } TRY_nomem(ipath = rpath + a_value); TRY_nomem(es = "No configuration file(s) found: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_instead(dir.path(ipath),es); if (dir.size() == 0) throw(ERROR(0,es)); for (cdi = dir.begin(); cdi != dir.end(); cdi++) { if (dir.size() > 1) { TRY_nomem(es = "For files found matching: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); try { read_job(*cdi); } catch(error e) { e.push_back(ERROR_INSTANCE(es)); throw(e); } catch(...) { error e = err_unknown; e.push_back(ERROR_INSTANCE(es)); throw(e); } } else read_job(*cdi); } } /** Parse a "jobname" command */ void job_parser::parse_jobname(const std::string& a_value) { TRY_nomem((*m_job).jobname = a_value); } /** Parse a "path" command */ void job_parser::parse_path(const std::string& a_value) { TRY_nomem((*m_job).paths.push_back(a_value)); } /** Parse an "rsync-behavior" command */ void job_parser::parse_rsync_behavior(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid rsync-behavior value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if (str == "clear") { (*m_job).rsync_behavior.clear(); } else if (str == "reset") { (*m_job).rsync_behavior.reset(); } else (*m_job).rsync_behavior.assign(a_value); } /** Parse an "rsync-connection-type" command */ void job_parser::parse_rsync_connection_type(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid rsync-connection-type value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if (str == "local") { (*m_job).rsync_connection = job::connection_local; } else if (str == "remote") { (*m_job).rsync_connection = job::connection_remote; } else if (str == "server") { (*m_job).rsync_connection = job::connection_server; } else { throw(ERROR(0,es)); } } /** Parse an "rsync-hardlink" command */ void job_parser::parse_rsync_hardlink(const std::string& a_value) { std::string es; estring str; TRY_nomem(es = "Invalid rsync-hardlink value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY(str = estring(a_value).lower(),es); if ( (str == "y") || (str == "yes") || (str == "t") || (str == "true") || (str == "1") || (str == "on") ) { (*m_job).rsync_hardlink = true; } else if ( (str == "n") || (str == "no") || (str == "f") || (str == "false") || (str == "0") || (str == "off") ) { (*m_job).rsync_hardlink = false; } else { throw(ERROR(0,es)); } } /** Parse an "rsync-options" command */ void job_parser::parse_rsync_options(const std::string& a_value) { if (*m_delimiter == "") { TRY_nomem((*m_job).rsync_options = a_value); } else { if ((*m_job).rsync_options.size() != 0) { TRY_nomem((*m_job).rsync_options += " "); } TRY_nomem((*m_job).rsync_options += a_value); } } /** Parse an rsync-options context */ void job_parser::parse_rsync_options_context(const std::string& a_value) { std::string es; std::string value; std::string rsync_options; char ch; while ((*m_in)) { (*m_line)++; value.erase(); (*m_in).get(ch); while ((ch == ' ') || (ch == '\t')) (*m_in).get(ch); while (((*m_in)) && (ch != '\n')) { TRY_nomem(value += ch); (*m_in).get(ch); } if ((!(*m_in)) && (value != "")) { TRY_nomem(es = "Unexpected end of file, expected "); TRY_nomem(es += "\"\" here"); throw(ERROR(errno,es)); } if (value.size() == 0) continue; if (value[0] == '#') continue; if (value == "") { parse_rsync_options(rsync_options); return; } if (rsync_options.size() != 0) { TRY_nomem(rsync_options += ' '); } TRY_nomem(rsync_options += value); } } /** Parse a "remote-user" command */ void job_parser::parse_rsync_remote_user(const std::string& a_value) { TRY_nomem((*m_job).rsync_remote_user = a_value); } /** Parse an "rsync-remote-path" command */ void job_parser::parse_rsync_remote_path(const std::string& a_value) { TRY_nomem((*m_job).rsync_remote_path = a_value); } /** Parse an "rsync-remote-port" command */ void job_parser::parse_rsync_remote_port(const std::string& a_value) { std::string es; estring str; uint16 num; TRY_nomem(es = "Invalid rsync-remote-port value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_nomem(str = a_value); TRY(num = str,es); (*m_job).rsync_remote_port = num; } /** Parse an "rsync-remote-module" command */ void job_parser::parse_rsync_remote_module(const std::string& a_value) { TRY_nomem((*m_job).rsync_remote_module = a_value); } /** Parse an "rsync-retry-count" command */ void job_parser::parse_rsync_retry_count(const std::string& a_value) { std::string es; estring str; uint16 num; TRY_nomem(es = "Invalid rsync-retry-count value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_nomem(str = a_value); TRY(num = str,es); (*m_job).rsync_retry_count = num; } /** Parse an "rsync-timeout" command */ void job_parser::parse_rsync_timeout(const std::string& a_value) { std::string es; estring str; uint16 num; TRY_nomem(es = "Invalid rsync-timeout value: \""); TRY_nomem(es += a_value); TRY_nomem(es += "\""); TRY_nomem(str = a_value); TRY(num = str,es); (*m_job).rsync_timeout = num; } //---------------------------------------------------------------------------- /** The global configuration manager instance */ configuration_manager config;