~ubuntu-branches/debian/squeeze/erlang/squeeze

« back to all changes in this revision

Viewing changes to lib/kernel/src/erl_ddll.erl

  • Committer: Bazaar Package Importer
  • Author(s): Erlang Packagers, Sergei Golovan
  • Date: 2006-12-03 17:07:44 UTC
  • mfrom: (2.1.11 feisty)
  • Revision ID: james.westby@ubuntu.com-20061203170744-rghjwupacqlzs6kv
Tags: 1:11.b.2-4
[ Sergei Golovan ]
Fixed erlang-base and erlang-base-hipe prerm scripts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
%%
18
18
%% Dynamic Driver Loader and Linker
19
19
%%
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.
22
 
%%
23
 
%% XXX To do:
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.
33
22
 
34
23
-module(erl_ddll).
35
24
 
36
 
-behaviour(gen_server).
37
 
 
38
 
-export([load_driver/2,unload_driver/1,loaded_drivers/0, format_error/1]).
39
 
-export([start/0, start_link/0, stop/0]).
40
 
 
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]).
44
 
 
45
 
%% Defines for ddll_drv.
46
 
 
47
 
-define(LOAD_DRIVER,$l).
48
 
-define(UNLOAD_DRIVER,$u).
49
 
-define(GET_DRIVERS,$g).
50
 
 
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]).
54
28
 
55
29
start() ->
56
 
    gen_server:start({local,ddll_server}, erl_ddll, [], []).
57
 
 
58
 
start_link() ->
59
 
    gen_server:start_link({local,ddll_server}, erl_ddll, [], []).
60
 
 
61
 
init([]) ->
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.
66
 
    case whereis(user) of
67
 
        Pid when pid(Pid) ->
68
 
            group_leader(Pid, self());
69
 
        _ -> ok
70
 
    end,
71
 
    Port = open_port({spawn, ddll}, []),
72
 
    {ok,Drivers} = loaded_drivers(Port),
73
 
    DynDrivers = [D || D <- Drivers, unload_driver(Port, D) == ok],
74
 
    case DynDrivers of
75
 
        [] -> ok;
76
 
        _ -> error_logger:error_msg(?MODULE_STRING" ~p forced unload of ~p~n", 
77
 
                                    [self(),DynDrivers])
78
 
    end,
79
 
    {ok, {Port, []}}.
80
 
 
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}).
88
 
 
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}}.
93
31
 
94
32
stop() ->
95
 
    req(stop).
96
 
 
97
 
loaded_drivers() ->
98
 
    req(loaded_drivers).
99
 
 
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".
112
 
 
113
 
req(Req) ->
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
117
 
            start(),
118
 
            gen_server:call(ddll_server, Req, infinity);
119
 
        {'EXIT',Reason} -> exit(Reason);
120
 
        Reply -> Reply
121
 
    end.
122
 
 
123
 
 
124
 
%%% --------------------------------------------------------
125
 
%%% The call-back functions.
126
 
%%% --------------------------------------------------------
127
 
 
128
 
handle_call({load_driver, Filename, Driver}, {From, _}, Data) ->
129
 
    {Result, NewData} = load_command(Filename, Driver, From, Data),
130
 
    {reply, Result, NewData};
131
 
 
132
 
handle_call({unload_driver, Driver}, {From, _}, Data) ->
133
 
    {Result, NewData} = unload_command(Driver, From, Data),
134
 
    {reply, Result, NewData};
135
 
 
136
 
handle_call(loaded_drivers, _From, {Port, Drivers}) ->
137
 
    {reply, loaded_drivers(Port), {Port, Drivers}};
138
 
 
139
 
handle_call(stop, _, Data) ->
140
 
    {stop, normal, ok, Data}.
141
 
 
142
 
handle_cast(_, Data) ->
143
 
    {noreply, Data}.
144
 
 
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"
152
 
                          "   ~p~n", [Other]),
153
 
    {noreply, Data}.
154
 
 
155
 
terminate(_Reason, {Port, Drivers}) ->
156
 
    unload_all_drivers(Drivers, Port),
157
 
    Port ! {self, close}.
158
 
 
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.
162
 
    {ok,State}.
163
 
 
164
 
 
165
 
 
166
 
%%% --------------------------------------------------------
167
 
%%% Implementation of server commands.
168
 
%%% --------------------------------------------------------
169
 
 
170
 
%% Loads a driver, or increments the reference count for the driver.
171
 
 
172
 
load_command(Filename, Driver, From, {Port, Drivers}) ->
173
 
    DriverAtom = list_to_atom(Driver),
174
 
    case increment_count(Drivers, From, DriverAtom, []) of
175
 
        {ok, NewDrivers} ->
176
 
            {ok, {Port, NewDrivers}};
177
 
        not_loaded ->
178
 
            case load_driver(Port, Filename, Driver) of
179
 
                ok ->
180
 
                    link(From),
181
 
                    {ok, {Port, [{DriverAtom, [{From, 1}]}|Drivers]}};
182
 
                Other ->
183
 
                    {Other, {Port, Drivers}}
 
33
    ok.
 
34
 
 
35
load_driver(Path, Driver) ->
 
36
    do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]).
 
37
 
 
38
load(Path, Driver) ->
 
39
    do_load_driver(Path, Driver, []).
 
40
 
 
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 
 
45
        {error, What} ->
 
46
            {error,What};
 
47
        {ok, already_loaded} ->
 
48
            ok;
 
49
        {ok,loaded} ->
 
50
            ok;
 
51
        {ok, pending_driver, Ref} ->
 
52
            receive
 
53
                {'DOWN', Ref, driver, Driver, load_cancelled} ->
 
54
                    {error, load_cancelled};
 
55
                {'UP', Ref, driver, Driver, permanent} ->
 
56
                    {error, permanent}; 
 
57
                {'DOWN', Ref, driver, Driver, {load_failure, Failure}} ->
 
58
                    {error, Failure};
 
59
                {'UP', Ref, driver, Driver, loaded} ->
 
60
                    ok
184
61
            end
185
62
    end.
186
63
 
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) ->
193
 
    not_loaded.
194
 
 
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) ->
200
 
    [{From, 1}|Result].
201
 
 
202
 
%% Decrements the reference count for Driver in process From,
203
 
%% and unloads the driver if this was the last reference.
204
 
 
205
 
unload_command(Driver, From, {Port, Drivers}) ->
206
 
    DriverAtom = list_to_atom(Driver),
207
 
    case decrement_count(Drivers, From, DriverAtom, []) of
208
 
        {ok, NewDrivers} ->
209
 
            {ok, {Port, NewDrivers}};
210
 
        {unload, NewDrivers} ->
211
 
            {unload_driver(Port, Driver), {Port, NewDrivers}};
212
 
        Error0 ->
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}}
216
 
    end.
217
 
 
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.)
222
 
 
223
 
transform_static_driver_error({error, not_loaded}, Driver, Port) ->
224
 
    {ok, All} = loaded_drivers(Port),
225
 
    case lists:member(Driver, All) of
226
 
        true ->
227
 
            {error, linked_in_driver};
228
 
        false ->
229
 
            {error, not_loaded}
230
 
    end;
231
 
transform_static_driver_error(Error, _, _) ->
232
 
    Error.
233
 
 
234
 
decrement_count([{Driver, Processes}|Rest], From, Driver, Result) ->
235
 
    case decrement_process_count(Processes, From, []) of
236
 
        [] ->
237
 
            {unload, Result ++ Rest};
238
 
        {error, Error} ->
239
 
            {error, Error};
240
 
        NewProcesses ->
241
 
            {ok, Result ++ [{Driver, NewProcesses}|Rest]}
242
 
    end;
243
 
decrement_count([Drv|Rest], From, Driver, Result) ->
244
 
    decrement_count(Rest, From, Driver, [Drv|Result]);
245
 
decrement_count([], _From, _Driver, _Result) ->
246
 
    {error, not_loaded}.
247
 
 
248
 
decrement_process_count([{From, 1}|Rest], From, Result) ->
249
 
    unlink(From),
250
 
    Result ++ Rest;
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}.
257
 
 
258
 
%% Unloads all drivers owned by Pid.
259
 
 
260
 
unload_drivers([{Driver, Processes}|Rest], Pid, Port, Result) ->
261
 
    case unload_process(Processes, Pid, []) of
262
 
        [] ->
263
 
            %% XXX There is a problem if this unload fails.
264
 
            case unload_driver(Port, atom_to_list(Driver)) of
265
 
                ok -> ok;
266
 
                Error ->
267
 
                    error_logger:error_msg(
268
 
                      ?MODULE_STRING" unload_driver(~p, ~p)~n"
269
 
                      "-> ~p~n"
270
 
                      "From ~p~n",
271
 
                      [Port,Driver,Error,Pid])
272
 
            end,
273
 
            unload_drivers(Rest, Pid, Port, Result);
274
 
        NewProcesses ->
275
 
            unload_drivers(Rest, Pid, Port, [{Driver, NewProcesses}|Result])
276
 
    end;
277
 
unload_drivers([], _Pid, _Port, Result) ->
278
 
    Result.
279
 
 
280
 
unload_process([{Pid, _}|Rest], Pid, Result) ->
281
 
    Result ++ Rest;
282
 
unload_process([P|Rest], Pid, Result) ->
283
 
    unload_process(Rest, Pid, [P|Result]);
284
 
unload_process([], _Pid, Result) ->
285
 
    Result.
286
 
 
287
 
%% Unloads all drivers (called when the server terminates).
288
 
 
289
 
unload_all_drivers([{Driver, _}|Rest], Port) ->
290
 
    case unload_driver(Port, atom_to_list(Driver)) of
291
 
        ok -> ok;
292
 
        Error ->
293
 
            error_logger:error_msg(
294
 
              ?MODULE_STRING" unload_driver(~p, ~p)~n"
295
 
              "-> ~p~n",
296
 
              [Port,Driver,Error])
297
 
    end,
298
 
    unload_all_drivers(Rest, Port);
299
 
unload_all_drivers([], _) ->
300
 
    ok.
301
 
 
302
 
 
303
 
%%% --------------------------------------------------------
304
 
%%% Talk to the linked in driver
305
 
%%% --------------------------------------------------------
306
 
 
307
 
load_driver(Port, FullName, Driver) ->
308
 
    command(Port, [?LOAD_DRIVER, Driver, 0, FullName, 0]).
309
 
 
310
 
unload_driver(Port, Driver) ->
311
 
    command(Port, [?UNLOAD_DRIVER, Driver, 0]).
312
 
 
313
 
loaded_drivers(Port) ->
314
 
    loaded_drivers(Port, command(Port, [?GET_DRIVERS]), []).
315
 
 
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, []) ->
321
 
    Status.
322
 
 
323
 
command(Port, Command) ->
324
 
    Port ! {self(), {command, Command}},
325
 
    get_response(Port).
326
 
 
327
 
get_response(Port) ->
328
 
    receive
329
 
        {Port, {data, [Status|Rest]}} ->
330
 
            get_response(Status, Rest);
331
 
        {'EXIT', Port, Reason} ->
332
 
            exit({'ddll_drv port died', Reason})
333
 
    end.
334
 
 
335
 
get_response($o, []) ->
336
 
    ok;
337
 
get_response($o, List) ->
338
 
    {ok, 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) ->
344
 
    {list_item, Item}.
345
 
 
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
 
66
       {error,What} ->
 
67
           {error,What};
 
68
       {ok, pending_process} ->
 
69
           ok;
 
70
       {ok, unloaded} ->
 
71
           ok;
 
72
       {ok, pending_driver, Ref} ->
 
73
           receive
 
74
               {'UP', Ref, driver, Driver, permanent} ->
 
75
                   {error, permanent}; 
 
76
               {'DOWN', Ref, driver, Driver, unloaded} ->
 
77
                   ok
 
78
           end
 
79
   end.
 
80
 
 
81
unload_driver(Driver) ->
 
82
    do_unload_driver(Driver,[kill_ports]).
 
83
    %do_unload_driver(Driver,[]).                           
 
84
unload(Driver) ->
 
85
    do_unload_driver(Driver,[]).
 
86
 
 
87
force_unload(Driver) ->
 
88
    do_unload_driver(Driver,[kill_ports]).
 
89
                            
 
90
format_error(Code) ->
 
91
    case Code of
 
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...
 
94
        load_cancelled ->
 
95
            "Loading was cancelled from other process";
 
96
        _ ->
 
97
            erl_ddll:format_error_int(Code)
 
98
    end.
 
99
    
 
100
info(Driver) ->
 
101
    [{name, Driver},
 
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)}].
 
109
 
 
110
info() ->
 
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...