diff options
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/ipc.c | 240 | ||||
-rw-r--r-- | src/server/net.c | 21 | ||||
-rw-r--r-- | src/server/server.c | 31 | ||||
-rw-r--r-- | src/server/server.h | 8 | ||||
-rw-r--r-- | src/server/utils.c | 312 | ||||
-rw-r--r-- | src/server/utils.h | 4 | ||||
-rw-r--r-- | src/server/xmlutil.c | 27 | ||||
-rw-r--r-- | src/server/xmlutil.h | 26 |
8 files changed, 453 insertions, 216 deletions
diff --git a/src/server/ipc.c b/src/server/ipc.c index 38066c8..ebc907b 100644 --- a/src/server/ipc.c +++ b/src/server/ipc.c @@ -25,6 +25,7 @@ #include <sys/stat.h> #include <grp.h> #include <stdlib.h> +#include <sys/ioctl.h> #include <unistd.h> #include <pthread.h> #include <netinet/in.h> @@ -34,6 +35,7 @@ #include <libxml/parser.h> #include <libxml/xpath.h> +#include "xmlutil.h" #include "ipc.h" #include "../config.h" @@ -41,14 +43,24 @@ #include "utils.h" #include "memlog.h" +#define IPC_PORT (PORT+1) + static int server_sock = -1; 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 ipc_receive(int client_sock); static int get_highest_fd(GSList *sockets); static int send_reply(int client_sock, void *data_in, int len); static int recv_data(int client_sock, void *buffer_out, int len); +static int is_password_correct(xmlDocPtr doc); +static int get_terminal_width(); static int get_highest_fd(GSList *sockets) { @@ -67,7 +79,11 @@ static int get_highest_fd(GSList *sockets) void *dnbd3_ipc_mainloop() { - payload = malloc(MAX_PAYLOAD); + + // Check version and initialize + LIBXML_TEST_VERSION + + payload = malloc(MAX_IPC_PAYLOAD); if (payload == NULL) { memlogf("[CRITICAL] Couldn't allocate IPC payload buffer. IPC disabled."); @@ -173,8 +189,6 @@ void *dnbd3_ipc_mainloop() flags = 0; fcntl(server_sock, F_SETFL, flags | O_NONBLOCK); - xmlInitParser(); - while (keep_running) { readset = exceptset = all_sockets; @@ -358,7 +372,7 @@ static int ipc_receive(int client_sock) int ret, locked; int return_value = 0; - xmlDocPtr doc = NULL; + xmlDocPtr docReply = NULL, docRequest = NULL; xmlNodePtr root_node, images_node, clients_node, tmp_node, log_parent_node, log_node; xmlChar *xmlbuff; int buffersize; @@ -374,7 +388,7 @@ static int ipc_receive(int client_sock) if (header.size != 0) { // Message has payload, receive it - if (header.size > MAX_PAYLOAD) + if (header.size > MAX_IPC_PAYLOAD) { memlogf("[WARNING] IPC command with payload of %u bytes ignored.", (unsigned int)header.size); return 0; @@ -396,13 +410,13 @@ static int ipc_receive(int client_sock) case IPC_INFO: locked = 0; xmlbuff = NULL; - doc = xmlNewDoc(BAD_CAST "1.0"); - if (doc == NULL) + docReply = xmlNewDoc(BAD_CAST "1.0"); + if (docReply == NULL) goto get_info_reply_cleanup; - root_node = xmlNewNode(NULL, BAD_CAST "info"); + root_node = xmlNewNode(NULL, BAD_CAST "data"); if (root_node == NULL) goto get_info_reply_cleanup; - xmlDocSetRootElement(doc, root_node); + xmlDocSetRootElement(docReply, root_node); // Images images_node = xmlNewNode(NULL, BAD_CAST "images"); @@ -420,7 +434,7 @@ static int ipc_receive(int client_sock) tmp_node = xmlNewNode(NULL, BAD_CAST "image"); if (tmp_node == NULL) goto get_info_reply_cleanup; - xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->name); + xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->config_group); xmlNewProp(tmp_node, BAD_CAST "atime", BAD_CAST time_buff); xmlNewProp(tmp_node, BAD_CAST "rid", BAD_CAST rid); xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST image->file); @@ -458,13 +472,13 @@ static int ipc_receive(int client_sock) char *log = fetchlog(0); if (log == NULL) log = "LOG IS NULL"; - log_node = xmlNewCDataBlock(doc, BAD_CAST log, strlen(log)); + log_node = xmlNewCDataBlock(docReply, BAD_CAST log, strlen(log)); if (log_node == NULL) goto get_info_reply_cleanup; xmlAddChild(log_parent_node, log_node); // Dump and send - xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1); + xmlDocDumpFormatMemory(docReply, &xmlbuff, &buffersize, 1); header.size = htonl(buffersize); header.error = htonl(0); @@ -477,7 +491,6 @@ get_info_reply_cleanup: return_value = send_reply(client_sock, xmlbuff, buffersize); // Cleanup xmlFree(xmlbuff); - xmlFreeDoc(doc); free(log); break; @@ -490,55 +503,50 @@ get_info_reply_cleanup: return_value = send_reply(client_sock, &header, sizeof(header)); break; } - doc = xmlReadMemory(payload, header.size, "noname.xml", NULL, 0); + docRequest = xmlReadMemory(payload, header.size, "noname.xml", NULL, 0); - if (doc) + if (docRequest) { - xmlXPathContextPtr xpathCtx = NULL; - xmlXPathObjectPtr xpathObj = NULL; - xmlNodeSetPtr nodes = NULL; + if (!is_password_correct(docRequest)) + { + header.error = htonl(ERROR_WRONG_PASSWORD); + header.size = htonl(0); + return_value = send_reply(client_sock, &header, sizeof(header)); + break; + } + xmlNodePtr cur = NULL; + int count = 0; - xpathCtx = xmlXPathNewContext(doc); - if (xpathCtx == NULL) - goto add_del_cleanup; - xpathObj = xmlXPathEvalExpression(BAD_CAST "/info/images/image", xpathCtx); - if (xpathObj == NULL) - goto add_del_cleanup; - nodes = xpathObj->nodesetval; - if (nodes == NULL || nodes->nodeNr < 1) - goto add_del_cleanup; - cur = nodes->nodeTab[0]; - if (cur->type == XML_ELEMENT_NODE) + FOR_EACH_NODE(docRequest, "/data/images/image", cur) { - dnbd3_image_t image; - memset(&image, 0, sizeof(dnbd3_image_t)); - image.name = (char *)xmlGetNoNsProp(cur, BAD_CAST "name"); - char *rid_str = (char *)xmlGetNoNsProp(cur, BAD_CAST "rid"); - image.file = (char *)xmlGetNoNsProp(cur, BAD_CAST "file"); - image.cache_file = (char *)xmlGetNoNsProp(cur, BAD_CAST "cache"); - if (image.name && rid_str && image.file && image.cache_file) + if (cur->type == XML_ELEMENT_NODE) { - image.rid = atoi(rid_str); - if (cmd == IPC_ADDIMG) - header.error = htonl(dnbd3_add_image(&image)); + ++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)); + } else - header.error = htonl(dnbd3_del_image(&image)); + header.error = htonl(ERROR_MISSING_ARGUMENT); + xmlFree(image.config_group); + xmlFree(rid_str); + xmlFree(image.file); + xmlFree(image.cache_file); } - else - header.error = htonl(ERROR_MISSING_ARGUMENT); - xmlFree(image.name); - xmlFree(rid_str); - xmlFree(image.file); - xmlFree(image.cache_file); - } - else + } END_FOR_EACH; + if (count == 0) header.error = htonl(ERROR_MISSING_ARGUMENT); - -add_del_cleanup: - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - xmlFreeDoc(doc); } else header.error = htonl(ERROR_INVALID_XML); @@ -555,6 +563,10 @@ add_del_cleanup: break; } + + xmlFreeDoc(docReply); + xmlFreeDoc(docRequest); + return return_value; } @@ -562,6 +574,9 @@ void dnbd3_ipc_send(int cmd) { int client_sock, size; + // Check version and initialize + LIBXML_TEST_VERSION + #ifdef IPC_TCP struct sockaddr_in server; struct timeval client_timeout; @@ -629,72 +644,76 @@ void dnbd3_ipc_send(int cmd) if (doc) { - int n, i; - - xmlXPathContextPtr xpathCtx; - xmlXPathObjectPtr xpathObj; - xmlChar *xpathExpr; - xmlNodeSetPtr nodes; + int count; + int term_width = get_terminal_width(); xmlNodePtr cur; // Print log - xpathExpr = BAD_CAST "/info/log"; - xpathCtx = xmlXPathNewContext(doc); - xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); - if (xpathObj->nodesetval && xpathObj->nodesetval->nodeTab && xpathObj->nodesetval->nodeTab[0]) + xmlChar *log = getTextFromPath(doc, "/data/log"); + if (log) { - printf("--- Last log lines ----\n%s\n\n", xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0])); + printf("--- Last log lines ----\n%s\n\n", log); } - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); + + int watime = 0, wname = 0, wrid = 5; + FOR_EACH_NODE(doc, "/data/images/image", cur) + { + if (cur->type == XML_ELEMENT_NODE) + { + 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)); + wname = MAX(wname, xmlStrlen(vid)); + wrid = MAX(wrid, xmlStrlen(rid)); + // Too lazy to free vars, client will exit anyways + } + } END_FOR_EACH; + + char format[100]; + snprintf(format, 100, + "%%-%ds %%-%ds %%%ds %%s\n", watime, wname, wrid); // Print images - xpathExpr = BAD_CAST "/info/images/image"; - xpathCtx = xmlXPathNewContext(doc); - xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); - printf("Exported images (atime, name, rid, file):\n"); - printf("========================================\n"); - nodes = xpathObj->nodesetval; - n = (nodes) ? nodes->nodeNr : 0; - for (i = 0; i < n; ++i) + 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 (nodes->nodeTab[i]->type == XML_ELEMENT_NODE) + if (cur->type == XML_ELEMENT_NODE) { - cur = nodes->nodeTab[i]; + ++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("%s\t%s\t%s\t%s\n", atime, vid, rid, file); + printf(format, atime, vid, rid, file); + // Too lazy to free vars, client will exit anyways } - } - printf("\nNumber images: %d\n\n", n); - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); + } END_FOR_EACH; + char_repeat_br('=', term_width); + printf("\nNumber of images: %d\n\n", count); // Print clients - xpathExpr = BAD_CAST "/info/clients/client"; - xpathCtx = xmlXPathNewContext(doc); - xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); printf("Connected clients (ip, file):\n"); - printf("=============================\n"); - nodes = xpathObj->nodesetval; - n = (nodes) ? nodes->nodeNr : 0; - for (i = 0; i < n; ++i) + char_repeat_br('=', term_width); + count = 0; + FOR_EACH_NODE(doc, "/data/clients/client", cur) { - if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE) + if (cur->type == XML_ELEMENT_NODE) { - cur = nodes->nodeTab[i]; + ++count; xmlChar *ip = xmlGetNoNsProp(cur, BAD_CAST "ip"); xmlChar *file = xmlGetNoNsProp(cur, BAD_CAST "file"); - printf("%s\t%s\n", ip, file); + printf("%-40s %s\n", ip, file); + // Too lazy to free vars, client will exit anyways } - } - printf("\nNumber clients: %d\n\n", n); + } END_FOR_EACH; + char_repeat_br('=', term_width); + printf("\nNumber clients: %d\n\n", count); // Cleanup - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); xmlFreeDoc(doc); xmlCleanupParser(); @@ -710,3 +729,34 @@ void dnbd3_ipc_send(int cmd) 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 (_ipc_password == NULL) + { + memlogf("[WARNING] IPC access granted as no password is set!"); + return 1; + } + xmlChar *pass = getTextFromPath(doc, "/data/password"); + if (pass == NULL) + return 0; + if (strcmp((char*)pass, _ipc_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; +} diff --git a/src/server/net.c b/src/server/net.c index 49cfb24..b1a7b20 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -175,9 +175,15 @@ void *dnbd3_handle_query(void *dnbd3_client) { pthread_spin_lock(&_spinlock); image = dnbd3_get_image(image_name, rid, 0); + const time_t now = time(NULL); if (!image) { - printf("[DEBUG] Client requested non-existent image '%s' (rid:%d, protocol:%d)\n", image_name, (int)rid, (int)client_version); + printf("[DEBUG] Client requested non-existent image '%s' (rid:%d)\n", image_name, (int)rid); + } + else if ((image->delete_soft != 0 && image->delete_soft < now) + || (image->delete_hard != 0 && image->delete_hard < now)) + { + printf("[DEBUG] Client requested end-of-life image '%s' (rid:%d)\n", image_name, (int)rid); } else { @@ -267,6 +273,7 @@ void *dnbd3_handle_query(void *dnbd3_client) { printf("[ERROR] sendfile failed (image to net)\n"); close(client->sock); + client->sock = -1; } break; } @@ -296,6 +303,7 @@ void *dnbd3_handle_query(void *dnbd3_client) { printf("[ERROR] sendfile failed (copy to cache 1)\n"); close(client->sock); + client->sock = -1; // Reset these so we don't update the cache map with false information dirty = 0; todo_size = 0; @@ -320,6 +328,7 @@ void *dnbd3_handle_query(void *dnbd3_client) { printf("[ERROR] sendfile failed (copy to cache 2)\n"); close(client->sock); + client->sock = -1; break; } dirty = 1; @@ -344,6 +353,7 @@ void *dnbd3_handle_query(void *dnbd3_client) { memlogf("[ERROR] sendfile failed (cache to net)\n"); close(client->sock); + client->sock = -1; } break; @@ -374,13 +384,14 @@ void *dnbd3_handle_query(void *dnbd3_client) } } - close(client->sock); - if (image_file != -1) close(image_file); - if (image_cache != -1) close(image_cache); pthread_spin_lock(&_spinlock); _dnbd3_clients = g_slist_remove(_dnbd3_clients, client); pthread_spin_unlock(&_spinlock); - free(client); + if (client->sock != -1) + close(client->sock); + if (image_file != -1) close(image_file); + if (image_cache != -1) close(image_cache); + g_free(client); pthread_exit((void *) 0); } diff --git a/src/server/server.c b/src/server/server.c index 8981d77..28da0ae 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -36,7 +36,7 @@ #include "ipc.h" #include "memlog.h" -int _sock; +static int sock; #ifdef _DEBUG int _fake_delay = 0; #endif @@ -45,6 +45,7 @@ pthread_spinlock_t _spinlock; GSList *_dnbd3_clients = NULL; char *_config_file_name = DEFAULT_SERVER_CONFIG_FILE; char *_local_namespace = NULL; +char *_ipc_password = NULL; GSList *_dnbd3_images = NULL; // of dnbd3_image_t void dnbd3_print_help(char* argv_0) @@ -75,7 +76,8 @@ void dnbd3_cleanup() int fd; memlogf("INFO: Cleanup...\n"); - close(_sock); + close(sock); + sock = -1; dnbd3_ipc_shutdown(); @@ -108,11 +110,12 @@ void dnbd3_cleanup() close(fd); } - free(image->name); - g_free(image->file); - g_free(image->cache_file); - free(image->cache_map); - g_free(image); + free(image->cache_map); + free(image->config_group); + free(image->low_name); + free(image->file); + free(image->cache_file); + g_free(image); } g_slist_free(_dnbd3_images); @@ -202,12 +205,13 @@ int main(int argc, char* argv[]) signal(SIGINT, dnbd3_handle_sigterm); // setup network - _sock = dnbd3_setup_socket(); - if (_sock < 0) + sock = dnbd3_setup_socket(); + if (sock < 0) exit(EXIT_FAILURE); struct sockaddr_in client; unsigned int len = sizeof(client); int fd; + time_t next_delete_invocation = 0; struct timeval timeout; timeout.tv_sec = SOCKET_TIMEOUT_SERVER; timeout.tv_usec = 0; @@ -221,7 +225,7 @@ int main(int argc, char* argv[]) // main loop while (1) { - fd = accept(_sock, (struct sockaddr*) &client, &len); + fd = accept(sock, (struct sockaddr*) &client, &len); if (fd < 0) { memlogf("[ERROR] Accept failure"); @@ -263,6 +267,13 @@ int main(int argc, char* argv[]) continue; } pthread_detach(dnbd3_client->thread); + // Call image deletion function if last call is more than 5 minutes ago + const time_t now = time(NULL); + if (now < next_delete_invocation) + { + next_delete_invocation = now + 300; + dnbd3_exec_delete(TRUE); + } } dnbd3_cleanup(); diff --git a/src/server/server.h b/src/server/server.h index 875a5af..90aae5c 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -30,8 +30,8 @@ typedef struct { - char *name; // full name of image, eg. "uni-freiburg/rz/ubuntu-12.04" - char *low_name; // full name of image, lowercased for comparison + 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 @@ -40,6 +40,8 @@ typedef struct 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 } dnbd3_image_t; typedef struct @@ -53,7 +55,7 @@ typedef struct extern GSList *_dnbd3_clients; // of dnbd3_client_t extern pthread_spinlock_t _spinlock; -extern char *_config_file_name, *_local_namespace; +extern char *_config_file_name, *_local_namespace, *_ipc_password; extern GSList *_dnbd3_images; // of dnbd3_image_t diff --git a/src/server/utils.c b/src/server/utils.c index ddc52d8..665d96d 100644 --- a/src/server/utils.c +++ b/src/server/utils.c @@ -31,15 +31,20 @@ #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 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" + * @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 @@ -153,22 +158,21 @@ static void strtolower(char *string) void dnbd3_load_config() { gint i; - GKeyFile* gkf; - if (_local_namespace != NULL || _dnbd3_images != NULL) + if (_config_handle != NULL) { printf("dnbd3_load_config() called more than once\n\n"); exit(EXIT_FAILURE); } - gkf = g_key_file_new(); - if (!g_key_file_load_from_file(gkf, _config_file_name, G_KEY_FILE_NONE, NULL)) + _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(gkf, "settings", "default_namespace", NULL); + _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); @@ -176,13 +180,15 @@ void dnbd3_load_config() _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(gkf, §ion_count); + groups = g_key_file_get_groups(_config_handle, §ion_count); for (i = 0; i < section_count; i++) { - // Special group + // Special group, ignore if (strcmp(groups[i], "settings") == 0 || strcmp(groups[i], "trusted") == 0) { continue; @@ -190,17 +196,25 @@ void dnbd3_load_config() // An actual image definition - int rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL); + 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; } - char *image_file = g_key_file_get_string(gkf, groups[i], "file", NULL); - char *cache_file = g_key_file_get_string(gkf, groups[i], "cache", NULL); + 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(gkf, groups[i], "servers", &num_servers, NULL); + gchar **servers = g_key_file_get_string_list(_config_handle, groups[i], "servers", &num_servers, NULL); pthread_spin_lock(&_spinlock); dnbd3_image_t *image = prepare_image(groups[i], rid, image_file, cache_file, servers, num_servers); @@ -216,7 +230,6 @@ void dnbd3_load_config() } g_strfreev(groups); - g_key_file_free(gkf); } int dnbd3_add_image(dnbd3_image_t *image) @@ -226,15 +239,15 @@ int dnbd3_add_image(dnbd3_image_t *image) // but better be safe for the future... pthread_spin_lock(&_spinlock); if (image->rid == 0) - { // TODO: globalize image->name somewhere for this call - const dnbd3_image_t *latest = dnbd3_get_image(image->name, 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->name, image->rid, image->file, image->cache_file, NULL, 0); + dnbd3_image_t *newimage = prepare_image(image->config_group, image->rid, image->file, image->cache_file, NULL, 0); if (newimage) { _dnbd3_images = g_slist_prepend(_dnbd3_images, image); @@ -246,105 +259,94 @@ int dnbd3_add_image(dnbd3_image_t *image) } // Adding image was successful, write config file - GKeyFile* gkf; - gkf = g_key_file_new(); - if (!g_key_file_load_from_file(gkf, _config_file_name, G_KEY_FILE_NONE, NULL)) - { - printf("ERROR: Config file not found: %s\n", _config_file_name); - exit(EXIT_FAILURE); - } - - g_key_file_set_integer(gkf, image->name, "rid", image->rid); - g_key_file_set_string(gkf, image->name, "file", image->file); - //g_key_file_set_string(gkf, image->name, "servers", image->serverss); // TODO: Save servers as string - g_key_file_set_string(gkf, image->name, "cache", image->cache_file); + 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); - gchar* data = g_key_file_to_data(gkf, NULL, NULL); - - FILE *f = fopen(_config_file_name, "w"); - if (f >= 0) - { - fputs((char*) data, f); - fclose(f); - pthread_spin_unlock(&_spinlock); - g_free(data); - g_key_file_free(gkf); - memlogf("[INFO] Added new image '%s' (rid %d)", newimage->name, newimage->rid); - return 0; - } pthread_spin_unlock(&_spinlock); - g_free(data); - g_key_file_free(gkf); - memlogf("[ERROR] Image added, but config file is not writable (%s)", _config_file_name); - return ERROR_SEE_LOG; + + 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) { - return ERROR_IMAGE_NOT_FOUND; // TODO: Make it work with image names - /* - if (image->rid == 0) - { - printf("ERROR: Delete with rid=0 is not allowed\n"); - return ERROR_RID; - } + 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; + } - dnbd3_image_t* tmp = dnbd3_get_image(image->vid, image->rid); - if (!tmp) - { - printf("ERROR: Image not found: (%d,%d)\n", image->vid, image->rid); - return ERROR_IMAGE_NOT_FOUND; - } + 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); - GSList *iterator = NULL; - for (iterator = _dnbd3_clients; iterator; iterator = iterator->next) - { - dnbd3_client_t *client = iterator->data; - if (tmp == client->image) - { - printf("ERROR: Delete is not allowed, image is in use (%d,%d)\n", tmp->vid, tmp->rid); - return ERROR_IMAGE_IN_USE; - } - } + 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; +} - GKeyFile* gkf; - gkf = g_key_file_new(); - if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL)) +static int save_config() +{ + pthread_spin_lock(&_spinlock); + char* data = (char*)g_key_file_to_data(_config_handle, NULL, NULL); + if (data == NULL) { - printf("ERROR: Config file not found: %s\n", file); - exit(EXIT_FAILURE); + pthread_spin_unlock(&_spinlock); + memlogf("[ERROR] g_key_file_to_data() failed"); + return ERROR_UNSPECIFIED_ERROR; } - g_key_file_remove_group(gkf, tmp->group, NULL); - gchar* data = g_key_file_to_data(gkf, NULL, NULL); - - FILE* f = fopen(file,"w"); - if (f) + FILE *f = fopen(_config_file_name, "w"); + if (f < 0) { - fputs((char*) data, f); + 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); - g_key_file_free(gkf); - // TODO: unlink image file return 0; - } - else - { - g_free(data); - g_key_file_free(gkf); - printf("ERROR: Config file is not writable: %s\n", file); - return ERROR_CONFIG_FILE_PERMISSIONS; - } - */ } dnbd3_image_t* dnbd3_get_image(char *name_orig, int rid, const char do_lock) { dnbd3_image_t *result = NULL, *image; GSList *iterator; - char name[strlen(name_orig) + 1]; - strcpy(name, name_orig); + // 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) @@ -418,22 +420,22 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, if (strchr(image_name, '/') == NULL) { // Local image, build global name - image->name = calloc(strlen(_local_namespace) + strlen(image_name) + 2, sizeof(char)); - sprintf(image->name, "%s/%s", _local_namespace, image_name); + 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->name = strdup(image_name); + image->low_name = strdup(image_name); } - if (dnbd3_get_image(image->name, rid, 0)) + if (dnbd3_get_image(image->low_name, rid, 0)) { - memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image->name, rid); + memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image_name, rid); goto error; } - image->low_name = strdup(image->name); strtolower(image->low_name); + image->config_group = strdup(image_name); image->rid = rid; const char relayed = (image_file == NULL || image_file == '\0'); @@ -460,7 +462,8 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, 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); + 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; @@ -512,7 +515,7 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, // "+ (1 << 15) - 1" is required to account for the last bit of // the image that is smaller than 32kib // this would be the case whenever the image file size is not a - // multiple of 32kib (= the number of blocks is not dividable by 8) + // 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 char tmp[strlen(image->cache_file) + 5]; @@ -543,7 +546,7 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, 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); + memlogf("[INFO] Instantly publishing relayed image '%s' because the local cache copy is complete", image_name); } /* @@ -564,10 +567,113 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, error: // Free stuff. Some pointers might be zero, but calling free() on those is safe. free(image->cache_map); - free(image->name); + 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 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 index 18b205a..e923f82 100644 --- a/src/server/utils.h +++ b/src/server/utils.h @@ -26,6 +26,7 @@ #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 @@ -37,11 +38,14 @@ #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); void dnbd3_handle_sigpipe(int signum); diff --git a/src/server/xmlutil.c b/src/server/xmlutil.c new file mode 100644 index 0000000..62736cd --- /dev/null +++ b/src/server/xmlutil.c @@ -0,0 +1,27 @@ +#include "xmlutil.h" +#include <libxml/parser.h> +#include <libxml/xpath.h> + + +xmlChar *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; + } + xmlChar *retval = NULL; + if (xpathObj->stringval) + retval = xmlStrdup(xpathObj->stringval); + else if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 && xpathObj->nodesetval->nodeTab && xpathObj->nodesetval->nodeTab[0]) + { + retval = xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]); + } + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + return retval; +} diff --git a/src/server/xmlutil.h b/src/server/xmlutil.h new file mode 100644 index 0000000..46a5790 --- /dev/null +++ b/src/server/xmlutil.h @@ -0,0 +1,26 @@ +#ifndef XMLUTIL_H_ +#define XMLUTIL_H_ + +#include <libxml/xmlstring.h> +#include <libxml/tree.h> + +xmlChar *getTextFromPath(xmlDocPtr doc, char *xpath); + +#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) + + +#endif |