blob: c88abeb711a83e6dab766dcfe28ab2c29d4ccb1a [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/network/tc_process.h"
#include <memory>
#include <utility>
#include <vector>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include <net-base/process_manager.h>
#include "shill/logging.h"
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kTC;
} // namespace Logging
std::unique_ptr<TCProcess> TCProcess::Create(
const std::vector<std::string>& commands,
ExitCallback exit_callback,
net_base::ProcessManager* process_manager) {
CHECK(process_manager != nullptr);
std::unique_ptr<TCProcess> tc_process = base::WrapUnique(
new TCProcess(process_manager, commands, std::move(exit_callback)));
if (!tc_process->Initialize()) {
return nullptr;
}
return tc_process;
}
TCProcess::TCProcess(net_base::ProcessManager* process_manager,
const std::vector<std::string>& commands,
ExitCallback exit_callback)
: process_manager_(process_manager),
commands_(commands),
exit_callback_(std::move(exit_callback)) {}
TCProcess::~TCProcess() {
if (tc_pid_ != net_base::ProcessManager::kInvalidPID) {
process_manager_->StopProcess(tc_pid_);
}
}
bool TCProcess::Initialize() {
const std::vector<std::string> args = {
"-f", // Continue if there is a failure or no-op
"-b", // Batch mode
"-" // Use stdin for input
};
const net_base::ProcessManager::MinijailOptions minijail_options{
.user = std::string(kTCUser),
.group = std::string(kTCGroup),
.capmask = CAP_TO_MASK(CAP_NET_ADMIN),
.inherit_supplementary_groups = false,
};
// shill's stderr is wired to syslog, so nullptr for stderr
// here implies the tc process's errors show up in /var/log/net.log.
int stdin_fd = net_base::ProcessManager::kInvalidPID;
struct net_base::std_file_descriptors std_fds {
&stdin_fd, nullptr, nullptr
};
tc_pid_ = process_manager_->StartProcessInMinijailWithPipes(
FROM_HERE, base::FilePath(kTCPath), args, {}, minijail_options,
base::BindOnce(&TCProcess::OnProcessExited, weak_factory_.GetWeakPtr()),
std_fds);
if (tc_pid_ == net_base::ProcessManager::kInvalidPID) {
LOG(ERROR) << "Failed to start TC process";
return false;
}
SLOG(1) << "Spawned tc with pid: " << tc_pid_;
tc_stdin_ = base::ScopedFD(stdin_fd);
if (!base::SetNonBlocking(tc_stdin_.get())) {
LOG(ERROR) << "Unable to set TC pipes to be non-blocking";
return false;
}
tc_stdin_watcher_ = base::FileDescriptorWatcher::WatchWritable(
tc_stdin_.get(), base::BindRepeating(&TCProcess::OnTCProcessWritable,
base::Unretained(this)));
return true;
}
void TCProcess::OnTCProcessWritable() {
for (const auto& command : commands_) {
SLOG(2) << "Issuing tc command: " << command;
if (!base::WriteFileDescriptor(tc_stdin_.get(), command)) {
PLOG(ERROR) << "Failed to write command to TC process: " << command;
break;
}
}
tc_stdin_watcher_.reset();
tc_stdin_.reset();
}
void TCProcess::OnProcessExited(int exit_status) {
tc_pid_ = net_base::ProcessManager::kInvalidPID;
std::move(exit_callback_).Run(exit_status);
}
std::unique_ptr<TCProcess> TCProcessFactory::Create(
const std::vector<std::string>& commands,
TCProcess::ExitCallback exit_callback,
net_base::ProcessManager* process_manager) {
return TCProcess::Create(commands, std::move(exit_callback), process_manager);
}
} // namespace shill