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