0c924855755d574b28a00874bf929e631c7cab64
[obnox/samba/samba-obnox.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
42 static int ctdb_client_connect(struct ctdb_client_context *client,
43                                struct tevent_context *ev,
44                                const char *sockpath);
45
46 int ctdb_client_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
47                      const char *sockpath, struct ctdb_client_context **out)
48 {
49         struct ctdb_client_context *client;
50         int ret;
51
52         client = talloc_zero(mem_ctx, struct ctdb_client_context);
53         if (client == NULL) {
54                 DEBUG(DEBUG_ERR, (__location__ " memory allocation error\n"));
55                 return ENOMEM;
56         }
57
58         ret = reqid_init(client, INT_MAX-200, &client->idr);
59         if (ret != 0) {
60                 DEBUG(DEBUG_ERR, ("reqid_init() failed, ret=%d\n", ret));
61                 talloc_free(client);
62                 return ret;
63         }
64
65         ret = srvid_init(client, &client->srv);
66         if (ret != 0) {
67                 DEBUG(DEBUG_ERR, ("srvid_init() failed, ret=%d\n", ret));
68                 talloc_free(client);
69                 return ret;
70         }
71
72         client->fd = -1;
73         client->pnn = CTDB_UNKNOWN_PNN;
74
75         ret = ctdb_client_connect(client, ev, sockpath);
76         if (ret != 0) {
77                 talloc_free(client);
78                 return ret;
79         }
80
81         *out = client;
82         return 0;
83 }
84
85 static void client_read_handler(uint8_t *buf, size_t buflen,
86                                 void *private_data);
87 static void client_dead_handler(void *private_data);
88
89 static int ctdb_client_connect(struct ctdb_client_context *client,
90                                struct tevent_context *ev, const char *sockpath)
91 {
92         struct sockaddr_un addr;
93         size_t len;
94         int fd, ret;
95
96         if (sockpath == NULL) {
97                 DEBUG(DEBUG_ERR, ("socket path cannot be NULL\n"));
98                 return EINVAL;
99         }
100
101         memset(&addr, 0, sizeof(addr));
102         addr.sun_family = AF_UNIX;
103         len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
104         if (len != strlen(sockpath)) {
105                 DEBUG(DEBUG_ERR, ("socket path too long, len=%zu\n",
106                                   strlen(sockpath)));
107                 return ENAMETOOLONG;
108         }
109
110         fd = socket(AF_UNIX, SOCK_STREAM, 0);
111         if (fd == -1) {
112                 ret = errno;
113                 DEBUG(DEBUG_ERR, ("socket() failed, errno=%d\n", ret));
114                 return ret;
115         }
116
117         ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
118         if (ret == -1) {
119                 ret = errno;
120                 DEBUG(DEBUG_ERR, ("connect() failed, errno=%d\n", ret));
121                 close(fd);
122                 return ret;
123         }
124         client->fd = fd;
125
126         ret = comm_setup(client, ev, fd, client_read_handler, client,
127                          client_dead_handler, client, &client->comm);
128         if (ret != 0) {
129                 DEBUG(DEBUG_ERR, ("comm_setup() failed, ret=%d\n", ret));
130                 close(fd);
131                 client->fd = -1;
132                 return ret;
133         }
134
135         ret = ctdb_ctrl_get_pnn(client, ev, client, CTDB_CURRENT_NODE,
136                                 tevent_timeval_zero(), &client->pnn);
137         if (ret != 0) {
138                 DEBUG(DEBUG_ERR, ("failed to get current node pnn\n"));
139                 close(fd);
140                 client->fd = -1;
141                 TALLOC_FREE(client->comm);
142                 return ret;
143         }
144
145         return 0;
146 }
147
148 static void client_read_handler(uint8_t *buf, size_t buflen,
149                                 void *private_data)
150 {
151         struct ctdb_client_context *client = talloc_get_type_abort(
152                 private_data, struct ctdb_client_context);
153         struct ctdb_req_header hdr;
154         int ret;
155
156         ret = ctdb_req_header_pull(discard_const(buf), buflen, &hdr);
157         if (ret != 0) {
158                 DEBUG(DEBUG_WARNING, ("invalid header, ret=%d\n", ret));
159                 return;
160         }
161
162         if (buflen != hdr.length) {
163                 DEBUG(DEBUG_WARNING, ("packet size mismatch %zu != %d\n",
164                                       buflen, hdr.length));
165                 return;
166         }
167
168         ret = ctdb_req_header_verify(&hdr, 0);
169         if (ret != 0) {
170                 DEBUG(DEBUG_WARNING, ("invalid header, ret=%d\n", ret));
171                 return;
172         }
173
174         switch (hdr.operation) {
175         case CTDB_REPLY_CALL:
176                 ctdb_client_reply_call(client, buf, buflen, hdr.reqid);
177                 break;
178
179         case CTDB_REQ_MESSAGE:
180                 ctdb_client_req_message(client, buf, buflen, hdr.reqid);
181                 break;
182
183         case CTDB_REPLY_CONTROL:
184                 ctdb_client_reply_control(client, buf, buflen, hdr.reqid);
185                 break;
186
187         default:
188                 break;
189         }
190 }
191
192 static void client_dead_handler(void *private_data)
193 {
194         struct ctdb_client_context *client = talloc_get_type_abort(
195                 private_data, struct ctdb_client_context);
196
197         DEBUG(DEBUG_NOTICE, ("connection to daemon closed, exiting\n"));
198         talloc_free(client);
199         exit(1);
200 }
201
202 uint32_t ctdb_client_pnn(struct ctdb_client_context *client)
203 {
204         return client->pnn;
205 }
206
207 void ctdb_client_wait(struct tevent_context *ev, bool *done)
208 {
209         while (! (*done)) {
210                 tevent_loop_once(ev);
211         }
212 }
213
214 struct ctdb_recovery_wait_state {
215         struct tevent_context *ev;
216         struct ctdb_client_context *client;
217 };
218
219 static void ctdb_recovery_wait_retry(struct tevent_req *subreq);
220
221 struct tevent_req *ctdb_recovery_wait_send(TALLOC_CTX *mem_ctx,
222                                            struct tevent_context *ev,
223                                            struct ctdb_client_context *client)
224 {
225         struct tevent_req *req, *subreq;
226         struct ctdb_recovery_wait_state *state;
227         int recmode;
228         int ret;
229
230         req = tevent_req_create(mem_ctx, &state,
231                                 struct ctdb_recovery_wait_state);
232         if (req == NULL) {
233                 return NULL;
234         }
235
236         state->ev = ev;
237         state->client = client;
238
239         ret = ctdb_ctrl_get_recmode(client, ev, client, client->pnn,
240                                     tevent_timeval_zero(), &recmode);
241         if (ret != 0) {
242                 tevent_req_error(req, ret);
243                 return tevent_req_post(req, ev);
244         }
245
246         if (recmode == CTDB_RECOVERY_NORMAL) {
247                 tevent_req_done(req);
248                 return tevent_req_post(req, ev);
249         }
250
251         subreq = tevent_wakeup_send(state, ev,
252                                     tevent_timeval_current_ofs(1, 0));
253         if (tevent_req_nomem(subreq, req)) {
254                 return tevent_req_post(req, ev);
255         }
256         tevent_req_set_callback(subreq, ctdb_recovery_wait_retry, req);
257
258         return req;
259 }
260
261 static void ctdb_recovery_wait_retry(struct tevent_req *subreq)
262 {
263         struct tevent_req *req = tevent_req_callback_data(
264                 subreq, struct tevent_req);
265         struct ctdb_recovery_wait_state *state = tevent_req_data(
266                 req, struct ctdb_recovery_wait_state);
267         int ret, recmode;
268         bool status;
269
270         status = tevent_wakeup_recv(subreq);
271         TALLOC_FREE(subreq);
272         if (! status) {
273                 tevent_req_error(req, ENOMEM);
274                 return;
275         }
276
277         ret = ctdb_ctrl_get_recmode(state, state->ev, state->client,
278                                     ctdb_client_pnn(state->client),
279                                     tevent_timeval_zero(), &recmode);
280         if (ret != 0) {
281                 tevent_req_error(req, ret);
282                 return;
283         }
284
285         if (recmode == CTDB_RECOVERY_NORMAL) {
286                 tevent_req_done(req);
287                 return;
288         }
289
290         subreq = tevent_wakeup_send(state, state->ev,
291                                     tevent_timeval_current_ofs(1, 0));
292         if (tevent_req_nomem(subreq, req)) {
293                 return;
294         }
295         tevent_req_set_callback(subreq, ctdb_recovery_wait_retry, req);
296 }
297
298 bool ctdb_recovery_wait_recv(struct tevent_req *req, int *perr)
299 {
300         int err;
301
302         if (tevent_req_is_unix_error(req, &err)) {
303                 if (perr != NULL) {
304                         *perr = err;
305                 }
306                 return false;
307         }
308
309         return true;
310 }