lib: unmap full ring buffer
[project/udebug.git] / client.c
1 #define _GNU_SOURCE
2 #include <sys/un.h>
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 #include <sys/ioctl.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <libgen.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <poll.h>
12
13 #ifdef linux
14 #include <linux/sockios.h>
15 #endif
16 #ifdef __APPLE__
17 #include <libproc.h>
18 #endif
19
20 #include "server.h"
21
22 #define UDEBUG_SNDBUF 65536
23
24 static LIST_HEAD(clients);
25
26 static void client_send_msg(struct client *cl, struct udebug_client_msg *data, int fd)
27 {
28 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = { 0 };
29 struct iovec iov = {
30 .iov_base = data,
31 .iov_len = sizeof(*data),
32 };
33 struct msghdr msg = {
34 .msg_control = fd_buf,
35 .msg_controllen = sizeof(fd_buf),
36 .msg_iov = &iov,
37 .msg_iovlen = 1,
38 };
39 struct cmsghdr *cmsg;
40 int buffered = 0;
41 int *pfd;
42 int len;
43
44 #ifdef linux
45 ioctl(cl->fd.fd, SIOCOUTQ, &buffered);
46 #elif defined(__APPLE__)
47 socklen_t slen = sizeof(buffered);
48 getsockopt(cl->fd.fd, SOL_SOCKET, SO_NWRITE, &buffered, &slen);
49 #endif
50
51 DC(3, cl, "send msg type=%d len=%d, fd=%d",
52 data->type, (unsigned int)iov.iov_len, fd);
53
54 if (buffered > UDEBUG_SNDBUF / 2) {
55 DC(3, cl, "skip message due to limited buffer size");
56 return;
57 }
58
59 if (fd >= 0) {
60 cmsg = CMSG_FIRSTHDR(&msg);
61 cmsg->cmsg_type = SCM_RIGHTS;
62 cmsg->cmsg_level = SOL_SOCKET;
63 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
64 msg.msg_controllen = cmsg->cmsg_len;
65
66 pfd = (int *)CMSG_DATA(cmsg);
67 *pfd = fd;
68 } else {
69 msg.msg_control = NULL;
70 msg.msg_controllen = 0;
71 }
72
73 do {
74 len = sendmsg(cl->fd.fd, &msg, 0);
75 } while (len < 0 && errno == EINTR);
76 }
77
78 static int client_alloc_notify_id(void)
79 {
80 struct client *cl;
81 uint32_t mask = 0;
82
83 list_for_each_entry(cl, &clients, list)
84 if (cl->notify_id >= 0)
85 mask |= 1 << cl->notify_id;
86
87 for (int i = 0; i < 32; i++, mask >>= 1)
88 if (!(mask & 1))
89 return i;
90
91 return 31;
92 }
93
94 static void client_msg_get_handle(struct client *cl)
95 {
96 struct udebug_client_msg msg = {
97 .type = CL_MSG_GET_HANDLE,
98 };
99
100 if (cl->notify_id < 0 && cl->uid == 0)
101 cl->notify_id = client_alloc_notify_id();
102
103 msg.id = cl->notify_id;
104 client_send_msg(cl, &msg, -1);
105 }
106
107 static void client_msg_ring_get(struct client *cl, uint32_t id)
108 {
109 struct udebug_client_msg msg = {
110 .type = CL_MSG_RING_GET,
111 .id = id,
112 };
113 struct client_ring *r = ring_get_by_id(id);
114 int fd = -1;
115
116 if (!r || cl->uid != 0) {
117 DC(2, cl, "could not get ring %x", id);
118 goto out;
119 }
120
121 fd = r->fd;
122 msg.ring_size = r->ring_size;
123 msg.data_size = r->data_size;
124
125 out:
126 client_send_msg(cl, &msg, fd);
127 }
128
129 static void client_msg_notify(struct client_ring *r, uint32_t mask)
130 {
131 struct udebug_client_msg msg = {
132 .type = CL_MSG_RING_NOTIFY,
133 .id = ring_id(r),
134 .notify_mask = mask,
135 };
136 struct client *cl;
137
138 list_for_each_entry(cl, &clients, list) {
139 if (cl->notify_id < 0 ||
140 !(mask & (1 << cl->notify_id)))
141 continue;
142
143 client_send_msg(cl, &msg, -1);
144 }
145 }
146
147 static void client_free(struct client *cl)
148 {
149 struct client_ring *r;
150
151 while (!list_empty(&cl->bufs)) {
152 r = list_first_entry(&cl->bufs, struct client_ring, list);
153 client_ring_free(r);
154 }
155
156 DC(2, cl, "disconnect");
157 uloop_fd_delete(&cl->fd);
158 close(cl->fd.fd);
159 list_del(&cl->list);
160
161 free(cl);
162 }
163
164 static void client_parse_message(struct client *cl)
165 {
166 struct udebug_client_msg *msg = &cl->rx_buf.msg;
167 struct client_ring *r;
168
169 DC(3, cl, "msg type=%d len=%d", msg->type, (unsigned int)cl->rx_ofs);
170 switch (msg->type) {
171 case CL_MSG_RING_ADD:
172 client_ring_alloc(cl);
173 break;
174 case CL_MSG_RING_REMOVE:
175 DC(2, cl, "delete ring %x", msg->id);
176 r = client_ring_get_by_id(cl, msg->id);
177 if (r)
178 client_ring_free(r);
179 else
180 DC(2, cl, "ring not found");
181 break;
182 case CL_MSG_RING_NOTIFY:
183 DC(3, cl, "notify on ring %d", msg->id);
184 r = client_ring_get_by_id(cl, msg->id);
185 if (r)
186 client_msg_notify(r, msg->notify_mask);
187 else
188 DC(2, cl, "local ring %d not found", msg->id);
189 break;
190 case CL_MSG_GET_HANDLE:
191 client_msg_get_handle(cl);
192 DC(2, cl, "get notify handle: %d", cl->notify_id);
193 break;
194 case CL_MSG_RING_GET:
195 DC(2, cl, "get ring %x", msg->id);
196 client_msg_ring_get(cl, msg->id);
197 break;
198 default:
199 DC(3, cl, "Invalid message type %d", msg->type);
200 break;
201 }
202 }
203
204 static void client_fd_cb(struct uloop_fd *fd, unsigned int events)
205 {
206 struct client *cl = container_of(fd, struct client, fd);
207 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = {};
208 struct iovec iov = {};
209 struct msghdr msg = {
210 .msg_iov = &iov,
211 .msg_iovlen = 1,
212 .msg_control = fd_buf,
213 .msg_controllen = sizeof(fd_buf),
214 };
215 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
216 size_t min_sz = sizeof(cl->rx_buf.msg) + sizeof(struct blob_attr);
217 ssize_t len;
218 int *pfd;
219
220 cmsg->cmsg_type = SCM_RIGHTS;
221 cmsg->cmsg_level = SOL_SOCKET;
222 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
223
224 pfd = (int *)CMSG_DATA(cmsg);
225 msg.msg_controllen = cmsg->cmsg_len;
226
227 retry:
228 if (fd->eof) {
229 client_free(cl);
230 return;
231 }
232
233 iov.iov_base = &cl->rx_buf;
234 iov.iov_len = min_sz;
235 if (!cl->rx_ofs) {
236 iov.iov_base = &cl->rx_buf.msg;
237 iov.iov_len = min_sz;
238
239 len = recvmsg(fd->fd, &msg, 0);
240 if (len < 0)
241 return;
242 if (!len)
243 fd->eof = true;
244
245 cl->rx_ofs = len;
246 cl->rx_fd = *pfd;
247 goto retry;
248 } else if (cl->rx_ofs >= min_sz) {
249 iov.iov_len += blob_pad_len(&cl->rx_buf.data);
250 iov.iov_len -= sizeof(struct blob_attr);
251 if (iov.iov_len > sizeof(cl->rx_buf)) {
252 client_free(cl);
253 return;
254 }
255 }
256
257 iov.iov_base += cl->rx_ofs;
258 iov.iov_len -= cl->rx_ofs;
259 if (iov.iov_len) {
260 len = read(fd->fd, iov.iov_base, iov.iov_len);
261 if (len <= 0)
262 return;
263
264 cl->rx_ofs += len;
265 goto retry;
266 }
267
268 client_parse_message(cl);
269 cl->rx_ofs = 0;
270 goto retry;
271 }
272
273 static void client_get_info(struct client *cl)
274 {
275 #ifdef LOCAL_PEERPID
276 socklen_t len = sizeof(&cl->pid);
277 if (getsockopt(cl->fd.fd, SOL_LOCAL, LOCAL_PEERPID, &cl->pid, &len) < 0)
278 return;
279 #elif defined(SO_PEERCRED)
280 struct ucred uc;
281 socklen_t len = sizeof(uc);
282 if (getsockopt(cl->fd.fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) < 0)
283 return;
284 cl->pid = uc.pid;
285 cl->uid = uc.uid;
286 #endif
287 }
288
289 static void client_get_procname(struct client *cl)
290 {
291 #ifdef linux
292 char buf[256];
293 FILE *f;
294
295 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", cl->pid);
296 f = fopen(buf, "r");
297 if (!f)
298 return;
299 buf[fread(buf, 1, sizeof(buf) - 1, f)] = 0;
300 fclose(f);
301 snprintf(cl->proc_name, sizeof(cl->proc_name), "%s", basename(buf));
302 #endif
303 #ifdef __APPLE__
304 proc_name(cl->pid, cl->proc_name, sizeof(cl->proc_name) - 1);
305 #endif
306 }
307
308 void client_alloc(int fd)
309 {
310 int sndbuf = UDEBUG_SNDBUF;
311 struct client *cl;
312
313 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
314
315 cl = calloc(1, sizeof(*cl));
316 INIT_LIST_HEAD(&cl->bufs);
317 cl->fd.fd = fd;
318 cl->fd.cb = client_fd_cb;
319 cl->rx_fd = -1;
320 client_get_info(cl);
321 if (cl->pid)
322 client_get_procname(cl);
323 if (!cl->proc_name[0])
324 snprintf(cl->proc_name, sizeof(cl->proc_name), "<unknown>");
325
326 DC(2, cl, "connect");
327 uloop_fd_add(&cl->fd, ULOOP_READ);
328 list_add_tail(&cl->list, &clients);
329 }