blob: ea1168808ddfee01b999b72c0297d2f01a7a0e6c [file] [log] [blame]
// Copyright 2017 The Chromium 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 "device/udev_linux/udev_watcher.h"
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_piece.h"
#include "base/threading/scoped_blocking_call.h"
namespace device {
UdevWatcher::Filter::Filter(base::StringPiece subsystem_in,
base::StringPiece devtype_in) {
if (!subsystem_in.empty())
subsystem_ = std::string(subsystem_in);
if (!devtype_in.empty())
devtype_ = std::string(devtype_in);
}
UdevWatcher::Filter::Filter(const Filter&) = default;
UdevWatcher::Filter::~Filter() = default;
const char* UdevWatcher::Filter::devtype() const {
return devtype_ ? devtype_.value().c_str() : nullptr;
}
const char* UdevWatcher::Filter::subsystem() const {
return subsystem_ ? subsystem_.value().c_str() : nullptr;
}
UdevWatcher::Observer::~Observer() = default;
std::unique_ptr<UdevWatcher> UdevWatcher::StartWatching(
Observer* observer,
const std::vector<Filter>& filters) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
ScopedUdevPtr udev(udev_new());
if (!udev) {
LOG(ERROR) << "Failed to initialize udev.";
return nullptr;
}
ScopedUdevMonitorPtr udev_monitor(
udev_monitor_new_from_netlink(udev.get(), "udev"));
if (!udev_monitor) {
LOG(ERROR) << "Failed to initialize a udev monitor.";
return nullptr;
}
for (const Filter& filter : filters) {
const int ret = udev_monitor_filter_add_match_subsystem_devtype(
udev_monitor.get(), filter.subsystem(), filter.devtype());
CHECK_EQ(0, ret);
}
if (udev_monitor_enable_receiving(udev_monitor.get()) != 0) {
LOG(ERROR) << "Failed to enable receiving udev events.";
return nullptr;
}
int monitor_fd = udev_monitor_get_fd(udev_monitor.get());
if (monitor_fd < 0) {
LOG(ERROR) << "Udev monitor file descriptor unavailable.";
return nullptr;
}
return base::WrapUnique(new UdevWatcher(
std::move(udev), std::move(udev_monitor), monitor_fd, observer, filters));
}
UdevWatcher::~UdevWatcher() {
DCHECK(sequence_checker_.CalledOnValidSequence());
}
void UdevWatcher::EnumerateExistingDevices() {
DCHECK(sequence_checker_.CalledOnValidSequence());
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get()));
if (!enumerate) {
LOG(ERROR) << "Failed to initialize a udev enumerator.";
return;
}
for (const Filter& filter : udev_filters_) {
const int ret =
udev_enumerate_add_match_subsystem(enumerate.get(), filter.subsystem());
CHECK_EQ(0, ret);
}
if (udev_enumerate_scan_devices(enumerate.get()) != 0) {
LOG(ERROR) << "Failed to begin udev enumeration.";
return;
}
udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
for (udev_list_entry* i = devices; i != nullptr;
i = udev_list_entry_get_next(i)) {
ScopedUdevDevicePtr device(
udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i)));
if (device)
observer_->OnDeviceAdded(std::move(device));
}
}
UdevWatcher::UdevWatcher(ScopedUdevPtr udev,
ScopedUdevMonitorPtr udev_monitor,
int monitor_fd,
Observer* observer,
const std::vector<Filter>& filters)
: udev_(std::move(udev)),
udev_monitor_(std::move(udev_monitor)),
observer_(observer),
udev_filters_(filters) {
file_watcher_ = base::FileDescriptorWatcher::WatchReadable(
monitor_fd, base::BindRepeating(&UdevWatcher::OnMonitorReadable,
base::Unretained(this)));
}
void UdevWatcher::OnMonitorReadable() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
ScopedUdevDevicePtr device(udev_monitor_receive_device(udev_monitor_.get()));
if (!device)
return;
std::string action(udev_device_get_action(device.get()));
if (action == "add")
observer_->OnDeviceAdded(std::move(device));
else if (action == "remove")
observer_->OnDeviceRemoved(std::move(device));
else if (action == "change")
observer_->OnDeviceChanged(std::move(device));
else
DVLOG(1) << "Unknown udev action: " << action;
}
} // namespace device