rconfig.cc

Go to the documentation of this file.
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_options.erase();
00587         rsync_remote_user.erase();
00588         rsync_remote_path.erase();
00589         rsync_remote_port = 0;
00590         rsync_remote_module.erase();
00591         rsync_retry_count = 3;
00592         rsync_timeout = 60 * 60 * 4; // 4 hours;
00593 }
00594 
00595 /** Assign values from another job instance */
00596 void job::assign(const job& a_job)
00597 {
00598         clear();
00599 
00600         default_config_path = a_job.default_config_path;
00601         default_config_line = a_job.default_config_line;
00602         config_path = a_job.config_path;
00603         config_line = a_job.config_line;
00604         archive_path = a_job.archive_path;
00605         excludes = a_job.excludes;
00606         includes = a_job.includes;
00607         groupname = a_job.groupname;
00608         hostname = a_job.hostname;
00609         jobname = a_job.jobname;
00610         paths = a_job.paths;
00611         rsync_behavior.clear();
00612         rsync_behavior = a_job.rsync_behavior;
00613         rsync_connection = a_job.rsync_connection;
00614         rsync_hardlink = a_job.rsync_hardlink;
00615         rsync_options = a_job.rsync_options;
00616         rsync_remote_user = a_job.rsync_remote_user;
00617         rsync_remote_path = a_job.rsync_remote_path;
00618         rsync_remote_port = a_job.rsync_remote_port;
00619         rsync_remote_module = a_job.rsync_remote_module;
00620         rsync_retry_count = a_job.rsync_retry_count;
00621         rsync_timeout = a_job.rsync_timeout;
00622 }
00623 
00624 /** Assignment */
00625 job& job::operator=(const job& a_job)
00626 {
00627         assign(a_job);
00628 
00629         return(*this);
00630 }
00631 
00632 /** Generate the archive-path subdirectory for this job */
00633 const std::string job::generate_archive_path(const std::string& a_path) const
00634 {
00635         std::string es;
00636         std::string path;
00637         archive_path::const_iterator capi;
00638 
00639         for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) {
00640                 if (capi->type() == archive_path_element::jobname) {
00641                         if (jobname.size() == 0) {
00642                                 TRY_nomem(es = "archive-path references jobname, ");
00643                                 TRY_nomem(es += "but jobname is empty");
00644                                 throw(ERROR(0,es));
00645                         }
00646                         if (path.size() != 0) {
00647                                 TRY_nomem(path += '/');
00648                         }
00649                         TRY_nomem(path += jobname);
00650                 }
00651                 else if (capi->type() == archive_path_element::groupname) {
00652                         if (groupname.size() == 0) {
00653                                 TRY_nomem(es = "archive-path references groupname, ");
00654                                 TRY_nomem(es += "but groupname is empty");
00655                                 throw(ERROR(0,es));
00656                         }
00657                         if (path.size() != 0) {
00658                                 TRY_nomem(path += '/');
00659                         }
00660                         TRY_nomem(path += groupname);
00661                 }
00662                 else if (capi->type() == archive_path_element::hostname) {
00663                         if (hostname.size() == 0) {
00664                                 TRY_nomem(es = "archive-path references hostname, ");
00665                                 TRY_nomem(es += "but hostname is empty");
00666                                 throw(ERROR(0,es));
00667                         }
00668                         if (path.size() != 0) {
00669                                 TRY_nomem(path += '/');
00670                         }
00671                         TRY_nomem(path += hostname);
00672                 }
00673                 else if (capi->type() == archive_path_element::pathname) {
00674                         if (a_path.size() == 0) {
00675                                 TRY_nomem(es = "archive-path references path name, ");
00676                                 TRY_nomem(es += "but path name is empty");
00677                                 throw(ERROR(0,es));
00678                         }
00679                         if (path.size() != 0) {
00680                                 TRY_nomem(path += '/');
00681                         }
00682                         TRY_nomem(path += a_path);
00683                 }
00684                 else if (capi->type() == archive_path_element::permutation) {
00685                         if (a_path.size() == 0) {
00686                                 TRY_nomem(es = "archive-path references path name, ");
00687                                 TRY_nomem(es += "but path name is empty");
00688                                 throw(ERROR(0,es));
00689                         }
00690                         if (path.size() != 0) {
00691                                 TRY_nomem(path += '/');
00692                         }
00693                         TRY_nomem(path += permute_path(a_path));
00694                 }
00695                 else if (capi->type() == archive_path_element::literal) {
00696                         if (capi->value().size() == 0) {
00697                                 TRY_nomem(es = "archive-path references literal string, ");
00698                                 TRY_nomem(es += "but literal string value is empty");
00699                                 throw(INTERNAL_ERROR(0,es));
00700                         }
00701                         if (path.size() != 0) {
00702                                 TRY_nomem(path += '/');
00703                         }
00704                         TRY_nomem(path += capi->value());
00705                 }
00706                 else
00707                         throw(INTERNAL_ERROR(0,"Unknown archive path element type"));
00708         }
00709 
00710         // If path does not end with a '/', strip off characters until it does.
00711         while ((path.size() > 0) && (path[path.size()-1] != '/'))
00712                 path.erase(path.size()-1);
00713 
00714         path = reform_path(path);
00715 
00716         return(path);
00717 }
00718 
00719 /** Generate the source path to be passed to rsync on the command line */
00720 const std::string job::generate_source_path(const std::string& a_path) const
00721 {
00722         estring path;
00723 
00724         if (rsync_connection == connection_server) {
00725                 path += "rsync://";
00726                 if (rsync_remote_user.size() != 0) {
00727                         path += rsync_remote_user;
00728                         path += "@";
                }
                TRY_nomem(path += hostname);
                if (rsync_remote_port != 0) {
                        path += ":";
00729                         path += estring(rsync_remote_port);
00730                 }
00731                 if (rsync_remote_module.size() != 0) {
00732                         path += "/";
00733                         path += rsync_remote_module;
00734                         if ((a_path.size() > 0) && (a_path[0] != '/'))
00735                                 path += "/";
00736                 }
00737         }
00738         else if (rsync_connection == connection_remote) {
00739                 if (rsync_remote_user.size() != 0) {
00740                         path += rsync_remote_user;
00741                         path += "@";
                }
                path += hostname;
                path += ":";
00742         }
00743         path += a_path;
00744 
00745         return(path);
00746 }
00747 
00748 /** Generate a unique ID string for this job */
00749 const std::string job::generate_job_id(void) const
00750 {
00751         std::string es;
00752         std::string path;
00753         archive_path::const_iterator capi;
00754 
00755         for (capi = archive_path.begin(); capi != archive_path.end(); ++capi) {
00756                 if (capi->type() == archive_path_element::jobname) {
00757                         if (jobname.size() == 0) {
00758                                 TRY_nomem(es = "archive-path references jobname, ");
00759                                 TRY_nomem(es += "but jobname is empty");
00760                                 throw(ERROR(0,es));
00761                         }
00762                         if (path.size() != 0) {
00763                                 TRY_nomem(path += '/');
00764                         }
00765                         TRY_nomem(path += jobname);
00766                 }
00767                 else if (capi->type() == archive_path_element::groupname) {
00768                         if (groupname.size() == 0) {
00769                                 TRY_nomem(es = "archive-path references groupname, ");
00770                                 TRY_nomem(es += "but groupname is empty");
00771                                 throw(ERROR(0,es));
00772                         }
00773                         if (path.size() != 0) {
00774                                 TRY_nomem(path += '/');
00775                         }
00776                         TRY_nomem(path += groupname);
00777                 }
00778                 else if (capi->type() == archive_path_element::hostname) {
00779                         if (hostname.size() == 0) {
00780                                 TRY_nomem(es = "archive-path references hostname, ");
00781                                 TRY_nomem(es += "but hostname is empty");
00782                                 throw(ERROR(0,es));
00783                         }
00784                         if (path.size() != 0) {
00785                                 TRY_nomem(path += '/');
00786                         }
00787                         TRY_nomem(path += hostname);
00788                 }
00789                 else if (capi->type() == archive_path_element::literal) {
00790                         if (capi->value().size() == 0) {
00791                                 TRY_nomem(es = "archive-path references literal string, ");
00792                                 TRY_nomem(es += "but literal string value is empty");
00793                                 throw(INTERNAL_ERROR(0,es));
00794                         }
00795                         if (path.size() != 0) {
00796                                 TRY_nomem(path += '/');
00797                         }
00798                         TRY_nomem(path += capi->value());
00799                 }
00800         }
00801 
00802         path = reform_path(path);
00803 
00804         return(path);
00805 }
00806 
00807 /** Perform sanity checks for the configuration settings of this job */
00808 void job::check(void)
00809 {
00810         std::string es;
00811         std::string this_path;
00812         std::string that_path;
00813         paths_type::const_iterator cpi;
00814         configuration_manager::jobs_type::const_iterator cji;
00815         paths_type::const_iterator cjapi;
00816 
00817         if (
00818                         (
00819                                 (rsync_connection == connection_remote) 
00820                                 || (rsync_connection == connection_server)
00821                         )
00822                         && (hostname.size() == 0)
00823                 )
00824         {
00825                 TRY_nomem(es = "rsync-connection-type references hostname, ");
00826                 TRY_nomem(es += "but hostname is empty");
00827                 throw(ERROR(0,es));
00828         }
00829 
00830         if ((rsync_remote_module.size() != 0) 
00831                 && (rsync_connection != connection_server)) 
00832         {
00833                 TRY_nomem(es = "rsync-remote-module specifies a module, but ");
00834                 TRY_nomem(es = "rsync-connection-type is not server");
00835                 throw(ERROR(0,es));
00836         }
00837 
00838         if (paths.size() == 0) {
00839                 throw(ERROR(0,"No paths defined for this job"));
00840         }
00841 
00842         for (cpi = paths.begin() ; cpi != paths.end(); cpi++) {
00843                 TRY_nomem(this_path = generate_archive_path(*cpi));
00844 
00845                 for (
00846                         cji = config.jobs().begin();
00847                         cji != config.jobs().end();
00848                         cji++
00849                         )
00850                 {
00851                         for (
00852                                 cjapi = cji->paths.begin();
00853                                 cjapi != cji->paths.end();
00854                                 cjapi++
00855                                 )
00856                         {
00857                                 TRY_nomem(that_path = cji->generate_archive_path(*cjapi));
00858 
00859                                 if (this_path == that_path) {
00860                                         error e(0);
00861 
00862                                         TRY_nomem(es = "Duplicate archive-path values detected");
00863                                         e.push_back(ERROR_INSTANCE(es));
00864                                         TRY_nomem(es = "Archive path: \"");
00865                                         TRY_nomem(es += this_path);
00866                                         TRY_nomem(es += "\"");
00867                                         e.push_back(ERROR_INSTANCE(es));
00868                                         TRY_nomem(es = "Previously defined at ");
00869                                         TRY_nomem(es += cji->config_path);
00870                                         TRY_nomem(es += "[");
00871                                         TRY_nomem(es += estring(cji->config_line));
00872                                         TRY_nomem(es += "]");
00873                                         e.push_back(ERROR_INSTANCE(es));
00874                                         throw(e);
00875                                 }
00876 
00877                                 if (
00878                                                 (this_path.size() < that_path.size())
00879                                                 && (this_path == that_path.substr(0,this_path.size()))
00880                                                 && (
00881                                                         (that_path[this_path.size()] == '/')
00882                                                         || (this_path.size() == 0)
00883                                                         )
00884                                         )
00885                                 {
00886                                         error e(0);
00887 
00888                                         TRY_nomem(es = "Overlapping archive-path values detected");
00889                                         e.push_back(ERROR_INSTANCE(es));
00890                                         TRY_nomem(es = "Defined archive-path: \"");
00891                                         TRY_nomem(es += this_path);
00892                                         TRY_nomem(es += "\"");
00893                                         e.push_back(ERROR_INSTANCE(es));
00894                                         TRY_nomem(es = "Is in a parent directory of another job's previously defined archive-path");
00895                                         e.push_back(ERROR_INSTANCE(es));
00896                                         TRY_nomem(es = "At ");
00897                                         TRY_nomem(es += cji->config_path);
00898                                         TRY_nomem(es += "[");
00899                                         TRY_nomem(es += estring(cji->config_line));
00900                                         TRY_nomem(es += "]");
00901                                         e.push_back(ERROR_INSTANCE(es));
00902                                         throw(e);
00903                                 }
00904 
00905                                 if (
00906                                                 (this_path.size() > that_path.size())
00907                                                 && (this_path.substr(0,that_path.size()) == that_path)
00908                                                 && (
00909                                                         (this_path[that_path.size()] == '/')
00910                                                         || (that_path.size() == 0)
00911                                                         )
00912                                         )
00913                                 {
00914                                         error e(0);
00915 
00916                                         TRY_nomem(es = "Overlapping archive-path values detected");
00917                                         e.push_back(ERROR_INSTANCE(es));
00918                                         TRY_nomem(es = "Defined archive-path: \"");
00919                                         TRY_nomem(es += this_path);
00920                                         TRY_nomem(es += "\"");
00921                                         e.push_back(ERROR_INSTANCE(es));
00922                                         TRY_nomem(es = "Is in a subdirectory of another job's previously defined archive-path");
00923                                         e.push_back(ERROR_INSTANCE(es));
00924                                         TRY_nomem(es = "At ");
00925                                         TRY_nomem(es += cji->config_path);
00926                                         TRY_nomem(es += "[");
00927                                         TRY_nomem(es += estring(cji->config_line));
00928                                         TRY_nomem(es += "]");
00929                                         e.push_back(ERROR_INSTANCE(es));
00930                                         throw(e);
00931                                 }
00932                         }
00933                 }
00934         }
00935 }
00936 
00937 //----------------------------------------------------------------------------
00938 
00939 /** Reset configuration to default settings */
00940 void configuration_manager::clear(void)
00941 {
00942         m_initialized = false;
00943         m_configs_read = 0;
00944         m_default = true;
00945         TRY_nomem(m_default_file = CONFIGFILE);
00946         m_action = action_help;
00947         m_timestamp.set();
00948         m_cfgfiles.clear();
00949         m_link_catalog_dir.erase();
00950         TRY_nomem(m_log_dir = LOGDIR);
00951         m_rsync_local_path.erase();
00952         if (strlen(LOCAL_RSYNC) > 0) {
00953                 TRY_nomem(m_rsync_local_path = LOCAL_RSYNC);
00954         }
00955         m_rsync_parallel = 1;
00956         m_io_poll_interval = 1;
00957         m_vaults.clear();
00958         m_vault_overflow_behavior = overflow_quit;
00959         m_vault_overflow_blocks = 10;
00960         m_vault_overflow_inodes = 10;
00961         m_vault_selection_behavior = selection_round_robin;
00962         m_default_job.clear();
00963         m_logging_level = logging_rsync;
00964         m_jobs.clear();
00965 }
00966 
00967 /** C'tor */
00968 configuration_manager::configuration_manager()
00969 {
00970         if (this != &config)
00971                 throw(
00972                         INTERNAL_ERROR(0,"Attempt to allocate multiple configuration managers")
00973                         );
00974         clear();
00975 }
00976 
00977 /** Initialize the configuration manager from rvm's command line options */
00978 void configuration_manager::init(int argc, char *argv[])
00979 {
00980         int c;
00981         estring opt;
00982         estring arg;
00983         std::string es;
00984         std::string tes;
00985         cfgfiles_type::const_iterator cfi;
00986         bool use_custom_timestamp = false;
00987         class timestamp custom_timestamp;
00988 
00989         for (c = 1; c < argc; c++) {
00990                 TRY_nomem(opt = argv[c]);
00991                 if (c+1 == argc) {
00992                         TRY_nomem(arg = "");
00993                 }
00994                 else {
00995                         TRY_nomem(arg = argv[c+1]);
00996                 }
00997 
00998                 if (opt == "--archive") {
00999                         m_action = action_archive;
01000                 }
01001                 else if (opt == "--relink") {
01002                         m_action = action_relink;
01003                 }
01004                 else if (opt == "--help") {
01005                         m_action = action_help;
01006                 }
01007                 else if (opt == "--version") {
01008                         m_action = action_version;
01009                 }
01010                 else if (opt == "--check-config") {
01011                         m_action = action_check_config;
01012                 }
01013                 else if (opt == "--no-default-config") {
01014                         m_default = false;
01015                 }
01016                 else if (opt == "--config") {
01017                         directory dir;
01018                         directory::const_iterator cdi;
01019 
01020                         TRY_nomem(es = "Error finding configuration file(s) ");
01021                         TRY_nomem(es += "matching command line argument [");
01022                         TRY_nomem(es += estring(c+1));
01023                         TRY_nomem(es += "]: \"");
01024                         TRY_nomem(es += arg);
01025                         TRY_nomem(es += "\"");
01026 
01027                         try {
01028                                 dir.path(arg);
01029                         }
01030                         catch(error e) {
01031                                 e.push_back(ERROR_INSTANCE(es));
01032                                 throw(e);
01033                         }
01034                         catch(...) {
01035                                 error e = err_unknown;
01036 
01037                                 e.push_back(ERROR_INSTANCE(es));
01038                                 throw(e);
01039                         }
01040                         if (dir.size() == 0) {
01041                                 TRY_nomem(es = "No configuration file(s) found matching ");
01042                                 TRY_nomem(es += "command line argument [");
01043                                 TRY_nomem(es += estring(c+1));
01044                                 TRY_nomem(es += "]: \"");
01045                                 TRY_nomem(es += arg);
01046                                 TRY_nomem(es += "\"");
01047 
01048                                 throw(ERROR(0,es));
01049                         }
01050                         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
01051                                 TRY_nomem(m_cfgfiles.push_back(cfgfile_element(config_file, arg)));
01052                         }
01053 
01054                         c++;
01055                 }
01056                 else if (opt == "--job") {
01057                         directory dir;
01058                         directory::const_iterator cdi;
01059 
01060                         TRY_nomem(es = "Error finding job file(s) ");
01061                         TRY_nomem(es += "matching command line argument [");
01062                         TRY_nomem(es += estring(c+1));
01063                         TRY_nomem(es += "]: \"");
01064                         TRY_nomem(es += arg);
01065                         TRY_nomem(es += "\"");
01066 
01067                         try {
01068                                 dir.path(arg);
01069                         }
01070                         catch(error e) {
01071                                 e.push_back(ERROR_INSTANCE(es));
01072                                 throw(e);
01073                         }
01074                         catch(...) {
01075                                 error e = err_unknown;
01076                                 throw(e);
01077                         }
01078                         if (dir.size() == 0) {
01079                                 TRY_nomem(es = "No job file(s) found matching ");
01080                                 TRY_nomem(es += "command line argument [");
01081                                 TRY_nomem(es += estring(c+1));
01082                                 TRY_nomem(es += "]: \"");
01083                                 TRY_nomem(es += arg);
01084                                 TRY_nomem(es += "\"");
01085 
01086                                 throw(ERROR(0,es));
01087                         }
01088                         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
01089                                 TRY_nomem(m_cfgfiles.push_back(cfgfile_element(job_file, arg)));
01090                         }
01091 
01092                         c++;
01093                 }
01094                 else if (opt == "--timestamp") {
01095                         TRY_nomem(es = "From command line argument ");
01096                         TRY_nomem(es += estring(c+1));
01097                         TRY(custom_timestamp.assign(arg),es);
01098                         use_custom_timestamp = true;
01099                         c++;
01100                         TRY_nomem(tes = es);
01101                 }
01102                 else {
01103                         TRY_nomem(es = "Unknown command line option: \"");
01104                         TRY_nomem(es += opt);
01105                         TRY_nomem(es += "\"");
01106                         throw(ERROR(0,es));
01107                 }
01108         }
01109 
01110         m_initialized = true;
01111 
01112         if ((m_action == action_help) || (m_action == action_version))
01113                 return;
01114         
01115         if (use_default())
01116                 read_config(m_default_file);
01117 
01118         for (cfi = m_cfgfiles.begin(); cfi != m_cfgfiles.end(); cfi++) {
01119                 if (cfi->first == config_file) {
01120                         read_config(cfi->second);
01121                 }
01122                 else {
01123                         read_job(cfi->second);
01124                 }
01125         }
01126 
01127         if (m_configs_read == 0) {
01128                 throw(ERROR(0,"No configuration file(s) read"));
01129         }
01130 
01131         if (use_custom_timestamp) {
01132                 TRY(m_timestamp = custom_timestamp,tes);
01133         }
01134 
01135         check();
01136 }
01137 
01138 /** Perform sanity checks on configuration settings */
01139 void configuration_manager::check(void) const
01140 {
01141         std::string es;
01142         subdirectory subdir;
01143         filestatus fstat;
01144 
01145         if (!initialized())
01146                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01147 
01148         // log-dir -- If no log-dir is set then we use a default.  What if that
01149         // default doesn't exist?
01150         if (m_log_dir.size() == 0) {
01151                 throw(ERROR(0,"log-dir not set"));
01152         }
01153         TRY_nomem(es = "Invalid log-dir value: \"");
01154         TRY_nomem(es += m_log_dir);
01155         TRY_nomem(es += "\"");
01156         TRY_instead(subdir.path(m_log_dir,"*"),es);
01157 
01158         // rsync-local-path -- If (a) rsync-local-path is not set from config, and
01159         // (b) a default was determined at compile time, then (c) does the default
01160         // exist?
01161         TRY_nomem(es = "Invalid rsync-local-path value: \"");
01162         TRY_nomem(es += m_rsync_local_path);
01163         TRY_nomem(es += "\"");
01164         TRY(fstat.path(m_rsync_local_path),es);
01165 
01166         // vault -- Are there any?
01167         if (m_vaults.size() == 0) {
01168                 throw(ERROR(0,"No vaults defined"));
01169         }
01170 }
01171 
01172 /** Return the initialized state of the configuration manager */
01173 const bool configuration_manager::initialized(void) const
01174 {
01175         return(m_initialized);
01176 }
01177 
01178 /** Return the action rvm is to take */
01179 const configuration_manager::action_type 
01180         configuration_manager::action(void) const
01181 {
01182         if (!initialized())
01183                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01184 
01185         return(m_action);
01186 }
01187 
01188 /** Return whether or not rvm is to try to read it's default configuration file */
01189 const bool configuration_manager::use_default(void) const
01190 {
01191         if (!initialized())
01192                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01193 
01194         return(m_default);
01195 }
01196 
01197 /** Set the default configuration filename */
01198 void configuration_manager::default_file(const std::string& a_path)
01199 {
01200         TRY_nomem(m_default_file = a_path);
01201 }
01202 
01203 /** Return the default configuration filename */
01204 const std::string& configuration_manager::default_file(void) const
01205 {
01206         if (!initialized())
01207                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01208 
01209         return(m_default_file);
01210 }
01211 
01212 /** Return the default log-dir */
01213 void configuration_manager::default_logdir(const std::string& a_path)
01214 {
01215         TRY_nomem(m_log_dir = a_path);
01216 }
01217 
01218 /** Return the timestamp of this instance of rvm */
01219 const class timestamp& configuration_manager::timestamp(void) const
01220 {
01221         if (!initialized())
01222                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01223 
01224         return(m_timestamp);
01225 }
01226 
01227 /** Return the link-catalog-dir path */
01228 const std::string& configuration_manager::link_catalog_dir(void) const
01229 {
01230         if (!initialized())
01231                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01232 
01233         return(m_link_catalog_dir);
01234 }
01235 
01236 /** Return the log-dir path */
01237 const std::string& configuration_manager::log_dir(void) const
01238 {
01239         if (!initialized())
01240                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01241 
01242         return(m_log_dir);
01243 }
01244 
01245 /** Return the rsync-local-path */
01246 const std::string& configuration_manager::rsync_local_path(void) const
01247 {
01248         if (!initialized())
01249                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01250 
01251         return(m_rsync_local_path);
01252 }
01253 
01254 /** Return the rsync-parallel */
01255 const uint16& configuration_manager::rsync_parallel(void) const
01256 {
01257         if (!initialized())
01258                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01259 
01260         return(m_rsync_parallel);
01261 }
01262 
01263 /** Return the number of seconds to sleep between polling for I/O */
01264 const uint16& configuration_manager::io_poll_interval(void) const
01265 {
01266         if (!initialized())
01267                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01268 
01269         return(m_io_poll_interval);
01270 }
01271 
01272 /** Return the timestamp-resolution */
01273 const timestamp::resolution_type configuration_manager::timestamp_resolution(void) const
01274 {
01275         if (!initialized())
01276                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01277 
01278         return(m_timestamp.resolution());
01279 }
01280 
01281 /** Return the vaults */
01282 const configuration_manager::vaults_type& configuration_manager::vaults(void) const
01283 {
01284         if (!initialized())
01285                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01286 
01287         return(m_vaults);
01288 }
01289 
01290 /** Return the vault-overflow-behavior */
01291 const configuration_manager::overflow_type& configuration_manager::vault_overflow_behavior(void) const
01292 {
01293         if (!initialized())
01294                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01295 
01296         return(m_vault_overflow_behavior);
01297 }
01298 
01299 /** Return the vault-overflow-blocks */
01300 const uint16& configuration_manager::vault_overflow_blocks(void) const
01301 {
01302         if (!initialized())
01303                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01304 
01305         return(m_vault_overflow_blocks);
01306 }
01307 
01308 /** Return the vault-overflow-inodes */
01309 const uint16& configuration_manager::vault_overflow_inodes(void) const
01310 {
01311         if (!initialized())
01312                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01313 
01314         return(m_vault_overflow_inodes);
01315 }
01316 
01317 /** Return the vault-selection-behavior */
01318 const configuration_manager::selection_type& configuration_manager::vault_selection_behavior(void) const
01319 {
01320         if (!initialized())
01321                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01322 
01323         return(m_vault_selection_behavior);
01324 }
01325 
01326 /** Return the default job configuration */
01327 const job& configuration_manager::default_job(void) const
01328 {
01329         if (!initialized())
01330                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01331 
01332         return(m_default_job);
01333 }
01334 
01335 /** Return a list of jobs */
01336 const configuration_manager::jobs_type& configuration_manager::jobs(void) const
01337 {
01338         if (!initialized())
01339                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01340 
01341         return(m_jobs);
01342 }
01343 
01344 /** Return the logging-level */
01345 const configuration_manager::logging_type& configuration_manager::logging_level(void) const
01346 {
01347         if (!initialized())
01348                 throw(INTERNAL_ERROR(0,"Configuration manager is not initialized"));
01349 
01350         return(m_logging_level);
01351 }
01352 
01353 /** Read a configuration file */
01354 void configuration_manager::read_config(const std::string& a_path)
01355 {
01356         std::string es;
01357         std::ifstream in;
01358         uint16 line = 0;
01359 
01360         in.open(a_path.c_str());
01361         if (!in.is_open()) {
01362                 TRY_nomem(es = "Could not open configuration file: \"");
01363                 TRY_nomem(es += a_path);
01364                 TRY_nomem(es += "\"");
01365                 throw(ERROR(errno,es));
01366         }
01367 
01368         global_parser(a_path, line, in);
01369 
01370         in.close();
01371         m_configs_read++;
01372 }
01373 
01374 /** Read a job configuration file */
01375 void configuration_manager::read_job(const std::string& a_path)
01376 {
01377         std::string es;
01378         std::ifstream in;
01379         job njob;
01380         uint16 line = 0;
01381 
01382         in.open(a_path.c_str());
01383         if (!in.is_open()) {
01384                 TRY_nomem(es = "Could not open job file: \"");
01385                 TRY_nomem(es += a_path);
01386                 TRY_nomem(es += "\"");
01387                 throw(ERROR(errno,es));
01388         }
01389 
01390         njob = m_default_job;
01391 
01392         job_parser(&njob, a_path, line, in, "", false);
01393 
01394         njob.check();
01395 
01396         in.close();
01397 }
01398 
01399 //----------------------------------------------------------------------------
01400 
01401 /** Parse a keyword/value pair read from a configuration file */
01402 void parse_line(
01403         std::istream& a_in,
01404         std::string& a_keyword,
01405         std::string& a_value
01406         )
01407 {
01408         std::string es;
01409         char ch;
01410 
01411         a_keyword.erase();
01412         a_value.erase();
01413 
01414         while (true) {
01415                 a_in.get(ch);
01416                 // Skip whitespace before keyword.
01417                 while ((a_in) && ((ch == ' ') || (ch == '\t'))) {
01418                         a_in.get(ch);
01419                 }
01420                 // Read keyword
01421                 while ((a_in) && ((ch != ' ') && (ch != '\t') && (ch != '\n'))) {
01422                         TRY_nomem(a_keyword += ch);
01423                         a_in.get(ch);
01424                 }
01425                 if (!a_in)
01426                         return;
01427         
01428                 // If keyword is empty then this is a blank line, skip it.
01429                 if (a_keyword.size() == 0)
01430                         return;
01431 
01432                 // If keyword starts with a '#' then this is a comment line, skip it.
01433                 if (a_keyword[0] == '#') {
01434                         // Discard the rest of the line.
01435                         while ((a_in) && (ch != '\n')) {
01436                                 a_in.get(ch);
01437                         }
01438                         return;
01439                 }
01440 
01441                 a_keyword = estring(a_keyword).lower();
01442 
01443                 // If there is not value, return.
01444                 if (ch == '\n')
01445                         return;
01446 
01447                 // Skip whitespace between keyword and value.
01448                 a_in.get(ch);
01449                 while ((a_in) && ((ch == ' ') || (ch == '\t'))) {
01450                         a_in.get(ch);
01451                 }
01452                 if (!a_in)
01453                         return;
01454                 a_in.putback(ch);
01455                 if (!a_in)
01456                         return;
01457 
01458                 // Read value
01459                 a_in.get(ch);
01460                 while ((a_in) && (ch != '\n')) {
01461                         TRY_nomem(a_value += ch);
01462                         a_in.get(ch);
01463                 }
01464                 return;
01465         }
01466 }
01467 
01468 /** Given a path, strip off the basename -- like the dirname UNIX command */
01469 const std::string parse_dirname(const std::string& a_path)
01470 {
01471         std::string rpath;
01472 
01473         TRY_nomem(rpath = a_path);
01474 
01475         if (rpath.find_last_of('/') != std::string::npos)
01476                 rpath.erase(rpath.find_last_of('/'));
01477         
01478         if (rpath.size() == 0) {
01479                 TRY_nomem(rpath = "./");
01480                 return(rpath);
01481         }
01482 
01483         if (rpath[rpath.size()-1] != '/') {
01484                 TRY_nomem(rpath += '/');
01485         }
01486 
01487         return(rpath);
01488 }
01489 
01490 /** Given a path, strip off the directory -- like the basename UNIX commane */
01491 const std::string parse_basename(const std::string& a_path)
01492 {
01493         std::string es;
01494         std::string rpath;
01495 
01496         TRY_nomem(rpath = a_path);
01497 
01498         if (rpath.find_last_of('/') != std::string::npos)
01499                 rpath.erase(0,rpath.find_last_of('/'));
01500 
01501         return(rpath);
01502 }
01503 
01504 //----------------------------------------------------------------------------
01505 
01506 /** C'tor */
01507 global_parser::global_parser(
01508         const std::string& a_path,
01509         uint16& a_line,
01510         std::istream& a_in
01511         )
01512 {
01513         m_in = &a_in;
01514         m_path = &a_path;
01515         m_line = &a_line;
01516 
01517         parse();
01518 }
01519 
01520 /** Generate a string showing the current location within a configuration file
01521  * of the parser */
01522 const std::string global_parser::location(void)
01523 {
01524         std::string es;
01525 
01526         TRY_nomem(es = "At ");
01527         TRY_nomem(es += (*m_path));
01528         TRY_nomem(es += "[");
01529         TRY_nomem(es += estring((*m_line)));
01530         TRY_nomem(es += "]");
01531 
01532         return(es);
01533 }
01534 
01535 /** Read a configuration file, used by the "include" command */
01536 void global_parser::read_config(const std::string& a_path)
01537 {
01538         std::string es;
01539         std::ifstream in;
01540         uint16 line = 0;
01541 
01542         in.open(a_path.c_str());
01543         if (!in.is_open()) {
01544                 TRY_nomem(es = "Could not open configuraiton file: \"");
01545                 TRY_nomem(es += a_path);
01546                 TRY_nomem(es += "\"");
01547                 throw(ERROR(errno,es));
01548         }
01549 
01550         global_parser(a_path, line, in);
01551 
01552         in.close();
01553         config.m_configs_read++;
01554 }
01555 
01556 /** Read a job configuration file, used by the "include-job" command */
01557 void global_parser::read_job(const std::string& a_path, job& a_job)
01558 {
01559         std::string es;
01560         std::ifstream in;
01561         uint16 line = 0;
01562 
01563         in.open(a_path.c_str());
01564         if (!in.is_open()) {
01565                 TRY_nomem(es = "Could not open job file: \"");
01566                 TRY_nomem(es += a_path);
01567                 TRY_nomem(es += "\"");
01568                 throw(ERROR(errno,es));
01569         }
01570 
01571         job_parser(&a_job, a_path, line, in, "", false);
01572 
01573         TRY_nomem(a_job.config_path = *m_path);
01574         TRY_nomem(a_job.config_line = *m_line);
01575         a_job.check();
01576 
01577         in.close();
01578 }
01579 
01580 /** Global context parser */
01581 void global_parser::parse(void)
01582 {
01583         std::string es;
01584         std::string keyword;
01585         std::string value;
01586 
01587         while ((*m_in)) {
01588                 (*m_line)++;
01589                 
01590                 try {
01591                         parse_line((*m_in), keyword, value);
01592                 }
01593                 catch(error e) {
01594                         e.push_back(ERROR_INSTANCE(location()));
01595                         throw(e);
01596                 }
01597                 catch(...) {
01598                         error e = err_unknown;
01599 
01600                         e.push_back(ERROR_INSTANCE(location()));
01601                         throw(e);
01602                 }
01603 
01604                 if (keyword.size() == 0)
01605                         continue;
01606                 if (keyword[0] == '#')
01607                         continue;
01608 
01609                 try {
01610                         if (keyword == "<default>") {
01611                                 parse_default(value);
01612                         }
01613                         else if (keyword == "include") {
01614                                 parse_include(value);
01615                         }
01616                         else if (keyword == "include-job") {
01617                                 parse_include_job(value);
01618                         }
01619                         else if (keyword == "<job>") {
01620                                 parse_job(value);
01621                         }
01622                         else if (keyword == "link-catalog-dir") {
01623                                 parse_link_catalog_dir(value);
01624                         }
01625                         else if (keyword == "log-dir") {
01626                                 parse_log_dir(value);
01627                         }
01628                         else if (keyword == "logging-level") {
01629                                 parse_logging_level(value);
01630                         }
01631                         else if (keyword == "rsync-local-path") {
01632                                 parse_rsync_local_path(value);
01633                         }
01634                         else if (keyword == "rsync-parallel") {
01635                                 parse_rsync_parallel(value);
01636                         }
01637                         else if (keyword == "io-poll-interval") {
01638                                 parse_io_poll_interval(value);
01639                         }
01640                         else if (keyword == "timestamp-resolution") {
01641                                 parse_timestamp_resolution(value);
01642                         }
01643                         else if (keyword == "vault") {
01644                                 try {
01645                                         parse_vault(value);
01646                                 }
01647                                 catch(error e) {
01648                                         std::cerr << e;
01649                                 }
01650                                 catch(...) {
01651                                         throw(err_unknown);
01652                                 }
01653                         }
01654                         else if (keyword == "vault-overflow-behavior") {
01655                                 parse_vault_overflow_behavior(value);
01656                         }
01657                         else if (keyword == "vault-overflow-blocks") {
01658                                 parse_vault_overflow_blocks(value);
01659                         }
01660                         else if (keyword == "vault-overflow-inodes") {
01661                                 parse_vault_overflow_inodes(value);
01662                         }
01663                         else if (keyword == "vault-selection-behavior") {
01664                                 parse_vault_selection_behavior(value);
01665                         }
01666                         else {
01667                                 error e(0);
01668 
01669                                 TRY_nomem(es = "Unknown command in global context: \"");
01670                                 TRY_nomem(es += keyword);
01671                                 TRY_nomem(es += "\"");
01672                                 throw(ERROR(0,es));
01673                         }
01674                 }
01675                 catch(error e) {
01676                         e.push_back(ERROR_INSTANCE(location()));
01677                         throw(e);
01678                 }
01679                 catch(...) {
01680                         error e = err_unknown;
01681 
01682                         e.push_back(ERROR_INSTANCE(location()));
01683                         throw(e);
01684                 }
01685         }
01686 }
01687 
01688 /** Parse a default job context */
01689 void global_parser::parse_default(const std::string& a_value)
01690 {
01691         std::string delimiter;
01692         std::string default_config_path;
01693         uint16 default_config_line;
01694         job* jobPtr = &config.m_default_job;
01695 
01696         config.m_default_job.clear();
01697 
01698         TRY_nomem(default_config_path = *m_path);
01699         TRY_nomem(default_config_line = *m_line);
01700         TRY_nomem(delimiter = "</default>");
01701 
01702         job_parser(jobPtr, *m_path, *m_line, *m_in, delimiter, true);
01703 
01704         TRY_nomem(jobPtr->default_config_path = default_config_path);
01705         jobPtr->default_config_line = default_config_line;
01706 }
01707 
01708 /** Parse an "include" command */
01709 void global_parser::parse_include(const std::string& a_value)
01710 {
01711         std::string es;
01712         directory dir;
01713         directory::const_iterator cdi;
01714         std::string rpath;
01715         std::string ipath;
01716 
01717         if (a_value.size() == 0) {
01718                 TRY_nomem(es = "Invalid include path: \"");
01719                 TRY_nomem(es += a_value);
01720                 TRY_nomem(es += "\"");
01721                 throw(ERROR(0,es));
01722         }
01723 
01724         if (a_value[0] != '/') {
01725                 TRY_nomem(rpath = parse_dirname(*m_path));
01726         }
01727 
01728         TRY_nomem(ipath = rpath + a_value);
01729 
01730         TRY_nomem(es = "No configuration file(s) found: \"");
01731         TRY_nomem(es += a_value);
01732         TRY_nomem(es += "\"");
01733         TRY_instead(dir.path(ipath),es);
01734         if (dir.size() == 0)
01735                 throw(ERROR(0,es));
01736         
01737         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
01738                 if (dir.size() > 1) {
01739                         TRY_nomem(es = "For files found matching: \"");
01740                         TRY_nomem(es += a_value);
01741                         TRY_nomem(es += "\"");
01742                         try {
01743                                 read_config(*cdi);
01744                         }
01745                         catch(error e) {
01746                                 e.push_back(ERROR_INSTANCE(es));
01747                                 throw(e);
01748                         }
01749                         catch(...) {
01750                                 error e = err_unknown;
01751                 
01752                                 e.push_back(ERROR_INSTANCE(es));
01753                                 throw(e);
01754                         }
01755                 }
01756                 else
01757                         read_config(*cdi);
01758         }
01759 }
01760 
01761 /** Parse an "include-job" command */
01762 void global_parser::parse_include_job(const std::string& a_value)
01763 {
01764         std::string es;
01765         directory dir;
01766         directory::const_iterator cdi;
01767         std::string rpath;
01768         std::string ipath;
01769 
01770         if (a_value.size() == 0) {
01771                 TRY_nomem(es = "Invalid include-job path: \"");
01772                 TRY_nomem(es += a_value);
01773                 TRY_nomem(es += "\"");
01774                 throw(ERROR(0,es));
01775         }
01776 
01777         if (a_value[0] != '/') {
01778                 TRY_nomem(rpath = parse_dirname(*m_path));
01779         }
01780 
01781         TRY_nomem(ipath = rpath + a_value);
01782 
01783         TRY_nomem(es = "No job file(s) found: \"");
01784         TRY_nomem(es += a_value);
01785         TRY_nomem(es += "\"");
01786         TRY(dir.path(ipath),es);
01787         if (dir.size() == 0)
01788                 throw(ERROR(0,es));
01789 
01790         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
01791                 job new_job;
01792                 
01793                 new_job = config.m_default_job;
01794                 
01795                 if (dir.size() > 1) {
01796                         TRY_nomem(es = "For files found matching: \"");
01797                         TRY_nomem(es += a_value);
01798                         TRY_nomem(es += "\"");
01799                         try {
01800                                 read_job(*cdi, new_job);
01801                         }
01802                         catch(error e) {
01803                                 e.push_back(ERROR_INSTANCE(es));
01804                                 throw(e);
01805                         }
01806                         catch(...) {
01807                                 error e = err_unknown;
01808 
01809                                 e.push_back(ERROR_INSTANCE(es));
01810                                 throw(e);
01811                         }
01812                 }
01813                 else
01814                         read_job(*cdi, new_job);
01815                 TRY_nomem(config.m_jobs.push_back(new_job));
01816         }
01817 }
01818 
01819 /** Parse a job context */
01820 void global_parser::parse_job(const std::string& a_value)
01821 {
01822         std::string delimiter;
01823         std::string config_path;
01824         uint16 config_line;
01825         job new_job;
01826         
01827         TRY_nomem(config_path = *m_path);
01828         config_line = *m_line;
01829         new_job = config.m_default_job;
01830 
01831         TRY_nomem(new_job.config_path = config_path);
01832         new_job.config_line = config_line;
01833         TRY_nomem(delimiter = "</job>");
01834 
01835         job_parser(&new_job, *m_path, *m_line, *m_in, delimiter, false);
01836 
01837         new_job.check();
01838 
01839         TRY_nomem(config.m_jobs.push_back(new_job));
01840 }
01841 
01842 /** Parse a "link-catalog-dir" command */
01843 void global_parser::parse_link_catalog_dir(const std::string& a_value)
01844 {
01845         std::string es;
01846         subdirectory subdir;
01847         
01848         TRY_nomem(es = "Invalid link-catalog-dir path: \"");
01849         TRY_nomem(es += a_value);
01850         TRY_nomem(es += "\"");
01851         TRY_instead(subdir.path(a_value,"*"),es);
01852         TRY_nomem(config.m_link_catalog_dir = a_value);
01853 }
01854 
01855 /** Parse a "log-dir" command */
01856 void global_parser::parse_log_dir(const std::string& a_value)
01857 {
01858         std::string es;
01859         subdirectory subdir;
01860 
01861         TRY_nomem(es = "Invalid log-dir path: \"");
01862         TRY_nomem(es += a_value);
01863         TRY_nomem(es += "\"");
01864         TRY_instead(subdir.path(a_value,"*"),es);
01865         TRY_nomem(config.m_log_dir = a_value);
01866 }
01867 
01868 /** Parse a "loging-level" command */
01869 void global_parser::parse_logging_level(const std::string& a_value)
01870 {
01871         std::string es;
01872         estring str;
01873 
01874         TRY_nomem(es = "Invalid logging-level value: \"");
01875         TRY_nomem(es += a_value);
01876         TRY_nomem(es += "\"");
01877         TRY_nomem(str = estring(a_value).lower());
01878         if (str == "manager") {
01879                 config.m_logging_level = configuration_manager::logging_manager;
01880         }
01881         else if (str == "child") {
01882                 config.m_logging_level = configuration_manager::logging_child;
01883         }
01884         else if (str == "rsync") {
01885                 config.m_logging_level = configuration_manager::logging_rsync;
01886         }
01887         else {
01888                 throw(ERROR(0,es));
01889         }
01890 }
01891 
01892 /** Parse an "rsync-local-path" command */
01893 void global_parser::parse_rsync_local_path(const std::string& a_value)
01894 {
01895         std::string es;
01896         filestatus fstat;
01897 
01898         TRY_nomem(es = "Invalid rsync-local-path value: \"");
01899         TRY_nomem(es += a_value);
01900         TRY_nomem(es += "\"");
01901         TRY_instead(fstat.path(a_value),es);
01902         TRY_nomem(config.m_rsync_local_path = a_value);
01903 }
01904 
01905 /** Parse an "rsync-parallel" command */
01906 void global_parser::parse_rsync_parallel(const std::string& a_value)
01907 {
01908         std::string es;
01909         uint16 num;
01910 
01911         TRY_nomem(es = "Invalid rsync-parallel value: \"");
01912         TRY_nomem(es += a_value);
01913         TRY_nomem(es += "\"");
01914         TRY_instead(num = estring(a_value),es);
01915         if (num == 0)
01916                 throw(ERROR(0,es));
01917         config.m_rsync_parallel = num;
01918 }
01919 
01920 /** Parse "io-poll-interval" command */
01921 void global_parser::parse_io_poll_interval(const std::string& a_value)
01922 {
01923         std::string es;
01924         uint16 num;
01925 
01926         TRY_nomem(es = "Invalid io-poll-interval value: \"");
01927         TRY_nomem(es += a_value);
01928         TRY_nomem(es += "\"");
01929         TRY_instead(num = estring(a_value),es);
01930         if (num == 0)
01931                 throw(ERROR(0,es));
01932         config.m_io_poll_interval = num;
01933 }
01934 
01935 /** Parse a "timestamp-resolution" command */
01936 void global_parser::parse_timestamp_resolution(const std::string& a_value)
01937 {
01938         std::string es;
01939         estring str;
01940 
01941         TRY_nomem(es = "Invalid timestamp-resolution: \"");
01942         TRY_nomem(es += a_value);
01943         TRY_nomem(es += "\"");
01944         TRY(str = estring(a_value).lower(),es);
01945         if (str == "year") {
01946                 config.m_timestamp.resolution(timestamp::resolution_year);
01947         }
01948         else if (str == "month") {
01949                 config.m_timestamp.resolution(timestamp::resolution_month);
01950         }
01951         else if (str == "day") {
01952                 config.m_timestamp.resolution(timestamp::resolution_day);
01953         }
01954         else if (str == "hour") {
01955                 config.m_timestamp.resolution(timestamp::resolution_hour);
01956         }
01957         else if (str == "minute") {
01958                 config.m_timestamp.resolution(timestamp::resolution_minute);
01959         }
01960         else if (str == "second") {
01961                 config.m_timestamp.resolution(timestamp::resolution_second);
01962         }
01963         else {
01964                 throw(ERROR(0,es));
01965         }
01966 }
01967 
01968 /** Parse a "vault" command */
01969 void global_parser::parse_vault(const std::string& a_value)
01970 {
01971         std::string es;
01972         directory dir;
01973         directory::const_iterator cdi;
01974         filestatus fstat;
01975         subdirectory subdir;
01976 
01977         TRY_nomem(es = "Invalid vault path: \"");
01978         TRY_nomem(es += a_value);
01979         TRY_nomem(es += "\"");
01980 
01981         if (a_value.size() == 0)
01982                 throw(ERROR(0,es));
01983 
01984         TRY_instead(dir.path(a_value),es);
01985 
01986         if (dir.size() == 0) {
01987                 TRY_instead(subdir.path(a_value,"*"),es);
01988                 TRY_nomem(config.m_vaults.push_back(a_value));
01989                 return;
01990         }
01991 
01992         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
01993                 try {
01994                         subdir.path(*cdi,"*");
01995                 }
01996                 catch(error e) {
01997                         e.clear_stack();
01998 
01999                         if (dir.size() > 1) {
02000                                 TRY_nomem(es = "Invalid vault path: \"");
02001                                 TRY_nomem(es += *cdi);
02002                                 TRY_nomem(es += "\"");
02003                                 e.push_back(es);
02004 
02005                                 TRY_nomem(es = "For paths found matching: \"");
02006                                 TRY_nomem(es += a_value);
02007                                 TRY_nomem(es += "\"");
02008                                 e.push_back(es);
02009                         }
02010                         else
02011                                 e.push_back(es);
02012 
02013                         throw(e);
02014                 }
02015                 catch(...) {
02016                         if (errno == ENOMEM) {
02017                                 throw(err_nomem);
02018                         }
02019                         error e = err_unknown;
02020 
02021                         if (dir.size() > 1) {
02022                                 TRY_nomem(es = "Invalid vault path: \"");
02023                                 TRY_nomem(es += *cdi);
02024                                 TRY_nomem(es += "\"");
02025                                 e.push_back(es);
02026 
02027                                 TRY_nomem(es = "For paths found matching: \"");
02028                                 TRY_nomem(es += a_value);
02029                                 TRY_nomem(es += "\"");
02030                                 e.push_back(es);
02031                         }
02032                         else
02033                                 e.push_back(es);
02034 
02035                         throw(e);
02036                 }
02037                 TRY_nomem(config.m_vaults.push_back(*cdi));
02038         }
02039 }
02040 
02041 /** Parse a "vault-overflow-behavior" command */
02042 void global_parser::parse_vault_overflow_behavior(const std::string& a_value)
02043 {
02044         std::string es;
02045         estring str;
02046 
02047         TRY_nomem(es = "Invalid vault-overflow-behavior value: \"");
02048         TRY_nomem(es += a_value);
02049         TRY_nomem(es += "\"");
02050         TRY(str = estring(a_value).lower(),es);
02051         if (str == "quit") {
02052                 config.m_vault_overflow_behavior = configuration_manager::overflow_quit;
02053         }
02054         else if (str == "delete-oldest") {
02055                 config.m_vault_overflow_behavior = configuration_manager::overflow_delete_oldest;
02056         }
02057         else if (str == "delete-until-free") {
02058                 config.m_vault_overflow_behavior = configuration_manager::overflow_delete_until_free;
02059         }
02060         else {
02061                 throw(ERROR(0,es));
02062         }
02063 }
02064 
02065 /** Parse a "vault-overflow-blocks" command */
02066 void global_parser::parse_vault_overflow_blocks(const std::string& a_value)
02067 {
02068         std::string es;
02069         uint16 num;
02070 
02071         TRY_nomem(es = "Invalid vault-overflow-blocks value: \"");
02072         TRY_nomem(es += a_value);
02073         TRY_nomem(es += "\"");
02074         TRY(num = estring(a_value),es);
02075         if (num > 50)
02076                 throw(ERROR(0,es));
02077         config.m_vault_overflow_blocks = num;
02078 }
02079 
02080 /** Parse a "vault-overflow-inodes" command */
02081 void global_parser::parse_vault_overflow_inodes(const std::string& a_value)
02082 {
02083         std::string es;
02084         uint16 num;
02085 
02086         TRY_nomem(es = "Invalid vault-overflow-inodes value: \"");
02087         TRY_nomem(es += a_value);
02088         TRY_nomem(es += "\"");
02089         TRY(num = estring(a_value),es);
02090         if (num > 50)
02091                 throw(ERROR(0,es));
02092         config.m_vault_overflow_inodes = num;
02093 }
02094 
02095 /** Parse a "vault-selection-behavior" command */
02096 void global_parser::parse_vault_selection_behavior(const std::string& a_value)
02097 {
02098         std::string es;
02099         estring str;
02100 
02101         TRY_nomem(es = "Invalid vault-selection-behavior value: \"");
02102         TRY_nomem(es += a_value);
02103         TRY_nomem(es += "\"");
02104         TRY(str = estring(a_value).lower(),es);
02105         if (str == "max-free") {
02106                 config.m_vault_selection_behavior = configuration_manager::selection_max_free;
02107         }
02108         else if (str == "round-robin") {
02109                 config.m_vault_selection_behavior = configuration_manager::selection_round_robin;
02110         }
02111         else {
02112                 throw(ERROR(0,es));
02113         }
02114 }
02115 
02116 //----------------------------------------------------------------------------
02117 
02118 /** C'tor */
02119 job_parser::job_parser(
02120         job * a_job,
02121         const std::string& a_path,
02122         uint16& a_line,
02123         std::istream& a_in,
02124         const std::string& a_block_delimiter,
02125         const bool a_default_context = false
02126         )
02127 {
02128         m_job = a_job;
02129         m_in = &a_in;
02130         m_path = &a_path;
02131         m_line = &a_line;
02132         m_delimiter = &a_block_delimiter;
02133         m_default_context = a_default_context;
02134 
02135         parse();
02136 }
02137 
02138 /** Construct a string with the current location of the parser in a
02139  * configuration file */
02140 const std::string job_parser::location(void)
02141 {
02142         std::string es;
02143 
02144         TRY_nomem(es = "At ");
02145         TRY_nomem(es += (*m_path));
02146         TRY_nomem(es += "[");
02147         TRY_nomem(es += estring((*m_line)));
02148         TRY_nomem(es += "]");
02149 
02150         return(es);
02151 }
02152 
02153 /** Read a job configuration file, used by the "include" command */
02154 void job_parser::read_job(const std::string& a_path)
02155 {
02156         std::string es;
02157         std::ifstream in;
02158         uint16 line = 0;
02159 
02160         in.open(a_path.c_str());
02161         if (!in.is_open()) {
02162                 TRY_nomem(es = "Could not open job file: \"");
02163                 TRY_nomem(es += a_path);
02164                 TRY_nomem(es += "\"");
02165                 throw(ERROR(errno,es));
02166         }
02167 
02168         job_parser(m_job, a_path, line, in, "", m_default_context);
02169 
02170         in.close();
02171 }
02172 
02173 /** Job context parser */
02174 void job_parser::parse(void)
02175 {
02176         std::string es;
02177         std::string keyword;
02178         std::string value;
02179 
02180         while ((*m_in)) {
02181                 (*m_line)++;
02182                 
02183                 try {
02184                         parse_line((*m_in), keyword, value);
02185                 }
02186                 catch(error e) {
02187                         e.push_back(ERROR_INSTANCE(location()));
02188                         throw(e);
02189                 }
02190                 catch(...) {
02191                         error e = err_unknown;
02192 
02193                         e.push_back(ERROR_INSTANCE(location()));
02194                         throw(e);
02195                 }
02196 
02197                 if (!(*m_in) && (m_delimiter->size() != 0)) {
02198                         TRY_nomem(es = "Unexpected end of file, expected \"");
02199                         TRY_nomem(es += *m_delimiter);
02200                         TRY_nomem(es += "\" here");
02201                         throw(ERROR(errno,es));
02202                 }
02203 
02204                 if (keyword.size() == 0)
02205                         continue;
02206                 if (keyword[0] == '#')
02207                         continue;
02208 
02209                 try {
02210                         if (keyword == *m_delimiter) {
02211                                 return;
02212                         }
02213                         else if (keyword == "archive-path") {
02214                                 parse_archive_path(value);
02215                         }
02216                         else if (keyword == "clear") {
02217                                 parse_clear(value);
02218                         }
02219                         else if (keyword == "exclude-from") {
02220                                 parse_exclude_from(value);
02221                         }
02222                         else if (keyword == "include-from") {
02223                                 parse_include_from(value);
02224                         }
02225                         else if (keyword == "groupname") {
02226                                 parse_groupname(value);
02227                         }
02228                         else if (keyword == "hostname") {
02229                                 parse_hostname(value);
02230                         }
02231                         else if (keyword == "include") {
02232                                 parse_include(value);
02233                         }
02234                         else if ((keyword == "jobname") && (*m_delimiter == "</job>")) {
02235                                 parse_jobname(value);
02236                         }
02237                         else if (keyword == "path") {
02238                                 parse_path(value);
02239                         }
02240                         else if (keyword == "rsync-behavior") {
02241                                 parse_rsync_behavior(value);
02242                         }
02243                         else if (keyword == "rsync-connection-type") {
02244                                 parse_rsync_connection_type(value);
02245                         }
02246                         else if (keyword == "rsync-hardlink") {
02247                                 parse_rsync_hardlink(value);
02248                         }
02249                         else if (keyword == "rsync-options") {
02250                                 parse_rsync_options(value);
02251                         }
02252                         else if (keyword == "<rsync-options>") {
02253                                 parse_rsync_options_context(value);
02254                         }
02255                         else if (keyword == "rsync-remote-user") {
02256                                 parse_rsync_remote_user(value);
02257                         }
02258                         else if (keyword == "rsync-remote-path") {
02259                                 parse_rsync_remote_path(value);
02260                         }
02261                         else if (keyword == "rsync-remote-port") {
02262                                 parse_rsync_remote_port(value);
02263                         }
02264                         else if (keyword == "rsync-remote-module") {
02265                                 parse_rsync_remote_module(value);
02266                         }
02267                         else if (keyword == "rsync-retry-count") {
02268                                 parse_rsync_retry_count(value);
02269                         }
02270                         else if (keyword == "rsync-timeout") {
02271                                 parse_rsync_timeout(value);
02272                         }
02273                         else {
02274                                 error e(0);
02275 
02276                                 TRY_nomem(es = "Unknown command in ");
02277                                 if (m_default_context) {
02278                                         TRY_nomem(es += "default");
02279                                 }
02280                                 else {
02281                                         TRY_nomem(es += "job");
02282                                 }
02283                                 TRY_nomem(es += " context: \"");
02284                                 TRY_nomem(es += keyword);
02285                                 TRY_nomem(es += "\"");
02286                                 throw(ERROR(0,es));
02287                         }
02288                 }
02289                 catch(error e) {
02290                         if ((*m_delimiter).size() == 0)
02291                                 e.push_back(ERROR_INSTANCE(location()));
02292                         throw(e);
02293                 }
02294                 catch(...) {
02295                         error e = err_unknown;
02296 
02297                         e.push_back(ERROR_INSTANCE(location()));
02298                         throw(e);
02299                 }
02300         }
02301 }
02302 
02303 /** Parse an "archive-path" command */
02304 void job_parser::parse_archive_path(const std::string& a_value)
02305 {
02306         std::string es;
02307 
02308         TRY_nomem(es = "Invalid archive-path value");
02309         TRY((*m_job).archive_path = a_value,es);
02310 }
02311 
02312 /** Parse a "clear" command */
02313 void job_parser::parse_clear(const std::string& a_value)
02314 {
02315         std::string es;
02316         estring str;
02317 
02318         TRY_nomem(es = "Invalid clear value: \"");
02319         TRY_nomem(es += a_value);
02320         TRY_nomem(es += "\"");
02321         TRY(str = estring(a_value).lower(),es);
02322         if (str == "archive-path") {
02323                 m_job->archive_path.clear();
02324         }
02325         else if (str == "exclude-from") {
02326                 m_job->excludes.clear();
02327         }
02328         else if (str == "groupname") {
02329                 m_job->groupname.erase();
02330         }
02331         else if (str == "hostname") {
02332                 m_job->hostname.erase();
02333         }
02334         else if (str == "include-from") {
02335                 m_job->includes.clear();
02336         }
02337         else if ((str == "jobname") && (*m_delimiter == "</job>")) {
02338                 m_job->jobname.erase();
02339         }
02340         else if (str == "paths") {
02341                 m_job->paths.clear();
02342         }
02343         else if (str == "rsync-behavior") {
02344                 m_job->rsync_behavior.clear();
02345         }
02346         else if (str == "rsync-options") {
02347                 m_job->rsync_options.erase();
02348         }
02349         else if (str == "rsync-remote-user") {
02350                 m_job->rsync_remote_user.erase();
02351         }
02352         else if (str == "rsync-remotr-port") {
02353                 m_job->rsync_remote_port = 0;
02354         }
02355         else if (str == "rsync-remote-module") {
02356                 m_job->rsync_remote_module.erase();
02357         }
02358         else if (str == "rsync-remote-path") {
02359                 m_job->rsync_remote_path.erase();
02360         }
02361         else {
02362                 throw(ERROR(0,es));
02363         }
02364 }
02365 
02366 /** Parse an "exclude-from" command */
02367 void job_parser::parse_exclude_from(const std::string& a_value)
02368 {
02369         std::string es;
02370         directory dir;
02371         directory::const_iterator cdi;
02372         std::string rpath;
02373         std::string ipath;
02374 
02375         if (a_value.size() == 0) {
02376                 TRY_nomem(es = "Invalid exclude-from path: \"");
02377                 TRY_nomem(es += a_value);
02378                 TRY_nomem(es += "\"");
02379                 throw(ERROR(0,es));
02380         }
02381 
02382         if (a_value[0] != '/') {
02383                 TRY_nomem(rpath = parse_dirname(*m_path));
02384         }
02385 
02386         TRY_nomem(ipath = rpath + a_value);
02387 
02388         TRY_nomem(es = "No exclude-from file(s) found: \"");
02389         TRY_nomem(es += a_value);
02390         TRY_nomem(es += "\"");
02391         TRY(dir.path(ipath),es);
02392         if (dir.size() == 0)
02393                 throw(ERROR(0,es));
02394         
02395         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
02396                 TRY_nomem((*m_job).excludes.push_back(*cdi));
02397         }
02398 }
02399 
02400 /** Parse an "include-from" command */
02401 void job_parser::parse_include_from(const std::string& a_value)
02402 {
02403         std::string es;
02404         directory dir;
02405         directory::const_iterator cdi;
02406         std::string rpath;
02407         std::string ipath;
02408 
02409         if (a_value.size() == 0) {
02410                 TRY_nomem(es = "Invalid include-from path: \"");
02411                 TRY_nomem(es += a_value);
02412                 TRY_nomem(es += "\"");
02413                 throw(ERROR(0,es));
02414         }
02415 
02416         if (a_value[0] != '/') {
02417                 TRY_nomem(rpath = parse_dirname(*m_path));
02418         }
02419 
02420         TRY_nomem(ipath = rpath + a_value);
02421 
02422         TRY_nomem(es = "No include-from file(s) found: \"");
02423         TRY_nomem(es += a_value);
02424         TRY_nomem(es += "\"");
02425         TRY(dir.path(ipath),es);
02426         if (dir.size() == 0)
02427                 throw(ERROR(0,es));
02428         
02429         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
02430                 TRY_nomem((*m_job).includes.push_back(*cdi));
02431         }
02432 }
02433 
02434 /** Parse a "groupname" command */
02435 void job_parser::parse_groupname(const std::string& a_value)
02436 {
02437         TRY_nomem((*m_job).groupname = a_value);
02438 }
02439 
02440 /** Parse a "hostname" command */
02441 void job_parser::parse_hostname(const std::string& a_value)
02442 {
02443         TRY_nomem((*m_job).hostname = a_value);
02444 }
02445 
02446 /** Parse an "include" command */
02447 void job_parser::parse_include(const std::string& a_value)
02448 {
02449         std::string es;
02450         directory dir;
02451         directory::const_iterator cdi;
02452         std::string rpath;
02453         std::string ipath;
02454 
02455         if (a_value.size() == 0) {
02456                 TRY_nomem(es = "Invalid include path: \"");
02457                 TRY_nomem(es += a_value);
02458                 TRY_nomem(es += "\"");
02459                 throw(ERROR(0,es));
02460         }
02461 
02462         if (a_value[0] != '/') {
02463                 TRY_nomem(rpath = parse_dirname(*m_path));
02464         }
02465 
02466         TRY_nomem(ipath = rpath + a_value);
02467 
02468         TRY_nomem(es = "No configuration file(s) found: \"");
02469         TRY_nomem(es += a_value);
02470         TRY_nomem(es += "\"");
02471         TRY_instead(dir.path(ipath),es);
02472         if (dir.size() == 0)
02473                 throw(ERROR(0,es));
02474         
02475         for (cdi = dir.begin(); cdi != dir.end(); cdi++) {
02476                 if (dir.size() > 1) {
02477                         TRY_nomem(es = "For files found matching: \"");
02478                         TRY_nomem(es += a_value);
02479                         TRY_nomem(es += "\"");
02480                         try {
02481                                 read_job(*cdi);
02482                         }
02483                         catch(error e) {
02484                                 e.push_back(ERROR_INSTANCE(es));
02485                                 throw(e);
02486                         }
02487                         catch(...) {
02488                                 error e = err_unknown;
02489 
02490                                 e.push_back(ERROR_INSTANCE(es));
02491                                 throw(e);
02492                         }
02493                 }
02494                 else
02495                         read_job(*cdi);
02496         }
02497 }
02498 
02499 /** Parse a "jobname" command */
02500 void job_parser::parse_jobname(const std::string& a_value)
02501 {
02502         TRY_nomem((*m_job).jobname = a_value);
02503 }
02504 
02505 /** Parse a "path" command */
02506 void job_parser::parse_path(const std::string& a_value)
02507 {
02508         TRY_nomem((*m_job).paths.push_back(a_value));
02509 }
02510 
02511 /** Parse an "rsync-behavior" command */
02512 void job_parser::parse_rsync_behavior(const std::string& a_value)
02513 {
02514         std::string es;
02515         estring str;
02516 
02517         TRY_nomem(es = "Invalid rsync-behavior value: \"");
02518         TRY_nomem(es += a_value);
02519         TRY_nomem(es += "\"");
02520         TRY(str = estring(a_value).lower(),es);
02521         if (str == "clear") {
02522                 (*m_job).rsync_behavior.clear();
02523         }
02524         else if (str == "reset") {
02525                 (*m_job).rsync_behavior.reset();
02526         }
02527         else
02528                 (*m_job).rsync_behavior.assign(a_value);
02529 }
02530 
02531 /** Parse an "rsync-connection-type" command */
02532 void job_parser::parse_rsync_connection_type(const std::string& a_value)
02533 {
02534         std::string es;
02535         estring str;
02536 
02537         TRY_nomem(es = "Invalid rsync-connection-type value: \"");
02538         TRY_nomem(es += a_value);
02539         TRY_nomem(es += "\"");
02540         TRY(str = estring(a_value).lower(),es);
02541         if (str == "local") {
02542                 (*m_job).rsync_connection = job::connection_local;
02543         }
02544         else if (str == "remote") {
02545                 (*m_job).rsync_connection = job::connection_remote;
02546         }
02547         else if (str == "server") {
02548                 (*m_job).rsync_connection = job::connection_server;
02549         }
02550         else {
02551                 throw(ERROR(0,es));
02552         }
02553 }
02554 
02555 /** Parse an "rsync-hardlink" command */
02556 void job_parser::parse_rsync_hardlink(const std::string& a_value)
02557 {
02558         std::string es;
02559         estring str;
02560 
02561         TRY_nomem(es = "Invalid rsync-hardlink value: \"");
02562         TRY_nomem(es += a_value);
02563         TRY_nomem(es += "\"");
02564         TRY(str = estring(a_value).lower(),es);
02565         if (
02566                    (str == "y")
02567                 || (str == "yes")
02568                 || (str == "t")
02569                 || (str == "true")
02570                 || (str == "1")
02571                 || (str == "on")
02572                 ) {
02573                 (*m_job).rsync_hardlink = true;
02574         }
02575         else if (
02576                    (str == "n")
02577                 || (str == "no")
02578                 || (str == "f")
02579                 || (str == "false")
02580                 || (str == "0")
02581                 || (str == "off")
02582                 ) {
02583                 (*m_job).rsync_hardlink = false;
02584         }
02585         else {
02586                 throw(ERROR(0,es));
02587         }
02588 }
02589 
02590 /** Parse an "rsync-options" command */
02591 void job_parser::parse_rsync_options(const std::string& a_value)
02592 {
02593         if (*m_delimiter == "</default>") {
02594                 TRY_nomem((*m_job).rsync_options = a_value);
02595         }
02596         else {
02597                 if ((*m_job).rsync_options.size() != 0) {
02598                         TRY_nomem((*m_job).rsync_options += " ");
02599                 }
02600                 TRY_nomem((*m_job).rsync_options += a_value);
02601         }
02602 }
02603 
02604 /** Parse an rsync-options context */
02605 void job_parser::parse_rsync_options_context(const std::string& a_value)
02606 {
02607         std::string es;
02608         std::string value;
02609         std::string rsync_options;
02610         char ch;
02611 
02612         while ((*m_in)) {
02613                 (*m_line)++;
02614 
02615                 value.erase();
02616 
02617                 (*m_in).get(ch);
02618                 while ((ch == ' ') || (ch == '\t'))
02619                         (*m_in).get(ch);
02620                 while (((*m_in)) && (ch != '\n')) {
02621                         TRY_nomem(value += ch);
02622                         (*m_in).get(ch);
02623                 }
02624 
02625                 if ((!(*m_in)) && (value != "</rsync-options>")) {
02626                         TRY_nomem(es = "Unexpected end of file, expected ");
02627                         TRY_nomem(es += "\"</rsync-options>\" here");
02628                         throw(ERROR(errno,es));
02629                 }
02630 
02631                 if (value.size() == 0)
02632                         continue;
02633                 if (value[0] == '#')
02634                         continue;
02635 
02636                 if (value == "</rsync-options>") {
02637                         parse_rsync_options(rsync_options);
02638                         return;
02639                 }
02640 
02641                 if (rsync_options.size() != 0) {
02642                         TRY_nomem(rsync_options += ' ');
02643                 }
02644                 TRY_nomem(rsync_options += value);
02645         }
02646 }
02647 
02648 /** Parse a "remote-user" command */
02649 void job_parser::parse_rsync_remote_user(const std::string& a_value)
02650 {
02651         TRY_nomem((*m_job).rsync_remote_user = a_value);
02652 }
02653 
02654 /** Parse an "rsync-remote-path" command */
02655 void job_parser::parse_rsync_remote_path(const std::string& a_value)
02656 {
02657         TRY_nomem((*m_job).rsync_remote_path = a_value);
02658 }
02659 
02660 /** Parse an "rsync-remote-port" command */
02661 void job_parser::parse_rsync_remote_port(const std::string& a_value)
02662 {
02663         std::string es;
02664         estring str;
02665         uint16 num;
02666 
02667         TRY_nomem(es = "Invalid rsync-remote-port value: \"");
02668         TRY_nomem(es += a_value);
02669         TRY_nomem(es += "\"");
02670         TRY_nomem(str = a_value);
02671         TRY(num = str,es);
02672         (*m_job).rsync_remote_port = num;
02673 }
02674 
02675 /** Parse an "rsync-remote-module" command */
02676 void job_parser::parse_rsync_remote_module(const std::string& a_value)
02677 {
02678         TRY_nomem((*m_job).rsync_remote_module = a_value);
02679 }
02680 
02681 /** Parse an "rsync-retry-count" command */
02682 void job_parser::parse_rsync_retry_count(const std::string& a_value)
02683 {
02684         std::string es;
02685         estring str;
02686         uint16 num;
02687 
02688         TRY_nomem(es = "Invalid rsync-retry-count value: \"");
02689         TRY_nomem(es += a_value);
02690         TRY_nomem(es += "\"");
02691         TRY_nomem(str = a_value);
02692         TRY(num = str,es);
02693         (*m_job).rsync_retry_count = num;
02694 }
02695 
02696 /** Parse an "rsync-timeout" command */
02697 void job_parser::parse_rsync_timeout(const std::string& a_value)
02698 {
02699         std::string es;
02700         estring str;
02701         uint16 num;
02702 
02703         TRY_nomem(es = "Invalid rsync-timeout value: \"");
02704         TRY_nomem(es += a_value);
02705         TRY_nomem(es += "\"");
02706         TRY_nomem(str = a_value);
02707         TRY(num = str,es);
02708         (*m_job).rsync_timeout = num;
02709 }
02710 
02711 //----------------------------------------------------------------------------
02712 
02713 /** The global configuration manager instance */
02714 configuration_manager config;
02715 

Generated on Mon Jul 12 12:02:48 2004 for rvm by doxygen 1.3.6