@@ -371,3 +371,227 @@ fn set_nonblocking(fd: i32) -> Result<(), napi::Error> {
371371 }
372372 Ok ( ( ) )
373373}
374+
375+ #[ cfg( target_os = "linux" ) ]
376+ use nix:: sys:: inotify:: { AddWatchFlags , InitFlags } ;
377+ #[ cfg( target_os = "linux" ) ]
378+ use std:: ffi:: CString ;
379+
380+ /// A way to access Linux' `inotify(7)` subsystem. For simplicity, this only allows subscribing for
381+ /// events on directories (instead of files) and only for modify-close and rename events.
382+ #[ napi]
383+ #[ allow( dead_code) ]
384+ struct Inotify {
385+ fd : Option < OwnedFd > ,
386+ }
387+
388+ #[ cfg( target_os = "linux" ) ]
389+ #[ napi]
390+ #[ allow( dead_code) ]
391+ impl Inotify {
392+ #[ napi( constructor) ]
393+ pub fn new ( ) -> Result < Self , napi:: Error > {
394+ let fd = match Errno :: result ( unsafe {
395+ libc:: inotify_init1 ( ( InitFlags :: IN_CLOEXEC | InitFlags :: IN_NONBLOCK ) . bits ( ) )
396+ } ) {
397+ Ok ( fd) => unsafe { OwnedFd :: from_raw_fd ( fd) } ,
398+ Err ( err) => {
399+ return Err ( napi:: Error :: new (
400+ GenericFailure ,
401+ format ! ( "inotify_init: {}" , err) ,
402+ ) ) ;
403+ }
404+ } ;
405+ Ok ( Inotify { fd : Some ( fd) } )
406+ }
407+
408+ /// Close the inotify file descriptor. Must be called at most once to avoid file descriptor
409+ /// leaks.
410+ #[ napi]
411+ #[ allow( dead_code) ]
412+ pub fn close ( & mut self ) -> Result < ( ) , napi:: Error > {
413+ let inotify = self . fd . take ( ) ;
414+ if inotify. is_none ( ) {
415+ return Err ( napi:: Error :: new (
416+ GenericFailure ,
417+ "inotify fd has already been closed" ,
418+ ) ) ;
419+ }
420+
421+ Ok ( ( ) )
422+ }
423+
424+ /// Borrow the file descriptor. It is expected that Nod does not close the file descriptor and
425+ /// instead the .close() method should be called to clean the file descriptor up. Read the file
426+ /// descriptor on node according to `inotify(7)` to get events.
427+ #[ napi]
428+ #[ allow( dead_code) ]
429+ pub fn fd ( & self ) -> Result < c_int , napi:: Error > {
430+ if let Some ( fd) = & self . fd {
431+ Ok ( fd. as_raw_fd ( ) )
432+ } else {
433+ Err ( napi:: Error :: new (
434+ GenericFailure ,
435+ "inotify fd has already been closed" ,
436+ ) )
437+ }
438+ }
439+
440+ /// Register one directory to be watched. Events for close-after-write, renames, and deletions
441+ /// will be registered. Events for creation and modification will be ignored. Returns a watch
442+ /// descriptor, which can be used in `remove_watch`.
443+ #[ napi]
444+ #[ allow( dead_code) ]
445+ pub fn add_close_write ( & self , dir : String ) -> Result < i32 , napi:: Error > {
446+ let cstring_dir = match CString :: new ( dir. as_str ( ) ) {
447+ Ok ( cstring_dir) => cstring_dir,
448+ Err ( err) => {
449+ return Err ( napi:: Error :: new (
450+ GenericFailure ,
451+ format ! ( "CString::new: {}" , err) ,
452+ ) ) ;
453+ }
454+ } ;
455+ if let Some ( fd) = & self . fd {
456+ match Errno :: result ( unsafe {
457+ libc:: inotify_add_watch (
458+ fd. as_raw_fd ( ) ,
459+ cstring_dir. as_c_str ( ) . as_ptr ( ) ,
460+ ( AddWatchFlags :: IN_CLOSE_WRITE | AddWatchFlags :: IN_MOVED_TO | AddWatchFlags :: IN_DELETE )
461+ . bits ( ) ,
462+ )
463+ } ) {
464+ Ok ( wd) => Ok ( wd) ,
465+ Err ( err) => Err ( napi:: Error :: new (
466+ GenericFailure ,
467+ format ! ( "inotify_add_watch: {}" , err) ,
468+ ) ) ,
469+ }
470+ } else {
471+ Err ( napi:: Error :: new (
472+ GenericFailure ,
473+ "inotify fd has already been closed" ,
474+ ) )
475+ }
476+ }
477+
478+ /// Stop watching the watch descriptor provided.
479+ #[ napi]
480+ #[ allow( dead_code) ]
481+ pub fn remove_watch ( & self , wd : i32 ) -> Result < ( ) , napi:: Error > {
482+ if let Some ( fd) = & self . fd {
483+ if let Err ( err) = Errno :: result ( unsafe { libc:: inotify_rm_watch ( fd. as_raw_fd ( ) , wd) } ) {
484+ Err ( napi:: Error :: new (
485+ GenericFailure ,
486+ format ! ( "inotify_remove_watch: {}" , err) ,
487+ ) )
488+ } else {
489+ Ok ( ( ) )
490+ }
491+ } else {
492+ Err ( napi:: Error :: new (
493+ GenericFailure ,
494+ "inotify fd has already been closed" ,
495+ ) )
496+ }
497+ }
498+ }
499+
500+ #[ cfg( target_os = "linux" ) ]
501+ #[ napi]
502+ #[ allow( dead_code) ]
503+ pub const IN_CLOSE_WRITE : u32 = AddWatchFlags :: IN_CLOSE_WRITE . bits ( ) ;
504+
505+ #[ cfg( target_os = "linux" ) ]
506+ #[ napi]
507+ #[ allow( dead_code) ]
508+ pub const IN_MOVED_FROM : u32 = AddWatchFlags :: IN_MOVED_FROM . bits ( ) ;
509+
510+ #[ cfg( target_os = "linux" ) ]
511+ #[ napi]
512+ #[ allow( dead_code) ]
513+ pub const IN_MOVED_TO : u32 = AddWatchFlags :: IN_MOVED_TO . bits ( ) ;
514+
515+ #[ cfg( target_os = "linux" ) ]
516+ #[ napi]
517+ #[ allow( dead_code) ]
518+ pub const IN_CREATE : u32 = AddWatchFlags :: IN_CREATE . bits ( ) ;
519+
520+ #[ cfg( target_os = "linux" ) ]
521+ #[ napi]
522+ #[ allow( dead_code) ]
523+ pub const IN_DELETE : u32 = AddWatchFlags :: IN_DELETE . bits ( ) ;
524+
525+ #[ cfg( not( target_os = "linux" ) ) ]
526+ #[ napi]
527+ impl Inotify {
528+ #[ napi( constructor) ]
529+ #[ allow( dead_code) ]
530+ pub fn new ( ) -> Result < Self , napi:: Error > {
531+ Err ( napi:: Error :: new (
532+ GenericFailure ,
533+ format ! ( "inotify not supported in non-Linux" ) ,
534+ ) )
535+ }
536+
537+ #[ napi]
538+ #[ allow( dead_code) ]
539+ pub fn close ( & mut self ) -> Result < ( ) , napi:: Error > {
540+ Err ( napi:: Error :: new (
541+ GenericFailure ,
542+ format ! ( "inotify not supported in non-Linux" ) ,
543+ ) )
544+ }
545+
546+ #[ napi]
547+ #[ allow( dead_code) ]
548+ pub fn fd ( & self ) -> Result < c_int , napi:: Error > {
549+ Err ( napi:: Error :: new (
550+ GenericFailure ,
551+ format ! ( "inotify not supported in non-Linux" ) ,
552+ ) )
553+ }
554+
555+ #[ napi]
556+ #[ allow( dead_code) ]
557+ pub fn add_close_write ( & self , dir : String ) -> Result < i32 , napi:: Error > {
558+ Err ( napi:: Error :: new (
559+ GenericFailure ,
560+ format ! ( "inotify not supported in non-Linux" ) ,
561+ ) )
562+ }
563+
564+ #[ napi]
565+ #[ allow( dead_code) ]
566+ pub fn remove_watch ( & self , wd : i32 ) -> Result < ( ) , napi:: Error > {
567+ Err ( napi:: Error :: new (
568+ GenericFailure ,
569+ format ! ( "inotify not supported in non-Linux" ) ,
570+ ) )
571+ }
572+ }
573+
574+ #[ cfg( not( target_os = "linux" ) ) ]
575+ #[ napi]
576+ #[ allow( dead_code) ]
577+ pub const IN_CLOSE_WRITE : u32 = 0 ;
578+
579+ #[ cfg( not( target_os = "linux" ) ) ]
580+ #[ napi]
581+ #[ allow( dead_code) ]
582+ pub const IN_MOVED_FROM : u32 = 0 ;
583+
584+ #[ cfg( not( target_os = "linux" ) ) ]
585+ #[ napi]
586+ #[ allow( dead_code) ]
587+ pub const IN_MOVED_TO : u32 = 0 ;
588+
589+ #[ cfg( not( target_os = "linux" ) ) ]
590+ #[ napi]
591+ #[ allow( dead_code) ]
592+ pub const IN_CREATE : u32 = 0 ;
593+
594+ #[ cfg( not( target_os = "linux" ) ) ]
595+ #[ napi]
596+ #[ allow( dead_code) ]
597+ pub const IN_DELETE : u32 = 0 ;
0 commit comments