@@ -371,3 +371,257 @@ 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( target_os = "linux" ) ]
526+ #[ napi]
527+ #[ allow( dead_code) ]
528+ pub const IN_IGNORED : u32 = AddWatchFlags :: IN_IGNORED . bits ( ) ;
529+
530+ #[ cfg( target_os = "linux" ) ]
531+ #[ napi]
532+ #[ allow( dead_code) ]
533+ pub const IN_Q_OVERFLOW : u32 = AddWatchFlags :: IN_Q_OVERFLOW . bits ( ) ;
534+
535+ #[ cfg( target_os = "linux" ) ]
536+ #[ napi]
537+ #[ allow( dead_code) ]
538+ pub const IN_UNMOUNT : u32 = AddWatchFlags :: IN_UNMOUNT . bits ( ) ;
539+
540+ #[ cfg( not( target_os = "linux" ) ) ]
541+ #[ napi]
542+ impl Inotify {
543+ #[ napi( constructor) ]
544+ #[ allow( dead_code) ]
545+ pub fn new ( ) -> Result < Self , napi:: Error > {
546+ Err ( napi:: Error :: new (
547+ GenericFailure ,
548+ format ! ( "inotify not supported in non-Linux" ) ,
549+ ) )
550+ }
551+
552+ #[ napi]
553+ #[ allow( dead_code) ]
554+ pub fn close ( & mut self ) -> Result < ( ) , napi:: Error > {
555+ Err ( napi:: Error :: new (
556+ GenericFailure ,
557+ format ! ( "inotify not supported in non-Linux" ) ,
558+ ) )
559+ }
560+
561+ #[ napi]
562+ #[ allow( dead_code) ]
563+ pub fn fd ( & self ) -> Result < c_int , napi:: Error > {
564+ Err ( napi:: Error :: new (
565+ GenericFailure ,
566+ format ! ( "inotify not supported in non-Linux" ) ,
567+ ) )
568+ }
569+
570+ #[ napi]
571+ #[ allow( dead_code) ]
572+ pub fn add_close_write ( & self , dir : String ) -> Result < i32 , napi:: Error > {
573+ Err ( napi:: Error :: new (
574+ GenericFailure ,
575+ format ! ( "inotify not supported in non-Linux" ) ,
576+ ) )
577+ }
578+
579+ #[ napi]
580+ #[ allow( dead_code) ]
581+ pub fn remove_watch ( & self , wd : i32 ) -> Result < ( ) , napi:: Error > {
582+ Err ( napi:: Error :: new (
583+ GenericFailure ,
584+ format ! ( "inotify not supported in non-Linux" ) ,
585+ ) )
586+ }
587+ }
588+
589+ #[ cfg( not( target_os = "linux" ) ) ]
590+ #[ napi]
591+ #[ allow( dead_code) ]
592+ pub const IN_CLOSE_WRITE : u32 = 0 ;
593+
594+ #[ cfg( not( target_os = "linux" ) ) ]
595+ #[ napi]
596+ #[ allow( dead_code) ]
597+ pub const IN_MOVED_FROM : u32 = 0 ;
598+
599+ #[ cfg( not( target_os = "linux" ) ) ]
600+ #[ napi]
601+ #[ allow( dead_code) ]
602+ pub const IN_MOVED_TO : u32 = 0 ;
603+
604+ #[ cfg( not( target_os = "linux" ) ) ]
605+ #[ napi]
606+ #[ allow( dead_code) ]
607+ pub const IN_CREATE : u32 = 0 ;
608+
609+ #[ cfg( not( target_os = "linux" ) ) ]
610+ #[ napi]
611+ #[ allow( dead_code) ]
612+ pub const IN_DELETE : u32 = 0 ;
613+
614+ #[ cfg( not( target_os = "linux" ) ) ]
615+ #[ napi]
616+ #[ allow( dead_code) ]
617+ pub const IN_IGNORED : u32 = 0 ;
618+
619+ #[ cfg( not( target_os = "linux" ) ) ]
620+ #[ napi]
621+ #[ allow( dead_code) ]
622+ pub const IN_Q_OVERFLOW : u32 = 0 ;
623+
624+ #[ cfg( not( target_os = "linux" ) ) ]
625+ #[ napi]
626+ #[ allow( dead_code) ]
627+ pub const IN_UNMOUNT : u32 = 0 ;
0 commit comments