1 //
2 // Copyright (C) 2014 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #ifndef UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
18 #define UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
19
20 #include <memory>
21 #include <string>
22
23 #include <base/bind.h>
24 #include <base/location.h>
25 #include <brillo/message_loops/message_loop.h>
26
27 #include "update_engine/update_manager/evaluation_context.h"
28
29 namespace chromeos_update_manager {
30
31 template<typename R, typename... Args>
EvaluatePolicy(EvaluationContext * ec,EvalStatus (Policy::* policy_method)(EvaluationContext *,State *,std::string *,R *,Args...)const,R * result,Args...args)32 EvalStatus UpdateManager::EvaluatePolicy(
33 EvaluationContext* ec,
34 EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
35 std::string*, R*,
36 Args...) const,
37 R* result, Args... args) {
38 // If expiration timeout fired, dump the context and reset expiration.
39 // IMPORTANT: We must still proceed with evaluation of the policy in this
40 // case, so that the evaluation time (and corresponding reevaluation timeouts)
41 // are readjusted.
42 if (ec->is_expired()) {
43 LOG(WARNING) << "Request timed out, evaluation context: "
44 << ec->DumpContext();
45 ec->ResetExpiration();
46 }
47
48 // Reset the evaluation context.
49 ec->ResetEvaluation();
50
51 const std::string policy_name = policy_->PolicyRequestName(policy_method);
52 LOG(INFO) << policy_name << ": START";
53
54 // First try calling the actual policy.
55 std::string error;
56 EvalStatus status = (policy_.get()->*policy_method)(ec, state_.get(), &error,
57 result, args...);
58 // If evaluating the main policy failed, defer to the default policy.
59 if (status == EvalStatus::kFailed) {
60 LOG(WARNING) << "Evaluating policy failed: " << error
61 << "\nEvaluation context: " << ec->DumpContext();
62 error.clear();
63 status = (default_policy_.*policy_method)(ec, state_.get(), &error, result,
64 args...);
65 if (status == EvalStatus::kFailed) {
66 LOG(WARNING) << "Evaluating default policy failed: " << error;
67 } else if (status == EvalStatus::kAskMeAgainLater) {
68 LOG(ERROR)
69 << "Default policy would block; this is a bug, forcing failure.";
70 status = EvalStatus::kFailed;
71 }
72 }
73
74 LOG(INFO) << policy_name << ": END";
75
76 return status;
77 }
78
79 template<typename R, typename... Args>
OnPolicyReadyToEvaluate(scoped_refptr<EvaluationContext> ec,base::Callback<void (EvalStatus status,const R & result)> callback,EvalStatus (Policy::* policy_method)(EvaluationContext *,State *,std::string *,R *,Args...)const,Args...args)80 void UpdateManager::OnPolicyReadyToEvaluate(
81 scoped_refptr<EvaluationContext> ec,
82 base::Callback<void(EvalStatus status, const R& result)> callback,
83 EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
84 std::string*, R*,
85 Args...) const,
86 Args... args) {
87 // Evaluate the policy.
88 R result;
89 EvalStatus status = EvaluatePolicy(ec.get(), policy_method, &result, args...);
90
91 if (status != EvalStatus::kAskMeAgainLater) {
92 // AsyncPolicyRequest finished.
93 callback.Run(status, result);
94 return;
95 }
96
97 // Re-schedule the policy request based on used variables.
98 base::Closure reeval_callback = base::Bind(
99 &UpdateManager::OnPolicyReadyToEvaluate<R, Args...>,
100 base::Unretained(this), ec, callback,
101 policy_method, args...);
102 if (ec->RunOnValueChangeOrTimeout(reeval_callback))
103 return; // Reevaluation scheduled successfully.
104
105 // Scheduling a reevaluation can fail because policy method didn't use any
106 // non-const variable nor there's any time-based event that will change the
107 // status of evaluation. Alternatively, this may indicate an error in the use
108 // of the scheduling interface.
109 LOG(ERROR) << "Failed to schedule a reevaluation of policy "
110 << policy_->PolicyRequestName(policy_method) << "; this is a bug.";
111 callback.Run(status, result);
112 }
113
114 template<typename R, typename... ActualArgs, typename... ExpectedArgs>
PolicyRequest(EvalStatus (Policy::* policy_method)(EvaluationContext *,State *,std::string *,R *,ExpectedArgs...)const,R * result,ActualArgs...args)115 EvalStatus UpdateManager::PolicyRequest(
116 EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
117 std::string*, R*,
118 ExpectedArgs...) const,
119 R* result, ActualArgs... args) {
120 scoped_refptr<EvaluationContext> ec(
121 new EvaluationContext(clock_, evaluation_timeout_));
122 // A PolicyRequest always consists on a single evaluation on a new
123 // EvaluationContext.
124 // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
125 // explicitly instantiate EvaluatePolicy with the latter in lieu of the
126 // former.
127 EvalStatus ret = EvaluatePolicy<R, ExpectedArgs...>(ec.get(), policy_method,
128 result, args...);
129 // Sync policy requests must not block, if they do then this is an error.
130 DCHECK(EvalStatus::kAskMeAgainLater != ret);
131 LOG_IF(WARNING, EvalStatus::kAskMeAgainLater == ret)
132 << "Sync request used with an async policy; this is a bug";
133 return ret;
134 }
135
136 template<typename R, typename... ActualArgs, typename... ExpectedArgs>
AsyncPolicyRequest(base::Callback<void (EvalStatus,const R & result)> callback,EvalStatus (Policy::* policy_method)(EvaluationContext *,State *,std::string *,R *,ExpectedArgs...)const,ActualArgs...args)137 void UpdateManager::AsyncPolicyRequest(
138 base::Callback<void(EvalStatus, const R& result)> callback,
139 EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
140 std::string*, R*,
141 ExpectedArgs...) const,
142 ActualArgs... args) {
143 scoped_refptr<EvaluationContext> ec =
144 new EvaluationContext(
145 clock_, evaluation_timeout_, expiration_timeout_,
146 std::unique_ptr<base::Callback<void(EvaluationContext*)>>(
147 new base::Callback<void(EvaluationContext*)>(
148 base::Bind(&UpdateManager::UnregisterEvalContext,
149 weak_ptr_factory_.GetWeakPtr()))));
150 if (!ec_repo_.insert(ec.get()).second) {
151 LOG(ERROR) << "Failed to register evaluation context; this is a bug.";
152 }
153
154 // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
155 // explicitly instantiate UpdateManager::OnPolicyReadyToEvaluate with the
156 // latter in lieu of the former.
157 base::Closure eval_callback = base::Bind(
158 &UpdateManager::OnPolicyReadyToEvaluate<R, ExpectedArgs...>,
159 base::Unretained(this), ec, callback, policy_method, args...);
160 brillo::MessageLoop::current()->PostTask(FROM_HERE, eval_callback);
161 }
162
163 } // namespace chromeos_update_manager
164
165 #endif // UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
166