ctdb-recoverd: Call an election when the recovery lock is lost
[vlendec/samba-autobuild/.git] / ctdb / client / client_connect.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 "common/reqid.h"
29 #include "common/srvid.h"
30 #include "common/comm.h"
31 #include "common/logging.h"
32
33 #include "lib/util/tevent_unix.h"
34 #include "lib/util/debug.h"
35
36 #include "protocol/protocol.h"
37 #include "protocol/protocol_api.h"
38
39 #include "client/client_private.h"
40 #include "client/client.h"
41 #include "client/client_sync.h"
42
43 static void client_read_handler(uint8_t *buf, size_t buflen,
44                                 void *private_data);
45 static void client_dead_handler(void *private_data);
46
47 struct ctdb_client_init_state {
48         struct ctdb_client_context *client;
49 };
50
51 static int ctdb_client_context_destructor(struct ctdb_client_context *client);
52 static void ctdb_client_init_done(struct tevent_req *subreq);
53
54 struct tevent_req *ctdb_client_init_send(TALLOC_CTX *mem_ctx,
55                                          struct tevent_context *ev,
56                                          const char *sockpath)
57 {
58         struct tevent_req *req, *subreq;
59         struct ctdb_client_init_state *state;
60         struct ctdb_client_context *client;
61         struct ctdb_req_control request;
62         struct sockaddr_un addr;
63         size_t len;
64         int ret;
65
66         req = tevent_req_create(mem_ctx, &state,
67                                 struct ctdb_client_init_state);
68         if (req == NULL) {
69                 return NULL;
70         }
71
72         if (sockpath == NULL) {
73                 D_ERR("socket path cannot be NULL\n");
74                 tevent_req_error(req, EINVAL);
75                 return tevent_req_post(req, ev);
76         }
77
78         client = talloc_zero(state, struct ctdb_client_context);
79         if (tevent_req_nomem(client, req)) {
80                 return tevent_req_post(req, ev);
81         }
82
83         ret = reqid_init(client, INT_MAX-200, &client->idr);
84         if (ret != 0) {
85                 D_ERR("reqid_init() failed, ret=%d\n", ret);
86                 talloc_free(client);
87                 tevent_req_error(req, ret);
88                 return tevent_req_post(req, ev);
89         }
90
91         ret = srvid_init(client, &client->srv);
92         if (ret != 0) {
93                 DEBUG(DEBUG_ERR, ("srvid_init() failed, ret=%d\n", ret));
94                 talloc_free(client);
95                 tevent_req_error(req, ret);
96                 return tevent_req_post(req, ev);
97         }
98
99         ret = srvid_init(client, &client->tunnels);
100         if (ret != 0) {
101                 DEBUG(DEBUG_ERR, ("srvid_init() failed, ret=%d\n", ret));
102                 talloc_free(client);
103                 tevent_req_error(req, ret);
104                 return tevent_req_post(req, ev);
105         }
106
107         memset(&addr, 0, sizeof(addr));
108         addr.sun_family = AF_UNIX;
109         len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
110         if (len != strlen(sockpath)) {
111                 D_ERR("socket path too long, len=%zu\n", strlen(sockpath));
112                 talloc_free(client);
113                 tevent_req_error(req, ENAMETOOLONG);
114                 return tevent_req_post(req, ev);
115         }
116
117         client->fd = socket(AF_UNIX, SOCK_STREAM, 0);
118         if (client->fd == -1) {
119                 ret = errno;
120                 D_ERR("socket() failed, errno=%d\n", ret);
121                 talloc_free(client);
122                 tevent_req_error(req, ret);
123                 return tevent_req_post(req, ev);
124         }
125
126         ret = connect(client->fd, (struct sockaddr *)&addr, sizeof(addr));
127         if (ret == -1) {
128                 ret = errno;
129                 DEBUG(DEBUG_ERR, ("connect() failed, errno=%d\n", ret));
130                 close(client->fd);
131                 talloc_free(client);
132                 tevent_req_error(req, ret);
133                 return tevent_req_post(req, ev);
134         }
135
136         ret = comm_setup(client, ev, client->fd, client_read_handler, client,
137                          client_dead_handler, client, &client->comm);
138         if (ret != 0) {
139                 DEBUG(DEBUG_ERR, ("comm_setup() failed, ret=%d\n", ret));
140                 close(client->fd);
141                 talloc_free(client);
142                 tevent_req_error(req, ret);
143                 return tevent_req_post(req, ev);
144         }
145
146         client->pnn = CTDB_UNKNOWN_PNN;
147
148         talloc_set_destructor(client, ctdb_client_context_destructor);
149
150         state->client = client;
151
152         ctdb_req_control_get_pnn(&request);
153         subreq = ctdb_client_control_send(state, ev, client,
154                                           CTDB_CURRENT_NODE,
155                                           tevent_timeval_zero(),
156                                           &request);
157         if (tevent_req_nomem(subreq, req)) {
158                 TALLOC_FREE(state->client);
159                 return tevent_req_post(req, ev);
160         }
161         tevent_req_set_callback(subreq, ctdb_client_init_done, req);
162
163         return req;
164 }
165
166 static int ctdb_client_context_destructor(struct ctdb_client_context *client)
167 {
168         if (client->fd != -1) {
169                 close(client->fd);
170                 client->fd = -1;
171         }
172         return 0;
173 }
174
175 static void ctdb_client_init_done(struct tevent_req *subreq)
176 {
177         struct tevent_req *req = tevent_req_callback_data(
178                 subreq, struct tevent_req);
179         struct ctdb_client_init_state *state = tevent_req_data(
180                 req, struct ctdb_client_init_state);
181         struct ctdb_reply_control *reply;
182         int ret;
183         bool status;
184
185         status = ctdb_client_control_recv(subreq, &ret, state, &reply);
186         TALLOC_FREE(subreq);
187         if (! status) {
188                 tevent_req_error(req, ret);
189                 return;
190         }
191
192         ret = ctdb_reply_control_get_pnn(reply, &state->client->pnn);
193         if (ret != 0) {
194                 tevent_req_error(req, ret);
195                 return;
196         }
197
198         tevent_req_done(req);
199 }
200
201 bool ctdb_client_init_recv(struct tevent_req *req, int *perr,
202                            TALLOC_CTX *mem_ctx,
203                            struct ctdb_client_context **result)
204 {
205         struct ctdb_client_init_state *state = tevent_req_data(
206                 req, struct ctdb_client_init_state);
207         int ret;
208
209         if (tevent_req_is_unix_error(req, &ret)) {
210                 if (perr != NULL) {
211                         *perr = ret;
212                 }
213                 return false;
214         }
215
216         *result = talloc_steal(mem_ctx, state->client);
217         return true;
218 }
219
220
221 int ctdb_client_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
222                      const char *sockpath, struct ctdb_client_context **out)
223 {
224         struct tevent_req *req;
225         int ret;
226         bool status;
227
228         req = ctdb_client_init_send(mem_ctx, ev, sockpath);
229         if (req == NULL) {
230                 return ENOMEM;
231         }
232
233         tevent_req_poll(req, ev);
234
235         status = ctdb_client_init_recv(req, &ret, mem_ctx, out);
236         TALLOC_FREE(req);
237         if (! status) {
238                 return ret;
239         }
240
241         return 0;
242 }
243
244 static void client_read_handler(uint8_t *buf, size_t buflen,
245                                 void *private_data)
246 {
247         struct ctdb_client_context *client = talloc_get_type_abort(
248                 private_data, struct ctdb_client_context);
249         struct ctdb_req_header hdr;
250         size_t np;
251         int ret;
252
253         ret = ctdb_req_header_pull(buf, buflen, &hdr, &np);
254         if (ret != 0) {
255                 DEBUG(DEBUG_WARNING, ("invalid header, ret=%d\n", ret));
256                 return;
257         }
258
259         if (buflen != hdr.length) {
260                 DEBUG(DEBUG_WARNING, ("packet size mismatch %zu != %d\n",
261                                       buflen, hdr.length));
262                 return;
263         }
264
265         ret = ctdb_req_header_verify(&hdr, 0);
266         if (ret != 0) {
267                 DEBUG(DEBUG_WARNING, ("invalid header, ret=%d\n", ret));
268                 return;
269         }
270
271         switch (hdr.operation) {
272         case CTDB_REPLY_CALL:
273                 ctdb_client_reply_call(client, buf, buflen, hdr.reqid);
274                 break;
275
276         case CTDB_REQ_MESSAGE:
277                 ctdb_client_req_message(client, buf, buflen, hdr.reqid);
278                 break;
279
280         case CTDB_REPLY_CONTROL:
281                 ctdb_client_reply_control(client, buf, buflen, hdr.reqid);
282                 break;
283
284         case CTDB_REQ_TUNNEL:
285                 ctdb_client_req_tunnel(client, buf, buflen, hdr.reqid);
286                 break;
287
288         default:
289                 break;
290         }
291 }
292
293 static void client_dead_handler(void *private_data)
294 {
295         struct ctdb_client_context *client = talloc_get_type_abort(
296                 private_data, struct ctdb_client_context);
297         ctdb_client_callback_func_t callback = client->callback;
298         void *callback_data = client->private_data;
299
300         if (callback != NULL) {
301                 callback(callback_data);
302                 return;
303         }
304
305         DEBUG(DEBUG_NOTICE, ("connection to daemon closed, exiting\n"));
306         exit(1);
307 }
308
309 void ctdb_client_set_disconnect_callback(struct ctdb_client_context *client,
310                                          ctdb_client_callback_func_t callback,
311                                          void *private_data)
312 {
313         client->callback = callback;
314         client->private_data = private_data;
315 }
316
317 uint32_t ctdb_client_pnn(struct ctdb_client_context *client)
318 {
319         return client->pnn;
320 }
321
322 void ctdb_client_wait(struct tevent_context *ev, bool *done)
323 {
324         while (! (*done)) {
325                 tevent_loop_once(ev);
326         }
327 }
328
329 static void ctdb_client_wait_timeout_handler(struct tevent_context *ev,
330                                              struct tevent_timer *te,
331                                              struct timeval t,
332                                              void *private_data)
333 {
334         bool *timed_out = (bool *)private_data;
335
336         *timed_out = true;
337 }
338
339 int ctdb_client_wait_timeout(struct tevent_context *ev, bool *done,
340                              struct timeval timeout)
341 {
342         TALLOC_CTX *mem_ctx;
343         struct tevent_timer *timer;
344         bool timed_out = false;
345
346         mem_ctx = talloc_new(ev);
347         if (mem_ctx == NULL) {
348                 return ENOMEM;
349         }
350
351         timer = tevent_add_timer(ev, mem_ctx, timeout,
352                                  ctdb_client_wait_timeout_handler,
353                                  &timed_out);
354         if (timer == NULL) {
355                 talloc_free(mem_ctx);
356                 return ENOMEM;
357         }
358
359         while (! (*done) && ! timed_out) {
360                 tevent_loop_once(ev);
361         }
362
363         talloc_free(mem_ctx);
364
365         if (timed_out) {
366                 return ETIMEDOUT;
367         }
368
369         return 0;
370 }
371
372 struct ctdb_recovery_wait_state {
373         struct tevent_context *ev;
374         struct ctdb_client_context *client;
375 };
376
377 static void ctdb_recovery_wait_recmode(struct tevent_req *subreq);
378 static void ctdb_recovery_wait_retry(struct tevent_req *subreq);
379
380 struct tevent_req *ctdb_recovery_wait_send(TALLOC_CTX *mem_ctx,
381                                            struct tevent_context *ev,
382                                            struct ctdb_client_context *client)
383 {
384         struct tevent_req *req, *subreq;
385         struct ctdb_recovery_wait_state *state;
386         struct ctdb_req_control request;
387
388         req = tevent_req_create(mem_ctx, &state,
389                                 struct ctdb_recovery_wait_state);
390         if (req == NULL) {
391                 return NULL;
392         }
393
394         state->ev = ev;
395         state->client = client;
396
397         ctdb_req_control_get_recmode(&request);
398         subreq = ctdb_client_control_send(state, ev, client, client->pnn,
399                                           tevent_timeval_zero(), &request);
400         if (tevent_req_nomem(subreq, req)) {
401                 return tevent_req_post(req, ev);
402         }
403         tevent_req_set_callback(subreq, ctdb_recovery_wait_recmode, req);
404
405         return req;
406 }
407
408 static void ctdb_recovery_wait_recmode(struct tevent_req *subreq)
409 {
410         struct tevent_req *req = tevent_req_callback_data(
411                 subreq, struct tevent_req);
412         struct ctdb_recovery_wait_state *state = tevent_req_data(
413                 req, struct ctdb_recovery_wait_state);
414         struct ctdb_reply_control *reply;
415         int recmode;
416         int ret;
417         bool status;
418
419         status = ctdb_client_control_recv(subreq, &ret, state, &reply);
420         TALLOC_FREE(subreq);
421         if (! status) {
422                 tevent_req_error(req, ret);
423                 return;
424         }
425
426         ret = ctdb_reply_control_get_recmode(reply, &recmode);
427         if (ret != 0) {
428                 tevent_req_error(req, ret);
429                 return;
430         }
431
432         if (recmode == CTDB_RECOVERY_NORMAL) {
433                 tevent_req_done(req);
434                 return;
435         }
436
437         subreq = tevent_wakeup_send(state, state->ev,
438                                     tevent_timeval_current_ofs(1, 0));
439         if (tevent_req_nomem(subreq, req)) {
440                 return;
441         }
442         tevent_req_set_callback(subreq, ctdb_recovery_wait_retry, req);
443 }
444
445 static void ctdb_recovery_wait_retry(struct tevent_req *subreq)
446 {
447         struct tevent_req *req = tevent_req_callback_data(
448                 subreq, struct tevent_req);
449         struct ctdb_recovery_wait_state *state = tevent_req_data(
450                 req, struct ctdb_recovery_wait_state);
451         struct ctdb_req_control request;
452         bool status;
453
454         status = tevent_wakeup_recv(subreq);
455         TALLOC_FREE(subreq);
456         if (! status) {
457                 tevent_req_error(req, ENOMEM);
458                 return;
459         }
460
461         ctdb_req_control_get_recmode(&request);
462         subreq = ctdb_client_control_send(state, state->ev, state->client,
463                                           state->client->pnn,
464                                           tevent_timeval_zero(), &request);
465         if (tevent_req_nomem(subreq, req)) {
466                 return;
467         }
468         tevent_req_set_callback(subreq, ctdb_recovery_wait_recmode, req);
469 }
470
471 bool ctdb_recovery_wait_recv(struct tevent_req *req, int *perr)
472 {
473         int err;
474
475         if (tevent_req_is_unix_error(req, &err)) {
476                 if (perr != NULL) {
477                         *perr = err;
478                 }
479                 return false;
480         }
481
482         return true;
483 }
484
485 bool ctdb_recovery_wait(struct tevent_context *ev,
486                         struct ctdb_client_context *client)
487 {
488         TALLOC_CTX *mem_ctx;
489         struct tevent_req *req;
490         bool status;
491
492         mem_ctx = talloc_new(client);
493         if (mem_ctx == NULL) {
494                 return false;
495         }
496
497         req = ctdb_recovery_wait_send(mem_ctx, ev, client);
498         if (req == NULL) {
499                 return false;
500         }
501
502         tevent_req_poll(req, ev);
503
504         status = ctdb_recovery_wait_recv(req, NULL);
505
506         talloc_free(mem_ctx);
507         return status;
508 }