auser-poolparty 0.2.71 → 0.2.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +3 -0
- data/Manifest.txt +38 -81
- data/PostInstall.txt +1 -1
- data/README.txt +1 -0
- data/lib/erlang/messenger/Makefile +15 -0
- data/lib/erlang/messenger/lib/eunit/Makefile +28 -0
- data/lib/erlang/messenger/lib/eunit/ebin/autoload.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/code_monitor.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_autoexport.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_data.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_lib.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_proc.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_serial.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_server.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_striptests.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_test.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_tests.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_tty.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/file_monitor.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/src/Makefile +46 -0
- data/lib/poolparty/dependency_resolutions/puppet.rb +1 -0
- data/lib/poolparty/modules/resourcing_dsl.rb +5 -5
- data/lib/poolparty/plugins/deploydirectory.rb +1 -1
- data/lib/poolparty/pool/resource.rb +12 -3
- data/lib/poolparty/spec/core/string.rb +8 -2
- data/lib/poolparty/spec/matchers/a_spec_extensions_base.rb +3 -1
- data/lib/poolparty/spec/matchers/have_deploydirectory.rb +11 -11
- data/lib/poolparty/spec/matchers/have_gempackage.rb +1 -1
- data/lib/poolparty/version.rb +1 -1
- data/lib/poolpartyspec.rb +1 -3
- data/poolparty.gemspec +44 -87
- data/spec/poolparty/plugins/deploydirectory_spec.rb +1 -0
- data/spec/poolparty/pool/resource_spec.rb +13 -0
- data/spec/poolparty/spec/core/string_spec.rb +57 -0
- data/website/index.html +2 -2
- metadata +43 -86
- data/lib/erlang/messenger/lib/eunit/.svn/all-wcprops +0 -53
- data/lib/erlang/messenger/lib/eunit/.svn/entries +0 -140
- data/lib/erlang/messenger/lib/eunit/.svn/format +0 -1
- data/lib/erlang/messenger/lib/eunit/.svn/prop-base/NOTES.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/AUTHORS.svn-base +0 -2
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/CHANGELOG.svn-base +0 -14
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/COPYING.svn-base +0 -504
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/NOTES.svn-base +0 -276
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/README.svn-base +0 -3
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/sys.config.svn-base +0 -9
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/vsn.mk.svn-base +0 -1
- data/lib/erlang/messenger/lib/eunit/doc/.svn/all-wcprops +0 -59
- data/lib/erlang/messenger/lib/eunit/doc/.svn/entries +0 -142
- data/lib/erlang/messenger/lib/eunit/doc/.svn/format +0 -1
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/erlang.png.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/eunit.html.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/index.html.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/modules-frame.html.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/overview-summary.html.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/packages-frame.html.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/edoc-info.svn-base +0 -3
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/erlang.png.svn-base +0 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/eunit.html.svn-base +0 -172
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/index.html.svn-base +0 -17
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/modules-frame.html.svn-base +0 -12
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/overview-summary.html.svn-base +0 -984
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/overview.edoc.svn-base +0 -980
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/packages-frame.html.svn-base +0 -11
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/stylesheet.css.svn-base +0 -55
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/all-wcprops +0 -5
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/dir-prop-base +0 -8
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/entries +0 -28
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/format +0 -1
- data/lib/erlang/messenger/lib/eunit/examples/.svn/all-wcprops +0 -23
- data/lib/erlang/messenger/lib/eunit/examples/.svn/entries +0 -66
- data/lib/erlang/messenger/lib/eunit/examples/.svn/format +0 -1
- data/lib/erlang/messenger/lib/eunit/examples/.svn/prop-base/eunit_examples.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/examples/.svn/prop-base/fib.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/eunit_examples.erl.svn-base +0 -339
- data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/fib.erl.svn-base +0 -19
- data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/tests.txt.svn-base +0 -1
- data/lib/erlang/messenger/lib/eunit/include/.svn/all-wcprops +0 -11
- data/lib/erlang/messenger/lib/eunit/include/.svn/entries +0 -41
- data/lib/erlang/messenger/lib/eunit/include/.svn/format +0 -1
- data/lib/erlang/messenger/lib/eunit/include/.svn/prop-base/eunit.hrl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/include/.svn/text-base/eunit.hrl.svn-base +0 -313
- data/lib/erlang/messenger/lib/eunit/src/.svn/all-wcprops +0 -113
- data/lib/erlang/messenger/lib/eunit/src/.svn/entries +0 -259
- data/lib/erlang/messenger/lib/eunit/src/.svn/format +0 -1
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/autoload.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/code_monitor.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_autoexport.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_data.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_internal.hrl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_lib.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_proc.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_serial.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_server.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_striptests.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_test.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_tests.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_tty.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/file_monitor.erl.svn-base +0 -5
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/autoload.erl.svn-base +0 -388
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/code_monitor.erl.svn-base +0 -243
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.app.src.svn-base +0 -21
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.appup.src.svn-base +0 -1
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.erl.svn-base +0 -196
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_autoexport.erl.svn-base +0 -102
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_data.erl.svn-base +0 -798
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_internal.hrl.svn-base +0 -48
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_lib.erl.svn-base +0 -682
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_proc.erl.svn-base +0 -552
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_serial.erl.svn-base +0 -157
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_server.erl.svn-base +0 -340
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_striptests.erl.svn-base +0 -64
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_test.erl.svn-base +0 -334
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_tests.erl.svn-base +0 -45
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_tty.erl.svn-base +0 -272
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/file_monitor.erl.svn-base +0 -409
@@ -1,409 +0,0 @@
|
|
1
|
-
%% This library is free software; you can redistribute it and/or modify
|
2
|
-
%% it under the terms of the GNU Lesser General Public License as
|
3
|
-
%% published by the Free Software Foundation; either version 2 of the
|
4
|
-
%% License, or (at your option) any later version.
|
5
|
-
%%
|
6
|
-
%% This library is distributed in the hope that it will be useful, but
|
7
|
-
%% WITHOUT ANY WARRANTY; without even the implied warranty of
|
8
|
-
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
9
|
-
%% Lesser General Public License for more details.
|
10
|
-
%%
|
11
|
-
%% You should have received a copy of the GNU Lesser General Public
|
12
|
-
%% License along with this library; if not, write to the Free Software
|
13
|
-
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
14
|
-
%% USA
|
15
|
-
%%
|
16
|
-
%% $Id$
|
17
|
-
%%
|
18
|
-
%% @private (for now)
|
19
|
-
%% @author Richard Carlsson <richardc@it.uu.se>
|
20
|
-
%% @copyright 2006 Richard Carlsson
|
21
|
-
%% @doc Erlang file monitoring service
|
22
|
-
|
23
|
-
%% The behaviour of this service is inspired by the open source FAM
|
24
|
-
%% daemon [http://oss.sgi.com/projects/fam/].
|
25
|
-
|
26
|
-
-module(file_monitor).
|
27
|
-
|
28
|
-
-export([start/0, start/1, start/2, stop/0, stop/1, monitor_file/2,
|
29
|
-
monitor_file/3, monitor_dir/2, monitor_dir/3, demonitor/1,
|
30
|
-
demonitor/2]).
|
31
|
-
|
32
|
-
-export([main/1]). %% private
|
33
|
-
|
34
|
-
-include_lib("kernel/include/file.hrl").
|
35
|
-
|
36
|
-
-define(POLL_TIME, 5000). % default; change with option poll_time
|
37
|
-
-define(SERVER, file_monitor).
|
38
|
-
|
39
|
-
%% NOTE: paths should be absolute, but this is not checked
|
40
|
-
|
41
|
-
%% We don't change the paths, e.g. from relative to absolute, but we
|
42
|
-
%% make sure that the path is a flat string and return it to the caller.
|
43
|
-
|
44
|
-
monitor_file(Path, Pid) ->
|
45
|
-
monitor_file(?SERVER, Path, Pid).
|
46
|
-
|
47
|
-
monitor_file(Server, Path, Pid) ->
|
48
|
-
monitor(Server, file, filename:flatten(Path), Pid).
|
49
|
-
|
50
|
-
monitor_dir(Path, Pid) ->
|
51
|
-
monitor_dir(?SERVER, Path, Pid).
|
52
|
-
|
53
|
-
monitor_dir(Server, Path, Pid) ->
|
54
|
-
monitor(Server, dir, filename:flatten(Path), Pid).
|
55
|
-
|
56
|
-
monitor(Server, Type, Path, Pid) when is_pid(Pid) ->
|
57
|
-
{ok, Ref} = command(Server, {monitor, {Type, Path}, Pid}),
|
58
|
-
{ok, Path, Ref}.
|
59
|
-
|
60
|
-
demonitor(Ref) ->
|
61
|
-
demonitor(?SERVER, Ref).
|
62
|
-
|
63
|
-
demonitor(Server, Ref) ->
|
64
|
-
ok = command(Server, {demonitor, Ref}).
|
65
|
-
|
66
|
-
|
67
|
-
command(Server, Cmd) ->
|
68
|
-
ServerPid = ensure_started(Server),
|
69
|
-
ServerPid ! {command, self(), Cmd},
|
70
|
-
receive
|
71
|
-
{ServerPid, Result} -> Result
|
72
|
-
end.
|
73
|
-
|
74
|
-
|
75
|
-
stop() ->
|
76
|
-
stop(?SERVER).
|
77
|
-
|
78
|
-
stop(Server) ->
|
79
|
-
Server ! stop,
|
80
|
-
ok.
|
81
|
-
|
82
|
-
ensure_started(Name) when is_atom(Name) ->
|
83
|
-
start(Name, []);
|
84
|
-
ensure_started(Pid) when is_pid(Pid) ->
|
85
|
-
Pid.
|
86
|
-
|
87
|
-
start() ->
|
88
|
-
start([]).
|
89
|
-
|
90
|
-
start(Options) ->
|
91
|
-
start(?SERVER, Options).
|
92
|
-
|
93
|
-
start(Name, Options) ->
|
94
|
-
case whereis(Name) of
|
95
|
-
undefined ->
|
96
|
-
Parent = self(),
|
97
|
-
Pid = spawn(fun () -> server_init(Name, Parent, Options) end),
|
98
|
-
receive
|
99
|
-
{Pid, ok} -> Pid;
|
100
|
-
{Pid, error} -> throw(no_server)
|
101
|
-
end;
|
102
|
-
Pid -> Pid
|
103
|
-
end.
|
104
|
-
|
105
|
-
-record(state, {name, time, dirs, files, clients, refs}).
|
106
|
-
|
107
|
-
server_init(undefined = Name, Parent, Options) ->
|
108
|
-
%% anonymous server
|
109
|
-
server_init_1(Name, Parent, Options);
|
110
|
-
server_init(Name, Parent, Options) ->
|
111
|
-
case catch register(Name, self()) of
|
112
|
-
true ->
|
113
|
-
server_init_1(Name, Parent, Options);
|
114
|
-
_ ->
|
115
|
-
Parent ! {self(), error},
|
116
|
-
exit(failed)
|
117
|
-
end.
|
118
|
-
|
119
|
-
server_init_1(Name, Parent, Options) ->
|
120
|
-
Parent ! {self(), ok},
|
121
|
-
server(set_timer(init_state(Name, Options))).
|
122
|
-
|
123
|
-
init_state(Name, Options) ->
|
124
|
-
Time = case proplists:get_value(poll_time, Options) of
|
125
|
-
N when is_integer(N), N >= 100 -> N;
|
126
|
-
_ -> ?POLL_TIME
|
127
|
-
end,
|
128
|
-
#state{name = Name,
|
129
|
-
time = Time,
|
130
|
-
dirs = dict:new(),
|
131
|
-
files = dict:new(),
|
132
|
-
clients = dict:new(),
|
133
|
-
refs = dict:new()}.
|
134
|
-
|
135
|
-
server(St) -> ?MODULE:main(St).
|
136
|
-
|
137
|
-
%% @private
|
138
|
-
main(St) ->
|
139
|
-
receive
|
140
|
-
{command, From, {monitor, Object, Pid}} when is_pid(Pid) ->
|
141
|
-
{Ref, St1} = new_monitor(Object, Pid, St),
|
142
|
-
server_reply(From, {ok, Ref}),
|
143
|
-
server(add_client(Pid, St1));
|
144
|
-
{command, From, {demonitor, Ref}} ->
|
145
|
-
server_reply(From, ok),
|
146
|
-
server(delete_monitor(Ref, St));
|
147
|
-
stop ->
|
148
|
-
exit(normal);
|
149
|
-
time ->
|
150
|
-
server(set_timer(poll(St)));
|
151
|
-
{'DOWN', _Ref, process, Pid, _Info} ->
|
152
|
-
server(purge_pid(Pid, del_client(Pid, St)));
|
153
|
-
_ ->
|
154
|
-
server(St)
|
155
|
-
end.
|
156
|
-
|
157
|
-
server_reply(To, Msg) ->
|
158
|
-
To ! {self(), Msg}.
|
159
|
-
|
160
|
-
set_timer(St) ->
|
161
|
-
erlang:send_after(St#state.time, self(), time),
|
162
|
-
St.
|
163
|
-
|
164
|
-
|
165
|
-
%% client monitoring (once a client, always a client - until death)
|
166
|
-
|
167
|
-
add_client(Pid, St) ->
|
168
|
-
case dict:is_key(Pid, St#state.clients) of
|
169
|
-
true ->
|
170
|
-
St;
|
171
|
-
false ->
|
172
|
-
Ref = erlang:monitor(process, Pid),
|
173
|
-
St#state{clients = dict:store(Pid, Ref, St#state.clients)}
|
174
|
-
end.
|
175
|
-
|
176
|
-
del_client(Pid, St) ->
|
177
|
-
case dict:find(Pid, St#state.clients) of
|
178
|
-
{ok, Ref} ->
|
179
|
-
erlang:demonitor(Ref, [flush]),
|
180
|
-
St#state{clients = dict:erase(Pid, St#state.clients)};
|
181
|
-
error ->
|
182
|
-
St
|
183
|
-
end.
|
184
|
-
|
185
|
-
|
186
|
-
-record(monitor, {pid, reference}).
|
187
|
-
|
188
|
-
-record(entry, {info = undefined, files = [], monitors = sets:new()}).
|
189
|
-
|
190
|
-
new_monitor(Object, Pid, St) ->
|
191
|
-
Ref = make_ref(),
|
192
|
-
Monitor = #monitor{pid = Pid, reference = Ref},
|
193
|
-
new_monitor(Object, Monitor, Ref,
|
194
|
-
St#state{refs = dict:store(Ref, {Pid, Object},
|
195
|
-
St#state.refs)}).
|
196
|
-
|
197
|
-
%% We must separate the namespaces for files and dirs, since we can't
|
198
|
-
%% trust the users to keep them distinct; there may be simultaneous file
|
199
|
-
%% and dir monitors for the same path.
|
200
|
-
|
201
|
-
new_monitor({file, Path}, Monitor, Ref, St) ->
|
202
|
-
{Ref, St#state{files = add_monitor(Path, Monitor, file,
|
203
|
-
St#state.files)}};
|
204
|
-
new_monitor({dir, Path}, Monitor, Ref, St) ->
|
205
|
-
{Ref, St#state{dirs = add_monitor(Path, Monitor, dir,
|
206
|
-
St#state.dirs)}}.
|
207
|
-
|
208
|
-
%% Adding a new monitor forces an immediate poll of the file, such that
|
209
|
-
%% previous monitors only see any real change, while the new monitor
|
210
|
-
%% either gets {exists, ...} or {error, ...}.
|
211
|
-
|
212
|
-
add_monitor(Path, Monitor, Type, Dict) ->
|
213
|
-
Entry = case dict:find(Path, Dict) of
|
214
|
-
{ok, OldEntry} -> poll_file(Path, OldEntry, Type);
|
215
|
-
error -> new_entry(Path, Type)
|
216
|
-
end,
|
217
|
-
event(#entry{}, dummy_entry(Entry, Monitor), Type, Path),
|
218
|
-
NewEntry = Entry#entry{monitors =
|
219
|
-
sets:add_element(Monitor,
|
220
|
-
Entry#entry.monitors)},
|
221
|
-
dict:store(Path, NewEntry, Dict).
|
222
|
-
|
223
|
-
dummy_entry(Entry, Monitor) ->
|
224
|
-
Entry#entry{monitors = sets:add_element(Monitor, sets:new())}.
|
225
|
-
|
226
|
-
new_entry(Path, Type) ->
|
227
|
-
refresh_entry(Path, #entry{monitors = sets:new()}, Type).
|
228
|
-
|
229
|
-
%% deleting a monitor by reference
|
230
|
-
|
231
|
-
delete_monitor(Ref, St) ->
|
232
|
-
case dict:find(Ref, St#state.refs) of
|
233
|
-
{ok, {_Pid, Object}} ->
|
234
|
-
St1 = St#state{refs = dict:erase(Ref, St#state.refs)},
|
235
|
-
delete_monitor_1(Ref, Object, St1);
|
236
|
-
error ->
|
237
|
-
St
|
238
|
-
end.
|
239
|
-
|
240
|
-
delete_monitor_1(Ref, {file, Path}, St) ->
|
241
|
-
St#state{files = delete_monitor_2(Path, Ref, St#state.files)};
|
242
|
-
delete_monitor_1(Ref, {dir, Path}, St) ->
|
243
|
-
St#state{dirs = delete_monitor_2(Path, Ref, St#state.dirs)}.
|
244
|
-
|
245
|
-
delete_monitor_2(Path, Ref, Dict) ->
|
246
|
-
case dict:find(Path, Dict) of
|
247
|
-
{ok, Entry} ->
|
248
|
-
purge_empty_sets(
|
249
|
-
dict:store(Path, purge_monitor_ref(Ref, Entry), Dict));
|
250
|
-
error ->
|
251
|
-
Dict
|
252
|
-
end.
|
253
|
-
|
254
|
-
purge_monitor_ref(Ref, Entry) ->
|
255
|
-
Entry#entry{monitors =
|
256
|
-
sets:filter(fun (#monitor{reference = R})
|
257
|
-
when R == Ref -> false;
|
258
|
-
(_) -> true
|
259
|
-
end,
|
260
|
-
Entry#entry.monitors)}.
|
261
|
-
|
262
|
-
%% purging monitors belonging to dead clients
|
263
|
-
|
264
|
-
purge_pid(Pid, St) ->
|
265
|
-
Files = dict:map(fun (_Path, Entry) ->
|
266
|
-
purge_monitor_pid(Pid, Entry)
|
267
|
-
end,
|
268
|
-
St#state.files),
|
269
|
-
Dirs = dict:map(fun (_Path, Entry) ->
|
270
|
-
purge_monitor_pid(Pid, Entry)
|
271
|
-
end,
|
272
|
-
St#state.dirs),
|
273
|
-
Refs = dict:filter(fun (_Ref, {P, _})
|
274
|
-
when P == Pid -> false;
|
275
|
-
(_, _) -> true
|
276
|
-
end,
|
277
|
-
St#state.refs),
|
278
|
-
St#state{refs = Refs,
|
279
|
-
files = purge_empty_sets(Files),
|
280
|
-
dirs = purge_empty_sets(Dirs)}.
|
281
|
-
|
282
|
-
purge_monitor_pid(Pid, Entry) ->
|
283
|
-
Entry#entry{monitors =
|
284
|
-
sets:filter(fun (#monitor{pid = P})
|
285
|
-
when P == Pid -> false;
|
286
|
-
(_) -> true
|
287
|
-
end,
|
288
|
-
Entry#entry.monitors)}.
|
289
|
-
|
290
|
-
purge_empty_sets(Dict) ->
|
291
|
-
dict:filter(fun (_Path, Entry) ->
|
292
|
-
sets:size(Entry#entry.monitors) > 0
|
293
|
-
end, Dict).
|
294
|
-
|
295
|
-
|
296
|
-
%% generating events upon state changes
|
297
|
-
|
298
|
-
%% Message formats:
|
299
|
-
%% {exists, Path, Type, #file_info{}, Files}
|
300
|
-
%% {changed, Path, Type, #file_info{}, Files}
|
301
|
-
%% {error, Path, Type, Info}
|
302
|
-
%%
|
303
|
-
%% Type is dir or file. If Type is file, Files is always []. If Type is
|
304
|
-
%% dir, Files is a list of {added, FileName} and {deleted, FileName},
|
305
|
-
%% where FileName is relative to dir, without any path component.
|
306
|
-
%%
|
307
|
-
%% When a new monitor is installed for a path, an initial {exists,...}
|
308
|
-
%% or {error,...} message will be sent to the monitor owner.
|
309
|
-
%%
|
310
|
-
%% Subsequent events will be either {changed,...} or {error,...}.
|
311
|
-
|
312
|
-
event(#entry{info = Info}, #entry{info = Info}, _Type, _Path) ->
|
313
|
-
%% no change in state
|
314
|
-
ok;
|
315
|
-
event(#entry{info = undefined}, #entry{info = NewInfo}=Entry,
|
316
|
-
Type, Path)
|
317
|
-
when not is_atom(NewInfo) ->
|
318
|
-
%% file or directory exists, for a fresh monitor
|
319
|
-
Files = [{added, F} || F <- Entry#entry.files],
|
320
|
-
cast({exists, Path, Type, NewInfo, Files}, Entry#entry.monitors);
|
321
|
-
event(_OldEntry, #entry{info = NewInfo}=Entry, Type, Path)
|
322
|
-
when is_atom(NewInfo) ->
|
323
|
-
%% file is not available
|
324
|
-
cast({error, Path, Type, NewInfo}, Entry#entry.monitors);
|
325
|
-
event(_OldEntry, Entry, file, Path) ->
|
326
|
-
%% a normal file has changed
|
327
|
-
cast({changed, Path, file, Entry#entry.info, []},
|
328
|
-
Entry#entry.monitors);
|
329
|
-
event(#entry{info = OldInfo}, #entry{info = NewInfo}=Entry, dir, Path)
|
330
|
-
when is_atom(OldInfo) ->
|
331
|
-
%% a directory has suddenly become available
|
332
|
-
Files = [{added, F} || F <- Entry#entry.files],
|
333
|
-
cast({changed, Path, dir, NewInfo, Files}, Entry#entry.monitors);
|
334
|
-
event(OldEntry, #entry{info = NewInfo}=Entry, dir, Path) ->
|
335
|
-
%% a directory has changed
|
336
|
-
Files = diff_lists(Entry#entry.files, OldEntry#entry.files),
|
337
|
-
cast({changed, Path, dir, NewInfo, Files}, Entry#entry.monitors).
|
338
|
-
|
339
|
-
|
340
|
-
poll(St) ->
|
341
|
-
St#state{files = dict:map(fun (Path, Entry) ->
|
342
|
-
poll_file(Path, Entry, file)
|
343
|
-
end,
|
344
|
-
St#state.files),
|
345
|
-
dirs = dict:map(fun (Path, Entry) ->
|
346
|
-
poll_file(Path, Entry, dir)
|
347
|
-
end,
|
348
|
-
St#state.dirs)}.
|
349
|
-
|
350
|
-
poll_file(Path, Entry, Type) ->
|
351
|
-
NewEntry = refresh_entry(Path, Entry, Type),
|
352
|
-
event(Entry, NewEntry, Type, Path),
|
353
|
-
NewEntry.
|
354
|
-
|
355
|
-
refresh_entry(Path, Entry, Type) ->
|
356
|
-
Info = get_file_info(Path),
|
357
|
-
Files = case Type of
|
358
|
-
dir when not is_atom(Info) -> get_dir_files(Path);
|
359
|
-
_ -> []
|
360
|
-
end,
|
361
|
-
Entry#entry{info = Info, files = Files}.
|
362
|
-
|
363
|
-
|
364
|
-
%% We clear some fields of the file_info so that we only trigger on real
|
365
|
-
%% changes; see the //kernel/file.erl manual and file.hrl for details.
|
366
|
-
|
367
|
-
get_file_info(Path) ->
|
368
|
-
case file:read_file_info(Path) of
|
369
|
-
{ok, Info} ->
|
370
|
-
Info#file_info{access = undefined,
|
371
|
-
atime = undefined};
|
372
|
-
{error, Error} ->
|
373
|
-
Error % posix error code as atom
|
374
|
-
end.
|
375
|
-
|
376
|
-
%% Listing the members of a directory; note that it yields the empty
|
377
|
-
%% list if it fails - this is not the place for error detection.
|
378
|
-
|
379
|
-
get_dir_files(Path) ->
|
380
|
-
case file:list_dir(Path) of
|
381
|
-
{ok, Files} -> lists:sort(Files);
|
382
|
-
{error, _} -> []
|
383
|
-
end.
|
384
|
-
|
385
|
-
%% both lists must be sorted for this diff to work
|
386
|
-
|
387
|
-
diff_lists([F1 | Fs1], [F2 | _]=Fs2) when F1 < F2 ->
|
388
|
-
[{added, F1} | diff_lists(Fs1, Fs2)];
|
389
|
-
diff_lists([F1 | _]=Fs1, [F2 | Fs2]) when F1 > F2 ->
|
390
|
-
[{deleted, F2} | diff_lists(Fs1, Fs2)];
|
391
|
-
diff_lists([_ | Fs1], [_ | Fs2]) ->
|
392
|
-
diff_lists(Fs1, Fs2);
|
393
|
-
diff_lists([F | Fs1], Fs2) ->
|
394
|
-
[{added, F} | diff_lists(Fs1, Fs2)];
|
395
|
-
diff_lists(Fs1, [F | Fs2]) ->
|
396
|
-
[{deleted, F} | diff_lists(Fs1, Fs2)];
|
397
|
-
diff_lists([], []) ->
|
398
|
-
[].
|
399
|
-
|
400
|
-
|
401
|
-
%% Multicasting events to clients
|
402
|
-
|
403
|
-
cast(Msg, Monitors) ->
|
404
|
-
sets:fold(fun (#monitor{pid = Pid, reference = Ref}, Msg) ->
|
405
|
-
%%erlang:display({file_monitor, Ref, Msg}),
|
406
|
-
Pid ! {file_monitor, Ref, Msg},
|
407
|
-
Msg % note that this is a fold, not a map
|
408
|
-
end,
|
409
|
-
Msg, Monitors).
|