wscript: separate embedded_heimdal from system_heimdal
[Samba.git] / ctdb / common / sock_client.c
blob75f471fc5de7d9d493dfa264a3932478fd6d742d
1 /*
2 A client based on unix domain socket
4 Copyright (C) Amitay Isaacs 2017
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/>.
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "system/network.h"
24 #include <talloc.h>
25 #include <tevent.h>
27 #include "lib/util/debug.h"
28 #include "lib/util/time.h"
29 #include "lib/util/tevent_unix.h"
31 #include "common/logging.h"
32 #include "common/reqid.h"
33 #include "common/comm.h"
34 #include "common/sock_client.h"
36 struct sock_client_context {
37 struct sock_client_proto_funcs *funcs;
38 void *private_data;
40 void (*disconnect_callback)(void *private_data);
41 void *disconnect_data;
43 int fd;
44 struct comm_context *comm;
45 struct reqid_context *idr;
49 * connect to a unix domain socket
52 static int socket_connect(const char *sockpath)
54 struct sockaddr_un addr;
55 size_t len;
56 int fd, ret;
58 memset(&addr, 0, sizeof(addr));
59 addr.sun_family = AF_UNIX;
61 len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
62 if (len >= sizeof(addr.sun_path)) {
63 D_ERR("socket path too long: %s\n", sockpath);
64 return -1;
67 fd = socket(AF_UNIX, SOCK_STREAM, 0);
68 if (fd == -1) {
69 D_ERR("socket create failed - %s\n", sockpath);
70 return -1;
73 ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
74 if (ret != 0) {
75 D_ERR("socket connect failed - %s\n", sockpath);
76 close(fd);
77 return -1;
80 return fd;
84 * Socket client
87 static int sock_client_context_destructor(struct sock_client_context *sockc);
88 static void sock_client_read_handler(uint8_t *buf, size_t buflen,
89 void *private_data);
90 static void sock_client_dead_handler(void *private_data);
92 static void sock_client_msg_reply(struct sock_client_context *sockc,
93 uint8_t *buf, size_t buflen);
95 int sock_client_setup(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
96 const char *sockpath,
97 struct sock_client_proto_funcs *funcs,
98 void *private_data,
99 struct sock_client_context **result)
101 struct sock_client_context *sockc;
102 int ret;
104 if (sockpath == NULL) {
105 return EINVAL;
108 if (funcs == NULL || funcs->request_push == NULL ||
109 funcs->reply_pull == NULL || funcs->reply_reqid == NULL) {
110 return EINVAL;
113 sockc = talloc_zero(mem_ctx, struct sock_client_context);
114 if (sockc == NULL) {
115 return ENOMEM;
118 sockc->funcs = funcs;
119 sockc->private_data = private_data;
121 sockc->fd = socket_connect(sockpath);
122 if (sockc->fd == -1) {
123 talloc_free(sockc);
124 return EIO;
127 ret = comm_setup(sockc, ev, sockc->fd,
128 sock_client_read_handler, sockc,
129 sock_client_dead_handler, sockc,
130 &sockc->comm);
131 if (ret != 0) {
132 D_ERR("comm_setup() failed, ret=%d\n", ret);
133 close(sockc->fd);
134 talloc_free(sockc);
135 return ret;
138 ret = reqid_init(sockc, INT_MAX-200, &sockc->idr);
139 if (ret != 0) {
140 D_ERR("reqid_init() failed, ret=%d\n", ret);
141 close(sockc->fd);
142 talloc_free(sockc);
143 return ret;
146 talloc_set_destructor(sockc, sock_client_context_destructor);
148 *result = sockc;
149 return 0;
152 static int sock_client_context_destructor(struct sock_client_context *sockc)
154 TALLOC_FREE(sockc->comm);
155 if (sockc->fd != -1) {
156 close(sockc->fd);
157 sockc->fd = -1;
159 return 0;
163 static void sock_client_read_handler(uint8_t *buf, size_t buflen,
164 void *private_data)
166 struct sock_client_context *sockc = talloc_get_type_abort(
167 private_data, struct sock_client_context);
169 sock_client_msg_reply(sockc, buf, buflen);
172 static void sock_client_dead_handler(void *private_data)
174 struct sock_client_context *sockc = talloc_get_type_abort(
175 private_data, struct sock_client_context);
177 if (sockc->disconnect_callback != NULL) {
178 sockc->disconnect_callback(sockc->disconnect_data);
179 talloc_free(sockc);
180 return;
183 D_NOTICE("connection to daemon closed, exiting\n");
184 exit(1);
187 void sock_client_set_disconnect_callback(struct sock_client_context *sockc,
188 sock_client_callback_func_t callback,
189 void *private_data)
191 sockc->disconnect_callback = callback;
192 sockc->disconnect_data = private_data;
196 struct sock_client_msg_state {
197 struct sock_client_context *sockc;
198 uint32_t reqid;
199 struct tevent_req *req;
200 void *reply;
203 static int sock_client_msg_state_destructor(
204 struct sock_client_msg_state *state);
205 static void sock_client_msg_done(struct tevent_req *subreq);
207 struct tevent_req *sock_client_msg_send(TALLOC_CTX *mem_ctx,
208 struct tevent_context *ev,
209 struct sock_client_context *sockc,
210 struct timeval timeout,
211 void *request)
213 struct tevent_req *req, *subreq;
214 struct sock_client_msg_state *state;
215 uint8_t *buf;
216 size_t buflen;
217 int ret;
219 req = tevent_req_create(mem_ctx, &state, struct sock_client_msg_state);
220 if (req == NULL) {
221 return NULL;
224 state->sockc = sockc;
226 state->reqid = reqid_new(sockc->idr, state);
227 if (state->reqid == REQID_INVALID) {
228 talloc_free(req);
229 return NULL;
232 state->req = req;
234 talloc_set_destructor(state, sock_client_msg_state_destructor);
236 ret = sockc->funcs->request_push(request, state->reqid, state,
237 &buf, &buflen, sockc->private_data);
238 if (ret != 0) {
239 tevent_req_error(req, ret);
240 return tevent_req_post(req, ev);
243 subreq = comm_write_send(state, ev, sockc->comm, buf, buflen);
244 if (tevent_req_nomem(subreq, req)) {
245 return tevent_req_post(req, ev);
247 tevent_req_set_callback(subreq, sock_client_msg_done, req);
249 if (! timeval_is_zero(&timeout)) {
250 if (!tevent_req_set_endtime(req, ev, timeout)) {
251 return tevent_req_post(req, ev);
255 return req;
258 static int sock_client_msg_state_destructor(
259 struct sock_client_msg_state *state)
261 reqid_remove(state->sockc->idr, state->reqid);
262 return 0;
265 static void sock_client_msg_done(struct tevent_req *subreq)
267 struct tevent_req *req = tevent_req_callback_data(
268 subreq, struct tevent_req);
269 int ret;
270 bool status;
272 status = comm_write_recv(subreq, &ret);
273 TALLOC_FREE(subreq);
274 if (! status) {
275 tevent_req_error(req, ret);
276 return;
279 /* wait for the reply or timeout */
282 static void sock_client_msg_reply(struct sock_client_context *sockc,
283 uint8_t *buf, size_t buflen)
285 struct sock_client_msg_state *state;
286 uint32_t reqid;
287 int ret;
289 ret = sockc->funcs->reply_reqid(buf, buflen, &reqid,
290 sockc->private_data);
291 if (ret != 0) {
292 D_WARNING("Invalid packet received, ret=%d\n", ret);
293 return;
296 state = reqid_find(sockc->idr, reqid, struct sock_client_msg_state);
297 if (state == NULL) {
298 return;
301 if (reqid != state->reqid) {
302 return;
305 ret = sockc->funcs->reply_pull(buf, buflen, state, &state->reply,
306 sockc->private_data);
307 if (ret != 0) {
308 tevent_req_error(state->req, ret);
309 return;
312 tevent_req_done(state->req);
315 bool sock_client_msg_recv(struct tevent_req *req, int *perr,
316 TALLOC_CTX *mem_ctx, void *reply)
318 struct sock_client_msg_state *state = tevent_req_data(
319 req, struct sock_client_msg_state);
320 int ret;
322 if (tevent_req_is_unix_error(req, &ret)) {
323 if (perr != NULL) {
324 *perr = ret;
326 return false;
329 if (reply != NULL) {
330 *(void **)reply = talloc_steal(mem_ctx, state->reply);
333 return true;