Assign a value to serve when using modern protocol
[nbd.git] / nbd-tester-client.c
1 /*
2  * Test client to test the NBD server. Doesn't do anything useful, except
3  * checking that the server does, actually, work.
4  *
5  * Note that the only 'real' test is to check the client against a kernel. If
6  * it works here but does not work in the kernel, then that's most likely a bug
7  * in this program and/or in nbd-server.
8  *
9  * Copyright(c) 2006  Wouter Verhelst
10  *
11  * This program is Free Software; you can redistribute it and/or modify it
12  * under the terms of the GNU General Public License as published by the Free
13  * Software Foundation, in version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18  * more details.
19  *
20  * You should have received a copy of the GNU General Public License along with
21  * this program; if not, write to the Free Software Foundation, Inc., 51
22  * Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
23  */
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdbool.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <syslog.h>
32 #include <unistd.h>
33 #include "config.h"
34 #include "lfs.h"
35 #define MY_NAME "nbd-tester-client"
36 #include "cliserv.h"
37
38 #include <netinet/in.h>
39 #include <glib.h>
40
41 static gchar errstr[1024];
42 const static int errstr_len=1024;
43
44 static uint64_t size;
45
46 typedef enum {
47         CONNECTION_TYPE_NONE,
48         CONNECTION_TYPE_CONNECT,
49         CONNECTION_TYPE_INIT_PASSWD,
50         CONNECTION_TYPE_CLISERV,
51         CONNECTION_TYPE_FULL,
52 } CONNECTION_TYPE;
53
54 typedef enum {
55         CONNECTION_CLOSE_PROPERLY,
56         CONNECTION_CLOSE_FAST,
57 } CLOSE_TYPE;
58
59 inline int read_all(int f, void *buf, size_t len) {
60         ssize_t res;
61         size_t retval=0;
62
63         while(len>0) {
64                 if((res=read(f, buf, len)) <=0) {
65                         snprintf(errstr, errstr_len, "Read failed: %s", strerror(errno));
66                         return -1;
67                 }
68                 len-=res;
69                 buf+=res;
70                 retval+=res;
71         }
72         return retval;
73 }
74
75 int setup_connection(gchar *hostname, int port, gchar* name, CONNECTION_TYPE ctype) {
76         int sock;
77         struct hostent *host;
78         struct sockaddr_in addr;
79         char buf[256];
80         uint64_t mymagic = (name ? opts_magic : cliserv_magic);
81         u64 tmp64;
82         uint32_t tmp32 = 0;
83
84         sock=0;
85         if(ctype<CONNECTION_TYPE_CONNECT)
86                 goto end;
87         if((sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0) {
88                 strncpy(errstr, strerror(errno), errstr_len);
89                 goto err;
90         }
91         setmysockopt(sock);
92         if(!(host=gethostbyname(hostname))) {
93                 strncpy(errstr, strerror(errno), errstr_len);
94                 goto err_open;
95         }
96         addr.sin_family=AF_INET;
97         addr.sin_port=htons(port);
98         addr.sin_addr.s_addr=*((int *) host->h_addr);
99         if((connect(sock, (struct sockaddr *)&addr, sizeof(addr))<0)) {
100                 strncpy(errstr, strerror(errno), errstr_len);
101                 goto err_open;
102         }
103         if(ctype<CONNECTION_TYPE_INIT_PASSWD)
104                 goto end;
105         if(read_all(sock, buf, strlen(INIT_PASSWD))<0) {
106                 snprintf(errstr, errstr_len, "Could not read INIT_PASSWD: %s",
107                                 strerror(errno));
108                 goto err_open;
109         }
110         if(strlen(buf)==0) {
111                 snprintf(errstr, errstr_len, "Server closed connection");
112                 goto err_open;
113         }
114         if(strncmp(buf, INIT_PASSWD, strlen(INIT_PASSWD))) {
115                 snprintf(errstr, errstr_len, "INIT_PASSWD does not match");
116                 goto err_open;
117         }
118         if(ctype<CONNECTION_TYPE_CLISERV)
119                 goto end;
120         if(read_all(sock, &tmp64, sizeof(tmp64))<0) {
121                 snprintf(errstr, errstr_len, "Could not read cliserv_magic: %s",
122                                 strerror(errno));
123                 goto err_open;
124         }
125         tmp64=ntohll(tmp64);
126         if(tmp64 != mymagic) {
127                 strncpy(errstr, "mymagic does not match", errstr_len);
128                 goto err_open;
129         }
130         if(ctype<CONNECTION_TYPE_FULL)
131                 goto end;
132         if(!name) {
133                 read_all(sock, &size, sizeof(size));
134                 size=ntohll(size);
135                 read_all(sock, buf, 128);
136                 goto end;
137         }
138         /* flags */
139         read_all(sock, buf, sizeof(uint16_t));
140         /* reserved field */
141         write(sock, &tmp32, sizeof(tmp32));
142         /* magic */
143         tmp64 = htonll(opts_magic);
144         write(sock, &tmp64, sizeof(tmp64));
145         /* name */
146         tmp32 = htonl(NBD_OPT_EXPORT_NAME);
147         write(sock, &tmp32, sizeof(tmp32));
148         tmp32 = htonl((uint32_t)strlen(name));
149         write(sock, &tmp32, sizeof(tmp32));
150         write(sock, name, strlen(name));
151         read_all(sock, &size, sizeof(size));
152         size = ntohll(size);
153         read_all(sock, buf, sizeof(uint16_t)+124);
154         goto end;
155 err_open:
156         close(sock);
157 err:
158         sock=-1;
159 end:
160         return sock;
161 }
162
163 int close_connection(int sock, CLOSE_TYPE type) {
164         struct nbd_request req;
165         u64 counter=0;
166
167         switch(type) {
168                 case CONNECTION_CLOSE_PROPERLY:
169                         req.magic=htonl(NBD_REQUEST_MAGIC);
170                         req.type=htonl(NBD_CMD_DISC);
171                         memcpy(&(req.handle), &(counter), sizeof(counter));
172                         counter++;
173                         req.from=0;
174                         req.len=0;
175                         if(write(sock, &req, sizeof(req))<0) {
176                                 snprintf(errstr, errstr_len, "Could not write to socket: %s", strerror(errno));
177                                 return -1;
178                         }
179                 case CONNECTION_CLOSE_FAST:
180                         if(close(sock)<0) {
181                                 snprintf(errstr, errstr_len, "Could not close socket: %s", strerror(errno));
182                                 return -1;
183                         }
184                         break;
185                 default:
186                         g_critical("Your compiler is on crack!"); /* or I am buggy */
187                         return -1;
188         }
189         return 0;
190 }
191
192 int read_packet_check_header(int sock, size_t datasize, long long int curhandle) {
193         struct nbd_reply rep;
194         int retval=0;
195         char buf[datasize];
196
197         read_all(sock, &rep, sizeof(rep));
198         rep.magic=ntohl(rep.magic);
199         rep.error=ntohl(rep.error);
200         if(rep.magic!=NBD_REPLY_MAGIC) {
201                 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);
202                 retval=-1;
203                 goto end;
204         }
205         if(rep.error) {
206                 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));
207                 retval=-1;
208                 goto end;
209         }
210         read_all(sock, &buf, datasize);
211
212 end:
213         return retval;
214 }
215
216 int oversize_test(gchar* hostname, int port, char* name, int sock, char sock_is_open, char close_sock) {
217         int retval=0;
218         struct nbd_request req;
219         struct nbd_reply rep;
220         int request=0;
221         int i=0;
222         pid_t mypid = getpid();
223         char buf[((1024*1024)+sizeof(struct nbd_request)/2)<<1];
224         bool got_err;
225
226         /* This should work */
227         if(!sock_is_open) {
228                 if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL))<0) {
229                         g_warning("Could not open socket: %s", errstr);
230                         retval=-1;
231                         goto err;
232                 }
233         }
234         req.magic=htonl(NBD_REQUEST_MAGIC);
235         req.type=htonl(NBD_CMD_READ);
236         req.len=htonl(1024*1024);
237         memcpy(&(req.handle),&i,sizeof(i));
238         req.from=htonll(i);
239         write(sock, &req, sizeof(req));
240         printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
241         read_all(sock, &rep, sizeof(struct nbd_reply));
242         read_all(sock, &buf, ntohl(req.len));
243         if(rep.error) {
244                 printf("Received unexpected error\n");
245                 retval=-1;
246                 goto err;
247         } else {
248                 printf("OK\n");
249         }
250         /* This probably should not work */
251         i++; req.from=htonll(i);
252         req.len = htonl(ntohl(req.len) + sizeof(struct nbd_request) / 2);
253         write(sock, &req, sizeof(req));
254         printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
255         read_all(sock, &rep, sizeof(struct nbd_reply));
256         read_all(sock, &buf, ntohl(req.len));
257         if(rep.error) {
258                 printf("Received expected error\n");
259                 got_err=true;
260         } else {
261                 printf("OK\n");
262                 got_err=false;
263         }
264         /* ... unless this works, too */
265         i++; req.from=htonll(i);
266         req.len = htonl(ntohl(req.len) << 1);
267         write(sock, &req, sizeof(req));
268         printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
269         read_all(sock, &rep, sizeof(struct nbd_reply));
270         read_all(sock, &buf, ntohl(req.len));
271         if(rep.error) {
272                 printf("error\n");
273         } else {
274                 printf("OK\n");
275         }
276         if((rep.error && !got_err) || (!rep.error && got_err)) {
277                 printf("Received unexpected error\n");
278                 retval=-1;
279         }
280   err:
281         return retval;
282 }
283
284 int throughput_test(gchar* hostname, int port, char* name, int sock, char sock_is_open, char close_sock) {
285         long long int i;
286         char buf[1024];
287         struct nbd_request req;
288         int requests=0;
289         fd_set set;
290         struct timeval tv;
291         struct timeval start;
292         struct timeval stop;
293         float timespan;
294         int speed;
295         char speedchar[2] = { '\0', '\0' };
296         int retval=0;
297         size_t tmp;
298         signed int do_write=TRUE;
299         pid_t mypid = getpid();
300
301         size=0;
302         if(!sock_is_open) {
303                 if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL))<0) {
304                         g_warning("Could not open socket: %s", errstr);
305                         retval=-1;
306                         goto err;
307                 }
308         }
309         req.magic=htonl(NBD_REQUEST_MAGIC);
310         req.type=htonl(NBD_CMD_READ);
311         req.len=htonl(1024);
312         if(gettimeofday(&start, NULL)<0) {
313                 retval=-1;
314                 snprintf(errstr, errstr_len, "Could not measure start time: %s", strerror(errno));
315                 goto err_open;
316         }
317         for(i=0;i+1024<=size;i+=1024) {
318                 if(do_write) {
319                         memcpy(&(req.handle),&i,sizeof(i));
320                         req.from=htonll(i);
321                         write(sock, &req, sizeof(req));
322                         printf("%d: Requests(+): %d\n", (int)mypid, ++requests);
323                 }
324                 do {
325                         FD_ZERO(&set);
326                         FD_SET(sock, &set);
327                         tv.tv_sec=0;
328                         tv.tv_usec=0;
329                         select(sock+1, &set, NULL, NULL, &tv);
330                         if(FD_ISSET(sock, &set)) {
331                                 /* Okay, there's something ready for
332                                  * reading here */
333                                 if(read_packet_check_header(sock, 1024, i)<0) {
334                                         retval=-1;
335                                         goto err_open;
336                                 }
337                                 printf("%d: Requests(-): %d\n", (int)mypid, --requests);
338                         }
339                 } while FD_ISSET(sock, &set);
340                 /* Now wait until we can write again or until a second have
341                  * passed, whichever comes first*/
342                 FD_ZERO(&set);
343                 FD_SET(sock, &set);
344                 tv.tv_sec=1;
345                 tv.tv_usec=0;
346                 do_write=select(sock+1,NULL,&set,NULL,&tv);
347                 if(!do_write) printf("Select finished\n");
348                 if(do_write<0) {
349                         snprintf(errstr, errstr_len, "select: %s", strerror(errno));
350                         retval=-1;
351                         goto err_open;
352                 }
353         }
354         /* Now empty the read buffer */
355         do {
356                 FD_ZERO(&set);
357                 FD_SET(sock, &set);
358                 tv.tv_sec=0;
359                 tv.tv_usec=0;
360                 select(sock+1, &set, NULL, NULL, &tv);
361                 if(FD_ISSET(sock, &set)) {
362                         /* Okay, there's something ready for
363                          * reading here */
364                         read_packet_check_header(sock, 1024, i);
365                         printf("%d: Requests(-): %d\n", (int)mypid, --requests);
366                 }
367         } while (requests);
368         if(gettimeofday(&stop, NULL)<0) {
369                 retval=-1;
370                 snprintf(errstr, errstr_len, "Could not measure end time: %s", strerror(errno));
371                 goto err_open;
372         }
373         timespan=(float)(stop.tv_sec-start.tv_sec+(stop.tv_usec-start.tv_usec))/(float)1000000;
374         speed=(int)(size/timespan);
375         if(speed>1024) {
376                 speed>>=10;
377                 speedchar[0]='K';
378         }
379         if(speed>1024) {
380                 speed>>=10;
381                 speedchar[0]='M';
382         }
383         if(speed>1024) {
384                 speed>>=10;
385                 speedchar[0]='G';
386         }
387         g_message("%d: Throughput test complete. Took %.3f seconds to complete, %d%siB/s", (int)getpid(), timespan,speed,speedchar);
388
389 err_open:
390         if(close_sock) {
391                 close_connection(sock, CONNECTION_CLOSE_PROPERLY);
392         }
393 err:
394         return retval;
395 }
396
397 typedef int (*testfunc)(gchar*, int, char*, int, char, char);
398
399 int main(int argc, char**argv) {
400         gchar *hostname;
401         long int p = 0;
402         char* name = NULL;
403         int sock=0;
404         char c;
405         bool want_port = TRUE;
406         int nonopt=0;
407         testfunc test = throughput_test;
408
409         if(argc<3) {
410                 g_message("%d: Not enough arguments", (int)getpid());
411                 g_message("%d: Usage: %s <hostname> <port>", (int)getpid(), argv[0]);
412                 g_message("%d: Or: %s <hostname> -N <exportname>", (int)getpid(), argv[0]);
413                 exit(EXIT_FAILURE);
414         }
415         logging();
416         while((c=getopt(argc, argv, "-N:o"))>=0) {
417                 switch(c) {
418                         case 1:
419                                 switch(nonopt) {
420                                         case 0:
421                                                 hostname=g_strdup(optarg);
422                                                 nonopt++;
423                                                 break;
424                                         case 1:
425                                                 if(want_port)
426                                                 p=(strtol(argv[2], NULL, 0));
427                                                 if(p==LONG_MIN||p==LONG_MAX) {
428                                                         g_critical("Could not parse port number: %s", strerror(errno));
429                                                         exit(EXIT_FAILURE);
430                                                 }
431                                                 break;
432                                 }
433                                 break;
434                         case 'N':
435                                 name=g_strdup(optarg);
436                                 p = 10809;
437                                 want_port = false;
438                                 break;
439                         case 'o':
440                                 test=oversize_test;
441                                 break;
442                 }
443         }
444
445         if(test(hostname, (int)p, name, sock, FALSE, TRUE)<0) {
446                 g_warning("Could not run test: %s", errstr);
447                 exit(EXIT_FAILURE);
448         }
449
450         return 0;
451 }