summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsr2012-08-30 00:36:54 +0200
committersr2012-08-30 00:36:54 +0200
commit5b9b2174512d97399c68172bffe91cf928b1400a (patch)
treebf2cb9a7ae67cfe922fc7d2a6dcb432eaad4a2d9
parentFix linebreak, add comment (diff)
downloaddnbd3-5b9b2174512d97399c68172bffe91cf928b1400a.tar.gz
dnbd3-5b9b2174512d97399c68172bffe91cf928b1400a.tar.xz
dnbd3-5b9b2174512d97399c68172bffe91cf928b1400a.zip
[SERVER] Set _FILE_OFFSET_BITS=64 so that images >4GiB will be handled properly on 32bit
[SERVER] Change IPC interface to be able to handle more than 1 request per connection [SERVER] Change IPC interface to use select() so it can handle multiple connections at the same time [SERVER] Re-Implement dnbd3_add_image() to work with image list [SERVER] Add lots of sanity/safety checks and error messages when loading/adding an image
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/config.h2
-rw-r--r--src/server/ipc.c843
-rw-r--r--src/server/ipc.h5
-rw-r--r--src/server/server.c11
-rw-r--r--src/server/server.h10
-rw-r--r--src/server/utils.c766
-rw-r--r--src/server/utils.h18
8 files changed, 974 insertions, 682 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a959f66..3a68bce 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,6 +12,7 @@ SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall -Wno-unused-result -D_GNU_SOURCE -D_FILE
SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wno-unused-result -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" )
ADD_DEFINITIONS(-DIPC_TCP)
+ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
FIND_PACKAGE(Threads REQUIRED)
FIND_PACKAGE(PkgConfig REQUIRED)
diff --git a/src/config.h b/src/config.h
index 1cef874..7a68f7a 100644
--- a/src/config.h
+++ b/src/config.h
@@ -42,7 +42,7 @@
#define SOCKET_TIMEOUT_CLIENT_DATA 2
#define SOCKET_TIMEOUT_CLIENT_DISCOVERY 1
-#define NUMBER_SERVERS 8
+#define NUMBER_SERVERS 8 // Number of alt servers per image/device
#define RTT_THRESHOLD_FACTOR(us) (((us) * 2) / 3) // 2/3 = current to best must be 33% worse
#define RTT_UNREACHABLE 0x7FFFFFFul // Use this value for timeout/unreachable as RTT. Don't set too high or you might get overflows. 0x7FFFFFF = 134 seconds
// This must be a power of two:
diff --git a/src/server/ipc.c b/src/server/ipc.c
index 98584a6..e7474e8 100644
--- a/src/server/ipc.c
+++ b/src/server/ipc.c
@@ -29,6 +29,8 @@
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
@@ -39,57 +41,76 @@
#include "utils.h"
#include "memlog.h"
-void* dnbd3_ipc_receive()
-{
- GSList *iterator = NULL;
+static int server_sock = -1;
+static volatile int keep_running = 1;
+static char *payload = NULL;
- struct tm * timeinfo;
- char time_buff[64], rid[20], ipaddr[100];
+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);
- dnbd3_ipc_t header;
- int server_sock, client_sock;
+static int get_highest_fd(GSList *sockets)
+{
+ GSList *iterator;
+ int max = 0;
- struct timeval timeout;
- timeout.tv_sec = 2;
- timeout.tv_usec = 0;
+ for (iterator = sockets; iterator; iterator = iterator->next)
+ {
+ const int fd = (int)(size_t)iterator->data;
+ if (fd > max)
+ max = fd;
+ }
+ return max;
+}
+
+void *dnbd3_ipc_mainloop()
+{
+ payload = malloc(MAX_PAYLOAD);
+ if (payload == NULL)
+ {
+ memlogf("[CRITICAL] Couldn't allocate IPC payload buffer. IPC disabled.");
+ pthread_exit((void *)0);
+ return NULL;
+ }
#ifdef IPC_TCP
- struct sockaddr_in server, client;
- unsigned int len = sizeof(client);
-
- // Create socket
- if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
- {
- perror("ERROR: IPC socket");
- exit(EXIT_FAILURE);
- }
-
- memset(&server, 0, sizeof(server));
- server.sin_family = AF_INET; // IPv4
- server.sin_addr.s_addr = inet_addr("127.0.0.1");
- server.sin_port = htons(IPC_PORT); // set port number
-
- int optval = 1;
- setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
-
- // Bind to socket
- if (bind(server_sock, (struct sockaddr*) &server, sizeof(server)) < 0)
- {
- perror("ERROR: IPC bind");
- exit(EXIT_FAILURE);
- }
-
- // Listen on socket
- if (listen(server_sock, 5) < 0)
- {
- perror("ERROR: IPC listen");
- exit(EXIT_FAILURE);
- }
+ struct sockaddr_in server, client;
+ socklen_t len = sizeof(client);
+
+ // Create socket
+ if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ {
+ perror("ERROR: IPC socket");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&server, 0, sizeof(server));
+ server.sin_family = AF_INET; // IPv4
+ server.sin_addr.s_addr = INADDR_ANY;
+ server.sin_port = htons(IPC_PORT); // set port number
+
+ const int optval = 1;
+ setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+
+ // Bind to socket
+ if (bind(server_sock, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+ perror("ERROR: IPC bind");
+ exit(EXIT_FAILURE);
+ }
+
+ // Listen on socket
+ if (listen(server_sock, 5) < 0)
+ {
+ perror("ERROR: IPC listen");
+ exit(EXIT_FAILURE);
+ }
#else
- struct sockaddr_un server, client;
- unsigned int len = sizeof(client);
+ struct sockaddr_un server, client;
+ socklen_t len = sizeof(client);
- // Create socket
+ // Create socket
if ((server_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
perror("ERROR: IPC socket");
@@ -128,288 +149,481 @@ void* dnbd3_ipc_receive()
}
#endif
- while (1)
- {
- int size;
- char* buf;
- xmlDocPtr doc;
- xmlNodePtr root_node, images_node, clients_node, tmp_node, log_parent_node, log_node;
- xmlChar *xmlbuff;
- int buffersize;
-
- // Accept connection
- if ((client_sock = accept(server_sock, &client, &len)) < 0)
- {
- perror("ERROR: IPC accept");
- exit(EXIT_FAILURE);
- }
-
- setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout));
- setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout));
-
- recv(client_sock, &header, sizeof(header), MSG_WAITALL);
- header.cmd = ntohl(header.cmd);
- header.size = ntohl(header.size);
- header.error = ntohl(header.error);
-
- switch (header.cmd)
- {
- case IPC_EXIT:
- memlogf("INFO: Server shutdown...\n");
- header.size = ntohl(0);
- header.error = ntohl(0);
- send(client_sock, (char *) &header, sizeof(header), MSG_WAITALL);
- close(client_sock);
- close(server_sock);
- dnbd3_cleanup();
- break;
-
- case IPC_RELOAD:
- header.size = ntohl(0);
- header.error = ntohl(ERROR_UNKNOWN);
- send(client_sock, (char *) &header, sizeof(header), MSG_WAITALL);
- close(client_sock);
- break;
-
- case IPC_INFO:
- doc = xmlNewDoc(BAD_CAST "1.0");
- root_node = xmlNewNode(NULL, BAD_CAST "info");
- xmlDocSetRootElement(doc, root_node);
-
- // Images
- images_node = xmlNewNode(NULL, BAD_CAST "images");
- xmlAddChild(root_node, images_node);
- pthread_spin_lock(&_spinlock);
- for (iterator = _dnbd3_images; iterator; iterator = iterator->next)
- {
- const dnbd3_image_t *image = iterator->data;
- sprintf(rid,"%d",image->rid);
- timeinfo = localtime(&image->atime);
- strftime(time_buff,64,"%d.%m.%y %H:%M:%S",timeinfo);
- tmp_node = xmlNewNode(NULL, BAD_CAST "image");
- xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->name);
- 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);
- xmlNewProp(tmp_node, BAD_CAST "servers", BAD_CAST "???");
- xmlNewProp(tmp_node, BAD_CAST "cache", BAD_CAST image->cache_file);
- xmlAddChild(images_node, tmp_node);
+ // Run connection-accepting loop
+
+ fd_set all_sockets, readset, exceptset;
+
+ GSList *sockets = NULL, *iterator;
+
+ int client_sock, ret, flags;
+ int maxfd = server_sock + 1;
+ int error_count = 0;
+
+ struct timeval client_timeout, select_timeout;
+ client_timeout.tv_sec = 0;
+ client_timeout.tv_usec = 500 * 1000;
+
+ FD_ZERO(&all_sockets);
+ FD_SET(server_sock, &all_sockets);
+
+ // Make listening socket non-blocking
+ flags = fcntl(server_sock, F_GETFL, 0);
+ if (flags == -1)
+ flags = 0;
+ fcntl(server_sock, F_SETFL, flags | O_NONBLOCK);
+
+ xmlInitParser();
+
+ while (keep_running)
+ {
+ readset = exceptset = all_sockets;
+ select_timeout.tv_sec = 4;
+ select_timeout.tv_usec = 0;
+ ret = select(maxfd, &readset, NULL, &exceptset, &select_timeout);
+ while (ret > 0)
+ {
+ --ret;
+ if (FD_ISSET(server_sock, &readset))
+ {
+ // Accept connection
+ if ((client_sock = accept(server_sock, &client, &len)) < 0)
+ {
+ if (errno != EAGAIN)
+ {
+ memlogf("[ERROR] Error accepting an IPC connection");
+ if (++error_count > 10)
+ goto end_loop;
+ }
+ continue;
+ }
+ error_count = 0;
+ // Apply read/write timeout
+ setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout));
+ setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout));
+ // Make new connection blocking
+ flags = fcntl(client_sock, F_GETFL, 0);
+ if (flags == -1)
+ flags = 0;
+ fcntl(client_sock, F_SETFL, flags & ~(int)O_NONBLOCK);
+ sockets = g_slist_prepend(sockets, (void *)(size_t)client_sock);
+ if (client_sock >= maxfd)
+ maxfd = client_sock + 1;
+ FD_SET(client_sock, &all_sockets);
}
- // Clients
- clients_node = xmlNewNode(NULL, BAD_CAST "clients");
- log_node = xmlAddChild(root_node, clients_node);
- for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
+ else if (FD_ISSET(server_sock, &exceptset))
{
- dnbd3_client_t *client = iterator->data;
- if (client->image)
+ memlogf("[ERROR] An exception occurred on the IPC listening socket.");
+ if (++error_count > 10)
+ goto end_loop;
+ }
+ else
+ {
+ // Must be an active IPC connection
+ int del = -1;
+ for (iterator = sockets; iterator; iterator = iterator->next)
+ {
+ if (del != -1)
+ {
+ // Delete a previously closed connection from list (delayed, otherwise list might get messed up)
+ sockets = g_slist_remove(sockets, (void *)(size_t)del);
+ del = -1;
+ maxfd = get_highest_fd(sockets) + 1;
+ }
+ client_sock = (int)(size_t)iterator->data;
+ if (FD_ISSET(client_sock, &readset))
+ {
+ // Client sending data
+ if (!ipc_receive(client_sock))
+ {
+ // Connection has been closed
+ close(client_sock);
+ del = client_sock;
+ FD_CLR(client_sock, &all_sockets);
+ }
+ }
+ else if (FD_ISSET(client_sock, &exceptset))
+ {
+ // Something unexpected happened, just close connection
+ close(client_sock);
+ del = client_sock;
+ FD_CLR(client_sock, &all_sockets);
+ }
+ }
+ if (del != -1)
{
- tmp_node = xmlNewNode(NULL, BAD_CAST "client");
- *ipaddr = '\0';
- inet_ntop(client->addrtype, client->ipaddr, ipaddr, 100);
- xmlNewProp(tmp_node, BAD_CAST "ip", BAD_CAST ipaddr);
- xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST client->image->file);
- xmlAddChild(clients_node, tmp_node);
+ // In case last socket was closed during iteration
+ sockets = g_slist_remove(sockets, (void *)(size_t)del);
+ maxfd = get_highest_fd(sockets) + 1;
}
}
+ } // End select loop
+ } // End mainloop
+
+end_loop:
+ memlogf("[INFO] Shutting down IPC interface.");
+ if (server_sock != -1)
+ {
+ close(server_sock);
+ server_sock = -1;
+ }
+
+ free(payload);
+ xmlCleanupParser();
+ pthread_exit((void *)0);
+ return NULL;
+}
+
+void dnbd3_ipc_shutdown()
+{
+ keep_running = 0;
+ if (server_sock == -1)
+ return;
+ close(server_sock);
+ server_sock = -1;
+}
+
+/**
+ * Send message to client, return !=0 on success, 0 on failure
+ */
+static int send_reply(int client_sock, void *data_in, int len)
+{
+ if (len <= 0) // Nothing to send
+ return 1;
+ char *data = data_in; // Needed for pointer arithmetic
+ int ret, i;
+ for (i = 0; i < 3; ++i) // Retry at most 3 times, each try takes at most 0.5 seconds (socket timeout)
+ {
+ ret = send(client_sock, data, len, 0);
+ if (ret == 0) // Connection closed
+ return 0;
+ if (ret < 0)
+ {
+ if (errno != EAGAIN) // Some unexpected error
+ return 0;
+ usleep(1000); // 1ms
+ continue;
+ }
+ len -= ret;
+ if (len <= 0) // Sent everything
+ return 1;
+ data += ret; // move target buffer pointer
+ }
+ return 0;
+}
+
+/**
+ * Receive data from client, return !=0 on success, 0 on failure
+ */
+static int recv_data(int client_sock, void *buffer_out, int len)
+{
+ if (len <= 0) // Nothing to receive
+ return 1;
+ char *data = buffer_out; // Needed for pointer arithmetic
+ int ret, i;
+ for (i = 0; i < 3; ++i) // Retry at most 3 times, each try takes at most 0.5 seconds (socket timeout)
+ {
+ ret = recv(client_sock, data, len, MSG_WAITALL);
+ if (ret == 0) // Connection closed
+ return 0;
+ if (ret < 0)
+ {
+ if (errno != EAGAIN) // Some unexpected error
+ return 0;
+ usleep(1000); // 1ms
+ continue;
+ }
+ len -= ret;
+ if (len <= 0) // Received everything
+ return 1;
+ data += ret; // move target buffer pointer
+ }
+ return 0;
+}
+
+/**
+ * Returns !=0 if send/recv successful, 0 on any kind of network failure
+ */
+static int ipc_receive(int client_sock)
+{
+ GSList *iterator = NULL;
+
+ struct tm *timeinfo;
+ char time_buff[64], rid[20], ipaddr[100];
+
+ dnbd3_ipc_t header;
+
+ uint32_t cmd;
+
+ int ret, locked;
+ int return_value = 0;
+ xmlDocPtr doc = NULL;
+ xmlNodePtr root_node, images_node, clients_node, tmp_node, log_parent_node, log_node;
+ xmlChar *xmlbuff;
+ int buffersize;
+
+ ret = recv(client_sock, &header, sizeof(header), MSG_WAITALL);
+ if (ret != sizeof(header))
+ return ((ret < 0 && errno == EAGAIN) ? 1 : 0);
+ cmd = ntohl(header.cmd); // Leave header.cmd in network byte order for reply
+ header.size = ntohl(header.size);
+
+ header.error = htonl(ERROR_UNSPECIFIED_ERROR); // Default value of error, so remember to set it for the reply if call succeeded
+
+ if (header.size != 0)
+ {
+ // Message has payload, receive it
+ if (header.size > MAX_PAYLOAD)
+ {
+ memlogf("[WARNING] IPC command with payload of %u bytes ignored.", (unsigned int)header.size);
+ return 0;
+ }
+ if (!recv_data(client_sock, payload, header.size))
+ return 0;
+ }
+
+ switch (cmd)
+ {
+ case IPC_EXIT:
+ memlogf("[INFO] Server shutdown by IPC request");
+ header.size = ntohl(0);
+ header.error = ntohl(0);
+ return_value = send_reply(client_sock, &header, sizeof(header));
+ dnbd3_cleanup();
+ break;
+
+ case IPC_INFO:
+ locked = 0;
+ xmlbuff = NULL;
+ doc = xmlNewDoc(BAD_CAST "1.0");
+ if (doc == NULL)
+ goto get_info_reply_cleanup;
+ root_node = xmlNewNode(NULL, BAD_CAST "info");
+ if (root_node == NULL)
+ goto get_info_reply_cleanup;
+ xmlDocSetRootElement(doc, root_node);
+
+ // Images
+ images_node = xmlNewNode(NULL, BAD_CAST "images");
+ if (images_node == NULL)
+ goto get_info_reply_cleanup;
+ xmlAddChild(root_node, images_node);
+ locked = 1;
+ pthread_spin_lock(&_spinlock);
+ for (iterator = _dnbd3_images; iterator; iterator = iterator->next)
+ {
+ const dnbd3_image_t *image = iterator->data;
+ sprintf(rid, "%d", image->rid);
+ timeinfo = localtime(&image->atime);
+ strftime(time_buff, 64, "%d.%m.%y %H:%M:%S", timeinfo);
+ 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 "atime", BAD_CAST time_buff);
+ xmlNewProp(tmp_node, BAD_CAST "rid", BAD_CAST rid);
+ xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST image->file);
+ xmlNewProp(tmp_node, BAD_CAST "servers", BAD_CAST "???");
+ xmlNewProp(tmp_node, BAD_CAST "cache", BAD_CAST image->cache_file);
+ xmlAddChild(images_node, tmp_node);
+ }
+ // Clients
+ clients_node = xmlNewNode(NULL, BAD_CAST "clients");
+ if (clients_node == NULL)
+ goto get_info_reply_cleanup;
+ xmlAddChild(root_node, clients_node);
+ for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
+ {
+ dnbd3_client_t *client = iterator->data;
+ if (client->image)
+ {
+ tmp_node = xmlNewNode(NULL, BAD_CAST "client");
+ if (tmp_node == NULL)
+ goto get_info_reply_cleanup;
+ *ipaddr = '\0';
+ inet_ntop(client->addrtype, client->ipaddr, ipaddr, 100);
+ xmlNewProp(tmp_node, BAD_CAST "ip", BAD_CAST ipaddr);
+ xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST client->image->file);
+ xmlAddChild(clients_node, tmp_node);
+ }
+ }
+ pthread_spin_unlock(&_spinlock);
+ locked = 0;
+
+ // Log
+ log_parent_node = xmlNewChild(root_node, NULL, BAD_CAST "log", NULL);
+ if (log_parent_node == NULL)
+ goto get_info_reply_cleanup;
+ char *log = fetchlog(0);
+ if (log == NULL)
+ log = "LOG IS NULL";
+ log_node = xmlNewCDataBlock(doc, 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);
+ header.size = htonl(buffersize);
+ header.error = htonl(0);
+
+get_info_reply_cleanup:
+ if (locked)
pthread_spin_unlock(&_spinlock);
+ // Send reply
+ return_value = send_reply(client_sock, &header, sizeof(header));
+ if (return_value && xmlbuff)
+ return_value = send_reply(client_sock, xmlbuff, buffersize);
+ // Cleanup
+ xmlFree(xmlbuff);
+ xmlFreeDoc(doc);
+ free(log);
+ break;
+
+ case IPC_ADDIMG:
+ case IPC_DELIMG:
+ if (header.size == 0)
+ {
+ header.size = htonl(0);
+ header.error = htonl(ERROR_MISSING_ARGUMENT);
+ return_value = send_reply(client_sock, &header, sizeof(header));
+ break;
+ }
+ doc = xmlReadMemory(payload, header.size, "noname.xml", NULL, 0);
- // Log
- log_parent_node = xmlNewChild(root_node, NULL, BAD_CAST "log", NULL);
- char *log = fetchlog(0);
- if (log == NULL) log = "LOG IS NULL";
- log_node = xmlNewCDataBlock(doc, BAD_CAST log, strlen(log));
- xmlAddChild(log_parent_node, log_node);
-
- // Dump and send
- xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
- header.size = htonl(buffersize);
- header.error = htonl(0);
- send(client_sock, (char *) &header, sizeof(header), MSG_WAITALL);
- send(client_sock, (char *) xmlbuff, buffersize, MSG_WAITALL);
-
- // Cleanup
- close(client_sock);
- xmlFree(xmlbuff);
- xmlFreeDoc(doc);
- free(log);
- break;
-
- case IPC_ADDIMG:
- pthread_spin_lock(&_spinlock);
-
- // Parse reply
- buf = malloc(header.size);
- size = recv(client_sock, buf, header.size, MSG_WAITALL);
- doc = xmlReadMemory(buf, size, "noname.xml", NULL, 0);
-
- if (doc)
- {
-// xmlDocDump(stdout, doc_config);
-
- xmlXPathContextPtr xpathCtx;
- xmlXPathObjectPtr xpathObj;
- xmlChar* xpathExpr;
- xmlNodeSetPtr nodes;
- xmlNodePtr cur;
-
- xpathExpr = BAD_CAST "/info/images/image";
- xpathCtx = xmlXPathNewContext(doc);
- xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
-
- nodes = xpathObj->nodesetval;
- cur = nodes->nodeTab[0];
- if(cur->type == XML_ELEMENT_NODE)
- {
- dnbd3_image_t image;
- memset(&image, 0, sizeof(dnbd3_image_t));
- image.name = (char *) xmlGetNoNsProp(cur, BAD_CAST "name");
- image.rid = atoi((char *) xmlGetNoNsProp(cur, BAD_CAST "rid"));
- image.file = (char *) xmlGetNoNsProp(cur, BAD_CAST "file");
- image.cache_file = (char *) xmlGetNoNsProp(cur, BAD_CAST "cache");
- header.error = htonl(dnbd3_add_image(&image, _config_file_name));
- }
+ if (doc)
+ {
+ xmlXPathContextPtr xpathCtx = NULL;
+ xmlXPathObjectPtr xpathObj = NULL;
+ xmlNodeSetPtr nodes = NULL;
+ xmlNodePtr cur = NULL;
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
- }
-
- header.size = htonl(0);
- send(client_sock, (char *) &header, sizeof(header), MSG_WAITALL);
-
- // Cleanup
- pthread_spin_unlock(&_spinlock);
- close(client_sock);
- xmlFreeDoc(doc);
- xmlCleanupParser();
- free(buf);
- break;
-
- case IPC_DELIMG:
- pthread_spin_lock(&_spinlock);
-
- // Parse reply
- buf = malloc(header.size);
- size = recv(client_sock, buf, header.size, MSG_WAITALL);
- doc = xmlReadMemory(buf, size, "noname.xml", NULL, 0);
-
- if (doc)
- {
-// xmlDocDump(stdout, doc_config);
-
- xmlXPathContextPtr xpathCtx;
- xmlXPathObjectPtr xpathObj;
- xmlChar* xpathExpr;
- xmlNodeSetPtr nodes;
- xmlNodePtr cur;
-
- xpathExpr = BAD_CAST "/info/images/image";
- xpathCtx = xmlXPathNewContext(doc);
- xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
-
- nodes = xpathObj->nodesetval;
- cur = nodes->nodeTab[0];
- if(cur->type == XML_ELEMENT_NODE)
+ 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)
+ {
+ 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)
{
- dnbd3_image_t image;
- memset(&image, 0, sizeof(dnbd3_image_t));
- image.name = (char *) xmlGetNoNsProp(cur, BAD_CAST "name");
- image.rid = atoi((char *) xmlGetNoNsProp(cur, BAD_CAST "rid"));
- image.file = (char *) xmlGetNoNsProp(cur, BAD_CAST "file");
- image.cache_file = (char *) xmlGetNoNsProp(cur, BAD_CAST "cache");
- header.error = htonl(dnbd3_del_image(&image, _config_file_name));
+ image.rid = atoi(rid_str);
+ if (cmd == IPC_ADDIMG)
+ header.error = htonl(dnbd3_add_image(&image, _config_file_name));
+ else
+ header.error = htonl(dnbd3_del_image(&image, _config_file_name));
}
+ else
+ header.error = htonl(ERROR_MISSING_ARGUMENT);
+ xmlFree(image.name);
+ xmlFree(rid_str);
+ xmlFree(image.file);
+ xmlFree(image.cache_file);
+ }
+ else
+ header.error = htonl(ERROR_MISSING_ARGUMENT);
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
- }
-
- header.size = htonl(0);
- send(client_sock, (char *) &header, sizeof(header), MSG_WAITALL);
-
- // Cleanup
- pthread_spin_unlock(&_spinlock);
- close(client_sock);
- xmlFreeDoc(doc);
- xmlCleanupParser();
- free(buf);
- break;
-
- default:
- memlogf("ERROR: Unknown command: %i\n", header.cmd);
- header.size = htonl(0);
- header.error = htonl(ERROR_UNKNOWN);
- send(client_sock, (char *) &header, sizeof(header), MSG_WAITALL);
- close(client_sock);
- break;
-
- }
- }
- close(server_sock);
- pthread_exit((void *) 0);
+add_del_cleanup:
+ xmlXPathFreeObject(xpathObj);
+ xmlXPathFreeContext(xpathCtx);
+ xmlFreeDoc(doc);
+ }
+ else
+ header.error = htonl(ERROR_INVALID_XML);
+
+ header.size = htonl(0);
+ return_value = send_reply(client_sock, &header, sizeof(header));
+ break;
+
+ default:
+ memlogf("[ERROR] Unknown IPC command: %u", (unsigned int)header.cmd);
+ header.size = htonl(0);
+ header.error = htonl(ERROR_UNKNOWN_COMMAND);
+ return_value = send_reply(client_sock, &header, sizeof(header));
+ break;
+
+ }
+ return return_value;
}
void dnbd3_ipc_send(int cmd)
{
- int client_sock, size;
+ int client_sock, size;
#ifdef IPC_TCP
- struct sockaddr_in server;
-
- // Create socket
- if ((client_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
- {
- perror("ERROR: IPC socket");
- exit(EXIT_FAILURE);
- }
-
- memset(&server, 0, sizeof(server));
- server.sin_family = AF_INET; // IPv4
- server.sin_addr.s_addr = inet_addr("127.0.0.1");
- server.sin_port = htons(IPC_PORT); // set port number
-
- // Connect to server
- if (connect(client_sock, (struct sockaddr *) &server, sizeof(server)) < 0)
- {
- perror("ERROR: IPC connect");
- exit(EXIT_FAILURE);
- }
+ struct sockaddr_in server;
+ struct timeval client_timeout;
+
+ // Create socket
+ if ((client_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ {
+ perror("ERROR: IPC socket");
+ exit(EXIT_FAILURE);
+ }
+
+ client_timeout.tv_sec = 4;
+ client_timeout.tv_usec = 0;
+ setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout));
+ setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout));
+
+ memset(&server, 0, sizeof(server));
+ server.sin_family = AF_INET; // IPv4
+ server.sin_addr.s_addr = inet_addr("127.0.0.1");
+ server.sin_port = htons(IPC_PORT); // set port number
+
+ // Connect to server
+ if (connect(client_sock, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+ perror("ERROR: IPC connect");
+ exit(EXIT_FAILURE);
+ }
#else
- struct sockaddr_un server;
-
- // Create socket
- if ((client_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- {
- perror("ERROR: IPC socket");
- exit(EXIT_FAILURE);
- }
- server.sun_family = AF_UNIX;
- strcpy(server.sun_path, UNIX_SOCKET);
-
- // Connect to server
- if (connect(client_sock, &server, sizeof(server.sun_family) + strlen(server.sun_path)) < 0)
- {
- perror("ERROR: IPC connect");
- exit(EXIT_FAILURE);
- }
+ struct sockaddr_un server;
+
+ // Create socket
+ if ((client_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ {
+ perror("ERROR: IPC socket");
+ exit(EXIT_FAILURE);
+ }
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, UNIX_SOCKET);
+
+ // Connect to server
+ if (connect(client_sock, &server, sizeof(server.sun_family) + strlen(server.sun_path)) < 0)
+ {
+ perror("ERROR: IPC connect");
+ exit(EXIT_FAILURE);
+ }
#endif
- // Send message
- dnbd3_ipc_t header;
- header.cmd = htonl(cmd);
- header.size = 0;
- header.error = 0;
- send(client_sock, (char *) &header, sizeof(header), MSG_WAITALL);
- recv(client_sock, &header, sizeof(header), MSG_WAITALL);
- header.cmd = ntohl(header.cmd);
- header.size = ntohl(header.size);
- header.error = ntohl(header.error);
-
- if (cmd == IPC_INFO && header.size > 0)
- {
- char* buf = malloc(header.size+1);
- size = recv(client_sock, buf, header.size, MSG_WAITALL);
- xmlDocPtr doc = xmlReadMemory(buf, size, "noname.xml", NULL, 0);
- buf[header.size] = 0;
+ // Send message
+ dnbd3_ipc_t header;
+ header.cmd = htonl(cmd);
+ header.size = 0;
+ header.error = 0;
+ send(client_sock, (char *)&header, sizeof(header), MSG_WAITALL);
+ recv(client_sock, &header, sizeof(header), MSG_WAITALL);
+ header.cmd = ntohl(header.cmd);
+ header.size = ntohl(header.size);
+ header.error = ntohl(header.error);
+
+ if (cmd == IPC_INFO && header.size > 0)
+ {
+ char *buf = malloc(header.size + 1);
+ size = recv(client_sock, buf, header.size, MSG_WAITALL);
+ xmlDocPtr doc = xmlReadMemory(buf, size, "noname.xml", NULL, 0);
+ buf[header.size] = 0;
if (doc)
{
@@ -417,7 +631,7 @@ void dnbd3_ipc_send(int cmd)
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
- xmlChar* xpathExpr;
+ xmlChar *xpathExpr;
xmlNodeSetPtr nodes;
xmlNodePtr cur;
@@ -425,7 +639,8 @@ void dnbd3_ipc_send(int cmd)
xpathExpr = BAD_CAST "/info/log";
xpathCtx = xmlXPathNewContext(doc);
xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
- if (xpathObj->nodesetval && xpathObj->nodesetval->nodeTab && xpathObj->nodesetval->nodeTab[0]) {
+ if (xpathObj->nodesetval && xpathObj->nodesetval->nodeTab && xpathObj->nodesetval->nodeTab[0])
+ {
printf("--- Last log lines ----\n%s\n\n", xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]));
}
xmlXPathFreeObject(xpathObj);
@@ -439,9 +654,9 @@ void dnbd3_ipc_send(int cmd)
printf("========================================\n");
nodes = xpathObj->nodesetval;
n = (nodes) ? nodes->nodeNr : 0;
- for(i = 0; i < n; ++i)
+ for (i = 0; i < n; ++i)
{
- if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE)
+ if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE)
{
cur = nodes->nodeTab[i];
xmlChar *atime = xmlGetNoNsProp(cur, BAD_CAST "atime");
@@ -463,9 +678,9 @@ void dnbd3_ipc_send(int cmd)
printf("=============================\n");
nodes = xpathObj->nodesetval;
n = (nodes) ? nodes->nodeNr : 0;
- for(i = 0; i < n; ++i)
+ for (i = 0; i < n; ++i)
{
- if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE)
+ if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE)
{
cur = nodes->nodeTab[i];
xmlChar *ip = xmlGetNoNsProp(cur, BAD_CAST "ip");
@@ -489,7 +704,7 @@ void dnbd3_ipc_send(int cmd)
printf("ERROR: Failed to parse reply\n-----------\n%s\n-------------\n", buf);
}
- }
+ }
- close(client_sock);
+ close(client_sock);
}
diff --git a/src/server/ipc.h b/src/server/ipc.h
index 4a405ee..d4ec7db 100644
--- a/src/server/ipc.h
+++ b/src/server/ipc.h
@@ -27,7 +27,9 @@
#define IPC_ADDIMG 3
#define IPC_DELIMG 4
-void* dnbd3_ipc_receive();
+void* dnbd3_ipc_mainloop();
+
+void dnbd3_ipc_shutdown();
void dnbd3_ipc_send(int cmd);
@@ -35,6 +37,7 @@ void dnbd3_ipc_send(int cmd);
#pragma pack(1)
typedef struct
{
+ uint32_t handle;// 4byte
uint32_t cmd; // 4byte
uint32_t size; // 4byte
uint32_t error; // 4byte
diff --git a/src/server/server.c b/src/server/server.c
index 965c368..10a1d2f 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -44,7 +44,8 @@ pthread_spinlock_t _spinlock;
GSList *_dnbd3_clients = NULL;
char *_config_file_name = DEFAULT_SERVER_CONFIG_FILE;
-GSList *_dnbd3_images; // of dnbd3_image_t
+char *_local_namespace = NULL;
+GSList *_dnbd3_images = NULL; // of dnbd3_image_t
void dnbd3_print_help(char* argv_0)
{
@@ -76,6 +77,8 @@ void dnbd3_cleanup()
close(_sock);
+ dnbd3_ipc_shutdown();
+
pthread_spin_lock(&_spinlock);
GSList *iterator = NULL;
for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
@@ -209,9 +212,9 @@ int main(int argc, char* argv[])
timeout.tv_sec = SOCKET_TIMEOUT_SERVER;
timeout.tv_usec = 0;
- // setup icp
+ // setup ipc
pthread_t thread_ipc;
- pthread_create(&(thread_ipc), NULL, dnbd3_ipc_receive, NULL);
+ pthread_create(&(thread_ipc), NULL, &dnbd3_ipc_mainloop, NULL);
memlogf("[INFO] Server is ready...");
@@ -246,7 +249,7 @@ int main(int argc, char* argv[])
// This has to be done before creating the thread, otherwise a race condition might occur when the new thread dies faster than this thread adds the client to the list after creating the thread
pthread_spin_lock(&_spinlock);
- _dnbd3_clients = g_slist_append(_dnbd3_clients, dnbd3_client);
+ _dnbd3_clients = g_slist_prepend(_dnbd3_clients, dnbd3_client);
pthread_spin_unlock(&_spinlock);
if (0 != pthread_create(&(dnbd3_client->thread), NULL, dnbd3_handle_query, (void *) (uintptr_t) dnbd3_client))
diff --git a/src/server/server.h b/src/server/server.h
index 7e19f27..875a5af 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -25,12 +25,12 @@
#include <stdio.h>
#include <glib.h>
-#include "config.h"
+#include "../config.h"
#include "../types.h"
typedef struct
{
- char *name; // full name of image, eg. "uni-freiburg.ubuntu-12.04"
+ char *name; // full name of image, eg. "uni-freiburg/rz/ubuntu-12.04"
char *low_name; // full name of image, lowercased for comparison
int rid; // revision of provided image
char *file; // path to image file or device
@@ -53,7 +53,7 @@ typedef struct
extern GSList *_dnbd3_clients; // of dnbd3_client_t
extern pthread_spinlock_t _spinlock;
-extern char *_config_file_name;
+extern char *_config_file_name, *_local_namespace;
extern GSList *_dnbd3_images; // of dnbd3_image_t
@@ -63,4 +63,8 @@ extern int _fake_delay;
void dnbd3_cleanup();
+#if !defined(_FILE_OFFSET_BITS) || _FILE_OFFSET_BITS != 64
+#error Please set _FILE_OFFSET_BITS to 64 in your makefile/configuration
+#endif
+
#endif /* SERVER_H_ */
diff --git a/src/server/utils.c b/src/server/utils.c
index 3fe1900..d6e7a61 100644
--- a/src/server/utils.c
+++ b/src/server/utils.c
@@ -31,6 +31,12 @@
#include "utils.h"
#include "memlog.h"
+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);
+
/**
* 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"
@@ -65,19 +71,22 @@ static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *po
char *portpos = NULL, *ptr = string;
while (*ptr)
{
- if (*ptr == ':') portpos = ptr;
+ if (*ptr == ':')
+ portpos = ptr;
++ptr;
}
- if (portpos == NULL) return 0; // No port in string
+ if (portpos == NULL)
+ return 0; // No port in string
// Consider IP being surrounded by [ ]
- if (*string == '[' && *(portpos-1) == ']')
+ if (*string == '[' && *(portpos - 1) == ']')
{
++string;
- *(portpos-1) = '\0';
+ *(portpos - 1) = '\0';
}
*portpos++ = '\0';
int p = atoi(portpos);
- if (p < 1 || p > 65535) return 0; // Invalid port
+ if (p < 1 || p > 65535)
+ return 0; // Invalid port
*port = htons((uint16_t)p);
// Try IPv4 with port
@@ -101,30 +110,33 @@ static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *po
static char is_valid_namespace(char *namespace)
{
- if (*namespace == '\0' || *namespace == '/') return 0; // Invalid: Length = 0 or starting with a slash
+ if (*namespace == '\0' || *namespace == '/')
+ return 0; // Invalid: Length = 0 or starting with a slash
while (*namespace)
{
- if (*namespace != '/' && *namespace != '-'
- && (*namespace < 'a' || *namespace > 'z')
- && (*namespace < 'A' || *namespace > 'Z')) return 0;
+ if (*namespace != '/' && *namespace != '-' && (*namespace < 'a' || *namespace > 'z')
+ && (*namespace < 'A' || *namespace > 'Z'))
+ return 0;
++namespace;
}
- if (*(namespace - 1) == '/') return 0; // Invalid: Ends in a slash
+ if (*(namespace - 1) == '/')
+ return 0; // Invalid: Ends in a slash
return 1;
}
static char is_valid_imagename(char *namespace)
{
- if (*namespace == '\0' || *namespace == ' ') return 0; // Invalid: Length = 0 or starting with a space
+ if (*namespace == '\0' || *namespace == ' ')
+ return 0; // Invalid: Length = 0 or starting with a space
while (*namespace)
{ // Check for invalid chars
- if (*namespace != '.' && *namespace != '-' && *namespace != ' '
- && *namespace != '(' && *namespace != ')'
- && (*namespace < 'a' || *namespace > 'z')
- && (*namespace < 'A' || *namespace > 'Z')) return 0;
+ if (*namespace != '.' && *namespace != '-' && *namespace != ' ' && *namespace != '(' && *namespace != ')'
+ && (*namespace < 'a' || *namespace > 'z') && (*namespace < 'A' || *namespace > 'Z'))
+ return 0;
++namespace;
}
- if (*(namespace - 1) == ' ') return 0; // Invalid: Ends in a space
+ if (*(namespace - 1) == ' ')
+ return 0; // Invalid: Ends in a space
return 1;
}
@@ -132,380 +144,430 @@ static void strtolower(char *string)
{
while (*string)
{
- if (*string >= 'A' && *string <= 'Z') *string += 32;
+ if (*string >= 'A' && *string <= 'Z')
+ *string += 32;
++string;
}
}
-void dnbd3_load_config(char *file)
+void dnbd3_load_config()
{
- int fd;
- gint i, j, k;
- GKeyFile* gkf;
-
- gkf = g_key_file_new();
- if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL))
- {
- printf("ERROR: Config file not found: %s\n", file);
- exit(EXIT_FAILURE);
- }
-
- char *namespace = g_key_file_get_string(gkf, "settings", "default_namespace", NULL);
- if (namespace && !is_valid_namespace(namespace))
- {
- memlogf("[ERROR] Ignoring default namespace: '%s' is not a valid namespace", namespace);
- g_free(namespace);
- namespace = NULL;
- }
-
- gchar **groups = NULL;
- gsize section_count;
- groups = g_key_file_get_groups(gkf, &section_count);
-
- for (i = 0; i < section_count; i++)
- {
- // Special group
- if (strcmp(groups[i], "settings") == 0 || strcmp(groups[i], "trusted") == 0)
- {
- continue;
- }
-
- // An actual image definition
-
- if (!is_valid_imagename(groups[i]))
- {
- memlogf("[ERROR] Invalid image name: '%s'", groups[i]);
- continue;
- }
-
- int rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL);
- if (rid <= 0)
- {
- memlogf("[ERROR] Invalid rid '%d' for image '%s'", rid, groups[i]);
- continue;
- }
-
- if (strchr(groups[i], '.') == NULL && namespace == NULL)
- {
- memlogf("[ERROR] Image '%s' has local name and no default namespace is defined; entry ignored.", groups[i]);
- continue;
- }
-
- dnbd3_image_t *image = g_new0(dnbd3_image_t, 1);
- if (image == NULL)
- {
- memlogf("[ERROR] Could not allocate dnbd3_image_t while reading config");
- continue;
- }
-
- if (strchr(groups[i], '/') == NULL)
- { // Local image, build global name
- image->name = calloc(strlen(namespace) + strlen(groups[i]) + 2, sizeof(char));
- sprintf(image->name, "%s/%s", namespace, groups[i]);
- }
- else
- {
- image->name = strdup(groups[i]);
- }
-
- if (dnbd3_get_image(image->name, rid, 0))
- {
- memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image->name, rid);
- free(image->name);
- g_free(image);
- continue;
- }
-
- image->low_name = strdup(image->name);
- strtolower(image->low_name);
-
- image->rid = rid;
- image->file = g_key_file_get_string(gkf, groups[i], "file", NULL);
- char relayed = image->file == NULL || *image->file == '\0';
- if (relayed && image->file)
- {
- g_free(image->file);
- image->file = NULL;
- }
-
- if (relayed) // Image is relayed (this server acts as proxy)
- {
- if (strchr(groups[i], '.') == NULL)
- {
- memlogf("[ERROR] Relayed image without global name in config: '%s'", groups[i]);
- g_free(image);
- continue;
- }
- image->cache_file = g_key_file_get_string(gkf, groups[i], "cache", NULL);
- if (image->cache_file && *image->cache_file == '\0') g_free(image->cache_file);
- }
- else // Image is a local one, open file to get size
- {
- fd = open(image->file, O_RDONLY);
- if (fd > 0) {
- image->filesize = lseek(fd, 0, SEEK_END);
- if (image->filesize & 4095) {
- memlogf("[WARNING] Size of image '%s' is not a multiple of 4096. Last incomplete block will be ignored!", image->file);
- image->filesize &= ~(uint64_t)4095;
- }
- close(fd);
- image->working = 1;
- } else {
- memlogf("[ERROR] Image file not found: '%s'", image->file);
- }
- }
-
- // A list of servers that are known to also host or relay this image
- gsize num_servers;
- gchar **servers = g_key_file_get_string_list(gkf, groups[i], "servers", &num_servers, NULL);
- if (servers) for (k = 0, j = 0; j < MIN(num_servers, NUMBER_SERVERS); ++j)
- {
- if (parse_address(servers[j], &(image->servers[k].hostaddrtype), image->servers[k].hostaddr, &(image->servers[k].port)))
- {
- ++k; continue;
- }
- image->servers[k].hostaddrtype = 0;
- }
- g_strfreev(servers);
-
- if (image->cache_file)
- {
- // Determine size of cached image
- fd = open(image->cache_file, O_RDONLY);
- if (fd > 0)
- {
- image->filesize = lseek(fd, 0, SEEK_END);
- close(fd);
- }
- if (image->filesize & 4095)
- { // Cache files should always be trincated to 4kib boundaries already
- memlogf("[WARNING] Size of cache file '%s' is not a multiple of 4096. Something's fishy!", image->cache_file);
- image->filesize = 0;
- }
- else if (image->filesize > 0)
- {
- const size_t map_len_bytes = (image->filesize + (1 << 15) - 1) >> 15;
- image->cache_map = calloc(map_len_bytes, sizeof(uint8_t));
- // read cache map from file
- // one byte in the map covers 8 4kib blocks, so 32kib per byte
- // "+ (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)
- // 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)+4];
- strcpy(tmp, image->cache_file);
- strcat(tmp, ".map");
- fd = open(tmp, O_RDONLY);
- if (fd > 0)
- {
- read(fd, image->cache_map, map_len_bytes * sizeof(uint8_t));
- close(fd);
- // If the whole image is cached, mark it as working right away without waiting for an upstream server
- image->working = 1;
- for (j = 0; j < map_len_bytes - 1; ++j)
- {
- if (image->cache_map[j] != 0xFF)
- {
- image->working = 0;
- break;
- }
- }
- const int blocks_in_last_byte = (image->filesize >> 12) & 7;
- uint8_t last_byte = 0;
- if (blocks_in_last_byte == 0)
- last_byte = 0xFF;
- else
- for (j = 0; j < k; ++j) last_byte = (last_byte << 1) | 1;
- if ((image->cache_map[map_len_bytes-1] & last_byte) != last_byte)
- image->working = 0;
- else
- memlogf("[INFO] Publishing relayed image '%s' because the local cache copy is complete", image->name);
- }
+ gint i;
+ GKeyFile* gkf;
- /*
- // TODO: Do this as soon as a connection to a upstream server is established
- // open cache file
- fd = open(_images[i].cache_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 1)
- memlogf("ERROR: Could't create cache file '%s'", _images[i].cache_file);
+ if (_local_namespace != NULL || _dnbd3_images != NULL)
+ {
+ printf("dnbd3_load_config() called more than once\n\n");
+ exit(EXIT_FAILURE);
+ }
- if (_images[i].filesize != lseek(fd, 0, SEEK_END))
- fallocate(fd, 0, 0, _images[i].filesize);
+ 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);
+ }
- close(fd);
- */
- }
- } // end cache_file handling
- pthread_spin_lock(&_spinlock);
- _dnbd3_images = g_slist_append(_dnbd3_images, image);
- pthread_spin_unlock(&_spinlock);
- // DONE IMAGE
- }
-
- g_free(namespace);
- g_strfreev(groups);
- g_key_file_free(gkf);
-}
+ _local_namespace = g_key_file_get_string(gkf, "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);
+ g_free(_local_namespace);
+ _local_namespace = NULL;
+ }
-int dnbd3_add_image(dnbd3_image_t *image, char *file)
-{
- return ERROR_IMAGE_ALREADY_EXISTS; // TODO: Make it work with image names
- /*
- FILE* f = fopen(image->file,"r");
- if (f == NULL)
+ gchar **groups = NULL;
+ gsize section_count;
+ groups = g_key_file_get_groups(gkf, &section_count);
+
+ for (i = 0; i < section_count; i++)
{
- printf("ERROR: Image file not found: %s\n", image->file);
- return ERROR_FILE_NOT_FOUND;
+ // Special group
+ if (strcmp(groups[i], "settings") == 0 || strcmp(groups[i], "trusted") == 0)
+ {
+ continue;
+ }
+
+ // An actual image definition
+
+ int rid = g_key_file_get_integer(gkf, 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);
+ gsize num_servers;
+ gchar **servers = g_key_file_get_string_list(gkf, 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);
+ if (image)
+ {
+ _dnbd3_images = g_slist_prepend(_dnbd3_images, image);
+ }
+ pthread_spin_unlock(&_spinlock);
+
+ g_free(image_file);
+ g_free(cache_file);
+ g_strfreev(servers);
}
- fclose (f);
- dnbd3_image_t* tmp = dnbd3_get_image(image->vid, image->rid);
+ g_strfreev(groups);
+ g_key_file_free(gkf);
+}
+
+int dnbd3_add_image(dnbd3_image_t *image)
+{
+ // Lock here to prevent concurrent add calls to mess rids up. Cannot happen currently
+ // as IPC clients are not threaded and they're the only place where this is called,
+ // but better be safe for the future...
+ pthread_spin_lock(&_spinlock);
if (image->rid == 0)
- {
- if(tmp)
- image->rid = tmp->rid +1;
+ { // TODO: globalize image->name somewhere for this call
+ const dnbd3_image_t *latest = dnbd3_get_image(image->name, image->rid, 0);
+ if (latest)
+ image->rid = latest->rid + 1;
else
image->rid = 1;
- } else if (tmp)
+ }
+
+ dnbd3_image_t *newimage = prepare_image(image->name, image->rid, image->file, image->cache_file, NULL, 0);
+ if (newimage)
+ {
+ _dnbd3_images = g_slist_prepend(_dnbd3_images, image);
+ }
+ else
{
- printf("ERROR: Image already exists (%d,%d)\n", image->vid, image->rid);
- return ERROR_IMAGE_ALREADY_EXISTS;
+ pthread_spin_unlock(&_spinlock);
+ return ERROR_SEE_LOG;
}
- GKeyFile* gkf;
- gkf = g_key_file_new();
- if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL))
- {
- printf("ERROR: Config file not found: %s\n", file);
- exit(EXIT_FAILURE);
- }
-
- g_key_file_set_integer(gkf, image->group, "vid", image->vid);
- g_key_file_set_integer(gkf, image->group, "rid", image->rid);
- g_key_file_set_string(gkf, image->group, "file", image->file);
- g_key_file_set_string(gkf, image->group, "servers", image->serverss);
- g_key_file_set_string(gkf, image->group, "cache", image->cache_file);
-
- gchar* data = g_key_file_to_data(gkf, NULL, NULL);
-
- f = fopen(file,"w");
- if (f)
- {
- fputs((char*) data, f);
- fclose(f);
- g_free(data);
- g_key_file_free(gkf);
- 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;
- }
- */
+ // 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);
+
+ gchar* data = g_key_file_to_data(gkf, NULL, NULL);
+
+ int 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;
}
int dnbd3_del_image(dnbd3_image_t *image, char *file)
{
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;
- }
-
- 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;
- }
-
- 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;
- }
- }
-
- GKeyFile* gkf;
- gkf = g_key_file_new();
- if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL))
- {
- printf("ERROR: Config file not found: %s\n", file);
- exit(EXIT_FAILURE);
- }
-
- 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)
- {
- fputs((char*) data, f);
- fclose(f);
- 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;
- }
- */
+ if (image->rid == 0)
+ {
+ printf("ERROR: Delete with rid=0 is not allowed\n");
+ return ERROR_RID;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ GKeyFile* gkf;
+ gkf = g_key_file_new();
+ if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL))
+ {
+ printf("ERROR: Config file not found: %s\n", file);
+ exit(EXIT_FAILURE);
+ }
+
+ 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)
+ {
+ fputs((char*) data, f);
+ fclose(f);
+ 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;
+ dnbd3_image_t *result = NULL, *image;
GSList *iterator;
char name[strlen(name_orig) + 1];
strcpy(name, name_orig);
strtolower(name);
- if (do_lock) pthread_spin_lock(&_spinlock);
- for (iterator = _dnbd3_images; iterator; iterator = iterator->next)
- {
- image = iterator->data;
- if (rid != 0) // rid was specified
- {
- if (image->rid == rid && strcmp(name, image->low_name) == 0)
- {
- result = image;
- break;
- }
- }
- else // search max. rid available
- {
- if (strcmp(name, image->low_name) == 0 && (result == NULL || result->rid < image->rid))
- {
- result = image;
- }
- }
- }
- if (do_lock) pthread_spin_unlock(&_spinlock);
- return result;
+ if (do_lock)
+ pthread_spin_lock(&_spinlock);
+ for (iterator = _dnbd3_images; iterator; iterator = iterator->next)
+ {
+ image = iterator->data;
+ if (rid != 0) // rid was specified
+ {
+ if (image->rid == rid && strcmp(name, image->low_name) == 0)
+ {
+ result = image;
+ break;
+ }
+ }
+ else // search max. rid available
+ {
+ if (strcmp(name, image->low_name) == 0 && (result == NULL || result->rid < image->rid))
+ {
+ result = image;
+ }
+ }
+ }
+ if (do_lock)
+ pthread_spin_unlock(&_spinlock);
+ return result;
}
void dnbd3_handle_sigpipe(int signum)
{
- memlogf("ERROR: SIGPIPE received!\n");
+ memlogf("ERROR: SIGPIPE received!\n");
}
void dnbd3_handle_sigterm(int signum)
{
- memlogf("INFO: SIGTERM or SIGINT received!\n");
- dnbd3_cleanup();
+ memlogf("INFO: SIGTERM or SIGINT received!\n");
+ dnbd3_cleanup();
+}
+
+/**
+ * Prepare image to be added to image list. Returns a pointer to a newly allocated image struct
+ * on success, NULL otherwise.
+ * Note: This function calls dnbd3_get_image without locking, so make sure you lock
+ * before calling this function while the server is active.
+ */
+static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file, gchar **servers, gsize num_servers)
+{
+ int j, k;
+ if (image_name == NULL)
+ {
+ memlogf("[ERROR] Null Image-Name");
+ return NULL;
+ }
+ if (!is_valid_imagename(image_name))
+ {
+ memlogf("[ERROR] Invalid image name: '%s'", image_name);
+ return NULL;
+ }
+
+ if (strchr(image_name, '.') == NULL && _local_namespace == NULL)
+ {
+ memlogf("[ERROR] Image '%s' has local name and no default namespace is defined; entry ignored.", image_name);
+ return NULL;
+ }
+
+ // Allocate image struct and zero it out by using g_new0
+ dnbd3_image_t *image = g_new0(dnbd3_image_t, 1);
+ if (image == NULL)
+ {
+ memlogf("[ERROR] Could not allocate dnbd3_image_t while reading config");
+ return NULL;
+ }
+
+ 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);
+ }
+ else
+ {
+ image->name = strdup(image_name);
+ }
+
+ if (dnbd3_get_image(image->name, rid, 0))
+ {
+ 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->rid = rid;
+ const char relayed = (image_file == NULL || image_file == '\0');
+
+ if (relayed) // Image is relayed (this server acts as proxy)
+ {
+ if (strchr(image_name, '/') == NULL)
+ {
+ memlogf("[ERROR] Relayed image without global name in config: '%s'", image_name);
+ goto error;
+ }
+ if (cache_file && *cache_file)
+ image->cache_file = strdup(cache_file);
+ }
+ else // Image is a local one, open file to get size
+ {
+ image->file = strdup(image_file);
+ int fd = open(image->file, O_RDONLY);
+ if (fd < 0)
+ {
+ memlogf("[ERROR] Image file not found: '%s'", image->file);
+ goto error;
+ }
+ 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);
+ goto error;
+ }
+ image->filesize = (uint64_t)size;
+ if (image->filesize & 4095)
+ {
+ memlogf("[WARNING] Size of image '%s' is not a multiple of 4096. Last incomplete block will be ignored!",
+ image->file);
+ image->filesize &= ~(uint64_t)4095;
+ }
+ close(fd);
+ image->working = 1;
+ }
+
+ // A list of servers that are known to also host or relay this image
+ if (servers)
+ for (k = 0, j = 0; j < MIN(num_servers, NUMBER_SERVERS); ++j)
+ {
+ if (parse_address(servers[j], &(image->servers[k].hostaddrtype), image->servers[k].hostaddr,
+ &(image->servers[k].port)))
+ {
+ ++k;
+ continue;
+ }
+ image->servers[k].hostaddrtype = 0;
+ }
+
+ if (image->cache_file)
+ {
+ // Determine size of cached image
+ int fd = open(image->cache_file, O_RDONLY);
+ if (fd >= 0)
+ {
+ const off_t size = lseek(fd, 0, SEEK_END);
+ if (size > 0)
+ image->filesize = (uint64_t)size;
+ close(fd);
+ }
+ if (image->filesize & 4095)
+ { // Cache files should always be truncated to 4kib boundaries already
+ memlogf("[WARNING] Size of cache file '%s' is not a multiple of 4096. Something's fishy!", image->cache_file);
+ image->filesize = 0;
+ }
+ else if (image->filesize > 0)
+ {
+ const size_t map_len_bytes = (image->filesize + (1 << 15) - 1) >> 15;
+ image->cache_map = calloc(map_len_bytes, sizeof(uint8_t));
+ // read cache map from file
+ // one byte in the map covers 8 4kib blocks, so 32kib per byte
+ // "+ (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)
+ // 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];
+ strcpy(tmp, image->cache_file);
+ strcat(tmp, ".map");
+ fd = open(tmp, O_RDONLY); // TODO: Check if map file has expected size
+ if (fd >= 0)
+ {
+ read(fd, image->cache_map, map_len_bytes * sizeof(uint8_t));
+ close(fd);
+ // If the whole image is cached, mark it as working right away without waiting for an upstream server
+ image->working = 1;
+ for (j = 0; j < map_len_bytes - 1; ++j)
+ {
+ if (image->cache_map[j] != 0xFF)
+ {
+ image->working = 0;
+ break;
+ }
+ }
+ const int blocks_in_last_byte = (image->filesize >> 12) & 7;
+ uint8_t last_byte = 0;
+ if (blocks_in_last_byte == 0)
+ last_byte = 0xFF;
+ else
+ for (j = 0; j < blocks_in_last_byte; ++j)
+ last_byte = (last_byte << 1) | 1;
+ 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);
+ }
+
+ /*
+ // TODO: Do this as soon as a connection to a upstream server is established
+ // open cache file
+ fd = open(_images[i].cache_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd < 1)
+ memlogf("ERROR: Could't create cache file '%s'", _images[i].cache_file);
+
+ if (_images[i].filesize != lseek(fd, 0, SEEK_END))
+ fallocate(fd, 0, 0, _images[i].filesize);
+
+ close(fd);
+ */
+ }
+ } // end cache_file handling
+ return image;
+error:
+ // Free stuff. Some pointers might be zero, but calling free() on those is safe.
+ free(image->cache_map);
+ free(image->name);
+ free(image->low_name);
+ free(image->file);
+ free(image->cache_file);
+ g_free(image);
+ return NULL;
}
diff --git a/src/server/utils.h b/src/server/utils.h
index ec83e64..9f709d5 100644
--- a/src/server/utils.h
+++ b/src/server/utils.h
@@ -26,13 +26,17 @@
#ifndef UTILS_H_
#define UTILS_H_
-#define ERROR_FILE_NOT_FOUND 1
-#define ERROR_IMAGE_ALREADY_EXISTS 2
-#define ERROR_CONFIG_FILE_PERMISSIONS 3
-#define ERROR_IMAGE_NOT_FOUND 4
-#define ERROR_RID 5
-#define ERROR_IMAGE_IN_USE 6
-#define ERROR_UNKNOWN 10
+#define ERROR_FILE_NOT_FOUND 1
+#define ERROR_IMAGE_ALREADY_EXISTS 2
+#define ERROR_CONFIG_FILE_PERMISSIONS 3
+#define ERROR_IMAGE_NOT_FOUND 4
+#define ERROR_RID 5
+#define ERROR_IMAGE_IN_USE 6
+#define ERROR_MISSING_ARGUMENT 7
+#define ERROR_UNSPECIFIED_ERROR 8
+#define ERROR_INVALID_XML 9
+#define ERROR_UNKNOWN_COMMAND 10
+#define ERROR_SEE_LOG 11
void dnbd3_load_config(char *file);
int dnbd3_add_image(dnbd3_image_t *image, char *file);