1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2020. 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-module(snmpa_set).
21
22-behaviour(snmpa_set_mechanism).
23
24-define(VMODULE,"SET").
25-include("snmp_verbosity.hrl").
26
27
28%%%-----------------------------------------------------------------
29%%% This module implements a simple, basic atomic set mechanism.
30%%%-----------------------------------------------------------------
31%%% Table of contents
32%%% =================
33%%% 1. SET REQUEST
34%%% 1.1 SET phase one
35%%% 1.2 SET phase two
36%%% 2. Misc functions
37%%%-----------------------------------------------------------------
38
39%% External exports
40-export([do_set/2, do_subagent_set/1]).
41
42%%%-----------------------------------------------------------------
43%%% 1. SET REQUEST
44%%%
45%%% 1) Perform set_phase_one for all own vars
46%%% 2) Perform set_phase_one for all SAs
47%%%    IF nok THEN 2.1 ELSE 3
48%%% 2.1) Perform set_phase_two(undo) for all SAs that have performed
49%%%      set_phase_one.
50%%% 3) Perform set_phase_two for all own vars
51%%% 4) Perform set_phase_two(set) for all SAs
52%%%    IF nok THEN 4.1 ELSE 5
53%%% 4.1) Perform set_phase_two(undo) for all SAs that have performed
54%%%      set_phase_one but not set_phase_two(set).
55%%% 5) noError
56%%%-----------------------------------------------------------------
57%%-----------------------------------------------------------------
58%% First of all - validate MibView for all varbinds. In this way
59%% we don't have to send the MibView to all SAs for validation.
60%%-----------------------------------------------------------------
61do_set(MibView, UnsortedVarbinds) ->
62    ?vtrace("do set with"
63	    "~n   MibView: ~p",[MibView]),
64    case snmpa_acm:validate_all_mib_view(UnsortedVarbinds, MibView) of
65	true ->
66	    {MyVarbinds , SubagentVarbinds} =
67		sort_varbindlist(UnsortedVarbinds),
68	    case set_phase_one(MyVarbinds, SubagentVarbinds) of
69		{noError, 0} -> set_phase_two(MyVarbinds, SubagentVarbinds);
70		{Reason, Index} -> {Reason, Index}
71	    end;
72	{false, Index} ->
73	    {noAccess, Index}
74    end.
75
76%%-----------------------------------------------------------------
77%% This function is called when a subagents receives a message
78%% concerning some set_phase.
79%% Mandatory messages for all subagents:
80%%   [phase_one, UnsortedVarbinds]
81%%   [phase_two, set, UnsortedVarbinds]
82%%   [phase_two, undo, UnsortedVarbinds]
83%%-----------------------------------------------------------------
84do_subagent_set([phase_one, UnsortedVarbinds]) ->
85    ?vtrace("do subagent set, phase one",[]),
86    {MyVarbinds, SubagentVarbinds} = sort_varbindlist(UnsortedVarbinds),
87    set_phase_one(MyVarbinds, SubagentVarbinds);
88do_subagent_set([phase_two, State, UnsortedVarbinds]) ->
89    ?vtrace("do subagent set, phase two",[]),
90    {MyVarbinds, SubagentVarbinds} = sort_varbindlist(UnsortedVarbinds),
91    set_phase_two(State, MyVarbinds, SubagentVarbinds).
92
93%%%-----------------------------------------------------------------
94%%% 1.1 SET phase one
95%%%-----------------------------------------------------------------
96%%-----------------------------------------------------------------
97%% Func: set_phase_one/3
98%% Purpose: First, do set_phase_one for my own variables (i.e.
99%%          variables handled by this agent). Then, do set_phase_one
100%%          for all subagents. If any SA failed, do set_phase_two
101%%          (undo) for all SA that have done set_phase_one.
102%% Returns: {noError, 0} | {ErrorStatus, Index}
103%%-----------------------------------------------------------------
104set_phase_one(MyVarbinds, SubagentVarbinds) ->
105    ?vtrace("set phase one: "
106	    "~n   MyVarbinds:       ~p"
107	    "~n   SubagentVarbinds: ~p",
108	    [MyVarbinds, SubagentVarbinds]),
109    case set_phase_one_my_variables(MyVarbinds) of
110	{noError, 0} ->
111	    case set_phase_one_subagents(SubagentVarbinds, []) of
112		{noError, 0} ->
113		    {noError, 0};
114		{{ErrorStatus, Index}, PerformedSubagents} ->
115		    case set_phase_two_undo(MyVarbinds, PerformedSubagents) of
116			{noError, 0} ->
117			    {ErrorStatus, Index};
118			{WorseErrorStatus, WorseIndex} ->
119			    {WorseErrorStatus, WorseIndex}
120		    end
121	    end;
122	{ErrorStatus, Index} ->
123	    {ErrorStatus, Index}
124    end.
125
126set_phase_one_my_variables(MyVarbinds) ->
127    ?vtrace("my variables set, phase one:"
128	    "~n   ~p",[MyVarbinds]),
129    case snmpa_set_lib:is_varbinds_ok(MyVarbinds) of
130	{noError, 0} ->
131	    snmpa_set_lib:consistency_check(MyVarbinds);
132	{ErrorStatus, Index} ->
133	    {ErrorStatus, Index}
134    end.
135
136%%-----------------------------------------------------------------
137%% Loop all subagents, and perform set_phase_one for them.
138%%-----------------------------------------------------------------
139set_phase_one_subagents([{SubAgentPid, SAVbs}|SubagentVarbinds], Done) ->
140    {_SAOids, Vbs} = sa_split(SAVbs),
141    case (catch snmpa_agent:subagent_set(SubAgentPid, [phase_one, Vbs])) of
142	{noError, 0} ->
143	    set_phase_one_subagents(SubagentVarbinds,
144				    [{SubAgentPid, SAVbs} | Done]);
145	{'EXIT', Reason} ->
146	    user_err("Lost contact with subagent (set phase_one)"
147		     "~n~w. Using genErr", [Reason]),
148	    {{genErr, 0}, Done};
149	{ErrorStatus, ErrorIndex} ->
150	    {{ErrorStatus, ErrorIndex}, Done}
151    end;
152set_phase_one_subagents([], _Done) ->
153    {noError, 0}.
154
155%%%-----------------------------------------------------------------
156%%% 1.2 SET phase two
157%%%-----------------------------------------------------------------
158%% returns:  {ErrStatus, ErrIndex}
159set_phase_two(MyVarbinds, SubagentVarbinds) ->
160    ?vtrace("set phase two: "
161	    "~n   MyVarbinds:       ~p"
162	    "~n   SubagentVarbinds: ~p",
163	    [MyVarbinds, SubagentVarbinds]),
164    case snmpa_set_lib:try_set(MyVarbinds) of
165	{noError, 0} ->
166            ?vtrace("set phase two: (local) varbinds set ok", []),
167	    set_phase_two_subagents(SubagentVarbinds);
168	{ErrorStatus, ErrorIndex} ->
169            ?vlog("set phase two: (local) varbinds set failed"
170                  "~n   ErrorStatus: ~p"
171                  "~n   ErrorIndex:  ~p", [ErrorStatus, ErrorIndex]),
172	    set_phase_two_undo_subagents(SubagentVarbinds),
173	    {ErrorStatus, ErrorIndex}
174    end.
175
176%%-----------------------------------------------------------------
177%% This function is called for each phase_two state in the
178%% subagents. The undo state just pass undo along to each of its
179%% subagents.
180%%-----------------------------------------------------------------
181set_phase_two(set, MyVarbinds, SubagentVarbinds) ->
182    set_phase_two(MyVarbinds, SubagentVarbinds);
183set_phase_two(undo, MyVarbinds, SubagentVarbinds) ->
184    set_phase_two_undo(MyVarbinds, SubagentVarbinds).
185
186%%-----------------------------------------------------------------
187%% Loop all subagents, and perform set_phase_two(set) for them.
188%% If any fails, perform set_phase_two(undo) for the not yet
189%% called SAs.
190%%-----------------------------------------------------------------
191set_phase_two_subagents([{SubAgentPid, SAVbs} | SubagentVarbinds]) ->
192    {_SAOids, Vbs} = sa_split(SAVbs),
193    case catch snmpa_agent:subagent_set(SubAgentPid, [phase_two, set, Vbs]) of
194	{noError, 0} ->
195            ?vtrace("set phase two: subagent ~p varbinds set ok", [SubAgentPid]),
196	    set_phase_two_subagents(SubagentVarbinds);
197	{'EXIT', Reason} ->
198	    user_err("Lost contact with subagent (set)~n~w. Using genErr",
199		     [Reason]),
200	    set_phase_two_undo_subagents(SubagentVarbinds),
201	    {genErr, 0};
202	{ErrorStatus, ErrorIndex} ->
203            ?vlog("set phase two: subagent ~p varbinds set failed"
204                  "~n   ErrorStatus: ~p"
205                  "~n   ErrorIndex:  ~p", [SubAgentPid, ErrorStatus, ErrorIndex]),
206	    set_phase_two_undo_subagents(SubagentVarbinds),
207	    {ErrorStatus, ErrorIndex}
208    end;
209set_phase_two_subagents([]) ->
210    ?vtrace("set phase two: subagent(s) set ok", []),
211    {noError, 0}.
212
213%%-----------------------------------------------------------------
214%% This function undos phase_one, own and subagent.
215%%-----------------------------------------------------------------
216set_phase_two_undo(MyVarbinds, SubagentVarbinds) ->
217    case set_phase_two_undo_my_variables(MyVarbinds) of
218	{noError, 0} ->
219	    set_phase_two_undo_subagents(SubagentVarbinds);
220	{ErrorStatus, Index} ->
221	    set_phase_two_undo_subagents(SubagentVarbinds),
222	    {ErrorStatus, Index}
223    end.
224
225set_phase_two_undo_my_variables(MyVarbinds) ->
226    snmpa_set_lib:undo_varbinds(MyVarbinds).
227
228set_phase_two_undo_subagents([{SubAgentPid, SAVbs} | SubagentVarbinds]) ->
229    {_SAOids, Vbs} = sa_split(SAVbs),
230    case catch snmpa_agent:subagent_set(SubAgentPid, [phase_two, undo, Vbs]) of
231	{noError, 0} ->
232	    set_phase_two_undo_subagents(SubagentVarbinds);
233	{'EXIT', Reason} ->
234	    user_err("Lost contact with subagent (undo)~n~w. Using genErr",
235		     [Reason]),
236	    {genErr, 0};
237	{ErrorStatus, ErrorIndex} ->
238	    {ErrorStatus, ErrorIndex}
239    end;
240set_phase_two_undo_subagents([]) ->
241    {noError, 0}.
242
243%%%-----------------------------------------------------------------
244%%% 2. Misc functions
245%%%-----------------------------------------------------------------
246sort_varbindlist(Varbinds) ->
247    snmpa_svbl:sort_varbindlist(get(mibserver), Varbinds).
248
249sa_split(SubagentVarbinds) ->
250    snmpa_svbl:sa_split(SubagentVarbinds).
251
252
253user_err(F, A) ->
254    snmpa_error:user_err(F, A).
255