| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chaps/system_shutdown_blocker.h" |
| |
| #include <string> |
| |
| #include <base/bind.h> |
| #include <base/files/file_util.h> |
| #include <base/location.h> |
| #include <base/strings/stringprintf.h> |
| |
| namespace chaps { |
| |
| SystemShutdownBlocker::SystemShutdownBlocker( |
| const scoped_refptr<base::SingleThreadTaskRunner>& |
| origin_thread_task_runner) |
| : origin_thread_task_runner_(origin_thread_task_runner) {} |
| |
| SystemShutdownBlocker::~SystemShutdownBlocker() { |
| for (int slot_id : blocked_slots_) |
| PerformUnblock(slot_id); |
| blocked_slots_.clear(); |
| } |
| |
| void SystemShutdownBlocker::Block(int slot_id, |
| base::TimeDelta fallback_timeout) { |
| // Post block task. |
| auto perform_block_task = base::Bind(&SystemShutdownBlocker::PerformBlock, |
| base::Unretained(this), slot_id); |
| origin_thread_task_runner_->PostTask(FROM_HERE, perform_block_task); |
| |
| // Post delayed unblock task (fallback). |
| auto perform_unblock_task = |
| base::Bind(&SystemShutdownBlocker::PerformUnblockIfBlocked, |
| base::Unretained(this), slot_id); |
| origin_thread_task_runner_->PostDelayedTask(FROM_HERE, perform_unblock_task, |
| fallback_timeout); |
| } |
| |
| void SystemShutdownBlocker::Unblock(int slot_id) { |
| // Post unblock task. |
| auto perform_unblock_task = |
| base::Bind(&SystemShutdownBlocker::PerformUnblockIfBlocked, |
| base::Unretained(this), slot_id); |
| origin_thread_task_runner_->PostTask(FROM_HERE, perform_unblock_task); |
| } |
| |
| void SystemShutdownBlocker::PerformBlock(int slot_id) { |
| // Create lock file with chapsd PID as content and readable for powerd. |
| const base::FilePath lock_path = GetPowerdLockFilePath(slot_id); |
| if (!base::DirectoryExists(lock_path.DirName())) { |
| LOG(ERROR) << "Failed to create lock file (" << lock_path.DirName().value() |
| << " doesn't exist)"; |
| return; |
| } |
| blocked_slots_.insert(slot_id); |
| |
| base::File lock_file(lock_path, |
| base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| std::string lock_contents = base::StringPrintf("%d", getpid()); |
| int mode = base::FILE_PERMISSION_READ_BY_USER | |
| base::FILE_PERMISSION_WRITE_BY_USER | |
| base::FILE_PERMISSION_READ_BY_GROUP | |
| base::FILE_PERMISSION_READ_BY_OTHERS; // chmod 644 |
| if (!lock_file.IsValid() || |
| lock_file.WriteAtCurrentPos(lock_contents.data(), lock_contents.size()) < |
| 0 || |
| !base::SetPosixFilePermissions(lock_path, mode)) { |
| PLOG(ERROR) << "Failed to create lock file."; |
| return; |
| } |
| LOG(INFO) << "Created lock file: " << lock_path.value(); |
| } |
| |
| void SystemShutdownBlocker::PerformUnblockIfBlocked(int slot_id) { |
| if (blocked_slots_.count(slot_id) && PerformUnblock(slot_id)) |
| blocked_slots_.erase(slot_id); |
| } |
| |
| bool SystemShutdownBlocker::PerformUnblock(int slot_id) { |
| const base::FilePath lock_path = GetPowerdLockFilePath(slot_id); |
| if (!base::PathExists(lock_path)) { |
| LOG(WARNING) << "Couldn't delete lock file (not existant): " |
| << lock_path.value(); |
| return true; |
| } |
| if (!base::DeleteFile(lock_path, false /* recursive */)) { |
| PLOG(ERROR) << "Couldn't delete lock file: " << lock_path.value(); |
| return false; |
| } |
| LOG(INFO) << "Deleted lock file: " << lock_path.value(); |
| return true; |
| } |
| |
| base::FilePath SystemShutdownBlocker::GetPowerdLockFilePath(int slot_id) const { |
| return base::FilePath("/run/lock/power_override/chapsd_token_init_slot_" + |
| std::to_string(slot_id) + ".lock"); |
| } |
| |
| } // namespace chaps |