summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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