ctdb-client: Do not delete reqid explicitly
[obnox/samba/samba-obnox.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 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
95         talloc_set_destructor(state, ctdb_client_control_state_destructor);
96
97         ctdb_req_header_fill(&h, 0, CTDB_REQ_CONTROL, destnode,
98                              client->pnn, reqid);
99
100         ret = ctdb_req_control_push(&h, request, state, &buf, &buflen);
101         if (ret != 0) {
102                 tevent_req_error(req, ret);
103                 return tevent_req_post(req, ev);
104         }
105
106         if (!tevent_timeval_is_zero(&timeout)) {
107                 tevent_req_set_endtime(req, ev, timeout);
108         }
109
110         subreq = comm_write_send(state, ev, client->comm, buf, buflen);
111         if (tevent_req_nomem(subreq, req)) {
112                 return tevent_req_post(req, ev);
113         }
114         tevent_req_set_callback(subreq, ctdb_client_control_done, req);
115
116         return req;
117 }
118
119 static int ctdb_client_control_state_destructor(
120         struct ctdb_client_control_state *state)
121 {
122         reqid_remove(state->client->idr, state->reqid);
123         return 0;
124 }
125
126 static void ctdb_client_control_done(struct tevent_req *subreq)
127 {
128         struct tevent_req *req = tevent_req_callback_data(
129                 subreq, struct tevent_req);
130         struct ctdb_client_control_state *state = tevent_req_data(
131                 req, struct ctdb_client_control_state);
132         bool status;
133         int ret;
134
135         status = comm_write_recv(subreq, &ret);
136         TALLOC_FREE(subreq);
137         if (! status) {
138                 tevent_req_error(req, ret);
139                 return;
140         }
141
142         /* Daemon will not reply, so we set status to 0 */
143         if (state->flags & CTDB_CTRL_FLAG_NOREPLY) {
144                 state->reply->status = 0;
145                 tevent_req_done(req);
146         }
147
148         /* wait for the reply or timeout */
149 }
150
151 void ctdb_client_reply_control(struct ctdb_client_context *client,
152                                uint8_t *buf, size_t buflen, uint32_t reqid)
153 {
154         struct ctdb_req_header h;
155         struct ctdb_client_control_state *state;
156         int ret;
157
158         state = reqid_find(client->idr, reqid,
159                            struct ctdb_client_control_state);
160         if (state == NULL) {
161                 return;
162         }
163
164         if (reqid != state->reqid) {
165                 return;
166         }
167
168         ret = ctdb_reply_control_pull(buf, buflen, state->opcode, &h,
169                                       state->reply, state->reply);
170         if (ret != 0) {
171                 tevent_req_error(state->req, ret);
172                 return;
173         }
174
175         tevent_req_done(state->req);
176 }
177
178 bool ctdb_client_control_recv(struct tevent_req *req, int *perr,
179                               TALLOC_CTX *mem_ctx,
180                               struct ctdb_reply_control **reply)
181 {
182         struct ctdb_client_control_state *state = tevent_req_data(
183                 req, struct ctdb_client_control_state);
184         int err;
185
186         if (tevent_req_is_unix_error(req, &err)) {
187                 if (perr != NULL) {
188                         *perr = err;
189                 }
190                 return false;
191         }
192
193         if (reply != NULL) {
194                 *reply = talloc_steal(mem_ctx, state->reply);
195         }
196
197         return true;
198 }
199
200 /*
201  * Handle multiple nodes - there cannot be any return data
202  */
203
204 struct ctdb_client_control_multi_state {
205         uint32_t *pnn_list;
206         int count;
207         int done;
208         int err;
209         int *err_list;
210         struct ctdb_reply_control **reply;
211 };
212
213 struct control_index_state {
214         struct tevent_req *req;
215         int index;
216 };
217
218 static void ctdb_client_control_multi_done(struct tevent_req *subreq);
219
220 struct tevent_req *ctdb_client_control_multi_send(
221                                 TALLOC_CTX *mem_ctx,
222                                 struct tevent_context *ev,
223                                 struct ctdb_client_context *client,
224                                 uint32_t *pnn_list, int count,
225                                 struct timeval timeout,
226                                 struct ctdb_req_control *request)
227 {
228         struct tevent_req *req, *subreq;
229         struct ctdb_client_control_multi_state *state;
230         int i;
231
232         if (pnn_list == NULL || count == 0) {
233                 return NULL;
234         }
235
236         req = tevent_req_create(mem_ctx, &state,
237                                 struct ctdb_client_control_multi_state);
238         if (req == NULL) {
239                 return NULL;
240         }
241
242         state->pnn_list = pnn_list;
243         state->count = count;
244         state->done = 0;
245         state->err = 0;
246         state->err_list = talloc_zero_array(state, int, count);
247         if (tevent_req_nomem(state->err_list, req)) {
248                 return tevent_req_post(req, ev);
249         }
250         state->reply = talloc_zero_array(state, struct ctdb_reply_control *,
251                                          count);
252         if (tevent_req_nomem(state->reply, req)) {
253                 return tevent_req_post(req, ev);
254         }
255
256         for (i=0; i<count; i++) {
257                 struct control_index_state *substate;
258
259                 subreq = ctdb_client_control_send(state, ev, client,
260                                                   pnn_list[i], timeout,
261                                                   request);
262                 if (tevent_req_nomem(subreq, req)) {
263                         return tevent_req_post(req, ev);
264                 }
265
266                 substate = talloc(subreq, struct control_index_state);
267                 if (tevent_req_nomem(substate, req)) {
268                         return tevent_req_post(req, ev);
269                 }
270
271                 substate->req = req;
272                 substate->index = i;
273
274                 tevent_req_set_callback(subreq, ctdb_client_control_multi_done,
275                                         substate);
276         }
277
278         return req;
279 }
280
281 static void ctdb_client_control_multi_done(struct tevent_req *subreq)
282 {
283         struct control_index_state *substate = tevent_req_callback_data(
284                 subreq, struct control_index_state);
285         struct tevent_req *req = substate->req;
286         int idx = substate->index;
287         struct ctdb_client_control_multi_state *state = tevent_req_data(
288                 req, struct ctdb_client_control_multi_state);
289         bool status;
290         int ret;
291
292         status = ctdb_client_control_recv(subreq, &ret, state->reply,
293                                           &state->reply[idx]);
294         TALLOC_FREE(subreq);
295         if (! status) {
296                 if (state->err == 0) {
297                         state->err = ret;
298                         state->err_list[idx] = state->err;
299                 }
300         } else {
301                 if (state->reply[idx]->status != 0) {
302                         if (state->err == 0) {
303                                 state->err = state->reply[idx]->status;
304                                 state->err_list[idx] = state->err;
305                         }
306                 }
307         }
308
309         state->done += 1;
310
311         if (state->done == state->count) {
312                 tevent_req_done(req);
313         }
314 }
315
316 bool ctdb_client_control_multi_recv(struct tevent_req *req, int *perr,
317                                     TALLOC_CTX *mem_ctx, int **perr_list,
318                                     struct ctdb_reply_control ***preply)
319 {
320         struct ctdb_client_control_multi_state *state = tevent_req_data(
321                 req, struct ctdb_client_control_multi_state);
322         int err;
323
324         if (tevent_req_is_unix_error(req, &err)) {
325                 if (perr != NULL) {
326                         *perr = err;
327                 }
328                 if (perr_list != NULL) {
329                         *perr_list = talloc_steal(mem_ctx, state->err_list);
330                 }
331                 return false;
332         }
333
334         if (perr != NULL) {
335                 *perr = state->err;
336         }
337
338         if (perr_list != NULL) {
339                 *perr_list = talloc_steal(mem_ctx, state->err_list);
340         }
341
342         if (preply != NULL) {
343                 *preply = talloc_steal(mem_ctx, state->reply);
344         }
345
346         if (state->err != 0) {
347                 return false;
348         }
349
350         return true;
351 }
352
353 int ctdb_client_control_multi_error(uint32_t *pnn_list, int count,
354                                     int *err_list, uint32_t *pnn)
355 {
356         int ret = 0, i;
357
358         for (i=0; i<count; i++) {
359                 if (err_list[i] != 0) {
360                         ret = err_list[i];
361                         *pnn = pnn_list[i];
362                 }
363         }
364
365         return ret;
366 }
367
368 /*
369  * Sync version of control send/recv
370  */
371
372 int ctdb_client_control(TALLOC_CTX *mem_ctx,
373                         struct tevent_context *ev,
374                         struct ctdb_client_context *client,
375                         uint32_t destnode,
376                         struct timeval timeout,
377                         struct ctdb_req_control *request,
378                         struct ctdb_reply_control **reply)
379 {
380         struct tevent_req *req;
381         int ret;
382         bool status;
383
384         req = ctdb_client_control_send(mem_ctx, ev, client, destnode, timeout,
385                                        request);
386         if (req == NULL) {
387                 return ENOMEM;
388         }
389
390         tevent_req_poll(req, ev);
391
392         status = ctdb_client_control_recv(req, &ret, mem_ctx, reply);
393         if (! status) {
394                 return ret;
395         }
396
397         return 0;
398 }
399
400 int ctdb_client_control_multi(TALLOC_CTX *mem_ctx,
401                               struct tevent_context *ev,
402                               struct ctdb_client_context *client,
403                               uint32_t *pnn_list, int count,
404                               struct timeval timeout,
405                               struct ctdb_req_control *request,
406                               int **perr_list,
407                               struct ctdb_reply_control ***preply)
408 {
409         struct tevent_req *req;
410         bool status;
411         int ret;
412
413         req = ctdb_client_control_multi_send(mem_ctx, ev, client,
414                                              pnn_list, count,
415                                              timeout, request);
416         if (req == NULL) {
417                 return ENOMEM;
418         }
419
420         tevent_req_poll(req, ev);
421
422         status = ctdb_client_control_multi_recv(req, &ret, mem_ctx, perr_list,
423                                                 preply);
424         if (! status) {
425                 return ret;
426         }
427
428         return 0;
429 }