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 subdirectory logfile_list;
00445 subdirectory::const_iterator ilf;
00446
00447 if (!initialized())
00448 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00449
00450 if (!selected())
00451 throw(INTERNAL_ERROR(0,"No vault selected"));
00452
00453 TRY_nomem(ts = config.timestamp().str());
00454 TRY_nomem(tsi = ts + ".incomplete");
00455
00456 archive_list = get_archive_list();
00457 if (
00458 (archive_list.size() == 0)
00459 || (
00460 (archive_list.size() == 1)
00461 && (archive_list[0] == ts || archive_list[0] == tsi)
00462 )
00463 )
00464 {
00465 TRY_nomem(es = "Vault has insufficient space: \"");
00466 TRY_nomem(es += vault());
00467 TRY_nomem(es += "\"");
00468 exit_manager.assign(exitstat::vault_full);
00469 throw(ERROR(0,es));
00470 }
00471
00472 TRY_nomem(oldest = archive_list[0]);
00473 if (oldest == ts || oldest == tsi) {
00474 TRY_nomem(lstr = "Oldest is actually archive in use: \"");
00475 TRY_nomem(lstr += oldest);
00476 TRY_nomem(lstr += "\" Skipping to next archive...\n");
00477 logger.write(lstr);
00478 TRY_nomem(oldest = archive_list[1]);
00479 }
00480
00481 if (oldest.find(".incomplete") != std::string::npos) {
00482 TRY_nomem(lstr = "WARNING: Oldest archive found is incomplete.\n");
00483 logger.write(lstr);
00484 }
00485
00486 TRY_nomem(lstr = "Deleting oldest archive: \"");
00487 TRY_nomem(lstr += oldest);
00488 TRY_nomem(lstr += "\"\n");
00489 logger.write(lstr);
00490
00491 TRY_nomem(dir = vault());
00492 TRY_nomem(dir += "/");
00493 TRY_nomem(dir += oldest);
00494 t.start();
00495 try {
00496 m_deleted_archives.push_back(oldest);
00497 }
00498 catch(...) {
00499 m_da_err = true;
00500 }
00501 rm_recursive(dir);
00502 t.stop();
00503
00504 TRY_nomem(lstr = "Deletion complete, duration: ");
00505 TRY_nomem(lstr += t.duration());
00506 TRY_nomem(lstr += "\n");
00507 logger.write(lstr);
00508
00509 usage(free_blocks, free_inodes);
00510 TRY_nomem(lstr = "Vault capacity: ");
00511 TRY_nomem(lstr += percent_string(free_blocks,static_cast<uint16>(100)));
00512 TRY_nomem(lstr += " free blocks, ");
00513 TRY_nomem(lstr += percent_string(free_inodes,static_cast<uint16>(100)));
00514 TRY_nomem(lstr += " free inodes");
00515 TRY_nomem(lstr += "\n");
00516 logger.write(lstr);
00517
00518 estr = "Deleted ";
00519 estr += oldest;
00520 estr += ":";
00521 reporter.vault().add_report(vault_stats_report(estr,filesystem(vault())));
00522
00523
00524
00525
00526
00527 if (config.delete_old_log_files()) {
00528 estring wildcard;
00529
00530 logger.write("Searching for old log files to delete...\n");
00531 wildcard = oldest;
00532 wildcard += ".log*";
00533 logfile_list.path(config.log_dir(), wildcard);
00534 for (ilf = logfile_list.begin(); ilf != logfile_list.end(); ilf++) {
00535 estring file;
00536
00537 file = config.log_dir();
00538 file += "/";
00539 file += *ilf;
00540
00541 try {
00542 rm_file(file);
00543 }
00544 catch(error e) {
00545 estring es;
00546
00547 es = "*** ERROR: Could not remove log file: ";
00548 es += *ilf;
00549
00550 logger.write(es);
00551 logger.write(e.str());
00552
00553
00554
00555 }
00556 catch(...) {
00557 estring es;
00558
00559 es = "*** ERROR: Unknown error detected in vault_manager::delete_oldest_archive() while deleting old log file: ";
00560 es += *ilf;
00561 es += '\n';
00562
00563 logger.write(es);
00564
00565
00566
00567 }
00568 }
00569
00570 wildcard = oldest;
00571 wildcard += ".relink*";
00572 logfile_list.path(config.log_dir(), wildcard);
00573 for (ilf = logfile_list.begin(); ilf != logfile_list.end(); ilf++) {
00574 estring file;
00575
00576 file = config.log_dir();
00577 file += "/";
00578 file += *ilf;
00579
00580 try {
00581 rm_file(file);
00582 }
00583 catch(error e) {
00584 estring es;
00585
00586 es = "*** ERROR: Could not remove relink file: ";
00587 es += *ilf;
00588
00589 logger.write(es);
00590 logger.write(e.str());
00591
00592
00593
00594 }
00595 catch(...) {
00596 estring es;
00597
00598 es = "*** ERROR: Unknown error detected in vault_manager::delete_oldest_archive() while deleting old relink file: ";
00599 es += *ilf;
00600 es += '\n';
00601
00602 logger.write(es);
00603
00604
00605
00606 }
00607 }
00608 }
00609
00610 if (config.delete_old_report_files()) {
00611 estring wildcard;
00612
00613 logger.write("Searching for old report files to delete...\n");
00614 wildcard = oldest;
00615 wildcard += ".report*";
00616 logfile_list.path(config.log_dir(), wildcard);
00617 for (ilf = logfile_list.begin(); ilf != logfile_list.end(); ilf++) {
00618 estring file;
00619
00620 file = config.log_dir();
00621 file += "/";
00622 file += *ilf;
00623
00624 try {
00625 rm_file(file);
00626 }
00627 catch(error e) {
00628 estring es;
00629
00630 es = "*** ERROR: Could not remove report file: ";
00631 es += *ilf;
00632
00633 logger.write(es);
00634 logger.write(e.str());
00635
00636
00637
00638 }
00639 catch(...) {
00640 estring es;
00641
00642 es = "*** ERROR: Unknown error detected in vault_manager::delete_oldest_archive() while deleting old log file: ";
00643 es += *ilf;
00644 es += '\n';
00645
00646 logger.write(es);
00647
00648
00649
00650 }
00651 }
00652 }
00653 }
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664 void vault_manager::prepare(bool a_assume_overflow)
00665 {
00666 std::string es;
00667 std::string lstr;
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678 if (!initialized())
00679 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00680
00681 if (!selected())
00682 throw(INTERNAL_ERROR(0,"No vault selected"));
00683
00684 if (!overflow() && !a_assume_overflow)
00685 return;
00686
00687
00688
00689
00690 if (config.vault_overflow_behavior()
00691 == configuration_manager::overflow_quit)
00692 {
00693 TRY_nomem(es = "Vault has insufficient space: \"");
00694 TRY_nomem(es += vault());
00695 TRY_nomem(es += "\"");
00696 throw(ERROR(0,es));
00697 }
00698
00699 if (config.vault_overflow_behavior()
00700 == configuration_manager::overflow_delete_oldest)
00701 {
00702 if (m_deleted_archives.size() == 0) {
00703 TRY(delete_oldest_archive(),"Failure preparing vault");
00704 a_assume_overflow = false;
00705 }
00706 else {
00707 logger.write("Vault has insufficient space\n");
00708 throw(ERROR(0,"Vault has insufficient space"));
00709 }
00710 }
00711
00712 if (!overflow() && !a_assume_overflow)
00713 return;
00714
00715 if (config.vault_overflow_behavior()
00716 == configuration_manager::overflow_delete_until_free)
00717 {
00718 while (overflow() || a_assume_overflow) {
00719
00720 TRY(delete_oldest_archive(),"Failure preparing vault");
00721 a_assume_overflow = false;
00722 }
00723 }
00724 else {
00725
00726 exit_manager.assign(exitstat::vault_full);
00727
00728 }
00729 }
00730
00731
00732 const std::vector<std::string>& vault_manager::deleted_archives(void) const
00733 {
00734 if (!initialized())
00735 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00736
00737 return(m_deleted_archives);
00738 }
00739
00740
00741 const bool vault_manager::err_deleted_archives(void) const
00742 {
00743 if (!initialized())
00744 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00745
00746 return(m_da_err);
00747 }
00748
00749
00750 vault_manager vaulter;
00751