Skip to content
This repository has been archived by the owner on Jun 22, 2021. It is now read-only.

Send file descriptors of text files over libuv pipe #168

Open
manishmanu opened this issue Sep 16, 2020 · 1 comment
Open

Send file descriptors of text files over libuv pipe #168

manishmanu opened this issue Sep 16, 2020 · 1 comment

Comments

@manishmanu
Copy link

I have to send file descriptors of some shared memory buffers from one process to another. I'm able to transfer the fds directly over UNIX Domain Sockets as below:

Send FDs:

static void send_fds(int socket, int* fds, int n)  // send fd by socket
{
  struct msghdr msg = {0};
  struct cmsghdr* cmsg;
  char buf[CMSG_SPACE(n * sizeof(int))], dup[256];
  memset(buf, '\0', sizeof(buf));
  struct iovec io = {.iov_base = &dup, .iov_len = sizeof(dup)};

  msg.msg_iov = &io;
  msg.msg_iovlen = 1;
  msg.msg_control = buf;
  msg.msg_controllen = sizeof(buf);

  cmsg = CMSG_FIRSTHDR(&msg);
  cmsg->cmsg_level = SOL_SOCKET;
  cmsg->cmsg_type = SCM_RIGHTS;
  cmsg->cmsg_len = CMSG_LEN(n * sizeof(int));

  memcpy((int*)CMSG_DATA(cmsg), fds, n * sizeof(int));

  if (sendmsg(socket, &msg, 0) < 0)
    printf("Failed to send message\n");
}

Receive FDs:

static int* recv_fds(int socket, int n) {
  int* fds = (int*)malloc(n * sizeof(int));
  struct msghdr msg = {0};
  struct cmsghdr* cmsg;
  char buf[CMSG_SPACE(n * sizeof(int))], dup[256];
  memset(buf, '\0', sizeof(buf));
  struct iovec io = {.iov_base = &dup, .iov_len = sizeof(dup)};

  msg.msg_iov = &io;
  msg.msg_iovlen = 1;
  msg.msg_control = buf;
  msg.msg_controllen = sizeof(buf);

  if (recvmsg(socket, &msg, 0) < 0)
    printf("Failed to receive message\n");

  cmsg = CMSG_FIRSTHDR(&msg);

  memcpy(fds, (int*)CMSG_DATA(cmsg), n * sizeof(int));

  return fds;
}

But when i use domain sockets through abstraction libuv_pipe_t provided by libuv, I was not able to transfer the fds. Does libuv provides any way to transfer file descriptors between server pipe and client pipe ? If yes how to send and receive fds exactly ?

uv_pipe_t server:

#include <assert.h>
#include <memory.h>
#include <stdlib.h>
#include <unistd.h>

#include <uv.h>

#define SOCKET_NAME "socket_name"

void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
  static char buffer[1024];
  buf->base = buffer;
  buf->len = sizeof(buffer);
}

void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
  if (nread < 0) {
    uv_stop(stream->loop);
    uv_close((uv_handle_t*)stream, NULL);
  }
  printf("message received : %s\n", buf->base);
}

static void connection_cb(uv_stream_t* server, int status) {
  printf("new connection recevied\n");
  int r;
  uv_pipe_t connection;
  r = uv_pipe_init(server->loop, &connection, 0);
  assert(r == 0);
  r = uv_accept(server, (uv_stream_t*)&connection);
  assert(r == 0);

  r = uv_read_start((uv_stream_t*)&connection, alloc_buffer, read_cb);
  assert(r == 0);

 // send file descriptor
  int fds[2];
  fds[0] = fileno(fopen("fd_test_0.txt", "w"));
  fds[1] = fileno(fopen("fd_test_1.txt", "w"));
  
  // how to send "fds" array to client ?
}

int main() {
  uv_pipe_t p;
  int r;

  r = uv_pipe_init(uv_default_loop(), &p, 0);
  assert(r == 0);

  unlink(SOCKET_NAME);
  r = uv_pipe_bind(&p, SOCKET_NAME);
  assert(r == 0);

  r = uv_listen((uv_stream_t*)&p, 128, connection_cb);
  assert(r == 0);

  printf("listening...\n");
  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
  uv_loop_close(uv_default_loop());
}

uv_pipe_t client:

#include <assert.h>
#include <memory.h>

#include <uv.h>

#define SOCKET_NAME "socket_name"

void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
  static char buffer[1024];
  buf->base = buffer;
  buf->len = sizeof(buffer);
}

void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
   // read file descriptors
}

static void connect_cb(uv_connect_t* connect_req, int status) {
  printf("connected!");

  int r =
      uv_read_start((uv_stream_t*)connect_req->handle, alloc_buffer, read_cb);
}

int main() {
  uv_pipe_t p;
  uv_connect_t conn_req;
  int r;
  r = uv_pipe_init(uv_default_loop(), &p, 0);
  assert(r == 0);

  uv_pipe_connect(&conn_req, &p, SOCKET_NAME, connect_cb);

  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
  uv_loop_close(uv_default_loop());
}

this example shows how to transfer pipe handles over pipes but it's doesn't completely solve my problem.

Any help is appreciated !!!

@bnoordhuis
Copy link
Member

Does libuv provides any way to transfer file descriptors between server pipe and client pipe ?

Not random file descriptors, no, because of Windows. Libuv only supports what can be supported on all platforms (that's its raison d'être after all) and this is one of the things that fall in the "no can do" bucket.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants