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