sensord: ipc: add socket class 81/123081/5
authorkibak.yoon <kibak.yoon@samsung.com>
Tue, 4 Apr 2017 09:43:36 +0000 (18:43 +0900)
committerkibak.yoon <kibak.yoon@samsung.com>
Wed, 5 Apr 2017 00:52:28 +0000 (09:52 +0900)
- socket class is a wrapper class of unix domain socket
- supported socket types : stream_socket, seqpacket_socket

- TC : $ sensorctl test auto ipc_socket
  - [PASS] sensor_ipc_socket.socket_p_0
  - [PASS] sensor_ipc_socket.socket_p_10
  - [PASS] sensor_ipc_socket.socket_p_1000

Change-Id: I2cb7fcb360d4a9a0c87c77d3e2b39c2bb0c06380
Signed-off-by: kibak.yoon <kibak.yoon@samsung.com>
src/sensorctl/testcase/unit_socket.cpp [new file with mode: 0644]
src/shared/seqpacket_socket.cpp [new file with mode: 0644]
src/shared/seqpacket_socket.h [new file with mode: 0644]
src/shared/socket.cpp [new file with mode: 0644]
src/shared/socket.h [new file with mode: 0644]
src/shared/stream_socket.cpp [new file with mode: 0644]
src/shared/stream_socket.h [new file with mode: 0644]

diff --git a/src/sensorctl/testcase/unit_socket.cpp b/src/sensorctl/testcase/unit_socket.cpp
new file mode 100644 (file)
index 0000000..3b15b5a
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * sensorctl
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sensor_internal.h>
+
+#include "shared/socket.h"
+#include "shared/stream_socket.h"
+
+#include "log.h"
+#include "test_bench.h"
+
+using namespace ipc;
+
+#define MAX_BUF_SIZE 4096
+#define TEST_PATH "/run/.sensord_test.socket"
+
+typedef bool (*process_func_t)(const char *msg, int size, int count);
+
+static pid_t run_process(process_func_t func, const char *msg, int size, int count)
+{
+       pid_t pid = fork();
+       if (pid < 0)
+               return -1;
+
+       if (pid == 0) {
+               if (!func(msg, size, count))
+                       _E("Failed to run process\n");
+               exit(0);
+       }
+
+       return pid;
+}
+
+/* Socket */
+static bool run_socket_echo_server(const char *msg, int size, int count)
+{
+       char buf[MAX_BUF_SIZE] = {0, };
+       bool ret = false;
+       int send_size = 0;
+       int recv_size = 0;
+       int recv_count = 0;
+       int send_count = 0;
+       int total_recv_size = 0;
+       int total_send_size = 0;
+       stream_socket accept_sock;
+       stream_socket client_sock;
+
+       accept_sock.create(TEST_PATH);
+       accept_sock.set_blocking_mode(true);
+       accept_sock.bind();
+       accept_sock.listen(10);
+       accept_sock.accept(client_sock);
+
+       /* receive message */
+       while (recv_count++ < count) {
+               recv_size = client_sock.recv(&buf, size);
+               total_recv_size += recv_size;
+       }
+       ASSERT_EQ(total_recv_size, (size * count));
+
+       usleep(10000);
+       /* echo message */
+       while (send_count++ < count) {
+               send_size = client_sock.send(buf, size);
+               total_send_size += send_size;
+       }
+       ASSERT_EQ(total_send_size, (size * count));
+
+       ret = strncmp(buf, msg, size);
+       ASSERT_EQ(ret, 0);
+
+       accept_sock.close();
+       client_sock.close();
+
+       return true;
+}
+
+static bool run_socket_client(const char *msg, int size, int count)
+{
+       char buf[MAX_BUF_SIZE] = {0, };
+       bool ret = false;
+       int send_size = 0;
+       int recv_size = 0;
+       int send_count = 0;
+       int recv_count = 0;
+       int total_recv_size = 0;
+       int total_send_size = 0;
+       stream_socket sock;
+
+       usleep(100000);
+
+       sock.create(TEST_PATH);
+       sock.set_blocking_mode(true);
+       sock.connect();
+
+       while (send_count++ < count) {
+               send_size = sock.send(msg, size);
+               total_send_size += send_size;
+       }
+
+       ASSERT_EQ(total_send_size, (size * count));
+
+       while (recv_count++ < count) {
+               recv_size = sock.recv(&buf, size);
+               total_recv_size += recv_size;
+       }
+
+       ASSERT_EQ(total_recv_size, (size * count));
+
+       ret = strncmp(buf, msg, size);
+       ASSERT_EQ(ret, 0);
+
+       sock.close();
+
+       usleep(100000);
+
+       return true;
+}
+
+/**
+ * @brief   Test socket class with simple message
+ * @details 1. connect socket and listen event by socket
+ *          2. send/recv "TEST" text by using echo server
+ *          3. check "TEST" message
+ * @remarks we can test only regular socket, not systemd-based socket.
+ */
+TESTCASE(sensor_ipc_socket, socket_p_0)
+{
+       const char *msg = "TEST";
+       int size = 4;
+       int count = 1;
+
+       pid_t pid = run_process(run_socket_echo_server, msg, size, count);
+       EXPECT_GE(pid, 0);
+
+       bool ret = run_socket_client(msg, size, count);
+       ASSERT_TRUE(ret);
+
+       return true;
+}
+
+/**
+ * @brief   Test socket class with 40K message
+ * @details 1. connect socket and listen event by socket
+ *          2. send/recv 40960 bytes(4096 bytes * 10) by using echo server
+ *          3. check total size
+ * @remarks we can test only regular socket, not systemd-based socket.
+ */
+TESTCASE(sensor_ipc_socket, socket_p_10)
+{
+       const char msg[MAX_BUF_SIZE] = {1, };
+       int size = MAX_BUF_SIZE;
+       int count = 10;
+
+       pid_t pid = run_process(run_socket_echo_server, msg, size, count);
+       EXPECT_GE(pid, 0);
+
+       bool ret = run_socket_client(msg, size, count);
+       ASSERT_TRUE(ret);
+
+       return true;
+}
+
+/**
+ * @brief   Test socket class with 4M message
+ * @details 1. connect socket and listen event by socket
+ *          2. send/recv 4096000 bytes(4096 bytes * 1000) by using echo server
+ *          3. check total size
+ * @remarks we can test only regular socket, not systemd-based socket.
+ */
+TESTCASE(sensor_ipc_socket, socket_p_1000)
+{
+       const char msg[MAX_BUF_SIZE] = {1, };
+       int size = MAX_BUF_SIZE;
+       int count = 1000;
+
+       pid_t pid = run_process(run_socket_echo_server, msg, size, count);
+       EXPECT_GE(pid, 0);
+
+       bool ret = run_socket_client(msg, size, count);
+       ASSERT_TRUE(ret);
+
+       return true;
+}
\ No newline at end of file
diff --git a/src/shared/seqpacket_socket.cpp b/src/shared/seqpacket_socket.cpp
new file mode 100644 (file)
index 0000000..ef6e01b
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "seqpacket_socket.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "sensor_log.h"
+
+using namespace ipc;
+
+seqpacket_socket::seqpacket_socket()
+: socket()
+{
+}
+
+seqpacket_socket::~seqpacket_socket()
+{
+}
+
+bool seqpacket_socket::create(const std::string &path)
+{
+       return socket::create_by_type(path, SOCK_SEQPACKET);
+}
+
+ssize_t seqpacket_socket::on_send(const void *buffer, size_t size) const
+{
+       ssize_t err, len;
+
+       do {
+               len = ::send(socket::get_fd(),
+                               reinterpret_cast<const char *>(buffer),
+                               size,
+                               socket::get_mode());
+
+               err = len < 0 ? errno : 0;
+       } while (err == EINTR);
+
+       if (err) {
+               _ERRNO(errno, _E, "Failed to send(%d, %#x, %d) = %d",
+                       socket::get_fd(), buffer, size, len);
+       }
+
+       return err == 0 ? len : -err;
+}
+
+ssize_t seqpacket_socket::on_recv(void *buffer, size_t size) const
+{
+       ssize_t err, len;
+
+       do {
+               len = ::recv(socket::get_fd(),
+                               reinterpret_cast<char *>(buffer),
+                               size,
+                               socket::get_mode());
+
+               if (len > 0) {
+                       err = 0;
+               } else if (len == 0) {
+                       _E("Failed to recv(%d, %#p , %d) = %d, because the peer performed shutdown!",
+                               socket::get_fd(), buffer, size, len);
+                       err = 1;
+               } else {
+                       err = errno;
+               }
+       } while (err == EINTR);
+
+       if ((err == EAGAIN) || (err == EWOULDBLOCK))
+               return 0;
+
+       if (err) {
+               _ERRNO(errno, _E, "Failed to recv(%d, %#x, %d) = %d",
+                       socket::get_fd(), buffer, size, len);
+       }
+
+       return err == 0 ? len : -err;
+}
+
diff --git a/src/shared/seqpacket_socket.h b/src/shared/seqpacket_socket.h
new file mode 100644 (file)
index 0000000..5614903
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __SEQPACKET_SOCKET_H__
+#define __SEQPACKET_SOCKET_H__
+
+#include "socket.h"
+
+namespace ipc {
+
+class seqpacket_socket : public socket {
+public:
+       seqpacket_socket();
+       ~seqpacket_socket();
+
+       bool create(const std::string &path);
+
+private:
+       ssize_t on_send(const void *buffer, size_t size) const;
+       ssize_t on_recv(void *buffer, size_t size) const;
+
+};
+
+}
+
+#endif /* __SEQPACKET_SOCKET_H__ */
diff --git a/src/shared/socket.cpp b/src/shared/socket.cpp
new file mode 100644 (file)
index 0000000..239e554
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "socket.h"
+
+#include <fcntl.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <systemd/sd-daemon.h>
+
+#include "sensor_log.h"
+
+using namespace ipc;
+
+static bool set_close_on_exec(int fd)
+{
+       if (::fcntl(fd, F_SETFL, FD_CLOEXEC) == -1)
+               return false;
+
+       return true;
+}
+
+static int create_systemd_socket(const std::string &path, int type)
+{
+       int n;
+       int listening;
+
+       listening = (type == SOCK_STREAM) ? 1 : -1;
+
+       n = sd_listen_fds(0);
+       retvm_if(n < 0, -EPERM, "Failed to listen fds from systemd");
+
+       for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) {
+               if (sd_is_socket_unix(fd, type, listening, path.c_str(), 0) > 0) {
+                       set_close_on_exec(fd);
+                       return fd;
+               }
+       }
+
+       return -EPERM;
+}
+
+static int create_unix_socket(int type)
+{
+       int sock_fd = ::socket(AF_UNIX, type, 0);
+
+       if (sock_fd < 0) {
+               _ERRNO(errno, _E, "Failed to create socket");
+               return -EPERM;
+       }
+
+       set_close_on_exec(sock_fd);
+
+       int optval = 1;
+       if (::setsockopt(sock_fd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) < 0) {
+               _ERRNO(errno, _E, "Failed to create socket[%d]", sock_fd);
+               ::close(sock_fd);
+               return -EPERM;
+       }
+
+       return sock_fd;
+}
+
+static bool select_fds(int fd, fd_set *read_fds, fd_set *write_fds, const int timeout)
+{
+       struct timeval tv;
+       int err;
+
+       tv.tv_sec = timeout;
+       tv.tv_usec = 0;
+
+       while (true) {
+               err = ::select(fd + 1, read_fds, write_fds, NULL, &tv);
+               if (err <= 0)
+                       return false;
+
+               if (read_fds && FD_ISSET(fd, read_fds))
+                       break;
+               if (write_fds && FD_ISSET(fd, write_fds))
+                       break;
+       }
+
+       return true;
+}
+
+socket::socket()
+: m_sock_fd(-1)
+, m_mode(MSG_DONTWAIT | MSG_NOSIGNAL)
+, m_listening(false)
+{
+}
+
+socket::socket(int sock_fd)
+: m_sock_fd(sock_fd)
+, m_mode(MSG_DONTWAIT | MSG_NOSIGNAL)
+, m_listening(false)
+{
+}
+
+socket::socket(const socket &sock)
+: m_sock_fd(-1)
+, m_mode(MSG_DONTWAIT | MSG_NOSIGNAL)
+, m_listening(false)
+{
+       if (this == &sock)
+               return;
+
+       m_sock_fd = sock.m_sock_fd;
+       m_mode = sock.m_mode;
+       m_listening.store(sock.m_listening);
+}
+
+socket::~socket()
+{
+       close();
+}
+
+bool socket::connect(void)
+{
+       const int TIMEOUT = 3;
+       sockaddr_un addr;
+       fd_set write_fds;
+       FD_ZERO(&write_fds);
+       FD_SET(m_sock_fd, &write_fds);
+
+       retvm_if(m_path.size() >= sizeof(sockaddr_un::sun_path), false,
+                       "Failed to create socket[%s]", m_path.c_str());
+
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, m_path.c_str(), sizeof(sockaddr_un::sun_path));
+       addr.sun_path[m_path.size()] = '\0';
+
+       if (::connect(m_sock_fd,
+                               reinterpret_cast<struct sockaddr *>(&addr),
+                               sizeof(struct sockaddr_un)) < 0) {
+               _ERRNO(errno, _E, "Failed to connect() for socket[%d]", m_sock_fd);
+               close();
+               return false;
+       }
+
+       if (!select_fds(m_sock_fd, NULL, &write_fds, TIMEOUT)) {
+               _E("Failed to select for socket[%d]", m_sock_fd);
+               close();
+               return false;
+       }
+
+       if (!has_connected()) {
+               close();
+               return false;
+       }
+
+       _D("Connected[%d]", m_sock_fd);
+
+       return true;
+}
+
+bool socket::bind(void)
+{
+       sockaddr_un addr;
+       int file_mode;
+
+       retvm_if(m_path.size() >= sizeof(sockaddr_un::sun_path), false,
+                       "Failed to create socket[%s]", m_path.c_str());
+       retv_if(m_listening.load(), true);
+
+       if (!access(m_path.c_str(), F_OK))
+               unlink(m_path.c_str());
+
+       addr.sun_family = AF_UNIX;
+       ::strncpy(addr.sun_path, m_path.c_str(), sizeof(sockaddr_un::sun_path));
+       addr.sun_path[m_path.size()] = '\0';
+
+       if (::bind(m_sock_fd,
+                               reinterpret_cast<struct sockaddr *>(&addr),
+                               sizeof(struct sockaddr_un)) < 0) {
+               _ERRNO(errno, _E, "Failed to bind for socket[%d]", m_sock_fd);
+               close();
+               return false;
+       }
+
+       /* TODO: Is this really necessary? */
+       file_mode = (S_IRWXU | S_IRWXG | S_IRWXO);
+       if (chmod(m_path.c_str(), file_mode) < 0) {
+               _ERRNO(errno, _E, "Failed to create socket[%d]", m_sock_fd);
+               close();
+               return false;
+       }
+
+       _D("Bound to path[%d, %s]", m_sock_fd, m_path.c_str());
+
+       return true;
+}
+
+bool socket::listen(const int max_connections)
+{
+       retv_if(m_listening.load(), true);
+
+       if (::listen(m_sock_fd, max_connections) < 0) {
+               _ERRNO(errno, _E, "Failed to listen() for socket[%d]", m_sock_fd);
+               close();
+               return false;
+       }
+
+       m_listening.store(true);
+
+       _D("Listened[%d]", m_sock_fd);
+
+       return true;
+}
+
+bool socket::accept(socket &client_sock)
+{
+       int fd;
+       fd_set read_fds;
+       FD_ZERO(&read_fds);
+       FD_SET(m_sock_fd, &read_fds);
+
+       fd = ::accept(m_sock_fd, NULL, NULL);
+
+       if (fd < 0) {
+               _ERRNO(errno, _E, "Failed to accept[%d]", m_sock_fd);
+               return false;
+       }
+
+       set_close_on_exec(fd);
+       client_sock.set_fd(fd);
+       /* TODO : socket type should be adjusted here */
+
+       _D("Accepted[%d, %d]", m_sock_fd, fd);
+
+       return true;
+}
+
+bool socket::close(void)
+{
+       retv_if(m_sock_fd < 0, false);
+
+       if (::close(m_sock_fd) < 0) {
+               _ERRNO(errno, _E, "Failed to close socket[%d]", m_sock_fd);
+               return false;
+       }
+
+       _D("Closed[%d]", m_sock_fd);
+
+       m_sock_fd = -1;
+       m_listening.store(false);
+
+       return true;
+}
+
+int socket::get_fd(void) const
+{
+       return m_sock_fd;
+}
+
+void socket::set_fd(int sock_fd)
+{
+       m_sock_fd = sock_fd;
+}
+
+int socket::get_mode(void) const
+{
+       return m_mode;
+}
+
+bool socket::set_mode(int mode)
+{
+       /* TODO : implement send/recv message mode */
+       return true;
+}
+
+bool socket::create(const std::string &path)
+{
+       return false;
+}
+
+ssize_t socket::send(const void *buffer, size_t size, bool select) const
+{
+       if (select) {
+               const int TIMEOUT = 1;
+               fd_set write_fds;
+               FD_ZERO(&write_fds);
+               FD_SET(m_sock_fd, &write_fds);
+
+               if (!select_fds(m_sock_fd, NULL, &write_fds, TIMEOUT)) {
+                       _E("Failed to send message(timeout)");
+                       return 0;
+               }
+       }
+
+       return on_send(buffer, size);
+}
+
+ssize_t socket::recv(void* buffer, size_t size, bool select) const
+{
+       /* WARNING: if select() is called here, it affects performance */
+       return on_recv(buffer, size);
+}
+
+bool socket::create_by_type(const std::string &path, int type)
+{
+       m_sock_fd = ::create_systemd_socket(path, type);
+       if (m_sock_fd < 0) {
+               _D("Creating the UDS instead of systemd socket..");
+               m_sock_fd = create_unix_socket(type);
+       } else {
+               m_listening.store(true);
+       }
+
+       retvm_if((m_sock_fd < 0), false, "Failed to create socket");
+
+       /* non-blocking mode */
+       retvm_if(!set_blocking_mode(false), false, "Failed to set non-blocking mode");
+       /* recv timeout */
+       retvm_if(!set_recv_timeout(1), false, "Failed to set timeout");
+       /* TODO */
+       /*retvm_if(!set_reuse_addr(), false, "Failed to reuse address"); */
+
+       _D("Created[%d]", m_sock_fd);
+
+       m_path = path;
+
+       return true;
+}
+
+int socket::get_sock_type(void)
+{
+       socklen_t opt_len;
+       int sock_type;
+       opt_len = sizeof(sock_type);
+
+       retvm_if(m_sock_fd < 0, false, "Invalid socket[%d]", m_sock_fd);
+
+       if (getsockopt(m_sock_fd, SOL_SOCKET, SO_TYPE, &sock_type, &opt_len) < 0) {
+          _ERRNO(errno, _E, "Failed to getsockopt from socket[%d]", m_sock_fd);
+          return false;
+       }
+
+       return sock_type;
+}
+
+bool socket::set_recv_timeout(int sec)
+{
+       struct timeval timeout = {sec, 0};
+
+       retvm_if(m_sock_fd < 0, false, "Invalid socket[%d]", m_sock_fd);
+
+       if (setsockopt(m_sock_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
+          _ERRNO(errno, _E, "Failed to setsockopt[%d]", m_sock_fd);
+          return false;
+       }
+
+       return true;
+}
+
+bool socket::set_sock_type(int type)
+{
+       socklen_t opt_len;
+       int sock_type;
+       opt_len = sizeof(sock_type);
+
+       retvm_if(m_sock_fd < 0, false, "Invalid socket[%d]", m_sock_fd);
+
+       if (setsockopt(m_sock_fd, SOL_SOCKET, SO_TYPE, &sock_type, opt_len) < 0) {
+          _ERRNO(errno, _E, "Failed to setsockopt[%d]", m_sock_fd);
+          return false;
+       }
+
+       return true;
+}
+
+bool socket::set_blocking_mode(bool blocking)
+{
+       int flags;
+
+       flags = fcntl(m_sock_fd, F_GETFL);
+       retvm_if(flags == -1, false, "Failed to fcntl(F_GETFL)[%d]", m_sock_fd);
+
+       flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+
+       flags = fcntl(m_sock_fd, F_SETFL, flags);
+       retvm_if(flags == -1, false, "Failed to fcntl(F_SETFL)[%d]", m_sock_fd);
+
+       return true;
+}
+
+bool socket::has_connected(void)
+{
+       int so_error;
+       socklen_t len = sizeof(so_error);
+
+       if (getsockopt(m_sock_fd, SOL_SOCKET, SO_ERROR, &so_error, &len) == -1) {
+               _E("Failed to call getsockopt[%d]", m_sock_fd);
+               return false;
+       }
+
+       if (so_error) {
+               _E("Failed to connect[%d]: %d", so_error);
+               return false;
+       }
+
+       return true;
+}
+
+bool socket::set_buffer_size(int type, int size)
+{
+       retv_if(m_sock_fd < 0, false);
+
+       int ret = 0;
+
+       ret = setsockopt(m_sock_fd, SOL_SOCKET, type, &size, sizeof(size));
+       retvm_if(ret < 0, false, "Failed to call setsocketopt()");
+
+       return true;
+}
+
+int socket::get_buffer_size(int type)
+{
+       retv_if(m_sock_fd < 0, false);
+
+       int ret = 0;
+       int buf_size = 0;
+       socklen_t len;
+
+       ret = getsockopt(m_sock_fd, SOL_SOCKET, type, &buf_size, &len);
+       retvm_if(ret < 0, -EPERM, "Failed to call getsocketopt()");
+
+       return buf_size;
+}
+
+int socket::get_current_buffer_size(void)
+{
+       retv_if(m_sock_fd < 0, false);
+
+       int queue_size = 0;
+       ioctl(m_sock_fd, TIOCOUTQ, &queue_size);
+
+       return queue_size;
+}
+
diff --git a/src/shared/socket.h b/src/shared/socket.h
new file mode 100644 (file)
index 0000000..e9b6b66
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __SOCKET_H__
+#define __SOCKET_H__
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string>
+#include <atomic>
+
+namespace ipc {
+
+class socket {
+public:
+       socket();
+       socket(int sock_fd);
+       socket(const socket &sock);
+       virtual ~socket();
+
+       virtual bool create(const std::string &path);
+
+       bool connect(void);
+       bool bind(void);
+       bool listen(const int max_connections);
+       bool accept(socket &client_sock);
+
+       bool close(void);
+
+       int  get_fd(void) const;
+       void set_fd(int sock_fd);
+
+       int  get_mode(void) const;
+       bool set_mode(int mode);
+
+       bool set_blocking_mode(bool blocking);
+       bool set_recv_timeout(int timeout);
+
+       /* type : SO_SNDBUF, SO_RCVBUF */
+       bool set_buffer_size(int type, int size);
+       int  get_buffer_size(int type);
+       int  get_current_buffer_size(void);
+
+       ssize_t send(const void *buffer, size_t size, bool select = false) const;
+       ssize_t recv(void* buffer, size_t size, bool select = false) const;
+
+protected:
+       bool create_by_type(const std::string &path, int type);
+
+private:
+       virtual ssize_t on_send(const void *buffer, size_t size) const = 0;
+       virtual ssize_t on_recv(void* buffer, size_t size) const = 0;
+
+       int  get_sock_type(void);
+       bool set_sock_type(int type);
+       bool has_connected(void);
+
+       int m_sock_fd;
+       int m_mode;
+       std::atomic<bool> m_listening;
+       std::string m_path;
+};
+
+}
+
+#endif /* __SOCKET_H__ */
diff --git a/src/shared/stream_socket.cpp b/src/shared/stream_socket.cpp
new file mode 100644 (file)
index 0000000..cdf8ccc
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "stream_socket.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "sensor_log.h"
+
+using namespace ipc;
+
+stream_socket::stream_socket()
+: socket()
+{
+}
+
+stream_socket::~stream_socket()
+{
+}
+
+bool stream_socket::create(const std::string &path)
+{
+       return socket::create_by_type(path, SOCK_STREAM);
+}
+
+ssize_t stream_socket::on_send(const void *buffer, size_t size) const
+{
+       ssize_t len = 0;
+       size_t total_size = 0;
+
+       do {
+               len = ::send(get_fd(),
+                               reinterpret_cast<const char *>(buffer) + total_size,
+                               size - total_size, get_mode());
+
+               if (len < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+                               usleep(1);
+                               continue;
+                       }
+
+                       _ERRNO(errno, _E, "Failed to send(%d, %#p, %x, %d) = %d",
+                                       get_fd(), buffer, total_size, size - total_size, len);
+                       return -errno;
+               }
+
+               total_size += len;
+       } while (total_size < size);
+
+       return total_size;
+}
+
+ssize_t stream_socket::on_recv(void *buffer, size_t size) const
+{
+       ssize_t len = 0;
+       size_t total_size = 0;
+
+       do {
+               len = ::recv(get_fd(),
+                               reinterpret_cast<char *>(buffer) + total_size,
+                               size - total_size,
+                               socket::get_mode());
+
+               if (len == 0) {
+                       _E("Failed to recv(%d, %#p + %x, %d) = %d, because the peer performed shutdown",
+                               get_fd(), buffer, total_size, size - total_size, len);
+                       return -1;
+               }
+
+               if (len < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+                               usleep(10000);
+                               continue;
+                       }
+
+                       _ERRNO(errno, _E, "Failed to recv(%d, %#p, %x, %d) = %d",
+                                       get_fd(), buffer, total_size, size - total_size, len);
+                       return -errno;
+               }
+
+               total_size += len;
+       } while (total_size < size);
+
+       return total_size;
+}
diff --git a/src/shared/stream_socket.h b/src/shared/stream_socket.h
new file mode 100644 (file)
index 0000000..42af03e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * sensord
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __STREAM_SOCKET_H__
+#define __STREAM_SOCKET_H__
+
+#include "socket.h"
+
+namespace ipc {
+
+class stream_socket : public socket {
+public:
+       stream_socket();
+       ~stream_socket();
+
+       bool create(const std::string &path);
+
+private:
+       ssize_t on_send(const void *buffer, size_t size) const;
+       ssize_t on_recv(void *buffer, size_t size) const;
+};
+
+}
+
+#endif /* __STREAM_SOCKET_H__ */