summaryrefslogtreecommitdiffstats
path: root/src/server/utils.c
diff options
context:
space:
mode:
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;
+} //*/