18
18
%% Dynamic Driver Loader and Linker
20
%% Simple interface for dynamic library/shared object driver loader/linker.
20
%% Interface for dynamic library/shared object driver loader/linker.
21
21
%% Provides methods for loading, unloading and listing drivers.
24
%% - When starting, should place itself under a supervisor.
25
%% - After restart, must find out which drivers are loaded and
26
%% which ports use them. Alternative, if killed, should take
27
%% down the whole OTP.
28
%% = As a fix now, it sets its group leader to 'user' to not get
29
%% killed by the application_master that happens to start
30
%% ddll_server, and when started unloads all dynamically loaded
31
%% drivers, and thereby kills all ports belonging to them. This
32
%% should be done by the driver when its port owner disappears.
36
-behaviour(gen_server).
38
-export([load_driver/2,unload_driver/1,loaded_drivers/0, format_error/1]).
39
-export([start/0, start_link/0, stop/0]).
41
%% Internal exports, call-back functions.
42
-export([init/1,handle_call/3,handle_cast/2,handle_info/2,
43
terminate/2,code_change/3]).
45
%% Defines for ddll_drv.
47
-define(LOAD_DRIVER,$l).
48
-define(UNLOAD_DRIVER,$u).
49
-define(GET_DRIVERS,$g).
51
%%% --------------------------------------------------------
52
%%% Interface Functions.
53
%%% --------------------------------------------------------
25
-export([load_driver/2, load/2,
26
unload_driver/1, unload/1, force_unload/1,
27
format_error/1,info/1,info/0, start/0, stop/0]).
56
gen_server:start({local,ddll_server}, erl_ddll, [], []).
59
gen_server:start_link({local,ddll_server}, erl_ddll, [], []).
62
process_flag(trap_exit, true),
63
%% Must set group leader to something harmless. Otherwise this
64
%% server will be terminated (kill) when the application_master of the
65
%% invoking application is terminated.
68
group_leader(Pid, self());
71
Port = open_port({spawn, ddll}, []),
72
{ok,Drivers} = loaded_drivers(Port),
73
DynDrivers = [D || D <- Drivers, unload_driver(Port, D) == ok],
76
_ -> error_logger:error_msg(?MODULE_STRING" ~p forced unload of ~p~n",
81
load_driver(Path, Driver) when atom(Path) ->
82
load_driver(atom_to_list(Path), Driver);
83
load_driver(Path, Driver) when atom(Driver) ->
84
load_driver(Path, atom_to_list(Driver));
85
load_driver(Path, Driver) when list(Path), list(Driver) ->
86
FullName = filename:join(Path, Driver),
87
req({load_driver, FullName, Driver}).
89
unload_driver(Path) when atom(Path) ->
90
unload_driver(atom_to_list(Path));
91
unload_driver(Path) when list(Path) ->
92
req({unload_driver, Path}).
30
{error,{already_started,undefined}}.
100
format_error(already_loaded) -> "Driver already loaded";
101
format_error({open_error, Message}) -> "Open failed: " ++ Message;
102
format_error({close_error, Message}) -> "Close failed: " ++ Message;
103
format_error(no_driver_init) -> "No driver_init() function";
104
format_error(driver_init_failed) -> "Function driver_init() failed";
105
format_error(bad_driver_name) -> "Driver name doesn't match filename";
106
format_error(linked_in_driver) -> "Cannot unload linked-in driver";
107
format_error(driver_in_use) -> "Driver in use";
108
format_error(finish_failed) -> "Finish function failed";
109
format_error(not_loaded) -> "Driver not loaded";
110
format_error(not_loaded_by_this_process) -> "Driver not loaded by this process";
111
format_error(_) -> "Unknown error".
114
case catch gen_server:call(ddll_server, Req, infinity) of
115
{'EXIT',{noproc,_}} ->
116
erlang:yield(), % Give server time to die properly if dying
118
gen_server:call(ddll_server, Req, infinity);
119
{'EXIT',Reason} -> exit(Reason);
124
%%% --------------------------------------------------------
125
%%% The call-back functions.
126
%%% --------------------------------------------------------
128
handle_call({load_driver, Filename, Driver}, {From, _}, Data) ->
129
{Result, NewData} = load_command(Filename, Driver, From, Data),
130
{reply, Result, NewData};
132
handle_call({unload_driver, Driver}, {From, _}, Data) ->
133
{Result, NewData} = unload_command(Driver, From, Data),
134
{reply, Result, NewData};
136
handle_call(loaded_drivers, _From, {Port, Drivers}) ->
137
{reply, loaded_drivers(Port), {Port, Drivers}};
139
handle_call(stop, _, Data) ->
140
{stop, normal, ok, Data}.
142
handle_cast(_, Data) ->
145
handle_info({'EXIT', Port, Reason}, {Port, Drivers}) ->
146
{stop, {port_died, Reason}, {Port, Drivers}};
147
handle_info({'EXIT', Pid, _Reason}, {Port, Drivers}) ->
148
NewDrivers = unload_drivers(Drivers, Pid, Port, []),
149
{noreply, {Port, NewDrivers}};
150
handle_info(Other, Data) ->
151
error_logger:info_msg(?MODULE_STRING" received:~n"
155
terminate(_Reason, {Port, Drivers}) ->
156
unload_all_drivers(Drivers, Port),
157
Port ! {self, close}.
159
code_change(_OldVsn, State, _Extra) ->
160
%% I doubt that changing the code for this module will work,
161
%% but at least we avoid a compilation warning.
166
%%% --------------------------------------------------------
167
%%% Implementation of server commands.
168
%%% --------------------------------------------------------
170
%% Loads a driver, or increments the reference count for the driver.
172
load_command(Filename, Driver, From, {Port, Drivers}) ->
173
DriverAtom = list_to_atom(Driver),
174
case increment_count(Drivers, From, DriverAtom, []) of
176
{ok, {Port, NewDrivers}};
178
case load_driver(Port, Filename, Driver) of
181
{ok, {Port, [{DriverAtom, [{From, 1}]}|Drivers]}};
183
{Other, {Port, Drivers}}
35
load_driver(Path, Driver) ->
36
do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]).
39
do_load_driver(Path, Driver, []).
41
do_load_driver(Path, Driver, DriverFlags) ->
42
case erl_ddll:try_load(Path, Driver,[{monitor,pending}]++DriverFlags) of
43
{error, inconsistent} ->
44
{error,bad_driver_name}; % BC
47
{ok, already_loaded} ->
51
{ok, pending_driver, Ref} ->
53
{'DOWN', Ref, driver, Driver, load_cancelled} ->
54
{error, load_cancelled};
55
{'UP', Ref, driver, Driver, permanent} ->
57
{'DOWN', Ref, driver, Driver, {load_failure, Failure}} ->
59
{'UP', Ref, driver, Driver, loaded} ->
187
increment_count([{Driver, Processes}|Rest], From, Driver, Result) ->
188
NewProcesses = increment_process_count(Processes, From, []),
189
{ok, Result ++ [{Driver, NewProcesses}|Rest]};
190
increment_count([Drv|Rest], From, Driver, Result) ->
191
increment_count(Rest, From, Driver, [Drv|Result]);
192
increment_count([], _From, _Driver, _Result) ->
195
increment_process_count([{From, Count}|Rest], From, Result) ->
196
Result ++ [{From, Count+1}|Rest];
197
increment_process_count([Process|Rest], From, _Result) ->
198
increment_process_count(Rest, From, [Process|Rest]);
199
increment_process_count([], From, Result) ->
202
%% Decrements the reference count for Driver in process From,
203
%% and unloads the driver if this was the last reference.
205
unload_command(Driver, From, {Port, Drivers}) ->
206
DriverAtom = list_to_atom(Driver),
207
case decrement_count(Drivers, From, DriverAtom, []) of
209
{ok, {Port, NewDrivers}};
210
{unload, NewDrivers} ->
211
{unload_driver(Port, Driver), {Port, NewDrivers}};
213
%% XXX This is a problem -- this driver will never be unloaded.
214
Error = transform_static_driver_error(Error0, Driver, Port),
215
{Error, {Port, Drivers}}
218
%% If the error was "not_loaded" but the driver is in the list of
219
%% all drivers, it must be a statically linked driver, and we transform
220
%% the error message to "linked_in_driver". (If loaded_drivers/0 fails,
221
%% we get a badmatch.)
223
transform_static_driver_error({error, not_loaded}, Driver, Port) ->
224
{ok, All} = loaded_drivers(Port),
225
case lists:member(Driver, All) of
227
{error, linked_in_driver};
231
transform_static_driver_error(Error, _, _) ->
234
decrement_count([{Driver, Processes}|Rest], From, Driver, Result) ->
235
case decrement_process_count(Processes, From, []) of
237
{unload, Result ++ Rest};
241
{ok, Result ++ [{Driver, NewProcesses}|Rest]}
243
decrement_count([Drv|Rest], From, Driver, Result) ->
244
decrement_count(Rest, From, Driver, [Drv|Result]);
245
decrement_count([], _From, _Driver, _Result) ->
248
decrement_process_count([{From, 1}|Rest], From, Result) ->
251
decrement_process_count([{From, Count}|Rest], From, Result) ->
252
Result ++ [{From, Count-1}|Rest];
253
decrement_process_count([Process|Rest], From, Result) ->
254
decrement_process_count(Rest, From, [Process|Result]);
255
decrement_process_count([], _From, _Result) ->
256
{error, not_loaded_by_this_process}.
258
%% Unloads all drivers owned by Pid.
260
unload_drivers([{Driver, Processes}|Rest], Pid, Port, Result) ->
261
case unload_process(Processes, Pid, []) of
263
%% XXX There is a problem if this unload fails.
264
case unload_driver(Port, atom_to_list(Driver)) of
267
error_logger:error_msg(
268
?MODULE_STRING" unload_driver(~p, ~p)~n"
271
[Port,Driver,Error,Pid])
273
unload_drivers(Rest, Pid, Port, Result);
275
unload_drivers(Rest, Pid, Port, [{Driver, NewProcesses}|Result])
277
unload_drivers([], _Pid, _Port, Result) ->
280
unload_process([{Pid, _}|Rest], Pid, Result) ->
282
unload_process([P|Rest], Pid, Result) ->
283
unload_process(Rest, Pid, [P|Result]);
284
unload_process([], _Pid, Result) ->
287
%% Unloads all drivers (called when the server terminates).
289
unload_all_drivers([{Driver, _}|Rest], Port) ->
290
case unload_driver(Port, atom_to_list(Driver)) of
293
error_logger:error_msg(
294
?MODULE_STRING" unload_driver(~p, ~p)~n"
298
unload_all_drivers(Rest, Port);
299
unload_all_drivers([], _) ->
303
%%% --------------------------------------------------------
304
%%% Talk to the linked in driver
305
%%% --------------------------------------------------------
307
load_driver(Port, FullName, Driver) ->
308
command(Port, [?LOAD_DRIVER, Driver, 0, FullName, 0]).
310
unload_driver(Port, Driver) ->
311
command(Port, [?UNLOAD_DRIVER, Driver, 0]).
313
loaded_drivers(Port) ->
314
loaded_drivers(Port, command(Port, [?GET_DRIVERS]), []).
316
loaded_drivers(Port, {list_item, Item}, Result) ->
317
loaded_drivers(Port, get_response(Port), [Item|Result]);
318
loaded_drivers(_Port, ok, Result) ->
319
{ok, lists:reverse(Result)};
320
loaded_drivers(_Port, Status, []) ->
323
command(Port, Command) ->
324
Port ! {self(), {command, Command}},
327
get_response(Port) ->
329
{Port, {data, [Status|Rest]}} ->
330
get_response(Status, Rest);
331
{'EXIT', Port, Reason} ->
332
exit({'ddll_drv port died', Reason})
335
get_response($o, []) ->
337
get_response($o, List) ->
339
get_response($e, Atom) ->
340
{error, list_to_atom(Atom)};
341
get_response($E, Error) ->
342
get_error(Error, []);
343
get_response($i, Item) ->
346
get_error([0|Message], Atom) ->
347
{error, {list_to_atom(lists:reverse(Atom)), Message}};
348
get_error([C|Rest], Atom) ->
349
get_error(Rest, [C|Atom]).
64
do_unload_driver(Driver,Flags) ->
65
case erl_ddll:try_unload(Driver,[{monitor,pending_driver}]++Flags) of
68
{ok, pending_process} ->
72
{ok, pending_driver, Ref} ->
74
{'UP', Ref, driver, Driver, permanent} ->
76
{'DOWN', Ref, driver, Driver, unloaded} ->
81
unload_driver(Driver) ->
82
do_unload_driver(Driver,[kill_ports]).
83
%do_unload_driver(Driver,[]).
85
do_unload_driver(Driver,[]).
87
force_unload(Driver) ->
88
do_unload_driver(Driver,[kill_ports]).
92
% This is the only error code returned only from erlang code...
93
% 'permanent' has a translation in the emulator, even though the erlang code uses it to...
95
"Loading was cancelled from other process";
97
erl_ddll:format_error_int(Code)
102
{processes, erl_ddll:info(Driver,processes)},
103
{driver_options, erl_ddll:info(Driver,driver_options)},
104
{port_count, erl_ddll:info(Driver,port_count)},
105
{linked_in_driver, erl_ddll:info(Driver,linked_in_driver)},
106
{permanent, erl_ddll:info(Driver,permanent)},
107
{awaiting_load, erl_ddll:info(Driver,awaiting_load)},
108
{awaiting_unload, erl_ddll:info(Driver,awaiting_unload)}].
111
{ok,DriverList} = erl_ddll:loaded_drivers(),
112
[{X,Y} || X <- DriverList,
113
Y <- [catch info(X)],
114
is_list(Y)]. % The driver might have been unloaded between getting the list
115
% and calling info...