1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(ddll_SUITE).
22
23%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24%%% Checks if the dynamic driver and linker loader works.
25%%%
26%%% These tests can only be run installed (outside clearcase).
27%%%
28%%% XXX In this suite is missing test cases for reference counts
29%%% and that drivers are unloaded when their processes die.
30%%% (For me to add :-)
31%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32
33
34-export([all/0, suite/0,
35         ddll_test/1, errors/1, reference_count/1,
36         kill_port/1, dont_kill_port/1]).
37-export([unload_on_process_exit/1, delayed_unload_with_ports/1,
38         unload_due_to_process_exit/1,
39         no_unload_due_to_process_exit/1, no_unload_due_to_process_exit_2/1,
40         unload_reload_thingie/1, unload_reload_thingie_2/1,
41         unload_reload_thingie_3/1, reload_pending/1, reload_pending_kill/1,
42         load_fail_init/1,
43         reload_pending_fail_init/1,
44         more_error_codes/1, forced_port_killing/1,
45         no_trap_exit_and_kill_ports/1,
46         monitor_demonitor/1, monitor_demonitor_load/1, new_interface/1,
47         lock_driver/1]).
48
49% Private exports
50-export([echo_loader/2, nice_echo_loader/2 ,properties/1, load_and_unload/1]).
51
52-import(ordsets, [subtract/2]).
53
54-include_lib("common_test/include/ct.hrl").
55
56suite() ->
57    [{ct_hooks,[ts_install_cth]},
58     {timetrap, {minutes, 1}}].
59
60all() ->
61    [ddll_test, errors, reference_count, kill_port,
62     dont_kill_port, properties, load_and_unload,
63     unload_on_process_exit, delayed_unload_with_ports,
64     unload_due_to_process_exit,
65     no_unload_due_to_process_exit,
66     no_unload_due_to_process_exit_2, unload_reload_thingie,
67     unload_reload_thingie_2, unload_reload_thingie_3,
68     reload_pending, load_fail_init,
69     reload_pending_fail_init, reload_pending_kill,
70     more_error_codes, forced_port_killing,
71     no_trap_exit_and_kill_ports, monitor_demonitor,
72     monitor_demonitor_load, new_interface, lock_driver].
73
74%% Check that the driver is unloaded on process exit
75unload_on_process_exit(Config) when is_list(Config) ->
76    Path = proplists:get_value(data_dir, Config),
77    false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
78    Parent = self(),
79    Pid = spawn(fun() ->
80                        receive go -> ok end,
81                        erl_ddll:try_load(Path, echo_drv, []),
82                        Parent ! gone,
83                        receive go -> ok end,
84                        erl_ddll:loaded_drivers(),
85                        exit(banan)
86                end),
87    Ref = erlang:monitor(process,Pid),
88    false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
89    Pid ! go,
90    receive
91        gone -> ok
92    end,
93    true = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
94    Pid ! go,
95    receive
96        {'DOWN', Ref, process, Pid, banan} ->
97            ok
98    end,
99    receive after 500 -> ok end,
100    false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
101    ok.
102
103%% Check that the driver is unloaded when the last port is closed
104delayed_unload_with_ports(Config) when is_list(Config) ->
105    Path = proplists:get_value(data_dir, Config),
106    erl_ddll:try_load(Path, echo_drv, []),
107    erl_ddll:try_load(Path, echo_drv, []),
108    Port = open_port({spawn, echo_drv}, [eof]),
109    1 = erl_ddll:info(echo_drv, port_count),
110    Port2 = open_port({spawn, echo_drv}, [eof]),
111    2 = erl_ddll:info(echo_drv, port_count),
112    {ok,pending_process} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
113    {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
114    ok = receive _ -> false after 0 -> ok end,
115    Port ! {self(), close},
116    ok = receive {Port,closed} -> ok after 1000 -> false end,
117    1 = erl_ddll:info(echo_drv, port_count),
118    Port2 ! {self(), close},
119    ok = receive {Port2,closed} -> ok after 1000 -> false end,
120    ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end,
121    ok.
122
123%% Check that the driver with ports is unloaded on process exit
124unload_due_to_process_exit(Config) when is_list(Config) ->
125    Path = proplists:get_value(data_dir, Config),
126    Parent = self(),
127    F3 = fun() ->
128                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
129                 receive X -> Parent ! {got,X} end
130         end,
131    Pid = spawn(fun() ->
132                        receive go -> ok end,
133                        {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
134                        spawn(F3),
135                        receive go -> ok end,
136                        _Port = open_port({spawn, echo_drv}, [eof]),
137                        _Port2 = open_port({spawn, echo_drv}, [eof]),
138                        exit(banan)
139                end),
140    Ref = erlang:monitor(process,Pid),
141    Pid ! go,
142    {ok,Ref2} = receive
143                    R when is_reference(R) -> {ok,R};
144                    Other -> {error, Other}
145                after 500 -> {error, timeout}
146                end,
147    Pid ! go,
148    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
149    ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
150    ok.
151
152%% Check that a driver with driver loaded in another process is not unloaded on process exit
153no_unload_due_to_process_exit(Config) when is_list(Config) ->
154    Path = proplists:get_value(data_dir, Config),
155    Parent = self(),
156    F3 = fun() ->
157                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
158                 receive X -> Parent ! {got,X} end
159         end,
160    Pid = spawn(fun() ->
161                        receive go -> ok end,
162                        {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
163                        spawn(F3),
164                        receive go -> ok end,
165                        _Port = open_port({spawn, echo_drv}, [eof]),
166                        _Port2 = open_port({spawn, echo_drv}, [eof]),
167                        exit(banan)
168                end),
169    Ref = erlang:monitor(process,Pid),
170    Pid ! go,
171    {ok,Ref2} = receive
172                    R when is_reference(R) -> {ok,R};
173                    Other -> {error, Other}
174                after 500 -> {error, timeout}
175                end,
176    {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
177    Pid ! go,
178    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
179    ok = receive X -> {error, X} after 300 -> ok end,
180    ok = unload_expect_fast(echo_drv,[]),
181    ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
182    ok.
183
184%% Check that a driver with open ports in another process is not unloaded on process exit
185no_unload_due_to_process_exit_2(Config) when is_list(Config) ->
186    Path = proplists:get_value(data_dir, Config),
187    Parent = self(),
188    F3 = fun() ->
189                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
190                 receive X -> Parent ! {got,X} end
191         end,
192    Pid = spawn(fun() ->
193                        receive go -> ok end,
194                        {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
195                        spawn(F3),
196                        receive go -> ok end,
197                        _Port = open_port({spawn, echo_drv}, [eof]),
198                        _Port2 = open_port({spawn, echo_drv}, [eof]),
199                        exit(banan)
200                end),
201    Ref = erlang:monitor(process,Pid),
202    Pid ! go,
203    {ok,Ref2} = receive
204                    R when is_reference(R) -> {ok,R};
205                    Other -> {error, Other}
206                after 500 -> {error, timeout}
207                end,
208    Port = open_port({spawn, echo_drv}, [eof]),
209    Pid ! go,
210    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
211    ok = receive X -> {error, X} after 300 -> ok end,
212    erlang:port_close(Port),
213    ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
214    ok.
215
216%% Check delayed unload and reload
217unload_reload_thingie(Config) when is_list(Config) ->
218    Path = proplists:get_value(data_dir, Config),
219    Parent = self(),
220    {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
221    F3 = fun() ->
222                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
223                 receive X -> Parent ! {got,X} end
224         end,
225    Pid = spawn(fun() ->
226                        receive go -> ok end,
227                        _Port = open_port({spawn, echo_drv}, [eof]),
228                        spawn(F3),
229                        receive go -> ok end,
230                        exit(banan)
231                end),
232    Ref = erlang:monitor(process,Pid),
233    Pid ! go,
234    {ok,Ref2} = receive
235                    R when is_reference(R) -> {ok,R};
236                    Other -> {error, Other}
237                after 500 -> {error, timeout}
238                end,
239    {ok,pending_driver,Ref3} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
240    Ref4 = erl_ddll:monitor(driver,{echo_drv,loaded}),
241    ok = receive {'DOWN',Ref4, driver,echo_drv,load_cancelled} -> ok after 1000 -> false end,
242    {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
243    ok = receive {'UP',Ref3, driver,echo_drv,unload_cancelled} -> ok after 1000 -> false end,
244    Pid ! go,
245    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
246    [{Parent,1}] = erl_ddll:info(echo_drv, processes),
247    0 = erl_ddll:info(echo_drv, port_count),
248    ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
249    ok = receive
250             {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
251         after 300 -> error
252         end,
253    ok = receive X -> {error, X} after 300 -> ok end,
254    ok.
255
256%% Check delayed unload and reload
257unload_reload_thingie_2(Config) when is_list(Config) ->
258    Path = proplists:get_value(data_dir, Config),
259    Parent = self(),
260    {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
261    F3 = fun() ->
262                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
263                 receive X -> Parent ! {got,X} end
264         end,
265    Pid = spawn(fun() ->
266                        receive go -> ok end,
267                        _Port = open_port({spawn, echo_drv}, [eof]),
268                        spawn(F3),
269                        receive go -> ok end,
270                        exit(banan)
271                end),
272    Ref = erlang:monitor(process,Pid),
273    Pid ! go,
274    {ok,Ref2} = receive
275                    R when is_reference(R) -> {ok,R};
276                    Other -> {error, Other}
277                after 500 -> {error, timeout}
278                end,
279    {ok,pending_driver,Ref3} = erl_ddll:try_load(Path, echo_drv,
280                                                 [{monitor,pending_driver},{reload,pending_driver}]),
281    Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
282    Pid ! go,
283    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
284    ok = receive {'DOWN',Ref4, driver,echo_drv,unloaded} -> ok after 1000 -> false end,
285    ok = receive {'UP',Ref3, driver,echo_drv,loaded} -> ok after 1000 -> false end,
286    [{Parent,1}] = erl_ddll:info(echo_drv, processes),
287    0 = erl_ddll:info(echo_drv, port_count),
288    ok = receive
289             {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
290         after 300 -> error
291         end,
292    ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
293    ok = receive X -> {error, X} after 300 -> ok end,
294    ok.
295
296%% Check delayed unload and reload failure
297unload_reload_thingie_3(Config) when is_list(Config) ->
298    Path = proplists:get_value(data_dir, Config),
299    Parent = self(),
300    {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
301    F3 = fun() ->
302                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
303                 receive X -> Parent ! {got,X} end
304         end,
305    Pid = spawn(fun() ->
306                        receive go -> ok end,
307                        _Port = open_port({spawn, echo_drv}, [eof]),
308                        spawn(F3),
309                        receive go -> ok end,
310                        exit(banan)
311                end),
312    Ref = erlang:monitor(process,Pid),
313    Pid ! go,
314    {ok,Ref2} = receive
315                    R when is_reference(R) -> {ok,R};
316                    Other -> {error, Other}
317                after 500 -> {error, timeout}
318                end,
319    {ok,pending_driver,Ref3} = erl_ddll:try_load(filename:join([Path,"skrumpf"]), echo_drv,
320                                                 [{monitor,pending_driver},{reload,pending_driver}]),
321    Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
322    Pid ! go,
323    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
324    ok = receive
325             {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
326         after 300 -> error
327         end,
328    ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> false end,
329    ok = receive
330             {'DOWN',Ref3, driver,echo_drv,{load_failure,_}} -> ok
331         after 1000 -> false
332         end,
333    {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)),
334    {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
335    ok = receive X -> {error, X} after 300 -> ok end,
336    ok.
337
338%% Reload a driver that is pending on a user
339reload_pending(Config) when is_list(Config) ->
340    Path = proplists:get_value(data_dir, Config),
341    Parent = self(),
342    F3 = fun() ->
343                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
344                 receive X -> Parent ! {got,X} end
345         end,
346    Pid = spawn(fun() ->
347                        receive go -> ok end,
348                        {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
349                        spawn(F3),
350                        receive go -> ok end,
351                        _Port = open_port({spawn, echo_drv}, [eof]),
352                        _Port2 = open_port({spawn, echo_drv}, [eof]),
353                        Parent ! opened,
354                        receive go -> ok end,
355                        exit(banan)
356                end),
357    Ref = erlang:monitor(process,Pid),
358    Pid ! go,
359    {ok,Ref2} = receive
360                    R when is_reference(R) -> {ok,R};
361                    Other -> {error, Other}
362                after 500 -> {error, timeout}
363                end,
364    {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
365    Port = open_port({spawn, echo_drv}, [eof]),
366    Pid ! go,
367    receive opened -> ok end,
368    {error, pending_process} =
369    erl_ddll:try_load(Path, echo_drv,
370                      [{reload,pending_driver},
371                       {monitor,pending_driver}]),
372    {ok, pending_process, Ref3} =
373    erl_ddll:try_load(Path, echo_drv,
374                      [{reload,pending},
375                       {monitor,pending}]),
376    ok = receive X -> {error, X} after 300 -> ok end,
377    Pid ! go,
378    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
379    ok = receive Y -> {error, Y} after 300 -> ok end,
380    erlang:port_close(Port),
381    ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
382    ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
383    [{Parent,1}] = erl_ddll:info(echo_drv,processes),
384    ok = receive Z -> {error, Z} after 300 -> ok end,
385    ok.
386
387%% Tests failure in the init in driver struct.
388load_fail_init(Config) when is_list(Config) ->
389    Path = proplists:get_value(data_dir, Config),
390    PathFailing = proplists:get_value(priv_dir, Config),
391    [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
392    lists:foreach(fun(Name) ->
393                          Src = filename:join([Path,Name]),
394                          Ext = filename:extension(Name),
395                          Dst =filename:join([PathFailing,"echo_drv"++Ext]),
396                          file:delete(Dst),
397                          {ok,_} = file:copy(Src,Dst)
398                  end,
399                  AllFailInits),
400    [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
401    {error, driver_init_failed} = erl_ddll:try_load(PathFailing,
402                                                    echo_drv,
403                                                    [{monitor,pending}]),
404    ok = receive XX ->
405                     {unexpected,XX}
406         after 300 ->
407                   ok
408         end,
409    ok.
410
411
412%% Reload a driver that is pending but init fails
413reload_pending_fail_init(Config) when is_list(Config) ->
414    Path = proplists:get_value(data_dir, Config),
415    PathFailing = proplists:get_value(priv_dir, Config),
416    [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
417    lists:foreach(fun(Name) ->
418                          Src = filename:join([Path,Name]),
419                          Ext = filename:extension(Name),
420                          Dst =filename:join([PathFailing,"echo_drv"++Ext]),
421                          file:delete(Dst),
422                          {ok,_} = file:copy(Src,Dst)
423                  end,
424                  AllFailInits),
425    [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
426    Parent = self(),
427    F3 = fun() ->
428                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
429                 receive X -> Parent ! {got,X} end
430         end,
431    Pid = spawn(fun() ->
432                        receive go -> ok end,
433                        {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
434                        spawn(F3),
435                        receive go -> ok end,
436                        _Port = open_port({spawn, echo_drv}, [eof]),
437                        _Port2 = open_port({spawn, echo_drv}, [eof]),
438                        Parent ! opened,
439                        receive go -> ok end,
440                        exit(banan)
441                end),
442    Ref = erlang:monitor(process,Pid),
443    Pid ! go,
444    {ok,Ref2} = receive
445                    R when is_reference(R) -> {ok,R};
446                    Other -> {error, Other}
447                after 500 -> {error, timeout}
448                end,
449    {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
450    Port = open_port({spawn, echo_drv}, [eof]),
451    Pid ! go,
452    receive opened -> ok end,
453    {ok, pending_process, Ref3} =
454    erl_ddll:try_load(PathFailing, echo_drv,
455                      [{reload,pending},
456                       {monitor,pending}]),
457    ok = receive X -> {error, X} after 300 -> ok end,
458    Pid ! go,
459    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
460    ok = receive Y -> {error, Y} after 300 -> ok end,
461    erlang:port_close(Port),
462    ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
463    ok = receive {'DOWN', Ref3, driver, echo_drv, {load_failure,driver_init_failed}} -> ok  after 300 -> error end,
464    {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)),
465
466    ok = receive Z -> {error, Z} after 300 -> ok end,
467    ok.
468
469%% Reload a driver with kill_ports option that is pending on a user
470reload_pending_kill(Config) when is_list(Config) ->
471    OldFlag = process_flag(trap_exit,true),
472    Path = proplists:get_value(data_dir, Config),
473    Parent = self(),
474    F3 = fun() ->
475                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
476                 receive X -> Parent ! {got,X} end
477         end,
478    Pid = spawn(fun() ->
479                        process_flag(trap_exit,true),
480                        receive go -> ok end,
481                        {ok, loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
482                        spawn(F3),
483                        receive go -> ok end,
484                        Port = open_port({spawn, echo_drv}, [eof]),
485                        Port2 = open_port({spawn, echo_drv}, [eof]),
486                        Parent ! opened,
487                        receive go -> ok end,
488                        receive
489                            {'EXIT', Port2, driver_unloaded} ->
490                                Parent ! first_exit
491                        end,
492                        receive
493                            {'EXIT', Port, driver_unloaded} ->
494                                Parent ! second_exit
495                        end,
496                        receive go -> ok end,
497                        exit(banan)
498                end),
499    Ref = erlang:monitor(process,Pid),
500    Pid ! go,
501    {ok,Ref2} = receive
502                    R when is_reference(R) -> {ok,R};
503                    Other -> {error, Other}
504                after 500 -> {error, timeout}
505                end,
506    {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
507    {error,inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
508    Port = open_port({spawn, echo_drv}, [eof]),
509    Pid ! go,
510    receive opened -> ok end,
511    {error, pending_process} =
512    erl_ddll:try_load(Path, echo_drv,
513                      [{driver_options,[kill_ports]},
514                       {reload,pending_driver},
515                       {monitor,pending_driver}]),
516    {ok, pending_process, Ref3} =
517    erl_ddll:try_load(Path, echo_drv,
518                      [{driver_options,[kill_ports]},
519                       {reload,pending},
520                       {monitor,pending}]),
521    ok =  receive
522              {'EXIT', Port, driver_unloaded} ->
523                  ok
524          after 300 -> error
525          end,
526    Pid ! go,
527    ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
528    ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
529    [_,_] = erl_ddll:info(echo_drv,processes),
530    ok = receive first_exit -> ok after 300 -> error end,
531    ok = receive second_exit -> ok after 300 -> error end,
532    0 = erl_ddll:info(echo_drv,port_count),
533    ok = receive X -> {error, X} after 300 -> ok end,
534    Pid ! go,
535    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
536    ok = receive Y -> {error, Y} after 300 -> ok end,
537    Port2 = open_port({spawn, echo_drv}, [eof]),
538    true = is_port(Port2),
539    [{Parent,1}] = erl_ddll:info(echo_drv,processes),
540    1 = erl_ddll:info(echo_drv,port_count),
541    erlang:port_close(Port2),
542    ok = receive {'EXIT', Port2, normal} -> ok after 300 -> error end,
543    0 = erl_ddll:info(echo_drv,port_count),
544    [{Parent,1}] = erl_ddll:info(echo_drv,processes),
545    Port3 = open_port({spawn, echo_drv}, [eof]),
546    {ok, pending_driver, Ref4} =
547    erl_ddll:try_unload(echo_drv,[{monitor,pending_driver}]),
548    ok =  receive
549              {'EXIT', Port3, driver_unloaded} ->
550                  ok
551          after 300 -> error
552          end,
553    ok = receive {'DOWN', Ref4, driver, echo_drv, unloaded} -> ok after 300 -> error end,
554    io:format("Port = ~w, Port2 = ~w, Port3 = ~w~n",[Port,Port2,Port3]),
555    ok = receive Z -> {error, Z} after 300 -> ok end,
556    process_flag(trap_exit,OldFlag),
557    ok.
558
559
560%% Some more error code checking
561more_error_codes(Config) when is_list(Config) ->
562    {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]),
563    true = is_list(erl_ddll:format_error(Err)),
564    true = is_list(erl_ddll:format_error(not_loaded)),
565    ok.
566
567%% Check kill_ports option to try_unload
568forced_port_killing(Config) when is_list(Config) ->
569    Path = proplists:get_value(data_dir, Config),
570    OldFlag=process_flag(trap_exit,true),
571    Parent = self(),
572    F3 = fun() ->
573                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
574                 receive X -> Parent ! {got,X} end
575         end,
576    {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
577    spawn(F3),
578    {ok,Ref2} = receive
579                    R when is_reference(R) -> {ok,R};
580                    Other -> {error, Other}
581                after 500 -> {error, timeout}
582                end,
583    Port = open_port({spawn, echo_drv}, [eof]),
584    Port2 = open_port({spawn, echo_drv}, [eof]),
585    {ok, pending_driver, Ref1} =
586    erl_ddll:try_unload(echo_drv,[{monitor,pending_driver},kill_ports]),
587    ok = receive
588             {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok
589         after 300 -> error
590         end,
591    ok = receive {'EXIT',Port,driver_unloaded} -> ok after 300 -> false end,
592    ok = receive {'EXIT',Port2,driver_unloaded} -> ok after 300 -> false end,
593    ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end,
594    process_flag(trap_exit,OldFlag),
595    ok = receive X -> {error, X} after 300 -> ok end,
596    ok.
597
598%% Check delayed unload and reload with no trap_exit
599no_trap_exit_and_kill_ports(Config) when is_list(Config) ->
600    Path = proplists:get_value(data_dir, Config),
601    Parent = self(),
602    OldFlag=process_flag(trap_exit,true),
603    F3 = fun() ->
604                 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}),
605                 receive X -> Parent ! {got,X} end
606         end,
607    Pid = spawn(fun() ->
608                        process_flag(trap_exit,false),
609                        receive go -> ok end,
610                        {ok, loaded} = erl_ddll:try_load(Path, echo_drv,
611                                                         [{driver_options,[kill_ports]}]),
612                        spawn(F3),
613                        receive go -> ok end,
614                        _Port = open_port({spawn, echo_drv}, [eof]),
615                        _Port2 = open_port({spawn, echo_drv}, [eof]),
616                        exit(banan)
617                end),
618    Ref = erlang:monitor(process,Pid),
619    Pid ! go,
620    {ok,Ref2} = receive
621                    R when is_reference(R) -> {ok,R};
622                    Other -> {error, Other}
623                after 500 -> {error, timeout}
624                end,
625    {error, inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
626    MyPort = open_port({spawn, echo_drv}, [eof]),
627    Pid ! go,
628    ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
629    ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
630    ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end,
631    process_flag(trap_exit,OldFlag),
632    ok.
633
634%% Check monitor and demonitor of drivers
635monitor_demonitor(Config) when is_list(Config) ->
636    Path = proplists:get_value(data_dir, Config),
637    erl_ddll:try_load(Path, echo_drv, []),
638    Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
639    Self = self(),
640    [{Self,1}] = erl_ddll:info(echo_drv,awaiting_unload),
641    true = erl_ddll:demonitor(Ref),
642    [] = erl_ddll:info(echo_drv,awaiting_unload),
643    erl_ddll:try_unload(echo_drv,[]),
644    ok = receive _ -> error after 300 -> ok end,
645    ok.
646
647%% Check monitor/demonitor of driver loading
648monitor_demonitor_load(Config) when is_list(Config) ->
649    Path = proplists:get_value(data_dir, Config),
650    {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []),
651    Port = open_port({spawn, echo_drv}, [eof]),
652    Ref = erl_ddll:monitor(driver,{echo_drv,loaded}),
653    ok = receive {'UP',Ref,driver,echo_drv,loaded} -> ok after 500 -> error end,
654    {ok, pending_driver} = erl_ddll:try_unload(echo_drv,[]),
655    Ref2 = erl_ddll:monitor(driver,{echo_drv,loaded}),
656    ok = receive {'DOWN',Ref2,driver,echo_drv,load_cancelled} -> ok after 0 -> error end,
657    {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
658    {ok, pending_driver} =
659    erl_ddll:try_load(Path, echo_drv, [{reload,pending_driver}]),
660    Ref3 = erl_ddll:monitor(driver,{echo_drv,loaded}),
661    Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
662    ok = receive _ -> error after 300 -> ok end,
663    Self = self(),
664    [{Self,1}] = erl_ddll:info(echo_drv,awaiting_load),
665    true = erl_ddll:demonitor(Ref3),
666    [] = erl_ddll:info(echo_drv,awaiting_load),
667    erlang:port_close(Port),
668    ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end,
669    ok = receive _ -> error after 300 -> ok end,
670    ok = unload_expect_fast(echo_drv,[]),
671    ok.
672
673%% Test the new load/unload/reload interface
674new_interface(Config) when is_list(Config) ->
675    Path = proplists:get_value(data_dir, Config),
676    % Typical scenario
677    ok = erl_ddll:load(Path, echo_drv),
678    Port = open_port({spawn, echo_drv}, [eof]),
679    ok = erl_ddll:unload(echo_drv),
680    Port ! {self(), {command, "text"}},
681    ok = receive
682             {Port, {data, "text"}} -> ok;
683             _ -> error
684         after
685             1000 -> error
686         end,
687    Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
688    ok = receive X -> {error, X} after 300 -> ok end,
689    erlang:port_close(Port),
690    ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 300 -> error end,
691    % More than one user
692    ok = erl_ddll:load(Path, echo_drv),
693    Ref2 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
694    ok = erl_ddll:load(Path, echo_drv),
695    ok = erl_ddll:load(Path, echo_drv),
696    Port2 = open_port({spawn, echo_drv}, [eof]),
697    ok = erl_ddll:unload(echo_drv),
698    Port2 ! {self(), {command, "text"}},
699    ok = receive
700             {Port2, {data, "text"}} -> ok;
701             _ -> error
702         after
703             1000 -> error
704         end,
705    ok = erl_ddll:unload(echo_drv),
706    Port2 ! {self(), {command, "text"}},
707    ok = receive
708             {Port2, {data, "text"}} -> ok;
709             _ -> error
710         after
711             1000 -> error
712         end,
713    ok = erl_ddll:unload(echo_drv),
714    Port2 ! {self(), {command, "text"}},
715    ok = receive
716             {Port2, {data, "text"}} -> ok;
717             _ -> error
718         after
719             1000 -> error
720         end,
721    ok = receive X2 -> {error, X2} after 300 -> ok end,
722    ok = erl_ddll:load(Path, echo_drv),
723    ok = receive {'UP', Ref2, driver, echo_drv, unload_cancelled} -> ok after 300 -> error end,
724    Ref3 = erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
725    erlang:port_close(Port2),
726    ok = receive X3 -> {error, X3} after 300 -> ok end,
727    ok = erl_ddll:unload(echo_drv),
728    ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end,
729    ok.
730
731
732
733
734ddll_test(Config) when is_list(Config) ->
735    Path = proplists:get_value(data_dir, Config),
736
737    %{error,{already_started,ErlDdllPid}} = erl_ddll:start(),
738    %ErlDdllPid = whereis(ddll_server),
739
740    %% Load the echo driver and verify that it was loaded.
741    {ok,L1,L2}=load_echo_driver(Path),
742
743    %% Verify that the driver works.
744
745    Port = open_port({spawn, echo_drv}, [eof]),
746    {hej, "hopp",4711,123445567436543653} =
747    erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
748    {hej, "hopp",4711,123445567436543653} =
749    erlang:port_call(Port,47,{hej, "hopp",4711,123445567436543653}),
750    Port ! {self(), {command, "text"}},
751    1 = receive
752            {Port, {data, "text"}} -> 1;
753            _Other -> 2
754        after
755            1000 -> 2
756        end,
757    Port ! {self(), close},
758    receive {Port, closed} -> ok end,
759
760    %%     %% Unload the driver and verify that it was unloaded.
761    ok = unload_echo_driver(L1,L2),
762
763    %%     %{error, {already_started, _}} = erl_ddll:start(),
764    ok.
765
766%% Tests errors having to do with bad drivers.
767
768errors(Config) when is_list(Config) ->
769    Path = proplists:get_value(data_dir, Config),
770
771    {ok, L1} = erl_ddll:loaded_drivers(),
772
773    {error, {open_error, _}} = erl_ddll:load_driver(Path, bad_name),
774    {error, driver_init_failed} = erl_ddll:load_driver(Path, initfail_drv),
775    {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv),
776
777    %% We assume that there is a statically linked driver named "ddll":
778    {error, linked_in_driver} = erl_ddll:unload_driver(ram_file_drv),
779    {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"),
780
781    case os:type() of
782        {unix, _} ->
783            {error, no_driver_init} =
784            erl_ddll:load_driver(Path, noinit_drv);
785        _ ->
786            ok
787    end,
788
789    {ok, L1} = erl_ddll:loaded_drivers(),
790    ok.
791
792%% Check that drivers are unloaded when their reference count
793%% reaches zero, and that they cannot be unloaded while
794%% they are still referenced.
795reference_count(Config) when is_list(Config) ->
796    Path = proplists:get_value(data_dir, Config),
797
798    %% Spawn a process that loads the driver (and holds a reference
799    %% to it).
800    Pid1=spawn_link(?MODULE, echo_loader, [Path, self()]),
801    receive
802        {Pid1, echo_loaded} -> ok
803    after 2000 -> ct:fail("echo_loader failed to start.")
804    end,
805
806    Pid1 ! {self(), die},
807    test_server:sleep(200),   % Give time to unload.
808    % Verify that the driver was automatically unloaded when the
809    % process died.
810    {error, not_loaded}=erl_ddll:unload_driver(echo_drv),
811    ok.
812
813% Loads the echo driver, send msg to started, sits and waits to
814% get a signal to die, then unloads the driver and terminates.
815echo_loader(Path, Starter) ->
816    {ok, L1, L2}=load_echo_driver(Path),
817    Starter ! {self(), echo_loaded},
818    receive
819        {Starter, die} ->
820            unload_echo_driver(L1,L2)
821    end.
822
823% Loads the echo driver, send msg to started, sits and waits to
824% get a signal to die, then unloads the driver and terminates.
825nice_echo_loader(Path, Starter) ->
826    {ok, L1, L2}=load_nice_echo_driver(Path),
827    Starter ! {self(), echo_loaded},
828    receive
829        {Starter, die} ->
830            unload_echo_driver(L1,L2)
831    end.
832
833
834%% Test that a port that uses a driver is killed when the
835%% process that loaded the driver dies.
836kill_port(Config) when is_list(Config) ->
837    Path = proplists:get_value(data_dir, Config),
838
839    %% Spawn a process that loads the driver (and holds a reference
840    %% to it).
841    Pid1=spawn(?MODULE, echo_loader, [Path, self()]),
842    receive
843        {Pid1, echo_loaded} ->
844            ok
845    after 3000 ->
846              exit(Pid1, kill),
847              ct:fail("echo_loader failed to start.")
848    end,
849
850    % Spawn off a port that uses the driver.
851    Port = open_port({spawn, echo_drv}, [eof]),
852
853    % Kill the process / unload the driver.
854    process_flag(trap_exit, true),
855    exit(Pid1, kill),
856    test_server:sleep(200),    % Give some time to unload.
857    {error, not_loaded} = erl_ddll:unload_driver(echo_drv),
858
859    % See if the port is killed.
860    receive
861        {'EXIT', Port, Reason} ->
862            io:format("Port exited with reason ~w", [Reason])
863    after 5000 ->
864              ct:fail("Echo port did not terminate.")
865    end,
866    ok.
867
868%% Test that a port that uses a driver is not killed when the
869%% process that loaded the driver dies and it's nicely opened.
870dont_kill_port(Config) when is_list(Config) ->
871    Path = proplists:get_value(data_dir, Config),
872
873    %% Spawn a process that loads the driver (and holds a reference
874    %% to it).
875    Pid1=spawn(?MODULE, nice_echo_loader, [Path, self()]),
876    receive
877        {Pid1, echo_loaded} ->
878            ok
879    after 3000 ->
880              exit(Pid1, kill),
881              ct:fail("echo_loader failed to start.")
882    end,
883
884    % Spawn off a port that uses the driver.
885    Port = open_port({spawn, echo_drv}, [eof]),
886
887    % Kill the process / unload the driver.
888    process_flag(trap_exit, true),
889    exit(Pid1, kill),
890    test_server:sleep(200),    % Give some time to unload.
891    {hej, "hopp",4711,123445567436543653} =
892    erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
893    [] = erl_ddll:info(echo_drv,processes),
894    %% unload should work with no owner
895    ok = erl_ddll:unload_driver(echo_drv), %Kill ports while at it
896
897    % See if the port is killed.
898    receive
899        {'EXIT', Port, Reason} ->
900            io:format("Port exited with reason ~w", [Reason])
901    after 5000 ->
902              ct:fail("Echo port did not terminate.")
903    end,
904    ok.
905
906%% Test that a process that loaded a driver
907%% is the only process that can unload it.
908properties(Config) when is_list(Config) ->
909    Path = proplists:get_value(data_dir, Config),
910
911    % Let another process load the echo driver.
912    Pid=spawn_link(?MODULE, echo_loader, [Path, self()]),
913    receive
914        {Pid, echo_loaded} -> ok
915    after 2000 -> ct:fail("echo_loader failed to start.")
916    end,
917
918    % Try to unload the driver from this process (the wrong one).
919    {error, _} = erl_ddll:unload_driver(echo_drv),
920    {ok, Drivers} = erl_ddll:loaded_drivers(),
921    case lists:member("echo_drv", Drivers) of
922        true ->
923            ok;
924        false ->
925            ct:fail("Unload from wrong process succeeded.")
926    end,
927
928    % Unload the driver and terminate dummy process.
929    Pid ! {self(), die},
930    test_server:sleep(200),   % Give time to unload.
931    ok.
932
933%% Load two drivers and unload them in load order.
934load_and_unload(Config) when is_list(Config) ->
935    Path = proplists:get_value(data_dir, Config),
936    {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(),
937    ok = erl_ddll:load_driver(Path, echo_drv),
938    ok = erl_ddll:load_driver(Path, dummy_drv),
939    ok = erl_ddll:unload_driver(echo_drv),
940    ok = erl_ddll:unload_driver(dummy_drv),
941    {ok, Loaded_drivers2} = erl_ddll:loaded_drivers(),
942    Set1 = ordsets:from_list(Loaded_drivers1),
943    Set2 = ordsets:from_list(Loaded_drivers2),
944    io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]),
945    [] = ordsets:to_list(ordsets:subtract(Set2, Set1)),
946    ok.
947
948%% Check multiple calls to driver_lock_driver
949lock_driver(Config) when is_list(Config) ->
950    Path = proplists:get_value(data_dir, Config),
951    {ok, _} = erl_ddll:try_load(Path, lock_drv, []),
952    Port1 = open_port({spawn, lock_drv}, [eof]),
953    Port2 = open_port({spawn, lock_drv}, [eof]),
954    true = erl_ddll:info(lock_drv,permanent),
955    erlang:port_close(Port1),
956    erlang:port_close(Port2),
957    ok.
958
959
960% Load and unload the echo_drv driver.
961% Make sure that the driver doesn't exist before we load it,
962% and that it exists before we unload it.
963load_echo_driver(Path) ->
964    {ok, L1} = erl_ddll:loaded_drivers(),
965    ok = erl_ddll:load_driver(Path, echo_drv),
966    {ok, L2} = erl_ddll:loaded_drivers(),
967    ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2),
968                                            ordsets:from_list(L1))),
969    {ok,L1,L2}.
970
971load_nice_echo_driver(Path) ->
972    {ok, L1} = erl_ddll:loaded_drivers(),
973    ok = erl_ddll:load(Path, echo_drv),
974    {ok, L2} = erl_ddll:loaded_drivers(),
975    ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2),
976                                            ordsets:from_list(L1))),
977    {ok,L1,L2}.
978
979unload_echo_driver(L1,L2) ->
980    {ok, L2} = erl_ddll:loaded_drivers(),
981    ok = erl_ddll:unload_driver(echo_drv),
982    {ok, L3} = erl_ddll:loaded_drivers(),
983    [] = ordsets:to_list(subtract(ordsets:from_list(L3),
984                                  ordsets:from_list(L1))),
985    ok.
986
987unload_expect_fast(Driver,XFlags) ->
988    {ok, pending_driver, Ref} =
989    erl_ddll:try_unload(Driver,
990                        [{monitor,pending_driver}]++XFlags),
991    receive
992        {'DOWN', Ref, driver, Driver, unloaded} ->
993            case lists:member(atom_to_list(Driver),element(2,erl_ddll:loaded_drivers())) of
994                true ->
995                    {error, {still_there, Driver}};
996                false ->
997                    ok
998            end
999    after 1000 ->
1000              {error,{unable_to_unload, Driver}}
1001    end.
1002