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