1 /*
2 * Copyright (c) 2023 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 "watcher_n_exporter.h"
17
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <memory>
22
23 #include "../common_func.h"
24 #include "file_utils.h"
25 #include "filemgmt_libn.h"
26 #include "filemgmt_libhilog.h"
27 #include "securec.h"
28
29 namespace OHOS::FileManagement::ModuleFileIO {
30 using namespace std;
31 using namespace OHOS::FileManagement::LibN;
32
Constructor(napi_env env,napi_callback_info info)33 napi_value WatcherNExporter::Constructor(napi_env env, napi_callback_info info)
34 {
35 NFuncArg funcArg(env, info);
36 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
37 HILOGE("Failed to get param.");
38 NError(EINVAL).ThrowErr(env);
39 return nullptr;
40 }
41
42 auto watcherEntity = CreateUniquePtr<WatcherEntity>();
43 if (watcherEntity == nullptr) {
44 HILOGE("Failed to request heap memory.");
45 NError(ENOMEM).ThrowErr(env);
46 return nullptr;
47 }
48 if (!NClass::SetEntityFor<WatcherEntity>(env, funcArg.GetThisVar(), move(watcherEntity))) {
49 HILOGE("Failed to set watcherEntity.");
50 NError(EIO).ThrowErr(env);
51 return nullptr;
52 }
53 return funcArg.GetThisVar();
54 }
55
Stop(napi_env env,napi_callback_info info)56 napi_value WatcherNExporter::Stop(napi_env env, napi_callback_info info)
57 {
58 NFuncArg funcArg(env, info);
59 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
60 HILOGE("Failed to get param when stop.");
61 NError(EINVAL).ThrowErr(env);
62 return nullptr;
63 }
64
65 auto watchEntity = NClass::GetEntityOf<WatcherEntity>(env, funcArg.GetThisVar());
66 if (!watchEntity) {
67 HILOGE("Failed to get watcherEntity when stop.");
68 NError(EINVAL).ThrowErr(env);
69 return nullptr;
70 }
71 int ret = FileWatcher::GetInstance().StopNotify(watchEntity->data_);
72 if (ret != ERRNO_NOERR) {
73 HILOGE("Failed to stopNotify errno:%{public}d", errno);
74 NError(ret).ThrowErr(env);
75 return nullptr;
76 }
77 return NVal::CreateUndefined(env).val_;
78 }
79
Start(napi_env env,napi_callback_info info)80 napi_value WatcherNExporter::Start(napi_env env, napi_callback_info info)
81 {
82 NFuncArg funcArg(env, info);
83 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
84 HILOGE("Failed to get param when start.");
85 NError(EINVAL).ThrowErr(env);
86 return nullptr;
87 }
88
89 auto watchEntity = NClass::GetEntityOf<WatcherEntity>(env, funcArg.GetThisVar());
90 if (!watchEntity) {
91 HILOGE("Failed to get watcherEntity when start.");
92 NError(EINVAL).ThrowErr(env);
93 return nullptr;
94 }
95
96 int ret = FileWatcher::GetInstance().StartNotify(watchEntity->data_);
97 if (ret != ERRNO_NOERR) {
98 HILOGE("Failed to startNotify.");
99 NError(ret).ThrowErr(env);
100 return nullptr;
101 }
102
103 auto cbExec = []() -> NError {
104 FileWatcher::GetInstance().GetNotifyEvent(WatcherCallback);
105 return NError(ERRNO_NOERR);
106 };
107
108 auto cbCompl = [](napi_env env, NError err) -> NVal {
109 if (err) {
110 HILOGE("Failed to execute complete.");
111 return {env, err.GetNapiErr(env)};
112 }
113 return {NVal::CreateUndefined(env)};
114 };
115
116 const string procedureName = "FileIOStartWatcher";
117 NVal thisVar(env, funcArg.GetThisVar());
118 return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbCompl).val_;
119 }
120
WatcherCallbackComplete(uv_work_t * work,int stat)121 static void WatcherCallbackComplete(uv_work_t *work, int stat)
122 {
123 if (work == nullptr) {
124 HILOGE("Failed to get uv_queue_work pointer");
125 return;
126 }
127
128 WatcherNExporter::JSCallbackContext *callbackContext =
129 reinterpret_cast<WatcherNExporter::JSCallbackContext *>(work->data);
130 do {
131 if (callbackContext == nullptr) {
132 HILOGE("Failed to create context pointer");
133 break;
134 }
135 if (!callbackContext->ref_) {
136 HILOGE("Failed to get nref reference");
137 break;
138 }
139 napi_handle_scope scope = nullptr;
140 napi_status status = napi_open_handle_scope(callbackContext->env_, &scope);
141 if (status != napi_ok) {
142 HILOGE("Failed to open handle scope, status: %{public}d", status);
143 break;
144 }
145 napi_env env = callbackContext->env_;
146 napi_value jsCallback = callbackContext->ref_.Deref(env).val_;
147 NVal objn = NVal::CreateObject(env);
148 objn.AddProp("fileName", NVal::CreateUTF8String(env, callbackContext->fileName_).val_);
149 objn.AddProp("event", NVal::CreateUint32(env, callbackContext->event_).val_);
150 objn.AddProp("cookie", NVal::CreateUint32(env, callbackContext->cookie_).val_);
151 napi_value retVal = nullptr;
152 status = napi_call_function(env, nullptr, jsCallback, 1, &(objn.val_), &retVal);
153 if (status != napi_ok) {
154 HILOGE("Failed to call napi_call_function, status: %{public}d", status);
155 }
156 status = napi_close_handle_scope(callbackContext->env_, scope);
157 if (status != napi_ok) {
158 HILOGE("Failed to close handle scope, status: %{public}d", status);
159 }
160 } while (0);
161 delete callbackContext;
162 delete work;
163 }
164
WatcherCallback(napi_env env,NRef & callback,const std::string & fileName,const uint32_t & event,const uint32_t & cookie)165 void WatcherNExporter::WatcherCallback(napi_env env, NRef &callback, const std::string &fileName,
166 const uint32_t &event, const uint32_t &cookie)
167 {
168 uv_loop_s *loop = nullptr;
169 napi_get_uv_event_loop(env, &loop);
170 if (loop == nullptr) {
171 HILOGE("Failed to get uv event loop");
172 return;
173 }
174 if (!callback) {
175 HILOGE("Failed to parse watcher callback");
176 return;
177 }
178 uv_work_t *work = new (std::nothrow) uv_work_t;
179 if (work == nullptr) {
180 HILOGE("Failed to create uv_work_t pointer");
181 return;
182 }
183
184 JSCallbackContext *callbackContext = new (std::nothrow) JSCallbackContext(callback);
185 if (callbackContext == nullptr) {
186 delete work;
187 return;
188 }
189 callbackContext->env_ = env;
190 callbackContext->fileName_ = fileName;
191 callbackContext->event_ = event;
192 callbackContext->cookie_ = cookie;
193 work->data = reinterpret_cast<void *>(callbackContext);
194 int ret = uv_queue_work(
195 loop, work, [](uv_work_t *work) {}, reinterpret_cast<uv_after_work_cb>(WatcherCallbackComplete));
196 if (ret != 0) {
197 HILOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
198 delete callbackContext;
199 delete work;
200 }
201 }
202
Export()203 bool WatcherNExporter::Export()
204 {
205 vector<napi_property_descriptor> props = {
206 NVal::DeclareNapiFunction("start", Start),
207 NVal::DeclareNapiFunction("stop", Stop),
208 };
209
210 string className = GetClassName();
211 auto [resDefineClass, classValue] =
212 NClass::DefineClass(exports_.env_, className, WatcherNExporter::Constructor, std::move(props));
213 if (!resDefineClass) {
214 HILOGE("Failed to DefineClass");
215 NError(EIO).ThrowErr(exports_.env_);
216 return false;
217 }
218
219 bool succ = NClass::SaveClass(exports_.env_, className, classValue);
220 if (!succ) {
221 HILOGE("Failed to SaveClass");
222 NError(EIO).ThrowErr(exports_.env_);
223 return false;
224 }
225
226 return exports_.AddProp(className, classValue);
227 }
228
GetClassName()229 string WatcherNExporter::GetClassName()
230 {
231 return WatcherNExporter::className_;
232 }
233
WatcherNExporter(napi_env env,napi_value exports)234 WatcherNExporter::WatcherNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
235
~WatcherNExporter()236 WatcherNExporter::~WatcherNExporter() {}
237 } // namespace OHOS::FileManagement::ModuleFileIO
238