summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsr2012-08-31 22:30:52 +0200
committersr2012-08-31 22:30:52 +0200
commit51cc7103be13a9cb756d6a89d7e3306d4ef517ed (patch)
tree1ab6468b3ae4550a389b1b78d63bfece8975c036 /src
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')
-rw-r--r--src/config.h3
-rw-r--r--src/server/ipc.c240
-rw-r--r--src/server/net.c21
-rw-r--r--src/server/server.c31
-rw-r--r--src/server/server.h8
-rw-r--r--src/server/utils.c312
-rw-r--r--src/server/utils.h4
-rw-r--r--src/server/xmlutil.c27
-rw-r--r--src/server/xmlutil.h26
9 files changed, 454 insertions, 218 deletions
diff --git a/src/config.h b/src/config.h
index 7a68f7a..688b51c 100644
--- a/src/config.h
+++ b/src/config.h
@@ -24,7 +24,6 @@
// +++++ Network +++++
// Default port
#define PORT 5003
-#define PORTSTR "5003"
// Protocol version should be increased whenever new features/messages are added,
// so either the client or server can run in compatibility mode, or they can
@@ -71,6 +70,6 @@
#define DEFAULT_CLIENT_CONFIG_FILE "/etc/dnbd3/client.conf"
#define UNIX_SOCKET "/run/dnbd3-server.sock"
#define UNIX_SOCKET_GROUP "dnbd"
-#define IPC_PORT 5004
+#define MAX_IPC_PAYLOAD 3000
#endif /* CONFIG_H_ */
diff --git a/src/server/ipc.c b/src/server/ipc.c
index 38066c8..ebc907b 100644
--- a/src/server/ipc.c
+++ b/src/server/ipc.c
@@ -25,6 +25,7 @@
#include <sys/stat.h>
#include <grp.h>
#include <stdlib.h>
+#include <sys/ioctl.h>
#include <unistd.h>
#include <pthread.h>
#include <netinet/in.h>
@@ -34,6 +35,7 @@
#include <libxml/parser.h>
#include <libxml/xpath.h>
+#include "xmlutil.h"
#include "ipc.h"
#include "../config.h"
@@ -41,14 +43,24 @@
#include "utils.h"
#include "memlog.h"
+#define IPC_PORT (PORT+1)
+
static int server_sock = -1;
static volatile int keep_running = 1;
static char *payload = NULL;
+#define char_repeat_br(_c, _times) do { \
+ int _makro_i_ = (_times); \
+ while (--_makro_i_ >= 0) putchar(_c); \
+ putchar('\n'); \
+} while (0)
+
static int ipc_receive(int client_sock);
static int get_highest_fd(GSList *sockets);
static int send_reply(int client_sock, void *data_in, int len);
static int recv_data(int client_sock, void *buffer_out, int len);
+static int is_password_correct(xmlDocPtr doc);
+static int get_terminal_width();
static int get_highest_fd(GSList *sockets)
{
@@ -67,7 +79,11 @@ static int get_highest_fd(GSList *sockets)
void *dnbd3_ipc_mainloop()
{
- payload = malloc(MAX_PAYLOAD);
+
+ // Check version and initialize
+ LIBXML_TEST_VERSION
+
+ payload = malloc(MAX_IPC_PAYLOAD);
if (payload == NULL)
{
memlogf("[CRITICAL] Couldn't allocate IPC payload buffer. IPC disabled.");
@@ -173,8 +189,6 @@ void *dnbd3_ipc_mainloop()
flags = 0;
fcntl(server_sock, F_SETFL, flags | O_NONBLOCK);
- xmlInitParser();
-
while (keep_running)
{
readset = exceptset = all_sockets;
@@ -358,7 +372,7 @@ static int ipc_receive(int client_sock)
int ret, locked;
int return_value = 0;
- xmlDocPtr doc = NULL;
+ xmlDocPtr docReply = NULL, docRequest = NULL;
xmlNodePtr root_node, images_node, clients_node, tmp_node, log_parent_node, log_node;
xmlChar *xmlbuff;
int buffersize;
@@ -374,7 +388,7 @@ static int ipc_receive(int client_sock)
if (header.size != 0)
{
// Message has payload, receive it
- if (header.size > MAX_PAYLOAD)
+ if (header.size > MAX_IPC_PAYLOAD)
{
memlogf("[WARNING] IPC command with payload of %u bytes ignored.", (unsigned int)header.size);
return 0;
@@ -396,13 +410,13 @@ static int ipc_receive(int client_sock)
case IPC_INFO:
locked = 0;
xmlbuff = NULL;
- doc = xmlNewDoc(BAD_CAST "1.0");
- if (doc == NULL)
+ docReply = xmlNewDoc(BAD_CAST "1.0");
+ if (docReply == NULL)
goto get_info_reply_cleanup;
- root_node = xmlNewNode(NULL, BAD_CAST "info");
+ root_node = xmlNewNode(NULL, BAD_CAST "data");
if (root_node == NULL)
goto get_info_reply_cleanup;
- xmlDocSetRootElement(doc, root_node);
+ xmlDocSetRootElement(docReply, root_node);
// Images
images_node = xmlNewNode(NULL, BAD_CAST "images");
@@ -420,7 +434,7 @@ static int ipc_receive(int client_sock)
tmp_node = xmlNewNode(NULL, BAD_CAST "image");
if (tmp_node == NULL)
goto get_info_reply_cleanup;
- xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->name);
+ xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->config_group);
xmlNewProp(tmp_node, BAD_CAST "atime", BAD_CAST time_buff);
xmlNewProp(tmp_node, BAD_CAST "rid", BAD_CAST rid);
xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST image->file);
@@ -458,13 +472,13 @@ static int ipc_receive(int client_sock)
char *log = fetchlog(0);
if (log == NULL)
log = "LOG IS NULL";
- log_node = xmlNewCDataBlock(doc, BAD_CAST log, strlen(log));
+ log_node = xmlNewCDataBlock(docReply, BAD_CAST log, strlen(log));
if (log_node == NULL)
goto get_info_reply_cleanup;
xmlAddChild(log_parent_node, log_node);
// Dump and send
- xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
+ xmlDocDumpFormatMemory(docReply, &xmlbuff, &buffersize, 1);
header.size = htonl(buffersize);
header.error = htonl(0);
@@ -477,7 +491,6 @@ get_info_reply_cleanup:
return_value = send_reply(client_sock, xmlbuff, buffersize);
// Cleanup
xmlFree(xmlbuff);
- xmlFreeDoc(doc);
free(log);
break;
@@ -490,55 +503,50 @@ get_info_reply_cleanup:
return_value = send_reply(client_sock, &header, sizeof(header));
break;
}
- doc = xmlReadMemory(payload, header.size, "noname.xml", NULL, 0);
+ docRequest = xmlReadMemory(payload, header.size, "noname.xml", NULL, 0);
- if (doc)
+ if (docRequest)
{
- xmlXPathContextPtr xpathCtx = NULL;
- xmlXPathObjectPtr xpathObj = NULL;
- xmlNodeSetPtr nodes = NULL;
+ if (!is_password_correct(docRequest))
+ {
+ header.error = htonl(ERROR_WRONG_PASSWORD);
+ header.size = htonl(0);
+ return_value = send_reply(client_sock, &header, sizeof(header));
+ break;
+ }
+
xmlNodePtr cur = NULL;
+ int count = 0;
- xpathCtx = xmlXPathNewContext(doc);
- if (xpathCtx == NULL)
- goto add_del_cleanup;
- xpathObj = xmlXPathEvalExpression(BAD_CAST "/info/images/image", xpathCtx);
- if (xpathObj == NULL)
- goto add_del_cleanup;
- nodes = xpathObj->nodesetval;
- if (nodes == NULL || nodes->nodeNr < 1)
- goto add_del_cleanup;
- cur = nodes->nodeTab[0];
- if (cur->type == XML_ELEMENT_NODE)
+ FOR_EACH_NODE(docRequest, "/data/images/image", cur)
{
- dnbd3_image_t image;
- memset(&image, 0, sizeof(dnbd3_image_t));
- image.name = (char *)xmlGetNoNsProp(cur, BAD_CAST "name");
- char *rid_str = (char *)xmlGetNoNsProp(cur, BAD_CAST "rid");
- image.file = (char *)xmlGetNoNsProp(cur, BAD_CAST "file");
- image.cache_file = (char *)xmlGetNoNsProp(cur, BAD_CAST "cache");
- if (image.name && rid_str && image.file && image.cache_file)
+ if (cur->type == XML_ELEMENT_NODE)
{
- image.rid = atoi(rid_str);
- if (cmd == IPC_ADDIMG)
- header.error = htonl(dnbd3_add_image(&image));
+ ++count;
+ dnbd3_image_t image;
+ memset(&image, 0, sizeof(dnbd3_image_t));
+ image.config_group = (char *)xmlGetNoNsProp(cur, BAD_CAST "name");
+ char *rid_str = (char *)xmlGetNoNsProp(cur, BAD_CAST "rid");
+ image.file = (char *)xmlGetNoNsProp(cur, BAD_CAST "file");
+ image.cache_file = (char *)xmlGetNoNsProp(cur, BAD_CAST "cache");
+ if (image.config_group && rid_str && image.file && image.cache_file)
+ {
+ image.rid = atoi(rid_str);
+ if (cmd == IPC_ADDIMG)
+ header.error = htonl(dnbd3_add_image(&image));
+ else
+ header.error = htonl(dnbd3_del_image(&image));
+ }
else
- header.error = htonl(dnbd3_del_image(&image));
+ header.error = htonl(ERROR_MISSING_ARGUMENT);
+ xmlFree(image.config_group);
+ xmlFree(rid_str);
+ xmlFree(image.file);
+ xmlFree(image.cache_file);
}
- else
- header.error = htonl(ERROR_MISSING_ARGUMENT);
- xmlFree(image.name);
- xmlFree(rid_str);
- xmlFree(image.file);
- xmlFree(image.cache_file);
- }
- else
+ } END_FOR_EACH;
+ if (count == 0)
header.error = htonl(ERROR_MISSING_ARGUMENT);
-
-add_del_cleanup:
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
- xmlFreeDoc(doc);
}
else
header.error = htonl(ERROR_INVALID_XML);
@@ -555,6 +563,10 @@ add_del_cleanup:
break;
}
+
+ xmlFreeDoc(docReply);
+ xmlFreeDoc(docRequest);
+
return return_value;
}
@@ -562,6 +574,9 @@ void dnbd3_ipc_send(int cmd)
{
int client_sock, size;
+ // Check version and initialize
+ LIBXML_TEST_VERSION
+
#ifdef IPC_TCP
struct sockaddr_in server;
struct timeval client_timeout;
@@ -629,72 +644,76 @@ void dnbd3_ipc_send(int cmd)
if (doc)
{
- int n, i;
-
- xmlXPathContextPtr xpathCtx;
- xmlXPathObjectPtr xpathObj;
- xmlChar *xpathExpr;
- xmlNodeSetPtr nodes;
+ int count;
+ int term_width = get_terminal_width();
xmlNodePtr cur;
// Print log
- xpathExpr = BAD_CAST "/info/log";
- xpathCtx = xmlXPathNewContext(doc);
- xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
- if (xpathObj->nodesetval && xpathObj->nodesetval->nodeTab && xpathObj->nodesetval->nodeTab[0])
+ xmlChar *log = getTextFromPath(doc, "/data/log");
+ if (log)
{
- printf("--- Last log lines ----\n%s\n\n", xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]));
+ printf("--- Last log lines ----\n%s\n\n", log);
}
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
+
+ int watime = 0, wname = 0, wrid = 5;
+ FOR_EACH_NODE(doc, "/data/images/image", cur)
+ {
+ if (cur->type == XML_ELEMENT_NODE)
+ {
+ xmlChar *atime = xmlGetNoNsProp(cur, BAD_CAST "atime");
+ xmlChar *vid = xmlGetNoNsProp(cur, BAD_CAST "name");
+ xmlChar *rid = xmlGetNoNsProp(cur, BAD_CAST "rid");
+ watime = MAX(watime, xmlStrlen(atime));
+ wname = MAX(wname, xmlStrlen(vid));
+ wrid = MAX(wrid, xmlStrlen(rid));
+ // Too lazy to free vars, client will exit anyways
+ }
+ } END_FOR_EACH;
+
+ char format[100];
+ snprintf(format, 100,
+ "%%-%ds %%-%ds %%%ds %%s\n", watime, wname, wrid);
// Print images
- xpathExpr = BAD_CAST "/info/images/image";
- xpathCtx = xmlXPathNewContext(doc);
- xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
- printf("Exported images (atime, name, rid, file):\n");
- printf("========================================\n");
- nodes = xpathObj->nodesetval;
- n = (nodes) ? nodes->nodeNr : 0;
- for (i = 0; i < n; ++i)
+ printf("Exported images\n");
+ printf(format, "atime", "name", "rid", "file");
+ char_repeat_br('=', term_width);
+ count = 0;
+ FOR_EACH_NODE(doc, "/data/images/image", cur)
{
- if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE)
+ if (cur->type == XML_ELEMENT_NODE)
{
- cur = nodes->nodeTab[i];
+ ++count;
xmlChar *atime = xmlGetNoNsProp(cur, BAD_CAST "atime");
xmlChar *vid = xmlGetNoNsProp(cur, BAD_CAST "name");
xmlChar *rid = xmlGetNoNsProp(cur, BAD_CAST "rid");
xmlChar *file = xmlGetNoNsProp(cur, BAD_CAST "file");
- printf("%s\t%s\t%s\t%s\n", atime, vid, rid, file);
+ printf(format, atime, vid, rid, file);
+ // Too lazy to free vars, client will exit anyways
}
- }
- printf("\nNumber images: %d\n\n", n);
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
+ } END_FOR_EACH;
+ char_repeat_br('=', term_width);
+ printf("\nNumber of images: %d\n\n", count);
// Print clients
- xpathExpr = BAD_CAST "/info/clients/client";
- xpathCtx = xmlXPathNewContext(doc);
- xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
printf("Connected clients (ip, file):\n");
- printf("=============================\n");
- nodes = xpathObj->nodesetval;
- n = (nodes) ? nodes->nodeNr : 0;
- for (i = 0; i < n; ++i)
+ char_repeat_br('=', term_width);
+ count = 0;
+ FOR_EACH_NODE(doc, "/data/clients/client", cur)
{
- if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE)
+ if (cur->type == XML_ELEMENT_NODE)
{
- cur = nodes->nodeTab[i];
+ ++count;
xmlChar *ip = xmlGetNoNsProp(cur, BAD_CAST "ip");
xmlChar *file = xmlGetNoNsProp(cur, BAD_CAST "file");
- printf("%s\t%s\n", ip, file);
+ printf("%-40s %s\n", ip, file);
+ // Too lazy to free vars, client will exit anyways
}
- }
- printf("\nNumber clients: %d\n\n", n);
+ } END_FOR_EACH;
+ char_repeat_br('=', term_width);
+ printf("\nNumber clients: %d\n\n", count);
// Cleanup
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
xmlFreeDoc(doc);
xmlCleanupParser();
@@ -710,3 +729,34 @@ void dnbd3_ipc_send(int cmd)
close(client_sock);
}
+
+/**
+ * Check if the correct server password is present in xpath /data/password
+ * return !=0 if correct, 0 otherwise
+ */
+static int is_password_correct(xmlDocPtr doc)
+{
+ if (_ipc_password == NULL)
+ {
+ memlogf("[WARNING] IPC access granted as no password is set!");
+ return 1;
+ }
+ xmlChar *pass = getTextFromPath(doc, "/data/password");
+ if (pass == NULL)
+ return 0;
+ if (strcmp((char*)pass, _ipc_password) == 0)
+ {
+ xmlFree(pass);
+ return 1;
+ }
+ xmlFree(pass);
+ return 0;
+}
+
+static int get_terminal_width()
+{
+ struct winsize w;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0)
+ return 80;
+ return w.ws_col;
+}
diff --git a/src/server/net.c b/src/server/net.c
index 49cfb24..b1a7b20 100644
--- a/src/server/net.c
+++ b/src/server/net.c
@@ -175,9 +175,15 @@ void *dnbd3_handle_query(void *dnbd3_client)
{
pthread_spin_lock(&_spinlock);
image = dnbd3_get_image(image_name, rid, 0);
+ const time_t now = time(NULL);
if (!image)
{
- printf("[DEBUG] Client requested non-existent image '%s' (rid:%d, protocol:%d)\n", image_name, (int)rid, (int)client_version);
+ printf("[DEBUG] Client requested non-existent image '%s' (rid:%d)\n", image_name, (int)rid);
+ }
+ else if ((image->delete_soft != 0 && image->delete_soft < now)
+ || (image->delete_hard != 0 && image->delete_hard < now))
+ {
+ printf("[DEBUG] Client requested end-of-life image '%s' (rid:%d)\n", image_name, (int)rid);
}
else
{
@@ -267,6 +273,7 @@ void *dnbd3_handle_query(void *dnbd3_client)
{
printf("[ERROR] sendfile failed (image to net)\n");
close(client->sock);
+ client->sock = -1;
}
break;
}
@@ -296,6 +303,7 @@ void *dnbd3_handle_query(void *dnbd3_client)
{
printf("[ERROR] sendfile failed (copy to cache 1)\n");
close(client->sock);
+ client->sock = -1;
// Reset these so we don't update the cache map with false information
dirty = 0;
todo_size = 0;
@@ -320,6 +328,7 @@ void *dnbd3_handle_query(void *dnbd3_client)
{
printf("[ERROR] sendfile failed (copy to cache 2)\n");
close(client->sock);
+ client->sock = -1;
break;
}
dirty = 1;
@@ -344,6 +353,7 @@ void *dnbd3_handle_query(void *dnbd3_client)
{
memlogf("[ERROR] sendfile failed (cache to net)\n");
close(client->sock);
+ client->sock = -1;
}
break;
@@ -374,13 +384,14 @@ void *dnbd3_handle_query(void *dnbd3_client)
}
}
- close(client->sock);
- if (image_file != -1) close(image_file);
- if (image_cache != -1) close(image_cache);
pthread_spin_lock(&_spinlock);
_dnbd3_clients = g_slist_remove(_dnbd3_clients, client);
pthread_spin_unlock(&_spinlock);
- free(client);
+ if (client->sock != -1)
+ close(client->sock);
+ if (image_file != -1) close(image_file);
+ if (image_cache != -1) close(image_cache);
+ g_free(client);
pthread_exit((void *) 0);
}
diff --git a/src/server/server.c b/src/server/server.c
index 8981d77..28da0ae 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -36,7 +36,7 @@
#include "ipc.h"
#include "memlog.h"
-int _sock;
+static int sock;
#ifdef _DEBUG
int _fake_delay = 0;
#endif
@@ -45,6 +45,7 @@ pthread_spinlock_t _spinlock;
GSList *_dnbd3_clients = NULL;
char *_config_file_name = DEFAULT_SERVER_CONFIG_FILE;
char *_local_namespace = NULL;
+char *_ipc_password = NULL;
GSList *_dnbd3_images = NULL; // of dnbd3_image_t
void dnbd3_print_help(char* argv_0)
@@ -75,7 +76,8 @@ void dnbd3_cleanup()
int fd;
memlogf("INFO: Cleanup...\n");
- close(_sock);
+ close(sock);
+ sock = -1;
dnbd3_ipc_shutdown();
@@ -108,11 +110,12 @@ void dnbd3_cleanup()
close(fd);
}
- free(image->name);
- g_free(image->file);
- g_free(image->cache_file);
- free(image->cache_map);
- g_free(image);
+ free(image->cache_map);
+ free(image->config_group);
+ free(image->low_name);
+ free(image->file);
+ free(image->cache_file);
+ g_free(image);
}
g_slist_free(_dnbd3_images);
@@ -202,12 +205,13 @@ int main(int argc, char* argv[])
signal(SIGINT, dnbd3_handle_sigterm);
// setup network
- _sock = dnbd3_setup_socket();
- if (_sock < 0)
+ sock = dnbd3_setup_socket();
+ if (sock < 0)
exit(EXIT_FAILURE);
struct sockaddr_in client;
unsigned int len = sizeof(client);
int fd;
+ time_t next_delete_invocation = 0;
struct timeval timeout;
timeout.tv_sec = SOCKET_TIMEOUT_SERVER;
timeout.tv_usec = 0;
@@ -221,7 +225,7 @@ int main(int argc, char* argv[])
// main loop
while (1)
{
- fd = accept(_sock, (struct sockaddr*) &client, &len);
+ fd = accept(sock, (struct sockaddr*) &client, &len);
if (fd < 0)
{
memlogf("[ERROR] Accept failure");
@@ -263,6 +267,13 @@ int main(int argc, char* argv[])
continue;
}
pthread_detach(dnbd3_client->thread);
+ // Call image deletion function if last call is more than 5 minutes ago
+ const time_t now = time(NULL);
+ if (now < next_delete_invocation)
+ {
+ next_delete_invocation = now + 300;
+ dnbd3_exec_delete(TRUE);
+ }
}
dnbd3_cleanup();
diff --git a/src/server/server.h b/src/server/server.h
index 875a5af..90aae5c 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -30,8 +30,8 @@
typedef struct
{
- char *name; // full name of image, eg. "uni-freiburg/rz/ubuntu-12.04"
- char *low_name; // full name of image, lowercased for comparison
+ char *config_group; // exact name of group in config file that represents this image
+ char *low_name; // full (global) name of image, lowercased for comparison, eg. "uni-freiburg/rz/ubuntu-12.04"
int rid; // revision of provided image
char *file; // path to image file or device
uint64_t filesize; // size of image
@@ -40,6 +40,8 @@ typedef struct
uint8_t *cache_map; // cache map telling which parts are locally cached
char *cache_file; // path to local cache of image (in case the image is read from a dnbd3 device)
char working; // whether this image is considered working. local images are "working" if the local file exists, proxied images have to have at least one working upstream server or a complete local cache file
+ time_t delete_soft; // unixtime telling when this image should be deleted. if there are still clients using this image it weill be kept, but new clients requesting the image will be rejected. 0 = never
+ time_t delete_hard; // unixtime telling when this image should be deleted, no matter if there are still clients connected. 0 = never
} dnbd3_image_t;
typedef struct
@@ -53,7 +55,7 @@ typedef struct
extern GSList *_dnbd3_clients; // of dnbd3_client_t
extern pthread_spinlock_t _spinlock;
-extern char *_config_file_name, *_local_namespace;
+extern char *_config_file_name, *_local_namespace, *_ipc_password;
extern GSList *_dnbd3_images; // of dnbd3_image_t
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;
+} //*/
diff --git a/src/server/utils.h b/src/server/utils.h
index 18b205a..e923f82 100644
--- a/src/server/utils.h
+++ b/src/server/utils.h
@@ -26,6 +26,7 @@
#ifndef UTILS_H_
#define UTILS_H_
+#define ERROR_OK 0
#define ERROR_FILE_NOT_FOUND 1
#define ERROR_IMAGE_ALREADY_EXISTS 2
#define ERROR_CONFIG_FILE_PERMISSIONS 3
@@ -37,11 +38,14 @@
#define ERROR_INVALID_XML 9
#define ERROR_UNKNOWN_COMMAND 10
#define ERROR_SEE_LOG 11
+#define ERROR_WRONG_PASSWORD 12
void dnbd3_load_config();
int dnbd3_add_image(dnbd3_image_t *image);
int dnbd3_del_image(dnbd3_image_t *image);
+void dnbd3_exec_delete(int save_if_changed);
+
dnbd3_image_t* dnbd3_get_image(char *name, int rid, const char do_lock);
void dnbd3_handle_sigpipe(int signum);
diff --git a/src/server/xmlutil.c b/src/server/xmlutil.c
new file mode 100644
index 0000000..62736cd
--- /dev/null
+++ b/src/server/xmlutil.c
@@ -0,0 +1,27 @@
+#include "xmlutil.h"
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+
+xmlChar *getTextFromPath(xmlDocPtr doc, char *xpath)
+{
+ xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
+ if (xpathCtx == NULL)
+ return NULL;
+ xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(BAD_CAST xpath, xpathCtx);
+ if (xpathObj == NULL)
+ {
+ xmlXPathFreeContext(xpathCtx);
+ return NULL;
+ }
+ xmlChar *retval = NULL;
+ if (xpathObj->stringval)
+ retval = xmlStrdup(xpathObj->stringval);
+ else if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 && xpathObj->nodesetval->nodeTab && xpathObj->nodesetval->nodeTab[0])
+ {
+ retval = xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
+ }
+ xmlXPathFreeObject(xpathObj);
+ xmlXPathFreeContext(xpathCtx);
+ return retval;
+}
diff --git a/src/server/xmlutil.h b/src/server/xmlutil.h
new file mode 100644
index 0000000..46a5790
--- /dev/null
+++ b/src/server/xmlutil.h
@@ -0,0 +1,26 @@
+#ifndef XMLUTIL_H_
+#define XMLUTIL_H_
+
+#include <libxml/xmlstring.h>
+#include <libxml/tree.h>
+
+xmlChar *getTextFromPath(xmlDocPtr doc, char *xpath);
+
+#define FOR_EACH_NODE(_doc, _path, _node) do { \
+ xmlXPathContextPtr _makro_xpathCtx = xmlXPathNewContext(_doc); \
+ if (_makro_xpathCtx) { \
+ xmlXPathObjectPtr _makro_xpathObj = xmlXPathEvalExpression(BAD_CAST _path, _makro_xpathCtx); \
+ if (_makro_xpathObj) { \
+ int _makro_i_; \
+ if (_makro_xpathObj->nodesetval) for (_makro_i_ = 0; _makro_i_ < _makro_xpathObj->nodesetval->nodeNr; ++_makro_i_) \
+ { _node = _makro_xpathObj->nodesetval->nodeTab[_makro_i_];
+
+#define END_FOR_EACH \
+ } } \
+ xmlXPathFreeObject(_makro_xpathObj); \
+ } \
+ xmlXPathFreeContext(_makro_xpathCtx); \
+ } while(0)
+
+
+#endif