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

Generated on Fri Jun 23 16:46:30 2006 for rvm by  doxygen 1.4.2