ctdb-client: Move sync API to a separate header
[samba.git] / ctdb / client / client_event.c
1 /*
2    Eventd client api
3
4    Copyright (C) Amitay Isaacs  2016
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/filesys.h"
22 #include "system/network.h"
23
24 #include <talloc.h>
25 #include <tevent.h>
26
27 #include "lib/util/debug.h"
28 #include "lib/util/tevent_unix.h"
29
30 #include "common/logging.h"
31 #include "common/reqid.h"
32 #include "common/comm.h"
33
34 #include "protocol/protocol_api.h"
35
36 #include "client/client.h"
37
38 struct ctdb_event_context {
39         struct reqid_context *idr;
40         struct comm_context *comm;
41         int fd;
42
43         ctdb_client_callback_func_t callback;
44         void *private_data;
45 };
46
47 static int ctdb_event_connect(struct ctdb_event_context *eclient,
48                               struct tevent_context *ev,
49                               const char *sockpath);
50
51 static int ctdb_event_context_destructor(struct ctdb_event_context *eclient);
52
53 int ctdb_event_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
54                     const char *sockpath, struct ctdb_event_context **out)
55 {
56         struct ctdb_event_context *eclient;
57         int ret;
58
59         eclient = talloc_zero(mem_ctx, struct ctdb_event_context);
60         if (eclient == NULL) {
61                 DEBUG(DEBUG_ERR, (__location__ " memory allocation error\n"));
62                 return ENOMEM;
63         }
64
65         ret = reqid_init(eclient, INT_MAX-200, &eclient->idr);
66         if (ret != 0) {
67                 DEBUG(DEBUG_ERR, ("reqid_init() failed, ret=%d\n", ret));
68                 talloc_free(eclient);
69                 return ret;
70         }
71
72         eclient->fd = -1;
73
74         ret = ctdb_event_connect(eclient, ev, sockpath);
75         if (ret != 0) {
76                 talloc_free(eclient);
77                 return ret;
78         }
79
80         talloc_set_destructor(eclient, ctdb_event_context_destructor);
81
82         *out = eclient;
83         return 0;
84 }
85
86 static int ctdb_event_context_destructor(struct ctdb_event_context *eclient)
87 {
88         if (eclient->fd != -1) {
89                 close(eclient->fd);
90                 eclient->fd = -1;
91         }
92         return 0;
93 }
94
95 static void event_read_handler(uint8_t *buf, size_t buflen,
96                                void *private_data);
97 static void event_dead_handler(void *private_data);
98
99 static int ctdb_event_connect(struct ctdb_event_context *eclient,
100                               struct tevent_context *ev, const char *sockpath)
101 {
102         struct sockaddr_un addr;
103         size_t len;
104         int fd, ret;
105
106         if (sockpath == NULL) {
107                 DEBUG(DEBUG_ERR, ("socket path cannot be NULL\n"));
108                 return EINVAL;
109         }
110
111         memset(&addr, 0, sizeof(addr));
112         addr.sun_family = AF_UNIX;
113         len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
114         if (len >= sizeof(addr.sun_path)) {
115                 DEBUG(DEBUG_ERR, ("socket path too long, len=%zu\n",
116                                   strlen(sockpath)));
117                 return ENAMETOOLONG;
118         }
119
120         fd = socket(AF_UNIX, SOCK_STREAM, 0);
121         if (fd == -1) {
122                 ret = errno;
123                 DEBUG(DEBUG_ERR, ("socket() failed, errno=%d\n", ret));
124                 return ret;
125         }
126
127         ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
128         if (ret == -1) {
129                 ret = errno;
130                 DEBUG(DEBUG_ERR, ("connect() failed, errno=%d\n", ret));
131                 close(fd);
132                 return ret;
133         }
134         eclient->fd = fd;
135
136         ret = comm_setup(eclient, ev, fd, event_read_handler, eclient,
137                          event_dead_handler, eclient, &eclient->comm);
138         if (ret != 0) {
139                 DEBUG(DEBUG_ERR, ("comm_setup() failed, ret=%d\n", ret));
140                 close(fd);
141                 eclient->fd = -1;
142                 return ret;
143         }
144
145         return 0;
146 }
147
148 static void ctdb_event_msg_reply(struct ctdb_event_context *eclient,
149                                  uint8_t *buf, size_t buflen);
150
151 static void event_read_handler(uint8_t *buf, size_t buflen,
152                                void *private_data)
153 {
154         struct ctdb_event_context *eclient = talloc_get_type_abort(
155                 private_data, struct ctdb_event_context);
156
157         ctdb_event_msg_reply(eclient, buf, buflen);
158 }
159
160 static void event_dead_handler(void *private_data)
161 {
162         struct ctdb_event_context *eclient = talloc_get_type_abort(
163                 private_data, struct ctdb_event_context);
164         ctdb_client_callback_func_t callback = eclient->callback;
165         void *callback_data = eclient->private_data;
166
167         talloc_free(eclient);
168         if (callback != NULL) {
169                 callback(callback_data);
170                 return;
171         }
172
173         DEBUG(DEBUG_NOTICE, ("connection to daemon closed, exiting\n"));
174         exit(1);
175 }
176
177 void ctdb_event_set_disconnect_callback(struct ctdb_event_context *eclient,
178                                         ctdb_client_callback_func_t callback,
179                                         void *private_data)
180 {
181         eclient->callback = callback;
182         eclient->private_data = private_data;
183 }
184
185 /*
186  * Handle eventd_request and eventd_reply
187  */
188
189 struct ctdb_event_msg_state {
190         struct ctdb_event_context *eclient;
191
192         uint32_t reqid;
193         struct tevent_req *req;
194         struct ctdb_event_reply *reply;
195 };
196
197 static int ctdb_event_msg_state_destructor(struct ctdb_event_msg_state *state);
198 static void ctdb_event_msg_done(struct tevent_req *subreq);
199
200 struct tevent_req *ctdb_event_msg_send(TALLOC_CTX *mem_ctx,
201                                        struct tevent_context *ev,
202                                        struct ctdb_event_context *eclient,
203                                        struct ctdb_event_request *request)
204 {
205         struct tevent_req *req, *subreq;
206         struct ctdb_event_msg_state *state;
207         uint8_t *buf;
208         size_t buflen;
209         int ret;
210
211         req = tevent_req_create(mem_ctx, &state, struct ctdb_event_msg_state);
212         if (req == NULL) {
213                 return NULL;
214         }
215
216         state->eclient = eclient;
217
218         state->reqid = reqid_new(eclient->idr, state);
219         if (state->reqid == REQID_INVALID) {
220                 talloc_free(req);
221                 return NULL;
222         }
223         state->req = req;
224
225         talloc_set_destructor(state, ctdb_event_msg_state_destructor);
226
227         ctdb_event_header_fill(&request->header, state->reqid);
228
229         buflen = ctdb_event_request_len(request);
230         buf = talloc_size(state, buflen);
231         if (tevent_req_nomem(buf, req)) {
232                 return tevent_req_post(req, ev);
233         }
234
235         ret = ctdb_event_request_push(request, buf, &buflen);
236         if (ret != 0) {
237                 tevent_req_error(req, ret);
238                 return tevent_req_post(req, ev);
239         }
240
241         subreq = comm_write_send(state, ev, eclient->comm, buf, buflen);
242         if (tevent_req_nomem(subreq, req)) {
243                 return tevent_req_post(req, ev);
244         }
245         tevent_req_set_callback(subreq, ctdb_event_msg_done, req);
246
247         return req;
248 }
249
250 static int ctdb_event_msg_state_destructor(struct ctdb_event_msg_state *state)
251 {
252         reqid_remove(state->eclient->idr, state->reqid);
253         return 0;
254 }
255
256 static void ctdb_event_msg_done(struct tevent_req *subreq)
257 {
258         struct tevent_req *req = tevent_req_callback_data(
259                 subreq, struct tevent_req);
260         int ret;
261         bool status;
262
263         status = comm_write_recv(subreq, &ret);
264         TALLOC_FREE(subreq);
265         if (! status) {
266                 tevent_req_error(req, ret);
267                 return;
268         }
269
270         /* Wait for the reply or timeout */
271 }
272
273 static void ctdb_event_msg_reply(struct ctdb_event_context *eclient,
274                                  uint8_t *buf, size_t buflen)
275 {
276         struct ctdb_event_reply *reply;
277         struct ctdb_event_msg_state *state;
278         int ret;
279
280         reply = talloc_zero(eclient, struct ctdb_event_reply);
281         if (reply == NULL) {
282                 D_WARNING("memory allocation error\n");
283                 return;
284         }
285
286         ret = ctdb_event_reply_pull(buf, buflen, reply, reply);
287         if (ret != 0) {
288                 D_WARNING("Invalid packet received, ret=%d\n", ret);
289                 return;
290         }
291
292         state = reqid_find(eclient->idr, reply->header.reqid,
293                            struct ctdb_event_msg_state);
294         if (state == NULL) {
295                 return;
296         }
297
298         if (reply->header.reqid != state->reqid) {
299                 return;
300         }
301
302         state->reply = talloc_steal(state, reply);
303         tevent_req_done(state->req);
304 }
305
306 bool ctdb_event_msg_recv(struct tevent_req *req, int *perr,
307                          TALLOC_CTX *mem_ctx,
308                          struct ctdb_event_reply **reply)
309 {
310         struct ctdb_event_msg_state *state = tevent_req_data(
311                 req, struct ctdb_event_msg_state);
312         int ret;
313
314         if (tevent_req_is_unix_error(req, &ret)) {
315                 if (perr != NULL) {
316                         *perr = ret;
317                 }
318                 return false;
319         }
320
321         if (reply != NULL) {
322                 *reply = talloc_steal(mem_ctx, state->reply);
323         }
324
325         return true;
326 }
327
328 /*
329  * Run an event
330  */
331
332 struct tevent_req *ctdb_event_run_send(TALLOC_CTX *mem_ctx,
333                                        struct tevent_context *ev,
334                                        struct ctdb_event_context *eclient,
335                                        enum ctdb_event event,
336                                        uint32_t timeout, const char *arg_str)
337 {
338         struct ctdb_event_request request;
339         struct ctdb_event_request_run rdata;
340
341         rdata.event = event;
342         rdata.timeout = timeout;
343         rdata.arg_str = arg_str;
344
345         request.rdata.command = CTDB_EVENT_COMMAND_RUN;
346         request.rdata.data.run = &rdata;
347
348         return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
349 }
350
351 bool ctdb_event_run_recv(struct tevent_req *req, int *perr, int *result)
352 {
353         struct ctdb_event_reply *reply;
354         int ret;
355         bool status;
356
357         status = ctdb_event_msg_recv(req, &ret, req, &reply);
358         if (! status) {
359                 if (perr != NULL) {
360                         *perr = ret;
361                 }
362                 return false;
363         }
364
365         if (reply->rdata.command != CTDB_EVENT_COMMAND_RUN) {
366                 if (perr != NULL) {
367                         *perr = EPROTO;
368                 }
369                 talloc_free(reply);
370                 return false;
371         }
372
373         if (result != NULL) {
374                 *result = reply->rdata.result;
375         }
376
377         talloc_free(reply);
378         return true;
379 }
380
381 /*
382  * Get event status
383  */
384
385 struct tevent_req *ctdb_event_status_send(TALLOC_CTX *mem_ctx,
386                                           struct tevent_context *ev,
387                                           struct ctdb_event_context *eclient,
388                                           enum ctdb_event event,
389                                           enum ctdb_event_status_state state)
390 {
391         struct ctdb_event_request request;
392         struct ctdb_event_request_status rdata;
393
394         rdata.event = event;
395         rdata.state = state;
396
397         request.rdata.command = CTDB_EVENT_COMMAND_STATUS;
398         request.rdata.data.status = &rdata;
399
400         return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
401 }
402
403 bool ctdb_event_status_recv(struct tevent_req *req, int *perr,
404                             int32_t *result, int *event_status,
405                             TALLOC_CTX *mem_ctx,
406                             struct ctdb_script_list **script_list)
407 {
408         struct ctdb_event_reply *reply;
409         int ret;
410         bool status;
411
412         status = ctdb_event_msg_recv(req, &ret, req, &reply);
413         if (! status) {
414                 if (perr != NULL) {
415                         *perr = ret;
416                 }
417                 return false;
418         }
419
420         if (reply->rdata.command != CTDB_EVENT_COMMAND_STATUS) {
421                 if (perr != NULL) {
422                         *perr = EPROTO;
423                 }
424                 talloc_free(reply);
425                 return false;
426         }
427
428         if (result != NULL) {
429                 *result = reply->rdata.result;
430         }
431         if (event_status != NULL) {
432                 *event_status = reply->rdata.data.status->status;
433         }
434         if (script_list != NULL) {
435                 *script_list = talloc_steal(mem_ctx,
436                                         reply->rdata.data.status->script_list);
437         }
438
439         talloc_free(reply);
440         return true;
441 }
442
443 /*
444  * Get script list
445  */
446
447 struct tevent_req *ctdb_event_script_list_send(
448                                         TALLOC_CTX *mem_ctx,
449                                         struct tevent_context *ev,
450                                         struct ctdb_event_context *eclient)
451 {
452         struct ctdb_event_request request;
453
454         request.rdata.command = CTDB_EVENT_COMMAND_SCRIPT_LIST;
455
456         return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
457 }
458
459 bool ctdb_event_script_list_recv(struct tevent_req *req, int *perr,
460                                  int32_t *result, TALLOC_CTX *mem_ctx,
461                                  struct ctdb_script_list **script_list)
462 {
463         struct ctdb_event_reply *reply;
464         int ret;
465         bool status;
466
467         status = ctdb_event_msg_recv(req, &ret, req, &reply);
468         if (! status) {
469                 if (perr != NULL) {
470                         *perr = ret;
471                 }
472                 return false;
473         }
474
475         if (reply->rdata.command != CTDB_EVENT_COMMAND_SCRIPT_LIST) {
476                 if (perr != NULL) {
477                         *perr = EPROTO;
478                 }
479                 talloc_free(reply);
480                 return false;
481         }
482
483         if (result != NULL) {
484                 *result = reply->rdata.result;
485         }
486         if (script_list != NULL) {
487                 *script_list = talloc_steal(mem_ctx,
488                                 reply->rdata.data.script_list->script_list);
489         }
490
491         talloc_free(reply);
492         return true;
493 }
494
495 /*
496  * Enable a script
497  */
498
499 struct tevent_req *ctdb_event_script_enable_send(
500                                         TALLOC_CTX *mem_ctx,
501                                         struct tevent_context *ev,
502                                         struct ctdb_event_context *eclient,
503                                         const char *script_name)
504 {
505         struct ctdb_event_request request;
506         struct ctdb_event_request_script_enable rdata;
507
508         rdata.script_name = script_name;
509
510         request.rdata.command = CTDB_EVENT_COMMAND_SCRIPT_ENABLE;
511         request.rdata.data.script_enable = &rdata;
512
513         return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
514 }
515
516 bool ctdb_event_script_enable_recv(struct tevent_req *req, int *perr,
517                                    int *result)
518 {
519         struct ctdb_event_reply *reply;
520         int ret;
521         bool status;
522
523         status = ctdb_event_msg_recv(req, &ret, req, &reply);
524         if (! status) {
525                 if (perr != NULL) {
526                         *perr = ret;
527                 }
528                 return false;
529         }
530
531         if (reply->rdata.command != CTDB_EVENT_COMMAND_SCRIPT_ENABLE) {
532                 if (perr != NULL) {
533                         *perr = EPROTO;
534                 }
535                 talloc_free(reply);
536                 return false;
537         }
538
539         if (result != NULL) {
540                 *result = reply->rdata.result;
541         }
542
543         talloc_free(reply);
544         return true;
545 }
546
547 /*
548  * Disable a script
549  */
550
551 struct tevent_req *ctdb_event_script_disable_send(
552                                         TALLOC_CTX *mem_ctx,
553                                         struct tevent_context *ev,
554                                         struct ctdb_event_context *eclient,
555                                         const char *script_name)
556 {
557         struct ctdb_event_request request;
558         struct ctdb_event_request_script_disable rdata;
559
560         rdata.script_name = script_name;
561
562         request.rdata.command = CTDB_EVENT_COMMAND_SCRIPT_DISABLE;
563         request.rdata.data.script_disable = &rdata;
564
565         return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
566 }
567
568 bool ctdb_event_script_disable_recv(struct tevent_req *req, int *perr,
569                                     int *result)
570 {
571         struct ctdb_event_reply *reply;
572         int ret;
573         bool status;
574
575         status = ctdb_event_msg_recv(req, &ret, req, &reply);
576         if (! status) {
577                 if (perr != NULL) {
578                         *perr = ret;
579                 }
580                 return false;
581         }
582
583         if (reply->rdata.command != CTDB_EVENT_COMMAND_SCRIPT_DISABLE) {
584                 if (perr != NULL) {
585                         *perr = EPROTO;
586                 }
587                 talloc_free(reply);
588                 return false;
589         }
590
591         if (result != NULL) {
592                 *result = reply->rdata.result;
593         }
594
595         talloc_free(reply);
596         return true;
597 }