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