Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base/cvd/cuttlefish/common/libs/utils/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ cf_cc_library(
"//cuttlefish/common/libs/fs",
"//cuttlefish/common/libs/utils:wait_for_file",
"//cuttlefish/files:file_is_socket",
"//cuttlefish/posix:strerror",
"//cuttlefish/process:command",
"//cuttlefish/process:managed_stdio",
"//cuttlefish/result",
Expand Down
86 changes: 67 additions & 19 deletions base/cvd/cuttlefish/common/libs/utils/wait_for_unix_socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,84 @@

#include <sched.h>

#include <cerrno>
#include <chrono>
#include <cstdlib>
#include <fstream>
#include <regex>
#include <sstream>
#include <string>
#include <thread>
#include <utility>

#include "absl/log/log.h"

#include "cuttlefish/common/libs/fs/shared_fd.h"
#include "cuttlefish/common/libs/utils/wait_for_file.h"
#include "cuttlefish/files/file_is_socket.h"
#include "cuttlefish/posix/strerror.h"
#include "cuttlefish/process/command.h"
#include "cuttlefish/process/managed_stdio.h"
#include "cuttlefish/result/result.h"

namespace cuttlefish {
namespace {

#ifdef __linux__
// `__SO_ACCEPTCON`, not exported to userspace. Set in `Flags` after listen(2).
constexpr unsigned long kSoAcceptCon = 0x10000;

Result<bool> IsUnixSocketListeningViaProc(const std::string& path) {
std::ifstream proc_unix("/proc/net/unix");
CF_EXPECTF(proc_unix.is_open(), "Failed to open /proc/net/unix: {}",
StrError(errno));
// Columns: Num RefCount Protocol Flags Type St Inode Path
// Accepted sockets alias the listener's path, so check every match.
std::string line;
while (std::getline(proc_unix, line)) {
std::istringstream iss(line);
std::string num, refcount, protocol, flags, type, st, inode, socket_path;
if (!(iss >> num >> refcount >> protocol >> flags >> type >> st >> inode >>
socket_path)) {
continue;
}
if (socket_path == path &&
(std::strtoul(flags.c_str(), nullptr, 16) & kSoAcceptCon) != 0) {
return true;
}
}
return false;
}
#else
Result<bool> IsUnixSocketListeningViaLsof(const std::string& path) {
static const std::regex socket_state_regex("TST=(.*)");

Command lsof("/usr/bin/lsof");
lsof.AddParameter(/*"format"*/ "-F", /*"connection state"*/ "TST");
lsof.AddParameter(path);
const std::string lsof_out = CF_EXPECT(RunAndCaptureStdout(std::move(lsof)));

VLOG(0) << "lsof stdout:|" << lsof_out << "|";

std::smatch socket_state_match;
if (!std::regex_search(lsof_out, socket_state_match, socket_state_regex)) {
return false;
}
return socket_state_match.size() == 2 && socket_state_match[1] == "LISTEN";
}
#endif

// Whether `path` is a listening unix socket. Must not connect: a probe would be
// consumed as the vhost-user backend's one and only frontend.
Result<bool> IsUnixSocketListening(const std::string& path) {
#ifdef __linux__
return CF_EXPECT(IsUnixSocketListeningViaProc(path));
#else
return CF_EXPECT(IsUnixSocketListeningViaLsof(path));
#endif
}

} // namespace

Result<void> WaitForUnixSocket(const std::string& path, int timeoutSec) {
const auto targetTime =
Expand All @@ -59,7 +122,7 @@ Result<void> WaitForUnixSocket(const std::string& path, int timeoutSec) {
return {};
}

sched_yield();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}

return CF_ERR("This shouldn't be executed");
Expand All @@ -74,33 +137,18 @@ Result<void> WaitForUnixSocketListeningWithoutConnect(const std::string& path,
"Waiting for socket path creation failed");
CF_EXPECT(FileIsSocket(path), "Specified path is not a socket");

std::regex socket_state_regex("TST=(.*)");

while (true) {
const auto currentTime = std::chrono::system_clock::now();

if (currentTime >= targetTime) {
return CF_ERR("Timed out");
}

Command lsof("/usr/bin/lsof");
lsof.AddParameter(/*"format"*/ "-F", /*"connection state"*/ "TST");
lsof.AddParameter(path);
std::string lsof_out = CF_EXPECT(RunAndCaptureStdout(std::move(lsof)));

VLOG(0) << "lsof stdout:|" << lsof_out << "|";

std::smatch socket_state_match;
if (std::regex_search(lsof_out, socket_state_match, socket_state_regex)) {
if (socket_state_match.size() == 2) {
const std::string& socket_state = socket_state_match[1];
if (socket_state == "LISTEN") {
return {};
}
}
if (CF_EXPECT(IsUnixSocketListening(path))) {
return {};
}

sched_yield();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}

return CF_ERR("This shouldn't be executed");
Expand Down
10 changes: 6 additions & 4 deletions base/cvd/cuttlefish/host/commands/assemble_cvd/flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -905,10 +905,11 @@ Result<CuttlefishConfig> InitializeCuttlefishConfiguration(
}

if (vhost_user_vsock_vec[instance_index] == kVhostUserVsockModeAuto) {
std::set<Arch> default_on_arch = {Arch::Arm64};
std::set<Arch> default_on_arch = {Arch::Arm64, Arch::RiscV64};
if (guest_configs[instance_index].vhost_user_vsock) {
instance.set_vhost_user_vsock(true);
} else if (VmManagerIsCrosvm(tmp_config_obj) &&
} else if ((VmManagerIsCrosvm(tmp_config_obj) ||
VmManagerIsQemu(tmp_config_obj)) &&
default_on_arch.find(
guest_configs[instance_index].target_arch) !=
default_on_arch.end()) {
Expand All @@ -918,8 +919,9 @@ Result<CuttlefishConfig> InitializeCuttlefishConfiguration(
}
} else if (vhost_user_vsock_vec[instance_index] ==
kVhostUserVsockModeTrue) {
CF_EXPECT_EQ(tmp_config_obj.vm_manager(), VmmMode::kCrosvm,
"For now, only crosvm supports vhost_user_vsock");
CF_EXPECT(
VmManagerIsCrosvm(tmp_config_obj) || VmManagerIsQemu(tmp_config_obj),
"For now, only crosvm and qemu support vhost_user_vsock");
instance.set_vhost_user_vsock(true);
} else if (vhost_user_vsock_vec[instance_index] ==
kVhostUserVsockModeFalse) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ Result<void> VhostDeviceVsock::WaitForAvailability() {
fmt::format("{}/vsock_{}_{}/vm.vsock", TempDir(),
instance_.vsock_guest_cid(), std::to_string(getuid())),
30));
CF_EXPECT(WaitForUnixSocketListeningWithoutConnect(
fmt::format("{}/vsock_{}_{}/vhost.socket", TempDir(),
instance_.vsock_guest_cid(), std::to_string(getuid())),
30));
}
return {};
}
Expand Down
19 changes: 16 additions & 3 deletions base/cvd/cuttlefish/host/libs/vm_manager/qemu_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@

#include "absl/log/log.h"
#include "absl/strings/str_split.h"
#include "fmt/format.h"
#include "vulkan/vulkan.h"

#include "cuttlefish/common/libs/utils/files.h"
#include "cuttlefish/common/libs/utils/host_info.h"
#include "cuttlefish/common/libs/utils/in_sandbox.h"
#include "cuttlefish/common/libs/utils/known_paths.h"
#include "cuttlefish/common/libs/utils/wait_for_unix_socket.h"
#include "cuttlefish/files/file_exists.h"
#include "cuttlefish/host/libs/config/config_constants.h"
Expand Down Expand Up @@ -873,9 +875,20 @@ Result<std::vector<MonitorCommand>> QemuManager::StartCommands(
qemu_cmd.AddParameter("timestamp=on");

#ifdef __linux__
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("vhost-vsock-pci-non-transitional,guest-cid=",
instance.vsock_guest_cid());
if (instance.vhost_user_vsock()) {
const std::string vhost_socket =
fmt::format("{}/vsock_{}_{}/vhost.socket", TempDir(),
instance.vsock_guest_cid(), getuid());
qemu_cmd.AddParameter("-chardev");
qemu_cmd.AddParameter("socket,id=char_vsock,path=", vhost_socket);
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter(
"vhost-user-vsock-pci-non-transitional,chardev=char_vsock");
} else {
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("vhost-vsock-pci-non-transitional,guest-cid=",
instance.vsock_guest_cid());
}
#endif

qemu_cmd.AddParameter("-device");
Expand Down
Loading