Increase timeout of client socket
[platform/core/appfw/pkgmgr-info.git] / src / common / socket / client_socket.cc
index 7e79959..e60015b 100644 (file)
  * limitations under the License.
  */
 
+#include "client_socket.hh"
+
+#include <errno.h>
+#include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
+#include <unistd.h>
 
-#include <algorithm>
-#include <string>
+#include "utils/logging.hh"
 
-#include "client_socket.hh"
+#include "pkgmgrinfo_debug.h"
+
+namespace {
+
+bool IsDBWriteRequest(pkgmgr_common::ReqType req_type) {
+  if (req_type != pkgmgr_common::ReqType::SET_PKG_INFO &&
+      req_type != pkgmgr_common::ReqType::SET_CERT_INFO &&
+      req_type != pkgmgr_common::ReqType::WRITE_QUERY)
+    return false;
+
+  return true;
+}
+
+}
 
 namespace pkgmgr_common {
 namespace socket {
 
-ClientSocket::ClientSocket(std::string path) : AbstractSocket(std::move(path)) {
-  /* TODO implement code */
+ClientSocket::ClientSocket(std::string path)
+    : AbstractSocket(std::move(path)) {}
+
+void ClientSocket::SetTimeout(int timeout_msec) {
+  if (timeout_msec == -1)
+    timeout_msec = 5000;
+
+  if (timeout_msec < 0) {
+    LOG(ERROR) << "Invalid timeout_msec parameter";
+    return;
+  }
+
+  struct timeval timeout = {
+      .tv_sec = static_cast<time_t>(timeout_msec / 1000),
+      .tv_usec = static_cast<suseconds_t>((timeout_msec % 1000) * 1000)};
+
+  if (setsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)
+    LOG(ERROR) << "setsockopt() is failed. fd: " << fd_
+        << ", errno: " << errno;
 }
 
-ClientSocket::ClientSocket(int fd) : AbstractSocket(fd) {
-  /* TODO implement code */
+bool ClientSocket::Connect(ReqType req_type) {
+  if (Create() < 0)
+    return false;
+
+  SetTimeout(IsDBWriteRequest(req_type) ? 60 * 1000 : 30 * 1000);
+
+  int retry_cnt = 3;
+  do {
+    int ret = TryConnection();
+    if (ret == 0) {
+      break;
+    } else if (ret < -1) {
+      LOG(ERROR) << "Maybe peer not launched or peer dead. path: " << GetPath()
+          << ", fd: " << GetFd();
+
+      // If requester is root, don't wait
+      if (getuid() == 0)
+        return false;
+
+      usleep(100 * 1000);
+      --retry_cnt;
+    } else if (ret < 0) {
+      LOG(ERROR) << "Failed to connect to socket: " << GetPath()
+          << ", fd: " << GetFd();
+      return false;
+    }
+  } while (retry_cnt > 0);
+
+  return (retry_cnt > 0);
 }
 
-ClientSocket::~ClientSocket() { /* TODO implement code */ }
+int ClientSocket::TryConnection() {
+  int flags = fcntl(fd_, F_GETFL, 0);
+
+  if (fcntl(fd_, F_SETFL, flags | O_NONBLOCK) != 0) {
+    LOG(ERROR) << "Failed to set flags: " << (flags | O_NONBLOCK) << " on fd: "
+        << fd_ << ", errno: " << errno;
+    return -1;
+  }
+
+  int ret =
+      connect(fd_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_));
+  if (fcntl(fd_, F_SETFL, flags) != 0) {
+    LOG(ERROR) << "Failed to set flags: " << flags << " on fd: " << fd_
+        << ", errno: " <<  errno;
+    return -1;
+  }
+  if (ret < 0) {
+    if (errno != EAGAIN && errno != EINPROGRESS)
+      return -2;
+  } else if (ret == 0) {
+    SetOption();
+    return 0;
+  }
+
+  fd_set readfds;
+  FD_ZERO(&readfds);
+  FD_SET(fd_, &readfds);
+  fd_set writefds = readfds;
+  struct timeval timeout = {0, 100 * 1000};
+  ret = select(fd_ + 1, &readfds, &writefds, NULL, &timeout);
+  if (ret == 0) {
+    errno = ETIMEDOUT;
+    return -1;
+  }
+
+  if (FD_ISSET(fd_, &readfds) || FD_ISSET(fd_, &writefds)) {
+    int error = 0;
+    socklen_t len = sizeof(error);
+    if (getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+      return -1;
+  }
 
-int ClientSocket::Connect() {
-  /* TODO implement code */
-  return 0;
+  return -1;
 }
 
 }  // namespace socket