Browse Source

upd to v6.12

221V 3 years ago
parent
commit
c7d9fe4b78
13 changed files with 165 additions and 86 deletions
  1. 3 1
      .gitignore
  2. 27 5
      README.md
  3. 0 3
      c_src/bsd/main.c
  4. 20 15
      c_src/mac/main.c
  5. 3 3
      ebin/fs.app
  6. 61 23
      src/fs.erl
  7. 5 1
      src/fs_app.erl
  8. 8 8
      src/fs_server.erl
  9. 10 7
      src/fs_sup.erl
  10. 8 7
      src/sys/fsevents.erl
  11. 17 11
      src/sys/inotifywait.erl
  12. 1 1
      src/sys/inotifywait_win32.erl
  13. 2 1
      src/sys/kqueue.erl

+ 3 - 1
.gitignore

@@ -1,2 +1,4 @@
-deps/
+deps
+*.d
+*.o
 ebin/*.beam

+ 27 - 5
README.md

@@ -1,4 +1,4 @@
-FS Listener - fs v 1.9 fork
+FS: Native Listener - https://github.com/synrc/fs v1.9 fork (upd to v6.12)
 ===========
 
 Backends
@@ -13,8 +13,9 @@ NOTE: On Linux you need to install inotify-tools.
 ### Subscribe to Notifications
 
 ```erlang
-> fs:subscribe(). % the pid will receive events as messages
-> flush(). 
+> fs:start_link(fs_watcher, "/Users/5HT/synrc/fs"). % need to start the fs watcher
+> fs:subscribe(fs_watcher). % the pid will receive events as messages
+> flush().
 Shell got {<0.47.0>,
            {fs,file_event},
            {"/Users/5HT/synrc/fs/src/README.md",[closed,modified]}}
@@ -23,7 +24,7 @@ Shell got {<0.47.0>,
 ### List Events from Backend
 
 ```erlang
-> fs:known_events(). % returns events known by your current backend
+> fs:known_events(fs_watcher). % returns events known by your backend
 [mustscansubdirs,userdropped,kerneldropped,eventidswrapped,
  historydone,rootchanged,mount,unmount,created,removed,
  inodemetamod,renamed,modified,finderinfomod,changeowner,
@@ -33,11 +34,32 @@ Shell got {<0.47.0>,
 ### Sample Subscriber
 
 ```erlang
-> fs:start_logger(). % starts a sample process that logs events with error_logger
+> fs:start_looper(). % starts a sample process that logs events
 =INFO REPORT==== 28-Aug-2013::19:36:26 ===
 file_event: "/tank/proger/erlfsmon/src/4913" [closed,modified]
 ```
 
+### API compatibility
+
+API is per default compatible to version before 1.10.
+
+By application start, `fs` will start fs watcher on specified per enviroment `path` or
+if enviroment is unsetted, than in `CWD`.
+
+That means you can still use it, like:
+
+```erlang
+fs:subscribe()
+```
+
+If you do not want to use backwards_compatible mode, disable it by setting `fs` enviroment:
+
+```
+{backwards_compatible, false}
+```
+
+This option will lead, that default fs watcher willn't be started.
+
 Credits
 -------
 

+ 0 - 3
c_src/bsd/main.c

@@ -11,12 +11,9 @@ int main(int argc, char *argv[]) {
     int fd, kq, nev;
     if ((fd = open(argv[1], O_RDONLY)) == -1) return 1;
     EV_SET(&change, fd, EVFILT_VNODE , EV_ADD
-                                     | EV_ENABLE
-                                     | EV_DISABLE
                                      | EV_CLEAR
                                      | EV_DELETE
                                      | EV_EOF
-                                     | EV_RECEIPT
                                      | EV_DISPATCH
                                      | EV_ONESHOT,
                                        NOTE_DELETE

+ 20 - 15
c_src/mac/main.c

@@ -1,5 +1,8 @@
 #include "common.h"
 #include "cli.h"
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <stdio.h>
 
 // TODO: set on fire. cli.{h,c} handle both parsing and defaults, so there's
 //       no need to set those here. also, in order to scope metadata by path,
@@ -49,12 +52,14 @@ static void append_path(const char* path)
 static inline void parse_cli_settings(int argc, const char* argv[])
 {
   // runtime os version detection
-  SInt32 osMajorVersion, osMinorVersion;
-  if (!(Gestalt(gestaltSystemVersionMajor, &osMajorVersion) == noErr)) {
-    osMajorVersion = 0;
-  }
-  if (!(Gestalt(gestaltSystemVersionMinor, &osMinorVersion) == noErr)) {
-    osMinorVersion = 0;
+  char buf[25];
+  size_t buflen = 25;
+  sysctlbyname("kern.osproductversion", &buf, &buflen, NULL, 0);
+  int osMajorVersion, osMinorVersion;
+  int res = sscanf(buf, "%d.%d", &osMajorVersion, &osMinorVersion);
+  if (res != 2)
+  {
+    osMajorVersion = osMinorVersion = 0;
   }
 
   if ((osMajorVersion == 10) & (osMinorVersion < 5)) {
@@ -85,29 +90,29 @@ static inline void parse_cli_settings(int argc, const char* argv[])
   }
 
   if (args_info.ignore_self_flag) {
-    if ((osMajorVersion == 10) & (osMinorVersion >= 6)) {
-      config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
-    } else {
+    if ((osMajorVersion < 10) || ((osMajorVersion == 10) && (osMinorVersion >= 6))) {
       fprintf(stderr, "MacOSX 10.6 or later is required for --ignore-self\n");
       exit(EXIT_FAILURE);
+    } else {
+      config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
     }
   }
 
   if (args_info.file_events_flag) {
-    if ((osMajorVersion == 10) & (osMinorVersion >= 7)) {
-      config.flags |= kFSEventStreamCreateFlagFileEvents;
-    } else {
+    if ((osMajorVersion < 10) || ((osMajorVersion == 10) && (osMinorVersion < 7))) {
       fprintf(stderr, "MacOSX 10.7 or later required for --file-events\n");
       exit(EXIT_FAILURE);
+    } else {
+      config.flags |= kFSEventStreamCreateFlagFileEvents;
     }
   }
 
   if (args_info.mark_self_flag) {
-    if ((osMajorVersion == 10) & (osMinorVersion >= 9)) {
-      config.flags |= kFSEventStreamCreateFlagMarkSelf;
-    } else {
+    if ((osMajorVersion < 10) || ((osMajorVersion == 10) && (osMinorVersion < 9))) {
       fprintf(stderr, "MacOSX 10.9 or later required for --mark-self\n");
       exit(EXIT_FAILURE);
+    } else {
+      config.flags |= kFSEventStreamCreateFlagMarkSelf;
     }
   }
 

+ 3 - 3
ebin/fs.app

@@ -1,9 +1,9 @@
 {application, fs,
- [{description, "FS VXZ Listener"},
-  {vsn, "1.9"},
+ [{description, "FS Native Listener (Mac Linux Windows)"},
+  {vsn, "6.12.0"},
   {registered, [fs_sup]},
   {applications, [kernel, stdlib]},
   {modules, [fs_app, fs_event_bridge, fs_server, fs_sup, fs, fsevents, inotifywait_win32, inotifywait, kqueue]},
   {mod, { fs_app, []}},
-  {env, []}
+  {env, [ {backwards_compatible, true} ]}
  ]}.

+ 61 - 23
src/fs.erl

@@ -1,41 +1,58 @@
 -module(fs).
 -include_lib("kernel/include/file.hrl").
--export([subscribe/0, known_events/0, start_looper/0, path/0, find_executable/2]).
+-export([start_link/1, start_link/2, subscribe/0, subscribe/1, known_events/0, known_events/1,
+  start_looper/0, start_looper/1, find_executable/2, path/0]).
 
 % sample subscriber
 
-subscribe() -> gen_event:add_sup_handler(fs_events, {fs_event_bridge, self()}, [self()]).
-known_events() -> gen_server:call(fs_server, known_events).
-start_looper() -> spawn(fun() -> subscribe(), loop() end).
+start_link(Name) -> start_link(Name, path()).
+start_link(Name, Path) ->
+  SupName = name(Name, "sup"),
+  FileHandler = name(Name, "file"),
+  fs_sup:start_link(SupName, Name, FileHandler, Path).
+
+subscribe() -> subscribe(default_fs).
+subscribe(Name) -> gen_event:add_sup_handler(Name, {fs_event_bridge, self()}, [self()]).
 
 path() ->
-    case application:get_env(fs, path) of
-        {ok, P} -> filename:absname(P);
-        undefined -> filename:absname("") end.
+  case application:get_env(fs, path) of
+    {ok, P} -> filename:absname(P);
+    undefined -> filename:absname("")
+  end.
+
+known_events() -> known_events(default_fs).
+known_events(Name) -> gen_server:call(name(Name, "file"), known_events).
+
+start_looper() -> start_looper(default_fs).
+start_looper(Name) -> spawn(fun() -> subscribe(Name), loop() end).
 
 loop() ->
-    receive
-        {_Pid, {fs, file_event}, {Path, Flags}} -> error_logger:info_msg("file_event: ~p ~p", [Path, Flags]);
-        _ -> ignore end,
-    loop().
+  receive
+    {_Pid, {fs, file_event}, {Path, Flags}} -> error_logger:info_msg("file_event: ~p ~p", [Path, Flags]);
+    _ -> ignore
+  end,
+  loop().
 
 find_executable(Cmd, DepsPath) ->
-    case priv_file(Cmd) of
+  case priv_file(Cmd) of
     false -> mad_file(DepsPath);
-    Priv  -> Priv end.
+    Priv  -> Priv
+  end.
 
 mad_file(DepsPath) ->
-    case filelib:is_regular(DepsPath) of
-    true  -> DepsPath;
+  case filelib:is_regular(DepsPath) of
+    true -> path() ++ "/" ++ DepsPath;
     false ->
-        case mad_repl:load_file(DepsPath) of
-        {error,_} ->
-            %% This path has been already checked in find_executable/2
-            false;
-        {ok,ETSFile} ->
-            filelib:ensure_dir(DepsPath),
-            file:write_file(DepsPath, ETSFile),
-            file:write_file_info(DepsPath, #file_info{mode=8#00555}) end end.
+      case load_file(DepsPath) of
+        {error, _} ->
+          %% This path has been already checked in find_executable/2
+          false;
+        {ok, ETSFile} ->
+          filelib:ensure_dir(DepsPath),
+          file:write_file(DepsPath, ETSFile),
+          file:write_file_info(DepsPath, #file_info{mode=8#00555})
+      end
+  end.
 
 priv_file(Cmd) ->
     case code:priv_dir(fs) of
@@ -46,3 +63,24 @@ priv_file(Cmd) ->
         false -> false end;
     _ ->
         false end.
+
+
+name(Name, Prefix) ->
+  NameList = erlang:atom_to_list(Name),
+  list_to_atom(NameList ++ Prefix).
+
+
+ets_created() ->
+  case ets:info(filesystem) of
+    undefined -> ets:new(filesystem, [set, named_table, {keypos, 1}, public]);
+    _ -> skip
+  end.
+
+
+load_file(Name)  ->
+  ets_created(),
+  case ets:lookup(filesystem, Name) of
+    [{Name, Bin}] -> {ok, Bin};
+    _ -> {error, etsfs}
+  end.
+

+ 5 - 1
src/fs_app.erl

@@ -2,5 +2,9 @@
 -behaviour(application).
 -export([start/2, stop/1]).
 
-start(_StartType, _StartArgs) -> fs_sup:start_link().
+start(_StartType, _StartArgs) ->
+  case application:get_env(fs, backwards_compatible) of
+    {ok, false} -> {ok, self()};
+    {ok, true} -> fs:start_link(default_fs)
+  end.
 stop(_State) -> ok.

+ 8 - 8
src/fs_server.erl

@@ -1,21 +1,21 @@
 -module(fs_server).
 -behaviour(gen_server).
 -define(SERVER, ?MODULE).
--export([start_link/3]).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,terminate/2, code_change/3]).
+-export([start_link/5]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
 
--record(state, {port, path, backend}).
+-record(state, {event_handler, port, path, backend}).
 
-notify(file_event = A, Msg) -> Key = {fs, A}, gen_event:notify(fs_events, {self(), Key, Msg}).
-start_link(Backend, Path, Cwd) -> gen_server:start_link({local, ?SERVER}, ?MODULE, [Backend, Path, Cwd], []).
-init([Backend, Path, Cwd]) -> {ok, #state{port=Backend:start_port(Path, Cwd),path=Path,backend=Backend}}.
+notify(EventHandler, file_event = A, Msg) -> Key = {fs, A}, gen_event:notify(EventHandler, {self(), Key, Msg}).
+start_link(Name, EventHandler, Backend, Path, Cwd) -> gen_server:start_link({local, Name}, ?MODULE, [EventHandler, Backend, Path, Cwd], []).
+init([EventHandler, Backend, Path, Cwd]) -> {ok, #state{event_handler=EventHandler, port=Backend:start_port(Path, Cwd),path=Path,backend=Backend}}.
 
 handle_call(known_events, _From, #state{backend=Backend} = State) -> {reply, Backend:known_events(), State};
 handle_call(_Request, _From, State) -> {reply, ok, State}.
 handle_cast(_Msg, State) -> {noreply, State}.
-handle_info({_Port, {data, {eol, Line}}}, #state{backend=Backend} = State) ->
+handle_info({_Port, {data, {eol, Line}}}, #state{event_handler=EventHandler, backend=Backend} = State) ->
     Event = Backend:line_to_event(Line),
-    notify(file_event, Event),
+    notify(EventHandler, file_event, Event),
     {noreply, State};
 handle_info({_Port, {data, {noeol, Line}}}, State) ->
     error_logger:error_msg("~p line too long: ~p, ignoring~n", [?SERVER, Line]),

+ 10 - 7
src/fs_sup.erl

@@ -1,24 +1,26 @@
 -module(fs_sup).
 -behaviour(supervisor).
--export([start_link/0]).
+-export([start_link/4]).
 -export([init/1]).
 -define(CHILD(I, Type, Args), {I, {I, start_link, Args}, permanent, 5000, Type, [I]}).
 
-start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-init([]) ->
+start_link(SupName, EventHandler, FileHandler, Path) ->
+  supervisor:start_link({local, SupName}, ?MODULE, [EventHandler, FileHandler, Path]).
+init([EventHandler, FileHandler, Path]) ->
     Backend = case os:type() of
         {unix, darwin} -> fsevents;
         {unix, linux} -> inotifywait;
+        {unix, sunos} -> undefined;
         {unix, _} -> kqueue;
         {win32, nt} -> inotifywait_win32;
         _ -> undefined end,
 
     Children = case has_executable(Backend) of
         false -> [];
-        true  -> Path = fs:path(), [?CHILD(fs_server, worker, [Backend, Path, Path])] end,
+        true  -> [?CHILD(fs_server, worker, [FileHandler, EventHandler, Backend, Path, Path])] end,
 
     {ok, { {one_for_one, 5, 10},
-        Children ++ [?CHILD(gen_event, worker, [{local, fs_events}])]} }.
+        Children ++ [?CHILD(gen_event, worker, [{local, EventHandler}])]} }.
 
 has_executable(undefined) ->
     os_not_supported(), false;
@@ -28,7 +30,8 @@ has_executable(Backend) ->
         _     -> true end.
 
 os_not_supported() ->
-    error_logger:error_msg("fs does not support the current operating system~n",[]).
+  error_logger:warning_msg("fs does not support the current operating system: auto-reloading might not work~n", []).
 
 backend_port_not_found(Backend) ->
-    error_logger:error_msg("backend port not found: ~p~n",[Backend]).
+  error_logger:error_msg("backend port not found: ~p~n", [Backend]).
+

+ 8 - 7
src/sys/fsevents.erl

@@ -12,11 +12,12 @@ known_events() ->
 
 start_port(Path, Cwd) ->
     erlang:open_port({spawn_executable, find_executable()},
-        [stream, exit_status, {line, 16384}, {args, ["-F", Path]}, {cd, Cwd}]).
+        [stream, exit_status, binary, {line, 16384}, {args, ["-F", Path]}, {cd, Cwd}]).
 
-line_to_event(Line) ->
-    [_EventId, Flags1, Path] = string:tokens(Line, [$\t]),
-    [_, Flags2] = string:tokens(Flags1, [$=]),
-    {ok, T, _} = erl_scan:string(Flags2 ++ "."),
-    {ok, Flags} = erl_parse:parse_term(T),
-    {Path, Flags}.
+line_to_event(Line0) ->
+  Line = unicode:characters_to_list(Line0, utf8),
+  [_EventId, Flags1, Path] = string:tokens(Line, [$\t]),
+  [_, Flags2] = string:tokens(Flags1, [$=]),
+  {ok, T, _} = erl_scan:string(Flags2 ++ "."),
+  {ok, Flags} = erl_parse:parse_term(T),
+  {Path, Flags}.

+ 17 - 11
src/sys/inotifywait.erl

@@ -3,27 +3,33 @@
 -export(?API).
 
 find_executable() -> os:find_executable("inotifywait").
-known_events() -> [created, deleted, renamed, closed, modified, isdir, undefined].
+known_events() -> [created, deleted, renamed, closed, modified, isdir, attribute, undefined].
 
 start_port(Path, Cwd) ->
-    Path1 = filename:absname(Path),
-    Args = ["-c", "inotifywait $0 $@ & PID=$!; read a; kill $PID",
-            "-m", "-e", "close_write", "-e", "moved_to", "-e", "create", "-e", "delete", "-r", Path1],
-    erlang:open_port({spawn_executable, os:find_executable("sh")},
-        [stream, exit_status, {line, 16384}, {args, Args}, {cd, Cwd}]).
+  Path1 = filename:absname(Path),
+  Args = ["-c", "inotifywait \"$0\" \"$@\" & PID=$!; read a; kill $PID",
+          "-m", "-e", "modify", "-e", "close_write", "-e", "moved_to",
+          "-e", "moved_from", "-e", "create", "-e", "delete",
+          "-e", "attrib", "--quiet", "-r", Path1],
+  erlang:open_port({spawn_executable, os:find_executable("sh")},
+    [stream, exit_status, binary, {line, 16384}, {args, Args}, {cd, Cwd}]).
 
-line_to_event(Line) ->
-    {match, [Dir, Flags1, DirEntry]} = re:run(Line, re(), [{capture, all_but_first, list}]),
-    Flags = [convert_flag(F) || F <- string:tokens(Flags1, ",")],
-    Path = Dir ++ DirEntry,
-    {Path, Flags}.
+line_to_event(Line0) ->
+  Line = unicode:characters_to_list(Line0, utf8),
+  {match, [Dir, Flags1, DirEntry]} = re:run(Line, re(), [{capture, all_but_first, list}]),
+  Flags = [convert_flag(F) || F <- string:tokens(Flags1, ",")],
+  Path = Dir ++ DirEntry,
+  {Path, Flags}.
 
 convert_flag("CREATE") -> created;
 convert_flag("DELETE") -> deleted;
 convert_flag("ISDIR") -> isdir;
+convert_flag("MODIFY") -> modified;
 convert_flag("CLOSE_WRITE") -> modified;
 convert_flag("CLOSE") -> closed;
 convert_flag("MOVED_TO") -> renamed;
+convert_flag("MOVED_FROM") -> removed;
+convert_flag("ATTRIB") -> attribute;
 convert_flag(_) -> undefined.
 
 re() ->

+ 1 - 1
src/sys/inotifywait_win32.erl

@@ -23,7 +23,7 @@ line_to_event(Line) ->
 convert_flag("CREATE") -> created;
 convert_flag("MODIFY") -> modified;
 convert_flag("DELETE") -> removed;
-convert_flag("MOVE") -> renamed;
+convert_flag("MOVED_TO") -> renamed;
 convert_flag(_) -> undefined.
 
 re() ->

+ 2 - 1
src/sys/kqueue.erl

@@ -9,4 +9,5 @@ line_to_event(Line) ->
 find_executable() -> fs:find_executable("kqueue", "deps/fs/priv/kqueue").
 start_port(Path, Cwd) ->
     erlang:open_port({spawn_executable, find_executable()},
-        [stream, exit_status, {line, 16384}, {args, Path}, {cd, Cwd}]).
+        [stream, exit_status, {line, 16384}, {args, [Path]}, {cd, Cwd}]).
+