diff options
author | sr | 2012-09-03 21:00:26 +0200 |
---|---|---|
committer | sr | 2012-09-03 21:00:26 +0200 |
commit | 60b2ba6ef9ed779d750958b6422f05c93a4a2d91 (patch) | |
tree | 46fdacf6a9439947e91af4c3d9de3006d41e5380 /src | |
parent | [*] Fixed and unified formatting (diff) | |
download | dnbd3-60b2ba6ef9ed779d750958b6422f05c93a4a2d91.tar.gz dnbd3-60b2ba6ef9ed779d750958b6422f05c93a4a2d91.tar.xz dnbd3-60b2ba6ef9ed779d750958b6422f05c93a4a2d91.zip |
[SERVER] Check which dnbd3 devices are idle and ready to use for proxy mode
[SERVER] Skeleton of server-to-server communication
[SERVER] Update access-time of images in use by actual clients
[*] Add dnbd3_host_t type to handle address+port+addrtype consistently across the project
Diffstat (limited to 'src')
-rw-r--r-- | src/client/client.c | 18 | ||||
-rw-r--r-- | src/kernel/blk.c | 14 | ||||
-rw-r--r-- | src/kernel/dnbd3.h | 4 | ||||
-rw-r--r-- | src/kernel/net.c | 78 | ||||
-rw-r--r-- | src/kernel/sysfs.c | 22 | ||||
-rw-r--r-- | src/server/helper.c | 162 | ||||
-rw-r--r-- | src/server/helper.h | 30 | ||||
-rw-r--r-- | src/server/ipc.c | 126 | ||||
-rw-r--r-- | src/server/ipc.h | 2 | ||||
-rw-r--r-- | src/server/job.c | 113 | ||||
-rw-r--r-- | src/server/net.c | 4 | ||||
-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.c | 11 | ||||
-rw-r--r-- | src/server/server.h | 41 | ||||
-rw-r--r-- | src/server/xmlutil.h | 12 | ||||
-rw-r--r-- | src/types.h | 19 |
17 files changed, 511 insertions, 349 deletions
diff --git a/src/client/client.c b/src/client/client.c index b94076f..397ac38 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -93,8 +93,8 @@ int main(int argc, char *argv[]) memset(&msg, 0, sizeof(dnbd3_ioctl_t)); msg.len = (uint16_t)sizeof(dnbd3_ioctl_t); msg.read_ahead_kb = DEFAULT_READ_AHEAD_KB; - msg.port = htons(PORT); - msg.addrtype = 0; + msg.host.port = htons(PORT); + msg.host.type = 0; msg.imgname = NULL; msg.is_server = FALSE; @@ -125,8 +125,8 @@ int main(int argc, char *argv[]) _config_file_name = strdup(optarg); break; case 'h': - dnbd3_get_ip(optarg, msg.addr, &msg.addrtype); - printf("Host set to %s (type %d)\n", inet_ntoa(*(struct in_addr *)msg.addr), (int)msg.addrtype); + dnbd3_get_ip(optarg, msg.host.addr, &msg.host.type); + printf("Host set to %s (type %d)\n", inet_ntoa(*(struct in_addr *)msg.host.addr), (int)msg.host.type); break; case 'i': msg.imgname = strdup(optarg); @@ -147,7 +147,7 @@ int main(int argc, char *argv[]) close_dev = 1; break; case 's': - dnbd3_get_ip(optarg, msg.addr, &msg.addrtype); + dnbd3_get_ip(optarg, msg.host.addr, &msg.host.type); switch_host = 1; break; case 'H': @@ -164,7 +164,7 @@ int main(int argc, char *argv[]) } // close device - if (close_dev && msg.addrtype == 0 && dev && (msg.imgname == NULL)) + if (close_dev && msg.host.type == 0 && dev && (msg.imgname == NULL)) { fd = open(dev, O_WRONLY); printf("INFO: Closing device %s\n", dev); @@ -181,7 +181,7 @@ int main(int argc, char *argv[]) } // switch host - if (switch_host && msg.addrtype != 0 && dev && (msg.imgname == NULL)) + if (switch_host && msg.host.type != 0 && dev && (msg.imgname == NULL)) { fd = open(dev, O_WRONLY); printf("INFO: Switching device %s to %s\n", dev, "<fixme>"); @@ -198,7 +198,7 @@ int main(int argc, char *argv[]) } // connect - if (msg.addrtype != 0 && dev && (msg.imgname != NULL)) + if (msg.host.type != 0 && dev && (msg.imgname != NULL)) { msg.imgnamelen = (uint16_t)strlen(msg.imgname); fd = open(dev, O_WRONLY); @@ -229,7 +229,7 @@ int main(int argc, char *argv[]) for (i = 0; i < j; i++) { - dnbd3_get_ip(g_key_file_get_string(gkf, groups[i], "server", NULL), msg.addr, &msg.addrtype); + dnbd3_get_ip(g_key_file_get_string(gkf, groups[i], "server", NULL), msg.host.addr, &msg.host.type); msg.imgname = g_key_file_get_string(gkf, groups[i], "name", NULL); msg.rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL); dev = g_key_file_get_string(gkf, groups[i], "device", NULL); diff --git a/src/kernel/blk.c b/src/kernel/blk.c index bc138c3..4282295 100644 --- a/src/kernel/blk.c +++ b/src/kernel/blk.c @@ -140,9 +140,9 @@ int dnbd3_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u } else { - memcpy(dev->cur_server.hostaddr, msg->addr, 16); - dev->cur_server.port = msg->port; - dev->cur_server.hostaddrtype = msg->addrtype; + if (sizeof(msg->host) != sizeof(dev->cur_server.host)) + printk("Odd size bug#1 triggered in IOCTL\n"); + memcpy(&dev->cur_server.host, &msg->host, sizeof(msg->host)); dev->cur_server.failures = 0; memcpy(&dev->initial_server, &dev->cur_server, sizeof(dev->initial_server)); dev->imgname = imgname; @@ -176,9 +176,7 @@ int dnbd3_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u case IOCTL_SWITCH: dnbd3_net_disconnect(dev); - memcpy(dev->cur_server.hostaddr, msg->addr, 16); - dev->cur_server.port = msg->port; - dev->cur_server.hostaddrtype = msg->addrtype; + memcpy(&dev->cur_server.host, &msg->host, sizeof(msg->host)); result = dnbd3_net_connect(dev); break; @@ -195,9 +193,7 @@ int dnbd3_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, u result = -EAGAIN; else { - memcpy(dev->new_servers[dev->new_servers_num].hostaddr, msg->addr, 16); - dev->new_servers[dev->new_servers_num].port = msg->port; - dev->new_servers[dev->new_servers_num].hostaddrtype = msg->addrtype; + memcpy(&dev->new_servers[dev->new_servers_num].host, &msg->host, sizeof(msg->host)); dev->new_servers[dev->new_servers_num].failures = (cmd == IOCTL_ADD_SRV ? 0 : 1); // 0 = ADD, 1 = REM ++dev->new_servers_num; result = 0; diff --git a/src/kernel/dnbd3.h b/src/kernel/dnbd3.h index fb83575..e7911cf 100644 --- a/src/kernel/dnbd3.h +++ b/src/kernel/dnbd3.h @@ -35,11 +35,9 @@ extern int major; typedef struct { + dnbd3_host_t host; unsigned long rtts[4]; // Last four round trip time measurements in µs - uint16_t port; // Port in network representation uint16_t protocol_version; // dnbd3 protocol version of this server - uint8_t hostaddr[16]; // Address in network representation (IPv4 or IPv6) - uint8_t hostaddrtype; // Address type (AF_INET or AF_INET6) uint8_t failures; // How many times the server was unreachable } dnbd3_server_t; diff --git a/src/kernel/net.c b/src/kernel/net.c index 2cd3cac..f437464 100644 --- a/src/kernel/net.c +++ b/src/kernel/net.c @@ -36,34 +36,34 @@ */ #if 1 // Change to 0 to disable debug messages #define debug_print_va_host(_host, _fmt, ...) do { \ - if (dev->cur_server.hostaddrtype == AF_INET) \ - printk("%s:%d " _fmt " (%s, %pI4:%d)\n", __FILE__, __LINE__, __VA_ARGS__, dev->disk->disk_name, (_host).hostaddr, (int)ntohs((_host).port)); \ + if ((_host).type == AF_INET) \ + printk("%s:%d " _fmt " (%s, %pI4:%d)\n", __FILE__, __LINE__, __VA_ARGS__, dev->disk->disk_name, (_host).addr, (int)ntohs((_host).port)); \ else \ - printk("%s:%d " _fmt " (%s, [%pI6]:%d)\n", __FILE__, __LINE__, __VA_ARGS__, dev->disk->disk_name, (_host).hostaddr, (int)ntohs((_host).port)); \ + printk("%s:%d " _fmt " (%s, [%pI6]:%d)\n", __FILE__, __LINE__, __VA_ARGS__, dev->disk->disk_name, (_host).addr, (int)ntohs((_host).port)); \ } while(0) #define debug_error_va_host(_host, _fmt, ...) do { \ debug_print_va_host(_host, _fmt, __VA_ARGS__); \ goto error; \ } while(0) -#define debug_dev_va(_fmt, ...) debug_print_va_host(dev->cur_server, _fmt, __VA_ARGS__) -#define error_dev_va(_fmt, ...) debug_error_va_host(dev->cur_server, _fmt, __VA_ARGS__) -#define debug_alt_va(_fmt, ...) debug_print_va_host(dev->alt_servers[i], _fmt, __VA_ARGS__) -#define error_alt_va(_fmt, ...) debug_error_va_host(dev->alt_servers[i], _fmt, __VA_ARGS__) +#define debug_dev_va(_fmt, ...) debug_print_va_host(dev->cur_server.host, _fmt, __VA_ARGS__) +#define error_dev_va(_fmt, ...) debug_error_va_host(dev->cur_server.host, _fmt, __VA_ARGS__) +#define debug_alt_va(_fmt, ...) debug_print_va_host(dev->alt_servers[i].host, _fmt, __VA_ARGS__) +#define error_alt_va(_fmt, ...) debug_error_va_host(dev->alt_servers[i].host, _fmt, __VA_ARGS__) #define debug_print_host(_host, txt) do { \ - if (dev->cur_server.hostaddrtype == AF_INET) \ - printk("%s:%d " txt " (%s, %pI4:%d)\n", __FILE__, __LINE__, dev->disk->disk_name, (_host).hostaddr, (int)ntohs((_host).port)); \ + if ((_host).type == AF_INET) \ + printk("%s:%d " txt " (%s, %pI4:%d)\n", __FILE__, __LINE__, dev->disk->disk_name, (_host).addr, (int)ntohs((_host).port)); \ else \ - printk("%s:%d " txt " (%s, [%pI6]:%d)\n", __FILE__, __LINE__, dev->disk->disk_name, (_host).hostaddr, (int)ntohs((_host).port)); \ + printk("%s:%d " txt " (%s, [%pI6]:%d)\n", __FILE__, __LINE__, dev->disk->disk_name, (_host).addr, (int)ntohs((_host).port)); \ } while(0) #define debug_error_host(_host, txt) do { \ debug_print_host(_host, txt); \ goto error; \ } while(0) -#define debug_dev(txt) debug_print_host(dev->cur_server, txt) -#define error_dev(txt) debug_error_host(dev->cur_server, txt) -#define debug_alt(txt) debug_print_host(dev->alt_servers[i], txt) -#define error_alt(txt) debug_error_host(dev->alt_servers[i], txt) +#define debug_dev(txt) debug_print_host(dev->cur_server.host, txt) +#define error_dev(txt) debug_error_host(dev->cur_server.host, txt) +#define debug_alt(txt) debug_print_host(dev->alt_servers[i].host, txt) +#define error_alt(txt) debug_error_host(dev->alt_servers[i].host, txt) #else // Silent @@ -79,9 +79,9 @@ static inline int is_same_server(const dnbd3_server_t *const a, const dnbd3_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))); + 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))); } static inline dnbd3_server_t *get_existing_server(const dnbd3_server_entry_t *const newserver, dnbd3_device_t *const dev) @@ -89,9 +89,9 @@ static inline dnbd3_server_t *get_existing_server(const dnbd3_server_entry_t *co int i; for (i = 0; i < NUMBER_SERVERS; ++i) { - if ((newserver->hostaddrtype == dev->alt_servers[i].hostaddrtype) - && (newserver->port == dev->alt_servers[i].port) - && (0 == memcmp(newserver->hostaddr, dev->alt_servers[i].hostaddr, (newserver->hostaddrtype == AF_INET ? 4 : 16)))) + if ((newserver->host.type == dev->alt_servers[i].host.type) + && (newserver->host.port == dev->alt_servers[i].host.port) + && (0 == memcmp(newserver->host.addr, dev->alt_servers[i].host.addr, (newserver->host.type == AF_INET ? 4 : 16)))) { return &dev->alt_servers[i]; break; @@ -105,7 +105,7 @@ static inline dnbd3_server_t *get_free_alt_server(dnbd3_device_t *const dev) int i; for (i = 0; i < NUMBER_SERVERS; ++i) { - if (dev->alt_servers[i].hostaddrtype == 0) + if (dev->alt_servers[i].host.type == 0) return &dev->alt_servers[i]; } for (i = 0; i < NUMBER_SERVERS; ++i) @@ -153,12 +153,12 @@ int dnbd3_net_connect(dnbd3_device_t *dev) error_dev("FATAL: Kmalloc(1) failed."); } - if (dev->cur_server.port == 0 || dev->cur_server.hostaddrtype == 0 || dev->imgname == NULL) + if (dev->cur_server.host.port == 0 || dev->cur_server.host.type == 0 || dev->imgname == NULL) error_dev("FATAL: Host, port or image name not set."); if (dev->sock) error_dev("ERROR: Already connected."); - if (dev->cur_server.hostaddrtype != AF_INET) + if (dev->cur_server.host.type != AF_INET) error_dev("ERROR: IPv6 not implemented."); else debug_dev("INFO: Connecting..."); @@ -180,8 +180,8 @@ int dnbd3_net_connect(dnbd3_device_t *dev) kernel_setsockopt(dev->sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)); dev->sock->sk->sk_allocation = GFP_NOIO; sin.sin_family = AF_INET; - memcpy(&(sin.sin_addr.s_addr), dev->cur_server.hostaddr, 4); - sin.sin_port = dev->cur_server.port; + memcpy(&(sin.sin_addr.s_addr), dev->cur_server.host.addr, 4); + sin.sin_port = dev->cur_server.host.port; if (kernel_connect(dev->sock, (struct sockaddr *) &sin, sizeof(sin), 0) != 0) error_dev("FATAL: Connection to host failed."); // Request filesize @@ -291,8 +291,8 @@ error: sock_release(dev->sock); dev->sock = NULL; } - dev->cur_server.hostaddrtype = 0; - dev->cur_server.port = 0; + dev->cur_server.host.type = 0; + dev->cur_server.host.port = 0; if (req1) kfree(req1); return -1; } @@ -337,8 +337,8 @@ int dnbd3_net_disconnect(dnbd3_device_t *dev) sock_release(dev->sock); dev->sock = NULL; } - dev->cur_server.hostaddrtype = 0; - dev->cur_server.port = 0; + dev->cur_server.host.type = 0; + dev->cur_server.host.port = 0; dev->disconnecting = 0; @@ -451,7 +451,7 @@ int dnbd3_net_discover(void *data) spin_lock_irqsave(&dev->blk_lock, irqflags); for (i = 0; i < dev->new_servers_num; ++i) { - if (dev->new_servers[i].hostaddrtype != AF_INET) // Invalid entry.. (Add IPv6) + if (dev->new_servers[i].host.type != AF_INET) // Invalid entry.. (Add IPv6) continue; alt_server = get_existing_server(&dev->new_servers[i], dev); if (alt_server != NULL) // Server already known @@ -459,7 +459,7 @@ int dnbd3_net_discover(void *data) if (dev->new_servers[i].failures == 1) { // REMOVE request - alt_server->hostaddrtype = 0; + alt_server->host.type = 0; continue; } // ADD, so just reset fail counter @@ -472,9 +472,9 @@ int dnbd3_net_discover(void *data) if (alt_server == NULL) // All NUMBER_SERVERS slots are taken, ignore entry continue; // Add new server entry - memcpy(alt_server->hostaddr, dev->new_servers[i].hostaddr, 16); - alt_server->hostaddrtype = dev->new_servers[i].hostaddrtype; - alt_server->port = dev->new_servers[i].port; + memcpy(alt_server->host.addr, dev->new_servers[i].host.addr, 16); + alt_server->host.type = dev->new_servers[i].host.type; + alt_server->host.port = dev->new_servers[i].host.port; alt_server->rtts[0] = alt_server->rtts[1] = alt_server->rtts[2] = alt_server->rtts[3] = RTT_UNREACHABLE; @@ -490,7 +490,7 @@ int dnbd3_net_discover(void *data) for (i=0; i < NUMBER_SERVERS; ++i) { - if (dev->alt_servers[i].hostaddrtype == 0) // Empty slot + if (dev->alt_servers[i].host.type == 0) // Empty slot continue; if (!dev->panic && dev->alt_servers[i].failures > 50) // If not in panic mode, skip server if it failed too many times continue; @@ -506,8 +506,8 @@ int dnbd3_net_discover(void *data) kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)); sock->sk->sk_allocation = GFP_NOIO; sin.sin_family = AF_INET; // add IPv6..... - memcpy(&sin.sin_addr.s_addr, dev->alt_servers[i].hostaddr, 4); - sin.sin_port = dev->alt_servers[i].port; + memcpy(&sin.sin_addr.s_addr, dev->alt_servers[i].host.addr, 4); + sin.sin_port = dev->alt_servers[i].host.port; if (kernel_connect(sock, (struct sockaddr *) &sin, sizeof(sin), 0) < 0) goto error; @@ -565,7 +565,7 @@ int dnbd3_net_discover(void *data) // panic mode, take first responding server if (dev->panic) { - printk("WARN: Panic mode (%s), taking server %pI4 : %d\n", dev->disk->disk_name, dev->alt_servers[i].hostaddr, (int)ntohs(dev->alt_servers[i].port)); + debug_alt("WARN: Panic mode, changing server:"); if (best_sock != NULL) sock_release(best_sock); dev->better_sock = sock; // Pass over socket to take a shortcut in *_connect(); kfree(buf); @@ -796,7 +796,7 @@ int dnbd3_net_send(void *data) return 0; error: - printk("ERROR: Connection to server %pI4 : %d lost (send)\n", dev->cur_server.hostaddr, (int)ntohs(dev->cur_server.port)); + debug_dev("ERROR: Connection to server lost (send)"); if (dev->sock) kernel_sock_shutdown(dev->sock, SHUT_RDWR); dev->thread_send = NULL; diff --git a/src/kernel/sysfs.c b/src/kernel/sysfs.c index 596e745..d219c86 100644 --- a/src/kernel/sysfs.c +++ b/src/kernel/sysfs.c @@ -29,10 +29,10 @@ ssize_t show_cur_server_addr(char *buf, dnbd3_device_t *dev) { - if (dev->cur_server.hostaddrtype == AF_INET) - return MIN(snprintf(buf, PAGE_SIZE, "%pI4,%d\n", dev->cur_server.hostaddr, (int)ntohs(dev->cur_server.port)), PAGE_SIZE); - else if (dev->cur_server.hostaddrtype == AF_INET6) - return MIN(snprintf(buf, PAGE_SIZE, "%pI6,%d\n", dev->cur_server.hostaddr, (int)ntohs(dev->cur_server.port)), PAGE_SIZE); + if (dev->cur_server.host.type == AF_INET) + return MIN(snprintf(buf, PAGE_SIZE, "%pI4,%d\n", dev->cur_server.host.addr, (int)ntohs(dev->cur_server.host.port)), PAGE_SIZE); + else if (dev->cur_server.host.type == AF_INET6) + return MIN(snprintf(buf, PAGE_SIZE, "%pI6,%d\n", dev->cur_server.host.addr, (int)ntohs(dev->cur_server.host.port)), PAGE_SIZE); *buf = '\0'; return 0; } @@ -47,7 +47,7 @@ ssize_t show_alt_server_num(char *buf, dnbd3_device_t *dev) int i, num = 0; for (i = 0; i < NUMBER_SERVERS; ++i) { - if (dev->alt_servers[i].hostaddrtype) ++num; + if (dev->alt_servers[i].host.type) ++num; } return MIN(snprintf(buf, PAGE_SIZE, "%d\n", num), PAGE_SIZE); } @@ -57,17 +57,17 @@ ssize_t show_alt_servers(char *buf, dnbd3_device_t *dev) int i, size = PAGE_SIZE, ret; for (i = 0; i < NUMBER_SERVERS; ++i) { - if (dev->alt_servers[i].hostaddrtype == AF_INET) + if (dev->alt_servers[i].host.type == AF_INET) ret = MIN(snprintf(buf, size, "%pI4,%d,%llu,%d\n", - dev->alt_servers[i].hostaddr, - (int)ntohs(dev->alt_servers[i].port), + dev->alt_servers[i].host.addr, + (int)ntohs(dev->alt_servers[i].host.port), (unsigned long long)((dev->alt_servers[i].rtts[0] + dev->alt_servers[i].rtts[1] + dev->alt_servers[i].rtts[2] + dev->alt_servers[i].rtts[3]) / 4), (int)dev->alt_servers[i].failures) , size); - else if (dev->alt_servers[i].hostaddrtype == AF_INET6) + else if (dev->alt_servers[i].host.type == AF_INET6) ret = MIN(snprintf(buf, size, "%pI6,%d,%llu,%d\n", - dev->alt_servers[i].hostaddr, - (int)ntohs(dev->alt_servers[i].port), + dev->alt_servers[i].host.addr, + (int)ntohs(dev->alt_servers[i].host.port), (unsigned long long)((dev->alt_servers[i].rtts[0] + dev->alt_servers[i].rtts[1] + dev->alt_servers[i].rtts[2] + dev->alt_servers[i].rtts[3]) / 4), (int)dev->alt_servers[i].failures) , size); 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 diff --git a/src/types.h b/src/types.h index d41111a..22fbc50 100644 --- a/src/types.h +++ b/src/types.h @@ -60,12 +60,19 @@ static const uint16_t dnbd3_packet_magic = (0x73) | (0x72 << 8); #error "Unknown Endianness" #endif +#pragma pack(1) +typedef struct +{ + uint8_t addr[16]; // 16byte (network representation, so it can be directly passed to socket functions) + uint16_t port; // 2byte (network representation, so it can be directly passed to socket functions) + uint8_t type; // 1byte (ip version. AF_INET or AF_INET6. 0 means this struct is empty and should be ignored) +} dnbd3_host_t; +#pragma pack(0) + typedef struct { uint16_t len; - uint8_t addrtype; - uint8_t addr[16]; // network representation - uint16_t port; // network representation + dnbd3_host_t host; uint16_t imgnamelen; char *imgname; int rid; @@ -106,10 +113,8 @@ typedef struct #pragma pack(1) typedef struct { - uint8_t hostaddr[16]; // 16byte (network representation, so it can be directly passed to socket functions) - uint16_t port; // 2byte (network representation, so it can be directly passed to socket functions) - uint8_t hostaddrtype; // 1byte (ip version. AF_INET or AF_INET6. 0 means this struct is empty and should be ignored) - uint8_t failures; // 1byte (number of times server has been consecutively unreachable) + dnbd3_host_t host; + uint8_t failures; // 1byte (number of times server has been consecutively unreachable) } dnbd3_server_entry_t; #pragma pack(0) |