1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "lmks_client.h"
17
18 #include <cerrno>
19 #include <memory>
20 #include <string>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <sys/un.h>
24 #include <thread>
25 #include <unistd.h>
26
27 #include "app_log_wrapper.h"
28 #include "event_handler.h"
29 #include "securec.h"
30 #include "unique_fd.h"
31
32 namespace OHOS {
33 namespace AppExecFwk {
34 namespace {
35 constexpr int LMKS_SOCKET_TIMEOUT = 3;
36
37 constexpr size_t LMKS_START_VALUE = 1;
38 constexpr size_t LMKS_MULTIPLE = 2;
39 constexpr size_t LMKS_MAX_TARGETS = 6;
40
41 constexpr int LMKS_CMD_TARGET = 0;
42 constexpr int LMKS_CMD_PROCPRIO = 1;
43 constexpr int LMKS_CMD_PROCREMOVE = 2;
44 constexpr int LMKS_CMD_PROCPURGE = 3;
45
46 constexpr int APP_OOM_ADJ_MIN = -1000;
47 constexpr int APP_OOM_ADJ_MAX = 1000;
48 constexpr int LMKS_SOCKET_PATH_MAX = 108;
49
50 constexpr std::string_view LMKS_SOCKET_PATH("/dev/socket/lmks");
51 static_assert(LMKS_SOCKET_PATH.size() < LMKS_SOCKET_PATH_MAX);
52 } // namespace
53
LmksClient()54 LmksClient::LmksClient() : socket_(-1)
55 {}
56
~LmksClient()57 LmksClient::~LmksClient()
58 {
59 if (IsOpen()) {
60 Close();
61 }
62 }
63
Open()64 int32_t LmksClient::Open()
65 {
66 APP_LOGI("%{public}s(%{public}d) connecting lmks.", __func__, __LINE__);
67
68 if (socket_ >= 0) {
69 APP_LOGE("%{public}s(%{public}d) already connected.", __func__, __LINE__);
70 return -1;
71 }
72
73 UniqueFd sk(socket(PF_LOCAL, SOCK_SEQPACKET, 0));
74 if (sk.Get() < 0) {
75 APP_LOGE("%{public}s(%{public}d) failed to create local socket %{public}d.", __func__, __LINE__, errno);
76 return (-errno);
77 }
78
79 struct timeval timeOut = {.tv_sec = LMKS_SOCKET_TIMEOUT, .tv_usec = 0};
80 int fd = sk.Get();
81 if (fd < 0) {
82 APP_LOGE("%{public}s(%{public}d) fd is negative.", __func__, __LINE__);
83 return -1;
84 }
85 if ((setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeOut, sizeof(timeOut)) != 0) ||
86 (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeOut, sizeof(timeOut)) != 0)) {
87 APP_LOGE("%{public}s(%{public}d) failed to set local socket timeout %{public}s.",
88 __func__,
89 __LINE__,
90 strerror(errno));
91 return (-errno);
92 }
93
94 struct sockaddr_un addr;
95 int32_t ret;
96 ret = memset_s(&addr, sizeof(addr), 0, sizeof(addr));
97 if (ret != EOK) {
98 APP_LOGE("%{public}s(%{public}d) failed to clear local socket addr.", __func__, __LINE__);
99 return ret;
100 }
101
102 ret = memcpy_s(addr.sun_path, LMKS_SOCKET_PATH_MAX, LMKS_SOCKET_PATH.data(), LMKS_SOCKET_PATH.size());
103 if (ret != EOK) {
104 APP_LOGE("%{public}s(%{public}d) failed to make local socket path.", __func__, __LINE__);
105 return ret;
106 }
107
108 addr.sun_family = AF_LOCAL;
109 socklen_t addrLen = offsetof(struct sockaddr_un, sun_path) + LMKS_SOCKET_PATH.size() + 1;
110 if (connect(sk, reinterpret_cast<struct sockaddr *>(&addr), addrLen) < 0) {
111 APP_LOGE("%{public}s(%{public}d) failed to connect to lmks %{public}s.", __func__, __LINE__, strerror(errno));
112 return (-errno);
113 }
114
115 socket_ = sk.Release();
116
117 return ERR_OK;
118 }
119
Close()120 void LmksClient::Close()
121 {
122 APP_LOGI("%{public}s(%{public}d) closing lmks.", __func__, __LINE__);
123
124 if (socket_ < 0) {
125 APP_LOGE("%{public}s(%{public}d) not connected.", __func__, __LINE__);
126 return;
127 }
128
129 close(socket_);
130 socket_ = -1;
131 }
132
IsOpen() const133 bool LmksClient::IsOpen() const
134 {
135 return socket_ >= 0;
136 }
137
Target(const Targets & targets)138 int32_t LmksClient::Target(const Targets &targets)
139 {
140 APP_LOGI("Target enter");
141
142 if (targets.empty() || targets.size() > LMKS_MAX_TARGETS) {
143 APP_LOGE(
144 "%{public}s(%{public}d) empty target or too many targets. %{public}zu", __func__, __LINE__, targets.size());
145 return (-EINVAL);
146 }
147
148 int i = 0;
149 int32_t buf[LMKS_START_VALUE + LMKS_MULTIPLE * LMKS_MAX_TARGETS];
150 buf[i++] = LMKS_CMD_TARGET;
151
152 for (auto target : targets) {
153 if (target.first < 0 || !CheckOomAdj(target.second)) {
154 APP_LOGE("%{public}s(%{public}d) invalid target: %{public}d %{public}d",
155 __func__,
156 __LINE__,
157 target.first,
158 target.second);
159 return (-EINVAL);
160 }
161 buf[i++] = target.first;
162 buf[i++] = target.second;
163 }
164
165 return Write(buf, i * sizeof(int32_t)) ? ERR_OK : -1;
166 }
167
ProcPrio(pid_t pid,uid_t uid,int oomAdj)168 int32_t LmksClient::ProcPrio(pid_t pid, uid_t uid, int oomAdj)
169 {
170 APP_LOGI("ProcPrio enter");
171
172 if (pid < 0 || uid < 0 || !CheckOomAdj(oomAdj)) {
173 APP_LOGE("%{public}s(%{public}d) invalid parameter: %{public}d %{public}d %{public}d.",
174 __func__,
175 __LINE__,
176 pid,
177 uid,
178 oomAdj);
179 return (-EINVAL);
180 }
181
182 int32_t buf[4] = {LMKS_CMD_PROCPRIO, pid, uid, oomAdj};
183
184 return Write(buf, sizeof(buf)) ? ERR_OK : -1;
185 }
186
ProcRemove(pid_t pid)187 int32_t LmksClient::ProcRemove(pid_t pid)
188 {
189 APP_LOGI("ProcRemove enter");
190
191 if (pid < 1) {
192 APP_LOGE("%{public}s(%{public}d) invalid pid %{public}d.", __func__, __LINE__, pid);
193 return (-EINVAL);
194 }
195
196 int32_t buf[2] = {LMKS_CMD_PROCREMOVE, pid};
197 if (!Write(buf, sizeof(buf))) {
198 APP_LOGE("failed to write");
199 return -1;
200 }
201
202 LmksClientMsg msg;
203 if (!Read(msg.resultBuf, sizeof(int32_t))) {
204 APP_LOGE("failed to read");
205 return -1;
206 }
207
208 if (msg.result != 0) {
209 APP_LOGE("failed to remove process");
210 return -1;
211 }
212
213 APP_LOGI("success to remove process");
214 return 0;
215 }
216
ProcPurge()217 bool LmksClient::ProcPurge()
218 {
219 APP_LOGI("ProcPurge enter");
220
221 int32_t cmd = LMKS_CMD_PROCPURGE;
222
223 return Write(reinterpret_cast<void *>(&cmd), sizeof(cmd));
224 }
225
CheckOomAdj(int v)226 bool LmksClient::CheckOomAdj(int v)
227 {
228 return (APP_OOM_ADJ_MIN <= v && v <= APP_OOM_ADJ_MAX);
229 }
230
Write(const void * buf,size_t len)231 bool LmksClient::Write(const void *buf, size_t len)
232 {
233 if (buf == nullptr || len < 1) {
234 APP_LOGE("%{public}s(%{public}d) invalid parameter.", __func__, __LINE__);
235 return false;
236 }
237
238 constexpr int retryTimes = 5;
239 for (int i = 0; i < retryTimes; ++i) {
240 if (socket_ < 0 && !Open()) {
241 std::this_thread::yield();
242 continue;
243 }
244 int rLen = TEMP_FAILURE_RETRY(send(socket_, buf, len, 0));
245 if (rLen <= 0) {
246 APP_LOGE("failed to send data to lmks err %{public}s.", strerror(errno));
247 Close();
248 std::this_thread::yield();
249 } else {
250 return true;
251 }
252 }
253
254 return false;
255 }
256
Read(void * buf,size_t len)257 bool LmksClient::Read(void *buf, size_t len)
258 {
259 if (buf == nullptr || len < 1) {
260 APP_LOGE("invalid parameter. len %zu", len);
261 return false;
262 }
263
264 constexpr int retryTimes = 5;
265 for (int i = 0; i < retryTimes; ++i) {
266 if (socket_ < 0 && !Open()) {
267 std::this_thread::yield();
268 continue;
269 }
270 int rLen = TEMP_FAILURE_RETRY(recv(socket_, buf, len, 0));
271 if (rLen <= 0) {
272 APP_LOGE("failed to recv data from lmks err %{public}s.", strerror(errno));
273 Close();
274 std::this_thread::yield();
275 } else {
276 return true;
277 }
278 }
279
280 return false;
281 }
282
283 } // namespace AppExecFwk
284 } // namespace OHOS
285