summaryrefslogblamecommitdiffstats
path: root/src/server/rpc.c
blob: e253ace5ecaa55b4a1a5413cfe6ffc6ac967fd17 (plain) (tree)



















                                                               
                





                      

                   

                     
                   
                      

                    
                  
                       
 

                          
                    
 
                         
 

                                                                                                       

                                     
 





                                               
                                        
                                           

                                              
                                                                                             
 

                                          
                    
 






                                                                             




                                                           
                                      


                   
                          
 



                                       
                                          

                            
                                                                                          


                                        
 
                                       
 



                                                                                                             

                         
                              
         
                                                              

                                   
 





                                               

                                            

                            
                                      

                              




                                                      
 







                                                                                 
                                                              
                         
                                                                        
                                 



                                                                                                                          
                                         






                                                                                                             
                                         
















                                                                                                              
                                 
                         
                                    
                         
                                                                   












                                                                                                                                                
                                                      
                                                                      
                                                                              






                                                                                          
                                                                              
                                         
                                                      






                                                                                                       
                                 


                                                                                               

                                 



                                    
                                                       
                             






                                
                         

                         







                                              


   

                                                                        
                                       
 
                                     
 

                                  
 
                           


                     
                            
                             
                                                     
                                                                                            






                                                                                    
                                                                                                                                 



                                                  
                                                  
                 
                                                                                                                      



                                                                  

                                                                                        



                    

                                                                 
                                       
                                                                               


                                
                          

                                                                 

                         

                                                                  
                                      
                                                    




                                                                                   

                                                                      
                                              





                                                                                        

                                                                                            






                                                                                                       

                                                                                          
                                                                    





                                                                                                      
                                                                       
                                                          
                         
                                                                                             
                                                           
                 


                                                



                                
                             


                                                                 
                          

                                                                   
                                      


                                                    






                                                                                    
                                                      
                                                                                    
                                                                                             

                                                                                                         





                                                                   



                                
                              


                                                                 


                                                                   
                                      





                                                                                      
                                                   


                                                                               
                                                      

                                                                                                    
                                                      
                                                                                    
                                                                                             






                                                                                                            
                                                              
                                                                                 






                                                                                                            
                                                                      




                                                



                                
                         


                                                                 


                                                                                     
                                      

                                        
                                                    
                                                                                 
                          
                                     
                                      


                                                       
                              

                      

                         
                               
                 

                                                             
                                                                 


                                      
                                              
                                      
 
                                                                     
                         





                                                                         



                                                                              
                                                                           
                                 
                                                                               
                                                                         


                                                                                              
                                                                       
                                 
                                    



                                                                          
                                                                       
                                                 
                                                                                            
                                                 
                                                    






                                                                                                    
                                                                                            
                                                 

                                            
                                                                                   
                                 

                                                 
                                       
                                                                   

                    
                                                      
 

                      

                        



                                                             
                                                                 









                                                                                    



                                                                            


                                                              
                                                              

                                                                                                                               



                                                                                                             



                                                                                                                                



                                                                                                      




                                                                
                                                                       

                    
                                                      
 

                      
                
                                                                                     
                                                  


                      




                                                
                                                                                 



                               
                            

 
                            
 
                              
 


                                       
                                  



                                                        
                                                             

                            
                                                                     
         
                                             

                                   
 
                       
                           

                                



                                                                        
 
                                                   


                                                                        
                                        

                                                                                
 
                        
                 

                                                              
                                                
 
                                    
                                                                      
                                
                         
                                                                               
                                             
                         
 
                                                             

                                                                     

                                                                  
                                                                                           



                                                                                                                           

                                                                                                            
 
                                                               
                                             
                                                                                   
 
                                       




                                                                       
                         

                                                                  
                                                
                                        




                                                                           




                                                                                              

                                                                    

                                        
                                                                  


                                                                       
                         






                                                                                     
                                       










                                                                       
                                                
                                        
                                                                            
                                                                            
                                            
                                                                                 
                                    
                                                                   
                                                                                               
                                 
                                                                                                                                                               
                                                         
                                                        


                                                                                            





                                                                           
                                                         
                                 

                                                 
                                                        
                                                                  

                                  


                                           
                                                
 

                    
                 
                                                                                                      

                 
         
 
                           
 






                                                                          
                                  
         
                                                                               

                         
                                                            

                         
                                             









                               



                                                     
 


                                    
                                                                                            
















                                                                               
                                                           



                                                
                                       



                                                      
/*
 * This file is part of the Distributed Network Block Device 3
 *
 * Copyright(c) 2011-2012 Johann Latocha <johann@latocha.de>
 *
 * 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 <sys/un.h>
#include <stdio.h>
#include <sys/stat.h>
#include <grp.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include "sockhelper.h"

#include <libxml/parser.h>
#include <libxml/xpath.h>
#include "xmlutil.h"

#define RPC_PORT (PORT+1)

#define MAX_SERVER_SOCKETS 50 // Assume there will be no more than 50 sockets the server will listen on
static int server_socks[MAX_SERVER_SOCKETS], server_count = 0;
static volatile int keep_running = 1;
static char *payload = NULL;

#define char_repeat_br(_c, _times) do { \
	int _makro_i_ = (_times); \
	while (--_makro_i_ >= 0) putchar(_c); \
	putchar('\n'); \
} while (0)

static int rpc_receive(int client_sock);
static int get_highest_fd(GSList *sockets);
static int is_password_correct(xmlDocPtr doc);
static int get_terminal_width();
static int rpc_send_reply(int sock, dnbd3_rpc_t* header, int result_code, xmlDocPtr payload);

static int get_highest_fd(GSList *sockets)
{
	int max = 0;

	for (int i = 0; i < server_count; ++i)
	{
		if (server_socks[i] > max)
			max = server_socks[i];
	}

	for (GSList *iterator = sockets; iterator; iterator = iterator->next)
	{
		const int fd = (int)(size_t)iterator->data;
		if (fd > max)
			max = fd;
	}
	//printf("Max fd: %d\n", max);
	return max;
}

void *dnbd3_rpc_mainloop()
{

	// Check version and initialize
	LIBXML_TEST_VERSION

	payload = malloc(MAX_RPC_PAYLOAD);
	if (payload == NULL)
	{
		memlogf("[CRITICAL] Couldn't allocate RPC payload buffer. RPC disabled.");
		pthread_exit((void *)0);
		return NULL;
	}

	struct sockaddr_storage client;

	sock_add_array(sock_listen_any(PF_INET, RPC_PORT), server_socks, &server_count, MAX_SERVER_SOCKETS);
#ifdef WITH_IPV6
	sock_add_array(sock_listen_any(PF_INET6, RPC_PORT), server_socks, &server_count, MAX_SERVER_SOCKETS);
#endif

	// Bind to socket
	if (server_count == 0)
	{
		perror("ERROR: RPC bind/listen unsuccessful");
		exit(EXIT_FAILURE);
	}

	// Run connection-accepting loop

	fd_set all_sockets, readset, exceptset;

	GSList *sockets = NULL, *iterator;

	int client_sock, ret;
	int maxfd = get_highest_fd(sockets);
	int error_count = 0;

	struct timeval select_timeout;

	FD_ZERO(&all_sockets);
	for (int i = 0; i < server_count; ++i)
	{
		FD_SET(server_socks[i], &all_sockets);
		sock_set_nonblock(server_socks[i]);
	}

	while (keep_running)
	{
		readset = exceptset = all_sockets;
		select_timeout.tv_sec = 4;
		select_timeout.tv_usec = 0;
		ret = select(maxfd, &readset, NULL, &exceptset, &select_timeout);
		while (ret > 0)
		{
			for (int i = 0; i < server_count; ++i)
			{
				if (FD_ISSET(server_socks[i], &readset))
				{
					--ret;
					// Accept connection
					socklen_t len = sizeof(client);
					if ((client_sock = accept(server_socks[i], (struct sockaddr *)&client, &len)) < 0)
					{
						if (errno != EAGAIN)
						{
							memlogf("[ERROR] Error accepting an RPC connection");
							if (++error_count > 10)
								goto end_loop;
						}
						continue;
					}
					error_count = 0;
					// Apply read/write timeout
					sock_set_timeout(client_sock, 500);
					// Make new connection blocking
					sock_set_block(client_sock);
					sockets = g_slist_prepend(sockets, (void *)(size_t)client_sock);
					if (client_sock >= maxfd)
						maxfd = client_sock + 1;
					//printf("Max fd: %d\n", (maxfd-1));
					FD_SET(client_sock, &all_sockets);
				}
				if (FD_ISSET(server_socks[i], &exceptset))
				{
					--ret;
					memlogf("[ERROR] An exception occurred on the RPC listening socket.");
					if (++error_count > 10)
						goto end_loop;
				}
			}
			if (ret > 0)
			{
				// Must be an active RPC connection
				int del = -1;
				for (iterator = sockets; iterator; iterator = iterator->next)
				{
					if (del != -1)
					{
						// Delete a previously closed connection from list (delayed, otherwise list might get messed up)
						sockets = g_slist_remove(sockets, (void *)(size_t)del);
						del = -1;
						maxfd = get_highest_fd(sockets) + 1;
					}
					client_sock = (int)(size_t)iterator->data;
					if (FD_ISSET(client_sock, &readset))
					{
						--ret;
						// Client sending data
						if (!rpc_receive(client_sock))
						{
							// Connection has been closed
							close(client_sock);
							del = client_sock;
							FD_CLR(client_sock, &all_sockets);
						}
					}
					if (FD_ISSET(client_sock, &exceptset))
					{
						--ret;
						// Something unexpected happened, just close connection
						close(client_sock);
						del = client_sock;
						FD_CLR(client_sock, &all_sockets);
					}
				}
				if (del != -1)
				{
					// In case last socket was closed during iteration
					sockets = g_slist_remove(sockets, (void *)(size_t)del);
					maxfd = get_highest_fd(sockets) + 1;
				}
			}
		} // End select loop
	} // End mainloop

end_loop:
	memlogf("[INFO] Shutting down RPC interface.");
	dnbd3_rpc_shutdown();

	free(payload);
	xmlCleanupParser();
	pthread_exit((void *)0);
	return NULL;
}

void dnbd3_rpc_shutdown()
{
	keep_running = 0;
	for (int i = 0; i < server_count; ++i)
	{
		if (server_socks[i] == -1)
			continue;
		close(server_socks[i]);
		server_socks[i] = -1;
	}
	server_count = 0;
}

/**
 * Returns !=0 if send/recv successful, 0 on any kind of network failure
 */
static int rpc_receive(int client_sock)
{
	GSList *iterator, *iterator2;

#define STRBUFLEN 100
	char strbuffer[STRBUFLEN];

	dnbd3_rpc_t header;

	uint32_t cmd;

	int ret, locked = 0;
	int return_value = 0;
	xmlDocPtr docReply = NULL, docRequest = NULL;
	xmlNodePtr root_node, parent_node, tmp_node, log_parent_node, log_node, server_node;

	ret = recv(client_sock, &header, sizeof(header), MSG_WAITALL);
	if (ret != sizeof(header))
		return ((ret < 0 && errno == EAGAIN) ? 1 : 0);
	cmd = ntohl(header.cmd); // Leave header.cmd in network byte order for reply
	header.size = ntohl(header.size);

	int rpc_error = ERROR_UNSPECIFIED_ERROR; // Default value of error, so remember to set it for the reply if call succeeded

	if (header.size != 0)
	{
		// Message has payload, receive it
		if (header.size > MAX_RPC_PAYLOAD)
		{
			memlogf("[WARNING] RPC command with payload of %u bytes ignored.", (unsigned int)header.size);
			return 0;
		}
		if (!recv_data(client_sock, payload, header.size))
			return 0;

		docRequest = xmlReadMemory(payload, header.size, "noname.xml", NULL, 0);
	}

	switch (cmd)
	{
	case RPC_EXIT:
		memlogf("[INFO] Server shutdown by RPC request");
		header.size = ntohl(0);
		return_value = send_data(client_sock, &header, sizeof(header));
		dnbd3_cleanup();
		break;

	case RPC_IMG_LIST:
		if (!createXmlDoc(&docReply, &root_node, "data"))
			goto case_end;

		// Images
		parent_node = xmlNewNode(NULL, BAD_CAST "images");
		if (parent_node == NULL)
			goto case_end;
		xmlAddChild(root_node, parent_node);
		locked = 1;
		pthread_spin_lock(&_spinlock);
		for (iterator = _dnbd3_images; iterator; iterator = iterator->next)
		{
			const dnbd3_image_t *image = iterator->data;
			tmp_node = xmlNewNode(NULL, BAD_CAST "image");
			if (tmp_node == NULL)
				goto case_end;
			xmlNewProp(tmp_node, BAD_CAST "name", BAD_CAST image->low_name);
			xmlAddDecimalProp(image->rid, tmp_node, "rid");
			xmlAddDecimalProp(image->atime, tmp_node, "atime");
			xmlAddDecimalProp(image->delete_soft, tmp_node, "softdelete");
			xmlAddDecimalProp(image->delete_hard, tmp_node, "harddelete");
			xmlAddDecimalProp(image->filesize, tmp_node, "size");
			if (image->file)
				xmlNewProp(tmp_node, BAD_CAST "file", BAD_CAST image->file);
			if (image->cache_file && image->cache_map)
			{
				xmlNewProp(tmp_node, BAD_CAST "cachefile", BAD_CAST image->cache_file);
				int i, complete = 0, size = IMGSIZE_TO_MAPBYTES(image->filesize);
				for (i = 0; i < size; ++i)
					if (image->cache_map[i])
						complete += 100;
				xmlAddDecimalProp(complete / size, tmp_node, "cachefill");
			}
			// Build space separated list of alt servers
			int i;
			char serverstr[1000] = {0}, target[100];
			for (i = 0; i < NUMBER_SERVERS; ++i)
			{
				if (image->servers[i].host.type == 0) continue;
				if (!host_to_string(&(image->servers[i].host), target, 100)) continue;
				if (*serverstr) strcat(serverstr, " ");
				strcat(serverstr, target);
			}
			xmlNewProp(tmp_node, BAD_CAST "servers", BAD_CAST serverstr); // TODO
			xmlAddChild(parent_node, tmp_node);
		}
		pthread_spin_unlock(&_spinlock);
		locked = 0;

		// Dump and send
		rpc_error = 0;
		break;

	case RPC_CLIENT_LIST:
		if (!createXmlDoc(&docReply, &root_node, "data"))
			goto case_end;

		// Clients
		parent_node = xmlNewNode(NULL, BAD_CAST "clients");
		if (parent_node == NULL)
			goto case_end;
		xmlAddChild(root_node, parent_node);
		locked = 1;
		pthread_spin_lock(&_spinlock);
		for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
		{
			dnbd3_client_t *client = iterator->data;
			if (client->image)
			{
				tmp_node = xmlNewNode(NULL, BAD_CAST "client");
				if (tmp_node == NULL)
					goto case_end;
				host_to_string(&client->host, strbuffer, STRBUFLEN);
				xmlNewProp(tmp_node, BAD_CAST "address", BAD_CAST strbuffer);
				xmlNewProp(tmp_node, BAD_CAST "image", BAD_CAST client->image->low_name);
				xmlAddDecimalProp(client->image->rid, tmp_node, "rid");
				xmlAddChild(parent_node, tmp_node);
			}
		}
		pthread_spin_unlock(&_spinlock);
		locked = 0;

		// Dump and send
		rpc_error = 0;
		break;

	case RPC_TRUSTED_LIST:
		if (!createXmlDoc(&docReply, &root_node, "data"))
			goto case_end;

		// Trusted servers
		parent_node = xmlNewNode(NULL, BAD_CAST "trusted");
		if (parent_node == NULL)
			goto case_end;
		xmlAddChild(root_node, parent_node);
		locked = 1;
		pthread_spin_lock(&_spinlock);
		for (iterator = _trusted_servers; iterator; iterator = iterator->next)
		{
			dnbd3_trusted_server_t *server = iterator->data;
			if (server->host.type != 0)
			{
				tmp_node = xmlNewNode(NULL, BAD_CAST "server");
				if (tmp_node == NULL)
					goto case_end;
				xmlNodePtr namespace_root = xmlNewNode(NULL, BAD_CAST "namespaces");
				if (namespace_root == NULL)
					goto case_end;
				host_to_string(&server->host, strbuffer, STRBUFLEN);
				xmlNewProp(tmp_node, BAD_CAST "address", BAD_CAST strbuffer);
				if (server->comment)
					xmlNewProp(tmp_node, BAD_CAST "comment", BAD_CAST server->comment);
				for (iterator2 = server->namespaces; iterator2; iterator2 = iterator2->next)
				{
					const dnbd3_namespace_t *ns = iterator2->data;
					server_node = xmlNewNode(NULL, BAD_CAST "namespace");
					if (server_node == NULL)
						goto case_end;
					xmlAddChild(namespace_root, server_node);
					xmlNewProp(server_node, BAD_CAST "name", BAD_CAST ns->name);
					if (ns->auto_replicate)
						xmlNewProp(server_node, BAD_CAST "replicate", BAD_CAST "1");
					if (ns->recursive)
						xmlNewProp(server_node, BAD_CAST "recursive", BAD_CAST "1");
				}
				xmlAddChild(parent_node, tmp_node);
				xmlAddChild(tmp_node, namespace_root);
			}
		}
		pthread_spin_unlock(&_spinlock);
		locked = 0;

		// Dump and send
		rpc_error = 0;
		break;

	case RPC_GET_LOG:
		if (!createXmlDoc(&docReply, &root_node, "data"))
			goto case_end;

		// Log
		log_parent_node = xmlNewChild(root_node, NULL, BAD_CAST "log", NULL);
		if (log_parent_node == NULL)
			goto case_end;
		char *log = fetchlog(0);
		if (log == NULL)
			log = strdup("LOG IS NULL");
		log_node = xmlNewCDataBlock(docReply, BAD_CAST log, strlen(log));
		free(log);
		if (log_node == NULL)
			goto case_end;
		xmlAddChild(log_parent_node, log_node);

		// Dump and send
		rpc_error = 0;
		break;

	case RPC_ADD_IMG:
	case RPC_DEL_IMG:
		if (docRequest)
		{
			if (!is_password_correct(docRequest))
			{
				rpc_error = ERROR_WRONG_PASSWORD;
				break;
			}

			xmlNodePtr cur = NULL;
			int count = 0;

			FOR_EACH_NODE(docRequest, "/data/image", cur)
			{
				if (cur->type != XML_ELEMENT_NODE)
					continue;
				NEW_POINTERLIST;
				++count;
				dnbd3_image_t image;
				memset(&image, 0, sizeof(dnbd3_image_t));
				image.config_group = XML_GETPROP(cur, "name");
				char *rid_str = XML_GETPROP(cur, "rid");
				image.file = XML_GETPROP(cur, "file");
				image.cache_file = XML_GETPROP(cur, "cache");
				if (image.file && !file_exists(image.file))
				{
					printf("Image File: %s\n", image.file);
					rpc_error = ERROR_FILE_NOT_FOUND;
				}
				else if (image.cache_file && !file_writable(image.cache_file))
				{
					rpc_error = ERROR_NOT_WRITABLE;
				}
				else
				{
					if (image.config_group && rid_str)
					{
						image.rid = atoi(rid_str);
						if (cmd == RPC_ADD_IMG)
						{
							rpc_error = dnbd3_add_image(&image);
						}
						else
						{
							char *soft = XML_GETPROP(cur, "softdelete");
							char *hard = XML_GETPROP(cur, "harddelete");
							image.delete_soft = time(NULL);
							image.delete_hard = time(NULL);
							if (soft) image.delete_soft += atoi(soft);
							if (hard) image.delete_hard += atoi(hard);
							rpc_error = dnbd3_del_image(&image);
						}
					}
					else
						rpc_error = ERROR_MISSING_ARGUMENT;
				}
				FREE_POINTERLIST;
			} END_FOR_EACH;
			if (count == 0)
				rpc_error = ERROR_MISSING_ARGUMENT;
		}
		else
			rpc_error = ERROR_INVALID_XML;

		break;

	case RPC_ADD_NS:
	case RPC_DEL_NS:
		if (docRequest)
		{
			if (!is_password_correct(docRequest))
			{
				rpc_error = ERROR_WRONG_PASSWORD;
				break;
			}

			xmlNodePtr cur = NULL;

			FOR_EACH_NODE(docRequest, "/data/namespaces/namespace", cur)
			{
				if (cur->type != XML_ELEMENT_NODE)
					continue;
				NEW_POINTERLIST;
				char *host = XML_GETPROP(cur, "address");
				char *ns = XML_GETPROP(cur, "name");
				char *flags = XML_GETPROP(cur, "flags");
				char *comment = XML_GETPROP(cur, "comment");
				pthread_spin_lock(&_spinlock);
				if (host && ns)
				{
					if (cmd == RPC_ADD_NS)
					{
						dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(host, TRUE, comment);
						if (server && dnbd3_add_trusted_namespace(server, ns, flags))
							rpc_error = ERROR_OK;
						else
							rpc_error = ERROR_UNSPECIFIED_ERROR;
					}
					else
					{
						dnbd3_trusted_server_t *server = dnbd3_get_trusted_server(host, FALSE, comment);
						if (server && dnbd3_del_trusted_namespace(server, ns))
							rpc_error = ERROR_OK;
						else
							rpc_error = ERROR_FILE_NOT_FOUND;
					}
				}
				pthread_spin_unlock(&_spinlock);
				FREE_POINTERLIST;
			} END_FOR_EACH;
			if (rpc_error == ERROR_OK) dnbd3_save_config();
		}
		else
			rpc_error = ERROR_INVALID_XML;

		break;

	default:
		memlogf("[ERROR] Unknown RPC command: %u", (unsigned int)header.cmd);
		rpc_error = ERROR_UNKNOWN_COMMAND;
		break;

	}
case_end:

	if (locked)
		pthread_spin_unlock(&_spinlock);
	// Send reply
	return_value = rpc_send_reply(client_sock, &header, rpc_error, docReply);

	xmlFreeDoc(docReply);
	xmlFreeDoc(docRequest);

	return return_value;
}

void dnbd3_rpc_send(int cmd)
{
	int client_sock, size;

	// Check version and initialize
	LIBXML_TEST_VERSION

	struct sockaddr_in server;

	memset(&server, 0, sizeof(server));
	server.sin_family = AF_INET; // IPv4
	server.sin_addr.s_addr = inet_addr("127.0.0.1");
	server.sin_port = htons(RPC_PORT); // set port number

	// Connect to server
	if ((client_sock = sock_connect4(&server, 2000, 1000)) == -1)
	{
		perror("ERROR: RPC connect");
		exit(EXIT_FAILURE);
	}

	// Send message
	dnbd3_rpc_t header;
	header.cmd = htonl(cmd);
	header.size = 0;
	send(client_sock, (char *)&header, sizeof(header), MSG_WAITALL);
	recv(client_sock, &header, sizeof(header), MSG_WAITALL);
	header.cmd = ntohl(header.cmd);
	header.size = ntohl(header.size);

	if (cmd == RPC_IMG_LIST && header.size > 0)
	{
		char *buf = malloc(header.size + 1);
		size = recv(client_sock, buf, header.size, MSG_WAITALL);
		printf("\n%s\n\n", buf);
		xmlDocPtr doc = xmlReadMemory(buf, size, "noname.xml", NULL, 0);
		buf[header.size] = 0;

		if (doc)
		{
			int count;
			int term_width = get_terminal_width();
			xmlNodePtr cur, childit;

			// Print log
			char *log = getTextFromPath(doc, "/data/log");
			if (log)
			{
				printf("--- Last log lines ----\n%s\n\n", log);
				xmlFree(log);
			}

			int watime = 17, wname = 0, wrid = 5;
			FOR_EACH_NODE(doc, "/data/images/image", cur)
			{
				if (cur->type != XML_ELEMENT_NODE)
					continue;
				NEW_POINTERLIST; // This macro defines an array of pointers
				char *vid = XML_GETPROP(cur, "name"); // XML_GETPROP is a macro wrapping xmlGetNoNsProp()
				char *rid = XML_GETPROP(cur, "rid"); // Each of these calls allocates memory for the string
				wname = MAX(wname, strlen(vid));
				wrid = MAX(wrid, strlen(rid));
				FREE_POINTERLIST; // This macro simply frees all pointers in the above array
			} END_FOR_EACH;

			char format[100], strbuffer[STRBUFLEN];
			snprintf(format, 100,
			         "%%-%ds %%-%ds %%%ds %%s\n", watime, wname, wrid);

			// Print images
			printf("Exported images\n");
			printf(format, "atime", "name", "rid", "file");
			char_repeat_br('=', term_width);
			count = 0;
			FOR_EACH_NODE(doc, "/data/images/image", cur)
			{
				if (cur->type != XML_ELEMENT_NODE)
					continue;
				NEW_POINTERLIST;
				++count;
				char *numatime = XML_GETPROP(cur, "atime");
				char *vid = XML_GETPROP(cur, "name");
				char *rid = XML_GETPROP(cur, "rid");
				char *file = XML_GETPROP(cur, "file");
				time_t at = (time_t)atol(numatime);
				struct tm *timeinfo = localtime(&at);
				strftime(strbuffer, STRBUFLEN, "%d.%m.%y %H:%M:%S", timeinfo);
				printf(format, strbuffer, vid, rid, file);
				FREE_POINTERLIST;
			} END_FOR_EACH;
			char_repeat_br('=', term_width);
			printf("\nNumber of images: %d\n\n", count);

			// Print clients
			printf("Connected clients (ip, file):\n");
			char_repeat_br('=', term_width);
			count = 0;
			FOR_EACH_NODE(doc, "/data/clients/client", cur)
			{
				if (cur->type != XML_ELEMENT_NODE)
					continue;
				++count;
				xmlChar *ip = xmlGetNoNsProp(cur, BAD_CAST "ip");
				xmlChar *file = xmlGetNoNsProp(cur, BAD_CAST "file");
				printf("%-40s %s\n", ip, file);
				// Too lazy to free vars, client will exit anyways
			} END_FOR_EACH;
			char_repeat_br('=', term_width);
			printf("\nNumber clients: %d\n\n", count);

			// Print trusted servers
			printf("Trusted servers:\n");
			char_repeat_br('=', term_width);
			count = 0;
			FOR_EACH_NODE(doc, "/data/trusted/server", cur)
			{
				if (cur->type != XML_ELEMENT_NODE)
					continue;
				NEW_POINTERLIST;
				++count;
				char *address = XML_GETPROP(cur, "address");
				char *comment = XML_GETPROP(cur, "comment");
				if (comment)
					printf("%-30s (%s)\n", address, comment);
				else
					printf("%-30s\n", address);
				for (childit = cur->children; childit; childit = childit->next)
				{
					if (childit->type != XML_ELEMENT_NODE || childit->name == NULL || strcmp((const char*)childit->name, "namespace") != 0)
						continue;
					NEW_POINTERLIST;
					char *name = XML_GETPROP(childit, "name");
					char *replicate = XML_GETPROP(childit, "replicate");
					char *recursive = XML_GETPROP(childit, "recursive");
					printf("     %-40s ", name);
					if (replicate && *replicate != '0')
						printf(" replicate");
					if (recursive && *recursive != '0')
						printf(" recursive");
					putchar('\n');
					FREE_POINTERLIST;
				}
				FREE_POINTERLIST;
			} END_FOR_EACH;
			char_repeat_br('=', term_width);
			printf("\nNumber servers: %d\n\n", count);

			// Cleanup
			xmlFreeDoc(doc);
			xmlCleanupParser();

//			xmlDocDump(stdout, doc);

		}
		else
		{
			printf("ERROR: Failed to parse reply\n-----------\n%s\n-------------\n", buf);
		}

	}

	close(client_sock);
}

/**
 * Check if the correct server password is present in xpath /data/password
 * return !=0 if correct, 0 otherwise
 */
static int is_password_correct(xmlDocPtr doc)
{
	if (_rpc_password == NULL)
	{
		memlogf("[WARNING] RPC access granted as no password is set!");
		return 1;
	}
	char *pass = getTextFromPath(doc, "/data/password");
	if (pass == NULL)
		return 0;
	if (strcmp(pass, _rpc_password) == 0)
	{
		xmlFree(pass);
		return 1;
	}
	xmlFree(pass);
	return 0;
}

static int get_terminal_width()
{
	struct winsize w;
	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0)
		return 80;
	return w.ws_col;
}

#define RETBUFLEN 8000
static char returnbuffer[RETBUFLEN];
static int rpc_send_reply(int sock, dnbd3_rpc_t* header, int result_code, xmlDocPtr payload)
{
	if (result_code == 0 && payload != NULL)
	{
		// No error
		xmlChar *xmlbuff = NULL;
		int buffersize;
		xmlDocDumpFormatMemory(payload, &xmlbuff, &buffersize, 1);
		header->size = htonl(buffersize);
		if (!send_data(sock, header, sizeof(*header)))
			return FALSE;
		if (xmlbuff)
			return send_data(sock, xmlbuff, buffersize);
		return TRUE;
	}
	// Error code, build xml struct (lazy shortcut)
	int len = snprintf(returnbuffer, RETBUFLEN, "<?xml version=\"1.0\"?>\n"
		"<data>\n"
		"<result retcode=\"%d\" retstr=\"%s\" />\n"
		"</data>", 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);
}