From 60b2ba6ef9ed779d750958b6422f05c93a4a2d91 Mon Sep 17 00:00:00 2001 From: sr Date: Mon, 3 Sep 2012 21:00:26 +0200 Subject: [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 --- src/client/client.c | 18 +- src/kernel/blk.c | 14 +- src/kernel/dnbd3.h | 4 +- src/kernel/net.c | 78 ++--- src/kernel/sysfs.c | 22 +- src/server/helper.c | 162 +++++++++++ src/server/helper.h | 30 ++ src/server/ipc.c | 126 ++++---- src/server/ipc.h | 2 + src/server/job.c | 113 +++++++- src/server/net.c | 4 +- src/server/saveload.c | 655 ++++++++++++++++++++++++++++++++++++++++++ src/server/saveload.h | 60 ++++ src/server/server.c | 11 +- src/server/server.h | 41 +-- src/server/utils.c | 781 -------------------------------------------------- src/server/utils.h | 58 ---- src/server/xmlutil.h | 12 + src/types.h | 19 +- 19 files changed, 1186 insertions(+), 1024 deletions(-) create mode 100644 src/server/helper.c create mode 100644 src/server/helper.h create mode 100644 src/server/saveload.c create mode 100644 src/server/saveload.h delete mode 100644 src/server/utils.c delete mode 100644 src/server/utils.h (limited to 'src') 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, ""); @@ -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 +#include +#include +#include +#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, "", (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 +#include + +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 #include #include @@ -37,12 +44,6 @@ #include #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 + #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 #include #include @@ -7,8 +9,15 @@ #include #include #include +#include +#include + #include +#include +#include +#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 #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/saveload.c b/src/server/saveload.c new file mode 100644 index 0000000..6af9b39 --- /dev/null +++ b/src/server/saveload.c @@ -0,0 +1,655 @@ +/* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2011-2012 Johann Latocha + * + * This file may be licensed under the terms of of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "server.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 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); + + +void dnbd3_load_config() +{ + gint i, j; + + if (_config_handle != NULL) + { + printf("dnbd3_load_config() called more than once\n\n"); + exit(EXIT_FAILURE); + } + + _config_handle = g_key_file_new(); + if (!g_key_file_load_from_file(_config_handle, _config_file_name, G_KEY_FILE_NONE, NULL)) + { + printf("ERROR: Config file not found: %s\n", _config_file_name); + exit(EXIT_FAILURE); + } + + _local_namespace = g_key_file_get_string(_config_handle, "settings", "default_namespace", NULL); + if (_local_namespace && !is_valid_namespace(_local_namespace)) + { + memlogf("[ERROR] Ignoring default namespace: '%s' is not a valid namespace", _local_namespace); + g_free(_local_namespace); + _local_namespace = NULL; + } + + srand(time(NULL)); + + _ipc_password = g_key_file_get_string(_config_handle, "settings", "password", NULL); + + gchar **groups = NULL; + gsize section_count; + groups = g_key_file_get_groups(_config_handle, §ion_count); + + for (i = 0; i < section_count; i++) + { + // Ignore settings section + if (strcmp(groups[i], "settings") == 0) + continue; + + // List of trusted servers/namespaces + if (strncmp(groups[i], "trust:", 6) == 0) + { + 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, groups[i]+6); + g_free(addr); + if (server == NULL) + continue; + gsize key_count; + gchar **keys = g_key_file_get_keys(_config_handle, groups[i], &key_count, NULL); + for (j = 0; j < key_count; ++j) + { + if (strcmp(keys[j], "address") == 0) + continue; + char *flags = g_key_file_get_string(_config_handle, groups[i], keys[j], NULL); + g_key_file_remove_key(_config_handle, groups[i], keys[j], NULL); + dnbd3_add_trusted_namespace(server, keys[j], flags); + g_free(flags); + } + g_strfreev(keys); + continue; + } + + // An actual image definition + + int rid = g_key_file_get_integer(_config_handle, groups[i], "rid", NULL); + if (rid <= 0) + { + memlogf("[ERROR] Invalid rid '%d' for image '%s'", rid, groups[i]); + continue; + } + + const time_t delsoft = g_key_file_get_int64(_config_handle, groups[i], "delete_soft", NULL); + const time_t delhard = g_key_file_get_int64(_config_handle, groups[i], "delete_hard", NULL); + if ((delsoft != 0 && delsoft < time(NULL)) || (delhard != 0 && delhard < time(NULL))) + { + memlogf("[INFO] Ignoring image '%s' as its deletion is due", groups[i]); + continue; + } + + char *image_file = g_key_file_get_string(_config_handle, groups[i], "file", NULL); + char *cache_file = g_key_file_get_string(_config_handle, groups[i], "cache", NULL); + + dnbd3_image_t *image = prepare_image(groups[i], rid, image_file, cache_file); + if (image) + { + _dnbd3_images = g_slist_prepend(_dnbd3_images, image); + } + + g_free(image_file); + g_free(cache_file); + } + + g_strfreev(groups); +} + +int dnbd3_add_image(dnbd3_image_t *image) +{ + // Lock here to prevent concurrent add calls to mess rids up. Cannot happen currently + // as IPC clients are not threaded and they're the only place where this is called, + // but better be safe for the future... + pthread_spin_lock(&_spinlock); + if (image->rid == 0) + { + const dnbd3_image_t *latest = dnbd3_get_image(image->config_group, 0, 0); + if (latest) + image->rid = latest->rid + 1; + else + image->rid = 1; + } + + dnbd3_image_t *newimage = prepare_image(image->config_group, image->rid, image->file, image->cache_file); + if (newimage) + { + _dnbd3_images = g_slist_prepend(_dnbd3_images, image); + } + else + { + pthread_spin_unlock(&_spinlock); + return ERROR_SEE_LOG; + } + + // Adding image was successful, write config file + g_key_file_set_integer(_config_handle, image->config_group, "rid", image->rid); + g_key_file_set_string(_config_handle, image->config_group, "file", image->file); + //g_key_file_set_string(_config_handle, image->name, "servers", image->serverss); // TODO: Save servers as string + g_key_file_set_string(_config_handle, image->config_group, "cache", image->cache_file); + + pthread_spin_unlock(&_spinlock); + + const int ret = dnbd3_save_config(); + if (ret == ERROR_OK) + memlogf("[INFO] Added new image '%s' (rid %d)", newimage->config_group, newimage->rid); + else + memlogf("[INFO] Added new image '%s' (rid %d), but config file could not be written (%s)", newimage->config_group, newimage->rid, _config_file_name); + return ret; +} + +int dnbd3_del_image(dnbd3_image_t *image) +{ + if (image->rid <= 0) // Require a specific rid on deletion + return ERROR_RID; + pthread_spin_lock(&_spinlock); + dnbd3_image_t *existing_image = dnbd3_get_image(image->config_group, image->rid, 0); + if(existing_image == NULL) + { + pthread_spin_unlock(&_spinlock); + return ERROR_IMAGE_NOT_FOUND; + } + + existing_image->delete_soft = 1; // TODO: Configurable. + existing_image->delete_hard = time(NULL) + 86400; // TODO: Configurable + g_key_file_set_int64(_config_handle, image->config_group, "delete_soft", existing_image->delete_soft); + g_key_file_set_int64(_config_handle, image->config_group, "delete_hard", existing_image->delete_hard); + + pthread_spin_unlock(&_spinlock); + dnbd3_exec_delete(FALSE); + existing_image = NULL; + + const int ret = dnbd3_save_config(); + if (ret == ERROR_OK) + memlogf("[INFO] Marked for deletion: '%s' (rid %d)", image->config_group, image->rid); + else + memlogf("[WARNING] Marked for deletion: '%s' (rid %d), but config file could not be written (%s)", image->config_group, image->rid, _config_file_name); + return ret; +} + +int dnbd3_save_config() +{ + pthread_spin_lock(&_spinlock); + char *data = (char *)g_key_file_to_data(_config_handle, NULL, NULL); + if (data == NULL) + { + pthread_spin_unlock(&_spinlock); + memlogf("[ERROR] g_key_file_to_data() failed"); + return ERROR_UNSPECIFIED_ERROR; + } + + FILE *f = fopen(_config_file_name, "w"); + if (f < 0) + { + pthread_spin_unlock(&_spinlock); + g_free(data); + return ERROR_CONFIG_FILE_PERMISSIONS; + } + fputs("# Do not edit this file while dnbd3-server is running\n", f); + fputs(data, f); + fclose(f); + pthread_spin_unlock(&_spinlock); + g_free(data); + return 0; +} + +dnbd3_image_t *dnbd3_get_image(char *name_orig, int rid, const char do_lock) +{ + dnbd3_image_t *result = NULL, *image; + GSList *iterator; + // For comparison, make sure the name is global and lowercased + int slen; + int islocal = (strchr(name_orig, '/') == NULL); + if (islocal) + slen = strlen(name_orig) + strlen(_local_namespace) + 2; + else + slen = strlen(name_orig) + 1; + char name[slen]; + if (islocal) + sprintf(name, "%s/%s", _local_namespace, name_orig); + else + strcpy(name, name_orig); + strtolower(name); + // Now find the image + if (do_lock) + pthread_spin_lock(&_spinlock); + for (iterator = _dnbd3_images; iterator; iterator = iterator->next) + { + image = iterator->data; + if (rid != 0) // rid was specified + { + if (image->rid == rid && strcmp(name, image->low_name) == 0) + { + result = image; + break; + } + } + else // search max. rid available + { + if (strcmp(name, image->low_name) == 0 && (result == NULL || result->rid < image->rid)) + { + result = image; + } + } + } + if (do_lock) + pthread_spin_unlock(&_spinlock); + return result; +} + +void dnbd3_handle_sigpipe(int signum) +{ + memlogf("ERROR: SIGPIPE received (%s)", strsignal(signum)); +} + +void dnbd3_handle_sigterm(int signum) +{ + memlogf("INFO: SIGTERM or SIGINT received (%s)", strsignal(signum)); + dnbd3_cleanup(); +} + +/** + * Prepare image to be added to image list. Returns a pointer to a newly allocated image struct + * on success, NULL otherwise. + * Note: This function calls dnbd3_get_image without locking, so make sure you lock + * before calling this function while the server is active. + */ +static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file) +{ + int j; + if (image_name == NULL) + { + memlogf("[ERROR] Null Image-Name"); + return NULL; + } + if (!is_valid_imagename(image_name)) + { + memlogf("[ERROR] Invalid image name: '%s'", image_name); + return NULL; + } + + if (strchr(image_name, '.') == NULL && _local_namespace == NULL) + { + memlogf("[ERROR] Image '%s' has local name and no default namespace is defined; entry ignored.", image_name); + return NULL; + } + + // Allocate image struct and zero it out by using g_new0 + dnbd3_image_t *image = g_new0(dnbd3_image_t, 1); + if (image == NULL) + { + memlogf("[ERROR] Could not allocate dnbd3_image_t while reading config"); + return NULL; + } + + if (strchr(image_name, '/') == NULL) + { + // Local image, build global name + image->low_name = calloc(strlen(_local_namespace) + strlen(image_name) + 2, sizeof(char)); + sprintf(image->low_name, "%s/%s", _local_namespace, image_name); + } + else + { + image->low_name = strdup(image_name); + } + + if (dnbd3_get_image(image->low_name, rid, 0)) + { + memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image_name, rid); + goto error; + } + + strtolower(image->low_name); + image->config_group = strdup(image_name); + + image->rid = rid; + image->relayed = (image_file == NULL || image_file == '\0'); + + if (image->relayed) // Image is relayed (this server acts as proxy) + { + if (strchr(image_name, '/') == NULL) + { + memlogf("[ERROR] Relayed image without global name in config: '%s'", image_name); + goto error; + } + if (cache_file && *cache_file) + image->cache_file = strdup(cache_file); + } + else // Image is a local one, open file to get size + { + image->file = strdup(image_file); + int fd = open(image->file, O_RDONLY); + if (fd < 0) + { + memlogf("[ERROR] Image file not found: '%s'", image->file); + goto error; + } + const off_t size = lseek(fd, 0, SEEK_END); + if (size <= 0) + { + memlogf("[ERROR] File '%s' of image '%s' has size '%lld'. Image ignored.", + image->file, image_name, (long long)size); + goto error; + } + image->filesize = (uint64_t)size; + if (image->filesize & 4095) + { + memlogf("[WARNING] Size of image '%s' is not a multiple of 4096. Last incomplete block will be ignored!", + image->file); + image->filesize &= ~(uint64_t)4095; + } + close(fd); + image->working = 1; + } + + if (image->cache_file) + { + // Determine size of cached image + int fd = open(image->cache_file, O_RDONLY); + if (fd >= 0) + { + const off_t size = lseek(fd, 0, SEEK_END); + if (size > 0) + image->filesize = (uint64_t)size; + close(fd); + } + if (image->filesize & 4095) + { + // Cache files should always be truncated to 4kib boundaries already + memlogf("[WARNING] Size of cache file '%s' is not a multiple of 4096. Something's fishy!", image->cache_file); + image->filesize = 0; + } + else if (image->filesize > 0) + { + const size_t map_len_bytes = IMGSIZE_TO_MAPBYTES(image->filesize); + image->cache_map = calloc(map_len_bytes, sizeof(uint8_t)); + // read cache map from file + char tmp[strlen(image->cache_file) + 5]; + strcpy(tmp, image->cache_file); + strcat(tmp, ".map"); + fd = open(tmp, O_RDONLY); + if (fd >= 0) + { + const off_t size = lseek(fd, 0, SEEK_END); + if (size != map_len_bytes) + { + memlogf("[DEBUG] Cache-Map of %s is corrupted (%d != %d)", image_name, (int)size, (int)map_len_bytes); + } + else + { + lseek(fd, 0, SEEK_SET); + read(fd, image->cache_map, map_len_bytes); + // If the whole image is cached, mark it as working right away without waiting for an upstream server + image->working = 1; + for (j = 0; j < map_len_bytes - 1; ++j) + { + if (image->cache_map[j] != 0xFF) + { + image->working = 0; + break; + } + } + const int blocks_in_last_byte = (image->filesize >> 12) & 7; + uint8_t last_byte = 0; + if (blocks_in_last_byte == 0) + last_byte = 0xFF; + else + for (j = 0; j < blocks_in_last_byte; ++j) + last_byte |= (1 << j); + if ((image->cache_map[map_len_bytes - 1] & last_byte) != last_byte) + image->working = 0; + else + memlogf("[INFO] Instantly publishing relayed image '%s' because the local cache copy is complete", image_name); + } + close(fd); + } + + /* + // TODO: Do this as soon as a connection to a upstream server is established + // open cache file + fd = open(_images[i].cache_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 1) + memlogf("ERROR: Could't create cache file '%s'", _images[i].cache_file); + + if (_images[i].filesize != lseek(fd, 0, SEEK_END)) + fallocate(fd, 0, 0, _images[i].filesize); + + close(fd); + */ + } + } // end cache_file handling + return image; +error: + // Free stuff. Some pointers might be zero, but calling free() on those is safe. + free(image->cache_map); + free(image->config_group); + free(image->low_name); + free(image->file); + free(image->cache_file); + g_free(image); + return NULL; +} + +/** + * Iterate over all images and delete them if appropriate. + */ +void dnbd3_exec_delete(int save_if_changed) +{ + int changed = FALSE; + const time_t now = time(NULL); + GSList *image_iterator, *client_iterator; + char ipstr[100]; + + pthread_spin_lock(&_spinlock); + for (image_iterator = _dnbd3_images; image_iterator; image_iterator = image_iterator->next) + { + dnbd3_image_t *image = image_iterator->data; + int delete_now = TRUE; + if (image->delete_hard != 0 && image->delete_hard < now) + { + // Drop all clients still using it + for (client_iterator = _dnbd3_clients; client_iterator; client_iterator = client_iterator->next) + { + dnbd3_client_t *client = client_iterator->data; + if (client->image != image) + continue; + // Kill client's connection + *ipstr = '\0'; + 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; + close(fd); + delete_now = FALSE; // Wait for all clients being actually dropped; deletion will happen on next dnbd3_exec_delete() + } + } // END delete_hard image + else if (image->delete_soft != 0 && image->delete_soft < now && image->atime + 3600 < now) + { + // Image should be soft-deleted + // Check if it is still in use + for (client_iterator = _dnbd3_clients; client_iterator; client_iterator = client_iterator->next) + { + const dnbd3_client_t *client = client_iterator->data; + if (client->image == image) + { + // Yep, still in use, keep it + delete_now = FALSE; + break; + } + } + } + else // Neither hard nor soft delete, keep it + delete_now = FALSE; + if (delete_now) + { + // Image was not in use and should be deleted, free it! + memlogf("[INFO] Freeing end-of-life image %s", image->config_group); + changed = TRUE; + _dnbd3_images = g_slist_remove(_dnbd3_images, image); // Remove from image list + g_key_file_remove_group(_config_handle, image->config_group, NULL); // Also remove from config file + // Free any allocated memory + free(image->cache_map); + free(image->config_group); + free(image->low_name); + free(image->file); + free(image->cache_file); + g_free(image); + // Restart iteration as it would be messed up now + image_iterator = _dnbd3_images; + } + } // END image iteration + pthread_spin_lock(&_spinlock); + + if (changed && save_if_changed) + dnbd3_save_config(); +} + +/** + * Return pointer to trusted_server matching given address. + * If not found and create_if_not_found is TRUE, a new entry will be created, + * added to the list and then returned + * 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, char *comment) +{ + dnbd3_trusted_server_t server; + memset(&server, 0, sizeof(server)); + if (!parse_address(address, &server.host)) + { + memlogf("[WARNING] Could not parse address '%s' of trusted server", address); + return NULL; + } + GSList *iterator; + for (iterator = _trusted_servers; iterator; iterator = iterator->next) + { + dnbd3_trusted_server_t *comp = iterator->data; + if (is_same_server(comp, &server)) + return comp; + } + 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); + return copy; +} + +/** + * Add new trusted namespace to given trusted server, using given flags. + * Overwrites any existing entry for the given server and namespace + * !! Lock before calling this function !! + */ +int dnbd3_add_trusted_namespace(dnbd3_trusted_server_t *server, char *namespace, char *flags) +{ + int nslen = strlen(namespace) + 1; + char nslow[nslen]; + memcpy(nslow, namespace, nslen); + strtolower(nslow); + GSList *iterator; + dnbd3_namespace_t *ns = NULL; + for (iterator = server->namespaces; iterator; iterator = iterator->next) + { + dnbd3_namespace_t *cmp = iterator->data; + if (strcmp(nslow, cmp->name) == 0) + { + ns = cmp; + break; + } + } + if (ns == NULL) + { + ns = calloc(1, sizeof(*ns)); + ns->name = strdup(nslow); + server->namespaces = g_slist_prepend(server->namespaces, ns); + } + ns->auto_replicate = (flags && strstr(flags, "replicate")); + ns->recursive = (flags && strstr(flags, "recursive")); + return TRUE; +} + +/** + * Return local image name for a global image name + * eg. "uni-freiburg/rz/ubuntu 12.04" -> "ubuntu 12.04" + * ONLY IF the local name space really is "uni-freiburg/rz" + * Returns NULL otherwise + * The returned pointer points to memory inside the passed + * string (if not NULL), so do not modify or free + * / <--- +static char* get_local_image_name(char *global_name) +{ + if (_local_namespace == NULL) + return NULL; // No local namespace defined, so it cannot be local + char *first_slash = strchr(global_name, '/'); + if (first_slash == NULL) + return global_name; // Already local + const size_t buflen = strlen(_local_namespace) + 1; + if (first_slash - global_name + 1 != buflen) + return NULL; // Namespaces have different length, cannot be same + char namespace[buflen]; + char passedname[buflen]; + strcpy(namespace, _local_namespace); + strncpy(passedname, global_name, buflen); + passedname[buflen] = '\0'; + strtolower(namespace); + strtolower(passedname); + if (strcmp(namespace, passedname) == 0) + return global_name + buflen; // points somewhere into passed buffer + return NULL; +} //*/ diff --git a/src/server/saveload.h b/src/server/saveload.h new file mode 100644 index 0000000..065c0b1 --- /dev/null +++ b/src/server/saveload.h @@ -0,0 +1,60 @@ +/* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2011-2012 Johann Latocha + * + * This file may be licensed under the terms of of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include + +#include "server.h" +#include "../config.h" + +#ifndef UTILS_H_ +#define UTILS_H_ + +#define ERROR_OK 0 +#define ERROR_FILE_NOT_FOUND 1 +#define ERROR_IMAGE_ALREADY_EXISTS 2 +#define ERROR_CONFIG_FILE_PERMISSIONS 3 +#define ERROR_IMAGE_NOT_FOUND 4 +#define ERROR_RID 5 +#define ERROR_IMAGE_IN_USE 6 +#define ERROR_MISSING_ARGUMENT 7 +#define ERROR_UNSPECIFIED_ERROR 8 +#define ERROR_INVALID_XML 9 +#define ERROR_UNKNOWN_COMMAND 10 +#define ERROR_SEE_LOG 11 +#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); + +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, char *comment); +int dnbd3_add_trusted_namespace(dnbd3_trusted_server_t *server, char *namespace, char *flags); + +void dnbd3_handle_sigpipe(int signum); +void dnbd3_handle_sigterm(int signum); + +#endif /* UTILS_H_ */ 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/utils.c b/src/server/utils.c deleted file mode 100644 index ad40322..0000000 --- a/src/server/utils.c +++ /dev/null @@ -1,781 +0,0 @@ -/* - * This file is part of the Distributed Network Block Device 3 - * - * Copyright(c) 2011-2012 Johann Latocha - * - * This file may be licensed under the terms of of the - * GNU General Public License Version 2 (the ``GPL''). - * - * Software distributed under the License is distributed - * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See the GPL for the specific language - * governing rights and limitations. - * - * You should have received a copy of the GPL along with this - * program. If not, go to http://www.gnu.org/licenses/gpl.html - * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "server.h" -#include "utils.h" -#include "memlog.h" - -// Keep parsed config file in memory so it doesn't need to be parsed again every time it's modified -static GKeyFile *_config_handle = NULL; - -static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *port); -static char is_valid_namespace(char *namespace); -static char is_valid_imagename(char *namespace); -static void strtolower(char *string); -static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file, gchar **servers, gsize num_servers); -static int save_config(); -//static char* get_local_image_name(char *global_name); - -/** - * Parse IPv4 or IPv6 address in string representation to a suitable format usable by the BSD socket library - * @string eg. "1.2.3.4" or "2a01::10:5", optially with port appended, eg "1.2.3.4:6666" or "[2a01::10:5]:6666" - * @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() -{ - gint i, j; - - if (_config_handle != NULL) - { - printf("dnbd3_load_config() called more than once\n\n"); - exit(EXIT_FAILURE); - } - - _config_handle = g_key_file_new(); - if (!g_key_file_load_from_file(_config_handle, _config_file_name, G_KEY_FILE_NONE, NULL)) - { - printf("ERROR: Config file not found: %s\n", _config_file_name); - exit(EXIT_FAILURE); - } - - _local_namespace = g_key_file_get_string(_config_handle, "settings", "default_namespace", NULL); - if (_local_namespace && !is_valid_namespace(_local_namespace)) - { - memlogf("[ERROR] Ignoring default namespace: '%s' is not a valid namespace", _local_namespace); - g_free(_local_namespace); - _local_namespace = NULL; - } - - _ipc_password = g_key_file_get_string(_config_handle, "settings", "password", NULL); - - gchar **groups = NULL; - gsize section_count; - groups = g_key_file_get_groups(_config_handle, §ion_count); - - for (i = 0; i < section_count; i++) - { - // Ignore settings section - if (strcmp(groups[i], "settings") == 0) - continue; - - // List of trusted servers/namespaces - if (strncmp(groups[i], "trust:", 6) == 0) - { - 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); - 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) - { - if (strcmp(keys[j], "address") == 0) - continue; - char *flags = g_key_file_get_string(_config_handle, groups[i], keys[j], NULL); - g_key_file_remove_key(_config_handle, groups[i], keys[j], NULL); - dnbd3_add_trusted_namespace(server, keys[j], flags); - g_free(flags); - } - g_strfreev(keys); - continue; - } - - // An actual image definition - - int rid = g_key_file_get_integer(_config_handle, groups[i], "rid", NULL); - if (rid <= 0) - { - memlogf("[ERROR] Invalid rid '%d' for image '%s'", rid, groups[i]); - continue; - } - - const time_t delsoft = g_key_file_get_int64(_config_handle, groups[i], "delete_soft", NULL); - const time_t delhard = g_key_file_get_int64(_config_handle, groups[i], "delete_hard", NULL); - if ((delsoft != 0 && delsoft < time(NULL)) || (delhard != 0 && delhard < time(NULL))) - { - memlogf("[INFO] Ignoring image '%s' as its deletion is due", groups[i]); - continue; - } - - char *image_file = g_key_file_get_string(_config_handle, groups[i], "file", NULL); - char *cache_file = g_key_file_get_string(_config_handle, groups[i], "cache", NULL); - gsize num_servers; - gchar **servers = g_key_file_get_string_list(_config_handle, groups[i], "servers", &num_servers, NULL); - - dnbd3_image_t *image = prepare_image(groups[i], rid, image_file, cache_file, servers, num_servers); - if (image) - { - _dnbd3_images = g_slist_prepend(_dnbd3_images, image); - } - - g_free(image_file); - g_free(cache_file); - g_strfreev(servers); - } - - g_strfreev(groups); -} - -int dnbd3_add_image(dnbd3_image_t *image) -{ - // Lock here to prevent concurrent add calls to mess rids up. Cannot happen currently - // as IPC clients are not threaded and they're the only place where this is called, - // but better be safe for the future... - pthread_spin_lock(&_spinlock); - if (image->rid == 0) - { - const dnbd3_image_t *latest = dnbd3_get_image(image->config_group, 0, 0); - if (latest) - image->rid = latest->rid + 1; - else - image->rid = 1; - } - - dnbd3_image_t *newimage = prepare_image(image->config_group, image->rid, image->file, image->cache_file, NULL, 0); - if (newimage) - { - _dnbd3_images = g_slist_prepend(_dnbd3_images, image); - } - else - { - pthread_spin_unlock(&_spinlock); - return ERROR_SEE_LOG; - } - - // Adding image was successful, write config file - g_key_file_set_integer(_config_handle, image->config_group, "rid", image->rid); - g_key_file_set_string(_config_handle, image->config_group, "file", image->file); - //g_key_file_set_string(_config_handle, image->name, "servers", image->serverss); // TODO: Save servers as string - g_key_file_set_string(_config_handle, image->config_group, "cache", image->cache_file); - - pthread_spin_unlock(&_spinlock); - - const int ret = save_config(); - if (ret == ERROR_OK) - memlogf("[INFO] Added new image '%s' (rid %d)", newimage->config_group, newimage->rid); - else - memlogf("[INFO] Added new image '%s' (rid %d), but config file could not be written (%s)", newimage->config_group, newimage->rid, _config_file_name); - return ret; -} - -int dnbd3_del_image(dnbd3_image_t *image) -{ - if (image->rid <= 0) // Require a specific rid on deletion - return ERROR_RID; - pthread_spin_lock(&_spinlock); - dnbd3_image_t *existing_image = dnbd3_get_image(image->config_group, image->rid, 0); - if(existing_image == NULL) - { - pthread_spin_unlock(&_spinlock); - return ERROR_IMAGE_NOT_FOUND; - } - - existing_image->delete_soft = 1; // TODO: Configurable. - existing_image->delete_hard = time(NULL) + 86400; // TODO: Configurable - g_key_file_set_int64(_config_handle, image->config_group, "delete_soft", existing_image->delete_soft); - g_key_file_set_int64(_config_handle, image->config_group, "delete_hard", existing_image->delete_hard); - - pthread_spin_unlock(&_spinlock); - dnbd3_exec_delete(FALSE); - existing_image = NULL; - - const int ret = save_config(); - if (ret == ERROR_OK) - memlogf("[INFO] Marked for deletion: '%s' (rid %d)", image->config_group, image->rid); - else - memlogf("[WARNING] Marked for deletion: '%s' (rid %d), but config file could not be written (%s)", image->config_group, image->rid, _config_file_name); - return ret; -} - -static int save_config() -{ - pthread_spin_lock(&_spinlock); - char *data = (char *)g_key_file_to_data(_config_handle, NULL, NULL); - if (data == NULL) - { - pthread_spin_unlock(&_spinlock); - memlogf("[ERROR] g_key_file_to_data() failed"); - return ERROR_UNSPECIFIED_ERROR; - } - - FILE *f = fopen(_config_file_name, "w"); - if (f < 0) - { - pthread_spin_unlock(&_spinlock); - g_free(data); - return ERROR_CONFIG_FILE_PERMISSIONS; - } - fputs("# Do not edit this file while dnbd3-server is running\n", f); - fputs(data, f); - fclose(f); - pthread_spin_unlock(&_spinlock); - g_free(data); - return 0; -} - -dnbd3_image_t *dnbd3_get_image(char *name_orig, int rid, const char do_lock) -{ - dnbd3_image_t *result = NULL, *image; - GSList *iterator; - // For comparison, make sure the name is global and lowercased - int slen; - int islocal = (strchr(name_orig, '/') == NULL); - if (islocal) - slen = strlen(name_orig) + strlen(_local_namespace) + 2; - else - slen = strlen(name_orig) + 1; - char name[slen]; - if (islocal) - sprintf(name, "%s/%s", _local_namespace, name_orig); - else - strcpy(name, name_orig); - strtolower(name); - // Now find the image - if (do_lock) - pthread_spin_lock(&_spinlock); - for (iterator = _dnbd3_images; iterator; iterator = iterator->next) - { - image = iterator->data; - if (rid != 0) // rid was specified - { - if (image->rid == rid && strcmp(name, image->low_name) == 0) - { - result = image; - break; - } - } - else // search max. rid available - { - if (strcmp(name, image->low_name) == 0 && (result == NULL || result->rid < image->rid)) - { - result = image; - } - } - } - if (do_lock) - pthread_spin_unlock(&_spinlock); - return result; -} - -void dnbd3_handle_sigpipe(int signum) -{ - memlogf("ERROR: SIGPIPE received!\n"); -} - -void dnbd3_handle_sigterm(int signum) -{ - memlogf("INFO: SIGTERM or SIGINT received!\n"); - dnbd3_cleanup(); -} - -/** - * Prepare image to be added to image list. Returns a pointer to a newly allocated image struct - * on success, NULL otherwise. - * Note: This function calls dnbd3_get_image without locking, so make sure you lock - * before calling this function while the server is active. - */ -static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file, gchar **servers, gsize num_servers) -{ - int j, k; - if (image_name == NULL) - { - memlogf("[ERROR] Null Image-Name"); - return NULL; - } - if (!is_valid_imagename(image_name)) - { - memlogf("[ERROR] Invalid image name: '%s'", image_name); - return NULL; - } - - if (strchr(image_name, '.') == NULL && _local_namespace == NULL) - { - memlogf("[ERROR] Image '%s' has local name and no default namespace is defined; entry ignored.", image_name); - return NULL; - } - - // Allocate image struct and zero it out by using g_new0 - dnbd3_image_t *image = g_new0(dnbd3_image_t, 1); - if (image == NULL) - { - memlogf("[ERROR] Could not allocate dnbd3_image_t while reading config"); - return NULL; - } - - if (strchr(image_name, '/') == NULL) - { - // Local image, build global name - image->low_name = calloc(strlen(_local_namespace) + strlen(image_name) + 2, sizeof(char)); - sprintf(image->low_name, "%s/%s", _local_namespace, image_name); - } - else - { - image->low_name = strdup(image_name); - } - - if (dnbd3_get_image(image->low_name, rid, 0)) - { - memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image_name, rid); - goto error; - } - - strtolower(image->low_name); - image->config_group = strdup(image_name); - - image->rid = rid; - image->relayed = (image_file == NULL || image_file == '\0'); - - if (image->relayed) // Image is relayed (this server acts as proxy) - { - if (strchr(image_name, '/') == NULL) - { - memlogf("[ERROR] Relayed image without global name in config: '%s'", image_name); - goto error; - } - if (cache_file && *cache_file) - image->cache_file = strdup(cache_file); - } - else // Image is a local one, open file to get size - { - image->file = strdup(image_file); - int fd = open(image->file, O_RDONLY); - if (fd < 0) - { - memlogf("[ERROR] Image file not found: '%s'", image->file); - goto error; - } - const off_t size = lseek(fd, 0, SEEK_END); - if (size <= 0) - { - memlogf("[ERROR] File '%s' of image '%s' has size '%lld'. Image ignored.", - image->file, image_name, (long long)size); - goto error; - } - image->filesize = (uint64_t)size; - if (image->filesize & 4095) - { - memlogf("[WARNING] Size of image '%s' is not a multiple of 4096. Last incomplete block will be ignored!", - image->file); - image->filesize &= ~(uint64_t)4095; - } - close(fd); - image->working = 1; - } - - // A list of servers that are known to also host or relay this image - if (servers) - for (k = 0, j = 0; j < MIN(num_servers, NUMBER_SERVERS); ++j) - { - if (parse_address(servers[j], &(image->servers[k].hostaddrtype), image->servers[k].hostaddr, - &(image->servers[k].port))) - { - ++k; - continue; - } - image->servers[k].hostaddrtype = 0; - } - - if (image->cache_file) - { - // Determine size of cached image - int fd = open(image->cache_file, O_RDONLY); - if (fd >= 0) - { - const off_t size = lseek(fd, 0, SEEK_END); - if (size > 0) - image->filesize = (uint64_t)size; - close(fd); - } - if (image->filesize & 4095) - { - // Cache files should always be truncated to 4kib boundaries already - memlogf("[WARNING] Size of cache file '%s' is not a multiple of 4096. Something's fishy!", image->cache_file); - image->filesize = 0; - } - else if (image->filesize > 0) - { - const size_t map_len_bytes = IMGSIZE_TO_MAPBYTES(image->filesize); - image->cache_map = calloc(map_len_bytes, sizeof(uint8_t)); - // read cache map from file - char tmp[strlen(image->cache_file) + 5]; - strcpy(tmp, image->cache_file); - strcat(tmp, ".map"); - fd = open(tmp, O_RDONLY); - if (fd >= 0) - { - const off_t size = lseek(fd, 0, SEEK_END); - if (size != map_len_bytes) - { - memlogf("[DEBUG] Cache-Map of %s is corrupted (%d != %d)", image_name, (int)size, (int)map_len_bytes); - } - else - { - lseek(fd, 0, SEEK_SET); - read(fd, image->cache_map, map_len_bytes); - // If the whole image is cached, mark it as working right away without waiting for an upstream server - image->working = 1; - for (j = 0; j < map_len_bytes - 1; ++j) - { - if (image->cache_map[j] != 0xFF) - { - image->working = 0; - break; - } - } - const int blocks_in_last_byte = (image->filesize >> 12) & 7; - uint8_t last_byte = 0; - if (blocks_in_last_byte == 0) - last_byte = 0xFF; - else - for (j = 0; j < blocks_in_last_byte; ++j) - last_byte |= (1 << j); - if ((image->cache_map[map_len_bytes - 1] & last_byte) != last_byte) - image->working = 0; - else - memlogf("[INFO] Instantly publishing relayed image '%s' because the local cache copy is complete", image_name); - } - close(fd); - } - - /* - // TODO: Do this as soon as a connection to a upstream server is established - // open cache file - fd = open(_images[i].cache_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (fd < 1) - memlogf("ERROR: Could't create cache file '%s'", _images[i].cache_file); - - if (_images[i].filesize != lseek(fd, 0, SEEK_END)) - fallocate(fd, 0, 0, _images[i].filesize); - - close(fd); - */ - } - } // end cache_file handling - return image; -error: - // Free stuff. Some pointers might be zero, but calling free() on those is safe. - free(image->cache_map); - free(image->config_group); - free(image->low_name); - free(image->file); - free(image->cache_file); - g_free(image); - return NULL; -} - -/** - * Iterate over all images and delete them if appropriate. - */ -void dnbd3_exec_delete(int save_if_changed) -{ - int changed = FALSE; - const time_t now = time(NULL); - GSList *image_iterator, *client_iterator; - char ipstr[100]; - - pthread_spin_lock(&_spinlock); - for (image_iterator = _dnbd3_images; image_iterator; image_iterator = image_iterator->next) - { - dnbd3_image_t *image = image_iterator->data; - int delete_now = TRUE; - if (image->delete_hard != 0 && image->delete_hard < now) - { - // Drop all clients still using it - for (client_iterator = _dnbd3_clients; client_iterator; client_iterator = client_iterator->next) - { - dnbd3_client_t *client = client_iterator->data; - if (client->image != image) - continue; - // Kill client's connection - *ipstr = '\0'; - inet_ntop(client->addrtype, client->ipaddr, ipstr, 100); - memlogf("[INFO] delete_hard of %s reached; dropping client %s", image->config_group, ipstr); - const int fd = client->sock; - client->sock = -1; - close(fd); - delete_now = FALSE; // Wait for all clients being actually dropped; deletion will happen on next dnbd3_exec_delete() - } - } // END delete_hard image - else if (image->delete_soft != 0 && image->delete_soft < now) - { - // Image should be soft-deleted - // Check if it is still in use - for (client_iterator = _dnbd3_clients; client_iterator; client_iterator = client_iterator->next) - { - const dnbd3_client_t *client = client_iterator->data; - if (client->image == image) - { - // Yep, still in use, keep it - delete_now = FALSE; - break; - } - } - } - else // Neither hard nor soft delete, keep it - delete_now = FALSE; - if (delete_now) - { - // Image was not in use and should be deleted, free it! - memlogf("[INFO] Freeing end-of-life image %s", image->config_group); - changed = TRUE; - _dnbd3_images = g_slist_remove(_dnbd3_images, image); // Remove from image list - g_key_file_remove_group(_config_handle, image->config_group, NULL); // Also remove from config file - // Free any allocated memory - free(image->cache_map); - free(image->config_group); - free(image->low_name); - free(image->file); - free(image->cache_file); - g_free(image); - // Restart iteration as it would be messed up now - image_iterator = _dnbd3_images; - } - } // END image iteration - pthread_spin_lock(&_spinlock); - - if (changed && save_if_changed) - save_config(); -} - -/** - * Return pointer to trusted_server matching given address. - * If not found and create_if_not_found is TRUE, a new entry will be created, - * added to the list and then returned - * 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 server; - memset(&server, 0, sizeof(server)); - if (!parse_address(address, &server.hostaddrtype, server.hostaddr, &server.port)) - { - memlogf("[WARNING] Could not parse address '%s' of trusted server", address); - return NULL; - } - GSList *iterator; - for (iterator = _trusted_servers; iterator; iterator = iterator->next) - { - dnbd3_trusted_server_t *comp = iterator->data; - if (is_same_server(comp, &server)) - return comp; - } - if (!create_if_not_found) - return NULL; - dnbd3_trusted_server_t *copy = malloc(sizeof(server)); - memcpy(copy, &server, sizeof(*copy)); - _trusted_servers = g_slist_prepend(_trusted_servers, copy); - return copy; -} - -/** - * Add new trusted namespace to given trusted server, using given flags. - * Overwrites any existing entry for the given server and namespace - * !! Lock before calling this function !! - */ -int dnbd3_add_trusted_namespace(dnbd3_trusted_server_t *server, char *namespace, char *flags) -{ - int nslen = strlen(namespace) + 1; - char nslow[nslen]; - memcpy(nslow, namespace, nslen); - strtolower(nslow); - GSList *iterator; - dnbd3_namespace_t *ns = NULL; - for (iterator = server->namespaces; iterator; iterator = iterator->next) - { - dnbd3_namespace_t *cmp = iterator->data; - if (strcmp(nslow, cmp->name) == 0) - { - ns = cmp; - break; - } - } - if (ns == NULL) - { - ns = calloc(1, sizeof(*ns)); - ns->name = strdup(nslow); - server->namespaces = g_slist_prepend(server->namespaces, ns); - } - ns->auto_replicate = (flags && strstr(flags, "replicate")); - ns->recursive = (flags && strstr(flags, "recursive")); - return TRUE; -} - -/** - * Return local image name for a global image name - * eg. "uni-freiburg/rz/ubuntu 12.04" -> "ubuntu 12.04" - * ONLY IF the local name space really is "uni-freiburg/rz" - * Returns NULL otherwise - * The returned pointer points to memory inside the passed - * string (if not NULL), so do not modify or free - * / <--- -static char* get_local_image_name(char *global_name) -{ - if (_local_namespace == NULL) - return NULL; // No local namespace defined, so it cannot be local - char *first_slash = strchr(global_name, '/'); - if (first_slash == NULL) - return global_name; // Already local - const size_t buflen = strlen(_local_namespace) + 1; - if (first_slash - global_name + 1 != buflen) - return NULL; // Namespaces have different length, cannot be same - char namespace[buflen]; - char passedname[buflen]; - strcpy(namespace, _local_namespace); - strncpy(passedname, global_name, buflen); - passedname[buflen] = '\0'; - strtolower(namespace); - strtolower(passedname); - if (strcmp(namespace, passedname) == 0) - return global_name + buflen; // points somewhere into passed buffer - return NULL; -} //*/ diff --git a/src/server/utils.h b/src/server/utils.h deleted file mode 100644 index e254cfd..0000000 --- a/src/server/utils.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of the Distributed Network Block Device 3 - * - * Copyright(c) 2011-2012 Johann Latocha - * - * This file may be licensed under the terms of of the - * GNU General Public License Version 2 (the ``GPL''). - * - * Software distributed under the License is distributed - * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See the GPL for the specific language - * governing rights and limitations. - * - * You should have received a copy of the GPL along with this - * program. If not, go to http://www.gnu.org/licenses/gpl.html - * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include - -#include "server.h" -#include "../config.h" - -#ifndef UTILS_H_ -#define UTILS_H_ - -#define ERROR_OK 0 -#define ERROR_FILE_NOT_FOUND 1 -#define ERROR_IMAGE_ALREADY_EXISTS 2 -#define ERROR_CONFIG_FILE_PERMISSIONS 3 -#define ERROR_IMAGE_NOT_FOUND 4 -#define ERROR_RID 5 -#define ERROR_IMAGE_IN_USE 6 -#define ERROR_MISSING_ARGUMENT 7 -#define ERROR_UNSPECIFIED_ERROR 8 -#define ERROR_INVALID_XML 9 -#define ERROR_UNKNOWN_COMMAND 10 -#define ERROR_SEE_LOG 11 -#define ERROR_WRONG_PASSWORD 12 - -void dnbd3_load_config(); -int dnbd3_add_image(dnbd3_image_t *image); -int dnbd3_del_image(dnbd3_image_t *image); - -void dnbd3_exec_delete(int save_if_changed); - -dnbd3_image_t *dnbd3_get_image(char *name, int rid, const char do_lock); - -dnbd3_trusted_server_t *dnbd3_get_trusted_server(char *address, char create_if_not_found); -int dnbd3_add_trusted_namespace(dnbd3_trusted_server_t *server, char *namespace, char *flags); - -void dnbd3_handle_sigpipe(int signum); -void dnbd3_handle_sigterm(int signum); - -#endif /* UTILS_H_ */ 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) -- cgit v1.2.3-55-g7522