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)(
35         EvaluationContext*, State*, std::string*, R*, Args...) const,
36     R* result,
37     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 
53   // First try calling the actual policy.
54   std::string error;
55   EvalStatus status = (policy_.get()->*policy_method)(
56       ec, state_.get(), &error, result, args...);
57   // If evaluating the main policy failed, defer to the default policy.
58   if (status == EvalStatus::kFailed) {
59     LOG(WARNING) << "Evaluating policy failed: " << error
60                  << "\nEvaluation context: " << ec->DumpContext();
61     error.clear();
62     status = (default_policy_.*policy_method)(
63         ec, state_.get(), &error, result, args...);
64     if (status == EvalStatus::kFailed) {
65       LOG(WARNING) << "Evaluating default policy failed: " << error;
66     } else if (status == EvalStatus::kAskMeAgainLater) {
67       LOG(ERROR)
68           << "Default policy would block; this is a bug, forcing failure.";
69       status = EvalStatus::kFailed;
70     }
71   }
72 
73   return status;
74 }
75 
76 template <typename R, typename... Args>
OnPolicyReadyToEvaluate(std::shared_ptr<EvaluationContext> ec,base::Callback<void (EvalStatus status,const R & result)> callback,EvalStatus (Policy::* policy_method)(EvaluationContext *,State *,std::string *,R *,Args...)const,Args...args)77 void UpdateManager::OnPolicyReadyToEvaluate(
78     std::shared_ptr<EvaluationContext> ec,
79     base::Callback<void(EvalStatus status, const R& result)> callback,
80     EvalStatus (Policy::*policy_method)(
81         EvaluationContext*, State*, std::string*, R*, Args...) const,
82     Args... args) {
83   // Evaluate the policy.
84   R result;
85   EvalStatus status = EvaluatePolicy(ec.get(), policy_method, &result, args...);
86 
87   if (status != EvalStatus::kAskMeAgainLater) {
88     // AsyncPolicyRequest finished.
89     callback.Run(status, result);
90     return;
91   }
92 
93   // Re-schedule the policy request based on used variables.
94   base::Closure reeval_callback =
95       base::Bind(&UpdateManager::OnPolicyReadyToEvaluate<R, Args...>,
96                  base::Unretained(this),
97                  ec,
98                  callback,
99                  policy_method,
100                  args...);
101   if (ec->RunOnValueChangeOrTimeout(reeval_callback))
102     return;  // Reevaluation scheduled successfully.
103 
104   // Scheduling a reevaluation can fail because policy method didn't use any
105   // non-const variable nor there's any time-based event that will change the
106   // status of evaluation.  Alternatively, this may indicate an error in the use
107   // of the scheduling interface.
108   LOG(ERROR) << "Failed to schedule a reevaluation of policy "
109              << policy_->PolicyRequestName(policy_method) << "; this is a bug.";
110   callback.Run(status, result);
111 }
112 
113 template <typename R, typename... ActualArgs, typename... ExpectedArgs>
PolicyRequest(EvalStatus (Policy::* policy_method)(EvaluationContext *,State *,std::string *,R *,ExpectedArgs...)const,R * result,ActualArgs...args)114 EvalStatus UpdateManager::PolicyRequest(
115     EvalStatus (Policy::*policy_method)(
116         EvaluationContext*, State*, std::string*, R*, ExpectedArgs...) const,
117     R* result,
118     ActualArgs... args) {
119   auto ec = std::make_shared<EvaluationContext>(evaluation_timeout_);
120   // A PolicyRequest always consists on a single evaluation on a new
121   // EvaluationContext.
122   // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
123   // explicitly instantiate EvaluatePolicy with the latter in lieu of the
124   // former.
125   EvalStatus ret = EvaluatePolicy<R, ExpectedArgs...>(
126       ec.get(), policy_method, result, args...);
127   // Sync policy requests must not block, if they do then this is an error.
128   DCHECK(EvalStatus::kAskMeAgainLater != ret);
129   LOG_IF(WARNING, EvalStatus::kAskMeAgainLater == ret)
130       << "Sync request used with an async policy; this is a bug";
131   return ret;
132 }
133 
134 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)135 void UpdateManager::AsyncPolicyRequest(
136     base::Callback<void(EvalStatus, const R& result)> callback,
137     EvalStatus (Policy::*policy_method)(
138         EvaluationContext*, State*, std::string*, R*, ExpectedArgs...) const,
139     ActualArgs... args) {
140   auto ec = std::make_shared<EvaluationContext>(
141       evaluation_timeout_,
142       expiration_timeout_,
143       std::unique_ptr<base::Callback<void(EvaluationContext*)>>(
144           new base::Callback<void(EvaluationContext*)>(
145               base::Bind(&UpdateManager::UnregisterEvalContext,
146                          weak_ptr_factory_.GetWeakPtr()))));
147   if (!ec_repo_.insert(ec).second) {
148     LOG(ERROR) << "Failed to register evaluation context; this is a bug.";
149   }
150 
151   // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
152   // explicitly instantiate UpdateManager::OnPolicyReadyToEvaluate with the
153   // latter in lieu of the former.
154   base::Closure eval_callback =
155       base::Bind(&UpdateManager::OnPolicyReadyToEvaluate<R, ExpectedArgs...>,
156                  base::Unretained(this),
157                  ec,
158                  callback,
159                  policy_method,
160                  args...);
161   brillo::MessageLoop::current()->PostTask(FROM_HERE, eval_callback);
162 }
163 
164 }  // namespace chromeos_update_manager
165 
166 #endif  // UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
167