summaryrefslogtreecommitdiffstats
path: root/src/server/utils.c
diff options
context:
space:
mode:
authorsr2012-08-31 22:30:52 +0200
committersr2012-08-31 22:30:52 +0200
commit51cc7103be13a9cb756d6a89d7e3306d4ef517ed (patch)
tree1ab6468b3ae4550a389b1b78d63bfece8975c036 /src/server/utils.c
parentlast minute messup fixed (diff)
downloaddnbd3-51cc7103be13a9cb756d6a89d7e3306d4ef517ed.tar.gz
dnbd3-51cc7103be13a9cb756d6a89d7e3306d4ef517ed.tar.xz
dnbd3-51cc7103be13a9cb756d6a89d7e3306d4ef517ed.zip
[SERVER] Added soft and hard timeouts for image deletion: reject any new clients for an image where the soft timeout has been reached, kill all clients for an image where the hard timeout has been reached and remove it from the server. Check for the hard timeout every five minutes
[SERVER] Re-Implement image deletion to work with image names instead of vids [SERVER] Add helper functions to simplify dealing with libxml2
Diffstat (limited to 'src/server/utils.c')
-rw-r--r--src/server/utils.c312
1 files changed, 209 insertions, 103 deletions
diff --git a/src/server/utils.c b/src/server/utils.c
index ddc52d8..665d96d 100644
--- a/src/server/utils.c
+++ b/src/server/utils.c
@@ -31,15 +31,20 @@
#include "utils.h"
#include "memlog.h"
+// Keep parsed config file in memory so it doesn't need to be parsed again every time it's modified
+static GKeyFile* _config_handle = NULL;
+
static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *port);
static char is_valid_namespace(char *namespace);
static char is_valid_imagename(char *namespace);
static void strtolower(char *string);
-static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file, gchar **servers, gsize num_servers);
+static dnbd3_image_t* prepare_image(char *image_name, int rid, char *image_file, char *cache_file, gchar **servers, gsize num_servers);
+static int save_config();
+//static char* get_local_image_name(char *global_name);
/**
* Parse IPv4 or IPv6 address in string representation to a suitable format usable by the BSD socket library
- * @string eg. "1.2.3.4" or "2a01::10:5", optially with port appended, eg "1.2.3.4:6666" or "2a01::10:5:6666"
+ * @string eg. "1.2.3.4" or "2a01::10:5", optially with port appended, eg "1.2.3.4:6666" or "[2a01::10:5]:6666"
* @af will contain either AF_INET or AF_INET6
* @addr will contain the address in network representation
* @port will contain the port in network representation, defaulting to #define PORT if none was given
@@ -153,22 +158,21 @@ static void strtolower(char *string)
void dnbd3_load_config()
{
gint i;
- GKeyFile* gkf;
- if (_local_namespace != NULL || _dnbd3_images != NULL)
+ if (_config_handle != NULL)
{
printf("dnbd3_load_config() called more than once\n\n");
exit(EXIT_FAILURE);
}
- gkf = g_key_file_new();
- if (!g_key_file_load_from_file(gkf, _config_file_name, G_KEY_FILE_NONE, NULL))
+ _config_handle = g_key_file_new();
+ if (!g_key_file_load_from_file(_config_handle, _config_file_name, G_KEY_FILE_NONE, NULL))
{
printf("ERROR: Config file not found: %s\n", _config_file_name);
exit(EXIT_FAILURE);
}
- _local_namespace = g_key_file_get_string(gkf, "settings", "default_namespace", NULL);
+ _local_namespace = g_key_file_get_string(_config_handle, "settings", "default_namespace", NULL);
if (_local_namespace && !is_valid_namespace(_local_namespace))
{
memlogf("[ERROR] Ignoring default namespace: '%s' is not a valid namespace", _local_namespace);
@@ -176,13 +180,15 @@ void dnbd3_load_config()
_local_namespace = NULL;
}
+ _ipc_password = g_key_file_get_string(_config_handle, "settings", "password", NULL);
+
gchar **groups = NULL;
gsize section_count;
- groups = g_key_file_get_groups(gkf, &section_count);
+ groups = g_key_file_get_groups(_config_handle, &section_count);
for (i = 0; i < section_count; i++)
{
- // Special group
+ // Special group, ignore
if (strcmp(groups[i], "settings") == 0 || strcmp(groups[i], "trusted") == 0)
{
continue;
@@ -190,17 +196,25 @@ void dnbd3_load_config()
// An actual image definition
- int rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL);
+ int rid = g_key_file_get_integer(_config_handle, groups[i], "rid", NULL);
if (rid <= 0)
{
memlogf("[ERROR] Invalid rid '%d' for image '%s'", rid, groups[i]);
continue;
}
- char *image_file = g_key_file_get_string(gkf, groups[i], "file", NULL);
- char *cache_file = g_key_file_get_string(gkf, groups[i], "cache", NULL);
+ const time_t delsoft = g_key_file_get_int64(_config_handle, groups[i], "delete_soft", NULL);
+ const time_t delhard = g_key_file_get_int64(_config_handle, groups[i], "delete_hard", NULL);
+ if ((delsoft != 0 && delsoft < time(NULL)) || (delhard != 0 && delhard < time(NULL)))
+ {
+ memlogf("[INFO] Ignoring image '%s' as its deletion is due", groups[i]);
+ continue;
+ }
+
+ char *image_file = g_key_file_get_string(_config_handle, groups[i], "file", NULL);
+ char *cache_file = g_key_file_get_string(_config_handle, groups[i], "cache", NULL);
gsize num_servers;
- gchar **servers = g_key_file_get_string_list(gkf, groups[i], "servers", &num_servers, NULL);
+ gchar **servers = g_key_file_get_string_list(_config_handle, groups[i], "servers", &num_servers, NULL);
pthread_spin_lock(&_spinlock);
dnbd3_image_t *image = prepare_image(groups[i], rid, image_file, cache_file, servers, num_servers);
@@ -216,7 +230,6 @@ void dnbd3_load_config()
}
g_strfreev(groups);
- g_key_file_free(gkf);
}
int dnbd3_add_image(dnbd3_image_t *image)
@@ -226,15 +239,15 @@ int dnbd3_add_image(dnbd3_image_t *image)
// but better be safe for the future...
pthread_spin_lock(&_spinlock);
if (image->rid == 0)
- { // TODO: globalize image->name somewhere for this call
- const dnbd3_image_t *latest = dnbd3_get_image(image->name, image->rid, 0);
+ {
+ const dnbd3_image_t *latest = dnbd3_get_image(image->config_group, 0, 0);
if (latest)
image->rid = latest->rid + 1;
else
image->rid = 1;
}
- dnbd3_image_t *newimage = prepare_image(image->name, image->rid, image->file, image->cache_file, NULL, 0);
+ dnbd3_image_t *newimage = prepare_image(image->config_group, image->rid, image->file, image->cache_file, NULL, 0);
if (newimage)
{
_dnbd3_images = g_slist_prepend(_dnbd3_images, image);
@@ -246,105 +259,94 @@ int dnbd3_add_image(dnbd3_image_t *image)
}
// Adding image was successful, write config file
- GKeyFile* gkf;
- gkf = g_key_file_new();
- if (!g_key_file_load_from_file(gkf, _config_file_name, G_KEY_FILE_NONE, NULL))
- {
- printf("ERROR: Config file not found: %s\n", _config_file_name);
- exit(EXIT_FAILURE);
- }
-
- g_key_file_set_integer(gkf, image->name, "rid", image->rid);
- g_key_file_set_string(gkf, image->name, "file", image->file);
- //g_key_file_set_string(gkf, image->name, "servers", image->serverss); // TODO: Save servers as string
- g_key_file_set_string(gkf, image->name, "cache", image->cache_file);
+ g_key_file_set_integer(_config_handle, image->config_group, "rid", image->rid);
+ g_key_file_set_string(_config_handle, image->config_group, "file", image->file);
+ //g_key_file_set_string(_config_handle, image->name, "servers", image->serverss); // TODO: Save servers as string
+ g_key_file_set_string(_config_handle, image->config_group, "cache", image->cache_file);
- gchar* data = g_key_file_to_data(gkf, NULL, NULL);
-
- FILE *f = fopen(_config_file_name, "w");
- if (f >= 0)
- {
- fputs((char*) data, f);
- fclose(f);
- pthread_spin_unlock(&_spinlock);
- g_free(data);
- g_key_file_free(gkf);
- memlogf("[INFO] Added new image '%s' (rid %d)", newimage->name, newimage->rid);
- return 0;
- }
pthread_spin_unlock(&_spinlock);
- g_free(data);
- g_key_file_free(gkf);
- memlogf("[ERROR] Image added, but config file is not writable (%s)", _config_file_name);
- return ERROR_SEE_LOG;
+
+ const int ret = save_config();
+ if (ret == ERROR_OK)
+ memlogf("[INFO] Added new image '%s' (rid %d)", newimage->config_group, newimage->rid);
+ else
+ memlogf("[INFO] Added new image '%s' (rid %d), but config file could not be written (%s)", newimage->config_group, newimage->rid, _config_file_name);
+ return ret;
}
int dnbd3_del_image(dnbd3_image_t *image)
{
- return ERROR_IMAGE_NOT_FOUND; // TODO: Make it work with image names
- /*
- if (image->rid == 0)
- {
- printf("ERROR: Delete with rid=0 is not allowed\n");
- return ERROR_RID;
- }
+ if (image->rid <= 0) // Require a specific rid on deletion
+ return ERROR_RID;
+ pthread_spin_lock(&_spinlock);
+ dnbd3_image_t *existing_image = dnbd3_get_image(image->config_group, image->rid, 0);
+ if(existing_image == NULL)
+ {
+ pthread_spin_unlock(&_spinlock);
+ return ERROR_IMAGE_NOT_FOUND;
+ }
- dnbd3_image_t* tmp = dnbd3_get_image(image->vid, image->rid);
- if (!tmp)
- {
- printf("ERROR: Image not found: (%d,%d)\n", image->vid, image->rid);
- return ERROR_IMAGE_NOT_FOUND;
- }
+ existing_image->delete_soft = 1; // TODO: Configurable.
+ existing_image->delete_hard = time(NULL) + 86400; // TODO: Configurable
+ g_key_file_set_int64(_config_handle, image->config_group, "delete_soft", existing_image->delete_soft);
+ g_key_file_set_int64(_config_handle, image->config_group, "delete_hard", existing_image->delete_hard);
- GSList *iterator = NULL;
- for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
- {
- dnbd3_client_t *client = iterator->data;
- if (tmp == client->image)
- {
- printf("ERROR: Delete is not allowed, image is in use (%d,%d)\n", tmp->vid, tmp->rid);
- return ERROR_IMAGE_IN_USE;
- }
- }
+ pthread_spin_unlock(&_spinlock);
+ dnbd3_exec_delete(FALSE);
+ existing_image = NULL;
+
+ const int ret = save_config();
+ if (ret == ERROR_OK)
+ memlogf("[INFO] Marked for deletion: '%s' (rid %d)", image->config_group, image->rid);
+ else
+ memlogf("[WARNING] Marked for deletion: '%s' (rid %d), but config file could not be written (%s)", image->config_group, image->rid, _config_file_name);
+ return ret;
+}
- GKeyFile* gkf;
- gkf = g_key_file_new();
- if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL))
+static int save_config()
+{
+ pthread_spin_lock(&_spinlock);
+ char* data = (char*)g_key_file_to_data(_config_handle, NULL, NULL);
+ if (data == NULL)
{
- printf("ERROR: Config file not found: %s\n", file);
- exit(EXIT_FAILURE);
+ pthread_spin_unlock(&_spinlock);
+ memlogf("[ERROR] g_key_file_to_data() failed");
+ return ERROR_UNSPECIFIED_ERROR;
}
- g_key_file_remove_group(gkf, tmp->group, NULL);
- gchar* data = g_key_file_to_data(gkf, NULL, NULL);
-
- FILE* f = fopen(file,"w");
- if (f)
+ FILE *f = fopen(_config_file_name, "w");
+ if (f < 0)
{
- fputs((char*) data, f);
+ pthread_spin_unlock(&_spinlock);
+ g_free(data);
+ return ERROR_CONFIG_FILE_PERMISSIONS;
+ }
+ fputs("# Do not edit this file while dnbd3-server is running\n", f);
+ fputs(data, f);
fclose(f);
+ pthread_spin_unlock(&_spinlock);
g_free(data);
- g_key_file_free(gkf);
- // TODO: unlink image file
return 0;
- }
- else
- {
- g_free(data);
- g_key_file_free(gkf);
- printf("ERROR: Config file is not writable: %s\n", file);
- return ERROR_CONFIG_FILE_PERMISSIONS;
- }
- */
}
dnbd3_image_t* dnbd3_get_image(char *name_orig, int rid, const char do_lock)
{
dnbd3_image_t *result = NULL, *image;
GSList *iterator;
- char name[strlen(name_orig) + 1];
- strcpy(name, name_orig);
+ // For comparison, make sure the name is global and lowercased
+ int slen;
+ int islocal = (strchr(name_orig, '/') == NULL);
+ if (islocal)
+ slen = strlen(name_orig) + strlen(_local_namespace) + 2;
+ else
+ slen = strlen(name_orig) + 1;
+ char name[slen];
+ if (islocal)
+ sprintf(name, "%s/%s", _local_namespace, name_orig);
+ else
+ strcpy(name, name_orig);
strtolower(name);
+ // Now find the image
if (do_lock)
pthread_spin_lock(&_spinlock);
for (iterator = _dnbd3_images; iterator; iterator = iterator->next)
@@ -418,22 +420,22 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file,
if (strchr(image_name, '/') == NULL)
{ // Local image, build global name
- image->name = calloc(strlen(_local_namespace) + strlen(image_name) + 2, sizeof(char));
- sprintf(image->name, "%s/%s", _local_namespace, image_name);
+ image->low_name = calloc(strlen(_local_namespace) + strlen(image_name) + 2, sizeof(char));
+ sprintf(image->low_name, "%s/%s", _local_namespace, image_name);
}
else
{
- image->name = strdup(image_name);
+ image->low_name = strdup(image_name);
}
- if (dnbd3_get_image(image->name, rid, 0))
+ if (dnbd3_get_image(image->low_name, rid, 0))
{
- memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image->name, rid);
+ memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image_name, rid);
goto error;
}
- image->low_name = strdup(image->name);
strtolower(image->low_name);
+ image->config_group = strdup(image_name);
image->rid = rid;
const char relayed = (image_file == NULL || image_file == '\0');
@@ -460,7 +462,8 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file,
const off_t size = lseek(fd, 0, SEEK_END);
if (size <= 0)
{
- memlogf("[ERROR] File '%s' of image '%s' has size '%lld'. Image ignored.", image->file, image->name, (long long)size);
+ memlogf("[ERROR] File '%s' of image '%s' has size '%lld'. Image ignored.",
+ image->file, image_name, (long long)size);
goto error;
}
image->filesize = (uint64_t)size;
@@ -512,7 +515,7 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file,
// "+ (1 << 15) - 1" is required to account for the last bit of
// the image that is smaller than 32kib
// this would be the case whenever the image file size is not a
- // multiple of 32kib (= the number of blocks is not dividable by 8)
+ // multiple of 32kib (= the number of blocks is not divisible by 8)
// ie: if the image is 49152 bytes and you do 49152 >> 15 you get 1,
// but you actually need 2 bytes to have a complete cache map
char tmp[strlen(image->cache_file) + 5];
@@ -543,7 +546,7 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file,
if ((image->cache_map[map_len_bytes - 1] & last_byte) != last_byte)
image->working = 0;
else
- memlogf("[INFO] Instantly publishing relayed image '%s' because the local cache copy is complete", image->name);
+ memlogf("[INFO] Instantly publishing relayed image '%s' because the local cache copy is complete", image_name);
}
/*
@@ -564,10 +567,113 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file,
error:
// Free stuff. Some pointers might be zero, but calling free() on those is safe.
free(image->cache_map);
- free(image->name);
+ free(image->config_group);
free(image->low_name);
free(image->file);
free(image->cache_file);
g_free(image);
return NULL;
}
+
+/**
+ * Iterate over all images and delete them if appropriate.
+ */
+void dnbd3_exec_delete(int save_if_changed)
+{
+ int changed = FALSE;
+ const time_t now = time(NULL);
+ GSList *image_iterator, *client_iterator;
+ char ipstr[100];
+
+ pthread_spin_lock(&_spinlock);
+ for (image_iterator = _dnbd3_images; image_iterator; image_iterator = image_iterator->next)
+ {
+ dnbd3_image_t *image = image_iterator->data;
+ int delete_now = TRUE;
+ if (image->delete_hard != 0 && image->delete_hard < now)
+ {
+ // Drop all clients still using it
+ for (client_iterator = _dnbd3_clients; client_iterator; client_iterator = client_iterator->next)
+ {
+ dnbd3_client_t *client = client_iterator->data;
+ if (client->image != image)
+ continue;
+ // Kill client's connection
+ *ipstr = '\0';
+ inet_ntop(client->addrtype, client->ipaddr, ipstr, 100);
+ memlogf("[INFO] delete_hard of %s reached; dropping client %s", image->config_group, ipstr);
+ const int fd = client->sock;
+ client->sock = -1;
+ close(fd);
+ delete_now = FALSE; // Wait for all clients being actually dropped; deletion will happen on next dnbd3_exec_delete()
+ }
+ } // END delete_hard image
+ else if (image->delete_soft != 0 && image->delete_soft < now)
+ {
+ // Image should be soft-deleted
+ // Check if it is still in use
+ for (client_iterator = _dnbd3_clients; client_iterator; client_iterator = client_iterator->next)
+ {
+ const dnbd3_client_t *client = client_iterator->data;
+ if (client->image == image)
+ { // Yep, still in use, keep it
+ delete_now = FALSE;
+ break;
+ }
+ }
+ }
+ else // Neither hard nor soft delete, keep it
+ delete_now = FALSE;
+ if (delete_now)
+ {
+ // Image was not in use and should be deleted, free it!
+ memlogf("[INFO] Freeing end-of-life image %s", image->config_group);
+ changed = TRUE;
+ _dnbd3_images = g_slist_remove(_dnbd3_images, image); // Remove from image list
+ g_key_file_remove_group(_config_handle, image->config_group, NULL); // Also remove from config file
+ // Free any allocated memory
+ free(image->cache_map);
+ free(image->config_group);
+ free(image->low_name);
+ free(image->file);
+ free(image->cache_file);
+ g_free(image);
+ // Restart iteration as it would be messed up now
+ image_iterator = _dnbd3_images;
+ }
+ } // END image iteration
+ pthread_spin_lock(&_spinlock);
+
+ if (changed && save_if_changed)
+ save_config();
+}
+
+/**
+ * Return local image name for a global image name
+ * eg. "uni-freiburg/rz/ubuntu 12.04" -> "ubuntu 12.04"
+ * ONLY IF the local name space really is "uni-freiburg/rz"
+ * Returns NULL otherwise
+ * The returned pointer points to memory inside the passed
+ * string (if not NULL), so do not modify or free
+ * / <---
+static char* get_local_image_name(char *global_name)
+{
+ if (_local_namespace == NULL)
+ return NULL; // No local namespace defined, so it cannot be local
+ char *first_slash = strchr(global_name, '/');
+ if (first_slash == NULL)
+ return global_name; // Already local
+ const size_t buflen = strlen(_local_namespace) + 1;
+ if (first_slash - global_name + 1 != buflen)
+ return NULL; // Namespaces have different length, cannot be same
+ char namespace[buflen];
+ char passedname[buflen];
+ strcpy(namespace, _local_namespace);
+ strncpy(passedname, global_name, buflen);
+ passedname[buflen] = '\0';
+ strtolower(namespace);
+ strtolower(passedname);
+ if (strcmp(namespace, passedname) == 0)
+ return global_name + buflen; // points somewhere into passed buffer
+ return NULL;
+} //*/