forked from facebook/flow
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfsnotify.ml
More file actions
129 lines (115 loc) · 3.95 KB
/
Copy pathfsnotify.ml
File metadata and controls
129 lines (115 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
(**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the "hack" directory of this source tree.
*
*)
open Hh_core
exception Error of string * Unix.error
let wrap f () =
try
f ()
with
| Unix.Unix_error (err, func, msg) ->
let reason =
Printf.sprintf "%s: %s: %s" func msg (Unix.error_message err) in
raise (Error (reason, err))
| e -> raise e
type watch = Inotify.watch
(* A watch in this case refers to an inotify watch. Inotify watches are used
* to subscribe to events on files in linux kernel.
* Once a watch has been added to a file, the kernel notifies us every time
* the file changes (by sending an event to a pipe, cf env.inotify).
* We need to be able to compare watches because there could be multiple
* paths that lead to the same watch (because of symlinks).
*)
module WMap = Map.Make(struct type t = watch let compare = compare end)
type env = {
fd : Unix.file_descr;
mutable wpaths : string WMap.t;
}
type event = {
path : string; (* The full path for the file/directory that changed *)
wpath : string; (* The watched path that triggered this event *)
}
let init _roots = {
fd = wrap (Inotify.create) ();
wpaths = WMap.empty;
}
let select_events = Inotify.(
[ S_Create; S_Delete; S_Delete_self;
S_Modify; S_Move_self; S_Moved_from;
S_Moved_to; S_Attrib;
])
(* Returns None if we're already watching that path and Some watch otherwise *)
let add_watch env path =
let watch = wrap (fun () -> Inotify.add_watch env.fd path select_events) () in
if WMap.mem watch env.wpaths && WMap.find watch env.wpaths = path
then None
else begin
env.wpaths <- WMap.add watch path env.wpaths;
Some watch
end
let check_event_type = function
| Inotify.Access
| Inotify.Attrib
| Inotify.Close_write
| Inotify.Close_nowrite
| Inotify.Create
| Inotify.Delete
| Inotify.Delete_self
| Inotify.Move_self
| Inotify.Moved_from
| Inotify.Moved_to
| Inotify.Open
| Inotify.Ignored
| Inotify.Modify
| Inotify.Isdir -> ()
| Inotify.Q_overflow ->
Printf.printf "INOTIFY OVERFLOW!!!\n";
exit 5
| Inotify.Unmount ->
Printf.printf "UNMOUNT EVENT!!!\n";
exit 5
let process_event env events event =
match event with
| _, _, _, None -> events
| watch, type_list, _, Some filename ->
List.iter type_list check_event_type;
let wpath = try WMap.find watch env.wpaths with _ -> assert false in
let path = Filename.concat wpath filename in
{ path; wpath; }::events
let read env =
let inotify_events = wrap (fun () -> Inotify.read env.fd) () in
List.fold_left inotify_events ~f:(process_event env) ~init:[]
module FDMap = Map.Make(
struct type t = Unix.file_descr let compare = compare end
)
type fd_select = Unix.file_descr * (unit -> unit)
let make_callback fdmap (fd, callback) = FDMap.add fd callback fdmap
let invoke_callback fdmap fd = (FDMap.find fd fdmap) ()
let select env ?(read_fdl=[]) ?(write_fdl=[]) ~timeout callback =
let callback () = callback (Unix.handle_unix_error read env) in
let read_fdl = (env.fd, callback) :: read_fdl in
let read_callbacks =
List.fold_left read_fdl ~f:make_callback ~init:FDMap.empty in
let write_callbacks =
List.fold_left write_fdl ~f:make_callback ~init:FDMap.empty in
let read_ready, write_ready, _ =
Unix.select (List.map read_fdl fst) (List.map write_fdl fst) [] timeout
in
List.iter write_ready (invoke_callback write_callbacks);
List.iter read_ready (invoke_callback read_callbacks)
(********** DEBUGGING ****************)
(* Can be useful to see what the event actually is, for debugging *)
let _string_of inotify_ev =
let wd, mask, cookie, s = inotify_ev in
let mask = String.concat ":" (List.map mask Inotify.string_of_event) in
let s = match s with
| Some s -> s
| None -> "\"\"" in
Printf.sprintf
"wd [%u] mask[%s] cookie[%ld] %s"
(Inotify.int_of_watch wd) mask cookie s