blob: bf4a16700f7ccb6e8abc74dfb2c48b5199f0fd54 [file] [log] [blame]
// Copyright (c) 2012 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 "cros-disks/device_ejector.h"
#include <linux/capability.h>
#include <memory>
#include <base/bind.h>
#include <base/logging.h>
#include "cros-disks/quote.h"
namespace cros_disks {
namespace {
// Expected location of the 'eject' program.
const char kEjectProgram[] = "/usr/bin/eject";
} // namespace
DeviceEjector::DeviceEjector(brillo::ProcessReaper* process_reaper)
: process_reaper_(process_reaper), weak_ptr_factory_(this) {}
DeviceEjector::~DeviceEjector() = default;
bool DeviceEjector::Eject(const std::string& device_path) {
CHECK(!device_path.empty()) << "Invalid device path";
LOG(INFO) << "Eject device " << quote(device_path);
if (base::Contains(eject_process_, device_path)) {
LOG(WARNING) << "Device " << quote(device_path)
<< " is already being ejected";
return false;
}
SandboxedProcess* process = &eject_process_[device_path];
process->SetNoNewPrivileges();
process->NewIpcNamespace();
process->NewNetworkNamespace();
process->AddArgument(kEjectProgram);
process->AddArgument(device_path);
process->SetCapabilities(CAP_TO_MASK(CAP_SYS_ADMIN));
// TODO(benchan): Set up a timeout to kill a hanging process.
bool started = process->Start();
if (started) {
process_reaper_->WatchForChild(
FROM_HERE, process->pid(),
base::BindOnce(&DeviceEjector::OnEjectProcessTerminated,
weak_ptr_factory_.GetWeakPtr(), device_path));
} else {
eject_process_.erase(device_path);
LOG(WARNING) << "Cannot eject media from device " << quote(device_path);
}
return started;
}
void DeviceEjector::OnEjectProcessTerminated(const std::string& device_path,
const siginfo_t& info) {
eject_process_.erase(device_path);
switch (info.si_code) {
case CLD_EXITED:
if (info.si_status == 0) {
LOG(INFO) << "Process " << info.si_pid << " for ejecting "
<< quote(device_path) << " completed successfully";
} else {
LOG(ERROR) << "Process " << info.si_pid << " for ejecting "
<< quote(device_path) << " exited with a status "
<< info.si_status;
}
break;
case CLD_DUMPED:
case CLD_KILLED:
LOG(ERROR) << "Process " << info.si_pid << " for ejecting "
<< quote(device_path) << " killed by a signal "
<< info.si_status;
break;
default:
break;
}
}
} // namespace cros_disks