93f31954f321f28eee09b8ee941991b6fe807d8e
[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 if (cl->rx_fd < 0)
204 return;
205
206 close(cl->rx_fd);
207 cl->rx_fd = -1;
208 }
209
210 static void client_fd_cb(struct uloop_fd *fd, unsigned int events)
211 {
212 struct client *cl = container_of(fd, struct client, fd);
213 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = {};
214 struct iovec iov = {};
215 struct msghdr msg = {
216 .msg_iov = &iov,
217 .msg_iovlen = 1,
218 .msg_control = fd_buf,
219 .msg_controllen = sizeof(fd_buf),
220 };
221 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
222 size_t min_sz = sizeof(cl->rx_buf.msg) + sizeof(struct blob_attr);
223 ssize_t len;
224 int *pfd;
225
226 cmsg->cmsg_type = SCM_RIGHTS;
227 cmsg->cmsg_level = SOL_SOCKET;
228 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
229
230 pfd = (int *)CMSG_DATA(cmsg);
231 msg.msg_controllen = cmsg->cmsg_len;
232
233 retry:
234 *pfd = -1;
235 if (fd->eof) {
236 client_free(cl);
237 return;
238 }
239
240 iov.iov_base = &cl->rx_buf;
241 iov.iov_len = min_sz;
242 if (!cl->rx_ofs) {
243 iov.iov_base = &cl->rx_buf.msg;
244 iov.iov_len = min_sz;
245
246 len = recvmsg(fd->fd, &msg, 0);
247 if (len < 0)
248 return;
249 if (!len)
250 fd->eof = true;
251
252 cl->rx_ofs = len;
253 cl->rx_fd = *pfd;
254 goto retry;
255 } else if (cl->rx_ofs >= min_sz) {
256 iov.iov_len += blob_pad_len(&cl->rx_buf.data);
257 iov.iov_len -= sizeof(struct blob_attr);
258 if (iov.iov_len > sizeof(cl->rx_buf)) {
259 client_free(cl);
260 return;
261 }
262 }
263
264 iov.iov_base += cl->rx_ofs;
265 iov.iov_len -= cl->rx_ofs;
266 if (iov.iov_len) {
267 len = read(fd->fd, iov.iov_base, iov.iov_len);
268 if (len <= 0)
269 return;
270
271 cl->rx_ofs += len;
272 goto retry;
273 }
274
275 client_parse_message(cl);
276 cl->rx_ofs = 0;
277 goto retry;
278 }
279
280 static void client_get_info(struct client *cl)
281 {
282 #ifdef LOCAL_PEERPID
283 socklen_t len = sizeof(&cl->pid);
284 if (getsockopt(cl->fd.fd, SOL_LOCAL, LOCAL_PEERPID, &cl->pid, &len) < 0)
285 return;
286 #elif defined(SO_PEERCRED)
287 struct ucred uc;
288 socklen_t len = sizeof(uc);
289 if (getsockopt(cl->fd.fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) < 0)
290 return;
291 cl->pid = uc.pid;
292 cl->uid = uc.uid;
293 #endif
294 }
295
296 static void client_get_procname(struct client *cl)
297 {
298 #ifdef linux
299 char buf[256];
300 FILE *f;
301
302 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", cl->pid);
303 f = fopen(buf, "r");
304 if (!f)
305 return;
306 buf[fread(buf, 1, sizeof(buf) - 1, f)] = 0;
307 fclose(f);
308 snprintf(cl->proc_name, sizeof(cl->proc_name), "%s", basename(buf));
309 #endif
310 #ifdef __APPLE__
311 proc_name(cl->pid, cl->proc_name, sizeof(cl->proc_name) - 1);
312 #endif
313 }
314
315 void client_alloc(int fd)
316 {
317 int sndbuf = UDEBUG_SNDBUF;
318 struct client *cl;
319
320 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
321
322 cl = calloc(1, sizeof(*cl));
323 INIT_LIST_HEAD(&cl->bufs);
324 cl->fd.fd = fd;
325 cl->fd.cb = client_fd_cb;
326 cl->rx_fd = -1;
327 client_get_info(cl);
328 if (cl->pid)
329 client_get_procname(cl);
330 if (!cl->proc_name[0])
331 snprintf(cl->proc_name, sizeof(cl->proc_name), "<unknown>");
332
333 DC(2, cl, "connect");
334 uloop_fd_add(&cl->fd, ULOOP_READ);
335 list_add_tail(&cl->list, &clients);
336 }