merge IB code from peter
[vlendec/samba-autobuild/.git] / ctdb / tests / ibwrapper_test.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Test the infiniband wrapper.
4  *
5  * Copyright (C) Sven Oehme <oehmes@de.ibm.com> 2006
6  *
7  * Major code contributions by Peter Somogyi <psomogyi@gamax.hu>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #include <arpa/inet.h>
33 #include <malloc.h>
34 #include <assert.h>
35 #include <unistd.h>
36 #include <signal.h>
37
38 #include "includes.h"
39 #include "lib/events/events.h"
40 #include "ib/ibwrapper.h"
41
42 struct ibwtest_ctx {
43         int     is_server;
44         char    *id; /* my id */
45
46         struct ibw_initattr *attrs;
47         int     nattrs;
48         char    *opts; /* option string */
49
50         struct sockaddr_in *addrs; /* dynamic array of dest addrs */
51         int     naddrs;
52
53         int     max_msg_size;
54         unsigned int    nsec; /* nanosleep between messages */
55
56         int     cnt;
57
58         int     kill_me;
59         struct ibw_ctx  *ibwctx;
60 };
61
62 struct ibwtest_conn {
63         char    *id;
64 };
65
66 enum testopcode {
67         TESTOP_SEND_ID = 1,
68         TESTOP_SEND_DATA = 2
69 };
70
71 int ibwtest_connect_everybody(struct ibwtest_ctx *tcx)
72 {
73         struct ibwtest_conn     *pconn = talloc_zero(tcx, struct ibwtest_conn);
74         int     i;
75
76         for(i=0; i<tcx->naddrs; i++) {
77                 if (ibw_connect(tcx->ibwctx, &tcx->addrs[i], pconn)) {
78                         fprintf(stderr, "ibw_connect error at %d\n", i);
79                         return -1;
80                 }
81         }
82         DEBUG(10, ("sent %d connect request...\n", tcx->naddrs));
83
84         return 0;
85 }
86
87 int ibwtest_send_id(struct ibw_conn *conn)
88 {
89         char *buf;
90         void *key;
91         struct ibwtest_ctx *tcx = talloc_get_type(conn->ctx->ctx_userdata, struct ibwtest_ctx);
92
93         DEBUG(10, ("test IBWC_CONNECTED\n"));
94         if (ibw_alloc_send_buf(conn, (void **)&buf, &key)) {
95                 DEBUG(0, ("send_id: ibw_alloc_send_buf failed\n"));
96                 return -1;
97         }
98         
99         buf[0] = (char)TESTOP_SEND_ID;
100         strcpy(buf+1, tcx->id);
101
102         if (ibw_send(conn, buf, key, strlen(buf+1))) {
103                 DEBUG(0, ("send_id: ibw_send error\n"));
104                 return -1;
105         }
106         return 0;
107 }
108
109 int ibwtest_send_test_msg(struct ibwtest_ctx *tcx, struct ibw_conn *conn, const char *msg)
110 {
111         char *buf;
112         void *key;
113
114         if (ibw_alloc_send_buf(conn, (void **)&buf, &key)) {
115                 fprintf(stderr, "send_test_msg: ibw_alloc_send_buf failed\n");
116                 return -1;
117         }
118
119         buf[0] = (char)TESTOP_SEND_DATA;
120         assert(strlen(msg)<tcx->max_msg_size-1);
121         strcpy(buf+1, msg);
122         
123         if (ibw_send(conn, buf, key, strlen(buf+1))) {
124                 DEBUG(0, ("send_test_msg: ibw_send error\n"));
125                 return -1;
126         }
127         return 0;
128 }
129
130 int ibwtest_connstate_handler(struct ibw_ctx *ctx, struct ibw_conn *conn)
131 {
132         struct ibwtest_ctx      *tcx = NULL; /* userdata */
133         struct ibwtest_conn     *pconn = NULL; /* userdata */
134
135         if (ctx) {
136                 tcx = talloc_get_type(ctx->ctx_userdata, struct ibwtest_ctx);
137
138                 switch(ctx->state) {
139                 case IBWS_INIT:
140                         DEBUG(10, ("test IBWS_INIT\n"));
141                         break;
142                 case IBWS_READY:
143                         DEBUG(10, ("test IBWS_READY\n"));
144                         break;
145                 case IBWS_CONNECT_REQUEST:
146                         DEBUG(10, ("test IBWS_CONNECT_REQUEST\n"));
147                         pconn = talloc_zero(conn, struct ibwtest_conn);
148                         if (ibw_accept(ctx, conn, pconn)) {
149                                 DEBUG(0, ("error accepting the connect request\n"));
150                         }
151                         break;
152                 case IBWS_STOPPED:
153                         DEBUG(10, ("test IBWS_STOPPED\n"));
154                         talloc_free(tcx->ibwctx);
155                         DEBUG(10, ("talloc_free(tcx->ibwctx) DONE\n"));
156                         tcx->kill_me = 1; /* main loop can exit */
157                         break;
158                 case IBWS_ERROR:
159                         DEBUG(10, ("test IBWS_ERROR\n"));
160                         ibw_stop(tcx->ibwctx);
161                         break;
162                 default:
163                         assert(0);
164                         break;
165                 }
166         }
167
168         if (conn) {
169                 pconn = talloc_get_type(conn->conn_userdata, struct ibwtest_conn);
170                 switch(conn->state) {
171                 case IBWC_INIT:
172                         DEBUG(10, ("test IBWC_INIT\n"));
173                         break;
174                 case IBWC_CONNECTED:
175                         ibwtest_send_id(conn);
176                         break;
177                 case IBWC_DISCONNECTED:
178                         DEBUG(10, ("test IBWC_DISCONNECTED\n"));
179                         break;
180                 case IBWC_ERROR:
181                         DEBUG(10, ("test IBWC_ERROR\n"));
182                         break;
183                 default:
184                         assert(0);
185                         break;
186                 }
187         }
188         return 0;
189 }
190
191 int ibwtest_receive_handler(struct ibw_conn *conn, void *buf, int n)
192 {
193         struct ibwtest_conn *pconn;
194         enum testopcode op;
195         struct ibwtest_ctx *tcx = talloc_get_type(conn->ctx->ctx_userdata, struct ibwtest_ctx);
196
197         assert(conn!=NULL);
198         pconn = talloc_get_type(conn->conn_userdata, struct ibwtest_conn);
199
200         op = (enum testopcode)((char *)buf)[0];
201         DEBUG(11, ("[%d]msg from %s: \"%s\"(%d)\n", op,
202                 pconn->id ? pconn->id : NULL, ((char *)buf)+1, n));
203
204         if (tcx->is_server) {
205                 char *buf2;
206                 void *key2;
207                 /* bounce message */
208                 if (ibw_alloc_send_buf(conn, (void **)&buf2, &key2)) {
209                         fprintf(stderr, "ibw_alloc_send_buf error #2\n");
210                         return -1;
211                 }
212                 memcpy(buf2, buf, n);
213                 if (ibw_send(conn, buf2, key2, n)) {
214                         fprintf(stderr, "ibw_send error #2\n");
215                         return -2;
216                 }
217         }
218
219         return 0;
220 }
221
222 void ibwtest_timeout_handler(struct event_context *ev, struct timed_event *te, 
223         struct timeval t, void *private)
224 {
225         struct ibwtest_ctx *tcx = talloc_get_type(private, struct ibwtest_ctx);
226
227         if (!tcx->is_server) {
228                 struct ibw_conn *p;
229                 char    msg[50];
230
231                 /* fill it with something variable... */
232                 sprintf(msg, "hello world %d", tcx->cnt++);
233
234                 /* send something to everybody... */
235                 for(p=tcx->ibwctx->conn_list; p!=NULL; p=p->next) {
236                         ibwtest_send_test_msg(tcx, p, msg);
237                 }
238         } /* else allow main loop run */
239 }
240
241 static struct ibwtest_ctx *testctx = NULL;
242
243 void ibwtest_sigquit_handler(int sig)
244 {
245         DEBUG(0, ("got SIGQUIT\n"));
246         if (testctx)
247                 ibw_stop(testctx->ibwctx);
248 }
249
250 int ibwtest_parse_attrs(struct ibwtest_ctx *tcx, char *optext,
251         struct ibw_initattr **pattrs, int *nattrs, char op)
252 {
253         int     i = 0, n = 1;
254         int     porcess_next = 1;
255         char    *p, *q;
256         struct ibw_initattr *attrs = NULL;
257
258         *pattrs = NULL;
259         for(p = optext; *p!='\0'; p++) {
260                 if (*p==',')
261                         n++;
262         }
263
264         attrs = (struct ibw_initattr *)talloc_size(tcx,
265                 n * sizeof(struct ibw_initattr));
266         for(p = optext; *p!='\0'; p++) {
267                 if (porcess_next) {
268                         attrs[i].name = p;
269                         q = strchr(p, ':');
270                         if (q==NULL) {
271                                 fprintf(stderr, "-%c format error\n", op);
272                                 return -1;
273                         }
274                         *q = '\0';
275                         attrs[i].value = q + 1;
276
277                         porcess_next = 0;
278                         i++;
279                 }
280                 if (*p==',') {
281                         *p = '\0';
282                         porcess_next = 1;
283                 }
284         }
285         *pattrs = attrs;
286         *nattrs = n;
287
288         return 0;
289 }
290
291 int ibwtest_getdests(struct ibwtest_ctx *tcx, char op)
292 {
293         int     i;
294         struct ibw_initattr     *attrs = NULL;
295         struct sockaddr_in      *p;
296         char    *tmp;
297
298         tmp = talloc_strdup(tcx, optarg);
299         /* hack to reuse the above ibw_initattr parser */
300         if (ibwtest_parse_attrs(tcx, tmp, &attrs, &tcx->naddrs, op))
301                 return -1;
302
303         tcx->addrs = talloc_size(tcx,
304                 tcx->naddrs * sizeof(struct sockaddr_in));
305         for(i=0; i<tcx->naddrs; i++) {
306                 p = tcx->addrs + i;
307                 p->sin_addr.s_addr = inet_addr(attrs[i].name);
308                 p->sin_port = atoi(attrs[i].value);
309                 p->sin_family = AF_INET;
310         }
311
312         return 0;
313 }
314
315 int ibwtest_init_server(struct ibwtest_ctx *tcx)
316 {
317         if (tcx->naddrs!=1) {
318                 fprintf(stderr, "incorrecr number of addrs(%d!=1)\n", tcx->naddrs);
319                 return -1;
320         }
321
322         if (ibw_bind(tcx->ibwctx, &tcx->addrs[0])) {
323                 DEBUG(0, ("ERROR: ibw_bind failed\n"));
324                 return -1;
325         }
326
327         /* continued at IBWS_READY */
328         return 0;
329 }
330
331 void ibwtest_usage(struct ibwtest_ctx *tcx, char *name)
332 {
333         printf("Usage:\n");
334         printf("\t%s -i <id> -o {name:value} -d {addr:port} -m max_msg_size -t nsec -s\n", name);
335         printf("\t-i <id> is a free text, acting as a server id, max 23 chars [mandatory]\n");
336         printf("\t-o name1:value1,name2:value2,... is a list of (name, value) pairs\n");
337         printf("\t-d addr1:port1,addr2:port2,... is a list of destination ip addresses\n");
338         printf("\t-m max_msg_size maximum message size [default %d]\n", tcx->max_msg_size);
339         printf("\t-t nsec delta time between sends in nanosec [default %d]\n", tcx->nsec);
340         printf("\t-s server mode (you have to give exactly one -d address:port in this case)\n");
341         printf("Press ctrl+C to stop the program.\n");
342 }
343
344 int main(int argc, char *argv[])
345 {
346         int     rc, op;
347         int     result = 1;
348         struct event_context *ev = NULL;
349         struct ibwtest_ctx *tcx = NULL;
350
351         tcx = talloc_zero(NULL, struct ibwtest_ctx);
352         memset(tcx, 0, sizeof(struct ibwtest_ctx));
353         tcx->max_msg_size = 256;
354         tcx->nsec = 1000;
355
356         /* here is the only case we can't avoid using global... */
357         testctx = tcx;
358         signal(SIGQUIT, ibwtest_sigquit_handler);
359
360         while ((op=getopt(argc, argv, "i:o:d:m:s")) != -1) {
361                 switch (op) {
362                 case 'i':
363                         tcx->id = talloc_strdup(tcx, optarg);
364                         break;
365                 case 'o':
366                         tcx->opts = talloc_strdup(tcx, optarg);
367                         if (ibwtest_parse_attrs(tcx, tcx->opts, &tcx->attrs,
368                                 &tcx->nattrs, op))
369                                 goto cleanup;
370                         break;
371                 case 'd':
372                         if (ibwtest_getdests(tcx, op))
373                                 goto cleanup;
374                         break;
375                 case 'm':
376                         tcx->max_msg_size = atoi(optarg);
377                         break;
378                 case 's':
379                         tcx->is_server = 1;
380                         break;
381                 default:
382                         fprintf(stderr, "ERROR: unknown option -%c\n", (char)op);
383                         ibwtest_usage(tcx, argv[0]);
384                         goto cleanup;
385                 }
386         }
387         if (tcx->id==NULL) {
388                 ibwtest_usage(tcx, argv[0]);
389                 goto cleanup;
390         }
391
392         ev = event_context_init(NULL);
393         assert(ev);
394
395         tcx->ibwctx = ibw_init(tcx->attrs, tcx->nattrs,
396                 tcx,
397                 ibwtest_connstate_handler,
398                 ibwtest_receive_handler,
399                 ev,
400                 tcx->max_msg_size
401         );
402         if (!tcx->ibwctx)
403                 goto cleanup;
404
405         if (tcx->is_server)
406                 rc = ibwtest_init_server(tcx);
407         else
408                 rc = ibwtest_connect_everybody(tcx);
409         if (rc)
410                 goto cleanup;
411
412         while(!tcx->kill_me) {
413                 event_add_timed(ev, tcx, timeval_current_ofs(0, tcx->nsec),
414                         ibwtest_timeout_handler, tcx);
415                 event_loop_once(ev);
416         }
417
418         result = 0; /* everything OK */
419
420 cleanup:
421         if (tcx)
422                 talloc_free(tcx);
423         if (ev)
424                 talloc_free(ev);
425         DEBUG(0, ("exited with code %d\n", result));
426         return result;
427 }