summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsr2012-09-06 20:53:14 +0200
committersr2012-09-06 20:53:14 +0200
commit3937c3b43161e130bf867183f4e36ac9786fab35 (patch)
tree61d475fd537ebda0c08848877741cf437040f2a7
parent... (diff)
downloaddnbd3-3937c3b43161e130bf867183f4e36ac9786fab35.tar.gz
dnbd3-3937c3b43161e130bf867183f4e36ac9786fab35.tar.xz
dnbd3-3937c3b43161e130bf867183f4e36ac9786fab35.zip
[SERVER] Automatically assign and connect a dnbd3 device to a relayed image
-rw-r--r--src/kernel/blk.c14
-rw-r--r--src/kernel/net.c7
-rw-r--r--src/server/job.c188
-rw-r--r--src/server/job.h1
-rw-r--r--src/server/saveload.c6
-rw-r--r--src/server/server.c1
-rw-r--r--src/server/server.h2
7 files changed, 206 insertions, 13 deletions
diff --git a/src/kernel/blk.c b/src/kernel/blk.c
index 4282295..388f8b6 100644
--- a/src/kernel/blk.c
+++ b/src/kernel/blk.c
@@ -148,6 +148,9 @@ int dnbd3_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u
dev->imgname = imgname;
dev->rid = msg->rid;
dev->is_server = msg->is_server;
+ // Forget all alt servers on explicit connect, set first al server to initial server
+ memset(dev->alt_servers, 0, sizeof(dev->alt_servers[0])*NUMBER_SERVERS);
+ memcpy(dev->alt_servers, &dev->initial_server, sizeof(dev->alt_servers[0]));
blk_queue->backing_dev_info.ra_pages = (msg->read_ahead_kb * 1024) / PAGE_CACHE_SIZE;
if (dnbd3_net_connect(dev) == 0)
{
@@ -175,9 +178,14 @@ int dnbd3_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u
break;
case IOCTL_SWITCH:
- dnbd3_net_disconnect(dev);
- memcpy(&dev->cur_server.host, &msg->host, sizeof(msg->host));
- result = dnbd3_net_connect(dev);
+ if (memcmp(&dev->cur_server.host, &msg->host, sizeof(msg->host)))
+ {
+ dnbd3_net_disconnect(dev);
+ dev->cur_server.host = msg->host;
+ result = dnbd3_net_connect(dev);
+ }
+ else
+ result = 0;
break;
case IOCTL_ADD_SRV:
diff --git a/src/kernel/net.c b/src/kernel/net.c
index 323c59c..67a4b17 100644
--- a/src/kernel/net.c
+++ b/src/kernel/net.c
@@ -131,15 +131,12 @@ int dnbd3_net_connect(dnbd3_device_t *dev)
// do some checks before connecting
- if (is_same_server(&dev->cur_server, &dev->initial_server))
+ if (!dev->is_server && is_same_server(&dev->cur_server, &dev->initial_server))
{
// Forget all known alt servers
memset(dev->alt_servers, 0, sizeof(dev->alt_servers[0])*NUMBER_SERVERS);
memcpy(dev->alt_servers, &dev->initial_server, sizeof(dev->alt_servers[0]));
- if (!dev->is_server)
- {
- get_servers = 1;
- }
+ get_servers = 1;
}
if (dev->better_sock)
{
diff --git a/src/server/job.c b/src/server/job.c
index a2f6ab5..44beb00 100644
--- a/src/server/job.c
+++ b/src/server/job.c
@@ -9,6 +9,8 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -45,7 +47,10 @@ static char keep_running = TRUE;
// Private functions
static char *get_free_device();
+static void return_free_device(char *name);
+static void connect_proxy_images();
static void query_servers();
+static char *create_cache_filename(char *name, int rid, char *buffer, int maxlen);
static void add_alt_server(dnbd3_image_t *image, dnbd3_host_t *host);
static void remove_alt_server(dnbd3_trusted_server_t *server);
static void update_image_atimes(time_t now);
@@ -97,6 +102,8 @@ void *dnbd3_job_thread(void *data)
next_delete_invocation = starttime + 300;
dnbd3_exec_delete(TRUE);
}
+ // Check for proxied images that have not been set up yet
+ connect_proxy_images();
// TODO: Replicate proxied images (limited bandwidth)
// Query other servers for new images/status/...
query_servers();
@@ -116,6 +123,125 @@ void dnbd3_job_shutdown()
keep_running = FALSE;
}
+static void connect_proxy_images()
+{
+ int s, n;
+ dnbd3_server_entry_t servers[NUMBER_SERVERS];
+ char imagename[1000];
+ int rid;
+ dnbd3_ioctl_t msg;
+ memset(&msg, 0, sizeof(dnbd3_ioctl_t));
+ msg.len = (uint16_t)sizeof(dnbd3_ioctl_t);
+ msg.read_ahead_kb = DEFAULT_READ_AHEAD_KB;
+ msg.is_server = TRUE;
+ for (n = 0 ;; ++n)
+ {
+ pthread_spin_lock(&_spinlock);
+ dnbd3_image_t *image = g_slist_nth_data(_dnbd3_images, n);
+ if (image == NULL)
+ { // End of list reached
+ pthread_spin_unlock(&_spinlock);
+ break;
+ }
+ if (image->working || image->file || image->low_name == NULL)
+ { // Nothing to do
+ pthread_spin_unlock(&_spinlock);
+ continue;
+ }
+ char *devname = get_free_device();
+ if (devname == NULL)
+ { // All devices busy
+ pthread_spin_unlock(&_spinlock);
+ continue;
+ }
+ // Remember image information as the image pointer isn't
+ // guaranteed to stay valid after unlocking
+ snprintf(imagename, 1000, "%s", image->low_name);
+ rid = image->rid;
+ memcpy(servers, image->servers, sizeof(servers[0]) * NUMBER_SERVERS);
+ pthread_spin_unlock(&_spinlock);
+ int dh = open(devname, O_WRONLY);
+ if (dh < 0)
+ continue;
+ for (s = 0; s < NUMBER_SERVERS; ++s)
+ {
+ if (servers[s].host.type == 0)
+ continue;
+ // connect device
+ msg.host = servers[s].host;
+ msg.imgname = imagename;
+ msg.imgnamelen = strlen(imagename);
+ msg.rid = rid;
+ if (ioctl(dh, IOCTL_OPEN, &msg) < 0)
+ continue;
+ // connected
+ for (++s; s < NUMBER_SERVERS; ++s)
+ {
+ if (servers[s].host.type == 0)
+ continue;
+ msg.host = servers[s].host;
+ if (ioctl(dh, IOCTL_ADD_SRV, &msg) < 0)
+ memlogf("[WARNING] Could not add alt server to proxy device");
+ }
+ // LOCK + UPDATE
+ pthread_spin_lock(&_spinlock);
+ if (g_slist_find(_dnbd3_images, image) == NULL)
+ { // Image not in list anymore, was deleted in meantime...
+ if (ioctl(dh, IOCTL_CLOSE, &msg) < 0)
+ memlogf("[WARNING] Could not close device after use - lost %s", devname);
+ else
+ return_free_device(devname);
+ }
+ else
+ {
+ image->file = strdup(devname);
+ const off_t off = lseek(dh, 0, SEEK_END);
+ if (off < 0)
+ memlogf("[ERROR] Could not get image size from connected device %s", devname);
+ else if (image->filesize != 0 && image->filesize != off)
+ memlogf("[ERROR] Remote and local size of image do not match: %llu != %llu for %s", (unsigned long long)off, (unsigned long long)image->filesize, image->low_name);
+ else
+ image->working = TRUE;
+ image->filesize = (uint64_t)off;
+ if (image->cache_file != NULL && image->working && image->cache_map == NULL)
+ {
+ const int mapsize = IMGSIZE_TO_MAPBYTES(image->filesize);
+ image->cache_map = calloc(mapsize, 1);
+ off_t cachelen = -1;
+ int ch = open(image->cache_file, O_RDONLY);
+ if (ch >= 0)
+ {
+ cachelen = lseek(ch, 0, SEEK_END);
+ close(ch);
+ }
+ if (cachelen == image->filesize)
+ {
+ char mapfile[strlen(image->cache_file) + 5];
+ sprintf(mapfile, "%s.map", image->cache_file);
+ int cmh = open(mapfile, O_RDONLY);
+ if (cmh >= 0)
+ {
+ if (lseek(cmh, 0, SEEK_END) != mapsize)
+ memlogf("[WARNING] Existing cache map has wrong size.");
+ else
+ {
+ lseek(cmh, 0, SEEK_CUR);
+ read(cmh, image->cache_map, mapsize);
+ printf("[DEBUG] Found existing cache file and map for %s\n", image->low_name);
+ }
+ close(cmh);
+ }
+ }
+ }
+ memlogf("[INFO] Enabled relayed image %s", image->low_name);
+ }
+ pthread_spin_unlock(&_spinlock);
+ break;
+ }
+ close(dh);
+ }
+}
+
static void update_image_atimes(time_t now)
{
GSList *iterator;
@@ -142,9 +268,9 @@ static void query_servers()
dnbd3_trusted_server_t *server;
dnbd3_host_t host;
struct sockaddr_in addr4;
+ char xmlbuffer[MAX_IPC_PAYLOAD];
for (num = 0;; ++num)
{
- char *xmlbuffer = NULL;
// "Iterate" this way to prevent holding the lock for a long time, although it is possible to skip a server this way...
pthread_spin_lock(&_spinlock);
server = g_slist_nth_data(_trusted_servers, num);
@@ -209,7 +335,6 @@ static void query_servers()
memlogf("[WARNING] XML payload from other server exceeds MAX_IPC_PAYLOAD (%d > %d)", (int)header.size, (int)MAX_IPC_PAYLOAD);
goto communication_error;
}
- xmlbuffer = malloc(header.size);
if (!recv_data(client_sock, xmlbuffer, header.size))
{
printf("[DEBUG] Error reading XML payload from other server.\n");
@@ -294,11 +419,13 @@ static void query_servers()
pthread_spin_unlock(&_spinlock);
// Image is NEW, add it!
// TODO: Check if replication is requested for this namespace
- // TODO: Automatically generate cache file
dnbd3_image_t newimage;
+ char cachefile[70];
memset(&newimage, 0, sizeof(newimage));
newimage.config_group = xmlbuffer;
newimage.rid = rid;
+ if (_cache_dir)
+ newimage.cache_file = create_cache_filename(xmlbuffer, rid, cachefile, 70);
dnbd3_add_image(&newimage);
pthread_spin_lock(&_spinlock);
local_image = dnbd3_get_image(xmlbuffer, rid, FALSE);
@@ -325,7 +452,6 @@ free_current_image:
continue;
communication_error:
close(client_sock);
- free(xmlbuffer);
pthread_spin_lock(&_spinlock);
if (g_slist_find(_trusted_servers, server))
{
@@ -336,6 +462,46 @@ communication_error:
}
}
+static char *create_cache_filename(char *name, int rid, char *buffer, int maxlen)
+{
+ if (_cache_dir == NULL)
+ return NULL;
+ size_t cdl = strlen(_cache_dir);
+ if (maxlen < 15 + cdl)
+ return NULL;
+ if (strlen(name) + 16 + cdl < maxlen)
+ snprintf(buffer, maxlen, "%s/%s_rid_%d.cache", _cache_dir, name, rid);
+ else
+ {
+ char *slash = strrchr(name, '/');
+ if (slash == NULL)
+ {
+ snprintf(buffer, maxlen - 17, "%s/%s", _cache_dir, name);
+ snprintf(buffer + maxlen - 17, 17, "_rid_%d.cache", rid);
+ }
+ else
+ {
+ snprintf(buffer, maxlen, "%s/%s", _cache_dir, name);
+ snprintf(buffer + cdl, maxlen - cdl, "%s_rid_%d.cache", slash, rid);
+ }
+ }
+ char *ptr = buffer + cdl + 1;
+ while (*ptr)
+ {
+ if (*ptr == '/' || *ptr < 32 || *ptr == ' ' || *ptr == '\\' || *ptr == '*' || *ptr == '?')
+ *ptr = '_';
+ ++ptr;
+ }
+ FILE *fh;
+ while ((fh = fopen(buffer, "rb")))
+ { // Alter file name as long as a file by that name already exists
+ fclose(fh);
+ char *c = buffer + rand() % strlen(buffer);
+ *c = rand() % 26 + 'A';
+ }
+ return buffer;
+}
+
/**
* !! Call this while holding the lock !!
*/
@@ -468,3 +634,17 @@ static char *get_free_device()
memlogf("[WARNING] No more free dnbd3 devices - proxy mode probably affected.");
return NULL;
}
+
+static void return_free_device(char *name)
+{
+ if (devices == NULL)
+ return;
+ int i;
+ for (i = 0; i < num_devices; ++i)
+ {
+ if (devices[i].available || strcmp(devices[i].name, name) != 0)
+ continue;
+ devices[i].available = TRUE;
+ break;
+ }
+}
diff --git a/src/server/job.h b/src/server/job.h
index d8bb090..8072148 100644
--- a/src/server/job.h
+++ b/src/server/job.h
@@ -2,5 +2,6 @@
#define JOB_H_
void *dnbd3_job_thread(void *data);
+void dnbd3_job_shutdown();
#endif
diff --git a/src/server/saveload.c b/src/server/saveload.c
index e56c5d1..b41bb56 100644
--- a/src/server/saveload.c
+++ b/src/server/saveload.c
@@ -67,6 +67,12 @@ void dnbd3_load_config()
srand(time(NULL));
_ipc_password = g_key_file_get_string(_config_handle, "settings", "password", NULL);
+ _cache_dir = g_key_file_get_string(_config_handle, "settings", "cache_dir", NULL);
+
+ if (_cache_dir == NULL)
+ memlogf("[WARNING] No cache dir set! Automatic replication will not work.");
+ else if (access(_cache_dir, R_OK | W_OK) != 0)
+ memlogf("[WARNING] Cache dir '%s' is not readable or writable", _cache_dir);
gchar **groups = NULL;
gsize section_count;
diff --git a/src/server/server.c b/src/server/server.c
index e248a2b..b0068ce 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -47,6 +47,7 @@ GSList *_dnbd3_clients = NULL;
char *_config_file_name = DEFAULT_SERVER_CONFIG_FILE;
char *_local_namespace = NULL;
char *_ipc_password = NULL;
+char *_cache_dir = NULL;
GSList *_dnbd3_images = NULL; // of dnbd3_image_t
GSList *_trusted_servers = NULL;
diff --git a/src/server/server.h b/src/server/server.h
index c23f80f..fda5e46 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -83,7 +83,7 @@ typedef struct
extern GSList *_dnbd3_clients; // of dnbd3_client_t
extern pthread_spinlock_t _spinlock;
-extern char *_config_file_name, *_local_namespace, *_ipc_password;
+extern char *_config_file_name, *_local_namespace, *_ipc_password, *_cache_dir;
extern GSList *_dnbd3_images; // of dnbd3_image_t
extern GSList *_trusted_servers;