{"id":1491,"url":"https://patchwork.libcamera.org/api/patches/1491/?format=json","web_url":"https://patchwork.libcamera.org/patch/1491/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20190621041519.29689-2-niklas.soderlund@ragnatech.se>","date":"2019-06-21T04:15:18","name":"[libcamera-devel,RFC,1/2] libcamera: ipc: unix: Add a IPC mechanism based on Unix sockets","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"a1def9729e1dce7bf7116e4727e974c1fe3dfac5","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/?format=json","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/1491/mbox/","series":[{"id":369,"url":"https://patchwork.libcamera.org/api/series/369/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=369","date":"2019-06-21T04:15:17","name":"libcamera: ipc: unix: Add a IPC mechanism based on Unix sockets","version":1,"mbox":"https://patchwork.libcamera.org/series/369/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/1491/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/1491/checks/","tags":{},"headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net\n\t[195.74.38.228])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id F139161583\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 21 Jun 2019 06:16:22 +0200 (CEST)","from bismarck.berto.se (unknown [145.14.112.32])\n\tby bin-vsp-out-03.atm.binero.net (Halon) with ESMTPA\n\tid 2c90f6eb-93db-11e9-8601-0050569116f7;\n\tFri, 21 Jun 2019 06:15:17 +0200 (CEST)"],"X-Halon-ID":"2c90f6eb-93db-11e9-8601-0050569116f7","Authorized-sender":"niklas@soderlund.pp.se","From":"=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","To":"libcamera-devel@lists.libcamera.org","Date":"Fri, 21 Jun 2019 06:15:18 +0200","Message-Id":"<20190621041519.29689-2-niklas.soderlund@ragnatech.se>","X-Mailer":"git-send-email 2.21.0","In-Reply-To":"<20190621041519.29689-1-niklas.soderlund@ragnatech.se>","References":"<20190621041519.29689-1-niklas.soderlund@ragnatech.se>","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [RFC 1/2] libcamera: ipc: unix: Add a IPC\n\tmechanism based on Unix sockets","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 21 Jun 2019 04:16:23 -0000"},"content":"To be able to isolate an IPA component in a separate process and IPC\nmechanism is needed to communicate with it. Add a IPC mechanism based on\nUnix sockets which allows users to pass both data and file descriptors\nto to and from the IPA process.\n\nThe implementation allows users to send both data and file descriptors\nin the same message. This allows users to more easily implement\nserialization and deserialization of objects as all elements belonging\nto an object can be sent in one message,\n\nThe implementation guarantees that a whole object is transmitted and\nreceived over IPC before it's handed of. This allows IPC users to not\nhave to deal with buffering and may depend on that it only needs to deal\nwith serialization/deserialization of complete object blobs.\n\nThe implementation also provides a priv field in the IPC message header\nwhich is a 32 bit integer that can be used by IPA implementations that\ndo not require a complex protocol header to describe what type of\nmessage is transmitted.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n---\n src/libcamera/include/ipc_unixsocket.h |  58 +++++\n src/libcamera/ipc_unixsocket.cpp       | 330 +++++++++++++++++++++++++\n src/libcamera/meson.build              |   2 +\n 3 files changed, 390 insertions(+)\n create mode 100644 src/libcamera/include/ipc_unixsocket.h\n create mode 100644 src/libcamera/ipc_unixsocket.cpp","diff":"diff --git a/src/libcamera/include/ipc_unixsocket.h b/src/libcamera/include/ipc_unixsocket.h\nnew file mode 100644\nindex 0000000000000000..864fa93b1f190fb7\n--- /dev/null\n+++ b/src/libcamera/include/ipc_unixsocket.h\n@@ -0,0 +1,58 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipc_unixsocket.h - IPC mechanism based on Unix sockets\n+ */\n+\n+#ifndef __LIBCAMERA_IPC_UNIXSOCKET_H__\n+#define __LIBCAMERA_IPC_UNIXSOCKET_H__\n+\n+#include <cstdint>\n+#include <sys/types.h>\n+#include <vector>\n+\n+namespace libcamera {\n+\n+class IPCUnixSocket\n+{\n+public:\n+\tstruct Payload {\n+\t\tuint32_t priv;\n+\t\tstd::vector<uint8_t> data;\n+\t\tstd::vector<int32_t> fds;\n+\t};\n+\n+\tIPCUnixSocket();\n+\tIPCUnixSocket(int fd);\n+\n+\tint create();\n+\tint connect();\n+\tvoid close();\n+\n+\tint send(const Payload &payload);\n+\tint recv(Payload *payload, int timeout);\n+\tint call(const Payload &payload, Payload *response, int timeout);\n+\n+private:\n+\tstruct Header {\n+\t\tuint32_t priv;\n+\t\tuint32_t data;\n+\t\tuint8_t fds;\n+\t};\n+\n+\tint poll(int timeout);\n+\n+\tint sendData(const void *buffer, ssize_t length);\n+\tint recvData(void *buffer, ssize_t length, int timeout);\n+\n+\tint sendFds(const int32_t *fds, unsigned int num);\n+\tint recvFds(int32_t *fds, unsigned int num, int timeout);\n+\n+\tint fd_;\n+\tbool master_;\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_IPC_UNIXSOCKET_H__ */\ndiff --git a/src/libcamera/ipc_unixsocket.cpp b/src/libcamera/ipc_unixsocket.cpp\nnew file mode 100644\nindex 0000000000000000..b34fa0317a18b37c\n--- /dev/null\n+++ b/src/libcamera/ipc_unixsocket.cpp\n@@ -0,0 +1,330 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipc_unixsocket.cpp - IPC mechanism based on Unix sockets\n+ */\n+\n+#include \"ipc_unixsocket.h\"\n+\n+#include <errno.h>\n+#include <poll.h>\n+#include <stdio.h>\n+#include <string.h>\n+#include <sys/socket.h>\n+#include <unistd.h>\n+\n+#include \"log.h\"\n+\n+/**\n+ * \\file ipc_unixsocket.h\n+ * \\brief IPC mechanism based on Unix sockets\n+ */\n+\n+/*\n+ * Markers to use in IPC protocol, there is no specific meaning to the values,\n+ * but they should be unique.\n+ */\n+#define CMD_PING 0x1f\n+#define CMD_PONG 0xf1\n+#define CMD_FD 0x77\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(IPCUnixSocket)\n+\n+IPCUnixSocket::IPCUnixSocket()\n+\t: fd_(-1), master_(false)\n+{\n+}\n+\n+IPCUnixSocket::IPCUnixSocket(int fd)\n+\t: fd_(fd), master_(false)\n+{\n+}\n+\n+int IPCUnixSocket::create()\n+{\n+\tint sockets[2];\n+\tint ret;\n+\n+\tret = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);\n+\tif (ret) {\n+\t\tret = -errno;\n+\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t<< \"Failed to create socket pair: \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tfd_ = sockets[0];\n+\tmaster_ = true;\n+\n+\treturn sockets[1];\n+}\n+\n+int IPCUnixSocket::connect()\n+{\n+\tPayload payload = {};\n+\tPayload response = {};\n+\n+\tif (master_) {\n+\t\tpayload.data.push_back(CMD_PING);\n+\n+\t\tif (call(payload, &response, 500))\n+\t\t\treturn -1;\n+\n+\t\tif (response.data[0] != CMD_PONG)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tif (recv(&payload, 500))\n+\t\t\treturn -1;\n+\n+\t\tif (payload.data[0] != CMD_PING)\n+\t\t\treturn -1;\n+\n+\t\tresponse.data.push_back(CMD_PONG);\n+\n+\t\tif (send(response))\n+\t\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void IPCUnixSocket::close()\n+{\n+\tif (fd_ == -1)\n+\t\treturn;\n+\n+\t::close(fd_);\n+\n+\tfd_ = -1;\n+}\n+\n+int IPCUnixSocket::send(const Payload &payload)\n+{\n+\tHeader hdr;\n+\tint ret;\n+\n+\tif (fd_ < 0)\n+\t\treturn -ENOTCONN;\n+\n+\thdr.priv = payload.priv;\n+\thdr.data = payload.data.size();\n+\thdr.fds = payload.fds.size();\n+\n+\tret = sendData(&hdr, sizeof(hdr));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (hdr.data) {\n+\t\tret = sendData(payload.data.data(), hdr.data);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (hdr.fds) {\n+\t\tret = sendFds(payload.fds.data(), hdr.fds);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int IPCUnixSocket::recv(Payload *payload, int timeout)\n+{\n+\tHeader hdr;\n+\tint ret;\n+\n+\tif (fd_ < 0)\n+\t\treturn -ENOTCONN;\n+\n+\tret = recvData(&hdr, sizeof(hdr), timeout);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tpayload->priv = hdr.priv;\n+\tpayload->data.resize(hdr.data);\n+\tpayload->fds.resize(hdr.fds);\n+\n+\tif (hdr.data) {\n+\t\tret = recvData(payload->data.data(), hdr.data, timeout);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tif (hdr.fds) {\n+\t\tret = recvFds(payload->fds.data(), hdr.fds, timeout);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int IPCUnixSocket::call(const Payload &payload, Payload *response, int timeout)\n+{\n+\tint ret = send(payload);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn recv(response, timeout);\n+}\n+\n+int IPCUnixSocket::poll(int timeout)\n+{\n+\tstruct pollfd pollfd = { fd_, POLLIN, 0 };\n+\n+\tint ret = ::poll(&pollfd, 1, timeout);\n+\tif (ret < 0) {\n+\t\tret = -errno;\n+\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t<< \"Failed to poll: \" << strerror(-ret);\n+\t\treturn ret;\n+\t} else if (ret == 0) {\n+\t\treturn -ETIMEDOUT;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int IPCUnixSocket::sendData(const void *buffer, ssize_t length)\n+{\n+\tssize_t len, sent;\n+\tconst uint8_t *pos;\n+\n+\tif (fd_ < 0)\n+\t\treturn -ENOTCONN;\n+\n+\tpos = static_cast<const uint8_t *>(buffer);\n+\tlen = length;\n+\n+\twhile (len) {\n+\t\tsent = ::send(fd_, pos, len, 0);\n+\t\tif (sent < 0) {\n+\t\t\tsent = -errno;\n+\t\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t\t<< \"Failed to send: \" << strerror(-sent);\n+\t\t\treturn sent;\n+\t\t}\n+\n+\t\tpos += sent;\n+\t\tlen -= sent;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int IPCUnixSocket::recvData(void *buffer, ssize_t length, int timeout)\n+{\n+\tssize_t len, recived;\n+\tuint8_t *pos;\n+\n+\tif (fd_ < 0)\n+\t\treturn -ENOTCONN;\n+\n+\tpos = static_cast<uint8_t *>(buffer);\n+\tlen = length;\n+\n+\twhile (len) {\n+\t\tint ret = poll(timeout);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\trecived = ::recv(fd_, pos, len, 0);\n+\t\tif (recived < 0) {\n+\t\t\trecived = -errno;\n+\t\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t\t<< \"Failed to recv: \" << strerror(-recived);\n+\t\t\treturn recived;\n+\t\t}\n+\n+\t\tpos += recived;\n+\t\tlen -= recived;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int IPCUnixSocket::sendFds(const int32_t *fds, unsigned int num)\n+{\n+\tchar cmd = CMD_FD;\n+\tstruct iovec iov[1];\n+\tiov[0].iov_base = &cmd;\n+\tiov[0].iov_len = sizeof(cmd);\n+\n+\tchar buf[CMSG_SPACE(num * sizeof(uint32_t))];\n+\tmemset(buf, 0, sizeof(buf));\n+\n+\tstruct cmsghdr *cmsg = (struct cmsghdr *)buf;\n+\tcmsg->cmsg_len = CMSG_LEN(num * sizeof(uint32_t));\n+\tcmsg->cmsg_level = SOL_SOCKET;\n+\tcmsg->cmsg_type = SCM_RIGHTS;\n+\n+\tstruct msghdr msg;\n+\tmsg.msg_name = nullptr;\n+\tmsg.msg_namelen = 0;\n+\tmsg.msg_iov = iov;\n+\tmsg.msg_iovlen = 1;\n+\tmsg.msg_control = cmsg;\n+\tmsg.msg_controllen = cmsg->cmsg_len;\n+\tmsg.msg_flags = 0;\n+\tmemcpy(CMSG_DATA(cmsg), fds, num * sizeof(uint32_t));\n+\n+\tif (sendmsg(fd_, &msg, 0) < 0) {\n+\t\tint ret = -errno;\n+\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t<< \"Failed to sendmsg: \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int IPCUnixSocket::recvFds(int32_t *fds, unsigned int num, int timeout)\n+{\n+\tchar cmd;\n+\tstruct iovec iov[1];\n+\tiov[0].iov_base = &cmd;\n+\tiov[0].iov_len = sizeof(cmd);\n+\n+\tchar buf[CMSG_SPACE(num * sizeof(uint32_t))];\n+\tmemset(buf, 0, sizeof(buf));\n+\n+\tstruct cmsghdr *cmsg = (struct cmsghdr *)buf;\n+\tcmsg->cmsg_len = CMSG_LEN(num * sizeof(uint32_t));\n+\tcmsg->cmsg_level = SOL_SOCKET;\n+\tcmsg->cmsg_type = SCM_RIGHTS;\n+\n+\tstruct msghdr msg;\n+\tmsg.msg_name = nullptr;\n+\tmsg.msg_namelen = 0;\n+\tmsg.msg_iov = iov;\n+\tmsg.msg_iovlen = 1;\n+\tmsg.msg_control = cmsg;\n+\tmsg.msg_controllen = cmsg->cmsg_len;\n+\tmsg.msg_flags = 0;\n+\n+\tint ret = poll(timeout);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (recvmsg(fd_, &msg, 0) < 0) {\n+\t\tint ret = -errno;\n+\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t<< \"Failed to recvmsg: \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (cmd != CMD_FD) {\n+\t\tLOG(IPCUnixSocket, Error) << \"FD marker wrong\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tmemcpy(fds, CMSG_DATA(cmsg), num * sizeof(uint32_t));\n+\n+\treturn 0;\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex f26ad5b2dc57c014..1158825fa5b0702d 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -13,6 +13,7 @@ libcamera_sources = files([\n     'ipa_interface.cpp',\n     'ipa_manager.cpp',\n     'ipa_module.cpp',\n+    'ipc_unixsocket.cpp',\n     'log.cpp',\n     'media_device.cpp',\n     'media_object.cpp',\n@@ -37,6 +38,7 @@ libcamera_headers = files([\n     'include/formats.h',\n     'include/ipa_manager.h',\n     'include/ipa_module.h',\n+    'include/ipc_unixsocket.h',\n     'include/log.h',\n     'include/media_device.h',\n     'include/media_object.h',\n","prefixes":["libcamera-devel","RFC","1/2"]}