summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorLars Müller2008-03-01 19:30:38 +0100
committerLars Müller2008-03-01 19:30:38 +0100
commit93b9103f7383d400616d222606c294e07b16e1aa (patch)
tree611a39f7bc1d1dd5a4335157ef95c101d64dddc8 /server
downloaddnbd2-93b9103f7383d400616d222606c294e07b16e1aa.tar.gz
dnbd2-93b9103f7383d400616d222606c294e07b16e1aa.tar.xz
dnbd2-93b9103f7383d400616d222606c294e07b16e1aa.zip
Import dnbd* from the former openslx-contrib repo as of revision 92.
openslx-contrib is currently read only and will get removed in some days. git-svn-id: http://svn.openslx.org/svn/openslx/contrib/dnbd2/trunk@1592 95ad53e4-c205-0410-b2fa-d234c58c8868
Diffstat (limited to 'server')
-rw-r--r--server/Makefile30
-rw-r--r--server/config.c213
-rw-r--r--server/config.h13
-rw-r--r--server/file.c65
-rw-r--r--server/file.h28
-rw-r--r--server/main.c227
-rw-r--r--server/query.c74
-rw-r--r--server/query.h14
-rw-r--r--server/tree.c67
-rw-r--r--server/tree.h37
10 files changed, 768 insertions, 0 deletions
diff --git a/server/Makefile b/server/Makefile
new file mode 100644
index 0000000..a41d6da
--- /dev/null
+++ b/server/Makefile
@@ -0,0 +1,30 @@
+#
+# server/Makefile
+#
+
+CPPFLAGS = -I../include
+CFLAGS = -Wall -O2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
+VPATH = ../include
+APP = dnbd2-server
+
+all: $(APP)
+
+install: $(APP)
+ cp $(APP) /usr/local/sbin
+
+dnbd2-server: main.o config.o query.o tree.o file.o
+ $(CC) -o $@ $^
+
+main.o: config.h query.h tree.h file.h dnbd2.h
+
+config.o: config.h tree.h file.h dnbd2.h
+
+query.o: query.h tree.h file.h dnbd2.h
+
+tree.o: tree.h dnbd2.h
+
+file.o: file.h
+
+.PHONY:
+clean:
+ $(RM) *.o *~ $(APP)
diff --git a/server/config.c b/server/config.c
new file mode 100644
index 0000000..5dec41e
--- /dev/null
+++ b/server/config.c
@@ -0,0 +1,213 @@
+/*
+ * server/config.c
+ */
+
+
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdio.h>
+#include "dnbd2.h"
+#include "tree.h"
+#include "file.h"
+#include "config.h"
+
+
+char *get_line(char *buf, FILE *file);
+dataset_t *parse_dataset_file(const char *filename);
+int parse_server(dnbd2_server_t *server, const char *str);
+
+
+/*
+ * The config file looks like this:
+ *
+ * 192.168.178.120
+ * 5000
+ * /etc/dnbd2/datasets/debian-3.1 192.168.178.119:5005
+ * /etc/dnbd2/datasets/suse-10.2 192.168.178.119:5005 192.168.178.118:5005
+ * ...
+ *
+ */
+int parse_config_file(const char *filename, struct sockaddr_in *sockaddr,
+ void **tree)
+{
+ char buf[LINE_SIZE_MAX];
+ FILE *file;
+
+ bzero(sockaddr, sizeof(sockaddr));
+ sockaddr->sin_family = AF_INET;
+
+ /* Open file. */
+ file = fopen(filename, "r");
+ if (!file) {
+ syslog(LOG_ERR, "Could not open config file %s", filename);
+ return -1;
+ }
+ syslog(LOG_NOTICE, "Config file: %s", filename);
+
+ /* Read IP from file. */
+ if (!get_line(buf, file) || !inet_aton(buf, &(sockaddr->sin_addr))) {
+ syslog(LOG_ERR, "Could not read IP.");
+ return -1;
+ }
+
+ /* Read port number from file. */
+ if (!get_line(buf, file) ||
+ sscanf(buf, "%hu", &(sockaddr->sin_port)) != 1) {
+ syslog(LOG_ERR, "Could not read port number.");
+ return -1;
+ }
+ sockaddr->sin_port = htons(sockaddr->sin_port);
+
+ syslog(LOG_NOTICE, "IP = %s, Port = %hu",
+ inet_ntoa(sockaddr->sin_addr), ntohs(sockaddr->sin_port));
+
+ int datasets = 0;
+ char server[ALT_SERVERS_MAX][LINE_SIZE_MAX];
+ char dsfile[LINE_SIZE_MAX];
+ while (get_line(buf, file)) {
+
+ int ret = sscanf(buf, "%s %s %s %s %s",
+ dsfile,
+ server[0],
+ server[1],
+ server[2],
+ server[3]) - 1;
+
+ /* Parse a dataset file and put it into a tree-node. */
+ dataset_t *dataset = parse_dataset_file(dsfile);
+ if (!dataset)
+ goto out_nodsfile;
+ node_t *data = (node_t *)malloc(sizeof(node_t));
+ if (!data) {
+ syslog(LOG_ERR,
+ "Could not allocate memory for new Dataset.");
+ goto out_nodataset;
+ }
+ int fd = file_open(dataset->path);
+ if (fd == -1) {
+ syslog(LOG_ERR,
+ "Could not open file or block device %s",
+ dataset->path);
+ goto out_nodata;
+ }
+ strncpy(data->path, dsfile, FILE_NAME_MAX);
+ data->ds = dataset;
+ data->fd = fd;
+
+ /* Parse the list of alterntive servers. */
+ int i;
+ int cnt = 0;
+ for (i=0 ; i<ret ; i++)
+ cnt += parse_server(&data->server[cnt], server[i]);
+ data->servers = cnt;
+
+ /* Check if the Volume-ID-Release-ID pair is already in use. */
+ node_t *data2 = tree_find(data, tree);
+ if (data2) {
+ syslog(LOG_ERR,
+ "Vol-ID/Rel-ID already used in Dataset file %s",
+ data2->path);
+ goto out_nodata;
+ }
+
+ /* Insert Dataset into tree. */
+ if (tree_insert(data, tree) == -1) {
+ syslog(LOG_ERR, "Could not insert Dataset into tree.");
+ goto out_nodata;
+ }
+
+ datasets++;
+ continue;
+
+ out_nodata:
+ free(data);
+ out_nodataset:
+ free(dataset);
+ out_nodsfile:
+ syslog(LOG_ERR, "Problem parsing %s", dsfile);
+ }
+
+ syslog(LOG_NOTICE, "Loaded %d Dataset(s).", datasets);
+
+ fclose(file);
+ return datasets;
+}
+
+
+/*
+ * A dataset config file looks like this:
+ *
+ * /path/to/file/or/block/device
+ * Volume-ID
+ * Release-ID
+ */
+dataset_t *parse_dataset_file(const char *filename)
+{
+ char buf[LINE_SIZE_MAX];
+
+ FILE *file = fopen(filename, "r");
+ if (!file) {
+ syslog(LOG_ERR, "Could not open Dataset file %s", filename);
+ return NULL;
+ }
+
+ dataset_t *dataset = (dataset_t *) malloc(sizeof(dataset_t));
+ if (!dataset) {
+ syslog(LOG_ERR, "Could not allocate memory for new Dataset.");
+ return NULL;
+ }
+
+ /* Read file- or block device name from file. */
+ if (!get_line(dataset->path, file)) {
+ syslog(LOG_ERR, "Could not read path to file or block device");
+ return NULL;
+ }
+
+ /* Read Volume-ID from file. */
+ if (!get_line(buf, file) || sscanf(buf, "%hu", &(dataset->vid)) != 1) {
+ syslog(LOG_ERR, "Could not read Volume-ID.");
+ return NULL;
+ }
+
+ /* Read Release-ID from file. */
+ if (!get_line(buf, file) || sscanf(buf, "%hu", &(dataset->rid)) != 1) {
+ syslog(LOG_ERR, "Could not read Release-ID.");
+ return NULL;
+ }
+
+ fclose(file);
+ return dataset;
+}
+
+
+char *get_line(char *buf, FILE *file)
+{
+ char *ret = fgets(buf, LINE_SIZE_MAX, file);
+
+ /* change \n with \0 */
+ if (ret)
+ buf[strlen(buf)-1] = '\0';
+
+ return ret;
+}
+
+
+int parse_server(dnbd2_server_t *server, const char *str)
+{
+ char ip[LINE_SIZE_MAX];
+ uint16_t port;
+ struct in_addr tmp;
+
+ if (sscanf(str, "%[^:]:%hu", ip, &port) != 2)
+ return 0;
+
+ if (!inet_aton(ip, &tmp))
+ return 0;
+
+ memcpy(&server->ip, &tmp, sizeof(uint32_t));
+ server->port = htons(port);
+
+ return 1;
+}
diff --git a/server/config.h b/server/config.h
new file mode 100644
index 0000000..dc9cff1
--- /dev/null
+++ b/server/config.h
@@ -0,0 +1,13 @@
+/*
+ * server/config.h
+ */
+
+
+/*
+ * Parse the server configuration file, create a socket address
+ * structure and a tree of datasets.
+ *
+ * Returns: Number of Datasets loaded or -1 on failure.
+ */
+int parse_config_file(const char *filename, struct sockaddr_in *sockaddr,
+ void **tree);
diff --git a/server/file.c b/server/file.c
new file mode 100644
index 0000000..26c3cd0
--- /dev/null
+++ b/server/file.c
@@ -0,0 +1,65 @@
+/*
+ * server/file.c
+ */
+
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "file.h"
+
+
+int file_open(char *filename)
+{
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ struct stat st;
+ if (fstat(fd, &st) == -1)
+ return -1;
+
+ return fd;
+}
+
+
+int file_getsize(int fd, off_t *size)
+{
+ *size = lseek64(fd, 0, SEEK_END);
+
+ if (*size == -1)
+ return -1;
+
+ return 0;
+}
+
+
+int file_read(int fd, void *buf, size_t size, off_t pos)
+{
+ off_t newpos = lseek(fd, pos, SEEK_SET);
+
+ if (newpos == -1)
+ return -1;
+
+ size_t nleft = size;
+ ssize_t nread;
+ char *ptr = buf;
+
+ while (nleft > 0) {
+ if ((nread = read(fd, ptr, nleft)) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return -1;
+ }
+
+ if (nread == 0) {
+ break;
+ }
+
+ nleft -= nread;
+ ptr += nread;
+ }
+
+ return 0;
+}
diff --git a/server/file.h b/server/file.h
new file mode 100644
index 0000000..720cf9b
--- /dev/null
+++ b/server/file.h
@@ -0,0 +1,28 @@
+/*
+ * server/file.h - Functions to work with files.
+ */
+
+
+/*
+ * Open a file and test it with stat.
+ *
+ * Returns: File descriptor on success or -1 on failure.
+ */
+int file_open(char *filename);
+
+
+/*
+ * Store in @size the size in bytes of the file pointed to by @fd.
+ *
+ * Returns: 0 on success -1 on failure;
+ *
+ */
+int file_getsize(int fd, off_t *size);
+
+
+/*
+ * Copy @size bytes of @fd, starting at @pos, into @buf.
+ *
+ * Returns: 0 on success -1 on failure.
+ */
+int file_read(int fd, void *buf, size_t size, off_t pos);
diff --git a/server/main.c b/server/main.c
new file mode 100644
index 0000000..5a923a9
--- /dev/null
+++ b/server/main.c
@@ -0,0 +1,227 @@
+/*
+ * server/main.c
+ */
+
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "dnbd2.h"
+#include "tree.h"
+#include "file.h"
+#include "config.h"
+#include "query.h"
+
+
+char *cfile = NULL; /* path to config file */
+void *tree = NULL; /* tree containing datasets */
+int server_fd; /* file descriptor for UPD communication */
+struct sockaddr_in server_addr;
+
+
+/*
+ * Flags and signal handlers.
+ */
+int sig_exit, sig_config;
+void exit_handler(int sig);
+void config_handler(int sig);
+
+
+/*
+ * Function to configure server. We call it with LOAD on
+ * initialization and with RELOAD when SIGHUP is caught.
+ */
+enum config_action { LOAD, RELOAD };
+int configure(enum config_action action);
+
+
+void print_usage(void);
+int daemon_init(void);
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+ ssize_t n;
+ socklen_t len;
+ dnbd2_data_reply_t reply;
+ dnbd2_data_request_t request;
+ struct sockaddr_in client_addr;
+ struct sigaction exit_act, config_act;
+
+ if (argc != 2) {
+ print_usage();
+ return -1;
+ }
+
+ cfile = argv[1];
+ openlog(argv[0], LOG_PID, LOG_LOCAL2);
+ syslog(LOG_NOTICE, "Starting DNBD2 Server.");
+
+ /* Load datasets and bind to socket. */
+ if (configure(LOAD) == -1)
+ goto out_no_start;
+
+ /* Daemonize. */
+ ret = daemon_init();
+ if (ret == -1) {
+ syslog(LOG_ERR, "Could not fork and background.");
+ goto out_no_start;
+ }
+
+ /* Setup signal handlers. */
+ sigaction(SIGTERM, NULL, &exit_act);
+ exit_act.sa_flags &= ~SA_RESTART;
+ exit_act.sa_handler = exit_handler;
+
+ sigaction(SIGHUP, NULL, &config_act);
+ config_act.sa_flags &= ~SA_RESTART;
+ config_act.sa_handler = config_handler;
+
+ sigaction(SIGTERM, &exit_act, NULL);
+ sigaction(SIGINT, &exit_act, NULL);
+ sigaction(SIGHUP, &config_act, NULL);
+
+ while (1) {
+ if (sig_exit) {
+ syslog(LOG_NOTICE, "Stopping Server.");
+ exit(0);
+ }
+ if (sig_config) {
+ syslog(LOG_NOTICE, "Reloading configuration.");
+ if (configure(RELOAD) == -1)
+ syslog(LOG_ERR, "Not using new configuration.");
+ sig_config = 0;
+ }
+
+ /* Receive request. */
+ len = sizeof(client_addr);
+ n = recvfrom(server_fd, &request, sizeof(request), 0,
+ &client_addr, &len);
+
+ if (n == -1 || n != sizeof(request))
+ continue;
+
+ /* Process request. */
+ ret = handle_query(&request, &reply, &tree);
+ if (ret == -1)
+ continue;
+
+ /* Send reply. */
+ sendto(server_fd, &reply, sizeof(reply), 0, &client_addr, len);
+ }
+
+ return 0;
+
+ out_no_start:
+ syslog(LOG_ERR, "Server not started.");
+ fprintf(stderr, "Server not started - "
+ "consult your syslog for errors.\n");
+ return -1;
+}
+
+
+void config_handler(int sig)
+{
+ sig_config = 1;
+}
+
+
+void exit_handler(int sig)
+{
+ sig_exit = 1;
+}
+
+
+int cmp_addr(struct sockaddr_in addr1, struct sockaddr_in addr2)
+{
+ int diff = memcmp(&addr1.sin_addr.s_addr,
+ &addr2.sin_addr.s_addr,
+ sizeof(in_addr_t));
+
+ if (diff)
+ return diff;
+
+ return memcmp(&addr1.sin_port,
+ &addr2.sin_port,
+ sizeof(in_port_t));
+}
+
+
+int configure(enum config_action action)
+{
+ void *tmp_tree = NULL;
+ int datasets, ret, tmp_fd = server_fd;
+ struct sockaddr_in tmp_addr;
+
+ datasets = parse_config_file(cfile, &tmp_addr, &tmp_tree);
+ if (datasets <= 0)
+ return -1;
+
+ /* Create a socket and bind to it. */
+ if (action == LOAD || cmp_addr(tmp_addr, server_addr)) {
+ tmp_fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (tmp_fd == -1) {
+ syslog(LOG_ERR, "Could not create socket.");
+ return -1;
+ }
+
+ ret = bind(tmp_fd, (struct sockaddr *) &tmp_addr,
+ sizeof(tmp_addr));
+
+ if (ret == -1) {
+ close(tmp_fd);
+ syslog(LOG_ERR, "Could not assign name to socket.");
+ return -1;
+ }
+ }
+
+ /* Make new socket available and close the old one if necesary. */
+ if (action == RELOAD && cmp_addr(tmp_addr, server_addr)) {
+ if (close(server_fd) == -1 ) {
+ syslog(LOG_ERR, "Could not close socket.");
+ }
+ }
+
+ server_fd = tmp_fd;
+ server_addr = tmp_addr;
+
+ tree_destroy(tree);
+ tree = tmp_tree;
+ return 0;
+}
+
+
+void print_usage(void)
+{
+ printf("usage: dnbd2-dserver config-file\n");
+}
+
+
+int daemon_init(void)
+{
+ pid_t pid = fork();
+
+ if (pid == -1)
+ return pid;
+
+ if (pid != 0) {
+ /* We are the parent. */
+ exit(0);
+ }
+
+ /* We are the child. */
+ setsid();
+ chdir("/");
+ umask(0);
+
+ return 0;
+}
diff --git a/server/query.c b/server/query.c
new file mode 100644
index 0000000..16f48b6
--- /dev/null
+++ b/server/query.c
@@ -0,0 +1,74 @@
+/*
+ * server/query.c
+ */
+
+
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <byteswap.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "dnbd2.h"
+#include "query.h"
+#include "tree.h"
+#include "file.h"
+
+
+int handle_query(dnbd2_data_request_t *request,
+ dnbd2_data_reply_t *reply,
+ void **tree)
+{
+ int fd, i;
+ node_t node1;
+ node_t *node2;
+ dataset_t ds;
+ off_t size, pos;
+ uint16_t cmd;
+
+ /* Fetch the right fd for this vid/rid pair. */
+ ds.vid = ntohs(request->vid);
+ ds.rid = ntohs(request->rid);
+ node1.ds = &ds;
+ node2 = tree_find(&node1, tree);
+ if (!node2)
+ return -1;
+ fd = node2->fd;
+
+ cmd = ntohs(request->cmd);
+ switch (cmd) {
+ case CMD_GET_BLOCK:
+ reply->num = request->num;
+ pos = ntohll(request->num);
+ file_read(fd, reply->payload.data, DNBD2_BLOCK_SIZE, pos);
+ break;
+
+ case CMD_GET_SIZE:
+ if (file_getsize(fd, &size) == -1)
+ return -1;
+ reply->num = htonll(size);
+ break;
+
+ case CMD_GET_SERVERS:
+ /* Fetch a random block to deliver a more realistic RTT. */
+ pos = 0;
+ if (!file_getsize(fd, &size))
+ pos = (off_t) (size * (rand() / (RAND_MAX + 1.0)));
+ file_read(fd, reply->payload.data, DNBD2_BLOCK_SIZE, pos);
+ reply->num = htonll(node2->servers);
+ for (i=0 ; i<node2->servers ; i++) {
+ memcpy(&reply->payload.server[i],
+ &node2->server[i],
+ sizeof(dnbd2_server_t));
+ }
+ break;
+ }
+
+ reply->cmd = request->cmd;
+ reply->time = request->time;
+ reply->vid = request->vid;
+ reply->rid = request->rid;
+
+ return 0;
+}
diff --git a/server/query.h b/server/query.h
new file mode 100644
index 0000000..ecf3969
--- /dev/null
+++ b/server/query.h
@@ -0,0 +1,14 @@
+/*
+ * server/query.h
+ */
+
+
+/*
+ * Builds a @reply based on @request, using the datasets stored in
+ * @tree.
+ *
+ * Returns: 0 on success or -1 on failure.
+ */
+int handle_query(dnbd2_data_request_t *request,
+ dnbd2_data_reply_t *reply,
+ void **tree);
diff --git a/server/tree.c b/server/tree.c
new file mode 100644
index 0000000..3daf6df
--- /dev/null
+++ b/server/tree.c
@@ -0,0 +1,67 @@
+/*
+ * server/tree.c
+ */
+
+
+#include <search.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "dnbd2.h"
+#include "tree.h"
+
+
+int compare_node(const void *node1, const void *node2)
+{
+
+ dataset_t *ds1 = ((node_t *) node1)->ds;
+ dataset_t *ds2 = ((node_t *) node2)->ds;
+
+ int diff = ds1->vid - ds2->vid;
+ if (diff != 0)
+ return diff;
+
+ return ds1->rid - ds2->rid;
+}
+
+
+void destroy_node(void *node)
+{
+ free(((node_t *) node)->ds);
+ free((node_t *) node);
+}
+
+
+int tree_insert(node_t *data, void **tree)
+{
+ void *tmp = tsearch((void *)data, tree, compare_node);
+ if (!tmp)
+ return -1;
+
+ node_t *ret = *(node_t **) tmp;
+
+ /* Check if there is another item
+ in the tree with the same key. */
+ if (ret != data)
+ return -1;
+
+ return 0;
+}
+
+
+node_t *tree_find(node_t *data, void **tree)
+{
+ void *tmp = tfind((void *)data, tree, compare_node);
+
+ if (!tmp)
+ return NULL;
+
+ node_t *ret = *(node_t **) tmp;
+
+ return ret;
+}
+
+
+void tree_destroy(void **tree)
+{
+ tdestroy(tree, &destroy_node);
+}
diff --git a/server/tree.h b/server/tree.h
new file mode 100644
index 0000000..e6eea84
--- /dev/null
+++ b/server/tree.h
@@ -0,0 +1,37 @@
+/*
+ * server/tree.h - Mechanism to store Datasets in binary trees.
+ */
+
+
+/*
+ * This structure represents a Dataset along with its config file and
+ * file descriptor. This type of items will be stored in a binary
+ * tree. The key to this structure is the Dataset's Volume-ID and
+ * Release-ID (they must be unique in each server instance).
+ */
+typedef struct node {
+ dataset_t *ds;
+ char path[FILE_NAME_MAX];
+ int fd;
+ int servers;
+ dnbd2_server_t server[ALT_SERVERS_MAX];
+} node_t;
+
+
+/*
+ * Returns: 0 on success or -1 on failure.
+ */
+int tree_insert(node_t *data, void **tree);
+
+
+/*
+ * Returns: Pointer to item on search-hit or NULL on search-miss.
+ */
+node_t *tree_find(node_t *data, void **tree);
+
+
+/*
+ * Free all resources used by the tree. Useful when reloading
+ * datasets.
+ */
+void tree_destroy(void **tree);