1 /*
2 * Copyright (c) 2024 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 "napi_security_event_querier.h"
17
18 #include <unistd.h>
19
20 #include "security_guard_define.h"
21 #include "security_guard_log.h"
22 #include "napi_request_data_manager.h"
23
24 namespace OHOS::Security::SecurityGuard {
NapiSecurityEventQuerier(QuerySecurityEventContext * context,ON_COMPLETE_FUNC handler)25 NapiSecurityEventQuerier::NapiSecurityEventQuerier(QuerySecurityEventContext *context, ON_COMPLETE_FUNC handler)
26 : callbackContext_(context), onCompleteHandler_(handler) {};
27
~NapiSecurityEventQuerier()28 NapiSecurityEventQuerier::~NapiSecurityEventQuerier()
29 {
30 if (callbackContext_->threadId == getproctid()) {
31 napi_delete_reference(callbackContext_->env, callbackContext_->ref);
32 }
33 delete callbackContext_;
34 };
35
NapiGetNamedProperty(const napi_env env,const napi_value & object,const std::string & name)36 napi_value NapiSecurityEventQuerier::NapiGetNamedProperty(const napi_env env, const napi_value &object,
37 const std::string &name)
38 {
39 napi_value result = nullptr;
40 napi_status status = napi_get_named_property(env, object, name.c_str(), &result);
41 if (status != napi_ok || result == nullptr) {
42 SGLOGE("failed to parse property named %{public}s from JS object.", name.c_str());
43 }
44 return result;
45 }
46
NapiCreateString(const napi_env env,const std::string & value)47 napi_value NapiSecurityEventQuerier::NapiCreateString(const napi_env env, const std::string &value)
48 {
49 napi_value result = nullptr;
50 napi_status status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
51 SGLOGD("create napi value of string type, value is %{public}s.", value.c_str());
52 if (status != napi_ok || result == nullptr) {
53 SGLOGE("failed to create napi value of string type.");
54 }
55 return result;
56 }
57
NapiCreateInt64(const napi_env env,int64_t value)58 napi_value NapiSecurityEventQuerier::NapiCreateInt64(const napi_env env, int64_t value)
59 {
60 napi_value result = nullptr;
61 napi_status status = napi_create_int64(env, value, &result);
62 SGLOGI("create napi value of int64 type, value is %{public}" PRId64 ".", value);
63 if (status != napi_ok || result == nullptr) {
64 SGLOGE("failed to create napi value of int64 type.");
65 }
66 return result;
67 }
68
DestoryWork(uv_work_t * work)69 void NapiSecurityEventQuerier::DestoryWork(uv_work_t *work)
70 {
71 if (work == nullptr) {
72 return;
73 }
74 if (work->data != nullptr) {
75 delete (reinterpret_cast<QuerySecurityEventContext *>(work->data));
76 }
77 delete work;
78 }
79
OnExecute(uv_work_t * work)80 static void OnExecute(uv_work_t *work)
81 {
82 SGLOGD("begin executr work error");
83 }
84
RunCallback(QuerySecurityEventContext * context,CALLBACK_FUNC callback,RELEASE_FUNC release)85 void NapiSecurityEventQuerier::RunCallback(QuerySecurityEventContext *context, CALLBACK_FUNC callback,
86 RELEASE_FUNC release)
87 {
88 uv_loop_t* loop = nullptr;
89 napi_get_uv_event_loop(context->env, &loop);
90 if (loop == nullptr) {
91 SGLOGE("failed to get uv_loop.");
92 return;
93 }
94
95 QuerySecurityEventContext *tmpContext = new QuerySecurityEventContext(context);
96 tmpContext->callback = callback;
97 tmpContext->release = release;
98 uv_work_t* work = new (std::nothrow) uv_work_t();
99 if (work == nullptr) {
100 SGLOGE("uv_work new failed, no memory left.");
101 delete tmpContext;
102 return;
103 }
104 work->data = reinterpret_cast<void *>(tmpContext);
105 uv_queue_work_with_qos(
106 loop,
107 work,
108 OnExecute,
109 [] (uv_work_t *work, int status) {
110 SGLOGD("Begin uv work.");
111 if (work == nullptr || work->data == nullptr) {
112 DestoryWork(work);
113 return;
114 }
115 QuerySecurityEventContext *context = reinterpret_cast<QuerySecurityEventContext*>(work->data);
116 if (context == nullptr || context->env == nullptr) {
117 DestoryWork(work);
118 return;
119 }
120 napi_handle_scope scope = nullptr;
121 napi_open_handle_scope(context->env, &scope);
122 if (scope == nullptr) {
123 DestoryWork(work);
124 return;
125 }
126 if (context->callback != nullptr) {
127 SGLOGD("Begin execute callback.");
128 context->callback(context->env, context->ref, context->threadId, context->events);
129 }
130 napi_close_handle_scope(context->env, scope);
131 if (context->release != nullptr) {
132 context->release(context->threadId);
133 }
134 DestoryWork(work);
135 }, uv_qos_default);
136 }
137
OnQuery(const std::vector<SecurityCollector::SecurityEvent> & events)138 void NapiSecurityEventQuerier::OnQuery(const std::vector<SecurityCollector::SecurityEvent> &events)
139 {
140 SGLOGD("NAPI OnQuery.");
141 callbackContext_->events = events;
142
143 RunCallback(callbackContext_,
144 [this] (const napi_env env, const napi_ref ref, pid_t threadId,
145 const std::vector<SecurityCollector::SecurityEvent> &napiEvents) {
146 SGLOGD("NAPI OnQuery Callback.");
147 if (threadId != getproctid() || !NapiRequestDataManager::GetInstance().GetDataCallback(env)) {
148 return;
149 }
150 napi_value eventJsArray = nullptr;
151 napi_create_array_with_length(env, napiEvents.size(), &eventJsArray);
152 auto len = napiEvents.size();
153 for (size_t i = 0; i < len; i++) {
154 napi_value item = nullptr;
155 napi_status status = napi_create_object(env, &item);
156 if (status != napi_ok) {
157 SGLOGE("napi_create_object failed, %{public}d", status);
158 return;
159 }
160 napi_value eventId = NapiCreateInt64(env, napiEvents[i].GetEventId());
161 napi_value version = NapiCreateString(env, napiEvents[i].GetVersion().c_str());
162 napi_value content = NapiCreateString(env, napiEvents[i].GetContent().c_str());
163 napi_set_named_property(env, item, "eventId", eventId);
164 napi_set_named_property(env, item, "version", version);
165 napi_set_named_property(env, item, "content", content);
166 status = napi_set_element(env, eventJsArray, i, item);
167 if (status != napi_ok) {
168 SGLOGE("napi_set_element failed, %{public}d", status);
169 return;
170 }
171 }
172 napi_value argv[1] = {eventJsArray};
173 napi_value querier = nullptr;
174 napi_get_reference_value(env, ref, &querier);
175 napi_value onQuery = NapiGetNamedProperty(env, querier, ON_QUERY_ATTR);
176 napi_value ret = nullptr;
177 SGLOGD("NAPI begin call OnQuery.");
178 napi_status res = napi_call_function(env, querier, onQuery, 1, argv, &ret);
179 if (res != napi_ok) {
180 SGLOGE("failed to call OnQuery JS function. %{public}d", res);
181 }
182 SGLOGD("NAPI OnQuery Callback END.");
183 }, nullptr);
184 };
185
OnComplete()186 void NapiSecurityEventQuerier::OnComplete()
187 {
188 RunCallback(callbackContext_, [] (const napi_env env, const napi_ref ref, pid_t threadId,
189 const std::vector<SecurityCollector::SecurityEvent> &napiEvents) {
190 SGLOGD("NAPI OnComplete Callback.");
191 napi_value querier = nullptr;
192 napi_get_reference_value(env, ref, &querier);
193 napi_value onComplete = NapiGetNamedProperty(env, querier, ON_COMPLETE_ATTR);
194 napi_value ret = nullptr;
195 napi_status status = napi_call_function(env, querier, onComplete, 0, nullptr, &ret);
196 if (status != napi_ok) {
197 SGLOGE("failed to call onComplete JS function.");
198 }
199 SGLOGD("NAPI OnComplete Callback END.");
200 }, [this] (pid_t threadId) {
201 SGLOGD("NAPI OnComplete Release.");
202 if (threadId != getproctid()) {
203 return;
204 }
205 if (onCompleteHandler_ != nullptr && callbackContext_ != nullptr) {
206 onCompleteHandler_(callbackContext_->env, callbackContext_->ref);
207 }
208 SGLOGD("NAPI OnComplete Release END.");
209 });
210 };
211
OnError(const std::string & message)212 void NapiSecurityEventQuerier::OnError(const std::string &message)
213 {
214 RunCallback(callbackContext_, [message] (const napi_env env, const napi_ref ref, pid_t threadId,
215 const std::vector<SecurityCollector::SecurityEvent> &napiEvents) {
216 SGLOGD("NAPI OnError.");
217 napi_value jsMessage = NapiCreateString(env, message);
218 napi_value argv[1] = {jsMessage};
219 napi_value querier = nullptr;
220 napi_get_reference_value(env, ref, &querier);
221 napi_value onQuery = NapiGetNamedProperty(env, querier, ON_ERROR_ATTR);
222 napi_value ret = nullptr;
223 napi_status status = napi_call_function(env, querier, onQuery, 1, argv, &ret);
224 if (status != napi_ok) {
225 SGLOGE("failed to call OnQuery JS function.");
226 }
227 SGLOGD("NAPI OnError END.");
228 }, [this] (pid_t threadId) {
229 if (threadId != getproctid()) {
230 return;
231 }
232 if (onCompleteHandler_ != nullptr && callbackContext_ != nullptr) {
233 onCompleteHandler_(callbackContext_->env, callbackContext_->ref);
234 }
235 });
236 };
237 } // OHOS::Security::SecurityGuard