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
175 uv_work_t *work = new (std::nothrow) uv_work_t;
176 if (work == nullptr) {
177 HILOGE("Failed to create uv_work_t pointer");
178 return;
179 }
180
181 if (!callback) {
182 HILOGE("Failed to parse watcher callback");
183 return;
184 }
185
186 JSCallbackContext *callbackContext = new (std::nothrow) JSCallbackContext(callback);
187 callbackContext->env_ = env;
188 callbackContext->fileName_ = fileName;
189 callbackContext->event_ = event;
190 callbackContext->cookie_ = cookie;
191 work->data = reinterpret_cast<void *>(callbackContext);
192
193 int ret = uv_queue_work(
194 loop, work, [](uv_work_t *work) {}, reinterpret_cast<uv_after_work_cb>(WatcherCallbackComplete));
195 if (ret != 0) {
196 HILOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
197 delete callbackContext;
198 delete work;
199 }
200 }
201
Export()202 bool WatcherNExporter::Export()
203 {
204 vector<napi_property_descriptor> props = {
205 NVal::DeclareNapiFunction("start", Start),
206 NVal::DeclareNapiFunction("stop", Stop),
207 };
208
209 string className = GetClassName();
210 auto [resDefineClass, classValue] =
211 NClass::DefineClass(exports_.env_, className, WatcherNExporter::Constructor, std::move(props));
212 if (!resDefineClass) {
213 HILOGE("Failed to DefineClass");
214 NError(EIO).ThrowErr(exports_.env_);
215 return false;
216 }
217
218 bool succ = NClass::SaveClass(exports_.env_, className, classValue);
219 if (!succ) {
220 HILOGE("Failed to SaveClass");
221 NError(EIO).ThrowErr(exports_.env_);
222 return false;
223 }
224
225 return exports_.AddProp(className, classValue);
226 }
227
GetClassName()228 string WatcherNExporter::GetClassName()
229 {
230 return WatcherNExporter::className_;
231 }
232
WatcherNExporter(napi_env env,napi_value exports)233 WatcherNExporter::WatcherNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
234
~WatcherNExporter()235 WatcherNExporter::~WatcherNExporter() {}
236 } // namespace OHOS::FileManagement::ModuleFileIO
237