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
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
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
00040 void vault_manager::init(void)
00041 {
00042 clear();
00043 m_initialized = true;
00044 }
00045
00046
00047 const bool vault_manager::initialized(void) const
00048 {
00049 return(m_initialized);
00050 }
00051
00052
00053
00054
00055
00056
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
00073
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
00152
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
00258
00259
00260
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
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
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
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
00336
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
00356
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
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
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
00523
00524
00525
00526
00527
00528
00529
00530
00531 void vault_manager::prepare(bool a_assume_overflow)
00532 {
00533 std::string es;
00534 std::string lstr;
00535
00536
00537
00538
00539
00540
00541
00542
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
00555
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
00587 TRY(delete_oldest_archive(),"Failure preparing vault");
00588 a_assume_overflow = false;
00589 }
00590 }
00591 else {
00592
00593 exit_manager.assign(exitstat::vault_full);
00594
00595 }
00596 }
00597
00598
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
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
00617 vault_manager vaulter;
00618