s3:torture: add '-T 'option=value' this is similar to '--option='=value'
[samba.git] / source3 / torture / test_ctdbd_conn.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Test async ctdb_req_send/recv
4  * Copyright (C) Volker Lendecke 2020
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "torture/proto.h"
22 #include "ctdbd_conn.h"
23 #include "lib/cluster_support.h"
24 #include "ctdb/include/ctdb_protocol.h"
25 #include "lib/util/tevent_unix.h"
26
27 extern int torture_nprocs;
28 extern int torture_numops;
29
30 struct ctdb_echo_state {
31         struct ctdb_req_control_old req;
32         struct iovec iov[2];
33         TDB_DATA echodata;
34 };
35
36 static void ctdb_echo_done(struct tevent_req *subreq);
37
38 static struct tevent_req *ctdb_echo_send(
39         TALLOC_CTX *mem_ctx,
40         struct tevent_context *ev,
41         struct ctdbd_connection *conn,
42         uint32_t delay)
43 {
44         struct tevent_req *req = NULL, *subreq = NULL;
45         struct ctdb_echo_state *state = NULL;
46         struct ctdb_req_header *hdr = NULL;
47         uint32_t datalen;
48
49         req = tevent_req_create(
50                 mem_ctx, &state, struct ctdb_echo_state);
51         if (req == NULL) {
52                 return NULL;
53         }
54
55         hdr = &state->req.hdr;
56         ctdbd_prep_hdr_next_reqid(conn, hdr);
57         hdr->operation = CTDB_REQ_CONTROL;
58         state->req.opcode = CTDB_CONTROL_ECHO_DATA;
59
60         state->iov[0] = (struct iovec) {
61                 .iov_base = &state->req,
62                 .iov_len = offsetof(struct ctdb_req_control_old, data),
63         };
64
65         datalen = generate_random() % 1024;
66
67         state->echodata.dptr = talloc_array(state, uint8_t, datalen+8);
68         if (tevent_req_nomem(state->echodata.dptr, req)) {
69                 return tevent_req_post(req, ev);
70         }
71         state->echodata.dsize = talloc_get_size(state->echodata.dptr);
72         generate_random_buffer(
73                 state->echodata.dptr, state->echodata.dsize);
74
75         memcpy(state->echodata.dptr, &delay, sizeof(delay));
76         memcpy(state->echodata.dptr+4, &datalen, sizeof(datalen));
77
78         state->req.datalen = state->echodata.dsize;
79
80         state->iov[1] = (struct iovec) {
81                 .iov_base = state->echodata.dptr,
82                 .iov_len = state->echodata.dsize,
83         };
84
85         hdr->length =
86                 offsetof(struct ctdb_req_control_old, data) +
87                 state->req.datalen;
88
89         subreq = ctdbd_req_send(
90                 state, ev, conn, state->iov, ARRAY_SIZE(state->iov));
91         if (tevent_req_nomem(subreq, req)) {
92                 return tevent_req_post(req, ev);
93         }
94         tevent_req_set_callback(subreq, ctdb_echo_done, req);
95
96         return req;
97 }
98
99 static void ctdb_echo_done(struct tevent_req *subreq)
100 {
101         struct tevent_req *req = tevent_req_callback_data(
102                 subreq, struct tevent_req);
103         struct ctdb_echo_state *state = tevent_req_data(
104                 req, struct ctdb_echo_state);
105         struct ctdb_req_header *hdr = NULL;
106         struct ctdb_reply_control_old *reply = NULL;
107         int cmp, ret;
108
109         ret = ctdbd_req_recv(subreq, state, &hdr);
110         TALLOC_FREE(subreq);
111         if (tevent_req_error(req, ret)) {
112                 printf("ctdbd_req_recv(%"PRIu32") returned %d (%s)\n",
113                        state->req.hdr.reqid,
114                        ret,
115                        strerror(ret));
116                 return;
117         }
118         if (hdr->operation != CTDB_REPLY_CONTROL) {
119                 printf("Expected CTDB_REPLY_CONTROL, got %"PRIu32"\n",
120                        hdr->operation);
121                 tevent_req_error(req, EIO);
122                 return;
123         }
124         reply = (struct ctdb_reply_control_old *)hdr;
125         if (reply->status != 0) {
126                 printf("reply->status = %"PRIi32"\n", reply->status);
127                 tevent_req_error(req, EIO);
128                 return;
129         }
130         if (reply->datalen != state->req.datalen) {
131                 printf("state->echodata.dsize=%zu datalen=%"PRIu32"\n",
132                        state->echodata.dsize,
133                        reply->datalen);
134                 tevent_req_error(req, EIO);
135                 return;
136         }
137         cmp = memcmp(reply->data,
138                      state->echodata.dptr,
139                      state->echodata.dsize);
140         if (cmp != 0) {
141                 printf("data mismatch\n");
142                 tevent_req_error(req, EIO);
143                 return;
144         }
145         TALLOC_FREE(reply);
146         tevent_req_done(req);
147 }
148
149 static int ctdb_echo_recv(struct tevent_req *req)
150 {
151         return tevent_req_simple_recv_unix(req);
152 }
153
154 struct ctdb_ping_flood_state {
155         struct tevent_context *ev;
156         struct ctdbd_connection *conn;
157         size_t num_running;
158         bool done;
159 };
160
161 static void ctdb_ping_flood_next(struct tevent_req *subreq);
162 static void ctdb_ping_flood_done(struct tevent_req *subreq);
163
164 static struct tevent_req *ctdb_ping_flood_send(
165         TALLOC_CTX *mem_ctx,
166         struct tevent_context *ev,
167         struct ctdbd_connection *conn,
168         size_t num_parallel,
169         unsigned usecs)
170 {
171         struct tevent_req *req = NULL, *subreq = NULL;
172         struct ctdb_ping_flood_state *state = NULL;
173         size_t i;
174
175         req = tevent_req_create(
176                 mem_ctx, &state, struct ctdb_ping_flood_state);
177         if (req == NULL) {
178                 return NULL;
179         }
180         state->ev = ev;
181         state->conn = conn;
182
183         for (i=0; i<num_parallel; i++) {
184                 subreq = ctdb_echo_send(
185                         state,
186                         state->ev,
187                         state->conn,
188                         generate_random() % 10);
189                 if (tevent_req_nomem(subreq, req)) {
190                         return tevent_req_post(req, ev);
191                 }
192                 tevent_req_set_callback(subreq, ctdb_ping_flood_next, req);
193         }
194         state->num_running = num_parallel;
195
196         subreq = tevent_wakeup_send(
197                 state,
198                 ev,
199                 tevent_timeval_current_ofs(0, usecs));
200         if (tevent_req_nomem(subreq, req)) {
201                 return tevent_req_post(req, ev);
202         }
203         tevent_req_set_callback(subreq, ctdb_ping_flood_done, req);
204
205         return req;
206 }
207
208 static void ctdb_ping_flood_next(struct tevent_req *subreq)
209 {
210         struct tevent_req *req = tevent_req_callback_data(
211                 subreq, struct tevent_req);
212         struct ctdb_ping_flood_state *state = tevent_req_data(
213                 req, struct ctdb_ping_flood_state);
214         int ret;
215
216         ret = ctdb_echo_recv(subreq);
217         TALLOC_FREE(subreq);
218         if (tevent_req_error(req, ret)) {
219                 return;
220         }
221         state->num_running -= 1;
222
223         if (state->done) {
224                 if (state->num_running == 0) {
225                         tevent_req_done(req);
226                 }
227                 return;
228         }
229
230         subreq = ctdb_echo_send(
231                 state,
232                 state->ev,
233                 state->conn,
234                 generate_random() % 10);
235         if (tevent_req_nomem(subreq, req)) {
236                 return;
237         }
238         tevent_req_set_callback(subreq, ctdb_ping_flood_next, req);
239         state->num_running += 1;
240 }
241
242 static void ctdb_ping_flood_done(struct tevent_req *subreq)
243 {
244         struct tevent_req *req = tevent_req_callback_data(
245                 subreq, struct tevent_req);
246         struct ctdb_ping_flood_state *state = tevent_req_data(
247                 req, struct ctdb_ping_flood_state);
248         bool ok;
249
250         ok = tevent_wakeup_recv(subreq);
251         TALLOC_FREE(subreq);
252         if (!ok) {
253                 tevent_req_oom(req);
254                 return;
255         }
256         state->done = true;
257 }
258
259 static int ctdb_ping_flood_recv(struct tevent_req *req)
260 {
261         return tevent_req_simple_recv_unix(req);
262 }
263
264 bool run_ctdbd_conn1(int dummy)
265 {
266         struct ctdbd_connection *conn = NULL;
267         struct tevent_context *ev = NULL;
268         struct tevent_req *req = NULL;
269         int ret;
270         bool ok;
271         bool result = false;
272
273         ev = samba_tevent_context_init(talloc_tos());
274         if (ev == NULL) {
275                 printf("samba_tevent_context_init failed\n");
276                 goto done;
277         }
278
279         ret = ctdbd_init_async_connection(
280                 ev, lp_ctdbd_socket(), 0, &conn);
281         if (ret != 0) {
282                 printf("ctdbd_init_async_connection failed: %s\n",
283                        strerror(ret));
284                 goto done;
285         }
286
287         req = ctdb_ping_flood_send(
288                 ev, ev, conn, torture_nprocs, torture_numops * 1000);
289         if (req == NULL) {
290                 printf("ctdb_ping_flood_send failed\n");
291                 goto done;
292         }
293
294         ok = tevent_req_poll_unix(req, ev, &ret);
295         if (!ok) {
296                 printf("tevent_req_poll_unix failed: %s\n",
297                        strerror(ret));
298                 goto done;
299         }
300
301         ret = ctdb_ping_flood_recv(req);
302         TALLOC_FREE(req);
303         if (ret != 0) {
304                 printf("ctdb_ping_flood failed: %s\n", strerror(ret));
305                 goto done;
306         }
307
308         result = true;
309 done:
310         TALLOC_FREE(conn);
311         return result;
312 }