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

Generated on Wed Jun 21 10:50:04 2006 for rvm by  doxygen 1.4.2