1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22#include "async_wrap-inl.h" 23#include "env-inl.h" 24#include "handle_wrap.h" 25#include "node.h" 26#include "node_external_reference.h" 27#include "string_bytes.h" 28 29namespace node { 30 31using v8::Context; 32using v8::DontDelete; 33using v8::DontEnum; 34using v8::FunctionCallbackInfo; 35using v8::FunctionTemplate; 36using v8::HandleScope; 37using v8::Integer; 38using v8::Isolate; 39using v8::Local; 40using v8::MaybeLocal; 41using v8::Object; 42using v8::PropertyAttribute; 43using v8::ReadOnly; 44using v8::Signature; 45using v8::String; 46using v8::Value; 47 48namespace { 49 50class FSEventWrap: public HandleWrap { 51 public: 52 static void Initialize(Local<Object> target, 53 Local<Value> unused, 54 Local<Context> context, 55 void* priv); 56 static void RegisterExternalReferences(ExternalReferenceRegistry* registry); 57 static void New(const FunctionCallbackInfo<Value>& args); 58 static void Start(const FunctionCallbackInfo<Value>& args); 59 static void GetInitialized(const FunctionCallbackInfo<Value>& args); 60 61 SET_NO_MEMORY_INFO() 62 SET_MEMORY_INFO_NAME(FSEventWrap) 63 SET_SELF_SIZE(FSEventWrap) 64 65 private: 66 static const encoding kDefaultEncoding = UTF8; 67 68 FSEventWrap(Environment* env, Local<Object> object); 69 ~FSEventWrap() override = default; 70 71 static void OnEvent(uv_fs_event_t* handle, const char* filename, int events, 72 int status); 73 74 uv_fs_event_t handle_; 75 enum encoding encoding_ = kDefaultEncoding; 76}; 77 78 79FSEventWrap::FSEventWrap(Environment* env, Local<Object> object) 80 : HandleWrap(env, 81 object, 82 reinterpret_cast<uv_handle_t*>(&handle_), 83 AsyncWrap::PROVIDER_FSEVENTWRAP) { 84 MarkAsUninitialized(); 85} 86 87 88void FSEventWrap::GetInitialized(const FunctionCallbackInfo<Value>& args) { 89 FSEventWrap* wrap = Unwrap<FSEventWrap>(args.This()); 90 CHECK_NOT_NULL(wrap); 91 args.GetReturnValue().Set(!wrap->IsHandleClosing()); 92} 93 94void FSEventWrap::Initialize(Local<Object> target, 95 Local<Value> unused, 96 Local<Context> context, 97 void* priv) { 98 Environment* env = Environment::GetCurrent(context); 99 Isolate* isolate = env->isolate(); 100 101 Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New); 102 t->InstanceTemplate()->SetInternalFieldCount( 103 FSEventWrap::kInternalFieldCount); 104 105 t->Inherit(HandleWrap::GetConstructorTemplate(env)); 106 SetProtoMethod(isolate, t, "start", Start); 107 108 Local<FunctionTemplate> get_initialized_templ = 109 FunctionTemplate::New(env->isolate(), 110 GetInitialized, 111 Local<Value>(), 112 Signature::New(env->isolate(), t)); 113 114 t->PrototypeTemplate()->SetAccessorProperty( 115 FIXED_ONE_BYTE_STRING(env->isolate(), "initialized"), 116 get_initialized_templ, 117 Local<FunctionTemplate>(), 118 static_cast<PropertyAttribute>(ReadOnly | DontDelete | DontEnum)); 119 120 SetConstructorFunction(context, target, "FSEvent", t); 121} 122 123void FSEventWrap::RegisterExternalReferences( 124 ExternalReferenceRegistry* registry) { 125 registry->Register(New); 126 registry->Register(Start); 127 registry->Register(GetInitialized); 128} 129 130void FSEventWrap::New(const FunctionCallbackInfo<Value>& args) { 131 CHECK(args.IsConstructCall()); 132 Environment* env = Environment::GetCurrent(args); 133 new FSEventWrap(env, args.This()); 134} 135 136// wrap.start(filename, persistent, recursive, encoding) 137void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) { 138 Environment* env = Environment::GetCurrent(args); 139 140 FSEventWrap* wrap = Unwrap<FSEventWrap>(args.This()); 141 CHECK_NOT_NULL(wrap); 142 CHECK(wrap->IsHandleClosing()); // Check that Start() has not been called. 143 144 const int argc = args.Length(); 145 CHECK_GE(argc, 4); 146 147 BufferValue path(env->isolate(), args[0]); 148 CHECK_NOT_NULL(*path); 149 150 unsigned int flags = 0; 151 if (args[2]->IsTrue()) 152 flags |= UV_FS_EVENT_RECURSIVE; 153 154 wrap->encoding_ = ParseEncoding(env->isolate(), args[3], kDefaultEncoding); 155 156 int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_); 157 if (err != 0) { 158 return args.GetReturnValue().Set(err); 159 } 160 161 err = uv_fs_event_start(&wrap->handle_, OnEvent, *path, flags); 162 wrap->MarkAsInitialized(); 163 164 if (err != 0) { 165 FSEventWrap::Close(args); 166 return args.GetReturnValue().Set(err); 167 } 168 169 // Check for persistent argument 170 if (!args[1]->IsTrue()) { 171 uv_unref(reinterpret_cast<uv_handle_t*>(&wrap->handle_)); 172 } 173 174 args.GetReturnValue().Set(err); 175} 176 177 178void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, 179 int events, int status) { 180 FSEventWrap* wrap = static_cast<FSEventWrap*>(handle->data); 181 Environment* env = wrap->env(); 182 183 HandleScope handle_scope(env->isolate()); 184 Context::Scope context_scope(env->context()); 185 186 CHECK_EQ(wrap->persistent().IsEmpty(), false); 187 188 // We're in a bind here. libuv can set both UV_RENAME and UV_CHANGE but 189 // the Node API only lets us pass a single event to JS land. 190 // 191 // The obvious solution is to run the callback twice, once for each event. 192 // However, since the second event is not allowed to fire if the handle is 193 // closed after the first event, and since there is no good way to detect 194 // closed handles, that option is out. 195 // 196 // For now, ignore the UV_CHANGE event if UV_RENAME is also set. Make the 197 // assumption that a rename implicitly means an attribute change. Not too 198 // unreasonable, right? Still, we should revisit this before v1.0. 199 Local<String> event_string; 200 if (status) { 201 event_string = String::Empty(env->isolate()); 202 } else if (events & UV_RENAME) { 203 event_string = env->rename_string(); 204 } else if (events & UV_CHANGE) { 205 event_string = env->change_string(); 206 } else { 207 UNREACHABLE("bad fs events flag"); 208 } 209 210 Local<Value> argv[] = { 211 Integer::New(env->isolate(), status), 212 event_string, 213 Null(env->isolate()) 214 }; 215 216 if (filename != nullptr) { 217 Local<Value> error; 218 MaybeLocal<Value> fn = StringBytes::Encode(env->isolate(), 219 filename, 220 wrap->encoding_, 221 &error); 222 if (fn.IsEmpty()) { 223 argv[0] = Integer::New(env->isolate(), UV_EINVAL); 224 argv[2] = StringBytes::Encode(env->isolate(), 225 filename, 226 strlen(filename), 227 BUFFER, 228 &error).ToLocalChecked(); 229 } else { 230 argv[2] = fn.ToLocalChecked(); 231 } 232 } 233 234 wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv); 235} 236 237} // anonymous namespace 238} // namespace node 239 240NODE_BINDING_CONTEXT_AWARE_INTERNAL(fs_event_wrap, 241 node::FSEventWrap::Initialize) 242NODE_BINDING_EXTERNAL_REFERENCE(fs_event_wrap, 243 node::FSEventWrap::RegisterExternalReferences) 244