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