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