Set client socket timeout from env if exists
[platform/core/appfw/pkgmgr-info.git] / src / common / socket / client_socket.cc
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "client_socket.hh"
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
23 #include <sys/un.h>
24 #include <unistd.h>
25
26 #include "utils/logging.hh"
27
28 #include "pkgmgrinfo_debug.h"
29
30 namespace {
31
32 bool IsDBWriteRequest(pkgmgr_common::ReqType req_type) {
33   if (req_type != pkgmgr_common::ReqType::SET_PKG_INFO &&
34       req_type != pkgmgr_common::ReqType::SET_CERT_INFO &&
35       req_type != pkgmgr_common::ReqType::WRITE_QUERY)
36     return false;
37
38   return true;
39 }
40
41 }
42
43 namespace pkgmgr_common {
44 namespace socket {
45
46 ClientSocket::ClientSocket(std::string path)
47     : AbstractSocket(std::move(path)) {}
48
49 void ClientSocket::SetTimeout(int timeout_msec) {
50   if (timeout_msec == -1)
51     timeout_msec = 5000;
52
53   if (timeout_msec < 0) {
54     LOG(ERROR) << "Invalid timeout_msec parameter";
55     return;
56   }
57
58   struct timeval timeout = {
59       .tv_sec = static_cast<time_t>(timeout_msec / 1000),
60       .tv_usec = static_cast<suseconds_t>((timeout_msec % 1000) * 1000)};
61
62   if (setsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)
63     LOG(ERROR) << "setsockopt() is failed. fd: " << fd_
64         << ", errno: " << errno;
65 }
66
67 int ClientSocket::GetTimeoutFromEnv() {
68   const char* timeout_str = getenv("PKGMGR_SOCKET_TIMEOUT");
69   if (timeout_str == nullptr)
70     return -1;
71
72   int timeout_msec = atoi(timeout_str);
73   if (timeout_msec <= 0)
74     return -1;
75
76   return timeout_msec;
77 }
78
79 bool ClientSocket::Connect(ReqType req_type) {
80   if (Create() < 0)
81     return false;
82
83   int timeout = GetTimeoutFromEnv();
84   if (timeout <= 0)
85     timeout = IsDBWriteRequest(req_type) ? 60 * 1000 : 5 * 1000;
86   SetTimeout(timeout);
87
88   int retry_cnt = 3;
89   do {
90     int ret = TryConnection();
91     if (ret == 0) {
92       break;
93     } else if (ret < -1) {
94       LOG(ERROR) << "Maybe peer not launched or peer dead. path: " << GetPath()
95           << ", fd: " << GetFd();
96
97       // If requester is root, don't wait
98       if (getuid() == 0)
99         return false;
100
101       usleep(100 * 1000);
102       --retry_cnt;
103     } else if (ret < 0) {
104       LOG(ERROR) << "Failed to connect to socket: " << GetPath()
105           << ", fd: " << GetFd();
106       return false;
107     }
108   } while (retry_cnt > 0);
109
110   return (retry_cnt > 0);
111 }
112
113 int ClientSocket::TryConnection() {
114   int flags = fcntl(fd_, F_GETFL, 0);
115
116   if (fcntl(fd_, F_SETFL, flags | O_NONBLOCK) != 0) {
117     LOG(ERROR) << "Failed to set flags: " << (flags | O_NONBLOCK) << " on fd: "
118         << fd_ << ", errno: " << errno;
119     return -1;
120   }
121
122   int ret =
123       connect(fd_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_));
124   if (fcntl(fd_, F_SETFL, flags) != 0) {
125     LOG(ERROR) << "Failed to set flags: " << flags << " on fd: " << fd_
126         << ", errno: " <<  errno;
127     return -1;
128   }
129   if (ret < 0) {
130     if (errno != EAGAIN && errno != EINPROGRESS)
131       return -2;
132   } else if (ret == 0) {
133     SetOption();
134     return 0;
135   }
136
137   fd_set readfds;
138   FD_ZERO(&readfds);
139   FD_SET(fd_, &readfds);
140   fd_set writefds = readfds;
141   struct timeval timeout = {0, 100 * 1000};
142   ret = select(fd_ + 1, &readfds, &writefds, NULL, &timeout);
143   if (ret == 0) {
144     errno = ETIMEDOUT;
145     return -1;
146   }
147
148   if (FD_ISSET(fd_, &readfds) || FD_ISSET(fd_, &writefds)) {
149     int error = 0;
150     socklen_t len = sizeof(error);
151     if (getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
152       return -1;
153   }
154
155   return -1;
156 }
157
158 }  // namespace socket
159 }  // namespace pkgmgr_common