00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <string.h>
00027 #include <sys/time.h>
00028 #include <sys/types.h>
00029 #include <sys/socket.h>
00030 #include <syslog.h>
00031 #include <unistd.h>
00032 #include "config.h"
00033 #include "lfs.h"
00034 #define MY_NAME "nbd-tester-client"
00035 #include "cliserv.h"
00036
00037 #include <netinet/in.h>
00038 #include <glib.h>
00039
00040 static gchar errstr[1024];
00041 const static int errstr_len=1024;
00042
00043 typedef enum {
00044 CONNECTION_TYPE_NONE,
00045 CONNECTION_TYPE_CONNECT,
00046 CONNECTION_TYPE_INIT_PASSWD,
00047 CONNECTION_TYPE_CLISERV,
00048 CONNECTION_TYPE_FULL,
00049 } CONNECTION_TYPE;
00050
00051 typedef enum {
00052 CONNECTION_CLOSE_PROPERLY,
00053 CONNECTION_CLOSE_FAST,
00054 } CLOSE_TYPE;
00055
00056 inline int read_all(int f, void *buf, size_t len) {
00057 ssize_t res;
00058 size_t retval=0;
00059
00060 while(len>0) {
00061 if((res=read(f, buf, len)) <=0) {
00062 snprintf(errstr, errstr_len, "Read failed: %s", strerror(errno));
00063 return -1;
00064 }
00065 len-=res;
00066 buf+=res;
00067 retval+=res;
00068 }
00069 return retval;
00070 }
00071
00072 int setup_connection(gchar *hostname, int port, CONNECTION_TYPE ctype) {
00073 int sock;
00074 struct hostent *host;
00075 struct sockaddr_in addr;
00076 char buf[256];
00077 u64 tmp64;
00078
00079 sock=0;
00080 if(ctype<CONNECTION_TYPE_CONNECT)
00081 goto end;
00082 if((sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0) {
00083 strncpy(errstr, strerror(errno), errstr_len);
00084 goto err;
00085 }
00086 setmysockopt(sock);
00087 if(!(host=gethostbyname(hostname))) {
00088 strncpy(errstr, strerror(errno), errstr_len);
00089 goto err_open;
00090 }
00091 addr.sin_family=AF_INET;
00092 addr.sin_port=htons(port);
00093 addr.sin_addr.s_addr=*((int *) host->h_addr);
00094 if((connect(sock, (struct sockaddr *)&addr, sizeof(addr))<0)) {
00095 strncpy(errstr, strerror(errno), errstr_len);
00096 goto err_open;
00097 }
00098 if(ctype<CONNECTION_TYPE_INIT_PASSWD)
00099 goto end;
00100 if(read_all(sock, buf, strlen(INIT_PASSWD))<0) {
00101 snprintf(errstr, errstr_len, "Could not read INIT_PASSWD: %s",
00102 strerror(errno));
00103 goto err_open;
00104 }
00105 if(strlen(buf)==0) {
00106 snprintf(errstr, errstr_len, "Server closed connection");
00107 goto err_open;
00108 }
00109 if(strncmp(buf, INIT_PASSWD, strlen(INIT_PASSWD))) {
00110 snprintf(errstr, errstr_len, "INIT_PASSWD does not match");
00111 goto err_open;
00112 }
00113 if(ctype<CONNECTION_TYPE_CLISERV)
00114 goto end;
00115 if(read_all(sock, &tmp64, sizeof(tmp64))<0) {
00116 snprintf(errstr, errstr_len, "Could not read cliserv_magic: %s",
00117 strerror(errno));
00118 goto err_open;
00119 }
00120 tmp64=ntohll(tmp64);
00121 if(tmp64 != cliserv_magic) {
00122 strncpy(errstr, "cliserv_magic does not match", errstr_len);
00123 goto err_open;
00124 }
00125 if(ctype<CONNECTION_TYPE_FULL)
00126 goto end;
00127
00128
00129
00130
00131
00132
00133
00134 read_all(sock, buf, sizeof(tmp64)+128);
00135 goto end;
00136 err_open:
00137 close(sock);
00138 err:
00139 sock=-1;
00140 end:
00141 return sock;
00142 }
00143
00144 int close_connection(int sock, CLOSE_TYPE type) {
00145 struct nbd_request req;
00146 u64 counter=0;
00147
00148 switch(type) {
00149 case CONNECTION_CLOSE_PROPERLY:
00150 req.magic=htonl(NBD_REQUEST_MAGIC);
00151 req.type=htonl(NBD_CMD_DISC);
00152 memcpy(&(req.handle), &(counter), sizeof(counter));
00153 counter++;
00154 req.from=0;
00155 req.len=0;
00156 if(write(sock, &req, sizeof(req))<0) {
00157 snprintf(errstr, errstr_len, "Could not write to socket: %s", strerror(errno));
00158 return -1;
00159 }
00160 case CONNECTION_CLOSE_FAST:
00161 if(close(sock)<0) {
00162 snprintf(errstr, errstr_len, "Could not close socket: %s", strerror(errno));
00163 return -1;
00164 }
00165 break;
00166 default:
00167 g_critical("Your compiler is on crack!");
00168 return -1;
00169 }
00170 return 0;
00171 }
00172
00173 int read_packet_check_header(int sock, size_t datasize, long long int curhandle) {
00174 struct nbd_reply rep;
00175 int retval=0;
00176 char buf[datasize];
00177
00178 read_all(sock, &rep, sizeof(rep));
00179 rep.magic=ntohl(rep.magic);
00180 rep.error=ntohl(rep.error);
00181 if(rep.magic!=NBD_REPLY_MAGIC) {
00182 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", curhandle, curhandle, *((u64*)rep.handle), *((u64*)rep.handle), (long unsigned int)rep.magic, (long unsigned int)NBD_REPLY_MAGIC);
00183 retval=-1;
00184 goto end;
00185 }
00186 if(rep.error) {
00187 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)), *((u64*)rep.handle));
00188 retval=-1;
00189 goto end;
00190 }
00191 read_all(sock, &buf, datasize);
00192
00193 end:
00194 return retval;
00195 }
00196
00197 int throughput_test(gchar* hostname, int port, int sock, char sock_is_open, char close_sock) {
00198 long long int i;
00199 char buf[1024];
00200 struct nbd_request req;
00201 u64 size;
00202 int requests=0;
00203 fd_set set;
00204 struct timeval tv;
00205 struct timeval start;
00206 struct timeval stop;
00207 float timespan;
00208 int speed;
00209 char speedchar[2] = { '\0', '\0' };
00210 int retval=0;
00211 size_t tmp;
00212 signed int do_write=TRUE;
00213
00214 size=0;
00215 if(!sock_is_open) {
00216 if((sock=setup_connection(hostname, port, CONNECTION_TYPE_CLISERV))<0) {
00217 g_warning("Could not open socket: %s", errstr);
00218 retval=-1;
00219 goto err;
00220 }
00221 } else {
00222
00223
00224 size=4096;
00225 }
00226 if((tmp=read_all(sock, &size, sizeof(u64)))<0) {
00227 retval=-1;
00228 snprintf(errstr, errstr_len, "Could not read from socket: %s", strerror(errno));
00229 goto err_open;
00230 }
00231 if(tmp==0) {
00232 retval=-1;
00233 snprintf(errstr, errstr_len, "Server closed connection unexpectedly when trying to read size of device in throughput test");
00234 goto err;
00235 }
00236 read_all(sock,&buf,128);
00237 size=ntohll(size);
00238 req.magic=htonl(NBD_REQUEST_MAGIC);
00239 req.type=htonl(NBD_CMD_READ);
00240 req.len=htonl(1024);
00241 if(gettimeofday(&start, NULL)<0) {
00242 retval=-1;
00243 snprintf(errstr, errstr_len, "Could not measure start time: %s", strerror(errno));
00244 goto err_open;
00245 }
00246 for(i=0;i+1024<=size;i+=1024) {
00247 if(do_write) {
00248 memcpy(&(req.handle),&i,sizeof(i));
00249 req.from=htonll(i);
00250 write(sock, &req, sizeof(req));
00251 printf("Requests(+): %d\n", ++requests);
00252 }
00253 do {
00254 FD_ZERO(&set);
00255 FD_SET(sock, &set);
00256 tv.tv_sec=0;
00257 tv.tv_usec=0;
00258 select(sock+1, &set, NULL, NULL, &tv);
00259 if(FD_ISSET(sock, &set)) {
00260
00261
00262 if(read_packet_check_header(sock, 1024, i)<0) {
00263 retval=-1;
00264 goto err_open;
00265 }
00266 printf("Requests(-): %d\n", --requests);
00267 }
00268 } while FD_ISSET(sock, &set);
00269
00270
00271 FD_ZERO(&set);
00272 FD_SET(sock, &set);
00273 tv.tv_sec=1;
00274 tv.tv_usec=0;
00275 do_write=select(sock+1,NULL,&set,NULL,&tv);
00276 if(!do_write) printf("Select finished\n");
00277 if(do_write<0) {
00278 snprintf(errstr, errstr_len, "select: %s", strerror(errno));
00279 retval=-1;
00280 goto err_open;
00281 }
00282 }
00283
00284 do {
00285 FD_ZERO(&set);
00286 FD_SET(sock, &set);
00287 tv.tv_sec=0;
00288 tv.tv_usec=0;
00289 select(sock+1, &set, NULL, NULL, &tv);
00290 if(FD_ISSET(sock, &set)) {
00291
00292
00293 read_packet_check_header(sock, 1024, i);
00294 printf("Requests(-): %d\n", --requests);
00295 }
00296 } while (requests);
00297 if(gettimeofday(&stop, NULL)<0) {
00298 retval=-1;
00299 snprintf(errstr, errstr_len, "Could not measure end time: %s", strerror(errno));
00300 goto err_open;
00301 }
00302 timespan=stop.tv_sec-start.tv_sec+(stop.tv_usec-start.tv_usec)/1000000;
00303 speed=(int)(size/timespan);
00304 if(speed>1024) {
00305 speed>>=10;
00306 speedchar[0]='K';
00307 }
00308 if(speed>1024) {
00309 speed>>=10;
00310 speedchar[0]='M';
00311 }
00312 if(speed>1024) {
00313 speed>>=10;
00314 speedchar[0]='G';
00315 }
00316 g_message("Throughput test complete. Took %.3f seconds to complete, %d%sB/s",timespan,speed,speedchar);
00317
00318 err_open:
00319 if(close_sock) {
00320 close_connection(sock, CONNECTION_CLOSE_PROPERLY);
00321 }
00322 err:
00323 return retval;
00324 }
00325
00326 int main(int argc, char**argv) {
00327 gchar *hostname;
00328 long int p;
00329 int port;
00330 int sock=0;
00331
00332 if(argc<3) {
00333 g_message("Not enough arguments");
00334 g_message("Usage: %s <hostname> <port>", argv[0]);
00335 exit(EXIT_FAILURE);
00336 }
00337 logging();
00338 hostname=g_strdup(argv[1]);
00339 p=(strtol(argv[2], NULL, 0));
00340 if(p==LONG_MIN||p==LONG_MAX) {
00341 g_critical("Could not parse port number: %s", strerror(errno));
00342 exit(EXIT_FAILURE);
00343 }
00344 port=(int)p;
00345
00346 if(throughput_test(hostname, port, sock, FALSE, TRUE)<0) {
00347 g_warning("Could not run throughput test: %s", errstr);
00348 exit(EXIT_FAILURE);
00349 }
00350
00351 return 0;
00352 }