Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #include <stdio.h>
32 : : #include <stdlib.h>
33 : : #include <unistd.h>
34 : : #include <errno.h>
35 : : #include <string.h>
36 : : #include <sys/types.h>
37 : : #include <sys/socket.h>
38 : : #include <netinet/in.h>
39 : : #include <netdb.h>
40 : : #include <arpa/inet.h>
41 : : #include <sys/wait.h>
42 : : #include <sys/un.h>
43 : :
44 : : #include "tapdisk.h"
45 : : #include "tapdisk-fdreceiver.h"
46 : : #include "tapdisk-server.h"
47 : : #include "timeout-math.h"
48 : : #include "scheduler.h"
49 : :
50 : : #define UNIX_BUFFER_SIZE 16384
51 : :
52 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, "nbd: " _f, ##_a)
53 : : #define ERROR(_f, _a...) tlog_syslog(TLOG_WARN, "nbd: " _f, ##_a)
54 : :
55 : : static void
56 : 0 : td_fdreceiver_recv_fd(event_id_t id, char mode, void *data)
57 : : {
58 : 0 : struct td_fdreceiver *fdreceiver = data;
59 : 0 : int ret, cv_flags = 0, *fdp, fd = -1;
60 : : long numbytes;
61 : : char *iobuf;
62 : : char buf[CMSG_SPACE(sizeof(fd))];
63 : : struct sockaddr_un unix_socket_name;
64 : :
65 : : struct msghdr msg;
66 : : struct iovec vec;
67 : : struct cmsghdr *cmsg;
68 : :
69 : 0 : numbytes = UNIX_BUFFER_SIZE;
70 : 0 : iobuf = malloc(numbytes);
71 [ # # ]: 0 : if (!iobuf) {
72 : 0 : ERROR("Failed to allocate iobuf");
73 : 0 : return;
74 : : }
75 : 0 : bzero(iobuf, numbytes);
76 : :
77 : 0 : msg.msg_name = &unix_socket_name;
78 : 0 : msg.msg_namelen = sizeof(unix_socket_name);
79 : 0 : vec.iov_base = iobuf;
80 : 0 : vec.iov_len = numbytes;
81 : 0 : msg.msg_iov = &vec;
82 : :
83 : 0 : msg.msg_iovlen = 1;
84 : :
85 : 0 : msg.msg_control = buf;
86 : 0 : msg.msg_controllen = sizeof(buf);
87 : :
88 : 0 : ret = recvmsg(fdreceiver->client_fd, &msg, cv_flags);
89 : :
90 [ # # ]: 0 : if (ret == -1) {
91 : 0 : ERROR("Failed to receive the message: %d", ret);
92 : : goto out;
93 : : }
94 : :
95 [ # # ][ # # ]: 0 : if (ret > 0 && msg.msg_controllen > 0) {
96 [ # # ]: 0 : cmsg = CMSG_FIRSTHDR(&msg);
97 [ # # ]: 0 : if (cmsg->cmsg_level == SOL_SOCKET &&
98 : : (cmsg->cmsg_type == SCM_RIGHTS)) {
99 : 0 : fdp = (int*)CMSG_DATA(cmsg);
100 : 0 : fd = *fdp;
101 : : } else {
102 : 0 : ERROR("Failed to recieve a file descriptor");
103 : : }
104 : : } else {
105 : : fd = -1;
106 : : }
107 : :
108 : 0 : INFO("Received fd %d with message: %s", fd, iobuf);
109 : :
110 : : /*
111 : : * We're done with this connection, it was only transiently used to
112 : : * connect the client
113 : : */
114 : 0 : close(fdreceiver->client_fd);
115 : 0 : fdreceiver->client_fd = -1;
116 : :
117 : 0 : tapdisk_server_unregister_event(fdreceiver->client_event_id);
118 : 0 : fdreceiver->client_event_id = -1;
119 : :
120 : : /*
121 : : * It is the responsibility of this callback function to arrange that
122 : : * the fd is eventually closed
123 : : */
124 [ # # ]: 0 : if (fd >= 0) {
125 : 0 : fdreceiver->callback(fd, iobuf, fdreceiver->callback_data);
126 : : }
127 : : out:
128 : 0 : free(iobuf);
129 : : }
130 : :
131 : : static void
132 : 0 : td_fdreceiver_accept_fd(event_id_t id, char mode, void *data)
133 : : {
134 : : struct sockaddr_storage their_addr;
135 : 0 : socklen_t sin_size = sizeof(their_addr);
136 : 0 : struct td_fdreceiver *fdreceiver = data;
137 : : int new_fd;
138 : :
139 : 0 : INFO("Unix domain socket is ready to accept");
140 : :
141 : 0 : new_fd = accept(fdreceiver->fd,
142 : : (struct sockaddr *)&their_addr, &sin_size);
143 : :
144 [ # # ]: 0 : if (new_fd == -1) {
145 : 0 : ERROR("td_receiver_accept_fd: failed to accept (errno=%d)", errno);
146 : 0 : return;
147 : : }
148 : :
149 [ # # ]: 0 : if (fdreceiver->client_fd != -1) {
150 : 0 : ERROR("td_fdreceiver_accept_fd: can only cope with one connec"
151 : : "tion at once to the unix domain socket!");
152 : 0 : close(new_fd);
153 : : return;
154 : : }
155 : :
156 : 0 : fdreceiver->client_event_id =
157 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
158 : 0 : new_fd, TV_ZERO,
159 : : td_fdreceiver_recv_fd,
160 : : fdreceiver);
161 : :
162 [ # # ]: 0 : if (fdreceiver->client_event_id < 0) {
163 : 0 : ERROR("td_fdreceiver_accept_fd: failed to register event "
164 : : "(errno=%d)", errno);
165 : 0 : close(new_fd);
166 : : } else {
167 : 0 : fdreceiver->client_fd = new_fd;
168 : : }
169 : : }
170 : :
171 : : void
172 : 0 : td_fdreceiver_stop(struct td_fdreceiver *fdreceiver)
173 : : {
174 [ # # ]: 0 : if (fdreceiver->client_fd >= 0)
175 : 0 : close(fdreceiver->client_fd);
176 : :
177 [ # # ]: 0 : if (fdreceiver->client_event_id >= 0)
178 : 0 : tapdisk_server_unregister_event(fdreceiver->client_event_id);
179 : :
180 [ # # ]: 0 : if (fdreceiver->fd >= 0)
181 : 0 : close(fdreceiver->fd);
182 : :
183 [ # # ]: 0 : if (fdreceiver->fd_event_id >= 0)
184 : 0 : tapdisk_server_unregister_event(fdreceiver->fd_event_id);
185 : :
186 [ # # ]: 0 : if (fdreceiver->path != NULL) {
187 : 0 : unlink(fdreceiver->path);
188 : 0 : free(fdreceiver->path);
189 : : }
190 : :
191 : 0 : free(fdreceiver);
192 : 0 : }
193 : :
194 : : struct td_fdreceiver *
195 : 0 : td_fdreceiver_start(char *path, fd_cb_t callback, void *data)
196 : : {
197 : 0 : int s = -1;
198 : : struct sockaddr_un local;
199 : : int len;
200 : : int err;
201 : : struct td_fdreceiver *fdreceiver;
202 : :
203 : 0 : fdreceiver = malloc(sizeof(struct td_fdreceiver));
204 [ # # ]: 0 : if (!fdreceiver) {
205 : 0 : ERROR("td_fdreceiver_start: error allocating memory for "
206 : : "fdreceiver (path=%s)", path);
207 : 0 : return NULL;
208 : : }
209 : :
210 : 0 : fdreceiver->path = strdup(path);
211 [ # # ]: 0 : if (unlikely(!fdreceiver->path)) {
212 : 0 : ERROR("td_fdreceiver_start: error allocating memory for "
213 : : "fdreceiver->path (path=%s)", path);
214 : : goto error;
215 : : }
216 : :
217 : 0 : fdreceiver->fd = -1;
218 : 0 : fdreceiver->fd_event_id = -1;
219 : 0 : fdreceiver->client_fd = -1;
220 : 0 : fdreceiver->client_event_id = -1;
221 : 0 : fdreceiver->callback = callback;
222 : 0 : fdreceiver->callback_data = data;
223 : :
224 : 0 : err = snprintf(local.sun_path, sizeof(local.sun_path), "%s", path);
225 [ # # ]: 0 : if (unlikely(err >= sizeof(local.sun_path))) {
226 : 0 : ERROR("td_fdreceiver_start: socket name too long (path=%s)", path);
227 : : goto error;
228 [ # # ]: 0 : } else if (unlikely(err < 0)) {
229 : 0 : ERROR("td_fdreceiver_start: snprintf() error (path=%s)", path);
230 : : goto error;
231 : : }
232 : 0 : local.sun_family = AF_UNIX;
233 : :
234 : : /*
235 : : * NB: here we unlink anything that was there before - be very careful
236 : : * with the paths you pass to this function!
237 : : */
238 : 0 : unlink(local.sun_path);
239 : 0 : len = strlen(local.sun_path) + sizeof(local.sun_family);
240 : :
241 : 0 : s = socket(AF_UNIX, SOCK_STREAM, 0);
242 : :
243 [ # # ]: 0 : if (s < 0) {
244 : 0 : ERROR("td_fdreceiver_start: error creating socket "
245 : : "(path=%s)", path);
246 : : goto error;
247 : : }
248 : :
249 : 0 : err = bind(s, (struct sockaddr *)&local, len);
250 [ # # ]: 0 : if (err < 0) {
251 : 0 : ERROR("td_fdreceiver_start: error binding (path=%s)", path);
252 : : goto error;
253 : : }
254 : :
255 : 0 : err = listen(s, 5);
256 [ # # ]: 0 : if (err < 0) {
257 : 0 : ERROR("td_fdreceiver_start: error listening (path=%s)", path);
258 : : goto error;
259 : : }
260 : :
261 : 0 : fdreceiver->fd = s;
262 : :
263 : 0 : fdreceiver->fd_event_id =
264 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
265 : 0 : fdreceiver->fd, TV_ZERO,
266 : : td_fdreceiver_accept_fd,
267 : : fdreceiver);
268 : :
269 [ # # ]: 0 : if (fdreceiver->fd_event_id < 0) {
270 : 0 : ERROR("td_fdreceiver_start: error registering event "
271 : : "(path=%s)", path);
272 : : goto error;
273 : : }
274 : :
275 : 0 : INFO("Set up local unix domain socket on path '%s'", path);
276 : :
277 : : return fdreceiver;
278 : :
279 : : error:
280 : 0 : free(fdreceiver->path);
281 : 0 : free(fdreceiver);
282 : :
283 [ # # ]: 0 : if (s >= 0)
284 : 0 : close(s);
285 : :
286 : : return NULL;
287 : : }
|