1 // 2 // Copyright (C) 2013 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 // This provides access to timestamps with nanosecond resolution in 18 // struct stat, See NOTES in stat(2) for details. 19 #ifndef _DEFAULT_SOURCE 20 #define _DEFAULT_SOURCE 21 #endif 22 #ifndef _BSD_SOURCE 23 #define _BSD_SOURCE 24 #endif 25 26 #include "update_engine/p2p_manager.h" 27 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <linux/falloc.h> 31 #include <signal.h> 32 #include <string.h> 33 #include <sys/stat.h> 34 #include <sys/statvfs.h> 35 #include <sys/types.h> 36 #include <sys/xattr.h> 37 #include <unistd.h> 38 39 #include <algorithm> 40 #include <map> 41 #include <memory> 42 #include <utility> 43 #include <vector> 44 45 #include <base/bind.h> 46 #include <base/files/file_enumerator.h> 47 #include <base/files/file_path.h> 48 #include <base/format_macros.h> 49 #include <base/logging.h> 50 #include <base/strings/string_util.h> 51 #include <base/strings/stringprintf.h> 52 53 #include "update_engine/common/subprocess.h" 54 #include "update_engine/common/utils.h" 55 #include "update_engine/update_manager/policy.h" 56 #include "update_engine/update_manager/update_manager.h" 57 58 using base::Bind; 59 using base::Callback; 60 using base::FilePath; 61 using base::StringPrintf; 62 using base::Time; 63 using base::TimeDelta; 64 using brillo::MessageLoop; 65 using chromeos_update_manager::EvalStatus; 66 using chromeos_update_manager::Policy; 67 using chromeos_update_manager::UpdateManager; 68 using std::map; 69 using std::pair; 70 using std::string; 71 using std::unique_ptr; 72 using std::vector; 73 74 namespace chromeos_update_engine { 75 76 namespace { 77 78 // The default p2p directory. 79 const char kDefaultP2PDir[] = "/var/cache/p2p"; 80 81 // The p2p xattr used for conveying the final size of a file - see the 82 // p2p ddoc for details. 83 const char kCrosP2PFileSizeXAttrName[] = "user.cros-p2p-filesize"; 84 85 } // namespace 86 87 // The default P2PManager::Configuration implementation. 88 class ConfigurationImpl : public P2PManager::Configuration { 89 public: 90 ConfigurationImpl() {} 91 92 FilePath GetP2PDir() override { 93 return FilePath(kDefaultP2PDir); 94 } 95 96 vector<string> GetInitctlArgs(bool is_start) override { 97 vector<string> args; 98 args.push_back("initctl"); 99 args.push_back(is_start ? "start" : "stop"); 100 args.push_back("p2p"); 101 return args; 102 } 103 104 vector<string> GetP2PClientArgs(const string &file_id, 105 size_t minimum_size) override { 106 vector<string> args; 107 args.push_back("p2p-client"); 108 args.push_back(string("--get-url=") + file_id); 109 args.push_back(StringPrintf("--minimum-size=%" PRIuS, minimum_size)); 110 return args; 111 } 112 113 private: 114 DISALLOW_COPY_AND_ASSIGN(ConfigurationImpl); 115 }; 116 117 // The default P2PManager implementation. 118 class P2PManagerImpl : public P2PManager { 119 public: 120 P2PManagerImpl(Configuration *configuration, 121 ClockInterface *clock, 122 UpdateManager* update_manager, 123 const string& file_extension, 124 const int num_files_to_keep, 125 const TimeDelta& max_file_age); 126 127 // P2PManager methods. 128 void SetDevicePolicy(const policy::DevicePolicy* device_policy) override; 129 bool IsP2PEnabled() override; 130 bool EnsureP2PRunning() override; 131 bool EnsureP2PNotRunning() override; 132 bool PerformHousekeeping() override; 133 void LookupUrlForFile(const string& file_id, 134 size_t minimum_size, 135 TimeDelta max_time_to_wait, 136 LookupCallback callback) override; 137 bool FileShare(const string& file_id, 138 size_t expected_size) override; 139 FilePath FileGetPath(const string& file_id) override; 140 ssize_t FileGetSize(const string& file_id) override; 141 ssize_t FileGetExpectedSize(const string& file_id) override; 142 bool FileGetVisible(const string& file_id, 143 bool *out_result) override; 144 bool FileMakeVisible(const string& file_id) override; 145 int CountSharedFiles() override; 146 147 private: 148 // Enumeration for specifying visibility. 149 enum Visibility { 150 kVisible, 151 kNonVisible 152 }; 153 154 // Returns "." + |file_extension_| + ".p2p" if |visibility| is 155 // |kVisible|. Returns the same concatenated with ".tmp" otherwise. 156 string GetExt(Visibility visibility); 157 158 // Gets the on-disk path for |file_id| depending on if the file 159 // is visible or not. 160 FilePath GetPath(const string& file_id, Visibility visibility); 161 162 // Utility function used by EnsureP2PRunning() and EnsureP2PNotRunning(). 163 bool EnsureP2P(bool should_be_running); 164 165 // Utility function to delete a file given by |path| and log the 166 // path as well as |reason|. Returns false on failure. 167 bool DeleteP2PFile(const FilePath& path, const string& reason); 168 169 // Schedules an async request for tracking changes in P2P enabled status. 170 void ScheduleEnabledStatusChange(); 171 172 // An async callback used by the above. 173 void OnEnabledStatusChange(EvalStatus status, const bool& result); 174 175 // The device policy being used or null if no policy is being used. 176 const policy::DevicePolicy* device_policy_ = nullptr; 177 178 // Configuration object. 179 unique_ptr<Configuration> configuration_; 180 181 // Object for telling the time. 182 ClockInterface* clock_; 183 184 // A pointer to the global Update Manager. 185 UpdateManager* update_manager_; 186 187 // A short string unique to the application (for example "cros_au") 188 // used to mark a file as being owned by a particular application. 189 const string file_extension_; 190 191 // If non-zero, this number denotes how many files in /var/cache/p2p 192 // owned by the application (cf. |file_extension_|) to keep after 193 // performing housekeeping. 194 const int num_files_to_keep_; 195 196 // If non-zero, files older than this will not be kept after 197 // performing housekeeping. 198 const TimeDelta max_file_age_; 199 200 // The string ".p2p". 201 static const char kP2PExtension[]; 202 203 // The string ".tmp". 204 static const char kTmpExtension[]; 205 206 // Whether P2P service may be running; initially, we assume it may be. 207 bool may_be_running_ = true; 208 209 // The current known enabled status of the P2P feature (initialized lazily), 210 // and whether an async status check has been scheduled. 211 bool is_enabled_; 212 bool waiting_for_enabled_status_change_ = false; 213 214 DISALLOW_COPY_AND_ASSIGN(P2PManagerImpl); 215 }; 216 217 const char P2PManagerImpl::kP2PExtension[] = ".p2p"; 218 219 const char P2PManagerImpl::kTmpExtension[] = ".tmp"; 220 221 P2PManagerImpl::P2PManagerImpl(Configuration *configuration, 222 ClockInterface *clock, 223 UpdateManager* update_manager, 224 const string& file_extension, 225 const int num_files_to_keep, 226 const TimeDelta& max_file_age) 227 : clock_(clock), 228 update_manager_(update_manager), 229 file_extension_(file_extension), 230 num_files_to_keep_(num_files_to_keep), 231 max_file_age_(max_file_age) { 232 configuration_.reset(configuration != nullptr ? configuration : 233 new ConfigurationImpl()); 234 } 235 236 void P2PManagerImpl::SetDevicePolicy( 237 const policy::DevicePolicy* device_policy) { 238 device_policy_ = device_policy; 239 } 240 241 bool P2PManagerImpl::IsP2PEnabled() { 242 if (!waiting_for_enabled_status_change_) { 243 // Get and store an initial value. 244 if (update_manager_->PolicyRequest(&Policy::P2PEnabled, &is_enabled_) == 245 EvalStatus::kFailed) { 246 is_enabled_ = false; 247 LOG(ERROR) << "Querying P2P enabled status failed, disabling."; 248 } 249 250 // Track future changes (async). 251 ScheduleEnabledStatusChange(); 252 } 253 254 return is_enabled_; 255 } 256 257 bool P2PManagerImpl::EnsureP2P(bool should_be_running) { 258 int return_code = 0; 259 string output; 260 261 may_be_running_ = true; // Unless successful, we must be conservative. 262 263 vector<string> args = configuration_->GetInitctlArgs(should_be_running); 264 if (!Subprocess::SynchronousExec(args, &return_code, &output)) { 265 LOG(ERROR) << "Error spawning " << utils::StringVectorToString(args); 266 return false; 267 } 268 269 // If initctl(8) does not exit normally (exit status other than zero), ensure 270 // that the error message is not benign by scanning stderr; this is a 271 // necessity because initctl does not offer actions such as "start if not 272 // running" or "stop if running". 273 // TODO(zeuthen,chromium:277051): Avoid doing this. 274 if (return_code != 0) { 275 const char *expected_error_message = should_be_running ? 276 "initctl: Job is already running: p2p\n" : 277 "initctl: Unknown instance \n"; 278 if (output != expected_error_message) 279 return false; 280 } 281 282 may_be_running_ = should_be_running; // Successful after all. 283 return true; 284 } 285 286 bool P2PManagerImpl::EnsureP2PRunning() { 287 return EnsureP2P(true); 288 } 289 290 bool P2PManagerImpl::EnsureP2PNotRunning() { 291 return EnsureP2P(false); 292 } 293 294 // Returns True if the timestamp in the first pair is greater than the 295 // timestamp in the latter. If used with std::sort() this will yield a 296 // sequence of elements where newer (high timestamps) elements precede 297 // older ones (low timestamps). 298 static bool MatchCompareFunc(const pair<FilePath, Time>& a, 299 const pair<FilePath, Time>& b) { 300 return a.second > b.second; 301 } 302 303 string P2PManagerImpl::GetExt(Visibility visibility) { 304 string ext = string(".") + file_extension_ + kP2PExtension; 305 switch (visibility) { 306 case kVisible: 307 break; 308 case kNonVisible: 309 ext += kTmpExtension; 310 break; 311 // Don't add a default case to let the compiler warn about newly 312 // added enum values. 313 } 314 return ext; 315 } 316 317 FilePath P2PManagerImpl::GetPath(const string& file_id, Visibility visibility) { 318 return configuration_->GetP2PDir().Append(file_id + GetExt(visibility)); 319 } 320 321 bool P2PManagerImpl::DeleteP2PFile(const FilePath& path, 322 const string& reason) { 323 LOG(INFO) << "Deleting p2p file " << path.value() 324 << " (reason: " << reason << ")"; 325 if (unlink(path.value().c_str()) != 0) { 326 PLOG(ERROR) << "Error deleting p2p file " << path.value(); 327 return false; 328 } 329 return true; 330 } 331 332 333 bool P2PManagerImpl::PerformHousekeeping() { 334 // Open p2p dir. 335 FilePath p2p_dir = configuration_->GetP2PDir(); 336 const string ext_visible = GetExt(kVisible); 337 const string ext_non_visible = GetExt(kNonVisible); 338 339 bool deletion_failed = false; 340 vector<pair<FilePath, Time>> matches; 341 342 base::FileEnumerator dir(p2p_dir, false, base::FileEnumerator::FILES); 343 // Go through all files and collect their mtime. 344 for (FilePath name = dir.Next(); !name.empty(); name = dir.Next()) { 345 if (!(base::EndsWith(name.value(), ext_visible, 346 base::CompareCase::SENSITIVE) || 347 base::EndsWith(name.value(), ext_non_visible, 348 base::CompareCase::SENSITIVE))) { 349 continue; 350 } 351 352 Time time = dir.GetInfo().GetLastModifiedTime(); 353 354 // If instructed to keep only files younger than a given age 355 // (|max_file_age_| != 0), delete files satisfying this criteria 356 // right now. Otherwise add it to a list we'll consider for later. 357 if (clock_ != nullptr && max_file_age_ != TimeDelta() && 358 clock_->GetWallclockTime() - time > max_file_age_) { 359 if (!DeleteP2PFile(name, "file too old")) 360 deletion_failed = true; 361 } else { 362 matches.push_back(std::make_pair(name, time)); 363 } 364 } 365 366 // If instructed to only keep N files (|max_files_to_keep_ != 0), 367 // sort list of matches, newest (biggest time) to oldest (lowest 368 // time). Then delete starting at element |num_files_to_keep_|. 369 if (num_files_to_keep_ > 0) { 370 std::sort(matches.begin(), matches.end(), MatchCompareFunc); 371 vector<pair<FilePath, Time>>::const_iterator i; 372 for (i = matches.begin() + num_files_to_keep_; i < matches.end(); ++i) { 373 if (!DeleteP2PFile(i->first, "too many files")) 374 deletion_failed = true; 375 } 376 } 377 378 return !deletion_failed; 379 } 380 381 // Helper class for implementing LookupUrlForFile(). 382 class LookupData { 383 public: 384 explicit LookupData(P2PManager::LookupCallback callback) 385 : callback_(callback) {} 386 387 ~LookupData() { 388 if (timeout_task_ != MessageLoop::kTaskIdNull) 389 MessageLoop::current()->CancelTask(timeout_task_); 390 if (child_pid_) 391 Subprocess::Get().KillExec(child_pid_); 392 } 393 394 void InitiateLookup(const vector<string>& cmd, TimeDelta timeout) { 395 // NOTE: if we fail early (i.e. in this method), we need to schedule 396 // an idle to report the error. This is because we guarantee that 397 // the callback is always called from the message loop (this 398 // guarantee is useful for testing). 399 400 // We expect to run just "p2p-client" and find it in the path. 401 child_pid_ = Subprocess::Get().ExecFlags( 402 cmd, Subprocess::kSearchPath, {}, 403 Bind(&LookupData::OnLookupDone, base::Unretained(this))); 404 405 if (!child_pid_) { 406 LOG(ERROR) << "Error spawning " << utils::StringVectorToString(cmd); 407 ReportErrorAndDeleteInIdle(); 408 return; 409 } 410 411 if (timeout > TimeDelta()) { 412 timeout_task_ = MessageLoop::current()->PostDelayedTask( 413 FROM_HERE, 414 Bind(&LookupData::OnTimeout, base::Unretained(this)), 415 timeout); 416 } 417 } 418 419 private: 420 void ReportErrorAndDeleteInIdle() { 421 MessageLoop::current()->PostTask(FROM_HERE, Bind( 422 &LookupData::OnIdleForReportErrorAndDelete, 423 base::Unretained(this))); 424 } 425 426 void OnIdleForReportErrorAndDelete() { 427 ReportError(); 428 delete this; 429 } 430 431 void IssueCallback(const string& url) { 432 if (!callback_.is_null()) 433 callback_.Run(url); 434 } 435 436 void ReportError() { 437 if (reported_) 438 return; 439 IssueCallback(""); 440 reported_ = true; 441 } 442 443 void ReportSuccess(const string& output) { 444 if (reported_) 445 return; 446 string url = output; 447 size_t newline_pos = url.find('\n'); 448 if (newline_pos != string::npos) 449 url.resize(newline_pos); 450 451 // Since p2p-client(1) is constructing this URL itself strictly 452 // speaking there's no need to validate it... but, anyway, can't 453 // hurt. 454 if (url.compare(0, 7, "http://") == 0) { 455 IssueCallback(url); 456 } else { 457 LOG(ERROR) << "p2p URL '" << url << "' does not look right. Ignoring."; 458 ReportError(); 459 } 460 reported_ = true; 461 } 462 463 void OnLookupDone(int return_code, const string& output) { 464 child_pid_ = 0; 465 if (return_code != 0) { 466 LOG(INFO) << "Child exited with non-zero exit code " 467 << return_code; 468 ReportError(); 469 } else { 470 ReportSuccess(output); 471 } 472 delete this; 473 } 474 475 void OnTimeout() { 476 timeout_task_ = MessageLoop::kTaskIdNull; 477 ReportError(); 478 delete this; 479 } 480 481 P2PManager::LookupCallback callback_; 482 483 // The Subprocess tag of the running process. A value of 0 means that the 484 // process is not running. 485 pid_t child_pid_{0}; 486 487 // The timeout task_id we are waiting on, if any. 488 MessageLoop::TaskId timeout_task_{MessageLoop::kTaskIdNull}; 489 490 bool reported_{false}; 491 }; 492 493 void P2PManagerImpl::LookupUrlForFile(const string& file_id, 494 size_t minimum_size, 495 TimeDelta max_time_to_wait, 496 LookupCallback callback) { 497 LookupData *lookup_data = new LookupData(callback); 498 string file_id_with_ext = file_id + "." + file_extension_; 499 vector<string> args = configuration_->GetP2PClientArgs(file_id_with_ext, 500 minimum_size); 501 lookup_data->InitiateLookup(args, max_time_to_wait); 502 } 503 504 bool P2PManagerImpl::FileShare(const string& file_id, 505 size_t expected_size) { 506 // Check if file already exist. 507 FilePath path = FileGetPath(file_id); 508 if (!path.empty()) { 509 // File exists - double check its expected size though. 510 ssize_t file_expected_size = FileGetExpectedSize(file_id); 511 if (file_expected_size == -1 || 512 static_cast<size_t>(file_expected_size) != expected_size) { 513 LOG(ERROR) << "Existing p2p file " << path.value() 514 << " with expected_size=" << file_expected_size 515 << " does not match the passed in" 516 << " expected_size=" << expected_size; 517 return false; 518 } 519 return true; 520 } 521 522 // Before creating the file, bail if statvfs(3) indicates that at 523 // least twice the size is not available in P2P_DIR. 524 struct statvfs statvfsbuf; 525 FilePath p2p_dir = configuration_->GetP2PDir(); 526 if (statvfs(p2p_dir.value().c_str(), &statvfsbuf) != 0) { 527 PLOG(ERROR) << "Error calling statvfs() for dir " << p2p_dir.value(); 528 return false; 529 } 530 size_t free_bytes = 531 static_cast<size_t>(statvfsbuf.f_bsize) * statvfsbuf.f_bavail; 532 if (free_bytes < 2 * expected_size) { 533 // This can easily happen and is worth reporting. 534 LOG(INFO) << "Refusing to allocate p2p file of " << expected_size 535 << " bytes since the directory " << p2p_dir.value() 536 << " only has " << free_bytes 537 << " bytes available and this is less than twice the" 538 << " requested size."; 539 return false; 540 } 541 542 // Okie-dokey looks like enough space is available - create the file. 543 path = GetPath(file_id, kNonVisible); 544 int fd = open(path.value().c_str(), O_CREAT | O_RDWR, 0644); 545 if (fd == -1) { 546 PLOG(ERROR) << "Error creating file with path " << path.value(); 547 return false; 548 } 549 ScopedFdCloser fd_closer(&fd); 550 551 // If the final size is known, allocate the file (e.g. reserve disk 552 // space) and set the user.cros-p2p-filesize xattr. 553 if (expected_size != 0) { 554 if (fallocate(fd, 555 FALLOC_FL_KEEP_SIZE, // Keep file size as 0. 556 0, 557 expected_size) != 0) { 558 if (errno == ENOSYS || errno == EOPNOTSUPP) { 559 // If the filesystem doesn't support the fallocate, keep 560 // going. This is helpful when running unit tests on build 561 // machines with ancient filesystems and/or OSes. 562 PLOG(WARNING) << "Ignoring fallocate(2) failure"; 563 } else { 564 // ENOSPC can happen (funky race though, cf. the statvfs() check 565 // above), handle it gracefully, e.g. use logging level INFO. 566 PLOG(INFO) << "Error allocating " << expected_size 567 << " bytes for file " << path.value(); 568 if (unlink(path.value().c_str()) != 0) { 569 PLOG(ERROR) << "Error deleting file with path " << path.value(); 570 } 571 return false; 572 } 573 } 574 575 string decimal_size = std::to_string(expected_size); 576 if (fsetxattr(fd, kCrosP2PFileSizeXAttrName, 577 decimal_size.c_str(), decimal_size.size(), 0) != 0) { 578 PLOG(ERROR) << "Error setting xattr " << path.value(); 579 return false; 580 } 581 } 582 583 return true; 584 } 585 586 FilePath P2PManagerImpl::FileGetPath(const string& file_id) { 587 struct stat statbuf; 588 FilePath path; 589 590 path = GetPath(file_id, kVisible); 591 if (stat(path.value().c_str(), &statbuf) == 0) { 592 return path; 593 } 594 595 path = GetPath(file_id, kNonVisible); 596 if (stat(path.value().c_str(), &statbuf) == 0) { 597 return path; 598 } 599 600 path.clear(); 601 return path; 602 } 603 604 bool P2PManagerImpl::FileGetVisible(const string& file_id, 605 bool *out_result) { 606 FilePath path = FileGetPath(file_id); 607 if (path.empty()) { 608 LOG(ERROR) << "No file for id " << file_id; 609 return false; 610 } 611 if (out_result != nullptr) 612 *out_result = path.MatchesExtension(kP2PExtension); 613 return true; 614 } 615 616 bool P2PManagerImpl::FileMakeVisible(const string& file_id) { 617 FilePath path = FileGetPath(file_id); 618 if (path.empty()) { 619 LOG(ERROR) << "No file for id " << file_id; 620 return false; 621 } 622 623 // Already visible? 624 if (path.MatchesExtension(kP2PExtension)) 625 return true; 626 627 LOG_ASSERT(path.MatchesExtension(kTmpExtension)); 628 FilePath new_path = path.RemoveExtension(); 629 LOG_ASSERT(new_path.MatchesExtension(kP2PExtension)); 630 if (rename(path.value().c_str(), new_path.value().c_str()) != 0) { 631 PLOG(ERROR) << "Error renaming " << path.value() 632 << " to " << new_path.value(); 633 return false; 634 } 635 636 return true; 637 } 638 639 ssize_t P2PManagerImpl::FileGetSize(const string& file_id) { 640 FilePath path = FileGetPath(file_id); 641 if (path.empty()) 642 return -1; 643 644 return utils::FileSize(path.value()); 645 } 646 647 ssize_t P2PManagerImpl::FileGetExpectedSize(const string& file_id) { 648 FilePath path = FileGetPath(file_id); 649 if (path.empty()) 650 return -1; 651 652 char ea_value[64] = { 0 }; 653 ssize_t ea_size; 654 ea_size = getxattr(path.value().c_str(), kCrosP2PFileSizeXAttrName, 655 &ea_value, sizeof(ea_value) - 1); 656 if (ea_size == -1) { 657 PLOG(ERROR) << "Error calling getxattr() on file " << path.value(); 658 return -1; 659 } 660 661 char* endp = nullptr; 662 long long int val = strtoll(ea_value, &endp, 0); // NOLINT(runtime/int) 663 if (*endp != '\0') { 664 LOG(ERROR) << "Error parsing the value '" << ea_value 665 << "' of the xattr " << kCrosP2PFileSizeXAttrName 666 << " as an integer"; 667 return -1; 668 } 669 670 return val; 671 } 672 673 int P2PManagerImpl::CountSharedFiles() { 674 int num_files = 0; 675 676 FilePath p2p_dir = configuration_->GetP2PDir(); 677 const string ext_visible = GetExt(kVisible); 678 const string ext_non_visible = GetExt(kNonVisible); 679 680 base::FileEnumerator dir(p2p_dir, false, base::FileEnumerator::FILES); 681 for (FilePath name = dir.Next(); !name.empty(); name = dir.Next()) { 682 if (base::EndsWith(name.value(), ext_visible, 683 base::CompareCase::SENSITIVE) || 684 base::EndsWith(name.value(), ext_non_visible, 685 base::CompareCase::SENSITIVE)) { 686 num_files += 1; 687 } 688 } 689 690 return num_files; 691 } 692 693 void P2PManagerImpl::ScheduleEnabledStatusChange() { 694 if (waiting_for_enabled_status_change_) 695 return; 696 697 Callback<void(EvalStatus, const bool&)> callback = Bind( 698 &P2PManagerImpl::OnEnabledStatusChange, base::Unretained(this)); 699 update_manager_->AsyncPolicyRequest(callback, &Policy::P2PEnabledChanged, 700 is_enabled_); 701 waiting_for_enabled_status_change_ = true; 702 } 703 704 void P2PManagerImpl::OnEnabledStatusChange(EvalStatus status, 705 const bool& result) { 706 waiting_for_enabled_status_change_ = false; 707 708 if (status == EvalStatus::kSucceeded) { 709 if (result == is_enabled_) { 710 LOG(WARNING) << "P2P enabled status did not change, which means that it " 711 "is permanent; not scheduling further checks."; 712 waiting_for_enabled_status_change_ = true; 713 return; 714 } 715 716 is_enabled_ = result; 717 718 // If P2P is running but shouldn't be, make sure it isn't. 719 if (may_be_running_ && !is_enabled_ && !EnsureP2PNotRunning()) { 720 LOG(WARNING) << "Failed to stop P2P service."; 721 } 722 } else { 723 LOG(WARNING) 724 << "P2P enabled tracking failed (possibly timed out); retrying."; 725 } 726 727 ScheduleEnabledStatusChange(); 728 } 729 730 P2PManager* P2PManager::Construct( 731 Configuration *configuration, 732 ClockInterface *clock, 733 UpdateManager* update_manager, 734 const string& file_extension, 735 const int num_files_to_keep, 736 const TimeDelta& max_file_age) { 737 return new P2PManagerImpl(configuration, 738 clock, 739 update_manager, 740 file_extension, 741 num_files_to_keep, 742 max_file_age); 743 } 744 745 } // namespace chromeos_update_engine 746