Back to index

nbd  3.2
nbd-tester-client.c
Go to the documentation of this file.
00001 /*
00002  * Test client to test the NBD server. Doesn't do anything useful, except
00003  * checking that the server does, actually, work.
00004  *
00005  * Note that the only 'real' test is to check the client against a kernel. If
00006  * it works here but does not work in the kernel, then that's most likely a bug
00007  * in this program and/or in nbd-server.
00008  *
00009  * Copyright(c) 2006  Wouter Verhelst
00010  *
00011  * This program is Free Software; you can redistribute it and/or modify it
00012  * under the terms of the GNU General Public License as published by the Free
00013  * Software Foundation, in version 2.
00014  *
00015  * This program is distributed in the hope that it will be useful, but WITHOUT
00016  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00017  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
00018  * more details.
00019  *
00020  * You should have received a copy of the GNU General Public License along with
00021  * this program; if not, write to the Free Software Foundation, Inc., 51
00022  * Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00023  */
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <stdbool.h>
00027 #include <string.h>
00028 #include <sys/time.h>
00029 #include <sys/types.h>
00030 #include <sys/socket.h>
00031 #include <sys/stat.h>
00032 #include <sys/mman.h>
00033 #include <fcntl.h>
00034 #include <syslog.h>
00035 #include <unistd.h>
00036 #include "config.h"
00037 #include "lfs.h"
00038 #include <netinet/in.h>
00039 #include <glib.h>
00040 
00041 #define MY_NAME "nbd-tester-client"
00042 #include "cliserv.h"
00043 
00044 static gchar errstr[1024];
00045 const static int errstr_len=1024;
00046 
00047 static uint64_t size;
00048 
00049 static int looseordering = 0;
00050 
00051 static gchar * transactionlog = "nbd-tester-client.tr";
00052 
00053 typedef enum {
00054        CONNECTION_TYPE_NONE,
00055        CONNECTION_TYPE_CONNECT,
00056        CONNECTION_TYPE_INIT_PASSWD,
00057        CONNECTION_TYPE_CLISERV,
00058        CONNECTION_TYPE_FULL,
00059 } CONNECTION_TYPE;
00060 
00061 typedef enum {
00062        CONNECTION_CLOSE_PROPERLY,
00063        CONNECTION_CLOSE_FAST,
00064 } CLOSE_TYPE;
00065 
00066 struct reqcontext {
00067        uint64_t seq;
00068        char orighandle[8];
00069        struct nbd_request req;
00070        struct reqcontext * next;
00071        struct reqcontext * prev;
00072 };
00073 
00074 struct rclist {
00075        struct reqcontext * head;
00076        struct reqcontext * tail;
00077        int numitems;
00078 };
00079 
00080 struct chunk {
00081        char * buffer;
00082        char * readptr;
00083        char * writeptr;
00084        uint64_t space;
00085        uint64_t length;
00086        struct chunk * next;
00087        struct chunk * prev;
00088 };
00089 
00090 struct chunklist {
00091        struct chunk * head;
00092        struct chunk * tail;
00093        int numitems;
00094 };
00095 
00096 struct blkitem {
00097        uint32_t seq;
00098        int32_t inflightr;
00099        int32_t inflightw;
00100 };
00101 
00102 void rclist_unlink(struct rclist * l, struct reqcontext * p) {
00103        if (p && l) {
00104               struct reqcontext * prev = p->prev;
00105               struct reqcontext * next = p->next;
00106               
00107               /* Fix link to previous */
00108               if (prev)
00109                      prev->next = next;
00110               else
00111                      l->head = next;
00112               
00113               if (next)
00114                      next->prev = prev;
00115               else
00116                      l->tail = prev;
00117 
00118               p->prev = NULL;
00119               p->next = NULL;
00120               l->numitems--;
00121        }                                                
00122 }                                                              
00123 
00124 /* Add a new list item to the tail */
00125 void rclist_addtail(struct rclist * l, struct reqcontext * p) {
00126        if (!p || !l)
00127               return;
00128        if (l->tail) {
00129               if (l->tail->next)
00130                      g_warning("addtail found list tail has a next pointer");
00131               l->tail->next = p;
00132               p->next = NULL;
00133               p->prev = l->tail;
00134               l->tail = p;
00135        } else {
00136               if (l->head)
00137                      g_warning("addtail found no list tail but a list head");
00138               l->head = p;
00139               l->tail = p;
00140               p->prev = NULL;
00141               p->next = NULL;
00142        }
00143        l->numitems++;
00144 }
00145 
00146 void chunklist_unlink(struct chunklist * l, struct chunk * p) {
00147        if (p && l) {
00148               struct chunk * prev = p->prev;
00149               struct chunk * next = p->next;
00150               
00151               /* Fix link to previous */
00152               if (prev)
00153                      prev->next = next;
00154               else
00155                      l->head = next;
00156               
00157               if (next)
00158                      next->prev = prev;
00159               else
00160                      l->tail = prev;
00161 
00162               p->prev = NULL;
00163               p->next = NULL;
00164               l->numitems--;
00165        }                                                
00166 }                                                              
00167 
00168 /* Add a new list item to the tail */
00169 void chunklist_addtail(struct chunklist * l, struct chunk * p) {
00170        if (!p || !l)
00171               return;
00172        if (l->tail) {
00173               if (l->tail->next)
00174                      g_warning("addtail found list tail has a next pointer");
00175               l->tail->next = p;
00176               p->next = NULL;
00177               p->prev = l->tail;
00178               l->tail = p;
00179        } else {
00180               if (l->head)
00181                      g_warning("addtail found no list tail but a list head");
00182               l->head = p;
00183               l->tail = p;
00184               p->prev = NULL;
00185               p->next = NULL;
00186        }
00187        l->numitems++;
00188 }
00189 
00190 /* Add some new bytes to a chunklist */
00191 void addbuffer(struct chunklist * l, void * data, uint64_t len) {
00192        void * buf;
00193        uint64_t size = 64*1024;
00194        struct chunk * pchunk;
00195 
00196        while (len>0)
00197        {
00198               /* First see if there is a current chunk, and if it has space */
00199               if (l->tail && l->tail->space) {
00200                      uint64_t towrite = len;
00201                      if (towrite > l->tail->space)
00202                             towrite = l->tail->space;
00203                      memcpy(l->tail->writeptr, data, towrite);
00204                      l->tail->length += towrite;
00205                      l->tail->space -= towrite;
00206                      l->tail->writeptr += towrite;
00207                      len -= towrite;
00208                      data += towrite;
00209               }
00210 
00211               if (len>0) {
00212                      /* We still need to write more, so prepare a new chunk */
00213                      if ((NULL == (buf = malloc(size))) || (NULL == (pchunk = calloc(1, sizeof(struct chunk))))) {
00214                             g_critical("Out of memory");
00215                             exit (1);
00216                      }
00217 
00218                      pchunk->buffer = buf;
00219                      pchunk->readptr = buf;
00220                      pchunk->writeptr = buf;
00221                      pchunk->space = size;
00222                      chunklist_addtail(l, pchunk);
00223               }
00224        }
00225 
00226 }
00227 
00228 /* returns 0 on success, -1 on failure */
00229 int writebuffer(int fd, struct chunklist * l) {
00230 
00231        struct chunk * pchunk = NULL;
00232        int res;
00233        if (!l)
00234               return 0;
00235 
00236        while (!pchunk)
00237        {
00238               pchunk = l->head;
00239               if (!pchunk)
00240                      return 0;
00241               if (!(pchunk->length) || !(pchunk->readptr)) {
00242                      chunklist_unlink(l, pchunk);
00243                      free(pchunk->buffer);
00244                      free(pchunk);
00245                      pchunk = NULL;
00246               }
00247        }
00248        
00249        /* OK we have a chunk with some data in */
00250        res = write(fd, pchunk->readptr, pchunk->length);
00251        if (res==0)
00252               errno = EAGAIN;
00253        if (res<=0)
00254               return -1;
00255        pchunk->length -= res;
00256        pchunk->readptr += res;
00257        if (!pchunk->length) {
00258               chunklist_unlink(l, pchunk);
00259               free(pchunk->buffer);
00260               free(pchunk);
00261        }
00262        return 0;
00263 }
00264 
00265 
00266 
00267 #define TEST_WRITE (1<<0)
00268 #define TEST_FLUSH (1<<1)
00269 
00270 int timeval_subtract (struct timeval *result, struct timeval *x,
00271                     struct timeval *y) {
00272        if (x->tv_usec < y->tv_usec) {
00273               int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
00274               y->tv_usec -= 1000000 * nsec;
00275               y->tv_sec += nsec;
00276        }
00277        
00278        if (x->tv_usec - y->tv_usec > 1000000) {
00279               int nsec = (x->tv_usec - y->tv_usec) / 1000000;
00280               y->tv_usec += 1000000 * nsec;
00281               y->tv_sec -= nsec;
00282        }
00283        
00284        result->tv_sec = x->tv_sec - y->tv_sec;
00285        result->tv_usec = x->tv_usec - y->tv_usec;
00286        
00287        return x->tv_sec < y->tv_sec;
00288 }
00289 
00290 double timeval_diff_to_double (struct timeval * x, struct timeval * y) {
00291        struct timeval r;
00292        timeval_subtract(&r, x, y);
00293        return r.tv_sec * 1.0 + r.tv_usec/1000000.0;
00294 }
00295 
00296 static inline int read_all(int f, void *buf, size_t len) {
00297        ssize_t res;
00298        size_t retval=0;
00299 
00300        while(len>0) {
00301               if((res=read(f, buf, len)) <=0) {
00302                      if (!res)
00303                             errno=EAGAIN;
00304                      snprintf(errstr, errstr_len, "Read failed: %s", strerror(errno));
00305                      return -1;
00306               }
00307               len-=res;
00308               buf+=res;
00309               retval+=res;
00310        }
00311        return retval;
00312 }
00313 
00314 static inline int write_all(int f, void *buf, size_t len) {
00315        ssize_t res;
00316        size_t retval=0;
00317 
00318        while(len>0) {
00319               if((res=write(f, buf, len)) <=0) {
00320                      if (!res)
00321                             errno=EAGAIN;
00322                      snprintf(errstr, errstr_len, "Write failed: %s", strerror(errno));
00323                      return -1;
00324               }
00325               len-=res;
00326               buf+=res;
00327               retval+=res;
00328        }
00329        return retval;
00330 }
00331 
00332 #define READ_ALL_ERRCHK(f, buf, len, whereto, errmsg...) if((read_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); goto whereto; }
00333 #define READ_ALL_ERR_RT(f, buf, len, whereto, rval, errmsg...) if((read_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); retval = rval; goto whereto; }
00334 
00335 #define WRITE_ALL_ERRCHK(f, buf, len, whereto, errmsg...) if((write_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); goto whereto; }
00336 #define WRITE_ALL_ERR_RT(f, buf, len, whereto, rval, errmsg...) if((write_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); retval = rval; goto whereto; }
00337 
00338 int setup_connection(gchar *hostname, int port, gchar* name, CONNECTION_TYPE ctype, int* serverflags) {
00339        int sock;
00340        struct hostent *host;
00341        struct sockaddr_in addr;
00342        char buf[256];
00343        uint64_t mymagic = (name ? opts_magic : cliserv_magic);
00344        u64 tmp64;
00345        uint32_t tmp32 = 0;
00346 
00347        sock=0;
00348        if(ctype<CONNECTION_TYPE_CONNECT)
00349               goto end;
00350        if((sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0) {
00351               strncpy(errstr, strerror(errno), errstr_len);
00352               goto err;
00353        }
00354        setmysockopt(sock);
00355        if(!(host=gethostbyname(hostname))) {
00356               strncpy(errstr, strerror(errno), errstr_len);
00357               goto err_open;
00358        }
00359        addr.sin_family=AF_INET;
00360        addr.sin_port=htons(port);
00361        addr.sin_addr.s_addr=*((int *) host->h_addr);
00362        if((connect(sock, (struct sockaddr *)&addr, sizeof(addr))<0)) {
00363               strncpy(errstr, strerror(errno), errstr_len);
00364               goto err_open;
00365        }
00366        if(ctype<CONNECTION_TYPE_INIT_PASSWD)
00367               goto end;
00368        READ_ALL_ERRCHK(sock, buf, strlen(INIT_PASSWD), err_open, "Could not read INIT_PASSWD: %s", strerror(errno));
00369        if(strlen(buf)==0) {
00370               snprintf(errstr, errstr_len, "Server closed connection");
00371               goto err_open;
00372        }
00373        if(strncmp(buf, INIT_PASSWD, strlen(INIT_PASSWD))) {
00374               snprintf(errstr, errstr_len, "INIT_PASSWD does not match");
00375               goto err_open;
00376        }
00377        if(ctype<CONNECTION_TYPE_CLISERV)
00378               goto end;
00379        READ_ALL_ERRCHK(sock, &tmp64, sizeof(tmp64), err_open, "Could not read cliserv_magic: %s", strerror(errno));
00380        tmp64=ntohll(tmp64);
00381        if(tmp64 != mymagic) {
00382               strncpy(errstr, "mymagic does not match", errstr_len);
00383               goto err_open;
00384        }
00385        if(ctype<CONNECTION_TYPE_FULL)
00386               goto end;
00387        if(!name) {
00388               READ_ALL_ERRCHK(sock, &size, sizeof(size), err_open, "Could not read size: %s", strerror(errno));
00389               size=ntohll(size);
00390               READ_ALL_ERRCHK(sock, buf, 128, err_open, "Could not read data: %s", strerror(errno));
00391               goto end;
00392        }
00393        /* flags */
00394        READ_ALL_ERRCHK(sock, buf, sizeof(uint16_t), err_open, "Could not read reserved field: %s", strerror(errno));
00395        /* reserved field */
00396        WRITE_ALL_ERRCHK(sock, &tmp32, sizeof(tmp32), err_open, "Could not write reserved field: %s", strerror(errno));
00397        /* magic */
00398        tmp64 = htonll(opts_magic);
00399        WRITE_ALL_ERRCHK(sock, &tmp64, sizeof(tmp64), err_open, "Could not write magic: %s", strerror(errno));
00400        /* name */
00401        tmp32 = htonl(NBD_OPT_EXPORT_NAME);
00402        WRITE_ALL_ERRCHK(sock, &tmp32, sizeof(tmp32), err_open, "Could not write option: %s", strerror(errno));
00403        tmp32 = htonl((uint32_t)strlen(name));
00404        WRITE_ALL_ERRCHK(sock, &tmp32, sizeof(tmp32), err_open, "Could not write name length: %s", strerror(errno));
00405        WRITE_ALL_ERRCHK(sock, name, strlen(name), err_open, "Could not write name:: %s", strerror(errno));
00406        READ_ALL_ERRCHK(sock, &size, sizeof(size), err_open, "Could not read size: %s", strerror(errno));
00407        size = ntohll(size);
00408        uint16_t flags;
00409        READ_ALL_ERRCHK(sock, &flags, sizeof(uint16_t), err_open, "Could not read flags: %s", strerror(errno));
00410        flags = ntohs(flags);
00411        *serverflags = flags;
00412        READ_ALL_ERRCHK(sock, buf, 124, err_open, "Could not read reserved zeroes: %s", strerror(errno));
00413        goto end;
00414 err_open:
00415        close(sock);
00416 err:
00417        sock=-1;
00418 end:
00419        return sock;
00420 }
00421 
00422 int close_connection(int sock, CLOSE_TYPE type) {
00423        struct nbd_request req;
00424        u64 counter=0;
00425 
00426        switch(type) {
00427               case CONNECTION_CLOSE_PROPERLY:
00428                      req.magic=htonl(NBD_REQUEST_MAGIC);
00429                      req.type=htonl(NBD_CMD_DISC);
00430                      memcpy(&(req.handle), &(counter), sizeof(counter));
00431                      counter++;
00432                      req.from=0;
00433                      req.len=0;
00434                      if(write(sock, &req, sizeof(req))<0) {
00435                             snprintf(errstr, errstr_len, "Could not write to socket: %s", strerror(errno));
00436                             return -1;
00437                      }
00438               case CONNECTION_CLOSE_FAST:
00439                      if(close(sock)<0) {
00440                             snprintf(errstr, errstr_len, "Could not close socket: %s", strerror(errno));
00441                             return -1;
00442                      }
00443                      break;
00444               default:
00445                      g_critical("Your compiler is on crack!"); /* or I am buggy */
00446                      return -1;
00447        }
00448        return 0;
00449 }
00450 
00451 int read_packet_check_header(int sock, size_t datasize, long long int curhandle) {
00452        struct nbd_reply rep;
00453        int retval=0;
00454        char buf[datasize];
00455 
00456        READ_ALL_ERR_RT(sock, &rep, sizeof(rep), end, -1, "Could not read reply header: %s", strerror(errno));
00457        rep.magic=ntohl(rep.magic);
00458        rep.error=ntohl(rep.error);
00459        if(rep.magic!=NBD_REPLY_MAGIC) {
00460               snprintf(errstr, errstr_len, "Received package with incorrect reply_magic. Index of sent packages is %lld (0x%llX), received handle is %lld (0x%llX). Received magic 0x%lX, expected 0x%lX", (long long int)curhandle, (long long unsigned int)curhandle, (long long int)*((u64*)rep.handle), (long long unsigned int)*((u64*)rep.handle), (long unsigned int)rep.magic, (long unsigned int)NBD_REPLY_MAGIC);
00461               retval=-1;
00462               goto end;
00463        }
00464        if(rep.error) {
00465               snprintf(errstr, errstr_len, "Received error from server: %ld (0x%lX). Handle is %lld (0x%llX).", (long int)rep.error, (long unsigned int)rep.error, (long long int)(*((u64*)rep.handle)), (long long unsigned int)*((u64*)rep.handle));
00466               retval=-1;
00467               goto end;
00468        }
00469        if (datasize)
00470               READ_ALL_ERR_RT(sock, &buf, datasize, end, -1, "Could not read data: %s", strerror(errno));
00471 
00472 end:
00473        return retval;
00474 }
00475 
00476 int oversize_test(gchar* hostname, int port, char* name, int sock,
00477                 char sock_is_open, char close_sock, int testflags) {
00478        int retval=0;
00479        struct nbd_request req;
00480        struct nbd_reply rep;
00481        int i=0;
00482        int serverflags = 0;
00483        pid_t G_GNUC_UNUSED mypid = getpid();
00484        char buf[((1024*1024)+sizeof(struct nbd_request)/2)<<1];
00485        bool got_err;
00486 
00487        /* This should work */
00488        if(!sock_is_open) {
00489               if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL, &serverflags))<0) {
00490                      g_warning("Could not open socket: %s", errstr);
00491                      retval=-1;
00492                      goto err;
00493               }
00494        }
00495        req.magic=htonl(NBD_REQUEST_MAGIC);
00496        req.type=htonl(NBD_CMD_READ);
00497        req.len=htonl(1024*1024);
00498        memcpy(&(req.handle),&i,sizeof(i));
00499        req.from=htonll(i);
00500        WRITE_ALL_ERR_RT(sock, &req, sizeof(req), err, -1, "Could not write request: %s", strerror(errno));
00501        printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
00502        READ_ALL_ERR_RT(sock, &rep, sizeof(struct nbd_reply), err, -1, "Could not read reply header: %s", strerror(errno));
00503        READ_ALL_ERR_RT(sock, &buf, ntohl(req.len), err, -1, "Could not read data: %s", strerror(errno));
00504        if(rep.error) {
00505               snprintf(errstr, errstr_len, "Received unexpected error: %d", rep.error);
00506               retval=-1;
00507               goto err;
00508        } else {
00509               printf("OK\n");
00510        }
00511        /* This probably should not work */
00512        i++; req.from=htonll(i);
00513        req.len = htonl(ntohl(req.len) + sizeof(struct nbd_request) / 2);
00514        WRITE_ALL_ERR_RT(sock, &req, sizeof(req), err, -1, "Could not write request: %s", strerror(errno));
00515        printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
00516        READ_ALL_ERR_RT(sock, &rep, sizeof(struct nbd_reply), err, -1, "Could not read reply header: %s", strerror(errno));
00517        READ_ALL_ERR_RT(sock, &buf, ntohl(req.len), err, -1, "Could not read data: %s", strerror(errno));
00518        if(rep.error) {
00519               printf("Received expected error\n");
00520               got_err=true;
00521        } else {
00522               printf("OK\n");
00523               got_err=false;
00524        }
00525        /* ... unless this works, too */
00526        i++; req.from=htonll(i);
00527        req.len = htonl(ntohl(req.len) << 1);
00528        WRITE_ALL_ERR_RT(sock, &req, sizeof(req), err, -1, "Could not write request: %s", strerror(errno));
00529        printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
00530        READ_ALL_ERR_RT(sock, &rep, sizeof(struct nbd_reply), err, -1, "Could not read reply header: %s", strerror(errno));
00531        READ_ALL_ERR_RT(sock, &buf, ntohl(req.len), err, -1, "Could not read data: %s", strerror(errno));
00532        if(rep.error) {
00533               printf("error\n");
00534        } else {
00535               printf("OK\n");
00536        }
00537        if((rep.error && !got_err) || (!rep.error && got_err)) {
00538               printf("Received unexpected error\n");
00539               retval=-1;
00540        }
00541   err:
00542        return retval;
00543 }
00544 
00545 int throughput_test(gchar* hostname, int port, char* name, int sock,
00546                   char sock_is_open, char close_sock, int testflags) {
00547        long long int i;
00548        char writebuf[1024];
00549        struct nbd_request req;
00550        int requests=0;
00551        fd_set set;
00552        struct timeval tv;
00553        struct timeval start;
00554        struct timeval stop;
00555        double timespan;
00556        double speed;
00557        char speedchar[2] = { '\0', '\0' };
00558        int retval=0;
00559        int serverflags = 0;
00560        signed int do_write=TRUE;
00561        pid_t mypid = getpid();
00562 
00563 
00564        if (!(testflags & TEST_WRITE))
00565               testflags &= ~TEST_FLUSH;
00566 
00567        memset (writebuf, 'X', 1024);
00568        size=0;
00569        if(!sock_is_open) {
00570               if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL, &serverflags))<0) {
00571                      g_warning("Could not open socket: %s", errstr);
00572                      retval=-1;
00573                      goto err;
00574               }
00575        }
00576        if ((testflags & TEST_FLUSH) && ((serverflags & (NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA))
00577                                     != (NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA))) {
00578               snprintf(errstr, errstr_len, "Server did not supply flush capability flags");
00579               retval = -1;
00580               goto err_open;
00581        }
00582        req.magic=htonl(NBD_REQUEST_MAGIC);
00583        req.len=htonl(1024);
00584        if(gettimeofday(&start, NULL)<0) {
00585               retval=-1;
00586               snprintf(errstr, errstr_len, "Could not measure start time: %s", strerror(errno));
00587               goto err_open;
00588        }
00589        for(i=0;i+1024<=size;i+=1024) {
00590               if(do_write) {
00591                      int sendfua = (testflags & TEST_FLUSH) && (((i>>10) & 15) == 3);
00592                      int sendflush = (testflags & TEST_FLUSH) && (((i>>10) & 15) == 11);
00593                      req.type=htonl((testflags & TEST_WRITE)?NBD_CMD_WRITE:NBD_CMD_READ);
00594                      if (sendfua)
00595                             req.type = htonl(NBD_CMD_WRITE | NBD_CMD_FLAG_FUA);
00596                      memcpy(&(req.handle),&i,sizeof(i));
00597                      req.from=htonll(i);
00598                      if (write_all(sock, &req, sizeof(req)) <0) {
00599                             retval=-1;
00600                             goto err_open;
00601                      }
00602                      if (testflags & TEST_WRITE) {
00603                             if (write_all(sock, writebuf, 1024) <0) {
00604                                    retval=-1;
00605                                    goto err_open;
00606                             }
00607                      }
00608                      printf("%d: Requests(+): %d\r", (int)mypid, ++requests);
00609                      if (sendflush) {
00610                             long long int j = i ^ (1LL<<63);
00611                             req.type = htonl(NBD_CMD_FLUSH);
00612                             memcpy(&(req.handle),&j,sizeof(j));
00613                             req.from=0;
00614                             if (write_all(sock, &req, sizeof(req)) <0) {
00615                                    retval=-1;
00616                                    goto err_open;
00617                             }
00618                             printf("%d: Requests(+): %d\r", (int)mypid, ++requests);
00619                      }
00620               }
00621               do {
00622                      FD_ZERO(&set);
00623                      FD_SET(sock, &set);
00624                      tv.tv_sec=0;
00625                      tv.tv_usec=0;
00626                      select(sock+1, &set, NULL, NULL, &tv);
00627                      if(FD_ISSET(sock, &set)) {
00628                             /* Okay, there's something ready for
00629                              * reading here */
00630                             if(read_packet_check_header(sock, (testflags & TEST_WRITE)?0:1024, i)<0) {
00631                                    retval=-1;
00632                                    goto err_open;
00633                             }
00634                             printf("%d: Requests(-): %d\r", (int)mypid, --requests);
00635                      }
00636               } while FD_ISSET(sock, &set);
00637               /* Now wait until we can write again or until a second have
00638                * passed, whichever comes first*/
00639               FD_ZERO(&set);
00640               FD_SET(sock, &set);
00641               tv.tv_sec=1;
00642               tv.tv_usec=0;
00643               do_write=select(sock+1,NULL,&set,NULL,&tv);
00644               if(!do_write) printf("Select finished\n");
00645               if(do_write<0) {
00646                      snprintf(errstr, errstr_len, "select: %s", strerror(errno));
00647                      retval=-1;
00648                      goto err_open;
00649               }
00650        }
00651        /* Now empty the read buffer */
00652        do {
00653               FD_ZERO(&set);
00654               FD_SET(sock, &set);
00655               tv.tv_sec=0;
00656               tv.tv_usec=0;
00657               select(sock+1, &set, NULL, NULL, &tv);
00658               if(FD_ISSET(sock, &set)) {
00659                      /* Okay, there's something ready for
00660                       * reading here */
00661                      read_packet_check_header(sock, (testflags & TEST_WRITE)?0:1024, i);
00662                      printf("%d: Requests(-): %d\r", (int)mypid, --requests);
00663               }
00664        } while (requests);
00665        printf("\n");
00666        if(gettimeofday(&stop, NULL)<0) {
00667               retval=-1;
00668               snprintf(errstr, errstr_len, "Could not measure end time: %s", strerror(errno));
00669               goto err_open;
00670        }
00671        timespan=timeval_diff_to_double(&stop, &start);
00672        speed=size/timespan;
00673        if(speed>1024) {
00674               speed=speed/1024.0;
00675               speedchar[0]='K';
00676        }
00677        if(speed>1024) {
00678               speed=speed/1024.0;
00679               speedchar[0]='M';
00680        }
00681        if(speed>1024) {
00682               speed=speed/1024.0;
00683               speedchar[0]='G';
00684        }
00685        g_message("%d: Throughput %s test (%s flushes) complete. Took %.3f seconds to complete, %.3f%sib/s", (int)getpid(), (testflags & TEST_WRITE)?"write":"read", (testflags & TEST_FLUSH)?"with":"without", timespan, speed, speedchar);
00686 
00687 err_open:
00688        if(close_sock) {
00689               close_connection(sock, CONNECTION_CLOSE_PROPERLY);
00690        }
00691 err:
00692        return retval;
00693 }
00694 
00695 /*
00696  * fill 512 byte buffer 'buf' with a hashed selection of interesting data based
00697  * only on handle and blknum. The first word is blknum, and the second handle, for ease
00698  * of understanding. Things with handle 0 are blank.
00699  */
00700 static inline void makebuf(char *buf, uint64_t seq, uint64_t blknum) {
00701        uint64_t x = ((uint64_t)blknum) ^ (seq << 32) ^ (seq >> 32);
00702        uint64_t* p = (uint64_t*)buf;
00703        int i;
00704        if (!seq) {
00705               bzero(buf, 512);
00706               return;
00707        }
00708        for (i = 0; i<512/sizeof(uint64_t); i++) {
00709               int s;
00710               *(p++) = x;
00711               x+=0xFEEDA1ECDEADBEEFULL+i+(((uint64_t)i)<<56);
00712               s = x & 63;
00713               x = x ^ (x<<s) ^ (x>>(64-s)) ^ 0xAA55AA55AA55AA55ULL ^ seq;
00714        }
00715 }
00716               
00717 static inline int checkbuf(char *buf, uint64_t seq, uint64_t blknum) {
00718        uint64_t cmp[64]; // 512/8 = 64
00719        makebuf((char *)cmp, seq, blknum);
00720        return memcmp(cmp, buf, 512)?-1:0;
00721 }
00722 
00723 static inline void dumpcommand(char * text, uint32_t command)
00724 {
00725 #ifdef DEBUG_COMMANDS
00726        command=ntohl(command);
00727        char * ctext;
00728        switch (command & NBD_CMD_MASK_COMMAND) {
00729        case NBD_CMD_READ:
00730               ctext="NBD_CMD_READ";
00731               break;
00732        case NBD_CMD_WRITE:
00733               ctext="NBD_CMD_WRITE";
00734               break;
00735        case NBD_CMD_DISC:
00736               ctext="NBD_CMD_DISC";
00737               break;
00738        case NBD_CMD_FLUSH:
00739               ctext="NBD_CMD_FLUSH";
00740               break;
00741        default:
00742               ctext="UNKNOWN";
00743               break;
00744        }
00745        printf("%s: %s [%s] (0x%08x)\n",
00746               text,
00747               ctext,
00748               (command & NBD_CMD_FLAG_FUA)?"FUA":"NONE",
00749               command);
00750 #endif
00751 }
00752 
00753 /* return an unused handle */
00754 uint64_t getrandomhandle(GHashTable *phash) {
00755        uint64_t handle = 0;
00756        int i;
00757        do {
00758               /* RAND_MAX may be as low as 2^15 */
00759               for (i= 1 ; i<=5; i++)
00760                      handle ^= random() ^ (handle << 15); 
00761        } while (g_hash_table_lookup(phash, &handle));
00762        return handle;
00763 }
00764 
00765 int integrity_test(gchar* hostname, int port, char* name, int sock,
00766                  char sock_is_open, char close_sock, int testflags) {
00767        struct nbd_reply rep;
00768        fd_set rset;
00769        fd_set wset;
00770        struct timeval tv;
00771        struct timeval start;
00772        struct timeval stop;
00773        double timespan;
00774        double speed;
00775        char speedchar[2] = { '\0', '\0' };
00776        int retval=0;
00777        int serverflags = 0;
00778        pid_t G_GNUC_UNUSED mypid = getpid();
00779        int blkhashfd = -1;
00780        char *blkhashname=NULL;
00781        struct blkitem *blkhash = NULL;
00782        int logfd=-1;
00783        uint64_t seq=1;
00784        uint64_t processed=0;
00785        uint64_t printer=0;
00786        uint64_t xfer=0;
00787        int readtransactionfile = 1;
00788        int blocked = 0;
00789        struct rclist txqueue={NULL, NULL, 0};
00790        struct rclist inflight={NULL, NULL, 0};
00791        struct chunklist txbuf={NULL, NULL, 0};
00792 
00793        GHashTable *handlehash = g_hash_table_new(g_int64_hash, g_int64_equal);
00794 
00795        size=0;
00796        if(!sock_is_open) {
00797               if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL, &serverflags))<0) {
00798                      g_warning("Could not open socket: %s", errstr);
00799                      retval=-1;
00800                      goto err;
00801               }
00802        }
00803 
00804        if ((serverflags & (NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA))
00805            != (NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA))
00806               g_warning("Server flags do not support FLUSH and FUA - these may error");
00807 
00808 #ifdef HAVE_MKSTEMP
00809        blkhashname=strdup("/tmp/blkarray-XXXXXX");
00810        if (!blkhashname || (-1 == (blkhashfd = mkstemp(blkhashname)))) {
00811               g_warning("Could not open temp file: %s", strerror(errno));
00812               retval=-1;
00813               goto err;
00814        }
00815 #else
00816        /* use tmpnam here to avoid further feature test nightmare */
00817        if (-1 == (blkhashfd = open(blkhashname=strdup(tmpnam(NULL)),
00818                                 O_CREAT | O_RDWR,
00819                                 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))) {
00820               g_warning("Could not open temp file: %s", strerror(errno));
00821               retval=-1;
00822               goto err;
00823        }
00824 #endif
00825        /* Ensure space freed if we die */
00826        if (-1 == unlink(blkhashname)) {
00827               g_warning("Could not unlink temp file: %s", strerror(errno));
00828               retval=-1;
00829               goto err;
00830        }
00831 
00832        if (-1 == lseek(blkhashfd, (off_t)((size>>9)*sizeof(struct blkitem)), SEEK_SET)) {
00833               g_warning("Could not llseek temp file: %s", strerror(errno));
00834               retval=-1;
00835               goto err;
00836        }
00837 
00838        if (-1 == write(blkhashfd, "\0", 1)) {
00839               g_warning("Could not write temp file: %s", strerror(errno));
00840               retval=-1;
00841               goto err;
00842        }
00843 
00844        if (NULL == (blkhash = mmap(NULL,
00845                                 (size>>9)*sizeof(struct blkitem),
00846                                 PROT_READ | PROT_WRITE,
00847                                 MAP_SHARED,
00848                                 blkhashfd,
00849                                 0))) {
00850               g_warning("Could not mmap temp file: %s", strerror(errno));
00851               retval=-1;
00852               goto err;
00853        }
00854 
00855        if (-1 == (logfd = open(transactionlog, O_RDONLY)))
00856        {
00857               g_warning("Could open log file: %s", strerror(errno));
00858               retval=-1;
00859               goto err;
00860        }
00861               
00862        if(gettimeofday(&start, NULL)<0) {
00863               retval=-1;
00864               snprintf(errstr, errstr_len, "Could not measure start time: %s", strerror(errno));
00865               goto err_open;
00866        }
00867 
00868        while (readtransactionfile || txqueue.numitems || txbuf.numitems || inflight.numitems) {
00869               int ret;
00870 
00871               uint32_t magic;
00872                 uint32_t command;
00873                 uint64_t from;
00874                 uint32_t len;
00875               struct reqcontext * prc;
00876 
00877               *errstr=0;
00878 
00879               FD_ZERO(&wset);
00880               FD_ZERO(&rset);
00881               if (readtransactionfile)
00882                      FD_SET(logfd, &rset);
00883               if ((!blocked && txqueue.numitems) || txbuf.numitems)
00884                      FD_SET(sock, &wset);
00885               if (inflight.numitems)
00886                      FD_SET(sock, &rset);
00887               tv.tv_sec=5;
00888               tv.tv_usec=0;
00889               ret = select(1+((sock>logfd)?sock:logfd), &rset, &wset, NULL, &tv);
00890               if (ret == 0) {
00891                      retval=-1;
00892                      snprintf(errstr, errstr_len, "Timeout reading from socket");
00893                      goto err_open;
00894               } else if (ret<0) {
00895                      g_warning("Could not mmap temp file: %s", errstr);
00896                      retval=-1;
00897                      goto err;
00898               }
00899               /* We know we've got at least one thing to do here then */
00900 
00901               /* Get a command from the transaction log */
00902               if (FD_ISSET(logfd, &rset)) {
00903                      
00904                      /* Read a request or reply from the transaction file */
00905                      READ_ALL_ERRCHK(logfd,
00906                                    &magic,
00907                                    sizeof(magic),
00908                                    err_open,
00909                                    "Could not read transaction log: %s",
00910                                    strerror(errno));
00911                      magic = ntohl(magic);
00912                      switch (magic) {
00913                      case NBD_REQUEST_MAGIC:
00914                             if (NULL == (prc = calloc(1, sizeof(struct reqcontext)))) {
00915                                    retval=-1;
00916                                    snprintf(errstr, errstr_len, "Could not allocate request");
00917                                    goto err_open;
00918                             }
00919                             READ_ALL_ERRCHK(logfd,
00920                                           sizeof(magic)+(char *)&(prc->req),
00921                                           sizeof(struct nbd_request)-sizeof(magic),
00922                                           err_open,
00923                                           "Could not read transaction log: %s",
00924                                           strerror(errno));
00925                             prc->req.magic = htonl(NBD_REQUEST_MAGIC);
00926                             memcpy(prc->orighandle, prc->req.handle, 8);
00927                             prc->seq=seq++;
00928                             if ((ntohl(prc->req.type) & NBD_CMD_MASK_COMMAND) == NBD_CMD_DISC) {
00929                                    /* no more to read; don't enqueue as no reply
00930                                     * we will disconnect manually at the end
00931                                     */
00932                                    readtransactionfile = 0;
00933                                    free (prc);
00934                             } else {
00935                                    dumpcommand("Enqueuing command", prc->req.type);
00936                                    rclist_addtail(&txqueue, prc);
00937                             }
00938                             prc = NULL;
00939                             break;
00940                      case NBD_REPLY_MAGIC:
00941                             READ_ALL_ERRCHK(logfd,
00942                                           sizeof(magic)+(char *)(&rep),
00943                                           sizeof(struct nbd_reply)-sizeof(magic),
00944                                           err_open,
00945                                           "Could not read transaction log: %s",
00946                                           strerror(errno));
00947 
00948                             if (rep.error) {
00949                                    retval=-1;
00950                                    snprintf(errstr, errstr_len, "Transaction log file contained errored transaction");
00951                                    goto err_open;
00952                             }
00953                                    
00954                             /* We do not need to consume data on a read reply as there is
00955                              * none in the log */
00956                             break;
00957                      default:
00958                             retval=-1;
00959                             snprintf(errstr, errstr_len, "Could not measure start time: %08x", magic);
00960                             goto err_open;
00961                      }
00962               }
00963 
00964               /* See if we have a write we can do */
00965               if (FD_ISSET(sock, &wset))
00966               {
00967                      if ((!(txqueue.head) && !(txbuf.head)) || blocked)
00968                             g_warning("Socket write FD set but we shouldn't have been interested");
00969 
00970                      /* If there is no buffered data, generate some */
00971                      if (!blocked && !(txbuf.head) && (NULL != (prc = txqueue.head)))
00972                      {
00973                             if (ntohl(prc->req.magic) != NBD_REQUEST_MAGIC) {
00974                                    retval=-1;
00975                                    g_warning("Asked to write a request without a magic number");
00976                                    goto err_open;
00977                             }
00978                                    
00979                             command = ntohl(prc->req.type);
00980                             from = ntohll(prc->req.from);
00981                             len = ntohl(prc->req.len);
00982 
00983                             /* First check whether we can touch this command at all. If this
00984                              * command is a read, and there is an inflight write, OR if this
00985                              * command is a write, and there is an inflight read or write, then
00986                              * we need to leave the command alone and signal that we are blocked
00987                              */
00988                             
00989                             if (!looseordering)
00990                             {
00991                                    uint64_t cfrom;
00992                                    uint32_t clen;
00993                                    cfrom = from;
00994                                    clen = len;
00995                                    while (clen > 0) {
00996                                           uint64_t blknum = cfrom>>9;
00997                                           if (cfrom>=size) {
00998                                                  snprintf(errstr, errstr_len, "offset %llx beyond size %llx",
00999                                                          (long long int) cfrom, (long long int)size);
01000                                                  goto err_open;
01001                                           }
01002                                           if (blkhash[blknum].inflightw ||
01003                                               (blkhash[blknum].inflightr &&
01004                                                ((command & NBD_CMD_MASK_COMMAND)==NBD_CMD_WRITE))) {
01005                                                  blocked=1;
01006                                                  break;
01007                                           }
01008                                           cfrom += 512;
01009                                           clen -= 512;
01010                                    }
01011                             }
01012 
01013                             if (blocked)
01014                                    goto skipdequeue;
01015 
01016                             rclist_unlink(&txqueue, prc);
01017                             rclist_addtail(&inflight, prc);
01018                             
01019                             dumpcommand("Sending command", prc->req.type);
01020                             /* we rewrite the handle as they otherwise may not be unique */
01021                             *((uint64_t*)(prc->req.handle))=getrandomhandle(handlehash);
01022                             g_hash_table_insert(handlehash, prc->req.handle, prc);
01023                             addbuffer(&txbuf, &(prc->req), sizeof(struct nbd_request));
01024                             switch (command & NBD_CMD_MASK_COMMAND) {
01025                             case NBD_CMD_WRITE:
01026                                    xfer+=len;
01027                                    while (len > 0)      {
01028                                           uint64_t blknum = from>>9;
01029                                           char dbuf[512];
01030                                           if (from>=size) {
01031                                                  snprintf(errstr, errstr_len, "offset %llx beyond size %llx",
01032                                                          (long long int) from, (long long int)size);
01033                                                  goto err_open;
01034                                           }
01035                                           (blkhash[blknum].inflightw)++;
01036                                           /* work out what we should be writing */
01037                                           makebuf(dbuf, prc->seq, blknum);
01038                                           addbuffer(&txbuf, dbuf, 512);
01039                                           from += 512;
01040                                           len -= 512;
01041                                    }
01042                                    break;
01043                             case NBD_CMD_READ:
01044                                    xfer+=len;
01045                                    while (len > 0)      {
01046                                           uint64_t blknum = from>>9;
01047                                           if (from>=size) {
01048                                                  snprintf(errstr, errstr_len, "offset %llx beyond size %llx",
01049                                                          (long long int) from, (long long int)size);
01050                                                  goto err_open;
01051                                           }
01052                                           (blkhash[blknum].inflightr)++;
01053                                           from += 512;
01054                                           len -= 512;
01055                                    }
01056                                    break;
01057                             case NBD_CMD_DISC:
01058                             case NBD_CMD_FLUSH:
01059                                    break;
01060                             default:
01061                                    retval=-1;
01062                                    snprintf(errstr, errstr_len, "Incomprehensible command: %08x", command);
01063                                    goto err_open;
01064                                    break;
01065                             }
01066                             
01067                             prc = NULL;
01068                      }
01069               skipdequeue:
01070 
01071                      /* there should be some now */
01072                      if (writebuffer(sock, &txbuf)<0) {
01073                             retval=-1;
01074                             snprintf(errstr, errstr_len, "Failed to write to socket buffer: %s", strerror(errno));
01075                             goto err_open;
01076                      }
01077                      
01078               }
01079 
01080               /* See if there is a reply to be processed from the socket */
01081               if(FD_ISSET(sock, &rset)) {
01082                      /* Okay, there's something ready for
01083                       * reading here */
01084                      
01085                      READ_ALL_ERRCHK(sock,
01086                                    &rep,
01087                                    sizeof(struct nbd_reply),
01088                                    err_open,
01089                                    "Could not read from server socket: %s",
01090                                    strerror(errno));
01091                      
01092                      if (rep.magic != htonl(NBD_REPLY_MAGIC)) {
01093                             retval=-1;
01094                             snprintf(errstr, errstr_len, "Bad magic from server");
01095                             goto err_open;
01096                      }
01097                      
01098                      if (rep.error) {
01099                             retval=-1;
01100                             snprintf(errstr, errstr_len, "Server errored a transaction");
01101                             goto err_open;
01102                      }
01103                             
01104                      uint64_t handle;
01105                      memcpy(&handle,rep.handle,8);
01106                      prc = g_hash_table_lookup(handlehash, &handle);
01107                      if (!prc) {
01108                             retval=-1;
01109                             snprintf(errstr, errstr_len, "Unrecognised handle in reply: 0x%llX", *(long long unsigned int*)(rep.handle));
01110                             goto err_open;
01111                      }
01112                      if (!g_hash_table_remove(handlehash, &handle)) {
01113                             retval=-1;
01114                             snprintf(errstr, errstr_len, "Could not remove handle from hash: 0x%llX", *(long long unsigned int*)(rep.handle));
01115                             goto err_open;
01116                      }
01117 
01118                      if (prc->req.magic != htonl(NBD_REQUEST_MAGIC)) {
01119                             retval=-1;
01120                             snprintf(errstr, errstr_len, "Bad magic in inflight data: %08x", prc->req.magic);
01121                             goto err_open;
01122                      }
01123                      
01124                      dumpcommand("Processing reply to command", prc->req.type);
01125                      command = ntohl(prc->req.type);
01126                      from = ntohll(prc->req.from);
01127                      len = ntohl(prc->req.len);
01128                      
01129                      switch (command & NBD_CMD_MASK_COMMAND) {
01130                      case NBD_CMD_READ:
01131                             while (len > 0)      {
01132                                    uint64_t blknum = from>>9;
01133                                    char dbuf[512];
01134                                    if (from>=size) {
01135                                           snprintf(errstr, errstr_len, "offset %llx beyond size %llx",
01136                                                   (long long int) from, (long long int)size);
01137                                           goto err_open;
01138                                    }
01139                                    READ_ALL_ERRCHK(sock,
01140                                                  dbuf,
01141                                                  512,
01142                                                  err_open,
01143                                                  "Could not read data: %s",
01144                                                  strerror(errno));
01145                                    if (--(blkhash[blknum].inflightr) <0 ) {
01146                                           snprintf(errstr, errstr_len, "Received a read reply for offset %llx when not in flight",
01147                                                   (long long int) from);
01148                                           goto err_open;
01149                                    }
01150                                    /* work out what we was written */
01151                                    if (checkbuf(dbuf, blkhash[blknum].seq, blknum)) {
01152                                           retval=-1;
01153                                           snprintf(errstr, errstr_len, "Bad reply data: I wanted blk %08x, seq %08x but I got (at a guess) blk %08x, seq %08x",
01154                                                   (unsigned int) blknum,
01155                                                   blkhash[blknum].seq,
01156                                                   ((uint32_t *)(dbuf))[0],
01157                                                   ((uint32_t *)(dbuf))[1]
01158                                                   );
01159                                           goto err_open;
01160                                           
01161                                    }
01162                                    from += 512;
01163                                    len -= 512;
01164                             }
01165                             break;
01166                      case NBD_CMD_WRITE:
01167                             /* subsequent reads should get data with this seq*/
01168                             while (len > 0)      {
01169                                    uint64_t blknum = from>>9;
01170                                    if (--(blkhash[blknum].inflightw) <0 ) {
01171                                           snprintf(errstr, errstr_len, "Received a write reply for offset %llx when not in flight",
01172                                                   (long long int) from);
01173                                           goto err_open;
01174                                    }
01175                                    blkhash[blknum].seq=(uint32_t)(prc->seq);
01176                                    from += 512;
01177                                    len -= 512;
01178                             }
01179                             break;
01180                      default:
01181                             break;
01182                      }
01183                      blocked = 0;
01184                      processed++;
01185                      rclist_unlink(&inflight, prc);
01186                      prc->req.magic=0; /* so a duplicate reply is detected */
01187                      free(prc);
01188               }
01189 
01190               if (!(printer++ % 1000) || !(readtransactionfile || txqueue.numitems || inflight.numitems) )
01191                      printf("%d: Seq %08lld Queued: %08d Inflight: %08d Done: %08lld\r",
01192                             (int)mypid,
01193                             (long long int) seq,
01194                             txqueue.numitems,
01195                             inflight.numitems,
01196                             (long long int) processed);
01197 
01198        }
01199 
01200        printf("\n");
01201 
01202        if (gettimeofday(&stop, NULL)<0) {
01203               retval=-1;
01204               snprintf(errstr, errstr_len, "Could not measure end time: %s", strerror(errno));
01205               goto err_open;
01206        }
01207        timespan=timeval_diff_to_double(&stop, &start);
01208        speed=xfer/timespan;
01209        if(speed>1024) {
01210               speed=speed/1024.0;
01211               speedchar[0]='K';
01212        }
01213        if(speed>1024) {
01214               speed=speed/1024.0;
01215               speedchar[0]='M';
01216        }
01217        if(speed>1024) {
01218               speed=speed/1024.0;
01219               speedchar[0]='G';
01220        }
01221        g_message("%d: Integrity %s test complete. Took %.3f seconds to complete, %.3f%sib/s", (int)getpid(), (testflags & TEST_WRITE)?"write":"read", timespan, speed, speedchar);
01222 
01223 err_open:
01224        if(close_sock) {
01225               close_connection(sock, CONNECTION_CLOSE_PROPERLY);
01226        }
01227 err:
01228        if (size && blkhash)
01229               munmap(blkhash, (size>>9)*sizeof(struct blkitem));
01230 
01231        if (blkhashfd != -1)
01232               close (blkhashfd);
01233 
01234        if (logfd != -1)
01235               close (logfd);
01236 
01237        if (blkhashname)
01238               free(blkhashname);
01239 
01240        if (*errstr)
01241               g_warning("%s",errstr);
01242 
01243        g_hash_table_destroy(handlehash);
01244 
01245        return retval;
01246 }
01247 
01248 typedef int (*testfunc)(gchar*, int, char*, int, char, char, int);
01249 
01250 int main(int argc, char**argv) {
01251        gchar *hostname;
01252        long int p = 0;
01253        char* name = NULL;
01254        int sock=0;
01255        int c;
01256        int nonopt=0;
01257        int testflags=0;
01258        testfunc test = throughput_test;
01259 
01260        /* Ignore SIGPIPE as we want to pick up the error from write() */
01261        signal (SIGPIPE, SIG_IGN);
01262 
01263        if(argc<3) {
01264               g_message("%d: Not enough arguments", (int)getpid());
01265               g_message("%d: Usage: %s <hostname> <port>", (int)getpid(), argv[0]);
01266               g_message("%d: Or: %s <hostname> -N <exportname> [<port>]", (int)getpid(), argv[0]);
01267               exit(EXIT_FAILURE);
01268        }
01269        logging();
01270        while((c=getopt(argc, argv, "-N:t:owfil"))>=0) {
01271               switch(c) {
01272                      case 1:
01273                             switch(nonopt) {
01274                                    case 0:
01275                                           hostname=g_strdup(optarg);
01276                                           nonopt++;
01277                                           break;
01278                                    case 1:
01279                                           p=(strtol(argv[2], NULL, 0));
01280                                           if(p==LONG_MIN||p==LONG_MAX) {
01281                                                  g_critical("Could not parse port number: %s", strerror(errno));
01282                                                  exit(EXIT_FAILURE);
01283                                           }
01284                                           break;
01285                             }
01286                             break;
01287                      case 'N':
01288                             name=g_strdup(optarg);
01289                             if(!p) {
01290                                    p = 10809;
01291                             }
01292                             break;
01293                      case 't':
01294                             transactionlog=g_strdup(optarg);
01295                             break;
01296                      case 'o':
01297                             test=oversize_test;
01298                             break;
01299                      case 'l':
01300                             looseordering=1;
01301                             break;
01302                      case 'w':
01303                             testflags|=TEST_WRITE;
01304                             break;
01305                      case 'f':
01306                             testflags|=TEST_FLUSH;
01307                             break;
01308                      case 'i':
01309                             test=integrity_test;
01310                             break;
01311               }
01312        }
01313 
01314        if(test(hostname, (int)p, name, sock, FALSE, TRUE, testflags)<0) {
01315               g_warning("Could not run test: %s", errstr);
01316               exit(EXIT_FAILURE);
01317        }
01318 
01319        return 0;
01320 }