From fb6432cb3de7fc8dde366805b5cb9f4a82b85660 Mon Sep 17 00:00:00 2001 From: sr Date: Wed, 19 Dec 2012 19:35:32 +0100 Subject: [SERVER] Refactor code [SERVER] Return List of Alt Servers on RPC_IMG_LIST --- src/config.h | 4 +- src/server/ipc.c | 821 ------------------------------------------------- src/server/ipc.h | 55 ---- src/server/job.c | 29 +- src/server/rpc.c | 823 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/server/rpc.h | 55 ++++ src/server/saveload.c | 8 +- src/server/server.c | 21 +- src/server/server.h | 2 +- src/server/xmlutil.h | 16 + 10 files changed, 925 insertions(+), 909 deletions(-) delete mode 100644 src/server/ipc.c delete mode 100644 src/server/ipc.h create mode 100644 src/server/rpc.c create mode 100644 src/server/rpc.h diff --git a/src/config.h b/src/config.h index 2f348aa..35e5647 100644 --- a/src/config.h +++ b/src/config.h @@ -68,8 +68,6 @@ // +++++ Misc +++++ #define DEFAULT_SERVER_CONFIG_FILE "/etc/dnbd3/server.conf" #define DEFAULT_CLIENT_CONFIG_FILE "/etc/dnbd3/client.conf" -#define UNIX_SOCKET "/run/dnbd3-server.sock" -#define UNIX_SOCKET_GROUP "dnbd" -#define MAX_IPC_PAYLOAD 3000 +#define MAX_RPC_PAYLOAD 3000 #endif /* CONFIG_H_ */ diff --git a/src/server/ipc.c b/src/server/ipc.c deleted file mode 100644 index a345794..0000000 --- a/src/server/ipc.c +++ /dev/null @@ -1,821 +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 "ipc.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 -#include -#include -#include -#include - -#include -#include -#include "xmlutil.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 is_password_correct(xmlDocPtr doc); -static int get_terminal_width(); -static int ipc_send_reply(int sock, dnbd3_ipc_t* header, int result_code, xmlDocPtr payload); - -static int get_highest_fd(GSList *sockets) -{ - GSList *iterator; - int max = server_sock; - - for (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_ipc_mainloop() -{ - - // 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."); - pthread_exit((void *)0); - return NULL; - } - - struct sockaddr_in server, client; - socklen_t len = sizeof(client); - - // Create socket - if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) - { - perror("ERROR: IPC socket"); - exit(EXIT_FAILURE); - } - - memset(&server, 0, sizeof(server)); - server.sin_family = AF_INET; // IPv4 - server.sin_addr.s_addr = INADDR_ANY; - server.sin_port = htons(IPC_PORT); // set port number - - const int optval = 1; - setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - - // Bind to socket - if (bind(server_sock, (struct sockaddr *)&server, sizeof(server)) < 0) - { - perror("ERROR: IPC bind"); - exit(EXIT_FAILURE); - } - - // Listen on socket - if (listen(server_sock, 5) < 0) - { - perror("ERROR: IPC listen"); - exit(EXIT_FAILURE); - } - - // Run connection-accepting loop - - fd_set all_sockets, readset, exceptset; - - GSList *sockets = NULL, *iterator; - - int client_sock, ret, flags; - int maxfd = server_sock + 1; - int error_count = 0; - - struct timeval client_timeout, select_timeout; - client_timeout.tv_sec = 0; - client_timeout.tv_usec = 500 * 1000; - - FD_ZERO(&all_sockets); - FD_SET(server_sock, &all_sockets); - - // Make listening socket non-blocking - flags = fcntl(server_sock, F_GETFL, 0); - if (flags == -1) - flags = 0; - fcntl(server_sock, F_SETFL, flags | O_NONBLOCK); - - 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) - { - --ret; - if (FD_ISSET(server_sock, &readset)) - { - // Accept connection - if ((client_sock = accept(server_sock, &client, &len)) < 0) - { - if (errno != EAGAIN) - { - memlogf("[ERROR] Error accepting an IPC connection"); - if (++error_count > 10) - goto end_loop; - } - continue; - } - error_count = 0; - // Apply read/write timeout - setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout)); - setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout)); - // Make new connection blocking - flags = fcntl(client_sock, F_GETFL, 0); - if (flags == -1) - flags = 0; - fcntl(client_sock, F_SETFL, flags & ~(int)O_NONBLOCK); - 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); - } - else if (FD_ISSET(server_sock, &exceptset)) - { - memlogf("[ERROR] An exception occurred on the IPC listening socket."); - if (++error_count > 10) - goto end_loop; - } - else - { - // Must be an active IPC 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)) - { - // Client sending data - if (!ipc_receive(client_sock)) - { - // Connection has been closed - close(client_sock); - del = client_sock; - FD_CLR(client_sock, &all_sockets); - } - } - else if (FD_ISSET(client_sock, &exceptset)) - { - // 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 IPC interface."); - if (server_sock != -1) - { - close(server_sock); - server_sock = -1; - } - - free(payload); - xmlCleanupParser(); - pthread_exit((void *)0); - return NULL; -} - -void dnbd3_ipc_shutdown() -{ - keep_running = 0; - if (server_sock == -1) - return; - close(server_sock); - server_sock = -1; -} - -/** - * Returns !=0 if send/recv successful, 0 on any kind of network failure - */ -static int ipc_receive(int client_sock) -{ - GSList *iterator, *iterator2; - -#define STRBUFLEN 100 - char strbuffer[STRBUFLEN]; - - dnbd3_ipc_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_IPC_PAYLOAD) - { - memlogf("[WARNING] IPC 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 IPC_EXIT: - memlogf("[INFO] Server shutdown by IPC request"); - header.size = ntohl(0); - return_value = send_data(client_sock, &header, sizeof(header)); - dnbd3_cleanup(); - break; - - case IPC_IMG_LIST: - if (!createXmlDoc(&docReply, &root_node, "data")) - goto case_end; - - xmlNewTextChild(root_node, NULL, BAD_CAST "defaultns", BAD_CAST _local_namespace); - - // 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->config_group); - sprintf(strbuffer, "%u", (unsigned int)image->atime); - xmlNewProp(tmp_node, BAD_CAST "atime", BAD_CAST strbuffer); - sprintf(strbuffer, "%d", image->rid); - xmlNewProp(tmp_node, BAD_CAST "rid", BAD_CAST strbuffer); - sprintf(strbuffer, "%llu", (unsigned long long)image->filesize); - xmlNewProp(tmp_node, BAD_CAST "size", BAD_CAST strbuffer); - if (image->file) - xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST image->file); - xmlNewProp(tmp_node, BAD_CAST "servers", BAD_CAST "???"); // TODO - 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; - sprintf(strbuffer, "%d", complete / size); - xmlNewProp(tmp_node, BAD_CAST "cachefill", BAD_CAST strbuffer); - } - xmlAddChild(parent_node, tmp_node); - } - pthread_spin_unlock(&_spinlock); - locked = 0; - - // Dump and send - rpc_error = 0; - break; - - case IPC_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; - *strbuffer = '\0'; - 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->config_group); - sprintf(strbuffer, "%d", client->image->rid); - xmlNewProp(tmp_node, BAD_CAST "rid", BAD_CAST strbuffer); - xmlAddChild(parent_node, tmp_node); - } - } - pthread_spin_unlock(&_spinlock); - locked = 0; - - // Dump and send - rpc_error = 0; - break; - - case IPC_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 IPC_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 IPC_ADD_IMG: - case IPC_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/images/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 = (char *)XML_GETPROP(cur, "name"); - char *rid_str = (char *)XML_GETPROP(cur, "rid"); - image.file = (char *)XML_GETPROP(cur, "file"); - image.cache_file = (char *)XML_GETPROP(cur, "cache"); - if (image.file && !file_exists(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 == IPC_ADD_IMG) - rpc_error = dnbd3_add_image(&image); - else - 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 IPC_ADD_NS: - case IPC_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 = (char *)XML_GETPROP(cur, "address"); - char *ns = (char *)XML_GETPROP(cur, "name"); - char *flags = (char *)XML_GETPROP(cur, "flags"); - char *comment = (char *)XML_GETPROP(cur, "comment"); - pthread_spin_lock(&_spinlock); - if (host && ns) - { - if (cmd == IPC_ADD_NS) - { - dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(host, TRUE, comment); - if (server) - dnbd3_add_trusted_namespace(server, ns, flags); - } - else - { - dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(host, FALSE, comment); - if (server) - dnbd3_del_trusted_namespace(server, ns); - } - } - pthread_spin_unlock(&_spinlock); - FREE_POINTERLIST; - } END_FOR_EACH; - - } - else - rpc_error = ERROR_INVALID_XML; - - break; - - default: - memlogf("[ERROR] Unknown IPC command: %u", (unsigned int)header.cmd); - rpc_error = htonl(ERROR_UNKNOWN_COMMAND); - break; - - } -case_end: - - if (locked) - pthread_spin_unlock(&_spinlock); - // Send reply - return_value = ipc_send_reply(client_sock, &header, rpc_error, docReply); - - xmlFreeDoc(docReply); - xmlFreeDoc(docRequest); - - return return_value; -} - -void dnbd3_ipc_send(int cmd) -{ - int client_sock, size; - - // Check version and initialize - LIBXML_TEST_VERSION - - struct sockaddr_in server; - struct timeval client_timeout; - - // Create socket - if ((client_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) - { - perror("ERROR: IPC socket"); - exit(EXIT_FAILURE); - } - - client_timeout.tv_sec = 4; - client_timeout.tv_usec = 0; - setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout)); - setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout)); - - 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(IPC_PORT); // set port number - - // Connect to server - if (connect(client_sock, (struct sockaddr *)&server, sizeof(server)) < 0) - { - perror("ERROR: IPC connect"); - exit(EXIT_FAILURE); - } - - // Send message - dnbd3_ipc_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 == IPC_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 (_ipc_password == NULL) - { - memlogf("[WARNING] IPC access granted as no password is set!"); - return 1; - } - char *pass = getTextFromPath(doc, "/data/password"); - if (pass == NULL) - return 0; - if (strcmp(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; -} - -#define RETBUFLEN 8000 -static char returnbuffer[RETBUFLEN]; -static int ipc_send_reply(int sock, dnbd3_ipc_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(IPC_ERROR); - if (!send_data(sock, header, sizeof(*header))) - return FALSE; - return send_data(sock, returnbuffer, len); -} diff --git a/src/server/ipc.h b/src/server/ipc.h deleted file mode 100644 index fabe6f8..0000000 --- a/src/server/ipc.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 IPC_H_ -#define IPC_H_ - -#include - -#define IPC_EXIT 0 -#define IPC_RELOAD 1 -#define IPC_IMG_LIST 2 -#define IPC_ADD_IMG 3 -#define IPC_DEL_IMG 4 -#define IPC_ADD_NS 5 -#define IPC_DEL_NS 6 -#define IPC_CLIENT_LIST 7 -#define IPC_TRUSTED_LIST 8 -#define IPC_GET_LOG 9 -#define IPC_FIX_IMAGE 10 -#define IPC_ERROR 11 - -void *dnbd3_ipc_mainloop(); - -void dnbd3_ipc_shutdown(); - -void dnbd3_ipc_send(int cmd); - - -#pragma pack(1) -typedef struct -{ - uint32_t handle;// 4byte - uint32_t cmd; // 4byte - uint32_t size; // 4byte -} dnbd3_ipc_t; -#pragma pack(0) - -#endif /* IPC_H_ */ diff --git a/src/server/job.c b/src/server/job.c index d1f99b8..71e09da 100644 --- a/src/server/job.c +++ b/src/server/job.c @@ -2,7 +2,7 @@ #include "saveload.h" #include "helper.h" #include "memlog.h" -#include "ipc.h" +#include "rpc.h" #include #include @@ -54,7 +54,7 @@ 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 update_image_atimes(time_t now); +static void dnbd3_update_atimes(time_t now); // @@ -97,7 +97,7 @@ void *dnbd3_job_thread(void *data) const time_t starttime = time(NULL); // // Update image atime - update_image_atimes(starttime); + dnbd3_update_atimes(starttime); // Call image deletion function if last call is more than 5 minutes ago if (starttime < next_delete_invocation) { @@ -345,7 +345,7 @@ static void connect_proxy_images() } } -static void update_image_atimes(time_t now) +static void dnbd3_update_atimes(time_t now) { GSList *iterator; pthread_spin_lock(&_spinlock); @@ -371,10 +371,11 @@ static void query_servers() dnbd3_trusted_server_t *server; dnbd3_host_t host; struct sockaddr_in addr4; - char xmlbuffer[MAX_IPC_PAYLOAD]; + char xmlbuffer[MAX_RPC_PAYLOAD]; for (num = 0;; ++num) { - // "Iterate" this way to prevent holding the lock for a long time, although it is possible to skip a server this way... + // "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) @@ -382,7 +383,7 @@ static void query_servers() pthread_spin_unlock(&_spinlock); break; // Done } - host = server->host; + host = server->host; // Copy host, in case server gets deleted by another thread pthread_spin_unlock(&_spinlock); // Connect if (host.type != AF_INET) @@ -415,8 +416,8 @@ static void query_servers() // // Send and receive info from server // Send message - dnbd3_ipc_t header; - header.cmd = htonl(IPC_IMG_LIST); + 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))) @@ -426,14 +427,14 @@ static void query_servers() } header.cmd = ntohl(header.cmd); header.size = ntohl(header.size); - if (header.cmd != IPC_IMG_LIST) + 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_IPC_PAYLOAD) + if (header.size > MAX_RPC_PAYLOAD) { - memlogf("[WARNING] XML payload from other server exceeds MAX_IPC_PAYLOAD (%d > %d)", (int)header.size, (int)MAX_IPC_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)) @@ -497,7 +498,7 @@ static void query_servers() printf("[DEBUG] Invalid image name: '%s'\n", image); goto free_current_image; } - snprintf(xmlbuffer, MAX_IPC_PAYLOAD, "%s/%s", ns, image); + snprintf(xmlbuffer, MAX_RPC_PAYLOAD, "%s/%s", ns, image); } else { @@ -512,7 +513,7 @@ static void query_servers() printf("[DEBUG] Ignoring remote image with invalid name '%s'\n", slash); goto free_current_image; } - snprintf(xmlbuffer, MAX_IPC_PAYLOAD, "%s/%s", image, slash); + snprintf(xmlbuffer, MAX_RPC_PAYLOAD, "%s/%s", image, slash); } // Image seems legit, check if there's a local copy dnbd3_namespace_t *trust; diff --git a/src/server/rpc.c b/src/server/rpc.c new file mode 100644 index 0000000..152bf7c --- /dev/null +++ b/src/server/rpc.c @@ -0,0 +1,823 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include "xmlutil.h" + +#define RPC_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 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) +{ + GSList *iterator; + int max = server_sock; + + for (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_in server, client; + socklen_t len = sizeof(client); + + // Create socket + if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + perror("ERROR: RPC socket"); + exit(EXIT_FAILURE); + } + + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; // IPv4 + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(RPC_PORT); // set port number + + const int optval = 1; + setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + // Bind to socket + if (bind(server_sock, (struct sockaddr *)&server, sizeof(server)) < 0) + { + perror("ERROR: RPC bind"); + exit(EXIT_FAILURE); + } + + // Listen on socket + if (listen(server_sock, 5) < 0) + { + perror("ERROR: RPC listen"); + exit(EXIT_FAILURE); + } + + // Run connection-accepting loop + + fd_set all_sockets, readset, exceptset; + + GSList *sockets = NULL, *iterator; + + int client_sock, ret, flags; + int maxfd = server_sock + 1; + int error_count = 0; + + struct timeval client_timeout, select_timeout; + client_timeout.tv_sec = 0; + client_timeout.tv_usec = 500 * 1000; + + FD_ZERO(&all_sockets); + FD_SET(server_sock, &all_sockets); + + // Make listening socket non-blocking + flags = fcntl(server_sock, F_GETFL, 0); + if (flags == -1) + flags = 0; + fcntl(server_sock, F_SETFL, flags | O_NONBLOCK); + + 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) + { + --ret; + if (FD_ISSET(server_sock, &readset)) + { + // Accept connection + if ((client_sock = accept(server_sock, &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 + setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout)); + setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout)); + // Make new connection blocking + flags = fcntl(client_sock, F_GETFL, 0); + if (flags == -1) + flags = 0; + fcntl(client_sock, F_SETFL, flags & ~(int)O_NONBLOCK); + 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); + } + else if (FD_ISSET(server_sock, &exceptset)) + { + memlogf("[ERROR] An exception occurred on the RPC listening socket."); + if (++error_count > 10) + goto end_loop; + } + else + { + // 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)) + { + // Client sending data + if (!rpc_receive(client_sock)) + { + // Connection has been closed + close(client_sock); + del = client_sock; + FD_CLR(client_sock, &all_sockets); + } + } + else if (FD_ISSET(client_sock, &exceptset)) + { + // 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."); + if (server_sock != -1) + { + close(server_sock); + server_sock = -1; + } + + free(payload); + xmlCleanupParser(); + pthread_exit((void *)0); + return NULL; +} + +void dnbd3_rpc_shutdown() +{ + keep_running = 0; + if (server_sock == -1) + return; + close(server_sock); + server_sock = -1; +} + +/** + * 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"); + } + 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; + 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/images/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 = (char *)XML_GETPROP(cur, "name"); + char *rid_str = (char *)XML_GETPROP(cur, "rid"); + image.file = (char *)XML_GETPROP(cur, "file"); + image.cache_file = (char *)XML_GETPROP(cur, "cache"); + if (image.file && !file_exists(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 + 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); + } + else + { + dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(host, FALSE, comment); + if (server) + dnbd3_del_trusted_namespace(server, ns); + } + } + pthread_spin_unlock(&_spinlock); + FREE_POINTERLIST; + } END_FOR_EACH; + + } + else + rpc_error = ERROR_INVALID_XML; + + break; + + default: + memlogf("[ERROR] Unknown RPC command: %u", (unsigned int)header.cmd); + rpc_error = htonl(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; + struct timeval client_timeout; + + // Create socket + if ((client_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + perror("ERROR: RPC socket"); + exit(EXIT_FAILURE); + } + + client_timeout.tv_sec = 4; + client_timeout.tv_usec = 0; + setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout)); + setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout)); + + 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 (connect(client_sock, (struct sockaddr *)&server, sizeof(server)) < 0) + { + 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 new file mode 100644 index 0000000..a7c9f39 --- /dev/null +++ b/src/server/rpc.h @@ -0,0 +1,55 @@ +/* + * 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 index 18dbae9..9b5318a 100644 --- a/src/server/saveload.c +++ b/src/server/saveload.c @@ -66,7 +66,7 @@ void dnbd3_load_config() srand(time(NULL)); - _ipc_password = g_key_file_get_string(_config_handle, "settings", "password", 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) @@ -154,7 +154,7 @@ int dnbd3_add_image(dnbd3_image_t *image) close(fh); } // Lock here to prevent concurrent add calls to mess rids up. Cannot happen currently - // as IPC clients are not threaded and they're the only place where this is called, + // 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) @@ -371,13 +371,15 @@ static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, 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; } - strtolower(image->low_name); image->config_group = strdup(image_name); image->rid = rid; diff --git a/src/server/server.c b/src/server/server.c index bd5f10a..0c2f37a 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -35,7 +35,7 @@ #include "saveload.h" #include "job.h" #include "net.h" -#include "ipc.h" +#include "rpc.h" #include "memlog.h" static int sock; @@ -47,7 +47,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; +char *_rpc_password = NULL; char *_cache_dir = NULL; GSList *_dnbd3_images = NULL; // of dnbd3_image_t GSList *_trusted_servers = NULL; @@ -83,7 +83,7 @@ void dnbd3_cleanup() close(sock); sock = -1; - dnbd3_ipc_shutdown(); + dnbd3_rpc_shutdown(); dnbd3_job_shutdown(); pthread_spin_lock(&_spinlock); @@ -135,9 +135,6 @@ void dnbd3_cleanup() g_slist_free(_dnbd3_images); pthread_spin_unlock(&_spinlock); -#ifndef IPC_TCP - unlink(UNIX_SOCKET); -#endif exit(EXIT_SUCCESS); } @@ -181,15 +178,15 @@ int main(int argc, char *argv[]) break; case 'r': printf("INFO: Reloading configuration file...\n\n"); - dnbd3_ipc_send(IPC_RELOAD); + dnbd3_rpc_send(RPC_RELOAD); return EXIT_SUCCESS; case 's': printf("INFO: Stopping running server...\n\n"); - dnbd3_ipc_send(IPC_EXIT); + dnbd3_rpc_send(RPC_EXIT); return EXIT_SUCCESS; case 'i': printf("INFO: Requesting information...\n\n"); - dnbd3_ipc_send(IPC_IMG_LIST); + dnbd3_rpc_send(RPC_IMG_LIST); return EXIT_SUCCESS; case 'H': dnbd3_print_help(argv[0]); @@ -231,9 +228,9 @@ int main(int argc, char *argv[]) timeout.tv_sec = SOCKET_TIMEOUT_SERVER; timeout.tv_usec = 0; - // setup ipc - pthread_t thread_ipc; - pthread_create(&(thread_ipc), NULL, &dnbd3_ipc_mainloop, NULL); + // setup rpc + pthread_t thread_rpc; + pthread_create(&(thread_rpc), NULL, &dnbd3_rpc_mainloop, NULL); pthread_t thread_job; pthread_create(&(thread_job), NULL, &dnbd3_job_thread, NULL); diff --git a/src/server/server.h b/src/server/server.h index fda5e46..8fb4f21 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -83,7 +83,7 @@ typedef struct extern GSList *_dnbd3_clients; // of dnbd3_client_t extern pthread_spinlock_t _spinlock; -extern char *_config_file_name, *_local_namespace, *_ipc_password, *_cache_dir; +extern char *_config_file_name, *_local_namespace, *_rpc_password, *_cache_dir; extern GSList *_dnbd3_images; // of dnbd3_image_t extern GSList *_trusted_servers; diff --git a/src/server/xmlutil.h b/src/server/xmlutil.h index ecfa4c4..4da8616 100644 --- a/src/server/xmlutil.h +++ b/src/server/xmlutil.h @@ -3,10 +3,13 @@ #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) { \ @@ -23,6 +26,8 @@ char createXmlDoc(xmlDocPtr *doc, xmlNodePtr* root, char* rootName); 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]; \ @@ -34,6 +39,17 @@ char createXmlDoc(xmlDocPtr *doc, xmlNodePtr* root, char* rootName); 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