1 /* Cumulus: Smart Filesystem Backup to Dumb Servers
3 * Copyright (C) 2008 The Regents of the University of California
4 * Written by Michael Vrable <mvrable@cs.ucsd.edu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 /* Backup data (segments and backup descriptors) may be stored on a remote
22 * fileserver instead of locally. The only local storage needed is for the
23 * local database and some temporary space for staging files before they are
24 * transferred to the remote server.
26 * Like encryption, remote storage is handled through the use of external
27 * scripts that are called when a file is to be transferred. */
32 #include <sys/types.h>
44 RemoteStore::RemoteStore(const string &stagedir)
46 staging_dir = stagedir;
48 /* A background thread is created for each RemoteStore to manage the actual
49 * transfers to a remote server. The main program thread can enqueue
50 * RemoteFile objects to be transferred asynchronously. */
51 pthread_mutex_init(&lock, NULL);
52 pthread_cond_init(&cond, NULL);
55 files_outstanding = 0;
57 if (pthread_create(&thread, NULL, RemoteStore::start_transfer_thread,
59 fprintf(stderr, "Cannot create remote storage thread: %m\n");
60 throw IOException("pthread_create");
64 /* The RemoteStore destructor will terminate the background transfer thread.
65 * It will wait for all work to finish. */
66 RemoteStore::~RemoteStore()
68 pthread_mutex_lock(&lock);
70 pthread_cond_broadcast(&cond);
71 pthread_mutex_unlock(&lock);
73 if (pthread_join(thread, NULL) != 0) {
74 fprintf(stderr, "Warning: Unable to join storage thread: %m\n");
77 assert(files_outstanding == 0);
79 pthread_cond_destroy(&cond);
80 pthread_mutex_destroy(&lock);
83 /* Prepare to write out a new file. Returns a RemoteFile object. The file
84 * will initially be created in a temporary directory. When the file is
85 * written out, the RemoteFile object should be passed to RemoteStore::enqueue,
86 * which will upload it to the remote server. */
87 RemoteFile *RemoteStore::alloc_file(const string &name, const string &type)
89 pthread_mutex_lock(&lock);
91 pthread_mutex_unlock(&lock);
92 return new RemoteFile(this, name, type, staging_dir + "/" + name);
95 /* Request that a file be transferred to the remote server. The actual
96 * transfer will happen asynchronously in another thread. The call to enqueue
97 * may block, however, if there is a backlog of data to be transferred.
98 * Ownership of the RemoteFile object is transferred; the RemoteStore will be
99 * responsible for its destruction. */
100 void RemoteStore::enqueue(RemoteFile *file)
102 pthread_mutex_lock(&lock);
104 while (transfer_queue.size() >= MAX_QUEUE_SIZE)
105 pthread_cond_wait(&cond, &lock);
107 transfer_queue.push_back(file);
111 pthread_cond_broadcast(&cond);
112 pthread_mutex_unlock(&lock);
115 /* Wait for all transfers to finish. */
116 void RemoteStore::sync()
118 pthread_mutex_lock(&lock);
121 pthread_cond_wait(&cond, &lock);
123 pthread_mutex_unlock(&lock);
126 void *RemoteStore::start_transfer_thread(void *arg)
128 RemoteStore *store = static_cast<RemoteStore *>(arg);
129 store->transfer_thread();
133 /* Background thread for transferring backups to a remote server. */
134 void RemoteStore::transfer_thread()
137 RemoteFile *file = NULL;
139 // Wait for a file to transfer
140 pthread_mutex_lock(&lock);
141 while (transfer_queue.empty() && !terminate) {
143 pthread_cond_broadcast(&cond);
144 pthread_cond_wait(&cond, &lock);
146 if (terminate && transfer_queue.empty()) {
148 pthread_cond_broadcast(&cond);
149 pthread_mutex_unlock(&lock);
153 file = transfer_queue.front();
154 transfer_queue.pop_front();
155 pthread_cond_broadcast(&cond);
156 pthread_mutex_unlock(&lock);
159 if (backup_script != "") {
162 fprintf(stderr, "Unable to fork for upload script: %m\n");
163 throw IOException("fork: upload script");
166 string cmd = backup_script;
167 cmd += " " + file->local_path + " " + file->type + " "
169 execlp("/bin/sh", "/bin/sh", "-c", cmd.c_str(), NULL);
170 throw IOException("exec failed");
174 waitpid(pid, &status, 0);
175 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
176 fprintf(stderr, "Warning: error code from upload script: %d\n",
180 if (unlink(file->local_path.c_str()) < 0) {
181 fprintf(stderr, "Warning: Deleting temporary file %s: %m\n",
182 file->local_path.c_str());
190 RemoteFile::RemoteFile(RemoteStore *remote,
191 const string &name, const string &type,
192 const string &local_path)
194 remote_store = remote;
196 this->local_path = local_path;
197 this->remote_path = name;
199 fd = open(local_path.c_str(), O_WRONLY | O_CREAT, 0666);
201 throw IOException("Error opening output file");