ctdb-client: Add client code for tunnel controls
[samba.git] / ctdb / client / client_control.c
1 /*
2    CTDB client code
3
4    Copyright (C) Amitay Isaacs  2015
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 "replace.h"
21 #include "system/network.h"
22 #include "system/filesys.h"
23
24 #include <talloc.h>
25 #include <tevent.h>
26 #include <tdb.h>
27
28 #include "lib/util/tevent_unix.h"
29
30 #include "common/reqid.h"
31 #include "common/srvid.h"
32 #include "common/comm.h"
33
34 #include "protocol/protocol.h"
35 #include "protocol/protocol_api.h"
36
37 #include "client/client_private.h"
38 #include "client/client.h"
39
40
41 /*
42  * Handle REQ_CONTROL and REPLY_CONTROL
43  */
44
45 struct ctdb_client_control_state {
46         struct ctdb_client_context *client;
47         uint32_t opcode;
48         uint32_t flags;
49         uint32_t reqid;
50         struct ctdb_reply_control *reply;
51         struct tevent_req *req;
52 };
53
54 static int ctdb_client_control_state_destructor(
55         struct ctdb_client_control_state *state);
56 static void ctdb_client_control_done(struct tevent_req *subreq);
57
58 struct tevent_req *ctdb_client_control_send(TALLOC_CTX *mem_ctx,
59                                             struct tevent_context *ev,
60                                             struct ctdb_client_context *client,
61                                             uint32_t destnode,
62                                             struct timeval timeout,
63                                             struct ctdb_req_control *request)
64 {
65         struct ctdb_req_header h;
66         struct tevent_req *req, *subreq;
67         struct ctdb_client_control_state *state;
68         uint32_t reqid;
69         uint8_t *buf;
70         size_t datalen, buflen;
71         int ret;
72
73         req = tevent_req_create(mem_ctx, &state,
74                                 struct ctdb_client_control_state);
75         if (req == NULL) {
76                 return NULL;
77         }
78
79         reqid = reqid_new(client->idr, state);
80         if (reqid == REQID_INVALID) {
81                 talloc_free(req);
82                 return NULL;
83         }
84
85         state->client = client;
86         state->flags = request->flags;
87         state->opcode = request->opcode;
88         state->reqid = reqid;
89         state->req = req;
90         state->reply = talloc_zero(state, struct ctdb_reply_control);
91         if (tevent_req_nomem(state->reply, req)) {
92                 return tevent_req_post(req, ev);
93         }
94         state->reply->rdata.opcode = request->rdata.opcode;
95
96         talloc_set_destructor(state, ctdb_client_control_state_destructor);
97
98         ctdb_req_header_fill(&h, 0, CTDB_REQ_CONTROL, destnode,
99                              client->pnn, reqid);
100
101         datalen = ctdb_req_control_len(&h, request);
102         ret = ctdb_allocate_pkt(state, datalen, &buf, &buflen);
103         if (ret != 0) {
104                 tevent_req_error(req, ret);
105                 return tevent_req_post(req, ev);
106         }
107
108         ret = ctdb_req_control_push(&h, request, buf, &buflen);
109         if (ret != 0) {
110                 tevent_req_error(req, ret);
111                 return tevent_req_post(req, ev);
112         }
113
114         if (!tevent_timeval_is_zero(&timeout)) {
115                 tevent_req_set_endtime(req, ev, timeout);
116         }
117
118         subreq = comm_write_send(state, ev, client->comm, buf, buflen);
119         if (tevent_req_nomem(subreq, req)) {
120                 return tevent_req_post(req, ev);
121         }
122         tevent_req_set_callback(subreq, ctdb_client_control_done, req);
123
124         return req;
125 }
126
127 static int ctdb_client_control_state_destructor(
128         struct ctdb_client_control_state *state)
129 {
130         reqid_remove(state->client->idr, state->reqid);
131         return 0;
132 }
133
134 static void ctdb_client_control_done(struct tevent_req *subreq)
135 {
136         struct tevent_req *req = tevent_req_callback_data(
137                 subreq, struct tevent_req);
138         struct ctdb_client_control_state *state = tevent_req_data(
139                 req, struct ctdb_client_control_state);
140         bool status;
141         int ret;
142
143         status = comm_write_recv(subreq, &ret);
144         TALLOC_FREE(subreq);
145         if (! status) {
146                 tevent_req_error(req, ret);
147                 return;
148         }
149
150         /* Daemon will not reply, so we set status to 0 */
151         if (state->flags & CTDB_CTRL_FLAG_NOREPLY) {
152                 state->reply->status = 0;
153                 tevent_req_done(req);
154         }
155
156         /* wait for the reply or timeout */
157 }
158
159 void ctdb_client_reply_control(struct ctdb_client_context *client,
160                                uint8_t *buf, size_t buflen, uint32_t reqid)
161 {
162         struct ctdb_req_header h;
163         struct ctdb_client_control_state *state;
164         int ret;
165
166         state = reqid_find(client->idr, reqid,
167                            struct ctdb_client_control_state);
168         if (state == NULL) {
169                 return;
170         }
171
172         if (reqid != state->reqid) {
173                 return;
174         }
175
176         ret = ctdb_reply_control_pull(buf, buflen, state->opcode, &h,
177                                       state->reply, state->reply);
178         if (ret != 0) {
179                 tevent_req_error(state->req, ret);
180                 return;
181         }
182
183         tevent_req_done(state->req);
184 }
185
186 bool ctdb_client_control_recv(struct tevent_req *req, int *perr,
187                               TALLOC_CTX *mem_ctx,
188                               struct ctdb_reply_control **reply)
189 {
190         struct ctdb_client_control_state *state = tevent_req_data(
191                 req, struct ctdb_client_control_state);
192         int err;
193
194         if (tevent_req_is_unix_error(req, &err)) {
195                 if (perr != NULL) {
196                         *perr = err;
197                 }
198                 return false;
199         }
200
201         if (reply != NULL) {
202                 *reply = talloc_steal(mem_ctx, state->reply);
203         }
204
205         return true;
206 }
207
208 /*
209  * Handle multiple nodes - there cannot be any return data
210  */
211
212 struct ctdb_client_control_multi_state {
213         uint32_t *pnn_list;
214         int count;
215         int done;
216         int err;
217         int *err_list;
218         struct ctdb_reply_control **reply;
219 };
220
221 struct control_index_state {
222         struct tevent_req *req;
223         int index;
224 };
225
226 static void ctdb_client_control_multi_done(struct tevent_req *subreq);
227
228 struct tevent_req *ctdb_client_control_multi_send(
229                                 TALLOC_CTX *mem_ctx,
230                                 struct tevent_context *ev,
231                                 struct ctdb_client_context *client,
232                                 uint32_t *pnn_list, int count,
233                                 struct timeval timeout,
234                                 struct ctdb_req_control *request)
235 {
236         struct tevent_req *req, *subreq;
237         struct ctdb_client_control_multi_state *state;
238         int i;
239
240         if (pnn_list == NULL || count == 0) {
241                 return NULL;
242         }
243
244         req = tevent_req_create(mem_ctx, &state,
245                                 struct ctdb_client_control_multi_state);
246         if (req == NULL) {
247                 return NULL;
248         }
249
250         state->pnn_list = pnn_list;
251         state->count = count;
252         state->done = 0;
253         state->err = 0;
254         state->err_list = talloc_zero_array(state, int, count);
255         if (tevent_req_nomem(state->err_list, req)) {
256                 return tevent_req_post(req, ev);
257         }
258         state->reply = talloc_zero_array(state, struct ctdb_reply_control *,
259                                          count);
260         if (tevent_req_nomem(state->reply, req)) {
261                 return tevent_req_post(req, ev);
262         }
263
264         for (i=0; i<count; i++) {
265                 struct control_index_state *substate;
266
267                 subreq = ctdb_client_control_send(state, ev, client,
268                                                   pnn_list[i], timeout,
269                                                   request);
270                 if (tevent_req_nomem(subreq, req)) {
271                         return tevent_req_post(req, ev);
272                 }
273
274                 substate = talloc(subreq, struct control_index_state);
275                 if (tevent_req_nomem(substate, req)) {
276                         return tevent_req_post(req, ev);
277                 }
278
279                 substate->req = req;
280                 substate->index = i;
281
282                 tevent_req_set_callback(subreq, ctdb_client_control_multi_done,
283                                         substate);
284         }
285
286         return req;
287 }
288
289 static void ctdb_client_control_multi_done(struct tevent_req *subreq)
290 {
291         struct control_index_state *substate = tevent_req_callback_data(
292                 subreq, struct control_index_state);
293         struct tevent_req *req = substate->req;
294         int idx = substate->index;
295         struct ctdb_client_control_multi_state *state = tevent_req_data(
296                 req, struct ctdb_client_control_multi_state);
297         bool status;
298         int ret;
299
300         status = ctdb_client_control_recv(subreq, &ret, state->reply,
301                                           &state->reply[idx]);
302         TALLOC_FREE(subreq);
303         if (! status) {
304                 if (state->err == 0) {
305                         state->err = ret;
306                         state->err_list[idx] = state->err;
307                 }
308         } else {
309                 if (state->reply[idx]->status != 0) {
310                         if (state->err == 0) {
311                                 state->err = state->reply[idx]->status;
312                                 state->err_list[idx] = state->err;
313                         }
314                 }
315         }
316
317         state->done += 1;
318
319         if (state->done == state->count) {
320                 tevent_req_done(req);
321         }
322 }
323
324 bool ctdb_client_control_multi_recv(struct tevent_req *req, int *perr,
325                                     TALLOC_CTX *mem_ctx, int **perr_list,
326                                     struct ctdb_reply_control ***preply)
327 {
328         struct ctdb_client_control_multi_state *state = tevent_req_data(
329                 req, struct ctdb_client_control_multi_state);
330         int err;
331
332         if (tevent_req_is_unix_error(req, &err)) {
333                 if (perr != NULL) {
334                         *perr = err;
335                 }
336                 if (perr_list != NULL) {
337                         *perr_list = talloc_steal(mem_ctx, state->err_list);
338                 }
339                 return false;
340         }
341
342         if (perr != NULL) {
343                 *perr = state->err;
344         }
345
346         if (perr_list != NULL) {
347                 *perr_list = talloc_steal(mem_ctx, state->err_list);
348         }
349
350         if (preply != NULL) {
351                 *preply = talloc_steal(mem_ctx, state->reply);
352         }
353
354         if (state->err != 0) {
355                 return false;
356         }
357
358         return true;
359 }
360
361 int ctdb_client_control_multi_error(uint32_t *pnn_list, int count,
362                                     int *err_list, uint32_t *pnn)
363 {
364         int ret = 0, i;
365
366         for (i=0; i<count; i++) {
367                 if (err_list[i] != 0) {
368                         ret = err_list[i];
369                         *pnn = pnn_list[i];
370                 }
371         }
372
373         return ret;
374 }
375
376 /*
377  * Sync version of control send/recv
378  */
379
380 int ctdb_client_control(TALLOC_CTX *mem_ctx,
381                         struct tevent_context *ev,
382                         struct ctdb_client_context *client,
383                         uint32_t destnode,
384                         struct timeval timeout,
385                         struct ctdb_req_control *request,
386                         struct ctdb_reply_control **reply)
387 {
388         struct tevent_req *req;
389         int ret;
390         bool status;
391
392         req = ctdb_client_control_send(mem_ctx, ev, client, destnode, timeout,
393                                        request);
394         if (req == NULL) {
395                 return ENOMEM;
396         }
397
398         tevent_req_poll(req, ev);
399
400         status = ctdb_client_control_recv(req, &ret, mem_ctx, reply);
401         if (! status) {
402                 return ret;
403         }
404
405         return 0;
406 }
407
408 int ctdb_client_control_multi(TALLOC_CTX *mem_ctx,
409                               struct tevent_context *ev,
410                               struct ctdb_client_context *client,
411                               uint32_t *pnn_list, int count,
412                               struct timeval timeout,
413                               struct ctdb_req_control *request,
414                               int **perr_list,
415                               struct ctdb_reply_control ***preply)
416 {
417         struct tevent_req *req;
418         bool status;
419         int ret;
420
421         req = ctdb_client_control_multi_send(mem_ctx, ev, client,
422                                              pnn_list, count,
423                                              timeout, request);
424         if (req == NULL) {
425                 return ENOMEM;
426         }
427
428         tevent_req_poll(req, ev);
429
430         status = ctdb_client_control_multi_recv(req, &ret, mem_ctx, perr_list,
431                                                 preply);
432         if (! status) {
433                 return ret;
434         }
435
436         return 0;
437 }