summaryrefslogtreecommitdiffstats
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/helper.c162
-rw-r--r--src/server/helper.h30
-rw-r--r--src/server/ipc.c126
-rw-r--r--src/server/ipc.h2
-rw-r--r--src/server/job.c113
-rw-r--r--src/server/net.c4
-rw-r--r--src/server/saveload.c (renamed from src/server/utils.c)200
-rw-r--r--src/server/saveload.h (renamed from src/server/utils.h)4
-rw-r--r--src/server/server.c11
-rw-r--r--src/server/server.h41
-rw-r--r--src/server/xmlutil.h12
11 files changed, 434 insertions, 271 deletions
diff --git a/src/server/helper.c b/src/server/helper.c
new file mode 100644
index 0000000..293fa36
--- /dev/null
+++ b/src/server/helper.c
@@ -0,0 +1,162 @@
+#include "helper.h"
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib/gmacros.h>
+#include "../config.h"
+
+/**
+ * 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"
+ * @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
+ * returns 1 on success, 0 in failure. contents of af, addr and port are undefined in the latter case
+ * !! Contents of @string might be modified by this function !!
+ */
+char parse_address(char *string, dnbd3_host_t *host)
+{
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ // Try IPv4 without port
+ if (1 == inet_pton(AF_INET, string, &v4))
+ {
+ host->type = AF_INET;
+ memcpy(host->addr, &v4, 4);
+ host->port = htons(PORT);
+ return 1;
+ }
+ // Try IPv6 without port
+ if (1 == inet_pton(AF_INET6, string, &v6))
+ {
+ host->type = AF_INET6;
+ memcpy(host->addr, &v6, 16);
+ host->port = htons(PORT);
+ return 1;
+ }
+
+ // Scan for port
+ char *portpos = NULL, *ptr = string;
+ while (*ptr)
+ {
+ if (*ptr == ':')
+ portpos = ptr;
+ ++ptr;
+ }
+ if (portpos == NULL)
+ return 0; // No port in string
+ // Consider IP being surrounded by [ ]
+ if (*string == '[' && *(portpos - 1) == ']')
+ {
+ ++string;
+ *(portpos - 1) = '\0';
+ }
+ *portpos++ = '\0';
+ int p = atoi(portpos);
+ if (p < 1 || p > 65535)
+ return 0; // Invalid port
+ host->port = htons((uint16_t)p);
+
+ // Try IPv4 with port
+ if (1 == inet_pton(AF_INET, string, &v4))
+ {
+ host->type = AF_INET;
+ memcpy(host->addr, &v4, 4);
+ return 1;
+ }
+ // Try IPv6 with port
+ if (1 == inet_pton(AF_INET6, string, &v6))
+ {
+ host->type = AF_INET6;
+ memcpy(host->addr, &v6, 16);
+ return 1;
+ }
+
+ // FAIL
+ return 0;
+}
+
+/**
+ * Convert a host and port (network byte order) to printable representation.
+ * Worst case required buffer len is 48, eg. [1234:1234:1234:1234:1234:1234:1234:1234]:12345 (+ \0)
+ * Returns TRUE on success, FALSE on error
+ */
+char host_to_string(const dnbd3_host_t *host, char *target, size_t targetlen)
+{
+ // Worst case: Port 5 chars, ':' to separate ip and port 1 char, terminating null 1 char = 7, [] for IPv6
+ if (targetlen < 10)
+ return FALSE;
+ if (host->type == AF_INET6)
+ {
+ *target++ = '[';
+ inet_ntop(AF_INET6, host->addr, target, targetlen - 9);
+ target += strlen(target);
+ *target++ = ']';
+ }
+ else if (host->type == AF_INET)
+ {
+ inet_ntop(AF_INET, host->addr, target, targetlen - 7);
+ target += strlen(target);
+ }
+ else
+ {
+ snprintf(target, targetlen, "<?addrtype=%d>", (int)host->type);
+ return FALSE;
+ }
+ *target = '\0';
+ if (host->port != 0)
+ {
+ // There are still at least 7 bytes left in the buffer, port is at most 5 bytes + ':' + '\0' = 7
+ snprintf(target, 7, ":%d", (int)ntohs(host->port));
+ }
+ return TRUE;
+}
+
+char is_valid_namespace(char *namespace)
+{
+ 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')
+ && (*namespace < '0' || *namespace > '9'))
+ return 0;
+ ++namespace;
+ }
+ if (strstr(namespace, "//") != NULL)
+ return 0; // Invalid: Double slash
+ if (*(namespace - 1) == '/')
+ return 0; // Invalid: Ends in a slash
+ return 1;
+}
+
+char is_valid_imagename(char *namespace)
+{
+ 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')
+ && (*namespace < '0' || *namespace > '9'))
+ return 0;
+ ++namespace;
+ }
+ if (*(namespace - 1) == ' ')
+ return 0; // Invalid: Ends in a space
+ return 1;
+}
+
+void strtolower(char *string)
+{
+ while (*string)
+ {
+ if (*string >= 'A' && *string <= 'Z')
+ *string += 32;
+ ++string;
+ }
+}
diff --git a/src/server/helper.h b/src/server/helper.h
new file mode 100644
index 0000000..e787c42
--- /dev/null
+++ b/src/server/helper.h
@@ -0,0 +1,30 @@
+#ifndef HELPER_H_
+#define HELPER_H_
+
+#include "server.h"
+#include <netinet/in.h>
+#include <string.h>
+
+char parse_address(char *string, dnbd3_host_t *host);
+char host_to_string(const dnbd3_host_t *host, char *target, size_t targetlen);
+char is_valid_namespace(char *namespace);
+char is_valid_imagename(char *namespace);
+void strtolower(char *string);
+
+static inline int is_same_server(const dnbd3_trusted_server_t *const a, const dnbd3_trusted_server_t *const b)
+{
+ return (a->host.type == b->host.type)
+ && (a->host.port == b->host.port)
+ && (0 == memcmp(a->host.addr, b->host.addr, (a->host.type == AF_INET ? 4 : 16)));
+}
+
+// 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 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
+#define IMGSIZE_TO_MAPBYTES(bytes) ((int)(((bytes) + (1 << 15) - 1) >> 15))
+
+#endif
diff --git a/src/server/ipc.c b/src/server/ipc.c
index 339dcc6..b93844a 100644
--- a/src/server/ipc.c
+++ b/src/server/ipc.c
@@ -18,6 +18,13 @@
*
*/
+#include "ipc.h"
+#include "../config.h"
+#include "server.h"
+#include "saveload.h"
+#include "memlog.h"
+#include "helper.h"
+
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -37,12 +44,6 @@
#include <libxml/xpath.h>
#include "xmlutil.h"
-#include "ipc.h"
-#include "../config.h"
-#include "server.h"
-#include "utils.h"
-#include "memlog.h"
-
#define IPC_PORT (PORT+1)
static int server_sock = -1;
@@ -363,7 +364,6 @@ static int ipc_receive(int client_sock)
{
GSList *iterator, *iterator2;
- struct tm *timeinfo;
#define STRBUFLEN 100
char strbuffer[STRBUFLEN];
@@ -433,8 +433,7 @@ static int ipc_receive(int client_sock)
if (tmp_node == NULL)
goto get_info_reply_cleanup;
xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->config_group);
- timeinfo = localtime(&image->atime);
- strftime(strbuffer, STRBUFLEN, "%d.%m.%y %H:%M:%S", timeinfo);
+ sprintf(strbuffer, "%u", (unsigned int)image->atime);
xmlNewProp(tmp_node, BAD_CAST "atime", BAD_CAST strbuffer);
sprintf(strbuffer, "%d", image->rid);
xmlNewProp(tmp_node, BAD_CAST "rid", BAD_CAST strbuffer);
@@ -471,7 +470,7 @@ static int ipc_receive(int client_sock)
if (tmp_node == NULL)
goto get_info_reply_cleanup;
*strbuffer = '\0';
- inet_ntop(client->addrtype, client->ipaddr, strbuffer, STRBUFLEN);
+ host_to_string(&client->host, strbuffer, STRBUFLEN);
xmlNewProp(tmp_node, BAD_CAST "ip", BAD_CAST strbuffer);
xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST client->image->file);
xmlAddChild(parent_node, tmp_node);
@@ -490,15 +489,14 @@ static int ipc_receive(int client_sock)
for (iterator = _trusted_servers; iterator; iterator = iterator->next)
{
dnbd3_trusted_server_t *server = iterator->data;
- if (server->hostaddrtype != 0)
+ if (server->host.type != 0)
{
tmp_node = xmlNewNode(NULL, BAD_CAST "server");
if (tmp_node == NULL)
goto get_info_reply_cleanup;
- *strbuffer = '\0';
- inet_ntop(server->hostaddrtype, server->hostaddr, strbuffer, STRBUFLEN);
+ host_to_string(&server->host, strbuffer, STRBUFLEN);
xmlNewProp(tmp_node, BAD_CAST "ip", BAD_CAST strbuffer);
- sprintf(strbuffer, "%d", (int)server->port);
+ sprintf(strbuffer, "%d", (int)server->host.port);
xmlNewProp(tmp_node, BAD_CAST "port", BAD_CAST strbuffer);
if (server->comment)
xmlNewProp(tmp_node, BAD_CAST "comment", BAD_CAST server->comment);
@@ -576,32 +574,28 @@ get_info_reply_cleanup:
FOR_EACH_NODE(docRequest, "/data/images/image", cur)
{
- if (cur->type == XML_ELEMENT_NODE)
+ if (cur->type != XML_ELEMENT_NODE)
+ continue;
+ NEW_POINTERLIST;
+ ++count;
+ dnbd3_image_t image;
+ memset(&image, 0, sizeof(dnbd3_image_t));
+ image.config_group = (char *)XML_GETPROP(cur, "name");
+ char *rid_str = (char *)XML_GETPROP(cur, "rid");
+ image.file = (char *)XML_GETPROP(cur, "file");
+ image.cache_file = (char *)XML_GETPROP(cur, "cache");
+ if (image.config_group && rid_str && image.file && image.cache_file)
{
- ++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));
- }
+ image.rid = atoi(rid_str);
+ if (cmd == IPC_ADDIMG)
+ header.error = htonl(dnbd3_add_image(&image));
else
- header.error = htonl(ERROR_MISSING_ARGUMENT);
- xmlFree(image.config_group);
- xmlFree(rid_str);
- xmlFree(image.file);
- xmlFree(image.cache_file);
+ header.error = htonl(dnbd3_del_image(&image));
}
- }
- END_FOR_EACH;
+ else
+ header.error = htonl(ERROR_MISSING_ARGUMENT);
+ FREE_POINTERLIST;
+ } END_FOR_EACH;
if (count == 0)
header.error = htonl(ERROR_MISSING_ARGUMENT);
}
@@ -712,22 +706,20 @@ void dnbd3_ipc_send(int cmd)
printf("--- Last log lines ----\n%s\n\n", log);
}
- int watime = 0, wname = 0, wrid = 5;
+ int watime = 17, wname = 0, wrid = 5;
FOR_EACH_NODE(doc, "/data/images/image", cur)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
- 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));
+ NEW_POINTERLIST; // This macro defines an array of pointers
+ xmlChar *vid = XML_GETPROP(cur, "name"); // XML_GETPROP is a macro wrapping xmlGetNoNsProp()
+ xmlChar *rid = XML_GETPROP(cur, "rid"); // Each of these calls allocates memory for the string
wname = MAX(wname, xmlStrlen(vid));
wrid = MAX(wrid, xmlStrlen(rid));
- // Too lazy to free vars, client will exit anyways
- }
- END_FOR_EACH;
+ FREE_POINTERLIST; // This macro simply frees all pointers in the above array
+ } END_FOR_EACH;
- char format[100];
+ char format[100], strbuffer[STRBUFLEN];
snprintf(format, 100,
"%%-%ds %%-%ds %%%ds %%s\n", watime, wname, wrid);
@@ -740,15 +732,18 @@ void dnbd3_ipc_send(int cmd)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
+ NEW_POINTERLIST;
++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(format, atime, vid, rid, file);
- // Too lazy to free vars, client will exit anyways
- }
- END_FOR_EACH;
+ xmlChar *numatime = XML_GETPROP(cur, "atime");
+ xmlChar *vid = XML_GETPROP(cur, "name");
+ xmlChar *rid = XML_GETPROP(cur, "rid");
+ xmlChar *file = XML_GETPROP(cur, "file");
+ time_t at = (time_t)atol((char*)numatime);
+ struct tm *timeinfo = localtime(&at);
+ strftime(strbuffer, STRBUFLEN, "%d.%m.%y %H:%M:%S", timeinfo);
+ printf(format, strbuffer, vid, rid, file);
+ FREE_POINTERLIST;
+ } END_FOR_EACH;
char_repeat_br('=', term_width);
printf("\nNumber of images: %d\n\n", count);
@@ -765,8 +760,7 @@ void dnbd3_ipc_send(int cmd)
xmlChar *file = xmlGetNoNsProp(cur, BAD_CAST "file");
printf("%-40s %s\n", ip, file);
// Too lazy to free vars, client will exit anyways
- }
- END_FOR_EACH;
+ } END_FOR_EACH;
char_repeat_br('=', term_width);
printf("\nNumber clients: %d\n\n", count);
@@ -778,30 +772,32 @@ void dnbd3_ipc_send(int cmd)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
+ NEW_POINTERLIST;
++count;
- xmlChar *ip = xmlGetNoNsProp(cur, BAD_CAST "ip");
- xmlChar *comment = xmlGetNoNsProp(cur, BAD_CAST "comment");
+ xmlChar *ip = XML_GETPROP(cur, "ip");
+ xmlChar *comment = XML_GETPROP(cur, "comment");
if (comment)
printf("%-30s (%s)\n", ip, comment);
else
printf("%-30s\n", ip);
for (childit = cur->children; childit; childit = childit->next)
{
- if (childit->type != XML_ELEMENT_NODE || childit->name == NULL || strcmp(childit->name, "namespace") != 0)
+ if (childit->type != XML_ELEMENT_NODE || childit->name == NULL || strcmp((const char*)childit->name, "namespace") != 0)
continue;
- xmlChar *name = xmlGetNoNsProp(childit, BAD_CAST "name");
- xmlChar *replicate = xmlGetNoNsProp(childit, BAD_CAST "replicate");
- xmlChar *recursive = xmlGetNoNsProp(childit, BAD_CAST "recursive");
+ NEW_POINTERLIST;
+ xmlChar *name = XML_GETPROP(childit, "name");
+ xmlChar *replicate = XML_GETPROP(childit, "replicate");
+ xmlChar *recursive = XML_GETPROP(childit, "recursive");
printf(" %-40s ", name);
if (replicate && *replicate != '0')
printf(" replicate");
if (recursive && *recursive != '0')
printf(" recursive");
putchar('\n');
+ FREE_POINTERLIST;
}
- // Too lazy to free vars, client will exit anyways
- }
- END_FOR_EACH;
+ FREE_POINTERLIST;
+ } END_FOR_EACH;
char_repeat_br('=', term_width);
printf("\nNumber servers: %d\n\n", count);
diff --git a/src/server/ipc.h b/src/server/ipc.h
index 36d1f60..fb29efc 100644
--- a/src/server/ipc.h
+++ b/src/server/ipc.h
@@ -21,6 +21,8 @@
#ifndef IPC_H_
#define IPC_H_
+#include <stdint.h>
+
#define IPC_EXIT 0
#define IPC_RELOAD 1
#define IPC_INFO 2
diff --git a/src/server/job.c b/src/server/job.c
index 1f10cb6..4a30cd1 100644
--- a/src/server/job.c
+++ b/src/server/job.c
@@ -1,5 +1,7 @@
#include "job.h"
-#include "utils.h"
+#include "saveload.h"
+#include "memlog.h"
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -7,8 +9,15 @@
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
#include <glib/gslist.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include "xmlutil.h"
+
#define DEV_STRLEN 12 // INCLUDING NULLCHAR (increase to 13 if you need more than 100 (0-99) devices)
#define MAX_NUM_DEVICES_TO_CHECK 100
@@ -34,6 +43,7 @@ static char keep_running = TRUE;
// Private functions
static char *get_free_device();
static void query_servers();
+static void update_image_atimes(time_t now);
//
@@ -73,7 +83,8 @@ void *dnbd3_job_thread(void *data)
{
const time_t starttime = time(NULL);
//
- // TODO: Update image atime
+ // Update image atime
+ update_image_atimes(starttime);
// Call image deletion function if last call is more than 5 minutes ago
if (starttime < next_delete_invocation)
{
@@ -99,32 +110,116 @@ void dnbd3_job_shutdown()
keep_running = FALSE;
}
+static void update_image_atimes(time_t now)
+{
+ GSList *iterator;
+ pthread_spin_lock(&_spinlock);
+ for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
+ {
+ dnbd3_client_t *client = iterator->data;
+ if (client && client->image && !client->is_server)
+ client->image->atime = now;
+ }
+ pthread_spin_unlock(&_spinlock);
+}
+
static void query_servers()
{
- struct timeval client_timeout;
+ if (_trusted_servers == NULL)
+ return;
+ struct timeval client_timeout, connect_timeout;
client_timeout.tv_sec = 0;
client_timeout.tv_usec = 500 * 1000;
- int client_sock;
- // 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));
+ connect_timeout.tv_sec = 2;
+ connect_timeout.tv_usec = 0;
+ int client_sock, num;
+ dnbd3_trusted_server_t *server;
+ dnbd3_host_t host;
+ struct sockaddr_in addr4;
+ for (num = 0;; ++num)
+ {
+ // "Iterate" this way to prevent holding the lock for a long time, although it is possible to skip a server this way...
+ pthread_spin_lock(&_spinlock);
+ server = g_slist_nth_data(_trusted_servers, num);
+ if (server == NULL)
+ {
+ pthread_spin_unlock(&_spinlock);
+ break; // Done
+ }
+ memcpy(&host, &server->host, sizeof(host));
+ pthread_spin_unlock(&_spinlock);
+ // Connect
+ if (host.type != AF_INET)
+ {
+ printf("[DEBUG] Unsupported addr type '%d', ignoring trusted server.\n", (int)host.type);
+ continue;
+ }
+ // Create socket (Extend for IPv6)
+ if ((client_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ {
+ printf("[DEBUG] Error creating server-to-server socket.\n");
+ continue;
+ }
+ // Set host (IPv4)
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_family = AF_INET;
+ memcpy(&addr4.sin_addr.s_addr, host.addr, 4);
+ addr4.sin_port = host.port;
+ // Connect to server
+ setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &connect_timeout, sizeof(connect_timeout));
+ setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &connect_timeout, sizeof(connect_timeout));
+ if (connect(client_sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0)
+ {
+ printf("[DEBUG] Could not connect to other server...\n");
+ close(client_sock); // TODO: Remove from alt server list if failed too often
+ continue;
+ }
+ // 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));
+ //
+ // TODO: Send and receive info from server
+ //
+ close(client_sock);
+ //
+ // TODO: Process data, update server info, add/remove this server as alt server for images, replicate images, etc.
+ }
}
/**
* Get full name of an available dnbd3 device, eg. /dev/dnbd4
* Returned buffer is owned by this module, do not modify or free!
+ * Returns NULL if all devices are in use
*/
static char *get_free_device()
{
if (devices == NULL)
return NULL;
- int i;
+ int i, c;
+ char buffer[100];
for (i = 0; i < num_devices; ++i)
{
if (!devices[i].available)
continue;
- // TODO: Check sysfs if device is maybe already connected
+ devices[i].available = FALSE;
+ // Check sysfs if device is maybe already connected
+ snprintf(buffer, 100, "/sys/devices/virtual/block/%s/net/cur_server_addr", devices[i].name + 5);
+ FILE *f = fopen(buffer, "r");
+ if (f == NULL)
+ {
+ printf("[DEBUG] Could not open %s - device marked as used.\n", buffer);
+ continue;
+ }
+ c = fgetc(f);
+ fclose(f);
+ if (c > 0)
+ {
+ // Could read something, so the device is connected
+ printf("[DEBUG] Free device %s is actually in use - marked as such.\n", devices[i].name);
+ continue;
+ }
return devices[i].name;
}
+ memlogf("[WARNING] No more free dnbd3 devices - proxy mode probably affected.");
return NULL;
}
diff --git a/src/server/net.c b/src/server/net.c
index afb7077..963d3ea 100644
--- a/src/server/net.c
+++ b/src/server/net.c
@@ -33,7 +33,7 @@
#include <netinet/tcp.h>
#include "server.h"
-#include "utils.h"
+#include "saveload.h"
#include "memlog.h"
#include "../serialize.h"
#include "../config.h"
@@ -369,7 +369,7 @@ void *dnbd3_handle_query(void *dnbd3_client)
num = 0;
for (i = 0; i < NUMBER_SERVERS; i++)
{
- if (image->servers[i].hostaddrtype == 0 || image->servers[i].failures > 200) continue;
+ if (image->servers[i].host.type == 0 || image->servers[i].failures > 200) continue;
memcpy(server_list + num++, image->servers + i, sizeof(dnbd3_server_entry_t));
}
reply.cmd = CMD_GET_SERVERS;
diff --git a/src/server/utils.c b/src/server/saveload.c
index ad40322..6af9b39 100644
--- a/src/server/utils.c
+++ b/src/server/saveload.c
@@ -24,147 +24,20 @@
#include <pthread.h>
#include <string.h>
#include <glib.h>
+#include <math.h>
#include <netinet/in.h>
-#include <arpa/inet.h>
#include "server.h"
-#include "utils.h"
+#include "saveload.h"
#include "memlog.h"
+#include "helper.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 int save_config();
+static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file);
//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"
- * @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
- * returns 1 on success, 0 in failure. contents of af, addr and port are undefined in the latter case
- */
-static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *port)
-{
- struct in_addr v4;
- struct in6_addr v6;
-
- // Try IPv4 without port
- if (1 == inet_pton(AF_INET, string, &v4))
- {
- *af = AF_INET;
- memcpy(addr, &v4, 4);
- *port = htons(PORT);
- return 1;
- }
- // Try IPv6 without port
- if (1 == inet_pton(AF_INET6, string, &v6))
- {
- *af = AF_INET6;
- memcpy(addr, &v6, 16);
- *port = htons(PORT);
- return 1;
- }
-
- // Scan for port
- char *portpos = NULL, *ptr = string;
- while (*ptr)
- {
- if (*ptr == ':')
- portpos = ptr;
- ++ptr;
- }
- if (portpos == NULL)
- return 0; // No port in string
- // Consider IP being surrounded by [ ]
- if (*string == '[' && *(portpos - 1) == ']')
- {
- ++string;
- *(portpos - 1) = '\0';
- }
- *portpos++ = '\0';
- int p = atoi(portpos);
- if (p < 1 || p > 65535)
- return 0; // Invalid port
- *port = htons((uint16_t)p);
-
- // Try IPv4 with port
- if (1 == inet_pton(AF_INET, string, &v4))
- {
- *af = AF_INET;
- memcpy(addr, &v4, 4);
- return 1;
- }
- // Try IPv6 with port
- if (1 == inet_pton(AF_INET6, string, &v6))
- {
- *af = AF_INET6;
- memcpy(addr, &v6, 16);
- return 1;
- }
-
- // FAIL
- return 0;
-}
-
-static char is_valid_namespace(char *namespace)
-{
- 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')
- && (*namespace < '0' || *namespace > '9'))
- return 0;
- ++namespace;
- }
- 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
- while (*namespace)
- {
- // Check for invalid chars
- if (*namespace != '.' && *namespace != '-' && *namespace != ' '
- && *namespace != '(' && *namespace != ')'
- && (*namespace < 'a' || *namespace > 'z') && (*namespace < 'A' || *namespace > 'Z')
- && (*namespace < '0' || *namespace > '9'))
- return 0;
- ++namespace;
- }
- if (*(namespace - 1) == ' ')
- return 0; // Invalid: Ends in a space
- return 1;
-}
-
-static inline int is_same_server(const dnbd3_trusted_server_t *const a, const dnbd3_trusted_server_t *const b)
-{
- return (a->hostaddrtype == b->hostaddrtype)
- && (a->port == b->port)
- && (0 == memcmp(a->hostaddr, b->hostaddr, (a->hostaddrtype == AF_INET ? 4 : 16)));
-}
-
-static void strtolower(char *string)
-{
- while (*string)
- {
- if (*string >= 'A' && *string <= 'Z')
- *string += 32;
- ++string;
- }
-}
void dnbd3_load_config()
{
@@ -191,6 +64,8 @@ void dnbd3_load_config()
_local_namespace = NULL;
}
+ srand(time(NULL));
+
_ipc_password = g_key_file_get_string(_config_handle, "settings", "password", NULL);
gchar **groups = NULL;
@@ -209,11 +84,10 @@ void dnbd3_load_config()
gchar *addr = g_key_file_get_string(_config_handle, groups[i], "address", NULL);
if (addr == NULL)
continue;
- dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(addr, TRUE);
+ dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(addr, TRUE, groups[i]+6);
g_free(addr);
if (server == NULL)
continue;
- server->comment = strdup(groups[i]+6);
gsize key_count;
gchar **keys = g_key_file_get_keys(_config_handle, groups[i], &key_count, NULL);
for (j = 0; j < key_count; ++j)
@@ -248,10 +122,8 @@ void dnbd3_load_config()
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(_config_handle, groups[i], "servers", &num_servers, NULL);
- dnbd3_image_t *image = prepare_image(groups[i], rid, image_file, cache_file, servers, num_servers);
+ dnbd3_image_t *image = prepare_image(groups[i], rid, image_file, cache_file);
if (image)
{
_dnbd3_images = g_slist_prepend(_dnbd3_images, image);
@@ -259,7 +131,6 @@ void dnbd3_load_config()
g_free(image_file);
g_free(cache_file);
- g_strfreev(servers);
}
g_strfreev(groups);
@@ -280,7 +151,7 @@ int dnbd3_add_image(dnbd3_image_t *image)
image->rid = 1;
}
- dnbd3_image_t *newimage = prepare_image(image->config_group, 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);
if (newimage)
{
_dnbd3_images = g_slist_prepend(_dnbd3_images, image);
@@ -299,7 +170,7 @@ int dnbd3_add_image(dnbd3_image_t *image)
pthread_spin_unlock(&_spinlock);
- const int ret = save_config();
+ const int ret = dnbd3_save_config();
if (ret == ERROR_OK)
memlogf("[INFO] Added new image '%s' (rid %d)", newimage->config_group, newimage->rid);
else
@@ -328,7 +199,7 @@ int dnbd3_del_image(dnbd3_image_t *image)
dnbd3_exec_delete(FALSE);
existing_image = NULL;
- const int ret = save_config();
+ const int ret = dnbd3_save_config();
if (ret == ERROR_OK)
memlogf("[INFO] Marked for deletion: '%s' (rid %d)", image->config_group, image->rid);
else
@@ -336,7 +207,7 @@ int dnbd3_del_image(dnbd3_image_t *image)
return ret;
}
-static int save_config()
+int dnbd3_save_config()
{
pthread_spin_lock(&_spinlock);
char *data = (char *)g_key_file_to_data(_config_handle, NULL, NULL);
@@ -408,12 +279,12 @@ dnbd3_image_t *dnbd3_get_image(char *name_orig, int rid, const char do_lock)
void dnbd3_handle_sigpipe(int signum)
{
- memlogf("ERROR: SIGPIPE received!\n");
+ memlogf("ERROR: SIGPIPE received (%s)", strsignal(signum));
}
void dnbd3_handle_sigterm(int signum)
{
- memlogf("INFO: SIGTERM or SIGINT received!\n");
+ memlogf("INFO: SIGTERM or SIGINT received (%s)", strsignal(signum));
dnbd3_cleanup();
}
@@ -423,9 +294,9 @@ void dnbd3_handle_sigterm(int signum)
* 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)
+static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file)
{
- int j, k;
+ int j;
if (image_name == NULL)
{
memlogf("[ERROR] Null Image-Name");
@@ -511,19 +382,6 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file,
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
@@ -637,7 +495,7 @@ void dnbd3_exec_delete(int save_if_changed)
continue;
// Kill client's connection
*ipstr = '\0';
- inet_ntop(client->addrtype, client->ipaddr, ipstr, 100);
+ host_to_string(&client->host, ipstr, 100);
memlogf("[INFO] delete_hard of %s reached; dropping client %s", image->config_group, ipstr);
const int fd = client->sock;
client->sock = -1;
@@ -645,7 +503,7 @@ void dnbd3_exec_delete(int save_if_changed)
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)
+ else if (image->delete_soft != 0 && image->delete_soft < now && image->atime + 3600 < now)
{
// Image should be soft-deleted
// Check if it is still in use
@@ -683,7 +541,7 @@ void dnbd3_exec_delete(int save_if_changed)
pthread_spin_lock(&_spinlock);
if (changed && save_if_changed)
- save_config();
+ dnbd3_save_config();
}
/**
@@ -693,11 +551,11 @@ void dnbd3_exec_delete(int save_if_changed)
* Returns NULL otherwise, or if the address could not be parsed
* !! Lock before calling this function !!
*/
-dnbd3_trusted_server_t *dnbd3_get_trusted_server(char *address, char create_if_not_found)
+dnbd3_trusted_server_t *dnbd3_get_trusted_server(char *address, char create_if_not_found, char *comment)
{
dnbd3_trusted_server_t server;
memset(&server, 0, sizeof(server));
- if (!parse_address(address, &server.hostaddrtype, server.hostaddr, &server.port))
+ if (!parse_address(address, &server.host))
{
memlogf("[WARNING] Could not parse address '%s' of trusted server", address);
return NULL;
@@ -711,6 +569,22 @@ dnbd3_trusted_server_t *dnbd3_get_trusted_server(char *address, char create_if_n
}
if (!create_if_not_found)
return NULL;
+ char *groupname = NULL;
+ if (comment == NULL)
+ {
+ groupname = malloc(50);
+ snprintf(groupname, 50, "trust:%x%x", rand(), (int)clock());
+ }
+ else
+ {
+ const size_t len = strlen(comment) + 8;
+ groupname = malloc(len);
+ snprintf(groupname, len, "trust:%s", comment);
+ }
+ char addrbuffer[50];
+ host_to_string(&server.host, addrbuffer, 50);
+ g_key_file_set_string(_config_handle, groupname, "address", addrbuffer);
+ free(groupname);
dnbd3_trusted_server_t *copy = malloc(sizeof(server));
memcpy(copy, &server, sizeof(*copy));
_trusted_servers = g_slist_prepend(_trusted_servers, copy);
diff --git a/src/server/utils.h b/src/server/saveload.h
index e254cfd..065c0b1 100644
--- a/src/server/utils.h
+++ b/src/server/saveload.h
@@ -42,6 +42,8 @@
#define ERROR_WRONG_PASSWORD 12
void dnbd3_load_config();
+int dnbd3_save_config();
+
int dnbd3_add_image(dnbd3_image_t *image);
int dnbd3_del_image(dnbd3_image_t *image);
@@ -49,7 +51,7 @@ void dnbd3_exec_delete(int save_if_changed);
dnbd3_image_t *dnbd3_get_image(char *name, int rid, const char do_lock);
-dnbd3_trusted_server_t *dnbd3_get_trusted_server(char *address, char create_if_not_found);
+dnbd3_trusted_server_t *dnbd3_get_trusted_server(char *address, char create_if_not_found, char *comment);
int dnbd3_add_trusted_namespace(dnbd3_trusted_server_t *server, char *namespace, char *flags);
void dnbd3_handle_sigpipe(int signum);
diff --git a/src/server/server.c b/src/server/server.c
index 9fc5383..4b30009 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -31,7 +31,7 @@
#include "../version.h"
#include "server.h"
-#include "utils.h"
+#include "saveload.h"
#include "net.h"
#include "ipc.h"
#include "memlog.h"
@@ -245,10 +245,11 @@ int main(int argc, char *argv[])
continue;
}
// TODO: Extend this if you ever want to add IPv6 (something like:)
- // dnbd3_client->addrtype = AF_INET6;
- // memcpy(dnbd3_client->ipaddr, &(client.sin6_addr), 16);
- dnbd3_client->addrtype = AF_INET;
- memcpy(dnbd3_client->ipaddr, &(client.sin_addr), 4);
+ // dnbd3_client->host.type = AF_INET6;
+ // memcpy(dnbd3_client->host.addr, &(client.sin6_addr), 16);
+ dnbd3_client->host.type = AF_INET;
+ memcpy(dnbd3_client->host.addr, &(client.sin_addr), 4);
+ dnbd3_client->host.port = client.sin_port;
dnbd3_client->sock = fd;
dnbd3_client->image = NULL;
diff --git a/src/server/server.h b/src/server/server.h
index c608066..93f219e 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -28,37 +28,28 @@
#include "../config.h"
#include "../types.h"
-// 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 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
-#define IMGSIZE_TO_MAPBYTES(bytes) ((int)(((bytes) + (1 << 15) - 1) >> 15))
typedef struct
{
- 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
+ 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
dnbd3_server_entry_t servers[NUMBER_SERVERS]; // known alt servers that also offer that image
- time_t atime; // last access time
- 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
- uint8_t relayed; // TRUE if relayed from other server (needs dnbd3 client module loaded)
+ time_t atime; // last access time
+ 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
+ uint8_t relayed; // TRUE if relayed from other server (needs dnbd3 client module loaded)
} dnbd3_image_t;
typedef struct
{
int sock;
- uint8_t ipaddr[16];
- uint8_t addrtype; // ip version (AF_INET or AF_INET6)
+ dnbd3_host_t host;
uint8_t is_server; // TRUE if a server in proxy mode, FALSE if real client
pthread_t thread;
dnbd3_image_t *image;
@@ -66,11 +57,9 @@ typedef struct
typedef struct
{
- uint8_t hostaddr[16];
- uint16_t port;
- uint8_t hostaddrtype;
+ dnbd3_host_t host;
gchar *comment;
- GSList *namespaces; // List of dnbd3_namespace_t
+ GSList *namespaces; // List of dnbd3_namespace_t
} dnbd3_trusted_server_t;
typedef struct
diff --git a/src/server/xmlutil.h b/src/server/xmlutil.h
index 46a5790..971d8a9 100644
--- a/src/server/xmlutil.h
+++ b/src/server/xmlutil.h
@@ -22,5 +22,17 @@ xmlChar *getTextFromPath(xmlDocPtr doc, char *xpath);
xmlXPathFreeContext(_makro_xpathCtx); \
} while(0)
+#define NUM_POINTERS_IN_LIST 20
+#define NEW_POINTERLIST \
+ void *_makro_ptrlist[NUM_POINTERS_IN_LIST]; \
+ int _makro_usedcount = 0
+
+#define FREE_POINTERLIST do { \
+ int _makro_i_; \
+ for (_makro_i_ = 0; _makro_i_ < _makro_usedcount; ++_makro_i_) { \
+ xmlFree(_makro_ptrlist[_makro_i_]); \
+ } } while(0)
+
+#define XML_GETPROP(_node, _name) (xmlChar*)(_makro_ptrlist[(_makro_usedcount >= NUM_POINTERS_IN_LIST ? 0 : _makro_usedcount++)] = xmlGetNoNsProp(_node, BAD_CAST _name))
#endif