From dc81a51d59ccf31a5b47b989060a626e97a3d709 Mon Sep 17 00:00:00 2001 From: sr Date: Mon, 8 Jul 2013 18:46:26 +0200 Subject: Rewriting.... --- .gitignore | 2 + LOCKS | 4 + src/config.h | 9 +- src/server/image.c | 38 +++ src/server/image.h | 8 + src/server/job.c | 772 --------------------------------------------- src/server/job.h | 7 - src/server/net.c | 2 +- src/server/net.h | 2 +- src/server/rpc.c | 810 ------------------------------------------------ src/server/rpc.h | 55 ---- src/server/saveload.c | 734 ------------------------------------------- src/server/saveload.h | 63 ---- src/server/server.c | 63 ++-- src/server/server.h | 62 ++-- src/server/sockhelper.h | 12 + src/server/xmlutil.c | 43 --- src/server/xmlutil.h | 55 ---- 18 files changed, 148 insertions(+), 2593 deletions(-) create mode 100644 src/server/image.c create mode 100644 src/server/image.h delete mode 100644 src/server/job.c delete mode 100644 src/server/job.h delete mode 100644 src/server/rpc.c delete mode 100644 src/server/rpc.h delete mode 100644 src/server/saveload.c delete mode 100644 src/server/saveload.h delete mode 100644 src/server/xmlutil.c delete mode 100644 src/server/xmlutil.h diff --git a/.gitignore b/.gitignore index cea98b8..97b41dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ build/ .cproject .project +*.swp +.autotools diff --git a/LOCKS b/LOCKS index 689dd06..d6085c5 100644 --- a/LOCKS +++ b/LOCKS @@ -12,6 +12,10 @@ This is a list of used locks, in the order they have to be aquired if you must hold multiple locks: _clients_lock _clients[].lock +_images_lock +_images[].lock +_alts_lock + If you need to lock multiple clients at once, lock the client with the lowest array index first. diff --git a/src/config.h b/src/config.h index 2668410..a59f7bc 100644 --- a/src/config.h +++ b/src/config.h @@ -21,9 +21,15 @@ #ifndef CONFIG_H_ #define CONFIG_H_ +// +++++ Performance related +#define SERVER_MAX_CLIENTS 5000 +#define SERVER_MAX_IMAGES 5000 +#define SERVER_MAX_ALTS 1000 + // +++++ Network +++++ // Default port #define PORT 5003 +#define RPC_PORT (PORT+1) // Protocol version should be increased whenever new features/messages are added, // so either the client or server can run in compatibility mode, or they can @@ -33,6 +39,8 @@ #define MIN_SUPPORTED_CLIENT 2 // Which is the minimum protocol version the client expects from the server #define MIN_SUPPORTED_SERVER 2 +// Length of comment fields (for alt server etc.) +#define COMMENT_LENGTH 120 // No payload allowed exceeding this many bytes (actual data from client->server is not affected by this limit!) #define MAX_PAYLOAD 1000 @@ -69,6 +77,5 @@ // +++++ Misc +++++ #define DEFAULT_SERVER_CONFIG_FILE "/etc/dnbd3/server.conf" #define DEFAULT_CLIENT_CONFIG_FILE "/etc/dnbd3/client.conf" -#define MAX_RPC_PAYLOAD 3000 #endif /* CONFIG_H_ */ diff --git a/src/server/image.c b/src/server/image.c new file mode 100644 index 0000000..8a2b943 --- /dev/null +++ b/src/server/image.c @@ -0,0 +1,38 @@ +#include "image.h" + +#include +#include + +int image_is_complete(dnbd3_image_t *image) +{ + assert(image != NULL); + if (image->working && image->cache_map == NULL) { + return TRUE; + } + if (image->filesize == 0 || !image->working) { + return FALSE; + } + int complete = TRUE, j; + const int map_len_bytes = IMGSIZE_TO_MAPBYTES(image->filesize); + for (j = 0; j < map_len_bytes - 1; ++j) + { + if (image->cache_map[j] != 0xFF) + { + complete = FALSE; + break; + } + } + if (complete) // Every block except the last one is complete + { // Last one might need extra treatment if it's not a full byte + 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); + } + complete = ((image->cache_map[map_len_bytes - 1] & last_byte) == last_byte); + } + return complete; +} diff --git a/src/server/image.h b/src/server/image.h new file mode 100644 index 0000000..ea09940 --- /dev/null +++ b/src/server/image.h @@ -0,0 +1,8 @@ +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include "server.h" + +int image_is_complete(dnbd3_image_t *image); + +#endif diff --git a/src/server/job.c b/src/server/job.c deleted file mode 100644 index 31f577b..0000000 --- a/src/server/job.c +++ /dev/null @@ -1,772 +0,0 @@ -#include "job.h" -#include "saveload.h" -#include "helper.h" -#include "memlog.h" -#include "rpc.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sockhelper.h" - -#include - -#include -#include -#include "xmlutil.h" -#include "../config.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 - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -typedef struct -{ - char available; - char name[DEV_STRLEN]; -} device_t; - -// "/dev/dnbdXX" == 11 bytes per device + nullchar = 12 -static device_t *devices = NULL; -static int num_devices = 0; -static char keep_running = TRUE; - -// Private functions -static char *get_free_device(); -static void return_free_device(char *name); -static void connect_proxy_images(); -static void query_servers(); -static char *create_cache_filename(char *name, int rid, char *buffer, int maxlen); -static void add_alt_server(dnbd3_image_t *image, dnbd3_host_t *host); -static void remove_alt_server(dnbd3_trusted_server_t *server); -static void dnbd3_update_atimes(time_t now); - -// - -void *dnbd3_job_thread(void *data) -{ - int i, j; - // Determine number of available dnbd3 devices, which are needed for proxy mode - char dev[DEV_STRLEN]; - for (i = 0; i < MAX_NUM_DEVICES_TO_CHECK; ++i) - { - snprintf(dev, DEV_STRLEN, "/dev/dnbd%d", i); - if (access(dev, W_OK | R_OK)) // Need RW access to device to read and do ioctl - continue; - ++num_devices; - } - if (num_devices > 0) - { - devices = calloc(num_devices, sizeof(*devices)); - for (i = 0, j = 0; i < MAX_NUM_DEVICES_TO_CHECK; ++i) - { - memset(dev, 0, DEV_STRLEN); - snprintf(dev, DEV_STRLEN, "/dev/dnbd%d", i); - if (access(dev, W_OK | R_OK)) - continue; - if (j >= num_devices) // More available devices during second iteration? :-( - break; - memcpy(devices[j].name, dev, DEV_STRLEN); - devices[j].available = TRUE; - ++j; - } - num_devices = j; - } - memlogf("[INFO] %d available dnbd3 devices for proxy mode", num_devices); - // - time_t next_delete_invocation = 0; - // - // Job/Watchdog mainloop - while (keep_running) - { - const time_t starttime = time(NULL); - // - // Update image atime - dnbd3_update_atimes(starttime); - // Call image deletion function if last call is more than 5 minutes ago - if (starttime > next_delete_invocation) - { - next_delete_invocation = starttime + 300; - dnbd3_exec_delete(TRUE); - } - // Check for proxied images that have not been set up yet - connect_proxy_images(); - // TODO: Replicate proxied images (limited bandwidth) - // Query other servers for new images/status/... - query_servers(); - // TODO: Switch server of dnbd device based on more sophisticated inputs than just rtt - // Calc sleep timeout for next iteration - sleep(30 - (time(NULL) - starttime)); // Sleep 30 seconds, but account for the time it took to execute the loop - } - // - free(devices); - devices = NULL; - pthread_exit(NULL); - return NULL; -} - -void dnbd3_job_shutdown() -{ - keep_running = FALSE; -} - -static void connect_proxy_images() -{ - int s, n; - dnbd3_server_entry_t servers[NUMBER_SERVERS]; - char imagename[1000]; - int rid; - dnbd3_ioctl_t msg; - memset(&msg, 0, sizeof(dnbd3_ioctl_t)); - msg.len = (uint16_t)sizeof(dnbd3_ioctl_t); - msg.read_ahead_kb = DEFAULT_READ_AHEAD_KB; - msg.is_server = TRUE; - for (n = 0 ;; ++n) // Iterate over all images - { - pthread_spin_lock(&_spinlock); - dnbd3_image_t *image = g_slist_nth_data(_dnbd3_images, n); - if (image == NULL) - { // End of list reached - pthread_spin_unlock(&_spinlock); - break; - } - if (image->working && image->cache_map && image->file) - { // Image is relayed and already connected, check if cache is complete - int complete = TRUE, j; - const int map_len_bytes = IMGSIZE_TO_MAPBYTES(image->filesize); - for (j = 0; j < map_len_bytes - 1; ++j) - { - if (image->cache_map[j] != 0xFF) - { - complete = FALSE; - break; - } - } - if (complete) // Every block except the last one is complete - { // Last one might need extra treatment if it's not a full byte - 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); - complete = ((image->cache_map[map_len_bytes - 1] & last_byte) == last_byte); - } - if (!complete) // Image is not complete, finished handling it - { - pthread_spin_unlock(&_spinlock); - continue; - } - // Image is 100% cached, disconnect dnbd3 device - memlogf("[INFO] Disconnecting %s because local copy of %s is complete.", image->file, image->config_group); - int dh = open(image->file, O_RDONLY); - if (dh < 0) - memlogf("[ERROR] Could not open() device '%s'", image->file); - else - { - if (ioctl(dh, IOCTL_CLOSE, (void*)0) != 0) - memlogf("[ERROR] Could not IOCTL_CLOSE device '%s'", image->file); - else - return_free_device(image->file); - close(dh); - } - free(image->file); - image->file = NULL; - pthread_spin_unlock(&_spinlock); - continue; - } - if (image->working || image->file || image->low_name == NULL) - { // Image is a local one, nothing to do - pthread_spin_unlock(&_spinlock); - continue; - } - // Image is relayed and not connected yet - char *devname = get_free_device(); - if (devname == NULL) - { // All devices busy, can't connect - pthread_spin_unlock(&_spinlock); - continue; - } - // Remember image information as the image pointer isn't - // guaranteed to stay valid after unlocking - snprintf(imagename, 1000, "%s", image->low_name); - rid = image->rid; - memcpy(servers, image->servers, sizeof(servers[0]) * NUMBER_SERVERS); - pthread_spin_unlock(&_spinlock); - int dh = open(devname, O_RDWR); - if (dh < 0) // Open device so we can issue ioctls to it - { - pthread_spin_lock(&_spinlock); - return_free_device(devname); // Failed :-( - pthread_spin_unlock(&_spinlock); - continue; - } - for (s = 0; s < NUMBER_SERVERS; ++s) - { // Try to connect to any of the alt servers known for that image - if (servers[s].host.type == 0) - continue; - // connect device - printf("[DEBUG] Connecting device....\n"); - msg.host = servers[s].host; - msg.imgname = imagename; - msg.imgnamelen = strlen(imagename); - msg.rid = rid; - if (ioctl(dh, IOCTL_OPEN, &msg) < 0) - continue; - printf("[DEBUG] Connected! Adding alt servers...\n"); - // connected. we manually add all known alt servers to this - // device, so even if the initial server doesn't consider some - // of those as trusted servers, they are still used for failover/load balancing - for (++s; s < NUMBER_SERVERS; ++s) - { - if (servers[s].host.type == 0) - continue; - msg.host = servers[s].host; - if (ioctl(dh, IOCTL_ADD_SRV, &msg) < 0) - memlogf("[WARNING] Could not add alt server to proxy device"); - else - printf("[DEBUG] Added an alt server\n"); - } - printf("[DEBUG] Done, handling file size...\n"); - // LOCK + UPDATE - int isworking = FALSE, alloc_cache = FALSE; - pthread_spin_lock(&_spinlock); - if (g_slist_find(_dnbd3_images, image) == NULL) - { // Image not in list anymore, was deleted in meantime... - if (ioctl(dh, IOCTL_CLOSE, &msg) < 0) - memlogf("[WARNING] Could not close device after use - lost %s", devname); - else - return_free_device(devname); - pthread_spin_unlock(&_spinlock); - break; - } - // Image still exists - image->file = strdup(devname); - long long oct = 0; - int t, ret; - for (t = 0; t < 10 && dh >= 0; ++t) - { // For some reason the getsize-ioctl might return 0 right after connecting - // No idea why this happen. Maybe the IOCTL_OPEN call returns before the - // connection is fully established, but I have no idea why. - // So let's retry a couple of times if it fails. - ret = ioctl(dh, BLKGETSIZE64, &oct); - if (ret == 0 && oct > 0) - break; - close(dh); - usleep(100 * 1000); - dh = open(devname, O_RDONLY); - } - if (dh < 0 || ret != 0) - memlogf("[ERROR] SIZE fail on %s (ret=%d, oct=%lld)", devname, ret, oct); - else if (oct == 0) - memlogf("[ERROR] Reported disk size is 0."); - else if (image->filesize != 0 && image->filesize != oct) - memlogf("[ERROR] Remote and local size of image do not match: %llu != %llu for %s", (unsigned long long)oct, (unsigned long long)image->filesize, image->low_name); - else - isworking = TRUE; - image->filesize = (uint64_t)oct; - if (image->cache_file != NULL && isworking && image->cache_map == NULL) - { - printf("[DEBUG] Image has cache file %s\n", image->cache_file); - const int mapsize = IMGSIZE_TO_MAPBYTES(image->filesize); - image->cache_map = calloc(mapsize, 1); - off_t cachelen = -1; - int ch = open(image->cache_file, O_RDONLY); - if (ch >= 0) - { - cachelen = lseek(ch, 0, SEEK_END); - close(ch); - } - if (ch < 0 || cachelen != image->filesize) - alloc_cache = TRUE; - if (cachelen == image->filesize) - { - char mapfile[strlen(image->cache_file) + 5]; - sprintf(mapfile, "%s.map", image->cache_file); - int cmh = open(mapfile, O_RDONLY); - if (cmh >= 0) - { - if (lseek(cmh, 0, SEEK_END) != mapsize) - memlogf("[WARNING] Existing cache map has wrong size."); - else - { - lseek(cmh, 0, SEEK_SET); - read(cmh, image->cache_map, mapsize); - printf("[DEBUG] Found existing cache file and map for %s\n", image->low_name); - } - close(cmh); - } - } - } - char cfname[1000] = {0}; - off_t fs = image->filesize; - if (isworking && !(alloc_cache && image->cache_file)) - { - image->working = TRUE; - if (!image->cache_file) // This should be removed. proxy with no cache is completely pointless. - memlogf("[WARNING] Proxy-Mode enabled without cache directory. This will most likely hurt performance."); - goto continue_with_next_image; - } - snprintf(cfname, 1000, "%s", image->cache_file); - pthread_spin_unlock(&_spinlock); - if (isworking && *cfname) - { - int ch = open(cfname, O_WRONLY | O_CREAT, 0600); - if (ch >= 0) - { - // Pre-allocate disk space - // TODO: Check if this has a performance impact on the rest of the server (ie. client lag) - // If so, do this gracefully by incrementing size and sleeping in between. - printf("[DEBUG] Pre-allocating disk space...\n"); - lseek(ch, fs - 1, SEEK_SET); - write(ch, &ch, 1); - close(ch); - printf("[DEBUG] Allocation complete.\n"); - pthread_spin_lock(&_spinlock); - if (g_slist_find(_dnbd3_images, image) != NULL) - { - image->working = TRUE; - memlogf("[INFO] Enabled relayed image %s (%lld)", image->low_name, (long long)fs); - goto continue_with_next_image; - } - unlink(cfname); - memlogf("[WARNING] Image has gone away"); - pthread_spin_unlock(&_spinlock); - } - else - memlogf("[WARNING] Could not pre-allocate %s", cfname); - } - break; - } // <-- end of loop over servers - // If this point is reached, setting up replication was not successful, - // so lock and free the filename allocated earlier, and return the device. - pthread_spin_lock(&_spinlock); - if (g_slist_find(_dnbd3_images, image) != NULL) - { - free(image->file); - image->file = NULL; - } - return_free_device(devname); -continue_with_next_image: - pthread_spin_unlock(&_spinlock); - close(dh); - } -} - -static void dnbd3_update_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() -{ - if (_trusted_servers == NULL) - return; - int client_sock, num; - dnbd3_trusted_server_t *server; - dnbd3_host_t host; - char xmlbuffer[MAX_RPC_PAYLOAD]; - for (num = 0;; ++num) - { - // "Iterate" this way to prevent holding the lock for a long time, - // although there is a very small chance 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 - } - host = server->host; // Copy host, in case server gets deleted by another thread while processing continues here - pthread_spin_unlock(&_spinlock); - // Connect - host.port = htons(ntohs(host.port) + 1); // RPC port is client port + 1 - client_sock = sock_connect(&host, 800, 600); - if (client_sock == -1) - { - char buf[100]; - if (host_to_string(&host, buf, 100)) - printf("[DEBUG] Could not connect to trusted server %s\n", buf); - goto communication_error; - } - // - // Send and receive info from server - // Send message - dnbd3_rpc_t header; - header.cmd = htonl(RPC_IMG_LIST); - header.size = 0; - send(client_sock, (char *)&header, sizeof(header), 0); - if (!recv_data(client_sock, &header, sizeof(header))) - { - printf("[DEBUG] Could not receive IMG_LIST header from a trusted server...\n"); - goto communication_error; - } - header.cmd = ntohl(header.cmd); - header.size = ntohl(header.size); - if (header.cmd != RPC_IMG_LIST) - { - printf("[DEBUG] Error. Reply from other server was cmd:%d, error:%d\n", (int)header.cmd, (int)-1); - goto communication_error; - } - if (header.size > MAX_RPC_PAYLOAD) - { - memlogf("[WARNING] XML payload from other server exceeds MAX_RPC_PAYLOAD (%d > %d)", (int)header.size, (int)MAX_RPC_PAYLOAD); - goto communication_error; - } - if (!recv_data(client_sock, xmlbuffer, header.size)) - { - printf("[DEBUG] Error reading XML payload from other server.\n"); - goto communication_error; - } - close(client_sock); - // - // Process data, update server info, add/remove this server as alt server for images, replicate images, etc. - xmlDocPtr doc = xmlReadMemory(xmlbuffer, header.size, "noname.xml", NULL, 0); - - if (doc == NULL) - { - memlogf("[WARNING] Could not parse XML data received by other server."); - goto communication_error; - } - // Data seems ok - - xmlNodePtr cur; - FOR_EACH_NODE(doc, "/data/images/image", cur) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - NEW_POINTERLIST; - char *image = XML_GETPROP(cur, "name"); - char *ridstr = XML_GETPROP(cur, "rid"); - char *sizestr = XML_GETPROP(cur, "size"); - if (!image || !ridstr || !sizestr) - goto free_current_image; - const int rid = atoi(ridstr); - const long long size = atoll(sizestr); - if (rid <= 0) - { - printf("[DEBUG] Ignoring remote image with rid %d\n", rid); - goto free_current_image; - } - char *slash = strrchr(image, '/'); - if (slash == NULL) - { - printf("[DEBUG] Ignoring remote image with no '/' in name...\n"); - goto free_current_image; - } - else - { - *slash++ = '\0'; - if (!is_valid_namespace(image)) - { - printf("[DEBUG] Ignoring remote image with invalid namespace '%s'\n", image); - goto free_current_image; - } - if (!is_valid_imagename(slash)) - { - printf("[DEBUG] Ignoring remote image with invalid name '%s'\n", slash); - goto free_current_image; - } - snprintf(xmlbuffer, MAX_RPC_PAYLOAD, "%s/%s", image, slash); - } - // Image seems legit, check if there's a local copy - dnbd3_namespace_t *trust; - pthread_spin_lock(&_spinlock); - trust = dnbd3_get_trust_level(&host, image); - if (trust == NULL) - { // Namespace of image is not trusted - pthread_spin_unlock(&_spinlock); - printf("[DEBUG] No NS match: '%s'\n", xmlbuffer); - goto free_current_image; - } - char *sdel = XML_GETPROP(cur, "softdelete"); - char *hdel = XML_GETPROP(cur, "harddelete"); - const time_t softdelete = sdel ? atol(sdel) : 0; - const time_t harddelete = hdel ? atol(hdel) : 0; - dnbd3_image_t *local_image = dnbd3_get_image(xmlbuffer, rid, FALSE); - if (local_image == NULL && trust->auto_replicate) - { - pthread_spin_unlock(&_spinlock); - const time_t deadline = time(NULL) + 60; - if ((softdelete != 0 && softdelete < deadline) - || (harddelete != 0 && harddelete < deadline)) - { // Image is already about to be deleted, ignore it - printf("[DEBUG] Not replicating old image: '%s'\n", xmlbuffer); - goto free_current_image; - } - // Image is NEW, add it! - dnbd3_image_t newimage; - char cachefile[90]; - memset(&newimage, 0, sizeof(newimage)); - newimage.config_group = xmlbuffer; - newimage.rid = rid; - newimage.filesize = size; - newimage.delete_hard = harddelete; - newimage.delete_soft = softdelete; - if (_cache_dir) - { - newimage.cache_file = create_cache_filename(xmlbuffer, rid, cachefile, 90); - printf("[DEBUG] Cache file is %s\n", newimage.cache_file); - } - dnbd3_add_image(&newimage); - pthread_spin_lock(&_spinlock); - local_image = dnbd3_get_image(xmlbuffer, rid, FALSE); - if (local_image) - add_alt_server(local_image, &host); - pthread_spin_unlock(&_spinlock); - } - else if (local_image != NULL) - { - // Image is already KNOWN, add alt server if appropriate - const time_t deadline = time(NULL) + 60; - if (softdelete == 0 || softdelete > deadline) - { - if (local_image->filesize == 0) // Size is unknown, just assume the trusted server got it right - local_image->filesize = size; - if (size != local_image->filesize) - printf("[DEBUG] Ignoring remote image '%s' because it has a different size from the local version! (remote: %llu, local: %llu)\n", local_image->config_group, size, (unsigned long long)local_image->filesize); - else - add_alt_server(local_image, &host); - } - if (local_image->cache_file && trust->auto_replicate) { - local_image->delete_hard = harddelete; - local_image->delete_soft = softdelete; - } - pthread_spin_unlock(&_spinlock); - } - else - { - printf("[DEBUG] No NS match: '%s'\n", xmlbuffer); - pthread_spin_unlock(&_spinlock); - } - // Cleanup -free_current_image: - FREE_POINTERLIST; - } END_FOR_EACH; - - - // ... - xmlFreeDoc(doc); - // - continue; -communication_error: - if (client_sock != -1) - close(client_sock); - pthread_spin_lock(&_spinlock); - if (g_slist_find(_trusted_servers, server)) - { - if (server->unreachable < 10 && ++server->unreachable == 5) - remove_alt_server(server); - } - pthread_spin_unlock(&_spinlock); - } -} - -static char *create_cache_filename(char *name, int rid, char *buffer, int maxlen) -{ - if (_cache_dir == NULL) - return NULL; - size_t cdl = strlen(_cache_dir); - if (maxlen < 15 + cdl) - return NULL; - if (strlen(name) + 16 + cdl < maxlen) - snprintf(buffer, maxlen, "%s/%s_rid_%d.cache", _cache_dir, name, rid); - else - { - char *slash = strrchr(name, '/'); - if (slash == NULL) - { - snprintf(buffer, maxlen - 17, "%s/%s", _cache_dir, name); - snprintf(buffer + maxlen - 17, 17, "_rid_%d.cache", rid); - } - else - { - snprintf(buffer, maxlen, "%s/%s", _cache_dir, name); - snprintf(buffer + cdl, maxlen - cdl, "%s_rid_%d.cache", slash, rid); - } - } - char *ptr = buffer + cdl + 1; - while (*ptr) - { - if (*ptr == '/' || *ptr < 32 || *ptr == ' ' || *ptr == '\\' || *ptr == '*' || *ptr == '?') - *ptr = '_'; - ++ptr; - } - FILE *fh; - while ((fh = fopen(buffer, "rb"))) - { // Alter file name as long as a file by that name already exists (el cheapo edition) - fclose(fh); - char *c = buffer + rand() % strlen(buffer); - *c = rand() % 26 + 'A'; - } - return buffer; -} - -/** - * !! Call this while holding the lock !! - */ -static void add_alt_server(dnbd3_image_t *image, dnbd3_host_t *host) -{ - int i; - for (i = 0; i < NUMBER_SERVERS; ++i) - { - if (is_same_server(host, &image->servers[i].host)) - { // Alt server already known for this image - if (image->servers[i].failures) - { // It was disabled, re-enable and send info to clients - image->servers[i].failures = 0; - break; - } - else // Alt-Server already known and active, do nothing - return; - } - } - // Add to list if it wasn't in there - if (i >= NUMBER_SERVERS) - for (i = 0; i < NUMBER_SERVERS; ++i) - { - if (image->servers[i].host.type == 0) - { - image->servers[i].host = *host; - image->servers[i].failures = 0; - break; - } - } - if (i >= NUMBER_SERVERS) // To many known alt servers already - return; - // Broadcast to connected clients. Note that 'i' now points to the new server - printf("[DEBUG] Adding alt server to %s\n", image->low_name); - GSList *itc; - dnbd3_reply_t header; - header.cmd = CMD_GET_SERVERS; - header.magic = dnbd3_packet_magic; - header.size = sizeof(dnbd3_server_entry_t); - fixup_reply(header); - for (itc = _dnbd3_clients; itc; itc = itc->next) - { - dnbd3_client_t *const client = itc->data; - if (client->image == image) - { - // Don't send message directly as the lock is being held; instead, enqueue it - NEW_BINSTRING(message, sizeof(header) + sizeof(image->servers[i])); - memcpy(message->data, &header, sizeof(header)); - memcpy(message->data + sizeof(header), &image->servers[i], sizeof(image->servers[i])); - client->sendqueue = g_slist_append(client->sendqueue, message); - } - } -} - -/** - * !! Call this while holding the lock !! - */ -static void remove_alt_server(dnbd3_trusted_server_t *server) -{ - GSList *iti, *itc; - int i; - dnbd3_reply_t header; - header.cmd = CMD_GET_SERVERS; - header.magic = dnbd3_packet_magic; - header.size = sizeof(dnbd3_server_entry_t); - fixup_reply(header); - // Iterate over all images - for (iti = _dnbd3_images; iti; iti = iti->next) - { - dnbd3_image_t *const image = iti->data; - // Check if any alt_server for that image is the server to be removed - for (i = 0; i < NUMBER_SERVERS; ++i) - { - if (is_same_server(&server->host, &image->servers[i].host)) - { - // Remove server from that image and tell every connected client about it - image->servers[i].failures = 1; - for (itc = _dnbd3_clients; itc; itc = itc->next) - { - dnbd3_client_t *const client = itc->data; - if (client->image == image) - { - // Don't send message directly as the lock is being held; instead, enqueue it - NEW_BINSTRING(message, sizeof(header) + sizeof(image->servers[i])); - memcpy(message->data, &header, sizeof(header)); - memcpy(message->data + sizeof(header), &image->servers[i], sizeof(image->servers[i])); - client->sendqueue = g_slist_append(client->sendqueue, message); - } - } - image->servers[i].host.type = 0; - } - } - } -} - -/** - * 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, c; - char buffer[100]; - for (i = 0; i < num_devices; ++i) - { - if (!devices[i].available) - continue; - 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; -} - -static void return_free_device(char *name) -{ - if (devices == NULL) - return; - int i; - for (i = 0; i < num_devices; ++i) - { - if (devices[i].available || strcmp(devices[i].name, name) != 0) - continue; - devices[i].available = TRUE; - break; - } -} diff --git a/src/server/job.h b/src/server/job.h deleted file mode 100644 index 8072148..0000000 --- a/src/server/job.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef JOB_H_ -#define JOB_H_ - -void *dnbd3_job_thread(void *data); -void dnbd3_job_shutdown(); - -#endif diff --git a/src/server/net.c b/src/server/net.c index f28fc25..e967d5c 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -116,7 +116,7 @@ static inline char send_reply(int sock, dnbd3_reply_t *reply, void *payload) return 1; } -void *dnbd3_handle_query(void *dnbd3_client) +void *net_client_handler(void *dnbd3_client) { dnbd3_client_t *client = (dnbd3_client_t *) (uintptr_t) dnbd3_client; dnbd3_request_t request; diff --git a/src/server/net.h b/src/server/net.h index 9b163de..2c4115d 100644 --- a/src/server/net.h +++ b/src/server/net.h @@ -21,6 +21,6 @@ #ifndef NET_H_ #define NET_H_ -void *dnbd3_handle_query(void *client_socket); +void *net_client_handler(void *client_socket); #endif /* NET_H_ */ diff --git a/src/server/rpc.c b/src/server/rpc.c deleted file mode 100644 index e253ace..0000000 --- a/src/server/rpc.c +++ /dev/null @@ -1,810 +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 "rpc.h" -#include "../config.h" -#include "server.h" -#include "saveload.h" -#include "memlog.h" -#include "helper.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sockhelper.h" - -#include -#include -#include "xmlutil.h" - -#define RPC_PORT (PORT+1) - -#define MAX_SERVER_SOCKETS 50 // Assume there will be no more than 50 sockets the server will listen on -static int server_socks[MAX_SERVER_SOCKETS], server_count = 0; -static volatile int keep_running = 1; -static char *payload = NULL; - -#define char_repeat_br(_c, _times) do { \ - int _makro_i_ = (_times); \ - while (--_makro_i_ >= 0) putchar(_c); \ - putchar('\n'); \ -} while (0) - -static int rpc_receive(int client_sock); -static int get_highest_fd(GSList *sockets); -static int is_password_correct(xmlDocPtr doc); -static int get_terminal_width(); -static int rpc_send_reply(int sock, dnbd3_rpc_t* header, int result_code, xmlDocPtr payload); - -static int get_highest_fd(GSList *sockets) -{ - int max = 0; - - for (int i = 0; i < server_count; ++i) - { - if (server_socks[i] > max) - max = server_socks[i]; - } - - for (GSList *iterator = sockets; iterator; iterator = iterator->next) - { - const int fd = (int)(size_t)iterator->data; - if (fd > max) - max = fd; - } - //printf("Max fd: %d\n", max); - return max; -} - -void *dnbd3_rpc_mainloop() -{ - - // Check version and initialize - LIBXML_TEST_VERSION - - payload = malloc(MAX_RPC_PAYLOAD); - if (payload == NULL) - { - memlogf("[CRITICAL] Couldn't allocate RPC payload buffer. RPC disabled."); - pthread_exit((void *)0); - return NULL; - } - - struct sockaddr_storage client; - - sock_add_array(sock_listen_any(PF_INET, RPC_PORT), server_socks, &server_count, MAX_SERVER_SOCKETS); -#ifdef WITH_IPV6 - sock_add_array(sock_listen_any(PF_INET6, RPC_PORT), server_socks, &server_count, MAX_SERVER_SOCKETS); -#endif - - // Bind to socket - if (server_count == 0) - { - perror("ERROR: RPC bind/listen unsuccessful"); - exit(EXIT_FAILURE); - } - - // Run connection-accepting loop - - fd_set all_sockets, readset, exceptset; - - GSList *sockets = NULL, *iterator; - - int client_sock, ret; - int maxfd = get_highest_fd(sockets); - int error_count = 0; - - struct timeval select_timeout; - - FD_ZERO(&all_sockets); - for (int i = 0; i < server_count; ++i) - { - FD_SET(server_socks[i], &all_sockets); - sock_set_nonblock(server_socks[i]); - } - - while (keep_running) - { - readset = exceptset = all_sockets; - select_timeout.tv_sec = 4; - select_timeout.tv_usec = 0; - ret = select(maxfd, &readset, NULL, &exceptset, &select_timeout); - while (ret > 0) - { - for (int i = 0; i < server_count; ++i) - { - if (FD_ISSET(server_socks[i], &readset)) - { - --ret; - // Accept connection - socklen_t len = sizeof(client); - if ((client_sock = accept(server_socks[i], (struct sockaddr *)&client, &len)) < 0) - { - if (errno != EAGAIN) - { - memlogf("[ERROR] Error accepting an RPC connection"); - if (++error_count > 10) - goto end_loop; - } - continue; - } - error_count = 0; - // Apply read/write timeout - sock_set_timeout(client_sock, 500); - // Make new connection blocking - sock_set_block(client_sock); - sockets = g_slist_prepend(sockets, (void *)(size_t)client_sock); - if (client_sock >= maxfd) - maxfd = client_sock + 1; - //printf("Max fd: %d\n", (maxfd-1)); - FD_SET(client_sock, &all_sockets); - } - if (FD_ISSET(server_socks[i], &exceptset)) - { - --ret; - memlogf("[ERROR] An exception occurred on the RPC listening socket."); - if (++error_count > 10) - goto end_loop; - } - } - if (ret > 0) - { - // Must be an active RPC connection - int del = -1; - for (iterator = sockets; iterator; iterator = iterator->next) - { - if (del != -1) - { - // Delete a previously closed connection from list (delayed, otherwise list might get messed up) - sockets = g_slist_remove(sockets, (void *)(size_t)del); - del = -1; - maxfd = get_highest_fd(sockets) + 1; - } - client_sock = (int)(size_t)iterator->data; - if (FD_ISSET(client_sock, &readset)) - { - --ret; - // Client sending data - if (!rpc_receive(client_sock)) - { - // Connection has been closed - close(client_sock); - del = client_sock; - FD_CLR(client_sock, &all_sockets); - } - } - if (FD_ISSET(client_sock, &exceptset)) - { - --ret; - // Something unexpected happened, just close connection - close(client_sock); - del = client_sock; - FD_CLR(client_sock, &all_sockets); - } - } - if (del != -1) - { - // In case last socket was closed during iteration - sockets = g_slist_remove(sockets, (void *)(size_t)del); - maxfd = get_highest_fd(sockets) + 1; - } - } - } // End select loop - } // End mainloop - -end_loop: - memlogf("[INFO] Shutting down RPC interface."); - dnbd3_rpc_shutdown(); - - free(payload); - xmlCleanupParser(); - pthread_exit((void *)0); - return NULL; -} - -void dnbd3_rpc_shutdown() -{ - keep_running = 0; - for (int i = 0; i < server_count; ++i) - { - if (server_socks[i] == -1) - continue; - close(server_socks[i]); - server_socks[i] = -1; - } - server_count = 0; -} - -/** - * Returns !=0 if send/recv successful, 0 on any kind of network failure - */ -static int rpc_receive(int client_sock) -{ - GSList *iterator, *iterator2; - -#define STRBUFLEN 100 - char strbuffer[STRBUFLEN]; - - dnbd3_rpc_t header; - - uint32_t cmd; - - int ret, locked = 0; - int return_value = 0; - xmlDocPtr docReply = NULL, docRequest = NULL; - xmlNodePtr root_node, parent_node, tmp_node, log_parent_node, log_node, server_node; - - ret = recv(client_sock, &header, sizeof(header), MSG_WAITALL); - if (ret != sizeof(header)) - return ((ret < 0 && errno == EAGAIN) ? 1 : 0); - cmd = ntohl(header.cmd); // Leave header.cmd in network byte order for reply - header.size = ntohl(header.size); - - int rpc_error = ERROR_UNSPECIFIED_ERROR; // Default value of error, so remember to set it for the reply if call succeeded - - if (header.size != 0) - { - // Message has payload, receive it - if (header.size > MAX_RPC_PAYLOAD) - { - memlogf("[WARNING] RPC command with payload of %u bytes ignored.", (unsigned int)header.size); - return 0; - } - if (!recv_data(client_sock, payload, header.size)) - return 0; - - docRequest = xmlReadMemory(payload, header.size, "noname.xml", NULL, 0); - } - - switch (cmd) - { - case RPC_EXIT: - memlogf("[INFO] Server shutdown by RPC request"); - header.size = ntohl(0); - return_value = send_data(client_sock, &header, sizeof(header)); - dnbd3_cleanup(); - break; - - case RPC_IMG_LIST: - if (!createXmlDoc(&docReply, &root_node, "data")) - goto case_end; - - // Images - parent_node = xmlNewNode(NULL, BAD_CAST "images"); - if (parent_node == NULL) - goto case_end; - xmlAddChild(root_node, parent_node); - locked = 1; - pthread_spin_lock(&_spinlock); - for (iterator = _dnbd3_images; iterator; iterator = iterator->next) - { - const dnbd3_image_t *image = iterator->data; - tmp_node = xmlNewNode(NULL, BAD_CAST "image"); - if (tmp_node == NULL) - goto case_end; - xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->low_name); - xmlAddDecimalProp(image->rid, tmp_node, "rid"); - xmlAddDecimalProp(image->atime, tmp_node, "atime"); - xmlAddDecimalProp(image->delete_soft, tmp_node, "softdelete"); - xmlAddDecimalProp(image->delete_hard, tmp_node, "harddelete"); - xmlAddDecimalProp(image->filesize, tmp_node, "size"); - if (image->file) - xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST image->file); - if (image->cache_file && image->cache_map) - { - xmlNewProp(tmp_node, BAD_CAST "cachefile", BAD_CAST image->cache_file); - int i, complete = 0, size = IMGSIZE_TO_MAPBYTES(image->filesize); - for (i = 0; i < size; ++i) - if (image->cache_map[i]) - complete += 100; - xmlAddDecimalProp(complete / size, tmp_node, "cachefill"); - } - // Build space separated list of alt servers - int i; - char serverstr[1000] = {0}, target[100]; - for (i = 0; i < NUMBER_SERVERS; ++i) - { - if (image->servers[i].host.type == 0) continue; - if (!host_to_string(&(image->servers[i].host), target, 100)) continue; - if (*serverstr) strcat(serverstr, " "); - strcat(serverstr, target); - } - xmlNewProp(tmp_node, BAD_CAST "servers", BAD_CAST serverstr); // TODO - xmlAddChild(parent_node, tmp_node); - } - pthread_spin_unlock(&_spinlock); - locked = 0; - - // Dump and send - rpc_error = 0; - break; - - case RPC_CLIENT_LIST: - if (!createXmlDoc(&docReply, &root_node, "data")) - goto case_end; - - // Clients - parent_node = xmlNewNode(NULL, BAD_CAST "clients"); - if (parent_node == NULL) - goto case_end; - xmlAddChild(root_node, parent_node); - locked = 1; - pthread_spin_lock(&_spinlock); - for (iterator = _dnbd3_clients; iterator; iterator = iterator->next) - { - dnbd3_client_t *client = iterator->data; - if (client->image) - { - tmp_node = xmlNewNode(NULL, BAD_CAST "client"); - if (tmp_node == NULL) - goto case_end; - host_to_string(&client->host, strbuffer, STRBUFLEN); - xmlNewProp(tmp_node, BAD_CAST "address", BAD_CAST strbuffer); - xmlNewProp(tmp_node, BAD_CAST "image", BAD_CAST client->image->low_name); - xmlAddDecimalProp(client->image->rid, tmp_node, "rid"); - xmlAddChild(parent_node, tmp_node); - } - } - pthread_spin_unlock(&_spinlock); - locked = 0; - - // Dump and send - rpc_error = 0; - break; - - case RPC_TRUSTED_LIST: - if (!createXmlDoc(&docReply, &root_node, "data")) - goto case_end; - - // Trusted servers - parent_node = xmlNewNode(NULL, BAD_CAST "trusted"); - if (parent_node == NULL) - goto case_end; - xmlAddChild(root_node, parent_node); - locked = 1; - pthread_spin_lock(&_spinlock); - for (iterator = _trusted_servers; iterator; iterator = iterator->next) - { - dnbd3_trusted_server_t *server = iterator->data; - if (server->host.type != 0) - { - tmp_node = xmlNewNode(NULL, BAD_CAST "server"); - if (tmp_node == NULL) - goto case_end; - xmlNodePtr namespace_root = xmlNewNode(NULL, BAD_CAST "namespaces"); - if (namespace_root == NULL) - goto case_end; - host_to_string(&server->host, strbuffer, STRBUFLEN); - xmlNewProp(tmp_node, BAD_CAST "address", BAD_CAST strbuffer); - if (server->comment) - xmlNewProp(tmp_node, BAD_CAST "comment", BAD_CAST server->comment); - for (iterator2 = server->namespaces; iterator2; iterator2 = iterator2->next) - { - const dnbd3_namespace_t *ns = iterator2->data; - server_node = xmlNewNode(NULL, BAD_CAST "namespace"); - if (server_node == NULL) - goto case_end; - xmlAddChild(namespace_root, server_node); - xmlNewProp(server_node, BAD_CAST "name", BAD_CAST ns->name); - if (ns->auto_replicate) - xmlNewProp(server_node, BAD_CAST "replicate", BAD_CAST "1"); - if (ns->recursive) - xmlNewProp(server_node, BAD_CAST "recursive", BAD_CAST "1"); - } - xmlAddChild(parent_node, tmp_node); - xmlAddChild(tmp_node, namespace_root); - } - } - pthread_spin_unlock(&_spinlock); - locked = 0; - - // Dump and send - rpc_error = 0; - break; - - case RPC_GET_LOG: - if (!createXmlDoc(&docReply, &root_node, "data")) - goto case_end; - - // Log - log_parent_node = xmlNewChild(root_node, NULL, BAD_CAST "log", NULL); - if (log_parent_node == NULL) - goto case_end; - char *log = fetchlog(0); - if (log == NULL) - log = strdup("LOG IS NULL"); - log_node = xmlNewCDataBlock(docReply, BAD_CAST log, strlen(log)); - free(log); - if (log_node == NULL) - goto case_end; - xmlAddChild(log_parent_node, log_node); - - // Dump and send - rpc_error = 0; - break; - - case RPC_ADD_IMG: - case RPC_DEL_IMG: - if (docRequest) - { - if (!is_password_correct(docRequest)) - { - rpc_error = ERROR_WRONG_PASSWORD; - break; - } - - xmlNodePtr cur = NULL; - int count = 0; - - FOR_EACH_NODE(docRequest, "/data/image", cur) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - NEW_POINTERLIST; - ++count; - dnbd3_image_t image; - memset(&image, 0, sizeof(dnbd3_image_t)); - image.config_group = XML_GETPROP(cur, "name"); - char *rid_str = XML_GETPROP(cur, "rid"); - image.file = XML_GETPROP(cur, "file"); - image.cache_file = XML_GETPROP(cur, "cache"); - if (image.file && !file_exists(image.file)) - { - printf("Image File: %s\n", image.file); - rpc_error = ERROR_FILE_NOT_FOUND; - } - else if (image.cache_file && !file_writable(image.cache_file)) - { - rpc_error = ERROR_NOT_WRITABLE; - } - else - { - if (image.config_group && rid_str) - { - image.rid = atoi(rid_str); - if (cmd == RPC_ADD_IMG) - { - rpc_error = dnbd3_add_image(&image); - } - else - { - char *soft = XML_GETPROP(cur, "softdelete"); - char *hard = XML_GETPROP(cur, "harddelete"); - image.delete_soft = time(NULL); - image.delete_hard = time(NULL); - if (soft) image.delete_soft += atoi(soft); - if (hard) image.delete_hard += atoi(hard); - rpc_error = dnbd3_del_image(&image); - } - } - else - rpc_error = ERROR_MISSING_ARGUMENT; - } - FREE_POINTERLIST; - } END_FOR_EACH; - if (count == 0) - rpc_error = ERROR_MISSING_ARGUMENT; - } - else - rpc_error = ERROR_INVALID_XML; - - break; - - case RPC_ADD_NS: - case RPC_DEL_NS: - if (docRequest) - { - if (!is_password_correct(docRequest)) - { - rpc_error = ERROR_WRONG_PASSWORD; - break; - } - - xmlNodePtr cur = NULL; - - FOR_EACH_NODE(docRequest, "/data/namespaces/namespace", cur) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - NEW_POINTERLIST; - char *host = XML_GETPROP(cur, "address"); - char *ns = XML_GETPROP(cur, "name"); - char *flags = XML_GETPROP(cur, "flags"); - char *comment = XML_GETPROP(cur, "comment"); - pthread_spin_lock(&_spinlock); - if (host && ns) - { - if (cmd == RPC_ADD_NS) - { - dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(host, TRUE, comment); - if (server && dnbd3_add_trusted_namespace(server, ns, flags)) - rpc_error = ERROR_OK; - else - rpc_error = ERROR_UNSPECIFIED_ERROR; - } - else - { - dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(host, FALSE, comment); - if (server && dnbd3_del_trusted_namespace(server, ns)) - rpc_error = ERROR_OK; - else - rpc_error = ERROR_FILE_NOT_FOUND; - } - } - pthread_spin_unlock(&_spinlock); - FREE_POINTERLIST; - } END_FOR_EACH; - if (rpc_error == ERROR_OK) dnbd3_save_config(); - } - else - rpc_error = ERROR_INVALID_XML; - - break; - - default: - memlogf("[ERROR] Unknown RPC command: %u", (unsigned int)header.cmd); - rpc_error = ERROR_UNKNOWN_COMMAND; - break; - - } -case_end: - - if (locked) - pthread_spin_unlock(&_spinlock); - // Send reply - return_value = rpc_send_reply(client_sock, &header, rpc_error, docReply); - - xmlFreeDoc(docReply); - xmlFreeDoc(docRequest); - - return return_value; -} - -void dnbd3_rpc_send(int cmd) -{ - int client_sock, size; - - // Check version and initialize - LIBXML_TEST_VERSION - - struct sockaddr_in server; - - memset(&server, 0, sizeof(server)); - server.sin_family = AF_INET; // IPv4 - server.sin_addr.s_addr = inet_addr("127.0.0.1"); - server.sin_port = htons(RPC_PORT); // set port number - - // Connect to server - if ((client_sock = sock_connect4(&server, 2000, 1000)) == -1) - { - perror("ERROR: RPC connect"); - exit(EXIT_FAILURE); - } - - // Send message - dnbd3_rpc_t header; - header.cmd = htonl(cmd); - header.size = 0; - send(client_sock, (char *)&header, sizeof(header), MSG_WAITALL); - recv(client_sock, &header, sizeof(header), MSG_WAITALL); - header.cmd = ntohl(header.cmd); - header.size = ntohl(header.size); - - if (cmd == RPC_IMG_LIST && header.size > 0) - { - char *buf = malloc(header.size + 1); - size = recv(client_sock, buf, header.size, MSG_WAITALL); - printf("\n%s\n\n", buf); - xmlDocPtr doc = xmlReadMemory(buf, size, "noname.xml", NULL, 0); - buf[header.size] = 0; - - if (doc) - { - int count; - int term_width = get_terminal_width(); - xmlNodePtr cur, childit; - - // Print log - char *log = getTextFromPath(doc, "/data/log"); - if (log) - { - printf("--- Last log lines ----\n%s\n\n", log); - xmlFree(log); - } - - int watime = 17, wname = 0, wrid = 5; - FOR_EACH_NODE(doc, "/data/images/image", cur) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - NEW_POINTERLIST; // This macro defines an array of pointers - char *vid = XML_GETPROP(cur, "name"); // XML_GETPROP is a macro wrapping xmlGetNoNsProp() - char *rid = XML_GETPROP(cur, "rid"); // Each of these calls allocates memory for the string - wname = MAX(wname, strlen(vid)); - wrid = MAX(wrid, strlen(rid)); - FREE_POINTERLIST; // This macro simply frees all pointers in the above array - } END_FOR_EACH; - - char format[100], strbuffer[STRBUFLEN]; - snprintf(format, 100, - "%%-%ds %%-%ds %%%ds %%s\n", watime, wname, wrid); - - // Print images - printf("Exported images\n"); - printf(format, "atime", "name", "rid", "file"); - char_repeat_br('=', term_width); - count = 0; - FOR_EACH_NODE(doc, "/data/images/image", cur) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - NEW_POINTERLIST; - ++count; - char *numatime = XML_GETPROP(cur, "atime"); - char *vid = XML_GETPROP(cur, "name"); - char *rid = XML_GETPROP(cur, "rid"); - char *file = XML_GETPROP(cur, "file"); - time_t at = (time_t)atol(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); - - // Print clients - printf("Connected clients (ip, file):\n"); - char_repeat_br('=', term_width); - count = 0; - FOR_EACH_NODE(doc, "/data/clients/client", cur) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - ++count; - xmlChar *ip = xmlGetNoNsProp(cur, BAD_CAST "ip"); - 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; - char_repeat_br('=', term_width); - printf("\nNumber clients: %d\n\n", count); - - // Print trusted servers - printf("Trusted servers:\n"); - char_repeat_br('=', term_width); - count = 0; - FOR_EACH_NODE(doc, "/data/trusted/server", cur) - { - if (cur->type != XML_ELEMENT_NODE) - continue; - NEW_POINTERLIST; - ++count; - char *address = XML_GETPROP(cur, "address"); - char *comment = XML_GETPROP(cur, "comment"); - if (comment) - printf("%-30s (%s)\n", address, comment); - else - printf("%-30s\n", address); - for (childit = cur->children; childit; childit = childit->next) - { - if (childit->type != XML_ELEMENT_NODE || childit->name == NULL || strcmp((const char*)childit->name, "namespace") != 0) - continue; - NEW_POINTERLIST; - char *name = XML_GETPROP(childit, "name"); - char *replicate = XML_GETPROP(childit, "replicate"); - char *recursive = XML_GETPROP(childit, "recursive"); - printf(" %-40s ", name); - if (replicate && *replicate != '0') - printf(" replicate"); - if (recursive && *recursive != '0') - printf(" recursive"); - putchar('\n'); - FREE_POINTERLIST; - } - FREE_POINTERLIST; - } END_FOR_EACH; - char_repeat_br('=', term_width); - printf("\nNumber servers: %d\n\n", count); - - // Cleanup - xmlFreeDoc(doc); - xmlCleanupParser(); - -// xmlDocDump(stdout, doc); - - } - else - { - printf("ERROR: Failed to parse reply\n-----------\n%s\n-------------\n", buf); - } - - } - - close(client_sock); -} - -/** - * Check if the correct server password is present in xpath /data/password - * return !=0 if correct, 0 otherwise - */ -static int is_password_correct(xmlDocPtr doc) -{ - if (_rpc_password == NULL) - { - memlogf("[WARNING] RPC access granted as no password is set!"); - return 1; - } - char *pass = getTextFromPath(doc, "/data/password"); - if (pass == NULL) - return 0; - if (strcmp(pass, _rpc_password) == 0) - { - xmlFree(pass); - return 1; - } - xmlFree(pass); - return 0; -} - -static int get_terminal_width() -{ - struct winsize w; - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) - return 80; - return w.ws_col; -} - -#define RETBUFLEN 8000 -static char returnbuffer[RETBUFLEN]; -static int rpc_send_reply(int sock, dnbd3_rpc_t* header, int result_code, xmlDocPtr payload) -{ - if (result_code == 0 && payload != NULL) - { - // No error - xmlChar *xmlbuff = NULL; - int buffersize; - xmlDocDumpFormatMemory(payload, &xmlbuff, &buffersize, 1); - header->size = htonl(buffersize); - if (!send_data(sock, header, sizeof(*header))) - return FALSE; - if (xmlbuff) - return send_data(sock, xmlbuff, buffersize); - return TRUE; - } - // Error code, build xml struct (lazy shortcut) - int len = snprintf(returnbuffer, RETBUFLEN, "\n" - "\n" - "\n" - "", result_code, "TODO"); - if (len >= RETBUFLEN) - len = 10; - header->size = htonl(len); - header->cmd = htonl(RPC_ERROR); - if (!send_data(sock, header, sizeof(*header))) - return FALSE; - return send_data(sock, returnbuffer, len); -} diff --git a/src/server/rpc.h b/src/server/rpc.h deleted file mode 100644 index a7c9f39..0000000 --- a/src/server/rpc.h +++ /dev/null @@ -1,55 +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. - * - */ - -#ifndef RPC_H_ -#define RPC_H_ - -#include - -#define RPC_EXIT 0 -#define RPC_RELOAD 1 -#define RPC_IMG_LIST 2 -#define RPC_ADD_IMG 3 -#define RPC_DEL_IMG 4 -#define RPC_ADD_NS 5 -#define RPC_DEL_NS 6 -#define RPC_CLIENT_LIST 7 -#define RPC_TRUSTED_LIST 8 -#define RPC_GET_LOG 9 -#define RPC_FIX_IMAGE 10 -#define RPC_ERROR 11 - -void *dnbd3_rpc_mainloop(); - -void dnbd3_rpc_shutdown(); - -void dnbd3_rpc_send(int cmd); - - -#pragma pack(1) -typedef struct -{ - uint32_t handle;// 4byte - uint32_t cmd; // 4byte - uint32_t size; // 4byte -} dnbd3_rpc_t; -#pragma pack(0) - -#endif /* RPC_H_ */ diff --git a/src/server/saveload.c b/src/server/saveload.c deleted file mode 100644 index e888f50..0000000 --- a/src/server/saveload.c +++ /dev/null @@ -1,734 +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 "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); - - -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); - } - - srand(time(NULL)); - - _rpc_password = g_key_file_get_string(_config_handle, "settings", "password", NULL); - _cache_dir = g_key_file_get_string(_config_handle, "settings", "cache_dir", NULL); - - if (_cache_dir == NULL) - memlogf("[WARNING] No cache dir set! Automatic replication will not work."); - else if (access(_cache_dir, R_OK | W_OK) != 0) - memlogf("[WARNING] Cache dir '%s' is not readable or writable", _cache_dir); - - 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) -{ - if (image->file) - { - if (strncmp(image->file, "/dev/dnbd", 9) == 0) - return ERROR_IMAGE_NOT_FOUND; - int fh = open(image->file, O_RDONLY); - if (fh < 0) - return ERROR_IMAGE_NOT_FOUND; - close(fh); - } - // Lock here to prevent concurrent add calls to mess rids up. Cannot happen currently - // as RPC 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) - { - if (image->file == NULL && newimage->filesize == 0) - newimage->filesize = image->filesize; - _dnbd3_images = g_slist_prepend(_dnbd3_images, newimage); - } - else - { - pthread_spin_unlock(&_spinlock); - return ERROR_SEE_LOG; - } - image = NULL; - - // Adding image was successful, write config file - g_key_file_set_integer(_config_handle, newimage->config_group, "rid", newimage->rid); - if (newimage->file) - g_key_file_set_string(_config_handle, newimage->config_group, "file", newimage->file); - if (newimage->cache_file) - g_key_file_set_string(_config_handle, newimage->config_group, "cache", newimage->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 = image->delete_soft; - existing_image->delete_hard = image->delete_hard; - 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 - char name[strlen(name_orig) + 1]; - 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; - } - - char *slash = strrchr(image_name, '/'); - - if (slash == NULL) - { - memlogf("[ERROR] Invalid image name: '%s'", image_name); - return NULL; - } - else - { - *slash = '\0'; - if (!is_valid_imagename(slash+1)) - { - memlogf("[ERROR] Invalid image name: '%s'", slash+1); - return NULL; - } - if (!is_valid_namespace(image_name)) - { - memlogf("[ERROR] Invalid namespace: '%s'", image_name); - *slash = '/'; - return NULL; - } - *slash = '/'; - } - - // 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; - } - - image->low_name = strdup(image_name); - - strtolower(image->low_name); - memlogf("[INFO] Loading image '%s'", image->low_name); - - if (dnbd3_get_image(image->low_name, rid, 0)) - { - memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image_name, rid); - goto error; - } - - image->config_group = strdup(image_name); - - image->rid = rid; - image->relayed = (image_file == NULL || image_file == '\0'); - - if (image_file && strncmp(image_file, "/dev/dnbd", 9) == 0) - { - printf("[BUG BUG BUG] Image file is %s\n", image_file); - image->relayed = TRUE; - } - - 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) - { - printf("[DEBUG] Size known %llu for %s\n", (unsigned long long)image->filesize, image->cache_file); - 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); - } - } - } // 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]; - printf("[DEBUG] Scanning for deletable images\n"); - - 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) - { - printf("[DEBUG] HARD %s\n", image->low_name); - // 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) - { - printf("[DEBUG] SOFT %s\n", image->low_name); - // 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; - if (image_iterator == NULL) - break; - } - } // END image iteration - pthread_spin_unlock(&_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; - } -#ifndef WITH_IPV6 - if (server.host.type == AF_INET6) - { - printf("[DEBUG] Ignoring IPv6 trusted server.\n"); - return NULL; - } -#endif - GSList *iterator; - for (iterator = _trusted_servers; iterator; iterator = iterator->next) - { - dnbd3_trusted_server_t *comp = iterator->data; - if (is_same_server(&comp->host, &server.host)) - 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); - dnbd3_trusted_server_t *copy = malloc(sizeof(server)); - memcpy(copy, &server, sizeof(*copy)); - copy->comment = strdup(groupname+6); - _trusted_servers = g_slist_prepend(_trusted_servers, copy); - free(groupname); - 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); - remove_trailing_slash(nslow); - 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")); - size_t len = strlen(server->comment) + 7; - char groupname[len]; - snprintf(groupname, len, "trust:%s", server->comment); - if (ns->auto_replicate && ns->recursive) - g_key_file_set_string(_config_handle, groupname, ns->name, "replicate,recursive"); - else if (ns->auto_replicate) - g_key_file_set_string(_config_handle, groupname, ns->name, "replicate"); - else if (ns->recursive) - g_key_file_set_string(_config_handle, groupname, ns->name, "recursive"); - else - g_key_file_set_string(_config_handle, groupname, ns->name, "-"); - return TRUE; -} - -/** - * Remove trusted namespace from given trusted server. - * !! Lock before calling this function !! - * @return TRUE if element could be removed - * !! Assume that the _trusted_servers list changed after calling this, - * since the trusted server gets removed from it if this was the last namespace - * it contained - */ -int dnbd3_del_trusted_namespace(dnbd3_trusted_server_t *server, char *namespace) -{ - int nslen = strlen(namespace) + 1; - char nslow[nslen]; - memcpy(nslow, namespace, nslen); - remove_trailing_slash(nslow); - strtolower(nslow); - GSList *iterator; - for (iterator = server->namespaces; iterator; iterator = iterator->next) - { - dnbd3_namespace_t *cmp = iterator->data; - if (strcmp(nslow, cmp->name) == 0) - { - // TODO: Remove from config file - free(cmp->name); - free(cmp); - server->namespaces = g_slist_remove(server->namespaces, cmp); - if (server->namespaces == NULL) - { - g_free(server->comment); - _trusted_servers = g_slist_remove(_trusted_servers, server); - free(server); - } - return TRUE; - } - } - return FALSE; -} - -/** - * Gives the closest match of a namespace rule that can be applied to - * the given namespace - * Returns NULL if none - * !! Lock before calling this function !! - */ -dnbd3_namespace_t *dnbd3_get_trust_level(dnbd3_host_t *host, char *namespace) -{ - dnbd3_trusted_server_t *server = NULL; - GSList *iterator; - for (iterator = _trusted_servers; iterator; iterator = iterator->next) - { - dnbd3_trusted_server_t *comp = iterator->data; - if (!is_same_server(host, &comp->host)) - continue; - server = comp; - break; - } - if (server == NULL) - return NULL; - dnbd3_namespace_t *best = NULL; - int bestlen = 0; - char nslow[strlen(namespace)+1]; - strcpy(nslow, namespace); - remove_trailing_slash(nslow); - strtolower(nslow); - for (iterator = server->namespaces; iterator; iterator = iterator-> next) - { - dnbd3_namespace_t *comp = iterator->data; - const int cmplen = strlen(comp->name); - if (strncmp(nslow, comp->name, cmplen) != 0) // names do not match at all - continue; - if (nslow[cmplen] == '/' && !comp->recursive) // partial match, but recursion is disabled - continue; - if (nslow[cmplen] != '\0' && nslow[cmplen] != '/') // in mid-string - continue; - if (cmplen < bestlen) // Match is not better than one found before - continue; - bestlen = cmplen; - best = comp; - } - return best; -} diff --git a/src/server/saveload.h b/src/server/saveload.h deleted file mode 100644 index 77528e4..0000000 --- a/src/server/saveload.h +++ /dev/null @@ -1,63 +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 -#define ERROR_NOT_WRITABLE 13 - -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); -int dnbd3_del_trusted_namespace(dnbd3_trusted_server_t *server, char *namespace); -dnbd3_namespace_t *dnbd3_get_trust_level(dnbd3_host_t *host, char *namespace); - -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 03e8564..b2e5ba7 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -33,10 +33,7 @@ #include "sockhelper.h" #include "server.h" -#include "saveload.h" -#include "job.h" #include "net.h" -#include "rpc.h" #include "memlog.h" #define MAX_SERVER_SOCKETS 50 // Assume there will be no more than 50 sockets the server will listen on @@ -44,14 +41,23 @@ static int sockets[MAX_SERVER_SOCKETS], socket_count = 0; #ifdef _DEBUG int _fake_delay = 0; #endif -pthread_spinlock_t _spinlock; -GSList *_dnbd3_clients = NULL; +dnbd3_client_t *_clients[SERVER_MAX_CLIENTS]; +int _num_clients = 0; +pthread_spinlock_t _clients_lock; + +dnbd3_image_t *_images[SERVER_MAX_IMAGES]; +int _num_images = 0; +pthread_spinlock_t _images_lock; + +dnbd3_alt_server_t *_alt_servers[SERVER_MAX_ALTS]; +int _num_alts = 0; +pthread_spinlock_t _alts_lock; + char *_config_file_name = DEFAULT_SERVER_CONFIG_FILE; char *_rpc_password = NULL; char *_cache_dir = NULL; -GSList *_dnbd3_images = NULL; // of dnbd3_image_t -GSList *_trusted_servers = NULL; + void dnbd3_print_help(char *argv_0) { @@ -78,7 +84,9 @@ void dnbd3_print_version() void dnbd3_cleanup() { - int fd; + int fd, i; + GSList *iterator = NULL; + memlogf("INFO: Cleanup...\n"); for (int i = 0; i < socket_count; ++i) @@ -90,25 +98,25 @@ void dnbd3_cleanup() } socket_count = 0; - dnbd3_rpc_shutdown(); - dnbd3_job_shutdown(); - - pthread_spin_lock(&_spinlock); - GSList *iterator = NULL; - for (iterator = _dnbd3_clients; iterator; iterator = iterator->next) + pthread_spin_lock(&_clients_lock); + for (i = 0; i < _num_clients; ++i) { - dnbd3_client_t *client = iterator->data; - shutdown(client->sock, SHUT_RDWR); - pthread_join(client->thread, NULL); - g_free(client); + dnbd3_client_t * const client = _clients[i]; + pthread_spin_lock(&client->lock); + if (client->sock != -1) shutdown(client->sock, SHUT_RDWR); + if (client->thread != 0) pthread_join(client->thread, NULL); + _clients[i] = NULL; + pthread_spin_unlock(&client->lock); + free(client); } - g_slist_free(_dnbd3_clients); - + _num_clients = 0; + pthread_spin_unlock(&_clients_lock); - for (iterator = _dnbd3_images; iterator; iterator = iterator->next) + pthread_spin_lock(&_images_lock); + for (i = 0; i < _num_images; ++i) { // save cache maps to files - dnbd3_image_t *image = iterator->data; + dnbd3_image_t *image = _images[i]; if (image->cache_file) { char tmp[strlen(image->cache_file)+4]; @@ -139,9 +147,8 @@ void dnbd3_cleanup() free(image->cache_file); g_free(image); } - g_slist_free(_dnbd3_images); + pthread_spin_unlock(&_images_lock); - pthread_spin_unlock(&_spinlock); exit(EXIT_SUCCESS); } @@ -185,15 +192,15 @@ int main(int argc, char *argv[]) break; case 'r': printf("INFO: Reloading configuration file...\n\n"); - dnbd3_rpc_send(RPC_RELOAD); + //dnbd3_rpc_send(RPC_RELOAD); return EXIT_SUCCESS; case 's': printf("INFO: Stopping running server...\n\n"); - dnbd3_rpc_send(RPC_EXIT); + //dnbd3_rpc_send(RPC_EXIT); return EXIT_SUCCESS; case 'i': printf("INFO: Requesting information...\n\n"); - dnbd3_rpc_send(RPC_IMG_LIST); + //dnbd3_rpc_send(RPC_IMG_LIST); return EXIT_SUCCESS; case 'H': dnbd3_print_help(argv[0]); @@ -298,7 +305,7 @@ int main(int argc, char *argv[]) _dnbd3_clients = g_slist_prepend(_dnbd3_clients, dnbd3_client); pthread_spin_unlock(&_spinlock); - if (0 != pthread_create(&(dnbd3_client->thread), NULL, dnbd3_handle_query, (void *) (uintptr_t) dnbd3_client)) + if (0 != pthread_create(&(dnbd3_client->thread), NULL, net_client_handler, (void *) (uintptr_t) dnbd3_client)) { memlogf("[ERROR] Could not start thread for new client."); pthread_spin_lock(&_spinlock); diff --git a/src/server/server.h b/src/server/server.h index 47aa0c0..74ca1a1 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -28,22 +28,30 @@ #include "../config.h" #include "../types.h" +typedef struct +{ + int fd; +} dnbd3_connection_t; +/** + * Image struct. An image path could be something like + * /mnt/images/rz/zfs/Windows7 ZfS.vmdk.1 + * and the lower_name would then be + * rz/zfs/windows7 zfs.vmdk + */ 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 + char *path; // absolute path of the image + char *lower_name; // relative path, all lowercase, minus revision ID + int rid; // revision of image 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 + dnbd3_connection_t *uplink; // NULL = local image / completely cached, pointer to a server connection otherwise + char working; // TRUE if image exists and completeness is == 100% or a working upstream proxy is connected 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) + pthread_spinlock_t lock; } dnbd3_image_t; typedef struct @@ -51,7 +59,8 @@ typedef struct uint16_t len; uint8_t data[65535]; } dnbd3_binstring_t; -// Do not always allocate as much memory as required to hold the entire binstring struct, but only as much as is required to hold the actual data +// Do not always allocate as much memory as required to hold the entire binstring struct, +// but only as much as is required to hold the actual data #define NEW_BINSTRING(_name, _len) \ dnbd3_binstring_t *_name = malloc(sizeof(uint16_t) + _len); \ _name->len = _len @@ -63,30 +72,37 @@ typedef struct uint8_t is_server; // TRUE if a server in proxy mode, FALSE if real client pthread_t thread; dnbd3_image_t *image; + pthread_spinlock_t lock; GSList *sendqueue; // list of dnbd3_binstring_t* } dnbd3_client_t; typedef struct { - gchar *comment; - GSList *namespaces; // List of dnbd3_namespace_t - dnbd3_host_t host; - uint8_t unreachable; -} dnbd3_trusted_server_t; + time_t last_told; + dnbd3_host_t host; + char comment[COMMENT_LENGTH]; +} dnbd3_alt_server_t; typedef struct { - char *name; - uint8_t auto_replicate; - uint8_t recursive; -} dnbd3_namespace_t; + char comment[COMMENT_LENGTH]; + dnbd3_host_t host; + dnbd3_host_t mask; +} dnbd3_acess_rules_t; -extern GSList *_dnbd3_clients; // of dnbd3_client_t -extern pthread_spinlock_t _spinlock; -extern char *_config_file_name, *_rpc_password, *_cache_dir; -extern GSList *_dnbd3_images; // of dnbd3_image_t -extern GSList *_trusted_servers; +extern dnbd3_client_t *_clients[SERVER_MAX_CLIENTS]; +extern int _num_clients; +extern pthread_spinlock_t _clients_lock; + +extern dnbd3_image_t *_images[SERVER_MAX_IMAGES]; +extern int _num_images; +extern pthread_spinlock_t _images_lock; +extern dnbd3_alt_server_t *_alt_servers[SERVER_MAX_ALTS]; +extern int _num_alts; +extern pthread_spinlock_t _alts_lock; + +extern char *_config_file_name, *_rpc_password, *_cache_dir; #ifdef _DEBUG extern int _fake_delay; diff --git a/src/server/sockhelper.h b/src/server/sockhelper.h index 2cbcda1..421b8b5 100644 --- a/src/server/sockhelper.h +++ b/src/server/sockhelper.h @@ -52,6 +52,18 @@ void sock_set_nonblock(int sock); void sock_set_block(int sock); +/** + * Take IPv4 as string and a port and fill sockaddr_in struct. + * This should be refactored to work for IPv4 and IPv6 and use sockaddr_storage. + */ +inline void sock_set_addr4(char *ip, uint16_t port, struct sockaddr_in *addr) +{ + memset(addr, 0, sizeof(*addr)); + addr->sin_family = AF_INET; // IPv4 + addr->sin_addr.s_addr = inet_addr(ip); + addr->sin_port = htons(port); // set port number +} + /** * Add given socket to array. Take an existing empty slot ( == -1) if available, * append to end otherwise. Updates socket count variable passed by reference. diff --git a/src/server/xmlutil.c b/src/server/xmlutil.c deleted file mode 100644 index d1163a7..0000000 --- a/src/server/xmlutil.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "xmlutil.h" -#include -#include - - -char *getTextFromPath(xmlDocPtr doc, char *xpath) -{ - xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); - if (xpathCtx == NULL) - return NULL; - xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(BAD_CAST xpath, xpathCtx); - if (xpathObj == NULL) - { - xmlXPathFreeContext(xpathCtx); - return NULL; - } - char *retval = NULL; - if (xpathObj->stringval) - retval = (char*)xmlStrdup(xpathObj->stringval); - else if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 && xpathObj->nodesetval->nodeTab && xpathObj->nodesetval->nodeTab[0]) - { - retval = (char*)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]); - } - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - return retval; -} - - -char createXmlDoc(xmlDocPtr *doc, xmlNodePtr* root, char* rootName) -{ - *doc = xmlNewDoc(BAD_CAST "1.0"); - if (*doc == NULL) - return 0; - *root = xmlNewNode(NULL, BAD_CAST rootName); - if (*root == NULL) - { - xmlFreeDoc(*doc); - return 0; - } - xmlDocSetRootElement(*doc, *root); - return 1; -} diff --git a/src/server/xmlutil.h b/src/server/xmlutil.h deleted file mode 100644 index 4da8616..0000000 --- a/src/server/xmlutil.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef XMLUTIL_H_ -#define XMLUTIL_H_ - -#include -#include -#include - -char *getTextFromPath(xmlDocPtr doc, char *xpath); -char createXmlDoc(xmlDocPtr *doc, xmlNodePtr* root, char* rootName); - -// Two macros to iterate over a node list - -#define FOR_EACH_NODE(_doc, _path, _node) do { \ - xmlXPathContextPtr _makro_xpathCtx = xmlXPathNewContext(_doc); \ - if (_makro_xpathCtx) { \ - xmlXPathObjectPtr _makro_xpathObj = xmlXPathEvalExpression(BAD_CAST _path, _makro_xpathCtx); \ - if (_makro_xpathObj) { \ - int _makro_i_; \ - if (_makro_xpathObj->nodesetval) for (_makro_i_ = 0; _makro_i_ < _makro_xpathObj->nodesetval->nodeNr; ++_makro_i_) \ - { _node = _makro_xpathObj->nodesetval->nodeTab[_makro_i_]; - -#define END_FOR_EACH \ - } } \ - xmlXPathFreeObject(_makro_xpathObj); \ - } \ - xmlXPathFreeContext(_makro_xpathCtx); \ - } while(0) - -// Two macros to deal with temporary pointers - -#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) - -// Macro to get a node property with automatic pointer handling (see two macros above) - -#define XML_GETPROP(_node, _name) (char*)(_makro_ptrlist[(_makro_usedcount >= NUM_POINTERS_IN_LIST ? 0 : _makro_usedcount++)] = xmlGetNoNsProp(_node, BAD_CAST _name)) - -// Inline function to print a numeric value to a char buffer and add as property - -static inline void xmlAddDecimalProp(int64_t value, xmlNodePtr node, char* name) -{ - char strbuffer[100]; - sprintf(strbuffer, "%" PRId64, value); - xmlNewProp(node, BAD_CAST name, BAD_CAST strbuffer); -} - -#endif -- cgit v1.2.3-55-g7522