vaulter.cc

Go to the documentation of this file.
00001 #include "config.h"
00002 
00003 #include <iostream>
00004 #include <vector>
00005 #include <map>
00006 #include <string>
00007 
00008 #include "asserts.h"
00009 #include "error.h"
00010 #include "rmath.h"
00011 #include "fs.h"
00012 #include "rconfig.h"
00013 #include "logger.h"
00014 #include "timer.h"
00015 #include "estat.h"
00016 #include "reporter.h"
00017 #include "strfmt.h"
00018 
00019 #include "vaulter.h"
00020 
00021 /** C'tor */
00022 vault_manager::vault_manager()
00023 {
00024         if (this != &vaulter)
00025                 throw(INTERNAL_ERROR(0,"Attempt to allocate multiple vault managers"));
00026         clear();
00027 }
00028 
00029 /** Clear the vault manager */
00030 void vault_manager::clear(void)
00031 {
00032         m_lock.clear();
00033         m_selected_vault.erase();
00034         m_deleted_archives.clear();
00035         m_da_err = false;
00036         m_initialized = false;
00037 }
00038 
00039 /** Initialize the vault manager */
00040 void vault_manager::init(void)
00041 {
00042         clear();
00043         m_initialized = true;
00044 }
00045 
00046 /** Return the initialized status of the vault manager */
00047 const bool vault_manager::initialized(void) const
00048 {
00049         return(m_initialized);
00050 }
00051 
00052 /** Select a vault
00053 
00054         If an archive of the same timestamp for the given timestamp resolution
00055         already exists on any vault, then that vault is selected.  Otherwise select
00056         a vault according to vault-selection-behavior.
00057 
00058  */
00059 void vault_manager::select(void)
00060 {
00061         std::string es;
00062         std::string ts;
00063         configuration_manager::vaults_type::const_iterator vi;
00064         std::string lockfile;
00065         std::string lstr;
00066 
00067         if (!initialized())
00068                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00069 
00070         TRY_nomem(es = "Could not select a vault");
00071         try {
00072                 // If an archive exists in any vault by the same timestamp as conf.stamp(),
00073                 // then use that vault regardless of the selection behavior.
00074                 for (
00075                         vi = config.vaults().begin(); 
00076                         vi != config.vaults().end(); 
00077                         vi++
00078                         )
00079                 {
00080                         subdirectory subdir;
00081                         subdirectory::iterator sdi;
00082         
00083                         subdir.path(*vi);
00084                         for (sdi = subdir.begin(); sdi != subdir.end(); sdi++) {
00085                                 TRY_nomem(ts = config.timestamp().str());
00086 
00087                                 if (*sdi == ts) {
00088                                         TRY_nomem(lstr = "Existing archive directory found in vault: \"");
00089                                         TRY_nomem(lstr += *vi);
00090                                         TRY_nomem(lstr += "\"\n");
00091                                         logger.write(lstr);
00092 
00093                                         if (config.vault_locking()) {
00094                                                 TRY_nomem(lockfile = *vi);
00095                                                 TRY_nomem(lockfile += "/.rvm_lock");
00096                                                 m_lock.clear();
00097                                                 m_lock.lockfile(lockfile);
00098                                                 if (!m_lock.lock()) {
00099                                                         error e(ENOLCK);
00100                                                         e.push_back(ERROR_INSTANCE(es));
00101                                                         TRY_nomem(es = "Could not lock vault: \"");
00102                                                         TRY_nomem(es += *vi);
00103                                                         TRY_nomem(es += "\"");
00104                                                         e.push_back(ERROR_INSTANCE(es));
00105                                                         if (m_lock.is_locked()) {
00106                                                                 TRY_nomem(es = "Vault is locked by PID: ");
00107                                                                 TRY_nomem(es += estring(m_lock.locked_by()));
00108                                                                 e.push_back(ERROR_INSTANCE(es));
00109                                                         }
00110                                                         throw(e);
00111                                                 }
00112                                         }
00113                                         TRY_nomem(m_selected_vault = *vi);
00114                                         return;
00115                                 }
00116 
00117                                 TRY_nomem(ts += ".incomplete");
00118 
00119                                 if (*sdi == ts) {
00120                                         TRY_nomem(lstr = "Existing archive directory found in vault: \"");
00121                                         TRY_nomem(lstr += *vi);
00122                                         TRY_nomem(lstr += "\"\n");
00123                                         logger.write(lstr);
00124 
00125                                         if (config.vault_locking()) {
00126                                                 TRY_nomem(lockfile = *vi);
00127                                                 TRY_nomem(lockfile += "/.rvm_lock");
00128                                                 m_lock.clear();
00129                                                 m_lock.lockfile(lockfile);
00130                                                 if (!m_lock.lock()) {
00131                                                         error e(ENOLCK);
00132                                                         e.push_back(ERROR_INSTANCE(es));
00133                                                         TRY_nomem(es = "Could not lock vault: \"");
00134                                                         TRY_nomem(es += *vi);
00135                                                         TRY_nomem(es += "\"");
00136                                                         e.push_back(ERROR_INSTANCE(es));
00137                                                         if (m_lock.is_locked()) {
00138                                                                 TRY_nomem(es = "Vault is locked by PID: ");
00139                                                                 TRY_nomem(es += estring(m_lock.locked_by()));
00140                                                                 e.push_back(ERROR_INSTANCE(es));
00141                                                         }
00142                                                         throw(e);
00143                                                 }
00144                                         }
00145                                         TRY_nomem(m_selected_vault = *vi);
00146                                         return;
00147                                 }
00148                         }
00149                 }
00150         
00151                 // If an archive by the same timestamp does not already exist, then select
00152                 // a vault.
00153                 if (config.vault_selection_behavior() 
00154                         == configuration_manager::selection_round_robin)
00155                 {
00156                         std::pair<std::string,std::string> youngest;
00157         
00158                         for (
00159                                 vi = config.vaults().begin();
00160                                 vi != config.vaults().end();
00161                                 vi++
00162                                 )
00163                         {
00164                                 subdirectory subdir;
00165                                 subdirectory::iterator sdi;
00166         
00167                                 if (config.vault_locking()) {
00168                                         TRY_nomem(lockfile = *vi);
00169                                         TRY_nomem(lockfile += "/.rvm_lock");
00170                                         m_lock.clear();
00171                                         m_lock.lockfile(lockfile);
00172                                         if (m_lock.is_locked()) {
00173                                                 std::string lstr;
00174 
00175                                                 TRY_nomem(lstr = "Skipping locked vault: \"");
00176                                                 TRY_nomem(lstr += *vi);
00177                                                 TRY_nomem(lstr += "\"\n");
00178                                                 logger.write(lstr);
00179 
00180                                                 continue;
00181                                         }
00182                                 }
00183 
00184                                 subdir.path(*vi);
00185                                 for (sdi = subdir.begin(); sdi != subdir.end(); sdi++) {
00186                                         if ((youngest.first < *sdi) || (youngest.first.size() == 0)) {
00187                                                 TRY_nomem(youngest.first = *sdi);
00188                                                 TRY_nomem(youngest.second = *vi);
00189                                         }
00190                                 }
00191                         }
00192                         
00193                         TRY_nomem(m_selected_vault = "");
00194                         if (youngest.second.size() == 0) {
00195                                 TRY_nomem(m_selected_vault = config.vaults()[0]);
00196                         }
00197                         else {
00198                                 for (
00199                                         vi = config.vaults().begin(); 
00200                                         vi != config.vaults().end(); 
00201                                         vi++
00202                                         )
00203                                 {
00204                                         if (*vi == youngest.second) {
00205                                                 if ((vi+1) == config.vaults().end()) {
00206                                                         TRY_nomem(m_selected_vault = config.vaults()[0]);
00207                                                 }
00208                                                 else {
00209                                                         TRY_nomem(m_selected_vault = *(vi+1));
00210                                                 }
00211                                         }
00212                                 }
00213                         }
00214                 }
00215                 else {
00216                         std::pair<std::string,filesystem::size_type> most_space;
00217                         filesystem fsys;
00218         
00219                         TRY_nomem(most_space.first = config.vaults()[0]);
00220                         fsys.path(config.vaults()[0]);
00221                         most_space.second = fsys.free_blocks();
00222                         for (
00223                                 vi = (config.vaults().begin()+1); 
00224                                 vi != config.vaults().end();
00225                                 vi++
00226                                 )
00227                         {
00228                                 fsys.path(*vi);
00229                                 if (most_space.second < fsys.free_blocks()) {
00230                                         TRY_nomem(most_space.first = *vi);
00231                                         most_space.second = fsys.free_blocks();
00232                                 }
00233                         }
00234                         TRY_nomem(m_selected_vault = most_space.first);
00235                 }
00236         }
00237         catch(error e) {
00238                 e.push_back(es);
00239                 throw(e);
00240         }
00241         catch(...) {
00242                 error e(0);
00243                 if (errno == ENOMEM) {
00244                         e = err_nomem;
00245                         e.push_back(es);
00246                         throw(e);
00247                 }
00248                 e = err_unknown;
00249                 e.push_back(es);
00250                 throw(e);
00251         }
00252 
00253         if (m_selected_vault.size() == 0)
00254                 throw(ERROR(0,es));
00255 }
00256 
00257 /** Return a list of archive directories in the selected vault
00258 
00259         Return a list of archive directories in the selected vault, including
00260         incomplete archives.  Ignore all other directories.
00261  */
00262 const subdirectory vault_manager::get_archive_list(void)
00263 {
00264         subdirectory subdir;
00265         subdirectory::iterator sdi;
00266 
00267         if (!initialized())
00268                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00269 
00270         if (!selected())
00271                 throw(INTERNAL_ERROR(0,"No vault selected"));
00272 
00273         subdir.path(vault());
00274 
00275         sdi = subdir.begin();
00276         while (sdi != subdir.end()) {
00277                 if (!is_timestamp((*sdi).substr(0,(*sdi).find(".incomplete"))))
00278                 {
00279                         subdir.erase(sdi);
00280                         sdi = subdir.begin();
00281                 }
00282                 else {
00283                         sdi++;
00284                 }
00285         }
00286 
00287         return(subdir);
00288 }
00289 
00290 /** Return the path to the selected vault */
00291 const std::string vault_manager::vault(void) const
00292 {
00293         if (!initialized())
00294                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00295 
00296         if (!selected())
00297                 throw(INTERNAL_ERROR(0,"No vault selected"));
00298 
00299         return(m_selected_vault);
00300 }
00301 
00302 /** Return whether or not a vault has been selected yet */
00303 const bool vault_manager::selected(void) const
00304 {
00305         bool value = true;
00306 
00307         if (!initialized())
00308                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00309 
00310         if (m_selected_vault.size() == 0)
00311                 value = false;
00312         
00313         return(value);
00314 }
00315 
00316 /** Return the percent of used blocks and used inodes in the selected vault */
00317 void vault_manager::usage(uint16 &a_blocks, uint16 &a_inodes) const
00318 {
00319         filesystem fsys;
00320         safe_num<uint64> blocks, inodes;
00321 
00322         if (!initialized())
00323                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00324 
00325         if (!selected())
00326                 throw(INTERNAL_ERROR(0,"No vault selected"));
00327 
00328         fsys.path(vault());
00329 
00330         if (fsys.total_blocks() == 0)
00331                 blocks = 0;
00332         else {
00333                 try {
00334                         blocks = fsys.free_blocks();
00335                         // blocks *= 100;
00336                         // blocks /= fsys.total_blocks();
00337                         blocks /= fsys.total_blocks() / 100;
00338                 }
00339                 catch(error e) {
00340                         logger.write("*** ERROR: Overflow error detected in vault_manager::usage() while calculating percent blocks used\n");
00341                         logger.write(e.str());
00342                         blocks = 0;
00343                 }
00344                 catch(...) {
00345                         logger.write("*** ERROR: Unknown error detected in vault_manager::usage() while calculating percent blocks used\n");
00346                         blocks = 0;
00347                 }
00348         }
00349 
00350         if (fsys.free_inodes() == 0)
00351                 inodes = 0;
00352         else {
00353                 try {
00354                         inodes = fsys.free_inodes();
00355                         // inodes *= 100;
00356                         // inodes /= fsys.total_inodes();
00357                         inodes /= fsys.total_inodes() / 100;
00358                 }
00359                 catch(error e) {
00360                         logger.write("*** ERROR: Overflow error detected in vault_manager::usage() while calculating percent inodes used\n");
00361                         logger.write(e.str());
00362                         blocks = 0;
00363                 }
00364                 catch(...) {
00365                         logger.write("*** ERROR: Unknown error detected in vault_manager::usage() while calculating percent inodes used\n");
00366                         blocks = 0;
00367                 }
00368         }
00369 
00370         ASSERT(blocks <= max_limit(blocks));
00371         ASSERT(inodes <= max_limit(inodes));
00372 
00373         a_blocks = blocks.value();
00374         a_inodes = inodes.value();
00375 }
00376 
00377 /** Test to see if a vault has exceeded it's overflow threshold */
00378 const bool vault_manager::overflow(bool a_report)
00379 {
00380         uint16 free_blocks;
00381         uint16 free_inodes;
00382         bool value = false;
00383         std::string lstr;
00384 
00385         if (!initialized())
00386                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00387 
00388         if (!selected())
00389                 throw(INTERNAL_ERROR(0,"No vault selected"));
00390 
00391         usage(free_blocks, free_inodes);
00392         if (free_blocks < config.vault_overflow_blocks())
00393                 value = true;
00394         if (free_inodes < config.vault_overflow_inodes())
00395                 value = true;
00396         
00397         if (value && a_report) {
00398                 TRY_nomem(lstr = "Vault overflow detected: ");
00399                 TRY_nomem(lstr += vault());
00400                 TRY_nomem(lstr += "\n");
00401                 logger.write(lstr);
00402 
00403                 TRY_nomem(lstr = "     Threshold: ");
00404                 TRY_nomem(lstr += 
00405                         percent_string(config.vault_overflow_blocks(),
00406                                 static_cast<uint16>(100)));
00407                 TRY_nomem(lstr += " free blocks, ");
00408                 TRY_nomem(lstr += 
00409                         percent_string(config.vault_overflow_inodes(),
00410                                 static_cast<uint16>(100)));
00411                 TRY_nomem(lstr += " free inodes");
00412                 TRY_nomem(lstr += "\n");
00413                 logger.write(lstr);
00414 
00415                 TRY_nomem(lstr = "Vault capacity: ");
00416                 TRY_nomem(lstr += percent_string(free_blocks,static_cast<uint16>(100)));
00417                 TRY_nomem(lstr += " free blocks, ");
00418                 TRY_nomem(lstr += percent_string(free_inodes,static_cast<uint16>(100)));
00419                 TRY_nomem(lstr += " free inodes");
00420                 TRY_nomem(lstr += "\n");
00421                 logger.write(lstr);
00422 
00423                 reporter.vault().add_report(
00424                         vault_stats_report(estring("Overflow Detected:"),filesystem(vault()))
00425                         );
00426         }
00427         
00428         return(value);
00429 }
00430 
00431 /** Find the oldest archive in the vault and delete it */
00432 void vault_manager::delete_oldest_archive(void)
00433 {
00434         std::string es;
00435         estring ts;
00436         estring tsi;
00437         std::string lstr;
00438         subdirectory archive_list; 
00439         std::string oldest;
00440         std::string dir;
00441         estring estr;
00442         timer t;
00443         uint16 free_blocks, free_inodes;
00444 
00445         if (!initialized())
00446                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00447         
00448         if (!selected())
00449                 throw(INTERNAL_ERROR(0,"No vault selected"));
00450 
00451         TRY_nomem(ts = config.timestamp().str());
00452         TRY_nomem(tsi = ts + ".incomplete");
00453 
00454         archive_list = get_archive_list();
00455         if (
00456                 (archive_list.size() == 0)
00457                 || (
00458                         (archive_list.size() == 1)
00459                         && (archive_list[0] == ts || archive_list[0] == tsi)
00460                         )
00461                 )
00462         {
00463                 TRY_nomem(es = "Vault has insufficient space: \"");
00464                 TRY_nomem(es += vault());
00465                 TRY_nomem(es += "\"");
00466                 exit_manager.assign(exitstat::vault_full);
00467                 throw(ERROR(0,es));
00468         }
00469 
00470         TRY_nomem(oldest = archive_list[0]);
00471         if (oldest == ts || oldest == tsi) {
00472                 TRY_nomem(lstr = "Oldest is actually archive in use: \"");
00473                 TRY_nomem(lstr += oldest);
00474                 TRY_nomem(lstr += "\"  Skipping to next archive...\n");
00475                 logger.write(lstr);
00476                 TRY_nomem(oldest = archive_list[1]);
00477         }
00478 
00479         if (oldest.find(".incomplete") != std::string::npos) {
00480                 TRY_nomem(lstr = "WARNING: Oldest archive found is incomplete.\n");
00481                 logger.write(lstr);
00482         }
00483 
00484         TRY_nomem(lstr = "Deleting oldest archive: \"");
00485         TRY_nomem(lstr += oldest);
00486         TRY_nomem(lstr += "\"\n");
00487         logger.write(lstr);
00488 
00489         TRY_nomem(dir = vault());
00490         TRY_nomem(dir += "/");
00491         TRY_nomem(dir += oldest);
00492         t.start();
00493         try {
00494                 m_deleted_archives.push_back(oldest);
00495         }
00496         catch(...) {
00497                 m_da_err = true;
00498         }
00499         rm_recursive(dir);
00500         t.stop();
00501 
00502         TRY_nomem(lstr = "Deletion complete, duration: ");
00503         TRY_nomem(lstr += t.duration());
00504         TRY_nomem(lstr += "\n");
00505         logger.write(lstr);
00506 
00507         usage(free_blocks, free_inodes);
00508         TRY_nomem(lstr = "Vault capacity: ");
00509         TRY_nomem(lstr += percent_string(free_blocks,static_cast<uint16>(100)));
00510         TRY_nomem(lstr += " free blocks, ");
00511         TRY_nomem(lstr += percent_string(free_inodes,static_cast<uint16>(100)));
00512         TRY_nomem(lstr += " free inodes");
00513         TRY_nomem(lstr += "\n");
00514         logger.write(lstr);
00515 
00516         estr = "Deleted ";
00517         estr += oldest;
00518         estr += ":";
00519         reporter.vault().add_report(vault_stats_report(estr,filesystem(vault())));
00520 }
00521 
00522 /** Prepare the selected vault
00523 
00524         Check the selected vault for overflow.  If the overflow threshold has been
00525         exceeded and the vault-overflow-behavior setting is not quit, then delete
00526         the oldest archive in the vault.  Depending on vault-overflow-behavior,
00527         possibly repeat this action until either there is space free or there are no
00528         archives left in the vault.
00529 
00530 */
00531 void vault_manager::prepare(bool a_assume_overflow)
00532 {
00533         std::string es;
00534         std::string lstr;
00535 
00536         /*
00537         estring debug_estr;
00538         
00539         debug_estr = "[DEBUG]: vault_manager::prepare() - a_assume_overflow = ";
00540         debug_estr += estring(a_assume_overflow);
00541         debug_estr += "\n";
00542         logger.write(debug_estr);
00543         */
00544 
00545         if (!initialized())
00546                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00547 
00548         if (!selected())
00549                 throw(INTERNAL_ERROR(0,"No vault selected"));
00550         
00551         if (!overflow() && !a_assume_overflow)
00552                 return;
00553         
00554         // logger.write("Vault has insufficient space\n");
00555         // logger.write("[DEBUG]: vault_manager::prepare() - Cleaning vault...\n");
00556 
00557         if (config.vault_overflow_behavior() 
00558                 == configuration_manager::overflow_quit)
00559         {
00560                 TRY_nomem(es = "Vault has insufficient space: \"");
00561                 TRY_nomem(es += vault());
00562                 TRY_nomem(es += "\"");
00563                 throw(ERROR(0,es));
00564         }
00565 
00566         if (config.vault_overflow_behavior() 
00567                         == configuration_manager::overflow_delete_oldest)
00568         {
00569                 if (m_deleted_archives.size() == 0) {
00570                         TRY(delete_oldest_archive(),"Failure preparing vault");
00571                         a_assume_overflow = false;
00572                 }
00573                 else {
00574                         logger.write("Vault has insufficient space\n");
00575                         throw(ERROR(0,"Vault has insufficient space"));
00576                 }
00577         }
00578 
00579         if (!overflow() && !a_assume_overflow)
00580                 return;
00581 
00582         if (config.vault_overflow_behavior() 
00583                 == configuration_manager::overflow_delete_until_free)
00584         {
00585                 while (overflow() || a_assume_overflow) {
00586                         // logger.write("Vault has insufficient space\n");
00587                         TRY(delete_oldest_archive(),"Failure preparing vault");
00588                         a_assume_overflow = false;
00589                 }
00590         }
00591         else {
00592                 // logger.write("Vault has insufficient space\n");
00593                 exit_manager.assign(exitstat::vault_full);
00594                 // throw(ERROR(0,"Vault has insufficient space"));
00595         }
00596 }
00597 
00598 /** Return a list of deleted archives */
00599 const std::vector<std::string>& vault_manager::deleted_archives(void) const
00600 {
00601         if (!initialized())
00602                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00603 
00604         return(m_deleted_archives);
00605 }
00606 
00607 /** Return whether or not there was an error deleting archives */
00608 const bool vault_manager::err_deleted_archives(void) const
00609 {
00610         if (!initialized())
00611                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00612 
00613         return(m_da_err);
00614 }
00615 
00616 /** The global vault manager */
00617 vault_manager vaulter;
00618 

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