From 0f1b6a14df1136de413535c3026ad512b2aa1042 Mon Sep 17 00:00:00 2001 From: Hwankyu Jhun Date: Wed, 29 Jun 2022 16:41:17 +0900 Subject: [PATCH] Refactor aul sock The aul sock is implemented using C++. Change-Id: I34573294f754e2469d19bd2867f89a6d7f88bc51 Signed-off-by: Hwankyu Jhun --- aul/common/exception.hh | 3 +- aul/socket/client_socket.cc | 256 +++++++++++ aul/socket/client_socket.hh | 53 +++ aul/socket/server_socket.cc | 155 +++++++ aul/socket/server_socket.hh | 52 +++ aul/socket/socket.cc | 10 +- aul/socket/socket.hh | 5 +- src/aul_sock.c | 1000 ------------------------------------------- src/aul_sock.cc | 674 +++++++++++++++++++++++++++++ test/unit_tests/main.cc | 7 +- 10 files changed, 1204 insertions(+), 1011 deletions(-) create mode 100644 aul/socket/client_socket.cc create mode 100644 aul/socket/client_socket.hh create mode 100644 aul/socket/server_socket.cc create mode 100644 aul/socket/server_socket.hh delete mode 100644 src/aul_sock.c create mode 100644 src/aul_sock.cc diff --git a/aul/common/exception.hh b/aul/common/exception.hh index 4d7fa1c..a4022e0 100644 --- a/aul/common/exception.hh +++ b/aul/common/exception.hh @@ -33,7 +33,6 @@ class Exception : public std::exception { error_code_ = error_code; message_ = file.substr(file.find_last_of("/") + 1) + ":" + std::to_string(line) + " code:" + std::to_string(error_code_); - _E("%s", message_.c_str()); } virtual ~Exception() {} @@ -42,7 +41,7 @@ class Exception : public std::exception { return message_.c_str(); } - int GetErrorCode() { + int GetErrorCode() const { return error_code_; } diff --git a/aul/socket/client_socket.cc b/aul/socket/client_socket.cc new file mode 100644 index 0000000..3f3e793 --- /dev/null +++ b/aul/socket/client_socket.cc @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 "aul/socket/client_socket.hh" + +#include +#include +#include +#include +#include +#include +#include + +namespace aul { +namespace { + +constexpr const int MAX_RETRY_CNT = 2; + +} // namespace + +ClientSocket::ClientSocket() { + fd_ = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd_ < 0) { + int error = -errno; + _E("socket() is failed. errno(%d)", errno); + THROW(error); + } +} + +ClientSocket::ClientSocket(int fd) : fd_(fd) {} + +ClientSocket::~ClientSocket() { + Close(); +} + +void ClientSocket::Close() { + if (fd_ > -1) { + close(fd_); + fd_ = -1; + } +} + +void ClientSocket::Connect(const std::string& endpoint) { + int flag = fcntl(fd_, F_GETFL, 0); + fcntl(GetFd(), F_SETFL, flag | O_NONBLOCK); + + struct sockaddr_un sockaddr = { 0, }; + sockaddr.sun_family = AF_UNIX; + snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", + endpoint.c_str()); + struct sockaddr* sockaddr_ptr = reinterpret_cast(&sockaddr); + socklen_t len = sizeof(sockaddr); + + int ret; + int retry = 2; + do { + ret = connect(fd_, sockaddr_ptr, len); + if (ret == 0) + break; + + retry--; + usleep(100 * 1000); + } while (retry > 0); + + fcntl(fd_, F_SETFL, flag); + if (ret < 0) { + ret = -errno; + _E("connect() is failed. errno(%d)", errno); + THROW(ret); + } +} + +int ClientSocket::Send(const void* buf, unsigned int size) { + const unsigned char* buffer = static_cast(buf); + size_t len = size; + int retry_cnt = MAX_RETRY_CNT; + while (len) { + ssize_t bytes = send(fd_, buffer, len, MSG_NOSIGNAL | MSG_DONTWAIT); + if (bytes < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + if (retry_cnt > 0) { + retry_cnt--; + _E("send(): fd(%d), errno(%d). sleep and retry ...", fd_, errno); + usleep(10 * 1000); + continue; + } + } + + _E("send() is failed. fd(%d), errno(%d)", fd_, errno); + return -ECOMM; + } + + len -= bytes; + buffer += bytes; + } + + return 0; +} + +int ClientSocket::Receive(void* buf, unsigned int size) { + bool is_blocking; + if (fcntl(fd_, F_GETFL, 0) & O_NONBLOCK) + is_blocking = false; + else + is_blocking = true; + + int retry_count = 20; + unsigned char* buffer = static_cast(buf); + size_t len = size; + while (len) { + ssize_t bytes = recv(fd_, buffer, len, 0); + if (bytes == 0) { + _W("EOF. fd(%d)", fd_); + return -ECOMM; + } + + if (bytes < 0) { + if (errno == EINTR || errno == EAGAIN) { + if (is_blocking && errno == EAGAIN) { + _E("Timed out. fd(%d)", fd_); + return -EAGAIN; + } + + if (retry_count > 0) { + usleep(100 * 1000); + retry_count--; + continue; } + } + + _E("recv() is failed. fd(%d), errno(%d)", fd_, errno); + return -ECOMM; + } + + len -= bytes; + buffer += bytes; + } + + return 0; +} + +int ClientSocket::GetReceiveBufferSize() { + int value; + socklen_t len = sizeof(int); + int ret = getsockopt(fd_, SOL_SOCKET, SO_RCVBUF, + reinterpret_cast(&value), &len); + if (ret < 0) { + ret = -errno; + _E("getsockopt() is failed. errno(%d)", errno); + THROW(ret); + } + + return value; +} + +int ClientSocket::GetSendBufferSize() { + int value; + socklen_t len = sizeof(int); + int ret = getsockopt(fd_, SOL_SOCKET, SO_SNDBUF, + reinterpret_cast(&value), &len); + if (ret < 0) { + ret = -errno; + _E("getsockopt() is failed. errno(%d)", errno); + THROW(ret); + } + + return value; +} + +int ClientSocket::GetReceiveTimeout() { + struct timeval timeout = { 0, }; + socklen_t len = sizeof(struct timeval); + int ret = getsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeout), &len); + if (ret < 0) { + ret = -errno; + _E("getsockopt() is failed. errno(%d)", errno); + THROW(ret); + } + + int value = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; + return value; +} + +void ClientSocket::SetReceiveBufferSize(int size) { + socklen_t len = sizeof(size); + int ret = setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &size, len); + if (ret < 0) { + ret = -errno; + _E("setsockopt() is failed. errno(%d)", errno); + THROW(ret); + } +} + +void ClientSocket::SetSendBufferSize(int size) { + socklen_t len = sizeof(size); + int ret = setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, &size, len); + if (ret < 0) { + ret = -errno; + _E("setsockopt() is failed. errno(%d)", errno); + THROW(ret); + } +} + +void ClientSocket::SetReceiveTimeout(int timeout) { + if (timeout == INT_MAX) + return; + + if (timeout == -1) + timeout = 5000; + + if (timeout < 0) { + _E("Invalid parameter"); + THROW(-EINVAL); + } + + struct timeval tv = { + .tv_sec = static_cast(timeout / 1000), + .tv_usec = static_cast((timeout % 1000) * 1000) + }; + socklen_t len = sizeof(struct timeval); + int ret = setsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, &tv, len); + if (ret < 0) { + ret = -errno; + _E("setsockopt() is failed. errno(%d)", errno); + THROW(ret); + } +} + +bool ClientSocket::IsClosed() { + return fd_ < 0; +} + +int ClientSocket::GetFd() const { + return fd_; +} + +int ClientSocket::RemoveFd() { + int fd = fd_; + fd_ = -1; + return fd; +} + +} // namespace aul diff --git a/aul/socket/client_socket.hh b/aul/socket/client_socket.hh new file mode 100644 index 0000000..70c987c --- /dev/null +++ b/aul/socket/client_socket.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 AUL_SOCKET_CLIENT_SOCKET_HH_ +#define AUL_SOCKET_CLIENT_SOCKET_HH_ + +#include +#include + +#include "aul/common/exception.hh" + +namespace aul { + +class ClientSocket { + public: + ClientSocket(); + explicit ClientSocket(int fd); + virtual ~ClientSocket(); + + void Close(); + void Connect(const std::string& endpoint); + int Send(const void* buf, unsigned int size); + int Receive(void* buf, unsigned int size); + int GetReceiveBufferSize(); + int GetSendBufferSize(); + int GetReceiveTimeout(); + void SetReceiveBufferSize(int size); + void SetSendBufferSize(int size); + void SetReceiveTimeout(int timeout); + bool IsClosed(); + int GetFd() const; + int RemoveFd(); + + private: + int fd_; +}; + +} // namespace aul + +#endif // AUL_SOCKET_CLIENT_SOCKET_HH_ diff --git a/aul/socket/server_socket.cc b/aul/socket/server_socket.cc new file mode 100644 index 0000000..12da427 --- /dev/null +++ b/aul/socket/server_socket.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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 "aul/socket/server_socket.hh" + +#include +#include +#include +#include +#include +#include +#include + +#include "aul/common/exception.hh" +#include "aul/common/log_private.hh" + +namespace aul { + +ServerSocket::ServerSocket() { + fd_ = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (fd_ < 0) { + fd_ = -errno; + _E("socket() is failed. errno(%d)", errno); + THROW(fd_); + } +} + +ServerSocket::ServerSocket(int fd) : fd_(fd) {} + +ServerSocket::~ServerSocket() { + Close(); +} + +ClientSocket* ServerSocket::Accept() { + struct sockaddr_un addr = { 0, }; + socklen_t len = sizeof(struct sockaddr_un); + auto* addr_ptr = reinterpret_cast(&addr); + int client_fd = accept(GetFd(), addr_ptr, &len); + if (client_fd == -1) { + _E("accept() is failed. errno(%d)", errno); + return nullptr; + } + + return new (std::nothrow) ClientSocket(client_fd); +} + +void ServerSocket::Bind(const std::string& bindpoint) { + struct sockaddr_un sockaddr = { 0, }; + sockaddr.sun_family = AF_UNIX; + snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", + bindpoint.c_str()); + struct sockaddr* sockaddr_ptr = reinterpret_cast(&sockaddr); + socklen_t len = sizeof(sockaddr); + + unlink(bindpoint.c_str()); + int ret = bind(GetFd(), sockaddr_ptr, len); + if (ret < 0) { + ret = -errno; + _E("bind() is failed. errno(%d)", errno); + THROW(ret); + } +} + +void ServerSocket::Close() { + if (fd_ > -1) { + close(fd_); + fd_ = -1; + } +} + +void ServerSocket::Listen(int backlog) { + int ret = listen(fd_, backlog); + if (ret < 0) { + ret = -errno; + _E("listen() is failed. errno(%d)", errno); + THROW(ret); + } +} + +int ServerSocket::GetReceiveBufferSize() { + int value; + socklen_t len = sizeof(int); + int ret = getsockopt(fd_, SOL_SOCKET, SO_RCVBUF, + reinterpret_cast(&value), &len); + if (ret < 0) { + ret = -errno; + _E("getsockopt() is failed. errno(%d)", errno); + THROW(ret); + } + + return value; +} + +int ServerSocket::GetSendBufferSize() { + int value; + socklen_t len = sizeof(int); + int ret = getsockopt(fd_, SOL_SOCKET, SO_SNDBUF, + reinterpret_cast(&value), &len); + if (ret < 0) { + ret = -errno; + _E("getsockopt() is failed. errno(%d)", errno); + THROW(ret); + } + + return value; +} + +void ServerSocket::SetReceiveBufferSize(int size) { + socklen_t len = sizeof(size); + int ret = setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &size, len); + if (ret < 0) { + ret = -errno; + _E("setsockopt() is failed. errno(%d)", errno); + THROW(ret); + } +} + +void ServerSocket::SetSendBufferSize(int size) { + socklen_t len = sizeof(size); + int ret = setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, &size, len); + if (ret < 0) { + ret = -errno; + _E("setsockopt() is failed. errno(%d)", errno); + THROW(ret); + } +} + +bool ServerSocket::IsClosed() { + return fd_ < 0; +} + +int ServerSocket::GetFd() const { + return fd_; +} + +int ServerSocket::RemoveFd() { + int fd = fd_; + fd_ = -1; + return fd; +} + +} // namespace aul diff --git a/aul/socket/server_socket.hh b/aul/socket/server_socket.hh new file mode 100644 index 0000000..dbd4105 --- /dev/null +++ b/aul/socket/server_socket.hh @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 AUL_SOCKET_SERVER_SOCKET_HH_ +#define AUL_SOCKET_SERVER_SOCKET_HH_ + +#include +#include + +#include "aul/common/exception.hh" +#include "aul/socket/client_socket.hh" + +namespace aul { + +class ServerSocket { + public: + ServerSocket(); + explicit ServerSocket(int fd); + virtual ~ServerSocket(); + + ClientSocket* Accept(); + void Bind(const std::string& bindpoint); + void Close(); + void Listen(int backlog); + int GetReceiveBufferSize(); + int GetSendBufferSize(); + void SetReceiveBufferSize(int size); + void SetSendBufferSize(int size); + bool IsClosed(); + int GetFd() const; + int RemoveFd(); + + private: + int fd_; +}; + +} // namespace aul + +#endif // AUL_SOCKET_SERVER_SOCKET_HH_ diff --git a/aul/socket/socket.cc b/aul/socket/socket.cc index 46487f0..46deda0 100644 --- a/aul/socket/socket.cc +++ b/aul/socket/socket.cc @@ -151,14 +151,20 @@ int Socket::Connect() { return -1; } -int Socket::GetFd() { +int Socket::GetFd() const { return fd_; } -std::string Socket::GetPath() { +const std::string& Socket::GetPath() const { return path_; } +int Socket::RemoveFd() { + int fd = fd_; + fd_ = -1; + return fd; +} + void Socket::SetOption() { int size = MAX_AUL_BUFF_SIZE; int ret = setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); diff --git a/aul/socket/socket.hh b/aul/socket/socket.hh index 6cb084b..928ad9f 100644 --- a/aul/socket/socket.hh +++ b/aul/socket/socket.hh @@ -40,9 +40,10 @@ class Socket { int Listen(); int Connect(); - int GetFd(); + int GetFd() const; void SetTimeout(int timeout_msec); - std::string GetPath(); + const std::string& GetPath() const; + int RemoveFd(); private: void SetOption(); diff --git a/src/aul_sock.c b/src/aul_sock.c deleted file mode 100644 index 5ad51b7..0000000 --- a/src/aul_sock.c +++ /dev/null @@ -1,1000 +0,0 @@ -/* - * Copyright (c) 2000 - 2018 Samsung Electronics Co., Ltd. All rights reserved. - * - * 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. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "aul_api.h" -#include "aul_sock.h" -#include "aul_util.h" - -#define MAX_NR_OF_DESCRIPTORS 2 -#define MAX_PAYLOAD_SIZE (1024 * 1024 * 1) -#define PATH_AMD_SOCK "/run/aul/daemons/.amd-sock" - -#define REGULAR_UID_MIN 5000 - -typedef struct app_pkt_header_s { - int cmd; - int len; - int opt; -} app_pkt_header_t; - -static int socket_timeout_initialized; -static int socket_link_created; -static char socket_link_path[108]; - -static struct timeval tv = { 5, 200 * 1000 }; /* 5.2 */ - -static int __connect_client_sock(int sockfd, const struct sockaddr *saptr, - socklen_t salen, int nsec); - -static int __recv_raw(int fd, unsigned char *data, size_t data_size) -{ - ssize_t recv_size = 0; - ssize_t r; - size_t size = data_size; - bool is_blocking; - int retry_count = 20; - - if (fcntl(fd, F_GETFL, 0) & O_NONBLOCK) - is_blocking = false; - else - is_blocking = true; - - while (size > 0) { - r = recv(fd, data, size, 0); - if (r == 0) { - _W("Socket was disconnected. fd(%d)", fd); - return -ECOMM; - } else if (r < 0) { - if (errno == EINTR || errno == EAGAIN) { - if (is_blocking && errno == EAGAIN) { - _E("recv timeout. fd(%d)", fd); - return -EAGAIN; - } - - if (retry_count > 0) { - usleep(100 * 1000); - retry_count--; - continue; - } - } - - _E("recv error. fd(%d), errno(%d)", - fd, errno); - return -ECOMM; - } - - size -= r; - data += r; - recv_size += r; - } - - if (recv_size != data_size) { - _E("Failed to receive messages. fd(%d)", fd); - return -ECOMM; - } - - return 0; -} - -static int __recv_pkt(int fd, app_pkt_t **out_pkt) -{ - app_pkt_header_t header = { 0, }; - app_pkt_t *pkt; - int r; - - *out_pkt = NULL; - r = __recv_raw(fd, (unsigned char *)&header, sizeof(header)); - if (r < 0) { - _E("Failed to receive packet header"); - return r; - } - - if (header.len < 0 || header.len > MAX_PAYLOAD_SIZE) { - _E("Invalid protocol. length(%d)", header.len); - return -ECOMM; - } - - pkt = calloc(1, sizeof(app_pkt_t) + header.len); - if (!pkt) { - _E("Out of memory"); - return -ECOMM; - } - pkt->cmd = header.cmd; - pkt->len = header.len; - pkt->opt = header.opt; - - r = __recv_raw(fd, (unsigned char *)pkt->data, pkt->len); - if (r < 0) { - free(pkt); - return r; - } - - *out_pkt = pkt; - - return 0; -} - -static void __set_timeval(double sec) -{ - char buf[12]; - gchar *ptr = NULL; - - snprintf(buf, sizeof(buf), "%.3f", sec); - tv.tv_sec = g_ascii_strtoull(buf, &ptr, 10); - tv.tv_usec = g_ascii_strtoull(ptr + 1, &ptr, 10) * 1000; - _D("tv_sec: %ld, tv_usec: %ld", (long)tv.tv_sec, (long)tv.tv_usec); -} - -static void __socket_timeout_vconf_cb(keynode_t *key, void *data) -{ - const char *name; - double sec; - - name = vconf_keynode_get_name(key); - if (name && strcmp(name, VCONFKEY_AUL_SOCKET_TIMEOUT) == 0) { - sec = vconf_keynode_get_dbl(key); - __set_timeval(sec); - } -} - -static void __init_socket_timeout(void) -{ - int r; - double sec = 5.2f; - - r = access("/run/aul/.socket_timeout", F_OK); - if (r < 0) { - socket_timeout_initialized = 1; - return; - } - - r = vconf_get_dbl(VCONFKEY_AUL_SOCKET_TIMEOUT, &sec); - if (r < 0) - _D("Failed to get vconf: %s", VCONFKEY_AUL_SOCKET_TIMEOUT); - - r = vconf_notify_key_changed(VCONFKEY_AUL_SOCKET_TIMEOUT, - __socket_timeout_vconf_cb, NULL); - if (r < 0) { - _E("Failed to register callback for %s", - VCONFKEY_AUL_SOCKET_TIMEOUT); - return; - } - - __set_timeval(sec); - socket_timeout_initialized = 1; -} - -API struct timeval aul_sock_get_rcv_timeval(void) -{ - return tv; -} - -API int aul_sock_set_sock_option(int fd, int cli) -{ - int size; - int r; - - size = AUL_SOCK_MAXBUFF; - r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - if (r < 0) - return r; - - r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - if (r < 0) - return r; - - if (cli) { - if (TIZEN_FEATURE_SOCKET_TIMEOUT && !socket_timeout_initialized) - __init_socket_timeout(); - r = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - if (r < 0) - return r; - } - - return 0; -} - -static int __create_socket_dir(int pid, uid_t uid) -{ - char path[108]; - - if (uid < REGULAR_UID_MIN) - snprintf(path, sizeof(path), "/run/aul/daemons/%d", uid); - else - snprintf(path, sizeof(path), "/run/aul/apps/%d/%d", uid, pid); - - if (mkdir(path, 0700) != 0) { - if (errno == EEXIST) { - if (access(path, R_OK) != 0) { - _E("Failed to acess %s directory", path); - return -1; - } - } else { - _E("Failed to create %s directory", path); - return -1; - } - } - - return 0; -} - -static void __create_socket_path(char *path_buf, int size, int pid, uid_t uid) -{ - if (uid < REGULAR_UID_MIN) { - snprintf(path_buf, size, - "/run/aul/daemons/%d/.app-sock-%d", uid, pid); - } else { - snprintf(path_buf, size, - "/run/aul/apps/%d/%d/.app-sock", uid, pid); - } -} - -static void __create_socket_link(const char *socket_path, int pid, uid_t uid) -{ - char path[108]; - - if (__create_socket_dir(pid, uid) < 0) - return; - - __create_socket_path(path, sizeof(path), pid, uid); - if (link(socket_path, path) < 0) { - if (errno == EEXIST) - _D("path(%s) - already exists", path); - else - _E("path(%s) - unknown create error", path); - } - - snprintf(socket_link_path, sizeof(socket_link_path), "%s", path); - socket_link_created = 1; -} - -API int aul_sock_create_server(int pid, uid_t uid) -{ - struct sockaddr_un saddr; - int fd; - int env_pid = -1; - char *env_str; - - fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - /* support above version 2.6.27*/ - if (fd < 0) { - if (errno == EINVAL) { - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - _E("second chance - socket create error"); - return -1; - } - } else { - _E("socket error"); - return -1; - } - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sun_family = AF_UNIX; - - if (__create_socket_dir(pid, uid) < 0) { - close(fd); - return -1; - } - - __create_socket_path(saddr.sun_path, sizeof(saddr.sun_path), pid, uid); - unlink(saddr.sun_path); - - /* labeling to socket for SMACK */ - if (getuid() == 0) { /* this is meaningful if current user is ROOT */ - if (fsetxattr(fd, "security.SMACK64IPOUT", "@", 1, 0) < 0) { - /* in case of unsupported filesystem on 'socket' */ - /* or permission error by using 'emulator', bypass*/ - if ((errno != EOPNOTSUPP) && (errno != EPERM)) { - _E("labeling to socket(IPOUT) error"); - close(fd); - return -1; - } - } - if (fsetxattr(fd, "security.SMACK64IPIN", "*", 1, 0) < 0) { - /* in case of unsupported filesystem on 'socket' */ - /* or permission error by using 'emulator', bypass*/ - if ((errno != EOPNOTSUPP) && (errno != EPERM)) { - _E("labeling to socket(IPIN) error"); - close(fd); - return -1; - } - } - } - - if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { - _E("bind error"); - close(fd); - return -1; - } - - aul_sock_set_sock_option(fd, 0); - - if (listen(fd, 128) == -1) { - _E("listen error"); - close(fd); - return -1; - } - - /* Create socket link */ - if (pid > 0) { - env_str = getenv("AUL_PID"); - if (env_str && isdigit(env_str[0])) - env_pid = atoi(env_str); - - if (env_pid > 1 && pid != env_pid) - __create_socket_link(saddr.sun_path, env_pid, uid); - } - - return fd; -} - -static int __create_client_sock(int pid, uid_t uid) -{ - int fd = -1; - struct sockaddr_un saddr = { 0, }; - int retry = 2; - int ret = -1; - - fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - /* support above version 2.6.27*/ - if (fd < 0) { - if (errno == EINVAL) { - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - _E("second chance - socket create error"); - return -1; - } - } else { - _E("socket error"); - return -1; - } - } - - saddr.sun_family = AF_UNIX; - if (pid == AUL_UTIL_PID) { - snprintf(saddr.sun_path, sizeof(saddr.sun_path), - "%s", PATH_AMD_SOCK); - } else { - __create_socket_path(saddr.sun_path, sizeof(saddr.sun_path), pid, uid); - } - -retry_con: - ret = __connect_client_sock(fd, (struct sockaddr *)&saddr, sizeof(saddr), - 100 * 1000); - if (ret < -1) { - _E("maybe peer not launched or peer(%d:%u) dead. fd(%d)", - pid, uid, fd); - if (retry > 0) { - usleep(100 * 1000); - retry--; - goto retry_con; - } - } - - if (ret < 0) { - _E("Failed to connect the socket. fd(%d), errno(%d)", - fd, errno); - close(fd); - return -1; - } - - aul_sock_set_sock_option(fd, 1); - - return fd; -} - -static int __connect_client_sock(int fd, const struct sockaddr *saptr, socklen_t salen, - int nsec) -{ - int flags; - int ret; - int error = 0; - socklen_t len; - fd_set readfds; - fd_set writefds; - struct timeval timeout; - - flags = fcntl(fd, F_GETFL, 0); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - - ret = connect(fd, (struct sockaddr *)saptr, salen); - if (ret < 0) { - if (errno != EAGAIN && errno != EINPROGRESS) { - (void)fcntl(fd, F_SETFL, flags); - return -2; - } - } - - /* Do whatever we want while the connect is taking place. */ - if (ret == 0) - goto done; /* connect completed immediately */ - - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - writefds = readfds; - timeout.tv_sec = 0; - timeout.tv_usec = nsec; - - ret = select(fd + 1, &readfds, &writefds, NULL, nsec ? &timeout : NULL); - if (ret == 0) { - errno = ETIMEDOUT; - return -1; - } - - if (FD_ISSET(fd, &readfds) || FD_ISSET(fd, &writefds)) { - len = sizeof(error); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) - return -1; /* Solaris pending error */ - } - - return -1; /* select error: sockfd not set*/ - -done: - ret = fcntl(fd, F_SETFL, flags); - if (ret < 0) - return ret; - - return 0; -} - -static int __send_raw_async_with_fd(int fd, int cmd, unsigned char *kb_data, int datalen, int opt) -{ - int len; - int sent = 0; - app_pkt_t *pkt = NULL; - - pkt = (app_pkt_t *)calloc(1, sizeof(app_pkt_t) + datalen); - if (NULL == pkt) { - _E("Malloc Failed!"); - return -ENOMEM; - } - - pkt->cmd = cmd; - pkt->len = datalen; - pkt->opt = opt; - - if (kb_data) - memcpy(pkt->data, kb_data, pkt->len); - - while (sent != AUL_PKT_HEADER_SIZE + pkt->len) { - len = send(fd, (char *)pkt + sent, - AUL_PKT_HEADER_SIZE + pkt->len - sent, - MSG_NOSIGNAL); - if (len <= 0) { - _E("send error fd:%d (errno %d)", fd, errno); - free(pkt); - return -ECOMM; - } - sent += len; - } - - free(pkt); - - return 0; -} - -API int aul_sock_send_raw_with_fd(int fd, int cmd, unsigned char *kb_data, int datalen, int opt) -{ - int len; - int res; - - _D("fd(%d): cmd(%d)", fd, cmd); - - res = __send_raw_async_with_fd(fd, cmd, kb_data, datalen, opt); - if (res < 0 || opt & AUL_SOCK_NOREPLY) { - if (!(opt & AUL_SOCK_ASYNC)) - close(fd); - return res; - } - - if (opt & AUL_SOCK_ASYNC) - return fd; - - len = __recv_raw(fd, (unsigned char *)&res, sizeof(int)); - if (len < 0) - res = len; - - close(fd); - - return res; -} - -API int aul_sock_send_bundle_with_fd(int fd, int cmd, bundle *kb, int opt) -{ - bundle_raw *kb_data = NULL; - int datalen; - int res; - - if (!kb) - return -EINVAL; - - res = bundle_encode(kb, &kb_data, &datalen); - if (res != BUNDLE_ERROR_NONE) - return -EINVAL; - - res = aul_sock_send_raw_with_fd(fd, cmd, kb_data, datalen, opt | AUL_SOCK_BUNDLE); - - if (kb_data) - free(kb_data); - - return res; -} - -/* - * @brief Send data (in raw) to the process with 'pid' via socket - */ -API int aul_sock_send_raw(int pid, uid_t uid, int cmd, - unsigned char *kb_data, int datalen, int opt) -{ - int fd; - int r; - - _D("pid(%d): cmd(%d)", pid, cmd); - - fd = __create_client_sock(pid, uid); - if (fd < 0) - return -ECOMM; - - r = aul_sock_send_raw_with_fd(fd, cmd, kb_data, datalen, opt); - if (r < 0) { - if (opt & AUL_SOCK_ASYNC) - close(fd); - } - - return r; -} - -API int aul_sock_send_bundle(int pid, uid_t uid, int cmd, bundle *kb, int opt) -{ - bundle_raw *kb_data = NULL; - int datalen; - int res; - - if (!kb) - return -EINVAL; - - res = bundle_encode(kb, &kb_data, &datalen); - if (res != BUNDLE_ERROR_NONE) - return -EINVAL; - - res = aul_sock_send_raw(pid, uid, cmd, kb_data, datalen, opt | AUL_SOCK_BUNDLE); - - if (kb_data) - free(kb_data); - - return res; -} - -API app_pkt_t *aul_sock_recv_pkt(int fd, int *clifd, struct ucred *cr) -{ - struct sockaddr_un aul_addr = { 0, }; - int sun_size = sizeof(struct sockaddr_un); - int cl = sizeof(struct ucred); - app_pkt_t *pkt = NULL; - int client_fd; - int ret; - - client_fd = accept(fd, (struct sockaddr *)&aul_addr, - (socklen_t *)&sun_size); - if (client_fd == -1) { - if (errno != EINTR) - _E("accept error"); - return NULL; - } - - ret = getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, - cr, (socklen_t *)&cl); - if (ret < 0) { - _E("peer information error"); - close(client_fd); - return NULL; - } - - aul_sock_set_sock_option(client_fd, 1); - - ret = __recv_pkt(client_fd, &pkt); - if (ret < 0) { - close(client_fd); - return NULL; - } - - *clifd = client_fd; - - return pkt; -} - -API int aul_sock_recv_reply_pkt(int fd, app_pkt_t **ret_pkt) -{ - return aul_sock_recv_reply_pkt_v2(fd, ret_pkt, true); -} - -static int __get_descriptors(struct cmsghdr *cmsg, struct msghdr *msg, int *fds, int maxdesc) -{ - int retnr = 0; - int nrdesc; - int payload; - int *recvdesc; - int i; - - if (cmsg == NULL || msg == NULL) - return 0; - if (cmsg->cmsg_type != SCM_RIGHTS) - return 0; - - if (msg->msg_controllen > 0) { - payload = cmsg->cmsg_len - sizeof(*cmsg); - recvdesc = (int *)CMSG_DATA(cmsg); - - nrdesc = payload / sizeof(int); - retnr = nrdesc < maxdesc ? nrdesc : maxdesc; - for (i = 0; i < nrdesc; ++i) { - if (maxdesc-- > 0) - *fds++ = *recvdesc++; - else - close(*recvdesc++); - } - } - - return retnr; -} - -static int __recv_message(int sock, struct iovec *vec, int vec_max_size, int *vec_size, - int *fds, int *nr_fds) -{ - char buff[CMSG_SPACE(sizeof(int) * MAX_NR_OF_DESCRIPTORS) + CMSG_SPACE(50)] = {0}; - struct msghdr msg = {0}; - struct cmsghdr *cmsg = NULL; - bool is_blocking; - int ret; - - if (vec == NULL || vec_max_size < 1 || vec_size == NULL) - return -EINVAL; - - msg.msg_iov = vec; - msg.msg_iovlen = vec_max_size; - msg.msg_control = buff; - msg.msg_controllen = sizeof(buff); - - if (fcntl(sock, F_GETFL, 0) & O_NONBLOCK) - is_blocking = false; - else - is_blocking = true; - -retry: - ret = recvmsg(sock, &msg, 0); - if (ret == 0) { - _W("Socket was disconnected. fd(%d)", sock); - return -ECOMM; - } else if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) { - if (is_blocking && errno == EAGAIN) { - _E("recvmsg timeout. fd(%d)", sock); - return -EAGAIN; - } - - goto retry; - } - - ret = -errno; - _E("recvmsg error. fd(%d), errno(%d)", sock, errno); - return ret; - } - - *vec_size = msg.msg_iovlen; - - /* get the ANCILLARY data */ - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg == NULL) { - if (nr_fds != NULL) - *nr_fds = 0; - } else { - int iter = 0; - int fdnum = 0; - - for (; cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg), iter++) { - switch (cmsg->cmsg_type) { - case SCM_RIGHTS: - if (fds != NULL) - fdnum = __get_descriptors(cmsg, &msg, fds, MAX_NR_OF_DESCRIPTORS); - if (nr_fds != NULL) - *nr_fds = fdnum; - break; - } - } - } - - return 0; -} - -int aul_sock_recv_reply_sock_fd(int fd, int (*ret_fd)[2], int fd_size) -{ - int fds[2] = {0,}; - char recv_buff[1024]; - struct iovec vec[3]; - int ret = 0; - int vec_len = 0; - int fds_len = 0; - - vec[0].iov_base = recv_buff; - vec[0].iov_len = sizeof(recv_buff); - ret = __recv_message(fd, vec, 1, &vec_len, fds, &fds_len); - if (ret < 0) { - _E("Error[%d]. while receiving message", -ret); - if (fds_len > 0) - close(fds[0]); - - ret = -ECOMM; - } else if ((fds_len == fd_size) && (fds_len == 2)) { - (*ret_fd)[0] = fds[0]; - (*ret_fd)[1] = fds[1]; - } else if ((fds_len == fd_size) && (fds_len == 1)) { - (*ret_fd)[0] = fds[0]; - } else { - _E("wrong number of FD recevied. Expected:%d Actual:%d", fd_size, fds_len); - ret = -ECOMM; - } - - close(fd); - return ret; -} - -int aul_sock_create_launchpad_client(const char *pad_type, uid_t uid) -{ - int fd = -1; - struct sockaddr_un saddr = { 0, }; - int retry = 1; - int ret = -1; - - fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - /* support above version 2.6.27*/ - if (fd < 0) { - if (errno == EINVAL) { - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - _E("second chance - socket create error"); - return -1; - } - } else { - _E("socket error"); - return -1; - } - } - - saddr.sun_family = AF_UNIX; - snprintf(saddr.sun_path, sizeof(saddr.sun_path), - "/run/aul/daemons/%d/%s", uid, pad_type); -retry_con: - ret = __connect_client_sock(fd, (struct sockaddr *)&saddr, - sizeof(saddr), 100 * 1000); - if (ret < -1) { - _E("maybe peer not launched or peer dead"); - if (retry > 0) { - usleep(100 * 1000); - retry--; - goto retry_con; - } - } - if (ret < 0) { - close(fd); - return -1; - } - - aul_sock_set_sock_option(fd, 1); - - return fd; -} - -API int aul_sock_recv_pkt_with_cb(int fd, - void (*callback)(app_pkt_t *pkt, void *data), - void *user_data) -{ - app_pkt_t **pkt; - int count = 0; - size_t size; - int ret; - int i; - - if (fd < 0 || fd >= sysconf(_SC_OPEN_MAX)) { - _E("Invalid parameter"); - return -1; - } - - if (callback == NULL) { - _E("Invalid parameter"); - close(fd); - return -1; - } - - ret = __recv_raw(fd, (unsigned char *)&count, sizeof(int)); - if (ret < 0) { - _E("recv error - %d", ret); - close(fd); - return ret; - } else if (count <= 0 || count > MAX_RUNNING_INSTANCE) { - _E("error - count: %d", count); - close(fd); - return -ECOMM; - } - - size = sizeof(app_pkt_t *) * count; - _D("count: %d, size: %zu", count, size); - pkt = (app_pkt_t **)calloc(1, size); - if (pkt == NULL) { - _E("out of memory"); - close(fd); - return -1; - } - - for (i = 0; i < count; ++i) { - ret = __recv_pkt(fd, &pkt[i]); - if (ret < 0) { - _E("Failed to receive packet"); - break; - } - } - - for (i = 0; i < count; ++i) { - callback(pkt[i], user_data); - free(pkt[i]); - } - - free(pkt); - close(fd); - - return ret; -} - -API int aul_sock_recv_result_with_fd(int fd) -{ - int len; - int res; - - len = __recv_raw(fd, (unsigned char *)&res, sizeof(int)); - if (len < 0) { - _E("Failed to receive the result. fd(%d)", fd); - res = -ECOMM; - } - - return res; -} - -static void __delete_dir(const char *path) -{ - DIR *dp; - struct dirent *dentry = NULL; - char buf[PATH_MAX]; - struct stat statbuf; - int ret; - - if (path == NULL) - return; - - dp = opendir(path); - if (dp == NULL) - return; - - while ((dentry = readdir(dp)) != NULL) { - if (!strcmp(dentry->d_name, ".") || - !strcmp(dentry->d_name, "..")) - continue; - - snprintf(buf, sizeof(buf), "%s/%s", path, dentry->d_name); - ret = stat(buf, &statbuf); - if (ret == 0) { - if (S_ISDIR(statbuf.st_mode)) - __delete_dir(buf); - else - unlink(buf); - } - } - - rmdir(path); - closedir(dp); -} - -API int aul_sock_destroy_server(int fd) -{ - char path[PATH_MAX]; - - if (fd > 3) - close(fd); - - if (getuid() >= REGULAR_UID_MIN) { - snprintf(path, sizeof(path), - "/run/aul/apps/%u/%d", - getuid(), getpid()); - __delete_dir(path); - - if (socket_link_created) { - __delete_dir(dirname(socket_link_path)); - socket_link_created = 0; - } - } else { - snprintf(path, sizeof(path), - "/run/aul/daemons/%u/.app-sock-%d", - getuid(), getpid()); - unlink(path); - - if (socket_link_created) { - unlink(socket_link_path); - socket_link_created = 0; - } - } - - return 0; -} - -API int aul_sock_send_result(int fd, int res) -{ - return aul_sock_send_result_v2(fd, res, true); -} - -API int aul_sock_send_result_v2(int fd, int res, bool do_close) -{ - int ret; - - if (fd < 0 || fd >= sysconf(_SC_OPEN_MAX)) { - _E("Invalid parameter"); - return -EINVAL; - } - - ret = send(fd, &res, sizeof(res), MSG_NOSIGNAL); - if (ret < 0) - _E("Failed to send result. fd(%d), errno(%d)", fd, errno); - - if (do_close) - close(fd); - - return ret; -} - -API int aul_sock_recv_reply_pkt_v2(int fd, app_pkt_t **pkt, bool do_close) -{ - int ret; - - if (fd < 0 || fd >= sysconf(_SC_OPEN_MAX)) - return -EINVAL; - - if (!pkt) { - if (do_close) - close(fd); - return -EINVAL; - } - - ret = __recv_pkt(fd, pkt); - if (do_close) - close(fd); - - return ret; -} diff --git a/src/aul_sock.cc b/src/aul_sock.cc new file mode 100644 index 0000000..893ee7d --- /dev/null +++ b/src/aul_sock.cc @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2000 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * + * 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 "include/aul_sock.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "aul_api.h" +#include "aul_util.h" + +#include "aul/socket/client_socket.hh" +#include "aul/socket/packet.hh" +#include "aul/socket/server_socket.hh" + +using namespace aul; + +namespace { + +constexpr const int MAX_NR_OF_DESCRIPTORS = 2; +constexpr const int MAX_PAYLOAD_SIZE = 1024 * 1024 * 1; +constexpr const char PATH_AUL_SOCKET_TIMEOUT[] = "/run/aul/.socket_timeout"; +constexpr const char PATH_AMD_SOCK[] = "/run/aul/daemons/.amd-sock"; +int MAX_FDS = sysconf(_SC_OPEN_MAX); + +// POD type +struct PacketHeader { + int cmd; + int len; + int opt; +}; + +class SocketTimeout { + public: + SocketTimeout() = default; + + ~SocketTimeout() { + if (initialized_) + vconf_ignore_key_changed(VCONFKEY_AUL_SOCKET_TIMEOUT, VconfCb); + } + + timeval GetTimeval() const { + return time_val_; + } + + bool IsInitialized() const { + return initialized_; + } + + void Init() { + if (access(PATH_AUL_SOCKET_TIMEOUT, F_OK) != 0) { + initialized_ = true; + return; + } + + double timeout = 5.2f; + int ret = vconf_get_dbl(VCONFKEY_AUL_SOCKET_TIMEOUT, &timeout); + if (ret != VCONF_OK) + _E("vconf_get_dbl() is failed. error(%d)", ret); + + ret = vconf_notify_key_changed(VCONFKEY_AUL_SOCKET_TIMEOUT, VconfCb, this); + if (ret != VCONF_OK) + _E("vconf_notify_key_changed() is failed. error(%d)", ret); + + SetTimeout(timeout); + initialized_ = true; + } + + private: + static void VconfCb(keynode_t* node, void* user_data) { + auto* h = static_cast(user_data); + double timeout = vconf_keynode_get_dbl(node); + h->SetTimeout(timeout); + } + + void SetTimeout(double timeout) { + char buf[12]; + snprintf(buf, sizeof(buf), "%.3f", timeout); + gchar* ptr = nullptr; + time_val_.tv_sec = g_ascii_strtoll(buf, &ptr, 10); + time_val_.tv_usec = g_ascii_strtoll(ptr + 1, &ptr, 10) * 1000; + } + + private: + bool initialized_ = false; + timeval time_val_ = { 5, 200 * 1000 }; +}; + +std::string GetSocketPath(pid_t pid, uid_t uid) { + if (pid == -2) + return std::string(PATH_AMD_SOCK); + + char path[108]; + if (uid < REGULAR_UID_MIN) + snprintf(path, sizeof(path), "/run/aul/daemons/%u/.app-sock-%d", uid, pid); + else + snprintf(path, sizeof(path), "/run/aul/apps/%u/%d/.app-sock", uid, pid); + + return std::string(path); +} + +int CreateSocketDirectory(pid_t pid, uid_t uid) { + std::string socket_path = GetSocketPath(pid, uid); + std::string path = dirname(const_cast(socket_path.c_str())); + if (mkdir(path.c_str(), 0700) != 0) { + if (errno == EEXIST) { + if (access(path.c_str(), R_OK) != 0) { + _E("access() is failed. path(%s), errno(%d)", path.c_str(), errno); + return -1; + } + } else { + _E("mkdir() is failed. path(%s), errno(%d)", path.c_str(), errno); + return -1; + } + } + + return 0; +} + +class SocketLink { + public: + SocketLink() = default; + + void Create(const std::string& socket_path, pid_t pid, uid_t uid) { + if (CreateSocketDirectory(pid, uid) != 0) + return; + + path_ = GetSocketPath(pid, uid); + if (link(socket_path.c_str(), path_.c_str()) != 0) { + _E("link() is failed. old(%s), new(%s), errno(%d)", + socket_path.c_str(), path_.c_str(), errno); + } + + created_ = true; + } + + void Delete() { + if (!created_) + return; + + unlink(path_.c_str()); + created_ = false; + } + + private: + bool created_ = false; + std::string path_; +}; + +int SendAndReceive(ClientSocket* client, int cmd, unsigned char* data, + int datalen, int opt) { + _D("fd(%d), cmd(%d)", client->GetFd(), cmd); + + Packet packet(cmd, opt, { data, data + datalen }); + tizen_base::Parcel parcel; + parcel.WriteParcelable(packet); + auto raw = parcel.GetRaw(); + + int ret = client->Send(reinterpret_cast(&raw[0]), raw.size()); + if (ret != 0 || opt & AUL_SOCK_NOREPLY) { + if (opt & AUL_SOCK_ASYNC) + client->RemoveFd(); + + return ret; + } + + if (opt & AUL_SOCK_ASYNC) + return client->RemoveFd(); + + int res; + ret = client->Receive(reinterpret_cast(&res), sizeof(res)); + if (ret < 0) + res = ret; + + return res; +} + +int SendAndReceive(int fd, int cmd, unsigned char* data, int datalen, + int opt) { + ClientSocket client(fd); + return SendAndReceive(&client, cmd, data, datalen, opt); +} + +int SendAndReceive(int pid, uid_t uid, int cmd, unsigned char* data, + int datalen, int opt) { + int ret; + try { + ClientSocket client; + std::string endpoint = GetSocketPath(pid, uid); + client.Connect(endpoint); + aul_sock_set_sock_option(client.GetFd(), 1); + + ret = SendAndReceive(&client, cmd, data, datalen, opt); + } catch (const Exception& e) { + _E("Exception occurs. error(%d)", e.GetErrorCode()); + return e.GetErrorCode(); + } + + return ret; +} + +int ReceiveAppPacket(ClientSocket* client, app_pkt_t** out_pkt) { + PacketHeader header; + static_assert(std::is_standard_layout(), + "PacketHeader should be POD type"); + static_assert(std::is_trivial(), + "Header should be POD type"); + + *out_pkt = nullptr; + int ret = client->Receive(&header, sizeof(header)); + if (ret < 0) + return ret; + + app_pkt_t* pkt = static_cast( + calloc(1, sizeof(app_pkt_t) + header.len)); + if (pkt == nullptr) { + _E("Out of memory"); + return -ENOMEM; + } + + pkt->cmd = header.cmd; + pkt->len = header.len; + pkt->opt = header.opt; + + ret = client->Receive(pkt->data, pkt->len); + if (ret < 0) { + free(pkt); + return ret; + } + + *out_pkt = pkt; + return 0; +} + +int GetFileDescriptor(struct cmsghdr* cmsg, struct msghdr* msg, int* fds, + int maxdesc) { + if (cmsg == nullptr || msg == nullptr) + return 0; + + if (cmsg->cmsg_type != SCM_RIGHTS) + return 0; + + int retnr = 0; + if (msg->msg_controllen > 0) { + int payload = cmsg->cmsg_len - sizeof(*cmsg); + int* recvdesc = reinterpret_cast(CMSG_DATA(cmsg)); + int nrdesc = payload / sizeof(int); + retnr = nrdesc < maxdesc ? nrdesc : maxdesc; + for (int i = 0; i < nrdesc; ++i) { + if (maxdesc-- > 0) + *fds++ = *recvdesc++; + else + close(*recvdesc++); + } + } + + return retnr; +} + +int ReceiveMessage(int fd, struct iovec* vec, int vec_max_size, int* vec_size, + int* fds, int* nr_fds) { + if (vec == nullptr || vec_max_size < 1 || vec_size == nullptr) + return -EINVAL; + + char buf[CMSG_SPACE(sizeof(int) * MAX_NR_OF_DESCRIPTORS) + CMSG_SPACE(50)] = { 0, }; + struct msghdr msg = { 0, }; + msg.msg_iov = vec; + msg.msg_iovlen = vec_max_size; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + bool is_blocking; + if (fcntl(fd, F_GETFL, 0) & O_NONBLOCK) + is_blocking = false; + else + is_blocking = true; + +retry: + int ret = recvmsg(fd, &msg, 0); + if (ret == 0) { + _W("Socket was disconnected. fd(%d)", fd); + return -ECOMM; + } + + if (ret < 0) { + if (errno == EINTR || errno == EAGAIN) { + if (is_blocking && errno == EAGAIN) { + _E("recvmsg() timed out. fd(%d)", fd); + return -EAGAIN; + } + + usleep(100 * 1000); + goto retry; + } + + ret = -errno; + _E("recvmsg() is failed. fd(%d), errno(%d)", fd, errno); + return ret; + } + + *vec_size = msg.msg_iovlen; + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == nullptr) { + if (nr_fds != nullptr) + *nr_fds = 0; + } else { + for (int i = 0; cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg), ++i) { + if (cmsg->cmsg_type == SCM_RIGHTS) { + if (fds != nullptr) { + int fdnum = GetFileDescriptor(cmsg, &msg, fds, MAX_NR_OF_DESCRIPTORS); + if (nr_fds != nullptr) + *nr_fds = fdnum; + } + } + } + } + + return 0; +} + +void DeleteDirectories(const std::string& path) { + if (path.empty()) + return; + + DIR* dp = opendir(path.c_str()); + if (dp == nullptr) + return; + + struct stat statbuf; + struct dirent* dentry = nullptr; + while ((dentry = readdir(dp)) != nullptr) { + if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) + continue; + + std::string entry = path + dentry->d_name; + if (stat(entry.c_str(), &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) + DeleteDirectories(entry); + else + unlink(entry.c_str()); + } + } + + rmdir(path.c_str()); + closedir(dp); +} + +SocketTimeout socket_timeout; +SocketLink socket_link; + +} // namespace + +extern "C" API struct timeval aul_sock_get_rcv_timeout(void) { + return socket_timeout.GetTimeval(); +} + +extern "C" API int aul_sock_set_sock_option(int fd, int cli) { + int size = AUL_SOCK_MAXBUFF; + int ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); + if (ret != 0) + return ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + if (ret != 0) + return ret; + + if (cli) { + if (TIZEN_FEATURE_SOCKET_TIMEOUT && !socket_timeout.IsInitialized()) + socket_timeout.Init(); + + auto timeout = socket_timeout.GetTimeval(); + ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + if (ret != 0) + return ret; + } + + return 0; +} + +extern "C" API int aul_sock_create_server(int pid, uid_t uid) { + auto path = GetSocketPath(pid, uid); + int fd = -1; + try { + if (CreateSocketDirectory(pid, uid) != 0) + return -1; + + ServerSocket socket; + socket.Bind(path); + aul_sock_set_sock_option(socket.GetFd(), 0); + socket.Listen(128); + fd = socket.RemoveFd(); + } catch (const Exception& e) { + _E("Exception occurs. error(%d)", e.GetErrorCode()); + return e.GetErrorCode(); + } + + if (pid > 0) { + pid_t aul_pid; + const char* aul_pid_str = getenv("AUL_PID"); + if (aul_pid_str != nullptr && isdigit(aul_pid_str[0])) + aul_pid = atoi(aul_pid_str); + else + aul_pid = -1; + + if (aul_pid > 1 && aul_pid != pid) + socket_link.Create(path.c_str(), aul_pid, uid); + } + + return fd; +} + +extern "C" API int aul_sock_send_raw_with_fd(int fd, int cmd, + unsigned char* kb_data, int datalen, int opt) { + return SendAndReceive(fd, cmd, kb_data, datalen, opt); +} + +extern "C" API int aul_sock_send_bundle_with_fd(int fd, int cmd, bundle* kb, + int opt) { + if (kb == nullptr) + return -EINVAL; + + tizen_base::Bundle b(kb, false, false); + auto raw = b.ToRaw(); + return SendAndReceive(fd, cmd, + reinterpret_cast(raw.first.get()), + raw.second, opt | AUL_SOCK_BUNDLE); +} + +extern "C" API int aul_sock_send_raw(int pid, uid_t uid, int cmd, + unsigned char* kb_data, int datalen, int opt) { + return SendAndReceive(pid, uid, cmd, kb_data, datalen, opt); +} + +extern "C" API int aul_sock_send_bundle(int pid, uid_t uid, int cmd, + bundle* kb, int opt) { + if (kb == nullptr) + return -EINVAL; + + tizen_base::Bundle b(kb, false, false); + auto raw = b.ToRaw(); + return SendAndReceive(pid, uid, cmd, + reinterpret_cast(raw.first.get()), raw.second, + opt | AUL_SOCK_BUNDLE); +} + +extern "C" API app_pkt_t* aul_sock_recv_pkt(int fd, int* clifd, + struct ucred* cred) { + ServerSocket server(fd); + auto client = std::unique_ptr(server.Accept()); + server.RemoveFd(); + if (client.get() == nullptr) + return nullptr; + + socklen_t optlen = sizeof(struct ucred); + int ret = getsockopt(client->GetFd(), SOL_SOCKET, SO_PEERCRED, + cred, &optlen); + if (ret < 0) { + _E("getsockopt() is failed. errno(%d)", errno); + return nullptr; + } + + aul_sock_set_sock_option(client->GetFd(), 1); + + app_pkt_t* pkt = nullptr; + ret = ReceiveAppPacket(client.get(), &pkt); + if (ret < 0) + return nullptr; + + *clifd = client->RemoveFd(); + return pkt; +} + +extern "C" API int aul_sock_recv_reply_pkt(int fd, app_pkt_t** ret_pkt) { + return aul_sock_recv_reply_pkt_v2(fd, ret_pkt, true); +} + +extern "C" API int aul_sock_recv_reply_sock_fd(int fd, int (*ret_fd)[2], + int fd_size) { + int fds[2] = { -1, -1 }; + int vec_len = 0; + int fds_len = 0; + char recv_buff[1024] = { 0, };; + struct iovec vec[3] = { 0, }; + vec[0].iov_base = recv_buff; + vec[0].iov_len = sizeof(recv_buff); + int ret = ReceiveMessage(fd, vec, 1, &vec_len, fds, &fds_len); + if (ret < 0) { + _E("ReceiveMessage() is failed. error(%d)", ret); + if (fds_len > 0) + close(fds[0]); + + ret = -ECOMM; + } else if (fds_len == fd_size && fds_len == 2) { + (*ret_fd)[0] = fds[0]; + (*ret_fd)[1] = fds[1]; + } else if (fds_len == fd_size && fds_len == 1) { + (*ret_fd)[0] = fds[0]; + } else { + _E("Wrong number of FD received. Expected: %d, Actual: %d", + fd_size, fds_len); + ret = -ECOMM; + } + + close(fd); + return ret; +} + +extern "C" API int aul_sock_create_launchpad_client(const char* pad_type, + uid_t uid) { + int fd = -1; + try { + ClientSocket client; + std::string endpoint = "/run/aul/daemons/" + std::to_string(uid) + "/" + + std::string(pad_type); + client.Connect(endpoint); + fd = client.RemoveFd(); + aul_sock_set_sock_option(fd, 1); + } catch (const Exception& e) { + _E("Exception occurs. error(%d)", e.GetErrorCode()); + return e.GetErrorCode(); + } + + return fd; +} + +extern "C" API int aul_sock_recv_pkt_with_cb(int fd, + void (*callback)(app_pkt_t* pkt, void* user_data), + void* user_data) { + if (fd < 0 || fd >= MAX_FDS) { + _E("Invalid parameter"); + return -1; + } + + if (callback == nullptr) { + _E("Invalid parameter"); + close(fd); + return -1; + } + + ClientSocket client(fd); + int count = 0; + int ret = client.Receive(&count, sizeof(count)); + if (ret != 0) { + _E("Receive() is failed. error(%d)", ret); + return ret; + } + + if (count <= 0 || count > MAX_RUNNING_INSTANCE) { + _E("Error occurs. count(%d)", count); + return -ECOMM; + } + + app_pkt_t** pkt = reinterpret_cast( + calloc(count, sizeof(app_pkt_t*))); + if (pkt == nullptr) { + _E("Out of memory"); + return -ENOMEM; + } + + for (int i = 0; i < count; ++i) { + ret = ReceiveAppPacket(&client, &pkt[i]); + if (ret != 0) { + _E("ReceiveAppPacket() is failed. error(%d)", ret); + break; + } + } + + for (int i = 0; i < count; ++i) { + if (pkt[i] != nullptr) { + callback(pkt[i], user_data); + free(pkt[i]); + } + } + + free(pkt); + return ret; +} + +extern "C" API int aul_sock_recv_result_with_fd(int fd) { + ClientSocket client(fd); + int res; + int ret = client.Receive(&res, sizeof(res)); + client.RemoveFd(); + if (ret < 0) { + _E("Receive() is failed. fd(%d), error(%d)", fd, ret); + res = -ECOMM; + } + + return res; +} + +extern "C" API int aul_sock_destroy_server(int fd) { + if (fd > -1) + close(fd); + + if (getuid() < REGULAR_UID_MIN) { + std::string path = "/run/aul/daemons/" + std::to_string(getuid()) + + "/.app-sock-" + std::to_string(getpid()); + unlink(path.c_str()); + } else { + std::string path = "/run/aul/apps/" + std::to_string(getuid()) + "/" + + std::to_string(getpid()); + DeleteDirectories(path); + } + socket_link.Delete(); + + return 0; +} + +extern "C" API int aul_sock_send_result(int fd, int res) { + return aul_sock_send_result_v2(fd, res, true); +} + +extern "C" API int aul_sock_send_result_v2(int fd, int res, bool do_close) { + if (fd < 0 || fd >= MAX_FDS) { + _E("Invalid parameter"); + return -EINVAL; + } + + int ret = send(fd, &res, sizeof(res), MSG_NOSIGNAL); + if (ret < 0) + _E("send() is failed. fd(%d), errno(%d)", fd, errno); + + if (do_close) + close(fd); + + return ret; +} + +extern "C" API int aul_sock_recv_reply_pkt_v2(int fd, app_pkt_t** pkt, + bool do_close) { + if (fd < 0 || fd >= MAX_FDS) + return -EINVAL; + + if (pkt == nullptr) { + if (do_close) + close(fd); + + return -EINVAL; + } + + ClientSocket client(fd); + int ret = ReceiveAppPacket(&client, pkt); + if (!do_close) + client.RemoveFd(); + + return ret; +} diff --git a/test/unit_tests/main.cc b/test/unit_tests/main.cc index b3b4e12..193fd2a 100644 --- a/test/unit_tests/main.cc +++ b/test/unit_tests/main.cc @@ -5,19 +5,16 @@ #include #include -//#define LOG_INTERNAL - #include -extern "C" int __dlog_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...) { -#ifdef LOG_INTERNAL +extern "C" int __dlog_print(log_id_t log_id, int prio, const char* tag, + const char* fmt, ...) { printf("%s:", tag); va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); -#endif return 0; } -- 2.7.4