|
22 | 22 | #include <stdlib.h> |
23 | 23 | #include <sys/socket.h> |
24 | 24 | #include <sys/stat.h> |
| 25 | +#include <sys/uio.h> |
25 | 26 | #include <unistd.h> |
26 | 27 |
|
27 | 28 | LOG_TYPE("api"); |
@@ -249,6 +250,50 @@ void api_send(struct api_ctx *ctx, uint32_t len, const void *payload) { |
249 | 250 | LOG(ERR, "pid=%d cannot write payload", ctx->pid); |
250 | 251 | } |
251 | 252 |
|
| 253 | +// Send a response header + optional payload together with a file |
| 254 | +// descriptor via sendmsg(SCM_RIGHTS). Closes the fd after sending. |
| 255 | +static void |
| 256 | +send_response_with_fd(struct bufferevent *bev, struct gr_api_response *resp, struct api_out *out) { |
| 257 | + bufferevent_flush(bev, EV_WRITE, BEV_FLUSH); |
| 258 | + |
| 259 | + struct iovec iov[2]; |
| 260 | + int iovlen = 1; |
| 261 | + iov[0].iov_base = resp; |
| 262 | + iov[0].iov_len = sizeof(*resp); |
| 263 | + if (out->len > 0 && out->payload != NULL) { |
| 264 | + iov[1].iov_base = out->payload; |
| 265 | + iov[1].iov_len = out->len; |
| 266 | + iovlen = 2; |
| 267 | + } |
| 268 | + |
| 269 | + union { |
| 270 | + char buf[CMSG_SPACE(sizeof(int))]; |
| 271 | + struct cmsghdr align; |
| 272 | + } cmsg_buf; |
| 273 | + memset(&cmsg_buf, 0, sizeof(cmsg_buf)); |
| 274 | + |
| 275 | + struct msghdr msg = { |
| 276 | + .msg_iov = iov, |
| 277 | + .msg_iovlen = iovlen, |
| 278 | + .msg_control = cmsg_buf.buf, |
| 279 | + .msg_controllen = sizeof(cmsg_buf.buf), |
| 280 | + }; |
| 281 | + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); |
| 282 | + cmsg->cmsg_level = SOL_SOCKET; |
| 283 | + cmsg->cmsg_type = SCM_RIGHTS; |
| 284 | + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
| 285 | + memcpy(CMSG_DATA(cmsg), &out->fd, sizeof(int)); |
| 286 | + |
| 287 | + ssize_t ret; |
| 288 | + do { |
| 289 | + ret = sendmsg(bufferevent_getfd(bev), &msg, MSG_NOSIGNAL); |
| 290 | + } while (ret < 0 && errno == EINTR); |
| 291 | + if (ret < 0) |
| 292 | + LOG(ERR, "sendmsg with fd: %s", strerror(errno)); |
| 293 | + close(out->fd); |
| 294 | + out->fd = -1; |
| 295 | +} |
| 296 | + |
252 | 297 | static void read_cb(struct bufferevent *bev, void *priv) { |
253 | 298 | struct evbuffer *input = bufferevent_get_input(bev); |
254 | 299 | struct api_ctx *ctx = priv; |
@@ -294,7 +339,7 @@ static void read_cb(struct bufferevent *bev, void *priv) { |
294 | 339 | // Reset state for next request |
295 | 340 | ctx->header_complete = false; |
296 | 341 |
|
297 | | - struct api_out out; |
| 342 | + struct api_out out = {.fd = -1}; |
298 | 343 |
|
299 | 344 | // We have a complete request, process it |
300 | 345 | const struct api_handler *handler = lookup_api_handler(ctx->header.type); |
@@ -333,16 +378,19 @@ static void read_cb(struct bufferevent *bev, void *priv) { |
333 | 378 | .payload_len = out.len, |
334 | 379 | }; |
335 | 380 |
|
336 | | - if (bufferevent_write(bev, &resp, sizeof(resp)) < 0) |
337 | | - LOG(ERR, "failed to write header"); |
338 | | - if (out.len > 0) { |
339 | | - assert(out.payload != NULL); |
340 | | - if (bufferevent_write(bev, out.payload, out.len) < 0) |
341 | | - LOG(ERR, "failed to write payload"); |
| 381 | + if (out.fd >= 0) { |
| 382 | + send_response_with_fd(bev, &resp, &out); |
| 383 | + } else { |
| 384 | + if (bufferevent_write(bev, &resp, sizeof(resp)) < 0) |
| 385 | + LOG(ERR, "failed to write header"); |
| 386 | + if (out.len > 0) { |
| 387 | + assert(out.payload != NULL); |
| 388 | + if (bufferevent_write(bev, out.payload, out.len) < 0) |
| 389 | + LOG(ERR, "failed to write payload"); |
| 390 | + } |
| 391 | + bufferevent_flush(bev, EV_WRITE, BEV_FLUSH); |
342 | 392 | } |
343 | 393 |
|
344 | | - bufferevent_flush(bev, EV_WRITE, BEV_FLUSH); |
345 | | - |
346 | 394 | free(req_payload); |
347 | 395 | free(out.payload); |
348 | 396 |
|
|
0 commit comments