4 Copyright (C) Amitay Isaacs 2015
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.
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.
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/>.
21 #include "system/network.h"
22 #include "system/filesys.h"
28 #include "common/reqid.h"
29 #include "common/srvid.h"
30 #include "common/comm.h"
31 #include "common/logging.h"
33 #include "lib/util/tevent_unix.h"
34 #include "lib/util/debug.h"
36 #include "protocol/protocol.h"
37 #include "protocol/protocol_api.h"
39 #include "client/client_private.h"
40 #include "client/client.h"
42 static int ctdb_client_connect(struct ctdb_client_context
*client
,
43 struct tevent_context
*ev
,
44 const char *sockpath
);
46 static int ctdb_client_context_destructor(struct ctdb_client_context
*client
);
48 int ctdb_client_init(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
49 const char *sockpath
, struct ctdb_client_context
**out
)
51 struct ctdb_client_context
*client
;
54 client
= talloc_zero(mem_ctx
, struct ctdb_client_context
);
56 DEBUG(DEBUG_ERR
, (__location__
" memory allocation error\n"));
60 ret
= reqid_init(client
, INT_MAX
-200, &client
->idr
);
62 DEBUG(DEBUG_ERR
, ("reqid_init() failed, ret=%d\n", ret
));
67 ret
= srvid_init(client
, &client
->srv
);
69 DEBUG(DEBUG_ERR
, ("srvid_init() failed, ret=%d\n", ret
));
75 client
->pnn
= CTDB_UNKNOWN_PNN
;
77 ret
= ctdb_client_connect(client
, ev
, sockpath
);
83 talloc_set_destructor(client
, ctdb_client_context_destructor
);
89 static int ctdb_client_context_destructor(struct ctdb_client_context
*client
)
91 if (client
->fd
!= -1) {
98 static void client_read_handler(uint8_t *buf
, size_t buflen
,
100 static void client_dead_handler(void *private_data
);
102 static int ctdb_client_connect(struct ctdb_client_context
*client
,
103 struct tevent_context
*ev
, const char *sockpath
)
105 struct sockaddr_un addr
;
109 if (sockpath
== NULL
) {
110 DEBUG(DEBUG_ERR
, ("socket path cannot be NULL\n"));
114 memset(&addr
, 0, sizeof(addr
));
115 addr
.sun_family
= AF_UNIX
;
116 len
= strlcpy(addr
.sun_path
, sockpath
, sizeof(addr
.sun_path
));
117 if (len
!= strlen(sockpath
)) {
118 DEBUG(DEBUG_ERR
, ("socket path too long, len=%zu\n",
123 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
126 DEBUG(DEBUG_ERR
, ("socket() failed, errno=%d\n", ret
));
130 ret
= connect(fd
, (struct sockaddr
*)&addr
, sizeof(addr
));
133 DEBUG(DEBUG_ERR
, ("connect() failed, errno=%d\n", ret
));
139 ret
= comm_setup(client
, ev
, fd
, client_read_handler
, client
,
140 client_dead_handler
, client
, &client
->comm
);
142 DEBUG(DEBUG_ERR
, ("comm_setup() failed, ret=%d\n", ret
));
148 ret
= ctdb_ctrl_get_pnn(client
, ev
, client
, CTDB_CURRENT_NODE
,
149 tevent_timeval_zero(), &client
->pnn
);
151 DEBUG(DEBUG_ERR
, ("failed to get current node pnn\n"));
154 TALLOC_FREE(client
->comm
);
161 static void client_read_handler(uint8_t *buf
, size_t buflen
,
164 struct ctdb_client_context
*client
= talloc_get_type_abort(
165 private_data
, struct ctdb_client_context
);
166 struct ctdb_req_header hdr
;
169 ret
= ctdb_req_header_pull(buf
, buflen
, &hdr
);
171 DEBUG(DEBUG_WARNING
, ("invalid header, ret=%d\n", ret
));
175 if (buflen
!= hdr
.length
) {
176 DEBUG(DEBUG_WARNING
, ("packet size mismatch %zu != %d\n",
177 buflen
, hdr
.length
));
181 ret
= ctdb_req_header_verify(&hdr
, 0);
183 DEBUG(DEBUG_WARNING
, ("invalid header, ret=%d\n", ret
));
187 switch (hdr
.operation
) {
188 case CTDB_REPLY_CALL
:
189 ctdb_client_reply_call(client
, buf
, buflen
, hdr
.reqid
);
192 case CTDB_REQ_MESSAGE
:
193 ctdb_client_req_message(client
, buf
, buflen
, hdr
.reqid
);
196 case CTDB_REPLY_CONTROL
:
197 ctdb_client_reply_control(client
, buf
, buflen
, hdr
.reqid
);
205 static void client_dead_handler(void *private_data
)
207 struct ctdb_client_context
*client
= talloc_get_type_abort(
208 private_data
, struct ctdb_client_context
);
209 ctdb_client_callback_func_t callback
= client
->callback
;
210 void *callback_data
= client
->private_data
;
213 if (callback
!= NULL
) {
214 callback(callback_data
);
218 DEBUG(DEBUG_NOTICE
, ("connection to daemon closed, exiting\n"));
222 void ctdb_client_set_disconnect_callback(struct ctdb_client_context
*client
,
223 ctdb_client_callback_func_t callback
,
226 client
->callback
= callback
;
227 client
->private_data
= private_data
;
230 uint32_t ctdb_client_pnn(struct ctdb_client_context
*client
)
235 void ctdb_client_wait(struct tevent_context
*ev
, bool *done
)
238 tevent_loop_once(ev
);
242 static void ctdb_client_wait_timeout_handler(struct tevent_context
*ev
,
243 struct tevent_timer
*te
,
247 bool *timed_out
= (bool *)private_data
;
252 int ctdb_client_wait_timeout(struct tevent_context
*ev
, bool *done
,
253 struct timeval timeout
)
256 struct tevent_timer
*timer
;
257 bool timed_out
= false;
259 mem_ctx
= talloc_new(ev
);
260 if (mem_ctx
== NULL
) {
264 timer
= tevent_add_timer(ev
, mem_ctx
, timeout
,
265 ctdb_client_wait_timeout_handler
,
268 talloc_free(mem_ctx
);
272 while (! (*done
) && ! timed_out
) {
273 tevent_loop_once(ev
);
276 talloc_free(mem_ctx
);
285 struct ctdb_recovery_wait_state
{
286 struct tevent_context
*ev
;
287 struct ctdb_client_context
*client
;
290 static void ctdb_recovery_wait_recmode(struct tevent_req
*subreq
);
291 static void ctdb_recovery_wait_retry(struct tevent_req
*subreq
);
293 struct tevent_req
*ctdb_recovery_wait_send(TALLOC_CTX
*mem_ctx
,
294 struct tevent_context
*ev
,
295 struct ctdb_client_context
*client
)
297 struct tevent_req
*req
, *subreq
;
298 struct ctdb_recovery_wait_state
*state
;
299 struct ctdb_req_control request
;
301 req
= tevent_req_create(mem_ctx
, &state
,
302 struct ctdb_recovery_wait_state
);
308 state
->client
= client
;
310 ctdb_req_control_get_recmode(&request
);
311 subreq
= ctdb_client_control_send(state
, ev
, client
, client
->pnn
,
312 tevent_timeval_zero(), &request
);
313 if (tevent_req_nomem(subreq
, req
)) {
314 return tevent_req_post(req
, ev
);
316 tevent_req_set_callback(subreq
, ctdb_recovery_wait_recmode
, req
);
321 static void ctdb_recovery_wait_recmode(struct tevent_req
*subreq
)
323 struct tevent_req
*req
= tevent_req_callback_data(
324 subreq
, struct tevent_req
);
325 struct ctdb_recovery_wait_state
*state
= tevent_req_data(
326 req
, struct ctdb_recovery_wait_state
);
327 struct ctdb_reply_control
*reply
;
332 status
= ctdb_client_control_recv(subreq
, &ret
, state
, &reply
);
335 tevent_req_error(req
, ret
);
339 ret
= ctdb_reply_control_get_recmode(reply
, &recmode
);
341 tevent_req_error(req
, ret
);
345 if (recmode
== CTDB_RECOVERY_NORMAL
) {
346 tevent_req_done(req
);
350 subreq
= tevent_wakeup_send(state
, state
->ev
,
351 tevent_timeval_current_ofs(1, 0));
352 if (tevent_req_nomem(subreq
, req
)) {
355 tevent_req_set_callback(subreq
, ctdb_recovery_wait_retry
, req
);
358 static void ctdb_recovery_wait_retry(struct tevent_req
*subreq
)
360 struct tevent_req
*req
= tevent_req_callback_data(
361 subreq
, struct tevent_req
);
362 struct ctdb_recovery_wait_state
*state
= tevent_req_data(
363 req
, struct ctdb_recovery_wait_state
);
364 struct ctdb_req_control request
;
367 status
= tevent_wakeup_recv(subreq
);
370 tevent_req_error(req
, ENOMEM
);
374 ctdb_req_control_get_recmode(&request
);
375 subreq
= ctdb_client_control_send(state
, state
->ev
, state
->client
,
377 tevent_timeval_zero(), &request
);
378 if (tevent_req_nomem(subreq
, req
)) {
381 tevent_req_set_callback(subreq
, ctdb_recovery_wait_recmode
, req
);
384 bool ctdb_recovery_wait_recv(struct tevent_req
*req
, int *perr
)
388 if (tevent_req_is_unix_error(req
, &err
)) {
398 bool ctdb_recovery_wait(struct tevent_context
*ev
,
399 struct ctdb_client_context
*client
)
402 struct tevent_req
*req
;
405 mem_ctx
= talloc_new(client
);
406 if (mem_ctx
== NULL
) {
410 req
= ctdb_recovery_wait_send(mem_ctx
, ev
, client
);
415 tevent_req_poll(req
, ev
);
417 status
= ctdb_recovery_wait_recv(req
, NULL
);
419 talloc_free(mem_ctx
);