From 868fec1f8eca7c344fc9ac057b7418331299d9ce Mon Sep 17 00:00:00 2001 From: Lars Müller Date: Sat, 1 Mar 2008 18:30:38 +0000 Subject: 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/dnbd/trunk@1592 95ad53e4-c205-0410-b2fa-d234c58c8868 --- client/Makefile | 15 ++ client/client.c | 549 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ client/client.h | 7 + 3 files changed, 571 insertions(+) create mode 100644 client/Makefile create mode 100644 client/client.c create mode 100644 client/client.h (limited to 'client') diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..ed11db3 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,15 @@ +CLIENT_BIN = dnbd-client +CLIENT_SRC = client.c + +BINS = $(CLIENT_BIN) + +CFLAGS = -Wall -D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -O2 + +$(CLIENT_BIN): + $(CC) $(CFLAGS) -o $@ $(CLIENT_SRC) + +all: $(BINS) + +.PHONY: +clean: + -$(RM) *.o $(BINS) *~ diff --git a/client/client.c b/client/client.c new file mode 100644 index 0000000..790606f --- /dev/null +++ b/client/client.c @@ -0,0 +1,549 @@ +/* + * client.c - controlling application for block device driver + * Copyright (C) 2006 Thorsten Zitterell + */ + +#include +#include +#include +#include +#include +#include + +/* network includes */ +#include +#include +#include +#include +#include + + +/* file operations */ +#include +#include +#include +#include + +#include +#include + +#include + +#define DNBD_USERSPACE 1 +#include "../common/dnbd-cliserv.h" +#include "client.h" + +/* device driver setup information */ +struct client_s { + const char *mnetname; /* multicast address */ + const char *cachefile; /* name of cache file */ + struct sockaddr_in mca_adr; /* multicast address */ + int mca_len; /* and its byte length */ + const char *devicename; /* name of the device */ + int dnbd; /* file descriptor of dnbd device */ + int port; /* used port for multicast */ + int sock; /* socket descriptor */ + uint64_t capacity; /* capacity of device */ + uint16_t blksize; /* blocksize of device */ +}; + +typedef struct client_s client_t; + +/* beeing more verbose, if necessary */ +static int verbose = 0; + +/* structure of request to server */ +struct dnbd_request request; + +/* + * function daemonize(): forks our process that it can run in background + * returns: 1 on success, otherwise 0; + */ +int daemonize(void) +{ + pid_t pid; + + pid = fork(); + + if (pid > 0) { + exit(0); + } + if (pid < 0) { + fprintf(stderr, "fork() failed!\n"); + return 0; + } + return 1; +} + +/* + * function open_dnbd(): open the block device and do some prechecking + * returns: file descriptor of block device + */ +int open_dnbd(client_t * client) +{ + int dnbd; + struct stat statbuf; + + /* try to open the network block device */ + if ((dnbd = open(client->devicename, O_RDONLY)) < 0) { + if (errno == ENXIO) + fprintf(stderr, + "ERROR: make sure dnbd module is loaded!\n"); + fprintf(stderr, + "ERROR: Dnbd devide could not be opened!\n"); + return -EINVAL; + } + + /* error, if we cannot get status of file */ + if (fstat(dnbd, &statbuf) == -1) { + fprintf(stderr, "ERROR: Can not stat dnbd!\n"); + return -EINVAL; + } + + /* error, if it is not a block device */ + if (!(S_ISBLK(statbuf.st_mode))) + return -EINVAL; + + return dnbd; +} + +/* + * function open_net(): configures network parameters + * returns: socket descriptor of multicast net (int) + */ +int open_net(client_t * client) +{ + int sock; + const int ttl = 64; /* a TTL of 64 for multicast should be enough */ + struct ip_mreq mreq; + u_char loop = 0; /* multicast looping is disabled */ + + /* zero multicast address and convert to appropriate type */ + memset(&client->mca_adr, 0, sizeof(client->mca_adr)); + if (inet_aton(client->mnetname, &client->mca_adr.sin_addr) < 0) { + fprintf(stderr, "ERROR: Wrong multicast address \"%s\"!", + client->mnetname); + return -EINVAL; + } + + /* configure multicast socket */ + client->mca_adr.sin_family = AF_INET; + client->mca_adr.sin_port = htons(client->port); + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + fprintf(stderr, "ERROR: Socket creation failed!\n"); + return -EINVAL; + } + + /* bind socket */ + if (bind + (sock, (struct sockaddr *) &client->mca_adr, + sizeof(client->mca_adr)) < 0) { + fprintf(stderr, "ERROR: Socket bind failed!\n"); + return -EINVAL; + } + + /* setup multicast, join multicast group, set TTL and disable looping */ + if (inet_aton(client->mnetname, &mreq.imr_multiaddr) < 0) { + fprintf(stderr, "ERROR: Wrong multicast address \"%s\"!", + client->mnetname); + return -EINVAL; + } + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt + (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + fprintf(stderr, "ERROR: Adding multicast membership\n"); + return -1; + } + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl)) < 0) { + fprintf(stderr, "ERROR: Setting TTL to %i\n",ttl); + return -1; + } + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, + sizeof(loop)); + + + return sock; +} + +/* + * function do_handshake(): send init requests to the network and wait for + * server replies + * returns: 0 on success, otherwise POSIX error code + */ +int do_handshake(client_t * client) +{ + int result; + struct pollfd read_fds[1]; + struct dnbd_request request; + int replylen; + struct dnbd_reply_init reply_init; + time_t starttime, stoptime; + int cycle = 1; + int servers = 0; + + client->capacity = 0; + client->blksize = 0; + + /* for network socket polls */ + read_fds[0].fd = client->sock; + read_fds[0].events = POLLIN; + read_fds[0].revents = 0; + + /* request comes from a client and is addressed to all servers */ + request.magic = htonl(DNBD_MAGIC); + request.cmd = htons(DNBD_CMD_INIT | DNBD_CMD_CLI); + request.id = htons(0); /* ask all servers */ + + /* send requests (in 1 second intervals) */ + /* and wait DNBD_TIMEOUT seconds for replies */ + printf("Searching for servers...\n"); + starttime = time(NULL); + (void) sendto(client->sock, &request, sizeof(request), + 0, (struct sockaddr *) &client->mca_adr, + sizeof(client->mca_adr)); + + + while (cycle) { + + stoptime = time(NULL); + + /* timeout after DNBD_TIMEOUT seconds */ + if ((stoptime - starttime) > DNBD_TIMEOUT) + break; + + /* wait for reply or send a request each second */ + if ((result = poll(read_fds, 1, 1000)) != 1) { + (void) sendto(client->sock, &request, + sizeof(request), 0, + (struct sockaddr *) &client->mca_adr, + sizeof(client->mca_adr)); + continue; + } + + /* handle reply */ + replylen = + recv(client->sock, &reply_init, + sizeof(struct dnbd_reply_init), MSG_WAITALL); + + /* check for integrity */ + if (replylen != sizeof(struct dnbd_reply_init)) + continue; + reply_init.magic = ntohl(reply_init.magic); + if (reply_init.magic != DNBD_MAGIC) { + continue; + } + reply_init.cmd = ntohs(reply_init.cmd); + if (!((reply_init.cmd & DNBD_CMD_SRV) && + ((reply_init.cmd & DNBD_CMD_MASK) == DNBD_CMD_INIT))) + continue; + + /* copy parameters of block device from reply */ + reply_init.id = ntohs(reply_init.id); + reply_init.capacity = ntohll(reply_init.capacity); + reply_init.blksize = ntohs(reply_init.blksize); + + /* add server to block device servers */ + if ((result = + ioctl(client->dnbd, DNBD_SET_SERVERID, + reply_init.id)) < 0) { + if (errno == EEXIST) + continue; + else + fprintf(stderr, + "ERROR: ioctl DNBD_SET_SERVERID failed!\n"); + return -EINVAL; + } else { + printf("* Added server with id %i\n", + reply_init.id); + + client->capacity = reply_init.capacity; + client->blksize = reply_init.blksize; + servers++; + } + + } + + /* check, if servers have been found */ + if (servers) { + printf("Capacity of device is %llu, blksize is %i\n", + client->capacity, client->blksize); + result = 0; + } else { + if (ioctl(client->dnbd, DNBD_DISCONNECT) < 0) { + fprintf(stderr, + "ERROR: ioctl DNBD_DISCONNECT failed!\n"); + } + fprintf(stderr, "No servers found!\n"); + result = -ECONNABORTED; + } + + return result; +} + +/* + * function do_bind(): open block devicename, bind network, do handshake, + * set block device configuration (size, blocksize), + * start session, fork and go to background + * returns: 1 when session finished, otherwise POSIX error code + */ +int do_bind(client_t * client) +{ + struct dnbd_file cfile; + + /* open block device */ + if ((client->dnbd = open_dnbd(client)) < 0) + return -EINVAL; + printf("DNBD device successfully set.\n"); + + /* bind network */ + if ((client->sock = open_net(client)) < 0) + return -EINVAL; + fprintf(stdout, "Socket successfully opened.\n"); + + /* configure block device */ + if (ioctl(client->dnbd, DNBD_SET_SOCK, client->sock) < 0) { + close(client->sock); + fprintf(stderr, "ERROR: ioctl DNBD_SET_SOCKET failed!\n"); + return -EINVAL; + } + if (ioctl(client->dnbd, DNBD_SET_GROUPNET, &client->mca_adr) < 0) { + fprintf(stderr, + "ERROR: ioctl DNBD_SET_GROUPNET failed!\n"); + return -EINVAL; + } + fprintf(stdout, "Multicast address successfully set to %s.\n", + inet_ntoa(client->mca_adr.sin_addr)); + + /* start handshake */ + if (do_handshake(client) < 0) + return -EINVAL; + + /* set block size and capacity of device */ + if (ioctl(client->dnbd, DNBD_SET_BLKSIZE, client->blksize) < 0) { + fprintf(stderr, "ERROR: ioctl DNBD_SET_BLKSIZE failed!\n"); + return -EINVAL; + } + if (ioctl(client->dnbd, DNBD_SET_CAPACITY, &client->capacity) < 0) { + fprintf(stderr, "ERROR: ioctl DNBD_SET_SIZE failed!\n"); + return -EINVAL; + } + + /* activate cache, if necessary */ + if (client->cachefile) { + cfile.name = client->cachefile; + cfile.len = strlen(client->cachefile); + if (ioctl(client->dnbd, DNBD_SET_CACHE, &cfile) < 0) { + fprintf(stderr, + "ERROR: ioctl DNBD_SET_CACHE failed!\n"); + return -EINVAL; + } + printf("Cachefile successfully set.\n"); + } + + /* go to background */ + if (!daemonize()) + return -ECHILD; + if (ioctl(client->dnbd, DNBD_DO_IT) < 0) { + fprintf(stderr, + "ERROR: ioctl DNBD_DO_IT terminated unexpected!\n"); + } else { + fprintf(stdout, "dnbd terminated.\n"); + } + + return 1; +} + +/* + * function do_unbind(): end session + * returns: 1 on success, otherwise POSIX error code + */ +int do_unbind(client_t * client) +{ + /* open block device */ + if ((client->dnbd = open_dnbd(client)) < 0) + return -EINVAL; + fprintf(stdout, "dnbd device successfully opened.\n"); + + /* send disconnect */ + if (ioctl(client->dnbd, DNBD_DISCONNECT) < 0) { + fprintf(stderr, "ERROR: ioctl DNBD_DISCONNECT failed!\n"); + return -EINVAL; + } + + return 1; +} + +/* + * function: do_setcache(): set cache when block device is already active + * returns: 1 on success, otherwise POSIX error code + */ +int do_setcache(client_t * client) +{ + struct dnbd_file cfile; + if ((client->dnbd = open_dnbd(client)) < 0) + return -EINVAL; + fprintf(stdout, "dnbd device successfully opened.\n"); + + if (client->cachefile) { + cfile.name = client->cachefile; + cfile.len = strlen(client->cachefile); + if (ioctl(client->dnbd, DNBD_SET_CACHE, &cfile) < 0) { + fprintf(stderr, + "ERROR: ioctl DNBD_SET_CACHE failed!\n"); + return -EINVAL; + } + printf("Cachefile successfully set.\n"); + } + + return 1; +} + +/* + * function: client_help + */ +void client_help(void) +{ + fprintf(stderr, "dnbd-client, version %s\n", DNBD_VERSION); + fprintf(stderr, + "Usage: dnbd-client -d device -b
[-c ]\n"); + fprintf(stderr, " or dnbd-client -d device -u\n"); + fprintf(stderr, " or dnbd-client -d device -c \n"); + fprintf(stderr, "\n"); + fprintf(stderr, "description:\n"); + fprintf(stderr, " -d|--device \n"); + fprintf(stderr, " -b|--bind \n"); + fprintf(stderr, " -u|--unbind \n"); + fprintf(stderr, " -c|--cache \n"); + fprintf(stderr, "\n"); +} + +/* + * function client_shutdown() + */ +void client_shutdown(client_t * client) +{ + if (client->dnbd > 0) + close(client->dnbd); +} + +/* + * function parseopts(): parse command line options + * returns: command identifier or error <= 0 + * -1: error + * 0: not defined + * 1: bind a block device + * 2: unbind a block devicename + * 3: set cache file + */ +int parseopts(int argc, char **argv, client_t * client) +{ + int cmd = 0, err = 0; + + memset(client, 0, sizeof(client_t)); + client->port = DNBD_PORT; + /* return value for getopt */ + int c; + /* start option parsing */ + while (1) { + static struct option long_options[] = { + {"verbose", no_argument, 0, 'v'}, + {"bind", required_argument, 0, 'b'}, + {"unbind", no_argument, 0, 'u'}, + {"cache", required_argument, 0, 'c'}, + {"device", required_argument, 0, 'd'}, + {0, 0, 0, 0} + }; + /* option index for getopt_long */ + int option_index = 0; + opterr = 0; + c = getopt_long(argc, argv, "b:ud:c:v", + 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 'b': + cmd = (cmd ? -1 : 1); /* bind */ + client->mnetname = optarg; + break; + case 'u': + cmd = (cmd ? -1 : 2); /* unbind */ + break; + case 'd': + cmd = (client->devicename ? -1 : cmd); + client->devicename = optarg; + break; + case 'c': + cmd = (client->cachefile ? -1 : cmd); + client->cachefile = optarg; + break; + case '?': + fprintf(stderr, "ERROR: wrong parameters\n"); + default: + cmd = -1; + } + + } + + /* no/wrong command given? */ + if (cmd <= 0) { + /* set cache file, when not (un)bind */ + if (client->cachefile) + cmd = 3; + else + err = -EINVAL; + } + + if (cmd > 0 && !client->devicename) { + fprintf(stderr, "ERROR: no device given!\n"); + err = -EINVAL; + } + + if (err < 0) { + fprintf(stderr, "\n"); + client_help(); + return -err; + } + + + return (cmd); +} + +/* + * function main(): calls option parser, + * executes subrotine (bind/unbind/set cache) + * returns: 0 on success, otherwise POSIX error code + */ + +int main(int argc, char **argv) +{ + client_t client; + int cmd, err = 0; + cmd = parseopts(argc, argv, &client); + if (cmd < 0) + return 1; + /* call function for command */ + switch (cmd) { + case 1: + err = do_bind(&client); + break; + case 2: + err = do_unbind(&client); + break; + case 3: + err = do_setcache(&client); + break; + } + + client_shutdown(&client); + return err; +} diff --git a/client/client.h b/client/client.h new file mode 100644 index 0000000..952af1b --- /dev/null +++ b/client/client.h @@ -0,0 +1,7 @@ +#ifndef LINUX_DNBD_CLIENT_H +#define LINUX_DNBD_CLIENT_H 1 + +/* timeout in seconds to wait for servers, not yet changeable from user space */ +#define DNBD_TIMEOUT 3 + +#endif -- cgit v1.2.3-55-g7522