ucode: check for errors in ftruncate()
[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 client_send_msg(cl, msg, -1);
174 break;
175 case CL_MSG_RING_REMOVE:
176 DC(2, cl, "delete ring %x", msg->id);
177 r = client_ring_get_by_id(cl, msg->id);
178 if (r)
179 client_ring_free(r);
180 else
181 DC(2, cl, "ring not found");
182 client_send_msg(cl, msg, -1);
183 break;
184 case CL_MSG_RING_NOTIFY:
185 DC(3, cl, "notify on ring %d", msg->id);
186 r = client_ring_get_by_id(cl, msg->id);
187 if (r)
188 client_msg_notify(r, msg->notify_mask);
189 else
190 DC(2, cl, "local ring %d not found", msg->id);
191 break;
192 case CL_MSG_GET_HANDLE:
193 client_msg_get_handle(cl);
194 DC(2, cl, "get notify handle: %d", cl->notify_id);
195 break;
196 case CL_MSG_RING_GET:
197 DC(2, cl, "get ring %x", msg->id);
198 client_msg_ring_get(cl, msg->id);
199 break;
200 default:
201 DC(3, cl, "Invalid message type %d", msg->type);
202 break;
203 }
204
205 if (cl->rx_fd < 0)
206 return;
207
208 close(cl->rx_fd);
209 cl->rx_fd = -1;
210 }
211
212 static void client_fd_cb(struct uloop_fd *fd, unsigned int events)
213 {
214 struct client *cl = container_of(fd, struct client, fd);
215 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = {};
216 struct iovec iov = {};
217 struct msghdr msg = {
218 .msg_iov = &iov,
219 .msg_iovlen = 1,
220 .msg_control = fd_buf,
221 .msg_controllen = sizeof(fd_buf),
222 };
223 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
224 size_t min_sz = sizeof(cl->rx_buf.msg) + sizeof(struct blob_attr);
225 ssize_t len;
226 int *pfd;
227
228 cmsg->cmsg_type = SCM_RIGHTS;
229 cmsg->cmsg_level = SOL_SOCKET;
230 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
231
232 pfd = (int *)CMSG_DATA(cmsg);
233 msg.msg_controllen = cmsg->cmsg_len;
234
235 retry:
236 *pfd = -1;
237 if (fd->eof) {
238 client_free(cl);
239 return;
240 }
241
242 iov.iov_base = &cl->rx_buf;
243 iov.iov_len = min_sz;
244 if (!cl->rx_ofs) {
245 iov.iov_base = &cl->rx_buf.msg;
246 iov.iov_len = min_sz;
247
248 len = recvmsg(fd->fd, &msg, 0);
249 if (len < 0)
250 return;
251 if (!len)
252 fd->eof = true;
253
254 cl->rx_ofs = len;
255 cl->rx_fd = *pfd;
256 goto retry;
257 } else if (cl->rx_ofs >= min_sz) {
258 iov.iov_len += blob_pad_len(&cl->rx_buf.data);
259 iov.iov_len -= sizeof(struct blob_attr);
260 if (iov.iov_len > sizeof(cl->rx_buf)) {
261 client_free(cl);
262 return;
263 }
264 }
265
266 iov.iov_base += cl->rx_ofs;
267 iov.iov_len -= cl->rx_ofs;
268 if (iov.iov_len) {
269 len = read(fd->fd, iov.iov_base, iov.iov_len);
270 if (len <= 0)
271 return;
272
273 cl->rx_ofs += len;
274 goto retry;
275 }
276
277 client_parse_message(cl);
278 cl->rx_ofs = 0;
279 goto retry;
280 }
281
282 static void client_get_info(struct client *cl)
283 {
284 #ifdef LOCAL_PEERPID
285 socklen_t len = sizeof(&cl->pid);
286 if (getsockopt(cl->fd.fd, SOL_LOCAL, LOCAL_PEERPID, &cl->pid, &len) < 0)
287 return;
288 #elif defined(SO_PEERCRED)
289 struct ucred uc;
290 socklen_t len = sizeof(uc);
291 if (getsockopt(cl->fd.fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) < 0)
292 return;
293 cl->pid = uc.pid;
294 cl->uid = uc.uid;
295 #endif
296 }
297
298 static void client_get_procname(struct client *cl)
299 {
300 #ifdef linux
301 char buf[256];
302 FILE *f;
303
304 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", cl->pid);
305 f = fopen(buf, "r");
306 if (!f)
307 return;
308 buf[fread(buf, 1, sizeof(buf) - 1, f)] = 0;
309 fclose(f);
310 snprintf(cl->proc_name, sizeof(cl->proc_name), "%s", basename(buf));
311 #endif
312 #ifdef __APPLE__
313 proc_name(cl->pid, cl->proc_name, sizeof(cl->proc_name) - 1);
314 #endif
315 }
316
317 void client_alloc(int fd)
318 {
319 int sndbuf = UDEBUG_SNDBUF;
320 struct client *cl;
321
322 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
323
324 cl = calloc(1, sizeof(*cl));
325 INIT_LIST_HEAD(&cl->bufs);
326 cl->fd.fd = fd;
327 cl->fd.cb = client_fd_cb;
328 cl->rx_fd = -1;
329 client_get_info(cl);
330 if (cl->pid)
331 client_get_procname(cl);
332 if (!cl->proc_name[0])
333 snprintf(cl->proc_name, sizeof(cl->proc_name), "<unknown>");
334
335 DC(2, cl, "connect");
336 uloop_fd_add(&cl->fd, ULOOP_READ);
337 list_add_tail(&cl->list, &clients);
338 }