summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/Makefile16
-rw-r--r--server/filer.c120
-rw-r--r--server/filer.h19
-rw-r--r--server/net.c147
-rw-r--r--server/net.h51
-rw-r--r--server/query.c349
-rw-r--r--server/query.h42
-rw-r--r--server/server.c216
-rw-r--r--server/server.h21
9 files changed, 981 insertions, 0 deletions
diff --git a/server/Makefile b/server/Makefile
new file mode 100644
index 0000000..3d10466
--- /dev/null
+++ b/server/Makefile
@@ -0,0 +1,16 @@
+SERVER_BIN = dnbd-server
+SERVER_SRC = filer.c net.c query.c server.c
+
+BINS = $(SERVER_BIN)
+
+CFLAGS = -Wall -D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -O2
+LDFLAGS = -lpthread
+
+$(SERVER_BIN):
+ $(CC) $(CFLAGS) -o $@ $(SERVER_SRC) $(LDFLAGS)
+
+all: $(BINS)
+
+.PHONY:
+clean:
+ -$(RM) *.o $(BINS) *~
diff --git a/server/filer.c b/server/filer.c
new file mode 100644
index 0000000..b8412b7
--- /dev/null
+++ b/server/filer.c
@@ -0,0 +1,120 @@
+/*
+ * filer.c - open, seeks in and reads from a file
+ *
+ * Copyright (C) 2006 Thorsten Zitterell <thorsten@zitterell.de>
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "filer.h"
+
+/*
+ * function filer_getcapacity()
+ * returns: size/capacity of file/device
+ */
+unsigned long long filer_getcapacity(filer_info_t * filer_info)
+{
+ return filer_info->size;
+}
+
+/*
+ * function filer_seekblock(): seek to position in file/block device
+ * returns: 1 on success, otherwise 0
+ */
+static inline int filer_seekblock(filer_info_t * filer_info, off_t newpos)
+{
+ if (lseek(filer_info->fd, newpos, SEEK_SET) == (off_t) -1) {
+ return 0;
+ }
+ filer_info->pos = newpos;
+ return 1;
+}
+
+/*
+ * function filer_readblock(): read bytes at specific position
+ * returns: 1 on success, otherwise 0
+ */
+inline int filer_readblock(filer_info_t * filer_info, void *buf, size_t size,
+ off_t pos)
+{
+ size_t remain = size;
+ int result = 0;
+ int numblocks = 0;
+
+ if (!filer_seekblock(filer_info, pos)) goto leave;
+
+ while (remain > 0) {
+ if ((numblocks = read(filer_info->fd, buf, remain)) <= 0) {
+ if (errno == EINTR)
+ continue;
+ goto leave;
+ }
+
+ if (numblocks == 0) {
+ goto leave;
+ }
+ remain -= numblocks;
+ buf += numblocks;
+ }
+ result = 1;
+ leave:
+ filer_info->pos += (size - remain);
+ return result;
+}
+
+/*
+ * function filer_init(): open file to be served
+ * returns: data structure with file information
+ */
+filer_info_t *filer_init(const char *filename)
+{
+ filer_info_t *filer_info;
+ struct stat64 stbuf;
+
+ filer_info = (filer_info_t *) malloc(sizeof(filer_info_t));
+ if (!filer_info)
+ return NULL;
+
+ filer_info->filename = strdup(filename);
+ if ((filer_info->fd = open(filename, O_RDONLY | O_LARGEFILE)) < 0) {
+ fprintf(stderr, "ERROR: Cannot open filename \"%s\"\n",
+ filename);
+ goto out_free;
+ }
+
+ stbuf.st_size = 0;
+
+ if (fstat64(filer_info->fd, &stbuf) < 0) {
+ fprintf(stderr, "ERROR: Cannot stat file \"%s\"\n",
+ filename);
+ goto out_free;
+ }
+
+ /* get file/device size */
+ if ((filer_info->size = stbuf.st_size) == 0) {
+ filer_info->size = lseek64(filer_info->fd, (off_t) 0, SEEK_END);
+ }
+
+ if (filer_info->size == 0) {
+ fprintf(stderr, "ERROR: File/device has zero size\n");
+ goto out_free;
+ }
+
+ goto out;
+
+ out_free:
+ if (filer_info)
+ free(filer_info);
+
+ filer_info = NULL;
+ out:
+ return filer_info;
+}
diff --git a/server/filer.h b/server/filer.h
new file mode 100644
index 0000000..6e39bfa
--- /dev/null
+++ b/server/filer.h
@@ -0,0 +1,19 @@
+#ifndef LINUX_DNBD_FILER_H
+#define LINUX_DNBD_FILER_H 1
+
+/* information of served file/block device */
+struct filer_info {
+ const char *filename;
+ int fd;
+ unsigned long long size;
+ off_t pos;
+};
+
+typedef struct filer_info filer_info_t;
+
+/* functions */
+unsigned long long filer_getcapacity(filer_info_t * filer);
+int inline filer_readblock(filer_info_t * filer_info, void *buf, size_t size, off_t pos);
+filer_info_t *filer_init(const char *filename);
+
+#endif
diff --git a/server/net.c b/server/net.c
new file mode 100644
index 0000000..02db9aa
--- /dev/null
+++ b/server/net.c
@@ -0,0 +1,147 @@
+/*
+ * net.c - network stuff for the server
+ * Copyright (C) 2006 Thorsten Zitterell <thorsten@zitterell.de>
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define DNBD_USERSPACE 1
+#include "../common/dnbd-cliserv.h"
+
+#include "net.h"
+
+struct listener_s {
+ pthread_t tid;
+ net_request_t *request;
+};
+
+typedef struct listener_s listener_t;
+listener_t listener;
+
+/*
+ * function net_tx(): send a server reply
+ */
+void net_tx(net_info_t * net_info, net_reply_t * reply)
+{
+ if (sendto
+ (net_info->sock, reply->data, reply->len, 0,
+ (struct sockaddr *) &net_info->groupnet,
+ sizeof(net_info->groupnet)) < 0)
+ fprintf(stderr, "net_tx: mcast sendproblem\n");
+
+}
+
+/*
+ * function net_rx(): receive a client request
+ * returns: 1 on correct size of reply, otherwise 0
+ */
+int net_rx(net_info_t * net_info, net_request_t * request)
+{
+ ssize_t n;
+
+ request->clientlen = sizeof(request->client);
+
+ n = recvfrom(net_info->sock, &request->data,
+ sizeof(request->data), 0,
+ &request->client, &request->clientlen);
+
+ /* sizeof of request must be size of a DNBD request */
+ return (n == sizeof(request->data) ? 1 : 0);
+}
+
+/*
+ * function net_init(): initialize network for multicast
+ * returns: structure with network related information
+ */
+net_info_t *net_init(const char *mnet)
+{
+ struct ip_mreq mreq;
+ const int ttl = 64; /* TTL of 64 should be enough */
+ u_char loop = 0;
+
+ net_info_t *net_info = NULL;
+
+ net_info = (net_info_t *) malloc(sizeof(net_info_t));
+ if (!net_info)
+ return NULL;
+
+ memset(net_info, 0, sizeof(net_info_t));
+
+ /* network setup */
+ net_info->server.sin_family = AF_INET;
+ net_info->server.sin_port = htons(DNBD_PORT);
+ net_info->sock = socket(PF_INET, SOCK_DGRAM, 0);
+
+ if (!inet_aton(mnet, &net_info->server.sin_addr)) {
+ fprintf(stderr,
+ "ERROR: multicast group %s is not a valid address!\n",
+ mnet);
+ goto out_free;
+ }
+
+ if (bind
+ (net_info->sock, (struct sockaddr *) &net_info->server,
+ sizeof(net_info->server)) < 0) {
+ fprintf(stderr, "ERROR: binding socket!\n");
+ goto out_free;
+ }
+
+ if (!inet_aton(mnet, &net_info->groupnet.sin_addr)) {
+ fprintf(stderr,
+ "ERROR: multicast group %s is not a valid address!\n",
+ mnet);
+ goto out_free;
+ }
+
+ /* multicast setup */
+ net_info->groupnet.sin_family = AF_INET;
+ net_info->groupnet.sin_port = htons(DNBD_PORT);
+
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ memcpy(&mreq.imr_multiaddr, &net_info->groupnet.sin_addr,
+ sizeof(struct in_addr));
+
+ if (setsockopt
+ (net_info->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) < 0) {
+ fprintf(stderr,
+ "ERROR: cannot add multicast membership!\n");
+ goto out_free;
+ }
+
+ if (setsockopt(net_info->sock, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl)) < 0) {
+ fprintf(stderr, "ERROR: Setting TTL to 2\n");
+ goto out_free;
+ }
+
+ /* no looping, please */
+ if (setsockopt
+ (net_info->sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
+ sizeof(loop)) < 0) {
+ fprintf(stderr,
+ "ERROR: cannot disable multicast looping!\n");
+ goto out_free;
+ }
+
+ goto out;
+
+ out_free:
+ fprintf(stderr,
+ "hint: check kernel multicast support, multicast routing\n");
+ if (net_info)
+ free(net_info);
+
+ net_info = NULL;
+ out:
+ return net_info;
+}
diff --git a/server/net.h b/server/net.h
new file mode 100644
index 0000000..d0f12c6
--- /dev/null
+++ b/server/net.h
@@ -0,0 +1,51 @@
+#ifndef LINUX_DNBD_NET_H
+#define LINUX_DNBD_NET_H 1
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* network information */
+struct net_info {
+ int sock;
+ struct sockaddr_in server;
+ struct sockaddr_in groupnet;
+};
+typedef struct net_info net_info_t;
+
+/* structure for received network packet */
+struct net_request {
+ struct sockaddr_in client;
+ socklen_t clientlen;
+ dnbd_request_t data;
+ size_t len;
+};
+typedef struct net_request net_request_t;
+
+/* structure for network packets to be sent */
+struct net_reply {
+ void *data;
+ size_t len;
+};
+typedef struct net_reply net_reply_t;
+
+
+/* struct net_info_s net_info; */
+
+net_info_t * net_init();
+
+/* functions */
+void net_tx(net_info_t *net, net_reply_t *reply);
+int net_rx(net_info_t * net, net_request_t *request);
+
+/* network to host byte order */
+#include <endian.h>
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define ntohll(x) (x)
+#else
+#define ntohll(x) bswap_64(x)
+#endif
+
+
+
+#endif
diff --git a/server/query.c b/server/query.c
new file mode 100644
index 0000000..59d1864
--- /dev/null
+++ b/server/query.c
@@ -0,0 +1,349 @@
+/*
+ * query.c - request/reply handling for the server
+ * Copyright (C) 2006 Thorsten Zitterell <thorsten@zitterell.de>
+ */
+
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/types.h>
+#include <unistd.h>
+#include <time.h>
+
+#define DNBD_USERSPACE 1
+#include "../common/dnbd-cliserv.h"
+
+#include "query.h"
+
+/* number of threads used to service requests */
+#define NUM_HANDLER_THREADS 1 /* default */
+#define MAX_BLOCK_SIZE 4096
+
+struct query_thread {
+ query_info_t *query_info;
+ int id;
+ pthread_t p_thread;
+};
+
+struct query_thread query_thread[NUM_HANDLER_THREADS];
+
+/* recursive global mutex for our program. */
+pthread_mutex_t query_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+/* mutex to avoid concurrent file access */
+pthread_mutex_t handler_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* global condition variable for our program. */
+pthread_cond_t got_query = PTHREAD_COND_INITIALIZER;
+
+int num_queries = 0; /* number of pending requests, initially none */
+int max_queries = 100; /* this value should be high enough */
+
+query_t *queries = NULL; /* head of linked list of requests. */
+int last_query = 0; /* initial position in circular buffer */
+int next_query = 0;
+
+
+void query_handle(struct query_info *query_info, query_t * query);
+
+/*
+ * function query_add_loop(): add incoming requests to circular buffer
+ */
+void *query_add_loop(void *data)
+{
+ int rc;
+ query_t *query;
+ query_info_t *query_info = (query_info_t *) data;
+
+ int tmp_query;
+
+ while (1) {
+
+ rc = pthread_mutex_lock(&query_mutex);
+ tmp_query = (next_query + 1) % max_queries;
+ rc = pthread_mutex_unlock(&query_mutex);
+
+ if (tmp_query == last_query)
+ continue;
+
+ query = &queries[next_query];
+
+ /* loop until a proper request arrives */
+ while (!net_rx(query_info->net_info, &query->request)) {}
+
+ rc = pthread_mutex_lock(&query_mutex);
+
+ next_query = tmp_query;
+
+ /* increase total number of pending requests */
+ num_queries++;
+
+ rc = pthread_mutex_unlock(&query_mutex);
+
+ /* signal that there's a new request to handle */
+ rc = pthread_cond_signal(&got_query);
+ }
+}
+
+/*
+ * function: query_get(): fetch request from circular buffer
+ * returns: pointer to request
+ */
+query_t *query_get(pthread_mutex_t * p_mutex)
+{
+ int rc;
+ query_t *query; /* pointer to request */
+
+ rc = pthread_mutex_lock(p_mutex);
+
+ if (last_query == next_query)
+ return NULL;
+
+ query = &queries[last_query];
+
+ last_query = (last_query + 1) % max_queries;
+ num_queries--;
+
+ rc = pthread_mutex_unlock(p_mutex);
+ /* return the request to the caller */
+ return query;
+}
+
+/*
+ * function query_handle(): handle a single request.
+ */
+void query_handle(struct query_info *query_info, query_t * query)
+{
+ int i, rc;
+ dnbd_request_t *dnbd_request;
+ dnbd_request_t *dnbd_old_request;
+ dnbd_reply_t *dnbd_reply = NULL;
+ struct dnbd_reply_init *dnbd_reply_init;
+ int tmp_query;
+ int recent = 0;
+ time_t timestamp;
+
+ dnbd_request = (dnbd_request_t *) & query->request.data;
+
+ query->reply.len = 0;
+
+ /* convert data from network to host byte order */
+ dnbd_request->magic = ntohl(dnbd_request->magic);
+ dnbd_request->time = ntohs(dnbd_request->time);
+ dnbd_request->id = ntohs(dnbd_request->id);
+ dnbd_request->cmd = ntohs(dnbd_request->cmd);
+ dnbd_request->pos = ntohll(dnbd_request->pos);
+ dnbd_request->len = ntohs(dnbd_request->len);
+
+ if (dnbd_request->magic != DNBD_MAGIC)
+ return;
+
+ /* we ususally only respond to a client */
+ if (!(dnbd_request->cmd & DNBD_CMD_CLI))
+ return;
+
+ /* does the client ask for our id? */
+ if (dnbd_request->id && (dnbd_request->id != query_info->id))
+ return;
+
+ switch (dnbd_request->cmd & DNBD_CMD_MASK) {
+ /* handle init request */
+ case DNBD_CMD_INIT:
+ /* handle heartbeat request */
+ case DNBD_CMD_HB:
+ dnbd_reply_init =
+ (struct dnbd_reply_init *) query->reply.data;
+ dnbd_reply_init->magic = htonl(DNBD_MAGIC);
+
+ dnbd_reply_init->capacity =
+ htonll(filer_getcapacity(query_info->filer_info));
+
+ dnbd_reply_init->cmd =
+ htons((dnbd_request->cmd
+ & ~DNBD_CMD_CLI) | DNBD_CMD_SRV);
+
+ dnbd_reply_init->blksize = htons(MAX_BLOCK_SIZE);
+ dnbd_reply_init->id = htons(query_info->id);
+
+ query->reply.len = sizeof(struct dnbd_reply_init);
+
+ net_tx(query_info->net_info, &query->reply);
+ break;
+ /* handle read request */
+ case DNBD_CMD_READ:
+ timestamp = time(NULL);
+
+ /* burst avoidance */
+ rc = pthread_mutex_lock(&query_mutex);
+ for (i = 2; i < max_queries; i++) {
+
+ tmp_query =
+ (last_query + (max_queries - i)) % max_queries;
+
+ if (tmp_query == last_query)
+ break;
+
+ /* check only up to one second */
+ if (!tmp_query
+ || queries[tmp_query].time - timestamp > 1) {
+ break;
+ }
+ dnbd_old_request =
+ (dnbd_request_t *) & queries[tmp_query].
+ request.data;
+
+ /* someone requested the same block before? */
+ if (dnbd_request->pos == dnbd_old_request->pos) {
+ /* was it the same client, then retransmit
+ as the packet was probably lost, otherwise
+ drop the request */
+ if (!((query->request.clientlen ==
+ queries[tmp_query].request.clientlen)
+ &&
+ (!memcmp
+ (&query->request.client,
+ &queries[tmp_query].request.client,
+ query->request.clientlen)))) {
+ recent = 1;
+ break;
+ }
+ else
+ break;
+ }
+ }
+ rc = pthread_mutex_unlock(&query_mutex);
+
+ if (recent)
+ break;
+
+ /* size of request block too high? */
+ if (dnbd_request->len > MAX_BLOCK_SIZE)
+ break;
+
+ /* create a DNBD reply packet */
+ dnbd_reply = (dnbd_reply_t *) query->reply.data;
+
+ dnbd_reply->magic = htonl(DNBD_MAGIC);
+ dnbd_reply->time = htons(dnbd_request->time);
+ dnbd_reply->id = htons(query_info->id);
+ dnbd_reply->pos = htonll(dnbd_request->pos);
+
+ dnbd_reply->cmd =
+ htons((dnbd_request->cmd
+ & ~DNBD_CMD_CLI) | DNBD_CMD_SRV);
+
+ /* read from underlying device/file */
+ pthread_mutex_lock(&handler_mutex);
+ filer_readblock(query_info->filer_info,
+ (void *) dnbd_reply +
+ sizeof(struct dnbd_reply),
+ dnbd_request->len, dnbd_request->pos);
+
+ pthread_mutex_unlock(&handler_mutex);
+
+ query->reply.len =
+ dnbd_request->len + sizeof(dnbd_reply_t);
+
+ query->time = time(NULL);
+
+ /* send reply */
+ net_tx(query_info->net_info, &query->reply);
+ break;
+ }
+
+
+}
+
+/*
+ * function query_handle_loop(): get queries and handle them in a loop
+ */
+void *query_handle_loop(void *data)
+{
+ int rc;
+ query_t *query; /* pointer to a request */
+ int thread_id = *((int *) data); /* thread id */
+
+ printf("Starting thread '%d'\n", thread_id);
+ fflush(stdout);
+
+ rc = pthread_mutex_lock(&query_mutex);
+
+ /* do forever.... */
+ while (1) {
+
+ if (num_queries > 0) {
+ /* a request is pending */
+ query = query_get(&query_mutex);
+
+ /* got a request? */
+ if (query) {
+
+ rc = pthread_mutex_unlock(&query_mutex);
+ /* handle request */
+ query_handle(query_thread[thread_id].
+ query_info, query);
+
+ rc = pthread_mutex_lock(&query_mutex);
+ }
+ } else {
+ /* wait for a request to arrive */
+ rc = pthread_cond_wait(&got_query, &query_mutex);
+ }
+ }
+}
+
+/*
+ * function query_init(): initialize request handling
+ * returns: pointer to data structure query_info (see header file)
+ */
+query_info_t *query_init(net_info_t * net_info, filer_info_t * filer_info,
+ int id, int threads)
+{
+ int i;
+ query_info_t *query_info = NULL;
+
+ query_info = (query_info_t *) malloc(sizeof(query_info_t));
+ if (!query_info)
+ return NULL;
+
+ /* fill query_info structure */
+ query_info->net_info = net_info;
+ query_info->filer_info = filer_info;
+ query_info->id = id;
+
+ if (!(queries = (query_t *) malloc(sizeof(query_t) * max_queries))) {
+ free(query_info);
+ return NULL;
+ }
+
+ last_query = 0;
+ next_query = 0;
+
+ /* reserve memory for circular buffer */
+ for (i = 0; i < max_queries; i++) {
+ queries[i].reply.data =
+ malloc(MAX_BLOCK_SIZE + sizeof(dnbd_reply_t));
+ }
+
+ /* create the request-handling threads */
+ for (i = 0; i < threads; i++) {
+
+ query_thread[i].id = i;
+ query_thread[i].query_info = query_info;
+
+ pthread_create(&query_thread[i].p_thread, NULL,
+ query_handle_loop,
+ (void *) &query_thread[i].id);
+ }
+
+ /* create thread for receiving network requests */
+ pthread_create(&query_info->p_thread, NULL,
+ query_add_loop, (void *) query_info);
+
+ return query_info;
+}
diff --git a/server/query.h b/server/query.h
new file mode 100644
index 0000000..f4a9376
--- /dev/null
+++ b/server/query.h
@@ -0,0 +1,42 @@
+#ifndef LINUX_DNBD_REQUEST_H
+#define LINUX_DNBD_REQUEST_H 1
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <time.h>
+
+#include "net.h"
+#include "filer.h"
+
+struct query_info {
+ pthread_t p_thread;
+ net_info_t *net_info;
+ filer_info_t *filer_info;
+ int id;
+};
+
+typedef struct query_info query_info_t;
+
+/* query information for requests and replies */
+struct query {
+ time_t time;
+ net_request_t request;
+ net_reply_t reply;
+};
+
+typedef struct query query_t;
+
+/* functions */
+query_info_t *query_init(net_info_t *, filer_info_t *, int id, int threads);
+
+/* host to network byte order */
+#include <endian.h>
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define htonll(x) (x)
+#else
+#define htonll(x) bswap_64(x)
+#endif
+
+
+#endif
diff --git a/server/server.c b/server/server.c
new file mode 100644
index 0000000..b701e4c
--- /dev/null
+++ b/server/server.c
@@ -0,0 +1,216 @@
+/*
+ * main.c - central part of the DNBD server application
+ * Copyright (C) 2006 Thorsten Zitterell <thorsten@zitterell.de>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/* network includes */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+
+#define DNBD_USERSPACE 1
+#include "../common/dnbd-cliserv.h"
+
+#include "server.h"
+#include "query.h"
+#include "net.h"
+#include "filer.h"
+
+
+static int verbose = 0;
+static int running = 1;
+
+/*
+ * function: handle_signal(): set global variable running to 0 if signal arrives
+ */
+void handle_signal(int signum)
+{
+ running = 0;
+}
+
+void server_help(void)
+{
+ fprintf(stderr, "dnbd-server, version %s\n", DNBD_VERSION);
+ fprintf(stderr,
+ "Usage: dnbd-server -m <address> -d <device/file> -i <number>\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "description:\n");
+ fprintf(stderr, " -m|--mcast <multicast-address>\n");
+ fprintf(stderr, " -d|--device <block device or file>\n");
+ fprintf(stderr, " -i|--id <unique identification number>\n");
+ fprintf(stderr, " -t|--threads <number of threads>\n");
+}
+
+/*
+ * function: server_init(): parse command lines
+ */
+server_info_t *server_init(int argc, char **argv)
+{
+ /* cmd
+ * -1: error
+ * 0: not defined
+ * 1: serve
+ */
+ int cmd = 0;
+ server_info_t *server_info = NULL;
+
+ server_info = (server_info_t *) malloc(sizeof(server_info_t));
+ if (!server_info)
+ return NULL;
+
+ memset(server_info, 0, sizeof(server_info_t));
+
+ server_info->threads = 1;
+
+ /* return value for getopt */
+ int c;
+
+ while (1) {
+ static struct option long_options[] = {
+ {"verbose", no_argument, 0, 'v'},
+ {"mcast", required_argument, 0, 'm'},
+ {"device", required_argument, 0, 'd'},
+ {"threads", required_argument, 0, 't'},
+ {"id", required_argument, 0, 'i'},
+ {0, 0, 0, 0}
+ };
+ /* option index for getopt_long */
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "vm:d:i:t:",
+ long_options, &option_index);
+
+ /* at end of options? */
+ if (c == -1)
+ break;
+
+ /*
+ cmd = (cmd ? -1 : xx) is used to set cmd when it was
+ unset (0) before. Otherwise save error value
+ */
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'm':
+ server_info->mnet = optarg; /* multicast address */
+ break;
+ case 'd':
+ cmd = (cmd ? -1 : 2); /* device/file */
+ server_info->filename = optarg;
+ break;
+ case 'i':
+ if (sscanf(optarg, "%u",&server_info->id) != 1) {
+ fprintf(stderr,"ERROR: Id not a 16bit-integer (>0)\n");
+ cmd = -1;
+ }
+ break;
+ case 't':
+ if (sscanf(optarg, "%u",&server_info->threads) != 1) {
+ fprintf(stderr,"ERROR: Number of threads is wrong (>0)\n");
+ cmd = -1;
+ }
+ break;
+
+ default:
+ cmd = -1;
+ }
+
+ if (cmd < 0) break;
+ }
+
+ /* no/wrong command given? */
+ if (cmd <= 0) {
+ server_help();
+ goto out_free;
+ }
+
+ if (!server_info->mnet) {
+ fprintf(stderr, "ERROR: multicast group was not set!\n");
+ goto out_free;
+ }
+
+ if (!(server_info->id > 0)) {
+ fprintf(stderr, "ERROR: unique id not set or not valid!\n");
+ goto out_free;
+ }
+
+ if (!(server_info->threads > 0)) {
+ fprintf(stderr, "ERROR: number of threads is not valid!\n");
+ goto out_free;
+ }
+
+
+ /* call function for command */
+ goto out;
+
+ out_free:
+ if (server_info)
+ free(server_info);
+ server_info = NULL;
+ out:
+ return server_info;
+}
+
+/*
+ * function: main(): server startup
+ */
+int main(int argc, char **argv)
+{
+
+ server_info_t *server_info;
+
+ signal(SIGINT, handle_signal);
+
+ /* parse and verify command line options */
+ if (!(server_info = server_init(argc, argv))) {
+ fprintf(stderr, "ERROR: Parsing arguments!\n");
+ goto out_server;
+ }
+
+ /* initialize network configuration and start listener thread */
+ if (!(server_info->net_info = net_init(server_info->mnet))) {
+ fprintf(stderr, "ERROR: Initializing net!\n");
+ goto out_net;
+ }
+
+ if (!(server_info->filer_info = filer_init(server_info->filename))) {
+ fprintf(stderr, "ERROR: Initializing filer!\n");
+ goto out_filer;
+ }
+
+ /* initialize threads to handle requests */
+ if (!
+ (server_info->query_info =
+ query_init(server_info->net_info, server_info->filer_info,
+ server_info->id, server_info->threads))) {
+ fprintf(stderr, "ERROR: Initializing query!\n");
+ goto out_query;
+ }
+
+ while (running)
+ pause();
+
+ fprintf(stdout, "cleaning up...\n");
+ out_query:
+ if (server_info->filer_info)
+ free(server_info->filer_info);
+
+ out_filer:
+ if (server_info->net_info)
+ free(server_info->net_info);
+
+ out_net:
+ if (server_info) {
+ free(server_info);
+ }
+ out_server:
+ return 0;
+}
diff --git a/server/server.h b/server/server.h
new file mode 100644
index 0000000..3e36bc3
--- /dev/null
+++ b/server/server.h
@@ -0,0 +1,21 @@
+#ifndef LINUX_DNBD_SERVER_H
+#define LINUX_DNBD_SERVER_H 1
+
+#include "filer.h"
+#include "net.h"
+#include "query.h"
+
+/* server relevant information mainly given by command line */
+struct server_info {
+ const char *filename;
+ int id;
+ int threads;
+ const char *mnet;
+ filer_info_t *filer_info;
+ net_info_t *net_info;
+ query_info_t *query_info;
+};
+
+typedef struct server_info server_info_t;
+
+#endif