blob: 638113949348d3dc155df076ae24b617de754392 [file] [log] [blame]
// Copyright 2021 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 "pciguard/udev_monitor.h"
#include "pciguard/daemon.h"
namespace pciguard {
namespace {
const char kUdev[] = "udev";
const char kThunderboltSubsystem[] = "thunderbolt";
const char kThunderboltDevice[] = "thunderbolt_device";
const char kPCISubsystem[] = "pci";
} // namespace
UdevMonitor::UdevMonitor(EventHandler* ev_handler, PcidevBlockedFn callback)
: event_handler_(ev_handler), pcidev_blocked_callback_(callback) {
udev_ = brillo::Udev::Create();
if (!udev_) {
PLOG(ERROR) << "Failed to initialize udev object.";
exit(EXIT_FAILURE);
}
udev_monitor_ = udev_->CreateMonitorFromNetlink(kUdev);
if (!udev_monitor_) {
PLOG(ERROR) << "Failed to create udev monitor.";
exit(EXIT_FAILURE);
}
if (!udev_monitor_->FilterAddMatchSubsystemDeviceType(kThunderboltSubsystem,
kThunderboltDevice)) {
PLOG(ERROR) << "Failed to add thunderbolt subsystem to udev monitor.";
exit(EXIT_FAILURE);
}
if (!udev_monitor_->FilterAddMatchSubsystemDeviceType(kPCISubsystem,
nullptr)) {
PLOG(ERROR) << "Failed to add PCI subsystem to udev monitor.";
exit(EXIT_FAILURE);
}
if (!udev_monitor_->EnableReceiving()) {
PLOG(ERROR) << "Failed to enable receiving for udev monitor.";
exit(EXIT_FAILURE);
}
int fd = udev_monitor_->GetFileDescriptor();
if (fd == brillo::UdevMonitor::kInvalidFileDescriptor) {
PLOG(ERROR) << "Failed to get udev monitor fd.";
exit(EXIT_FAILURE);
}
udev_monitor_watcher_ = base::FileDescriptorWatcher::WatchReadable(
fd,
base::BindRepeating(&UdevMonitor::OnUdevEvent, base::Unretained(this)));
if (!udev_monitor_watcher_) {
PLOG(ERROR) << "Failed to start watcher for udev monitor fd.";
exit(EXIT_FAILURE);
}
}
void UdevMonitor::OnUdevEvent() {
auto device = udev_monitor_->ReceiveDevice();
if (!device) {
LOG(ERROR) << "Udev receive device failed.";
return;
}
auto path = base::FilePath(device->GetSysPath());
if (path.empty()) {
LOG(ERROR) << "Failed to get device syspath.";
return;
}
auto action = std::string(device->GetAction());
if (action.empty()) {
LOG(ERROR) << "Failed to get device action.";
return;
}
auto subsystem = std::string(device->GetSubsystem());
if (subsystem.empty()) {
LOG(ERROR) << "Failed to get device subsystem";
return;
}
if (subsystem == "thunderbolt") {
if (action == "add" || action == "remove")
LOG(INFO) << "UdevEvent: " << subsystem << " " << action << " " << path;
if (action == "add")
event_handler_->OnNewThunderboltDev(path);
} else if (subsystem == "pci" && action == "change") {
auto property = device->GetPropertyValue("EVENT");
auto event = std::string(property ? property : "");
property = device->GetPropertyValue("DRVR");
auto drvr = std::string(property ? property : "");
LOG(INFO) << "UdevEvent: " << subsystem << " " << action << " " << event
<< " " << drvr << " " << path;
if (event == "BLOCKED")
pcidev_blocked_callback_(drvr);
}
}
} // namespace pciguard