1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2019. 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%% Don't use the system rpc server since it may overload other
22%% applications when using a lot of dirty read operations.
23
24-module(mnesia_rpc).
25-behaviour(gen_server).
26
27-export([start/0,
28         call/4
29        ]).
30
31
32%% gen_server callbacks
33-export([init/1,
34	 handle_call/3,
35	 handle_cast/2,
36	 handle_info/2,
37	 terminate/2,
38	 code_change/3
39	]).
40
41-include("mnesia.hrl").
42
43start() ->
44    gen_server:start_link({local, ?MODULE}, ?MODULE, [self()],
45			  [{timeout, infinity} %%, {debug, [trace]}
46			  ]).
47
48call(Node, M, F, Args) ->
49    case ?catch_val({protocol, Node}) of
50        {Ver, _} when Ver > {8,4} ->
51            try gen_server:call({?MODULE, Node}, {apply, M, F, Args}, infinity)
52            catch
53                _:Reason -> {badrpc, {'EXIT', Reason}}
54            end;
55        _ ->
56            rpc:call(Node, M, F, Args)
57    end.
58
59init([_Parent]) ->
60    {ok, #{}}.
61
62handle_call({apply, mnesia_lib, db_get=Func, Args}, From, State) ->
63    apply_lib(Func, Args, From, State);
64handle_call({apply, mnesia_lib, db_last=Func, Args}, From, State) ->
65    apply_lib(Func, Args, From, State);
66handle_call({apply, mnesia_lib, db_first=Func, Args}, From, State) ->
67    apply_lib(Func, Args, From, State);
68handle_call({apply, mnesia_lib, db_next_key=Func, Args}, From, State) ->
69    apply_lib(Func, Args, From, State);
70handle_call({apply, mnesia_lib, db_prev_key=Func, Args}, From, State) ->
71    apply_lib(Func, Args, From, State);
72handle_call({apply, Mod, Func, Args}, From, State) ->
73    Fun = apply_fun(Mod, Func, Args, From),
74    _Pid = spawn_link(Fun),
75    {noreply, State};
76
77handle_call(Msg, _From, State) ->
78    mnesia_lib:error("~p got unexpected call: ~tp~n", [?MODULE, Msg]),
79    {reply, badop, State}.
80
81handle_cast(Msg, State) ->
82    mnesia_lib:error("~p got unexpected cast: ~tp~n", [?MODULE, Msg]),
83    {noreply, State}.
84
85handle_info(Msg, State) ->
86    mnesia_lib:error("~p got unexpected info: ~tp~n", [?MODULE, Msg]),
87    {noreply, State}.
88
89
90code_change(_OldVsn, State, _Extra) ->
91    {ok, State}.
92
93terminate(_Reason, _State) ->
94    ok.
95
96%%%%
97
98apply_lib(Func, [Tab|_] = Args, From, State) ->
99    try
100        Ram = ?catch_val({Tab, storage_type}),
101        if Ram =:= ram_copies; Ram =:= disc_copies ->
102                {reply, apply(mnesia_lib, Func, [Ram|Args]), State};
103           true ->
104                Fun = apply_fun(mnesia_lib, Func, Args, From),
105                _Pid = spawn_link(Fun),
106                {noreply, State}
107        end
108    catch throw:Res -> {reply, Res, State};
109          _:Reason -> {reply, {badrpc, {'EXIT', Reason}}, State}
110    end.
111
112apply_fun(Mod, Func, Args, From) ->
113    fun() ->
114            Result = try apply(Mod, Func, Args)
115                     catch throw:Res -> Res;
116                           _:Reason -> {badrpc, {'EXIT', Reason}}
117                     end,
118            gen_server:reply(From, Result)
119    end.
120