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/>.
21 #include "system/filesys.h"
22 #include "system/network.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;
40 void (*disconnect_callback)(void *private_data);
41 void *disconnect_data;
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;
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);
67 fd = socket(AF_UNIX, SOCK_STREAM, 0);
69 D_ERR("socket create failed - %s\n", sockpath);
73 ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
75 D_ERR("socket connect failed - %s\n", sockpath);
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,
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,
97 struct sock_client_proto_funcs *funcs,
99 struct sock_client_context **result)
101 struct sock_client_context *sockc;
104 if (sockpath == NULL) {
108 if (funcs == NULL || funcs->request_push == NULL ||
109 funcs->reply_pull == NULL || funcs->reply_reqid == NULL) {
113 sockc = talloc_zero(mem_ctx, struct sock_client_context);
118 sockc->funcs = funcs;
119 sockc->private_data = private_data;
121 sockc->fd = socket_connect(sockpath);
122 if (sockc->fd == -1) {
127 ret = comm_setup(sockc, ev, sockc->fd,
128 sock_client_read_handler, sockc,
129 sock_client_dead_handler, sockc,
132 D_ERR("comm_setup() failed, ret=%d\n", ret);
138 ret = reqid_init(sockc, INT_MAX-200, &sockc->idr);
140 D_ERR("reqid_init() failed, ret=%d\n", ret);
146 talloc_set_destructor(sockc, sock_client_context_destructor);
152 static int sock_client_context_destructor(struct sock_client_context *sockc)
154 TALLOC_FREE(sockc->comm);
155 if (sockc->fd != -1) {
163 static void sock_client_read_handler(uint8_t *buf, size_t buflen,
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);
183 D_NOTICE("connection to daemon closed, exiting\n");
187 void sock_client_set_disconnect_callback(struct sock_client_context *sockc,
188 sock_client_callback_func_t callback,
191 sockc->disconnect_callback = callback;
192 sockc->disconnect_data = private_data;
196 struct sock_client_msg_state {
197 struct sock_client_context *sockc;
199 struct tevent_req *req;
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,
213 struct tevent_req *req, *subreq;
214 struct sock_client_msg_state *state;
219 req = tevent_req_create(mem_ctx, &state, struct sock_client_msg_state);
224 state->sockc = sockc;
226 state->reqid = reqid_new(sockc->idr, state);
227 if (state->reqid == REQID_INVALID) {
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);
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);
258 static int sock_client_msg_state_destructor(
259 struct sock_client_msg_state *state)
261 reqid_remove(state->sockc->idr, state->reqid);
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);
272 status = comm_write_recv(subreq, &ret);
275 tevent_req_error(req, ret);
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;
289 ret = sockc->funcs->reply_reqid(buf, buflen, &reqid,
290 sockc->private_data);
292 D_WARNING("Invalid packet received, ret=%d\n", ret);
296 state = reqid_find(sockc->idr, reqid, struct sock_client_msg_state);
301 if (reqid != state->reqid) {
305 ret = sockc->funcs->reply_pull(buf, buflen, state, &state->reply,
306 sockc->private_data);
308 tevent_req_error(state->req, ret);
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);
322 if (tevent_req_is_unix_error(req, &ret)) {
330 *(void **)reply = talloc_steal(mem_ctx, state->reply);