X-Git-Url: http://git.vrable.net/?a=blobdiff_plain;f=remote.cc;fp=remote.cc;h=1e77333aa0eadcd1c1143ea6a887d417b0b28b11;hb=dfb3bcd8cbcc6aa8737deddd332884e23d0e4b22;hp=0000000000000000000000000000000000000000;hpb=a285d431d827a198c6eb45c44158048deca3d772;p=cumulus.git diff --git a/remote.cc b/remote.cc new file mode 100644 index 0000000..1e77333 --- /dev/null +++ b/remote.cc @@ -0,0 +1,164 @@ +/* LBS: An LFS-inspired filesystem backup system + * Copyright (C) 2006 Michael Vrable + * + * Backup data (segments and backup descriptors) may be stored on a remote + * fileserver instead of locally. The only local storage needed is for the + * local database and some temporary space for staging files before they are + * transferred to the remote server. + * + * Like encryption, remote storage is handled through the use of external + * scripts that are called when a file is to be transferred. */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "remote.h" +#include "store.h" + +using std::string; + +RemoteStore::RemoteStore(const string &stagedir) +{ + staging_dir = stagedir; + + /* A background thread is created for each RemoteStore to manage the actual + * transfers to a remote server. The main program thread can enqueue + * RemoteFile objects to be transferred asynchronously. */ + pthread_mutex_init(&lock, NULL); + pthread_cond_init(&cond, NULL); + terminate = false; + busy = true; + files_outstanding = 0; + + if (pthread_create(&thread, NULL, RemoteStore::start_transfer_thread, + (void *)this) != 0) { + fprintf(stderr, "Cannot create remote storage thread: %m\n"); + throw IOException("pthread_create"); + } +} + +/* The RemoteStore destructor will terminate the background transfer thread. + * It will wait for all work to finish. */ +RemoteStore::~RemoteStore() +{ + pthread_mutex_lock(&lock); + terminate = true; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&lock); + + if (pthread_join(thread, NULL) != 0) { + fprintf(stderr, "Warning: Unable to join storage thread: %m\n"); + } + + assert(files_outstanding == 0); + + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); +} + +/* Prepare to write out a new file. Returns a RemoteFile object. The file + * will initially be created in a temporary directory. When the file is + * written out, the RemoteFile object should be passed to RemoteStore::enqueue, + * which will upload it to the remote server. */ +RemoteFile *RemoteStore::alloc_file(const string &name) +{ + fprintf(stderr, "Allocate file: %s\n", name.c_str()); + pthread_mutex_lock(&lock); + files_outstanding++; + pthread_mutex_unlock(&lock); + return new RemoteFile(this, name, staging_dir + "/" + name); +} + +/* Request that a file be transferred to the remote server. The actual + * transfer will happen asynchronously in another thread. The call to enqueue + * may block, however, if there is a backlog of data to be transferred. + * Ownership of the RemoteFile object is transferred; the RemoteStore will be + * responsible for its destruction. */ +void RemoteStore::enqueue(RemoteFile *file) +{ + fprintf(stderr, "Enqueue: %s\n", file->remote_path.c_str()); + + pthread_mutex_lock(&lock); + + while (transfer_queue.size() >= MAX_QUEUE_SIZE) + pthread_cond_wait(&cond, &lock); + + transfer_queue.push_back(file); + files_outstanding--; + busy = true; + + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&lock); +} + +/* Wait for all transfers to finish. */ +void RemoteStore::sync() +{ + fprintf(stderr, "RemoteStore::sync() start\n"); + pthread_mutex_lock(&lock); + + while (busy) + pthread_cond_wait(&cond, &lock); + + pthread_mutex_unlock(&lock); + fprintf(stderr, "RemoteStore::sync() end\n"); +} + +void *RemoteStore::start_transfer_thread(void *arg) +{ + RemoteStore *store = static_cast(arg); + store->transfer_thread(); + return NULL; +} + +/* Background thread for transferring backups to a remote server. */ +void RemoteStore::transfer_thread() +{ + while (true) { + RemoteFile *file = NULL; + + // Wait for a file to transfer + pthread_mutex_lock(&lock); + while (transfer_queue.empty() && !terminate) { + busy = false; + pthread_cond_broadcast(&cond); + pthread_cond_wait(&cond, &lock); + } + if (terminate && transfer_queue.empty()) { + busy = false; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&lock); + break; + } + busy = true; + file = transfer_queue.front(); + transfer_queue.pop_front(); + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&lock); + + // Transfer the file + fprintf(stderr, "Start transfer: %s\n", file->remote_path.c_str()); + // TODO + fprintf(stderr, "Finish transfer: %s\n", file->remote_path.c_str()); + + delete file; + } +} + +RemoteFile::RemoteFile(RemoteStore *remote, + const string &name, const string &local_path) +{ + remote_store = remote; + this->local_path = local_path; + this->remote_path = name; + + fd = open(local_path.c_str(), O_WRONLY | O_CREAT, 0666); + if (fd < 0) + throw IOException("Error opening output file"); +}