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
);