diff options
-rw-r--r-- | bin/bbstoreaccounts/bbstoreaccounts.cpp | 504 | ||||
-rw-r--r-- | lib/backupstore/BackupStoreAccounts.cpp | 491 | ||||
-rw-r--r-- | lib/backupstore/BackupStoreAccounts.h | 34 |
3 files changed, 535 insertions, 494 deletions
diff --git a/bin/bbstoreaccounts/bbstoreaccounts.cpp b/bin/bbstoreaccounts/bbstoreaccounts.cpp index f2292ca1..bcff886b 100644 --- a/bin/bbstoreaccounts/bbstoreaccounts.cpp +++ b/bin/bbstoreaccounts/bbstoreaccounts.cpp @@ -18,12 +18,6 @@ #include <sys/types.h> -#include <algorithm> -#include <cstring> -#include <iostream> -#include <ostream> -#include <vector> - #include "box_getopt.h" #include "BackupStoreAccounts.h" #include "BackupStoreAccountDatabase.h" @@ -43,476 +37,6 @@ #include <cstring> -// max size of soft limit as percent of hard limit -#define MAX_SOFT_LIMIT_SIZE 97 - -bool sMachineReadableOutput = false; - -void CheckSoftHardLimits(int64_t SoftLimit, int64_t HardLimit) -{ - if(SoftLimit > HardLimit) - { - BOX_FATAL("Soft limit must be less than the hard limit."); - exit(1); - } - if(SoftLimit > ((HardLimit * MAX_SOFT_LIMIT_SIZE) / 100)) - { - BOX_WARNING("We recommend setting the soft limit below " << - MAX_SOFT_LIMIT_SIZE << "% of the hard limit, or " << - HumanReadableSize((HardLimit * MAX_SOFT_LIMIT_SIZE) - / 100) << " in this case."); - } -} - -int BlockSizeOfDiscSet(int discSetNum) -{ - // Get controller, check disc set number - RaidFileController &controller(RaidFileController::GetController()); - if(discSetNum < 0 || discSetNum >= controller.GetNumDiscSets()) - { - BOX_FATAL("Disc set " << discSetNum << " does not exist."); - exit(1); - } - - // Return block size - return controller.GetDiscSet(discSetNum).GetBlockSize(); -} - -std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int discSetNum) -{ - return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(discSetNum), - MaxBlocks * BlockSizeOfDiscSet(discSetNum), - sMachineReadableOutput); -} - -int64_t SizeStringToBlocks(const char *string, int discSetNum) -{ - // Find block size - int blockSize = BlockSizeOfDiscSet(discSetNum); - - // Get number - char *endptr = (char*)string; - int64_t number = strtol(string, &endptr, 0); - if(endptr == string || number == LONG_MIN || number == LONG_MAX) - { - BOX_FATAL("'" << string << "' is not a valid number."); - exit(1); - } - - // Check units - switch(*endptr) - { - case 'M': - case 'm': - // Units: Mb - return (number * 1024*1024) / blockSize; - break; - - case 'G': - case 'g': - // Units: Gb - return (number * 1024*1024*1024) / blockSize; - break; - - case 'B': - case 'b': - // Units: Blocks - // Easy! Just return the number specified. - return number; - break; - - default: - BOX_FATAL(string << " has an invalid units specifier " - "(use B for blocks, M for MB, G for GB, eg 2GB)"); - exit(1); - break; - } -} - -bool OpenAccount(Configuration &rConfig, int32_t ID, std::string &rRootDirOut, - int &rDiscSetOut, std::auto_ptr<UnixUser> apUser, NamedLock* pLock); - -int SetLimit(Configuration &rConfig, int32_t ID, const char *SoftLimitStr, - const char *HardLimitStr) -{ - std::string rootDir; - int discSetNum; - std::auto_ptr<UnixUser> user; // used to reset uid when we return - NamedLock writeLock; - - if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user, &writeLock)) - { - BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) - << " to change limits."); - return 1; - } - - // Load the info - std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, rootDir, - discSetNum, false /* Read/Write */)); - - // Change the limits - int64_t softlimit = SizeStringToBlocks(SoftLimitStr, discSetNum); - int64_t hardlimit = SizeStringToBlocks(HardLimitStr, discSetNum); - CheckSoftHardLimits(softlimit, hardlimit); - info->ChangeLimits(softlimit, hardlimit); - - // Save - info->Save(); - - BOX_NOTICE("Limits on account " << BOX_FORMAT_ACCOUNT(ID) << - " changed to " << softlimit << " soft, " << - hardlimit << " hard."); - - return 0; -} - -int SetAccountName(Configuration &rConfig, int32_t ID, - const std::string& rNewAccountName) -{ - std::string rootDir; - int discSetNum; - std::auto_ptr<UnixUser> user; // used to reset uid when we return - NamedLock writeLock; - - if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user, &writeLock)) - { - BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) - << " to change name."); - return 1; - } - - // Load the info - std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, - rootDir, discSetNum, false /* Read/Write */)); - - info->SetAccountName(rNewAccountName); - - // Save - info->Save(); - - BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) << - " name changed to " << rNewAccountName); - - return 0; -} - -int AccountInfo(Configuration &rConfig, int32_t ID) -{ - std::string rootDir; - int discSetNum; - std::auto_ptr<UnixUser> user; // used to reset uid when we return - - if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user, - NULL /* no write lock needed for this read-only operation */)) - { - BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) - << " to display info."); - return 1; - } - - // Load it in - std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, - rootDir, discSetNum, true /* ReadOnly */)); - - // Then print out lots of info - std::cout << FormatUsageLineStart("Account ID", sMachineReadableOutput) << - BOX_FORMAT_ACCOUNT(ID) << std::endl; - std::cout << FormatUsageLineStart("Account Name", sMachineReadableOutput) << - info->GetAccountName() << std::endl; - std::cout << FormatUsageLineStart("Last object ID", sMachineReadableOutput) << - BOX_FORMAT_OBJECTID(info->GetLastObjectIDUsed()) << std::endl; - std::cout << FormatUsageLineStart("Used", sMachineReadableOutput) << - BlockSizeToString(info->GetBlocksUsed(), - info->GetBlocksHardLimit(), discSetNum) << std::endl; - std::cout << FormatUsageLineStart("Current files", - sMachineReadableOutput) << - BlockSizeToString(info->GetBlocksInCurrentFiles(), - info->GetBlocksHardLimit(), discSetNum) << std::endl; - std::cout << FormatUsageLineStart("Old files", sMachineReadableOutput) << - BlockSizeToString(info->GetBlocksInOldFiles(), - info->GetBlocksHardLimit(), discSetNum) << std::endl; - std::cout << FormatUsageLineStart("Deleted files", sMachineReadableOutput) << - BlockSizeToString(info->GetBlocksInDeletedFiles(), - info->GetBlocksHardLimit(), discSetNum) << std::endl; - std::cout << FormatUsageLineStart("Directories", sMachineReadableOutput) << - BlockSizeToString(info->GetBlocksInDirectories(), - info->GetBlocksHardLimit(), discSetNum) << std::endl; - std::cout << FormatUsageLineStart("Soft limit", sMachineReadableOutput) << - BlockSizeToString(info->GetBlocksSoftLimit(), - info->GetBlocksHardLimit(), discSetNum) << std::endl; - std::cout << FormatUsageLineStart("Hard limit", sMachineReadableOutput) << - BlockSizeToString(info->GetBlocksHardLimit(), - info->GetBlocksHardLimit(), discSetNum) << std::endl; - std::cout << FormatUsageLineStart("Client store marker", sMachineReadableOutput) << - info->GetLastObjectIDUsed() << std::endl; - std::cout << FormatUsageLineStart("Live Files", sMachineReadableOutput) << - info->GetNumFiles() << std::endl; - std::cout << FormatUsageLineStart("Old Files", sMachineReadableOutput) << - info->GetNumOldFiles() << std::endl; - std::cout << FormatUsageLineStart("Deleted Files", sMachineReadableOutput) << - info->GetNumDeletedFiles() << std::endl; - std::cout << FormatUsageLineStart("Directories", sMachineReadableOutput) << - info->GetNumDirectories() << std::endl; - std::cout << FormatUsageLineStart("Enabled", sMachineReadableOutput) << - (info->IsAccountEnabled() ? "yes" : "no") << std::endl; - - return 0; -} - -int SetAccountEnabled(Configuration &rConfig, int32_t ID, bool enabled) -{ - std::string rootDir; - int discSetNum; - std::auto_ptr<UnixUser> user; // used to reset uid when we return - NamedLock writeLock; - - if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user, &writeLock)) - { - BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) - << " to change enabled flag."); - return 1; - } - - // Load it in - std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, - rootDir, discSetNum, false /* ReadOnly */)); - info->SetAccountEnabled(enabled); - info->Save(); - return 0; -} - -int DeleteAccount(Configuration &rConfig, int32_t ID, bool AskForConfirmation) -{ - std::string rootDir; - int discSetNum; - std::auto_ptr<UnixUser> user; // used to reset uid when we return - NamedLock writeLock; - - // Obtain a write lock, as the daemon user - if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user, &writeLock)) - { - BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) - << " for deletion."); - return 1; - } - - // Check user really wants to do this - if(AskForConfirmation) - { - BOX_WARNING("Really delete account " << - BOX_FORMAT_ACCOUNT(ID) << "? (type 'yes' to confirm)"); - char response[256]; - if(::fgets(response, sizeof(response), stdin) == 0 || ::strcmp(response, "yes\n") != 0) - { - BOX_NOTICE("Deletion cancelled."); - return 0; - } - } - - // Back to original user, but write lock is maintained - user.reset(); - - std::auto_ptr<BackupStoreAccountDatabase> db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); - - // Delete from account database - db->DeleteEntry(ID); - - // Write back to disc - db->Write(); - - // Remove the store files... - - // First, become the user specified in the config file - std::string username; - { - const Configuration &rserverConfig(rConfig.GetSubConfiguration("Server")); - if(rserverConfig.KeyExists("User")) - { - username = rserverConfig.GetKeyValue("User"); - } - } - - // Become the right user - if(!username.empty()) - { - // Username specified, change... - user.reset(new UnixUser(username)); - user->ChangeProcessUser(true /* temporary */); - // Change will be undone when user goes out of scope - } - - // Secondly, work out which directories need wiping - std::vector<std::string> toDelete; - RaidFileController &rcontroller(RaidFileController::GetController()); - RaidFileDiscSet discSet(rcontroller.GetDiscSet(discSetNum)); - for(RaidFileDiscSet::const_iterator i(discSet.begin()); i != discSet.end(); ++i) - { - if(std::find(toDelete.begin(), toDelete.end(), *i) == toDelete.end()) - { - toDelete.push_back((*i) + DIRECTORY_SEPARATOR + rootDir); - } - } - -#ifdef WIN32 - // Cannot remove files while holding a lock on them - writeLock.ReleaseLock(); -#endif - - int retcode = 0; - - // Thirdly, delete the directories... - for(std::vector<std::string>::const_iterator d(toDelete.begin()); d != toDelete.end(); ++d) - { - BOX_NOTICE("Deleting store directory " << (*d) << "..."); - // Just use the rm command to delete the files - std::string cmd("rm -rf "); - cmd += *d; - // Run command - if(::system(cmd.c_str()) != 0) - { - BOX_ERROR("Failed to delete files in " << (*d) << - ", delete them manually."); - retcode = 1; - } - } - - // Success! - return retcode; -} - -bool OpenAccount(Configuration &rConfig, int32_t ID, std::string &rRootDirOut, - int &rDiscSetOut, std::auto_ptr<UnixUser> apUser, NamedLock* pLock) -{ - // Load in the account database - std::auto_ptr<BackupStoreAccountDatabase> db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); - - // Exists? - if(!db->EntryExists(ID)) - { - BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << - " does not exist."); - return false; - } - - // Get info from the database - BackupStoreAccounts acc(*db); - acc.GetAccountRoot(ID, rRootDirOut, rDiscSetOut); - - // Get the user under which the daemon runs - std::string username; - { - const Configuration &rserverConfig(rConfig.GetSubConfiguration("Server")); - if(rserverConfig.KeyExists("User")) - { - username = rserverConfig.GetKeyValue("User"); - } - } - - // Become the right user - if(!username.empty()) - { - // Username specified, change... - apUser.reset(new UnixUser(username)); - apUser->ChangeProcessUser(true /* temporary */); - // Change will be undone when apUser goes out of scope - // in the caller. - } - - if(pLock) - { - acc.LockAccount(ID, *pLock); - } - - return true; -} - -int CheckAccount(Configuration &rConfig, int32_t ID, bool FixErrors, bool Quiet) -{ - std::string rootDir; - int discSetNum; - std::auto_ptr<UnixUser> user; // used to reset uid when we return - NamedLock writeLock; - - if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user, &writeLock)) - { - BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) - << " for checking."); - return 1; - } - - // Check it - BackupStoreCheck check(rootDir, discSetNum, ID, FixErrors, Quiet); - check.Check(); - - return check.ErrorsFound()?1:0; -} - -int CreateAccount(Configuration &rConfig, int32_t ID, int32_t DiscNumber, - int32_t SoftLimit, int32_t HardLimit) -{ - // Load in the account database - std::auto_ptr<BackupStoreAccountDatabase> db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); - - // Already exists? - if(db->EntryExists(ID)) - { - BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << - " already exists."); - return 1; - } - - // Get the user under which the daemon runs - std::string username; - { - const Configuration &rserverConfig(rConfig.GetSubConfiguration("Server")); - if(rserverConfig.KeyExists("User")) - { - username = rserverConfig.GetKeyValue("User"); - } - } - - // Create it. - BackupStoreAccounts acc(*db); - acc.Create(ID, DiscNumber, SoftLimit, HardLimit, username); - - BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) << " created."); - - return 0; -} - -int HousekeepAccountNow(Configuration &rConfig, int32_t ID) -{ - std::string rootDir; - int discSetNum; - std::auto_ptr<UnixUser> user; // used to reset uid when we return - - if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user, - NULL /* housekeeping locks the account itself */)) - { - BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) - << " for housekeeping."); - return 1; - } - - HousekeepStoreAccount housekeeping(ID, rootDir, discSetNum, NULL); - bool success = housekeeping.DoHousekeeping(); - - if(!success) - { - BOX_ERROR("Failed to lock account " << BOX_FORMAT_ACCOUNT(ID) - << " for housekeeping: perhaps a client is " - "still connected?"); - return 1; - } - else - { - BOX_TRACE("Finished housekeeping on account " << - BOX_FORMAT_ACCOUNT(ID)); - return 0; - } -} - void PrintUsageAndExit() { printf( @@ -565,6 +89,7 @@ int main(int argc, const char *argv[]) // Filename for configuration file? std::string configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE; int logLevel = Log::EVERYTHING; + bool machineReadableOutput = false; // See if there's another entry on the command line int c; @@ -588,7 +113,7 @@ int main(int argc, const char *argv[]) case 'm': // enable machine readable output - sMachineReadableOutput = true; + machineReadableOutput = true; break; case '?': @@ -615,7 +140,7 @@ int main(int argc, const char *argv[]) BOX_ERROR("Invalid configuration file " << configFilename << ":" << errs); } - + // Initialise the raid file controller RaidFileController &rcontroller(RaidFileController::GetController()); rcontroller.Initialise(config->GetKeyValue("RaidFileConf").c_str()); @@ -634,6 +159,7 @@ int main(int argc, const char *argv[]) } std::string command = argv[0]; + BackupStoreAccountsControl control(*config, machineReadableOutput); // Now do the command. if(command == "create") @@ -651,17 +177,17 @@ int main(int argc, const char *argv[]) } // Decode limits - softlimit = SizeStringToBlocks(argv[3], discnum); - hardlimit = SizeStringToBlocks(argv[4], discnum); - CheckSoftHardLimits(softlimit, hardlimit); + softlimit = control.SizeStringToBlocks(argv[3], discnum); + hardlimit = control.SizeStringToBlocks(argv[4], discnum); + control.CheckSoftHardLimits(softlimit, hardlimit); // Create the account... - return CreateAccount(*config, id, discnum, softlimit, hardlimit); + return control.CreateAccount(id, discnum, softlimit, hardlimit); } else if(command == "info") { // Print information on this account - return AccountInfo(*config, id); + return control.PrintAccountInfo(id); } else if(command == "enabled") { @@ -686,7 +212,7 @@ int main(int argc, const char *argv[]) PrintUsageAndExit(); } - return SetAccountEnabled(*config, id, enabled); + return control.SetAccountEnabled(id, enabled); } else if(command == "setlimit") { @@ -697,7 +223,7 @@ int main(int argc, const char *argv[]) return 1; } - return SetLimit(*config, id, argv[2], argv[3]); + return control.SetLimit(id, argv[2], argv[3]); } else if(command == "name") { @@ -708,7 +234,7 @@ int main(int argc, const char *argv[]) return 1; } - return SetAccountName(*config, id, argv[2]); + return control.SetAccountName(id, argv[2]); } else if(command == "delete") { @@ -718,7 +244,7 @@ int main(int argc, const char *argv[]) { askForConfirmation = false; } - return DeleteAccount(*config, id, askForConfirmation); + return control.DeleteAccount(id, askForConfirmation); } else if(command == "check") { @@ -744,11 +270,11 @@ int main(int argc, const char *argv[]) } // Check the account - return CheckAccount(*config, id, fixErrors, quiet); + return control.CheckAccount(id, fixErrors, quiet); } else if(command == "housekeep") { - return HousekeepAccountNow(*config, id); + return control.HousekeepAccountNow(id); } else { diff --git a/lib/backupstore/BackupStoreAccounts.cpp b/lib/backupstore/BackupStoreAccounts.cpp index 18500fc1..5aed4a6d 100644 --- a/lib/backupstore/BackupStoreAccounts.cpp +++ b/lib/backupstore/BackupStoreAccounts.cpp @@ -9,19 +9,29 @@ #include "Box.h" -#include <stdio.h> +#include <algorithm> +#include <climits> +#include <cstdio> +#include <cstring> +#include <iostream> #include "BackupStoreAccounts.h" #include "BackupStoreAccountDatabase.h" +#include "BackupStoreCheck.h" +#include "BackupStoreConfigVerify.h" #include "BackupStoreConstants.h" #include "BackupStoreDirectory.h" #include "BackupStoreException.h" #include "BackupStoreInfo.h" #include "BackupStoreRefCountDatabase.h" #include "BoxPortsAndFiles.h" +#include "HousekeepStoreAccount.h" +#include "NamedLock.h" +#include "RaidFileController.h" #include "RaidFileWrite.h" #include "StoreStructure.h" #include "UnixUser.h" +#include "Utils.h" #include "MemLeakFindOn.h" @@ -110,10 +120,7 @@ void BackupStoreAccounts::Create(int32_t ID, int DiscSet, int64_t SizeSoftLimit, info->Save(); // Create the refcount database - BackupStoreRefCountDatabase::CreateNew(Entry); - std::auto_ptr<BackupStoreRefCountDatabase> refcount( - BackupStoreRefCountDatabase::Load(Entry, false)); - refcount->AddReference(BACKUPSTORE_ROOT_DIRECTORY_ID); + BackupStoreRefCountDatabase::Create(Entry)->Commit(); } // As the original user... @@ -201,3 +208,477 @@ void BackupStoreAccounts::LockAccount(int32_t ID, NamedLock& rNamedLock) "lock on account " << ID); } } + +BackupStoreAccountsControl::BackupStoreAccountsControl( + const Configuration& config, bool machineReadableOutput) +: mConfig(config), + mMachineReadableOutput(machineReadableOutput) +{ } + +void BackupStoreAccountsControl::CheckSoftHardLimits(int64_t SoftLimit, int64_t HardLimit) +{ + if(SoftLimit > HardLimit) + { + BOX_FATAL("Soft limit must be less than the hard limit."); + exit(1); + } + if(SoftLimit > ((HardLimit * MAX_SOFT_LIMIT_SIZE) / 100)) + { + BOX_WARNING("We recommend setting the soft limit below " << + MAX_SOFT_LIMIT_SIZE << "% of the hard limit, or " << + HumanReadableSize((HardLimit * MAX_SOFT_LIMIT_SIZE) + / 100) << " in this case."); + } +} + +int BackupStoreAccountsControl::BlockSizeOfDiscSet(int discSetNum) +{ + // Get controller, check disc set number + RaidFileController &controller(RaidFileController::GetController()); + if(discSetNum < 0 || discSetNum >= controller.GetNumDiscSets()) + { + BOX_FATAL("Disc set " << discSetNum << " does not exist."); + exit(1); + } + + // Return block size + return controller.GetDiscSet(discSetNum).GetBlockSize(); +} + +std::string BackupStoreAccountsControl::BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int discSetNum) +{ + return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(discSetNum), + MaxBlocks * BlockSizeOfDiscSet(discSetNum), + mMachineReadableOutput); +} + +int64_t BackupStoreAccountsControl::SizeStringToBlocks(const char *string, int discSetNum) +{ + // Find block size + int blockSize = BlockSizeOfDiscSet(discSetNum); + + // Get number + char *endptr = (char*)string; + int64_t number = strtol(string, &endptr, 0); + if(endptr == string || number == LONG_MIN || number == LONG_MAX) + { + BOX_FATAL("'" << string << "' is not a valid number."); + exit(1); + } + + // Check units + switch(*endptr) + { + case 'M': + case 'm': + // Units: Mb + return (number * 1024*1024) / blockSize; + break; + + case 'G': + case 'g': + // Units: Gb + return (number * 1024*1024*1024) / blockSize; + break; + + case 'B': + case 'b': + // Units: Blocks + // Easy! Just return the number specified. + return number; + break; + + default: + BOX_FATAL(string << " has an invalid units specifier " + "(use B for blocks, M for MB, G for GB, eg 2GB)"); + exit(1); + break; + } +} + +int BackupStoreAccountsControl::SetLimit(int32_t ID, const char *SoftLimitStr, + const char *HardLimitStr) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr<UnixUser> user; // used to reset uid when we return + NamedLock writeLock; + + if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to change limits."); + return 1; + } + + // Load the info + std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, rootDir, + discSetNum, false /* Read/Write */)); + + // Change the limits + int64_t softlimit = SizeStringToBlocks(SoftLimitStr, discSetNum); + int64_t hardlimit = SizeStringToBlocks(HardLimitStr, discSetNum); + CheckSoftHardLimits(softlimit, hardlimit); + info->ChangeLimits(softlimit, hardlimit); + + // Save + info->Save(); + + BOX_NOTICE("Limits on account " << BOX_FORMAT_ACCOUNT(ID) << + " changed to " << softlimit << " soft, " << + hardlimit << " hard."); + + return 0; +} + +int BackupStoreAccountsControl::SetAccountName(int32_t ID, const std::string& rNewAccountName) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr<UnixUser> user; // used to reset uid when we return + NamedLock writeLock; + + if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to change name."); + return 1; + } + + // Load the info + std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, + rootDir, discSetNum, false /* Read/Write */)); + + info->SetAccountName(rNewAccountName); + + // Save + info->Save(); + + BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) << + " name changed to " << rNewAccountName); + + return 0; +} + +int BackupStoreAccountsControl::PrintAccountInfo(int32_t ID) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr<UnixUser> user; // used to reset uid when we return + + if(!OpenAccount(ID, rootDir, discSetNum, user, + NULL /* no write lock needed for this read-only operation */)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to display info."); + return 1; + } + + // Load it in + std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, + rootDir, discSetNum, true /* ReadOnly */)); + + // Then print out lots of info + std::cout << FormatUsageLineStart("Account ID", mMachineReadableOutput) << + BOX_FORMAT_ACCOUNT(ID) << std::endl; + std::cout << FormatUsageLineStart("Account Name", mMachineReadableOutput) << + info->GetAccountName() << std::endl; + std::cout << FormatUsageLineStart("Last object ID", mMachineReadableOutput) << + BOX_FORMAT_OBJECTID(info->GetLastObjectIDUsed()) << std::endl; + std::cout << FormatUsageLineStart("Used", mMachineReadableOutput) << + BlockSizeToString(info->GetBlocksUsed(), + info->GetBlocksHardLimit(), discSetNum) << std::endl; + std::cout << FormatUsageLineStart("Current files", + mMachineReadableOutput) << + BlockSizeToString(info->GetBlocksInCurrentFiles(), + info->GetBlocksHardLimit(), discSetNum) << std::endl; + std::cout << FormatUsageLineStart("Old files", mMachineReadableOutput) << + BlockSizeToString(info->GetBlocksInOldFiles(), + info->GetBlocksHardLimit(), discSetNum) << std::endl; + std::cout << FormatUsageLineStart("Deleted files", mMachineReadableOutput) << + BlockSizeToString(info->GetBlocksInDeletedFiles(), + info->GetBlocksHardLimit(), discSetNum) << std::endl; + std::cout << FormatUsageLineStart("Directories", mMachineReadableOutput) << + BlockSizeToString(info->GetBlocksInDirectories(), + info->GetBlocksHardLimit(), discSetNum) << std::endl; + std::cout << FormatUsageLineStart("Soft limit", mMachineReadableOutput) << + BlockSizeToString(info->GetBlocksSoftLimit(), + info->GetBlocksHardLimit(), discSetNum) << std::endl; + std::cout << FormatUsageLineStart("Hard limit", mMachineReadableOutput) << + BlockSizeToString(info->GetBlocksHardLimit(), + info->GetBlocksHardLimit(), discSetNum) << std::endl; + std::cout << FormatUsageLineStart("Client store marker", mMachineReadableOutput) << + info->GetLastObjectIDUsed() << std::endl; + std::cout << FormatUsageLineStart("Current Files", mMachineReadableOutput) << + info->GetNumCurrentFiles() << std::endl; + std::cout << FormatUsageLineStart("Old Files", mMachineReadableOutput) << + info->GetNumOldFiles() << std::endl; + std::cout << FormatUsageLineStart("Deleted Files", mMachineReadableOutput) << + info->GetNumDeletedFiles() << std::endl; + std::cout << FormatUsageLineStart("Directories", mMachineReadableOutput) << + info->GetNumDirectories() << std::endl; + std::cout << FormatUsageLineStart("Enabled", mMachineReadableOutput) << + (info->IsAccountEnabled() ? "yes" : "no") << std::endl; + + return 0; +} + +int BackupStoreAccountsControl::SetAccountEnabled(int32_t ID, bool enabled) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr<UnixUser> user; // used to reset uid when we return + NamedLock writeLock; + + if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to change enabled flag."); + return 1; + } + + // Load it in + std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, + rootDir, discSetNum, false /* ReadOnly */)); + info->SetAccountEnabled(enabled); + info->Save(); + return 0; +} + +int BackupStoreAccountsControl::DeleteAccount(int32_t ID, bool AskForConfirmation) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr<UnixUser> user; // used to reset uid when we return + NamedLock writeLock; + + // Obtain a write lock, as the daemon user + if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " for deletion."); + return 1; + } + + // Check user really wants to do this + if(AskForConfirmation) + { + BOX_WARNING("Really delete account " << + BOX_FORMAT_ACCOUNT(ID) << "? (type 'yes' to confirm)"); + char response[256]; + if(::fgets(response, sizeof(response), stdin) == 0 || ::strcmp(response, "yes\n") != 0) + { + BOX_NOTICE("Deletion cancelled."); + return 0; + } + } + + // Back to original user, but write lock is maintained + user.reset(); + + std::auto_ptr<BackupStoreAccountDatabase> db( + BackupStoreAccountDatabase::Read( + mConfig.GetKeyValue("AccountDatabase"))); + + // Delete from account database + db->DeleteEntry(ID); + + // Write back to disc + db->Write(); + + // Remove the store files... + + // First, become the user specified in the config file + std::string username; + { + const Configuration &rserverConfig(mConfig.GetSubConfiguration("Server")); + if(rserverConfig.KeyExists("User")) + { + username = rserverConfig.GetKeyValue("User"); + } + } + + // Become the right user + if(!username.empty()) + { + // Username specified, change... + user.reset(new UnixUser(username)); + user->ChangeProcessUser(true /* temporary */); + // Change will be undone when user goes out of scope + } + + // Secondly, work out which directories need wiping + std::vector<std::string> toDelete; + RaidFileController &rcontroller(RaidFileController::GetController()); + RaidFileDiscSet discSet(rcontroller.GetDiscSet(discSetNum)); + for(RaidFileDiscSet::const_iterator i(discSet.begin()); i != discSet.end(); ++i) + { + if(std::find(toDelete.begin(), toDelete.end(), *i) == toDelete.end()) + { + toDelete.push_back((*i) + DIRECTORY_SEPARATOR + rootDir); + } + } + +#ifdef WIN32 + // Cannot remove files while holding a lock on them + writeLock.ReleaseLock(); +#endif + + int retcode = 0; + + // Thirdly, delete the directories... + for(std::vector<std::string>::const_iterator d(toDelete.begin()); d != toDelete.end(); ++d) + { + BOX_NOTICE("Deleting store directory " << (*d) << "..."); + // Just use the rm command to delete the files + std::string cmd("rm -rf "); + cmd += *d; + // Run command + if(::system(cmd.c_str()) != 0) + { + BOX_ERROR("Failed to delete files in " << (*d) << + ", delete them manually."); + retcode = 1; + } + } + + // Success! + return retcode; +} + +bool BackupStoreAccountsControl::OpenAccount(int32_t ID, std::string &rRootDirOut, + int &rDiscSetOut, std::auto_ptr<UnixUser> apUser, NamedLock* pLock) +{ + // Load in the account database + std::auto_ptr<BackupStoreAccountDatabase> db( + BackupStoreAccountDatabase::Read( + mConfig.GetKeyValue("AccountDatabase"))); + + // Exists? + if(!db->EntryExists(ID)) + { + BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << + " does not exist."); + return false; + } + + // Get info from the database + BackupStoreAccounts acc(*db); + acc.GetAccountRoot(ID, rRootDirOut, rDiscSetOut); + + // Get the user under which the daemon runs + std::string username; + { + const Configuration &rserverConfig(mConfig.GetSubConfiguration("Server")); + if(rserverConfig.KeyExists("User")) + { + username = rserverConfig.GetKeyValue("User"); + } + } + + // Become the right user + if(!username.empty()) + { + // Username specified, change... + apUser.reset(new UnixUser(username)); + apUser->ChangeProcessUser(true /* temporary */); + // Change will be undone when apUser goes out of scope + // in the caller. + } + + if(pLock) + { + acc.LockAccount(ID, *pLock); + } + + return true; +} + +int BackupStoreAccountsControl::CheckAccount(int32_t ID, bool FixErrors, bool Quiet) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr<UnixUser> user; // used to reset uid when we return + NamedLock writeLock; + + if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " for checking."); + return 1; + } + + // Check it + BackupStoreCheck check(rootDir, discSetNum, ID, FixErrors, Quiet); + check.Check(); + + return check.ErrorsFound()?1:0; +} + +int BackupStoreAccountsControl::CreateAccount(int32_t ID, int32_t DiscNumber, + int32_t SoftLimit, int32_t HardLimit) +{ + // Load in the account database + std::auto_ptr<BackupStoreAccountDatabase> db( + BackupStoreAccountDatabase::Read( + mConfig.GetKeyValue("AccountDatabase"))); + + // Already exists? + if(db->EntryExists(ID)) + { + BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << + " already exists."); + return 1; + } + + // Get the user under which the daemon runs + std::string username; + { + const Configuration &rserverConfig(mConfig.GetSubConfiguration("Server")); + if(rserverConfig.KeyExists("User")) + { + username = rserverConfig.GetKeyValue("User"); + } + } + + // Create it. + BackupStoreAccounts acc(*db); + acc.Create(ID, DiscNumber, SoftLimit, HardLimit, username); + + BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) << " created."); + + return 0; +} + +int BackupStoreAccountsControl::HousekeepAccountNow(int32_t ID) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr<UnixUser> user; // used to reset uid when we return + + if(!OpenAccount(ID, rootDir, discSetNum, user, + NULL /* housekeeping locks the account itself */)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " for housekeeping."); + return 1; + } + + HousekeepStoreAccount housekeeping(ID, rootDir, discSetNum, NULL); + bool success = housekeeping.DoHousekeeping(); + + if(!success) + { + BOX_ERROR("Failed to lock account " << BOX_FORMAT_ACCOUNT(ID) + << " for housekeeping: perhaps a client is " + "still connected?"); + return 1; + } + else + { + BOX_TRACE("Finished housekeeping on account " << + BOX_FORMAT_ACCOUNT(ID)); + return 0; + } +} + diff --git a/lib/backupstore/BackupStoreAccounts.h b/lib/backupstore/BackupStoreAccounts.h index 3163f15c..5ee67312 100644 --- a/lib/backupstore/BackupStoreAccounts.h +++ b/lib/backupstore/BackupStoreAccounts.h @@ -51,5 +51,39 @@ private: BackupStoreAccountDatabase &mrDatabase; }; +class Configuration; +class UnixUser; + +class BackupStoreAccountsControl +{ +private: + const Configuration& mConfig; + bool mMachineReadableOutput; + +public: + BackupStoreAccountsControl(const Configuration& config, + bool machineReadableOutput = false); + + void CheckSoftHardLimits(int64_t SoftLimit, int64_t HardLimit); + int BlockSizeOfDiscSet(int discSetNum); + std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int discSetNum); + int64_t SizeStringToBlocks(const char *string, int discSetNum); + bool OpenAccount(int32_t ID, std::string &rRootDirOut, + int &rDiscSetOut, std::auto_ptr<UnixUser> apUser, NamedLock* pLock); + int SetLimit(int32_t ID, const char *SoftLimitStr, + const char *HardLimitStr); + int SetAccountName(int32_t ID, const std::string& rNewAccountName); + int PrintAccountInfo(int32_t ID); + int SetAccountEnabled(int32_t ID, bool enabled); + int DeleteAccount(int32_t ID, bool AskForConfirmation); + int CheckAccount(int32_t ID, bool FixErrors, bool Quiet); + int CreateAccount(int32_t ID, int32_t DiscNumber, int32_t SoftLimit, + int32_t HardLimit); + int HousekeepAccountNow(int32_t ID); +}; + +// max size of soft limit as percent of hard limit +#define MAX_SOFT_LIMIT_SIZE 97 + #endif // BACKUPSTOREACCOUNTS__H |