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> 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> 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> 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> 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