summaryrefslogblamecommitdiffstats
path: root/test-app/data-client.c
blob: da85b893559bf2fc937e210f29a8db7af84857d5 (plain) (tree)
























































































































































































































































































                                                                                
/*
 * test-apps/data-client.c
 */

#include <sys/socket.h>
#include <netinet/in.h>
#include <byteswap.h>
#include <arpa/inet.h>
#include <strings.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "dnbd2.h"


void print_usage(void);
ssize_t writen(int fd, const void *msg, size_t n);
ssize_t readn(int fd, void *msg, size_t n);
int dnbd2_data_request(dnbd2_server_t server,
                       dnbd2_data_request_t request,
                       dnbd2_data_reply_t *reply);


int main(int argc, char **argv)
{
        if (argc != 6) {
		print_usage();
                return 0;
        }

	dnbd2_data_request_t request;
	dnbd2_data_reply_t reply;

	/* The time field is always echoed by the server.
	   This is an arbitrary value. */
	request.time = 28481;

	/* Read IP and Port */
	dnbd2_server_t server;
        if (!inet_aton(argv[1], (struct in_addr *) &(server.ip))) {
                fprintf(stderr, "Invalid IP\n");
                return -1;
        }
        if (sscanf(argv[2], "%hu", &(server.port)) != 1) {
                fprintf(stderr, "Invalid Port\n");
                return -1;
        }
        server.port = htons(server.port);

	/* Read Volume-ID and Release-ID */
	if (sscanf(argv[3], "%hu", &(request.vid)) != 1) {
                fprintf(stderr, "Invalid Volume-ID\n");
                return -1;
        }
	if (sscanf(argv[4], "%hu", &(request.rid)) != 1) {
                fprintf(stderr, "Invalid Release-ID\n");
                return -1;
        }

	/* Read command */
	char str[16];
	int cmd = 0;
	if (sscanf(argv[5], "%s", str) == 1) {
		if (!strcmp("getdata", str))
			cmd = 1;

		if (!strcmp("getsize", str))
			cmd = 2;

		if (!strcmp("getservers", str))
			cmd = 3;
	}
	if (!cmd) {
		fprintf(stderr, "Invalid CMD\n");
		return -1;
	}

	/* Complete and issue request(s) */
	int ret, i, rest;
	off_t size, blocks;
	switch (cmd) {
	case 1:
		/* Get Dataset size */
		request.cmd = CMD_GET_SIZE;
		ret = dnbd2_data_request(server, request, &reply);
		if (ret == -1)
			goto out_nosize;
		size = reply.num;

		/* size = blocks * DNBD2_BLOCK_SIZE + rest */
		blocks = size / DNBD2_BLOCK_SIZE;
		rest = size % DNBD2_BLOCK_SIZE;

		/* Fetch "blocks * DNBD2_BLOCK_SIZE" bytes */
		request.cmd = CMD_GET_BLOCK;
		for (i=0 ; i<blocks ; i++) {
			request.num = i * DNBD2_BLOCK_SIZE;
			ret = dnbd2_data_request(server, request, &reply);
			if (ret == -1)
				goto out_nodata;
			write(STDOUT_FILENO, reply.payload.data,
			      DNBD2_BLOCK_SIZE);
		}

		/* Fetch "rest" bytes */
		if (rest != 0) {
			request.num = i*DNBD2_BLOCK_SIZE;
			ret = dnbd2_data_request(server, request, &reply);
			if (ret == -1)
				goto out_nodata;
			write(STDOUT_FILENO, reply.payload.data, rest);
		}		
		break;

	case 2:
		/* Get Dataset size */
		request.cmd = CMD_GET_SIZE;
		ret = dnbd2_data_request(server, request, &reply);
		if (ret == -1)
			goto out_nosize;
		printf("Dataset Size = %lld\n", reply.num);
		break;

	case 3:
		/* Get list of alternative servers */
		request.cmd = CMD_GET_SERVERS;
		ret = dnbd2_data_request(server, request, &reply);
		if (ret == -1)
			goto out_noservers;
		if (reply.num == 0)
			printf("No alternative servers.\n");

		for (i=0 ; i<reply.num ; i++) {
			dnbd2_server_t server;
			struct in_addr addr;
			memcpy(&server,
			       &reply.payload.server[i],
			       sizeof(dnbd2_server_t));
			memcpy(&addr,
			       &server.ip,
			       sizeof(uint32_t));
			printf("%s:%hu\n", inet_ntoa(addr), ntohs(server.port));
		}
		break;
	}


	return 0;

 out_nodata:
	fprintf(stderr, "Could not get data.\n");
	return -1;

 out_nosize:
	fprintf(stderr, "Could not get Dataset size.\n");
	return -1;

 out_noservers:
	fprintf(stderr, "Could not get list of alternative servers.\n");
	return -1;
}


int dnbd2_data_request(dnbd2_server_t server,
                       dnbd2_data_request_t request,
                       dnbd2_data_reply_t *reply)
{
	struct sockaddr_in server_addr;
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_addr.s_addr = server.ip;
	server_addr.sin_port = server.port;
	server_addr.sin_family = AF_INET;

	int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
	if (sockfd == -1) {
		fprintf(stderr, "Could not create socket.\n");
		return -1;
	}
	if (connect(sockfd, (struct sockaddr *) &server_addr,
		    sizeof(server_addr)) == -1) {
		fprintf(stderr,"Could not connect UDP socket.");
		return -1;
	}

	request.cmd  = htons(request.cmd);
	request.time = htons(request.time);
	request.vid  = htons(request.vid);
	request.rid  = htons(request.rid);
	request.num  = htonll(request.num);

	/* Send request. */
	ssize_t n = writen(sockfd, &request, sizeof(request));
	if (n == -1) {
		fprintf(stderr, "Error sending request.\n");
		return -1;
	}
	if (n != sizeof(request)) {
		fprintf(stderr, "Sent wrong request size.\n");
		return -1;
	}

	/* Receive reply. */
	n = readn(sockfd, reply, sizeof(*reply));
	if (n == -1) {
		fprintf(stderr, "Error receiving reply.\n");
		return -1;
	}
	if (n != sizeof(*reply)) {
		fprintf(stderr, "Got wrong reply size.\n");
		return -1;
	}
	close(sockfd);

	reply->cmd  = ntohs(reply->cmd);
	reply->time = ntohs(reply->time);
	reply->vid  = ntohs(reply->vid);
	reply->rid  = ntohs(reply->rid);
	reply->num  = ntohll(reply->num);

	return 0;
}


ssize_t writen(int fd, const void *msg, size_t n) {

        size_t nleft;
        ssize_t nwritten;
        const char *ptr;

        ptr = msg;
        nleft = n;

        while (nleft > 0) {
                if ((nwritten = write(fd, ptr, nleft)) <= 0) {
                        if (errno == EINTR)
                                nwritten = 0;
                        else
                                return -1;
                }
                nleft -= nwritten;
                ptr += nwritten;
        }

        return (n);
}


ssize_t readn(int fd, void *msg, size_t n) {

        size_t nleft;
        ssize_t nread;
        char *ptr;

        ptr = msg;
        nleft = n;

        while (nleft > 0) {
                if ((nread = read(fd, ptr, nleft)) < 0) {
                        if (errno == EINTR)
                                nread = 0;
                        else
                                return -1;
                } else if (nread == 0) {
                        break;
                }
                nleft -= nread;
                ptr += nread;
        }

        return n - nleft;
}


void print_usage(void) {
	printf("usage: dnbd2-data IP Port Volume-ID Release-ID "
	       "(getsize|getdata|getservers)\n");
	printf("       getsize: Print the Dataset's size.\n");
	printf("       getdata: Write the Dataset's contents to stdout.\n");
	printf("       getsize: Print the list of alternative Servers.\n");
}