blob: 4718602c5f337e866dd3effaba5442e1cc660b21 [file] [log] [blame]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <iostream>
#include <limits.h>
#include <signal.h>
#include <sys/socket.h>
#include <syslog.h>
#include <unistd.h>
#include <linux/vm_sockets.h> // Needs to come after sys/socket.h
#include <vm_protos/proto_bindings/container_host.pb.h>
#include <memory>
#include <string>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/string_split.h>
// syslog.h and base/logging.h both try to #define LOG_INFO and LOG_WARNING.
// We need to #undef at least these two before including base/logging.h. The
// others are included to be consistent.
namespace {
const int kSyslogDebug = LOG_DEBUG;
const int kSyslogInfo = LOG_INFO;
const int kSyslogWarning = LOG_WARNING;
const int kSyslogError = LOG_ERR;
const int kSyslogCritical = LOG_CRIT;
#undef LOG_INFO
#undef LOG_WARNING
#undef LOG_ERR
#undef LOG_CRIT
} // namespace
#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/files/file_descriptor_watcher_posix.h>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/message_loop/message_pump_type.h>
#include <base/run_loop.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <base/synchronization/waitable_event.h>
#include <base/system/sys_info.h>
#include <base/task/single_thread_task_executor.h>
#include <base/task/task_runner.h>
#include <base/threading/thread.h>
#include <vm_protos/proto_bindings/container_guest.grpc.pb.h>
#include <chromeos/constants/vm_tools.h>
#include <base/files/scoped_file.h>
#include "google/protobuf/util/json_util.h"
#include "vm_tools/common/paths.h"
#include "vm_tools/common/spawn_util.h"
#include "vm_tools/garcon/file_chooser_dbus_service.h"
#include "vm_tools/garcon/host_notifier.h"
#include "vm_tools/garcon/package_kit_proxy.h"
#include "vm_tools/garcon/screensaver_dbus_service.h"
#include "vm_tools/garcon/service_impl.h"
namespace {
constexpr char kLogPrefix[] = "garcon: ";
constexpr char kAllowAnyUserSwitch[] = "allow_any_user";
constexpr char kServerSwitch[] = "server";
constexpr char kClientSwitch[] = "client";
constexpr char kUrlSwitch[] = "url";
constexpr char kTerminalSwitch[] = "terminal";
constexpr char kSelectFileSwitch[] = "selectfile";
constexpr char kSelectFileTypeSwitch[] = "type";
constexpr char kSelectFileTitleSwitch[] = "title";
constexpr char kSelectFilePathSwitch[] = "path";
constexpr char kSelectFileExtensionsSwitch[] = "extensions";
constexpr char kShaderSwitch[] = "borealis-shader-cache";
constexpr char kShaderAppIDSwitch[] = "app-id";
constexpr char kShaderInstallSwitch[] = "install";
constexpr char kShaderUninstallSwitch[] = "uninstall";
constexpr char kShaderMountSwitch[] = "mount";
constexpr char kShaderUnmountSwitch[] = "unmount";
constexpr char kShaderWaitSwitch[] = "wait";
constexpr char kSftpServer[] = "/usr/lib/openssh/sftp-server";
constexpr char kMetricsSwitch[] = "metrics";
constexpr char kNoStartupNotifySwitch[] = "no_startup_notify";
constexpr uint32_t kVsockPortStart = 10000;
constexpr uint32_t kVsockPortEnd = 20000;
constexpr int kSecurityTokenLength = 36;
constexpr uid_t kCrostiniDefaultUid = 1000;
bool LogToSyslog(logging::LogSeverity severity,
const char* /* file */,
int /* line */,
size_t message_start,
const std::string& message) {
switch (severity) {
case logging::LOGGING_INFO:
severity = kSyslogInfo;
break;
case logging::LOGGING_WARNING:
severity = kSyslogWarning;
break;
case logging::LOGGING_ERROR:
severity = kSyslogError;
break;
case logging::LOGGING_FATAL:
severity = kSyslogCritical;
break;
default:
severity = kSyslogDebug;
break;
}
syslog(severity, "%s", message.c_str() + message_start);
return true;
}
void BlockSigterm() {
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
PCHECK(sigprocmask(SIG_BLOCK, &mask, nullptr) == 0);
}
// Picks a vsock port, listens on it, and launches sftp-server when the
// other side connects.
void RunSftpHandler(uint32_t* sftp_port, base::WaitableEvent* event) {
BlockSigterm();
base::ScopedFD vsock(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0));
PCHECK(vsock.is_valid());
{
const sockaddr_vm addr{
.svm_family = AF_VSOCK,
.svm_port = VMADDR_PORT_ANY,
.svm_cid = VMADDR_CID_ANY,
};
PCHECK(bind(vsock.get(), reinterpret_cast<const struct sockaddr*>(&addr),
sizeof(addr)) == 0);
}
PCHECK(listen(vsock.get(), 1) == 0);
{
sockaddr_vm addr;
socklen_t len = sizeof(addr);
PCHECK(getsockname(vsock.get(), reinterpret_cast<struct sockaddr*>(&addr),
&len) == 0);
*sftp_port = addr.svm_port;
}
LOG(INFO) << "sftp listening on vsock port " << *sftp_port;
event->Signal();
// TODO(b/231500896): Does this loop need to exit? Maybe check
// base::Thread::IsRunning?
while (1) {
LOG(INFO) << "sftp: accept waiting";
sockaddr_vm addr;
socklen_t len = sizeof(addr);
base::ScopedFD fd(accept4(vsock.get(),
reinterpret_cast<struct sockaddr*>(&addr), &len,
SOCK_CLOEXEC));
PCHECK(fd.is_valid());
LOG(INFO) << "sftp: accepted connection from vsock:" << addr.svm_cid << ":"
<< addr.svm_port;
std::vector<std::string> argv = {kSftpServer};
std::map<std::string, std::string> env;
const std::string working_dir;
int stdio_fd[3] = {fd.get(), fd.get(), STDERR_FILENO};
if (vm_tools::Spawn(argv, env, working_dir, stdio_fd)) {
fd.reset();
LOG(INFO) << "sftp: forked child process";
} else {
PLOG(ERROR) << "sftp: failed to spawn child process";
}
}
}
void RunGarconService(vm_tools::garcon::PackageKitProxy* pk_proxy,
base::WaitableEvent* event,
std::shared_ptr<grpc::Server>* server_copy,
int* vsock_listen_port,
scoped_refptr<base::TaskRunner> task_runner,
vm_tools::garcon::HostNotifier* host_notifier,
bool startup_notify_allowed) {
// We don't want to receive SIGTERM on this thread.
BlockSigterm();
// See crbug.com/922694 for more reference.
// There's a bug in our patched version of gRPC where it uses signed integers
// for ports. VSOCK uses unsigned integers for ports. So if we let the kernel
// choose the port for us, then it can end up choosing one that has the high
// bit set and cause gRPC to assert on the negative port number. This was a
// much easier solution than patching gRPC or updating the kernel to keep the
// VSOCK ports in the signed integer range.
// The end on this for loop only exists to prevent running forever in case
// something else goes wrong.
for (*vsock_listen_port = kVsockPortStart; *vsock_listen_port < kVsockPortEnd;
++(*vsock_listen_port)) {
// Build the server.
grpc::ServerBuilder builder;
builder.AddListeningPort(
base::StringPrintf("vsock:%u:%d", VMADDR_CID_ANY, *vsock_listen_port),
grpc::InsecureServerCredentials(), nullptr);
vm_tools::garcon::ServiceImpl garcon_service(
pk_proxy, task_runner.get(), host_notifier, startup_notify_allowed);
builder.RegisterService(&garcon_service);
std::shared_ptr<grpc::Server> server(builder.BuildAndStart().release());
if (!server) {
LOG(WARNING) << "garcon failed binding requested vsock port "
<< *vsock_listen_port << ", trying again with a new port";
continue;
}
*server_copy = server;
event->Signal();
LOG(INFO) << "garcon listening on vsock port " << *vsock_listen_port;
// The following call will return once we invoke Shutdown on the gRPC
// server when the main RunLoop exits.
server->Wait();
break;
}
}
void CreatePackageKitProxy(
base::WaitableEvent* event,
vm_tools::garcon::HostNotifier* host_notifier,
std::unique_ptr<vm_tools::garcon::PackageKitProxy>* proxy_ptr) {
// We don't want to receive SIGTERM on this thread.
BlockSigterm();
*proxy_ptr = vm_tools::garcon::PackageKitProxy::Create(host_notifier);
event->Signal();
}
void CreateDBusServices(
base::WaitableEvent* event,
vm_tools::garcon::HostNotifier* host_notifier,
std::unique_ptr<vm_tools::garcon::ScreenSaverDBusService>*
screensaver_proxy_ptr,
std::unique_ptr<vm_tools::garcon::FileChooserDBusService>*
file_chooser_proxy_ptr) {
// We don't want to receive SIGTERM on this thread.
BlockSigterm();
*screensaver_proxy_ptr =
vm_tools::garcon::ScreenSaverDBusService::Create(host_notifier);
*file_chooser_proxy_ptr =
vm_tools::garcon::FileChooserDBusService::Create(host_notifier);
event->Signal();
}
void PrintUsage() {
LOG(INFO) << "Garcon: VM container bridge for Chrome OS\n\n"
<< "Mode Switches (must use one):\n"
<< "Mode Switch:\n"
<< " --server: run in background as daemon\n"
<< " --client: run as client and send message to host\n"
<< "Client Switches (only with --client):\n"
<< " --url: opens all arguments as URLs in host browser\n"
<< " --terminal: opens terminal\n"
<< " --selectfile: open file dialog and return file: URL list\n"
<< " --metrics: reports metrics to the host\n"
<< " --borealis-shader-cache: (un)install shader cache\n"
<< "Borealis Shader Cache Switches "
<< "(only with --client --borealis-shader-cache):\n"
<< " --app-id: Steam app ID\n"
<< " --install: (optional) Install shader cache DLC\n"
<< " --uninstall: (optional) Unmount and uninstall shader cache\n"
<< " DLC\n"
<< " --unmount: (optional) Unmount shader cache for this VM\n"
<< " --mount: (optional, use with --install) Upon shader cache\n"
<< " DLC installation, mount the DLC contents to VM's\n"
<< " GPU cache\n"
<< " --wait: (optional, use with --install or --unmount) Wait\n"
<< " for all the operations to complete, including DLC\n"
<< " download for --install\n"
<< "Select File Switches (only with --client --selectfile):\n"
<< " --type: "
"open-file|open-multi-file|saveas-file|folder|upload-folder\n"
<< " --title: title for dialog\n"
<< " --path: default path (file: URL or path)\n"
<< " --extensions: comma-separated list of allowed extensions\n"
<< "Metrics args (use with --client --metrics):\n"
<< " <metric_name>=<metric_value>,[...]\n"
<< "Server Switches (only with --server):\n"
<< " --allow_any_user: allow running as non-default uid\n";
}
std::string GetSecurityToken() {
char token[kSecurityTokenLength + 1];
base::FilePath security_token_path(vm_tools::kGarconContainerTokenFile);
int num_read = base::ReadFile(security_token_path, token, sizeof(token) - 1);
if (num_read <= 0) {
return "";
}
token[num_read] = '\0';
return std::string(token);
}
int HandleMetricsArgs(std::vector<std::string> args,
vm_tools::garcon::HostNotifier* host_notifier) {
vm_tools::container::ReportMetricsRequest request;
if (args.empty()) {
LOG(ERROR) << "Missing arguments in --metrics mode";
PrintUsage();
return -1;
}
// Expected argument: swap_bytes_written=1234567890,bytes_written=99999999,...
base::StringPairs key_value_pairs;
if (!base::SplitStringIntoKeyValuePairs(args.at(0), '=', ',',
&key_value_pairs)) {
LOG(ERROR) << "Invalid argument to --metrics";
PrintUsage();
return -1;
}
for (const auto& [metric_name, metric_value] : key_value_pairs) {
auto metric = request.add_metric();
metric->set_name(metric_name);
uint64_t metric_arg;
bool arg_conversion = base::StringToUint64(metric_value, &metric_arg);
if (!arg_conversion) {
LOG(ERROR) << "Couldn't parse metric value (expected Uint64)";
PrintUsage();
return -1;
}
metric->set_value(metric_arg);
}
vm_tools::container::ReportMetricsResponse response;
if (!host_notifier->ReportMetrics(std::move(request), &response)) {
LOG(ERROR) << "ReportMetrics RPC to host failed";
// Distinguish this error from other errors as it's reasonable
// to retry the request if this error happens.
return 1;
}
if (response.error() != 0) {
LOG(ERROR) << "ReportMetrics RPC to host returned error "
<< response.error();
return -1;
}
return 0;
}
int HandleShaderCacheArgs(base::CommandLine* cl,
vm_tools::garcon::HostNotifier* host_notifier) {
uint64_t app_id = 0;
std::string app_id_string = cl->GetSwitchValueNative(kShaderAppIDSwitch);
if (app_id_string.empty()) {
LOG(ERROR) << "Missing --" << kShaderAppIDSwitch << "=<Steam appid>";
return -1;
}
base::StringToUint64(app_id_string, &app_id);
if (app_id == 0) {
LOG(ERROR) << "Invalid app ID";
return -1;
}
auto flag_set = {kShaderInstallSwitch, kShaderUninstallSwitch,
kShaderUnmountSwitch};
int flag_count = 0;
std::ostringstream flags_combined;
for (auto flag : flag_set) {
flag_count += cl->HasSwitch(flag);
flags_combined << flag << " ";
}
if (flag_count > 1) {
LOG(ERROR) << "Only one of the following flags is allowed: "
<< flags_combined.str();
return -1;
} else if (flag_count == 0) {
LOG(ERROR) << "One of the following flags must be specified: "
<< flags_combined.str();
return -1;
}
bool success = false;
if (cl->HasSwitch(kShaderInstallSwitch)) {
LOG(INFO) << "Installing shader cache for " << app_id_string;
if (cl->HasSwitch(kShaderMountSwitch)) {
LOG(INFO) << "Upon successful installation, shader cache will be mounted";
}
if (cl->HasSwitch(kShaderWaitSwitch)) {
LOG(INFO) << "Waiting for all operations to complete";
}
success = host_notifier->InstallShaderCache(
app_id, cl->HasSwitch(kShaderMountSwitch),
cl->HasSwitch(kShaderWaitSwitch));
} else if (cl->HasSwitch(kShaderUnmountSwitch)) {
LOG(INFO) << "Queuing unmount command for " << app_id_string
<< " in the background. Shader cache will be unmounted once mesa"
<< " stops using them.";
if (cl->HasSwitch(kShaderWaitSwitch)) {
LOG(INFO) << "Waiting for all operations to complete";
}
success = host_notifier->UnmountShaderCache(
app_id, cl->HasSwitch(kShaderWaitSwitch));
} else if (cl->HasSwitch(kShaderUninstallSwitch)) {
if (cl->HasSwitch(kShaderMountSwitch)) {
LOG(WARNING) << "Shader cache being uninstalled, ignoring --"
<< kShaderMountSwitch;
}
if (cl->HasSwitch(kShaderWaitSwitch)) {
LOG(WARNING) << "Shader cache uninstall always waits, --"
<< kShaderWaitSwitch << " flag is redundant";
}
if (cl->HasSwitch(kShaderUnmountSwitch)) {
LOG(WARNING) << "Shader cache uninstall always unmounts, --"
<< kShaderWaitSwitch << " flag is redundant";
}
LOG(INFO) << "Unmounting and uninstalling shader cache for "
<< app_id_string;
success = host_notifier->UninstallShaderCache(app_id);
} else {
LOG(ERROR) << "No command specified, specify one of --"
<< kShaderInstallSwitch << ", --" << kShaderUnmountSwitch
<< ", --" << kShaderUninstallSwitch;
}
return success ? 0 : -1;
}
} // namespace
int main(int argc, char** argv) {
base::AtExitManager at_exit;
base::SingleThreadTaskExecutor task_executor(base::MessagePumpType::IO);
base::FileDescriptorWatcher watcher(task_executor.task_runner());
base::CommandLine::Init(argc, argv);
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
logging::InitLogging(logging::LoggingSettings());
bool serverMode = cl->HasSwitch(kServerSwitch);
bool clientMode = cl->HasSwitch(kClientSwitch);
// The standard says that bool to int conversion is implicit and that
// false => 0 and true => 1.
// clang-format off
if (serverMode + clientMode != 1) {
// clang-format on
LOG(ERROR) << "Exactly one of --server or --client must be used.";
PrintUsage();
return -1;
}
std::string token = GetSecurityToken();
if (token.empty()) {
if (clientMode) {
LOG(ERROR) << "Failed to read the security token.";
return -1;
} else {
LOG(WARNING) << "Failed to read the security token, retrying.";
base::TimeTicks start = base::TimeTicks::Now();
while (token.empty()) {
if (base::TimeTicks::Now() - start > base::Minutes(1)) {
LOG(ERROR) << "Timed out waiting for security token.";
return -1;
}
base::PlatformThread::Sleep(base::Milliseconds(100));
token = GetSecurityToken();
}
}
}
std::unique_ptr<vm_tools::garcon::HostNotifier> host_notifier =
vm_tools::garcon::HostNotifier::Create(token);
if (!host_notifier) {
LOG(ERROR) << "Failure setting up the HostNotifier";
return -1;
}
if (clientMode) {
if (cl->HasSwitch(kUrlSwitch)) {
std::vector<std::string> args = cl->GetArgs();
if (args.empty()) {
LOG(ERROR) << "Missing URL arguments in --url mode";
PrintUsage();
return -1;
}
// All arguments are URLs, send them to the host to be opened. The host
// will do its own verification for validity of the URLs.
for (const auto& arg : args) {
if (!host_notifier->OpenUrlInHost(arg)) {
return -1;
}
}
return 0;
} else if (cl->HasSwitch(kTerminalSwitch)) {
std::vector<std::string> args = cl->GetArgs();
if (host_notifier->OpenTerminal(std::move(args)))
return 0;
else
return -1;
} else if (cl->HasSwitch(kSelectFileSwitch)) {
std::string type = cl->GetSwitchValueNative(kSelectFileTypeSwitch);
std::string title = cl->GetSwitchValueNative(kSelectFileTitleSwitch);
std::string path = cl->GetSwitchValueNative(kSelectFilePathSwitch);
std::string extensions =
cl->GetSwitchValueNative(kSelectFileExtensionsSwitch);
std::vector<std::string> files;
if (host_notifier->SelectFile(type, title, path, extensions, &files)) {
for (const auto& file : files) {
std::cout << file << std::endl;
}
return 0;
} else {
return -1;
}
} else if (cl->HasSwitch(kMetricsSwitch)) {
return HandleMetricsArgs(cl->GetArgs(), host_notifier.get());
} else if (cl->HasSwitch(kShaderSwitch)) {
return HandleShaderCacheArgs(cl, host_notifier.get());
}
LOG(ERROR) << "Missing client switch for client mode.";
PrintUsage();
return -1;
}
// Set up logging to syslog for server mode.
openlog(kLogPrefix, LOG_PID, LOG_DAEMON);
logging::SetLogMessageHandler(LogToSyslog);
// Exit if not running as the container default user.
if (getuid() != kCrostiniDefaultUid && !cl->HasSwitch(kAllowAnyUserSwitch)) {
LOG(ERROR) << "garcon normally runs only as uid(" << kCrostiniDefaultUid
<< "). Use --allow_any_user to override";
return -1;
}
bool startup_notify_allowed = true;
if (cl->HasSwitch(kNoStartupNotifySwitch)) {
startup_notify_allowed = false;
}
// Note on the threading model: There are 5 threads used in garcon:
//
// - incoming gRPC requests
// - handling connections to the sftp-server over vsock
// - D-Bus communication with the PackageKit
// - the main thread which is for gRPC requests to the host as well as for
// monitoring filesystem changes (which result in a
// gRPC call to the host under certain conditions). The main thing to be
// careful of is that the gRPC thread for incoming requests is never blocking
// on the gRPC thread for outgoing requests (since they are both talking to
// cicerone, and both of those operations in cicerone are likely going to use
// the same D-Bus thread for communication within cicerone).
// - running tasks initiated by garcon service.
// Thread that the gRPC server is running on.
base::Thread grpc_thread{"gRPC Server Thread"};
if (!grpc_thread.Start()) {
LOG(ERROR) << "Failed starting the gRPC thread";
return -1;
}
// Thread that the sftp-server handler is running on.
base::Thread sftp_thread{"sftp-server Thread"};
if (!sftp_thread.Start()) {
LOG(ERROR) << "Failed starting the sftp-server thread";
return -1;
}
// Thread that D-Bus communication runs on.
base::Thread dbus_thread{"D-Bus Thread"};
if (!dbus_thread.StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0))) {
LOG(ERROR) << "Failed starting the D-Bus thread";
return -1;
}
// Thread that tasks started from garcon service run on.
// Specifically, Ansible playbook application runs on
// |garcon_service_tasks_thread|.
base::Thread garcon_service_tasks_thread{"Garcon Service Tasks Thread"};
if (!garcon_service_tasks_thread.StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0))) {
LOG(ERROR) << "Failed starting the garcon service tasks thread";
return -1;
}
// Setup the HostNotifier on the run loop for the main thread. It needs to
// have its own run loop separate from the gRPC server & D-Bus server since it
// will be using base::FilePathWatcher to identify installed application and
// mime type changes.
base::RunLoop run_loop;
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
// This needs to be created on the D-Bus thread.
std::unique_ptr<vm_tools::garcon::PackageKitProxy> pk_proxy;
bool ret = dbus_thread.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&CreatePackageKitProxy, &event,
host_notifier.get(), &pk_proxy));
if (!ret) {
LOG(ERROR) << "Failed to post PackageKit proxy creation to D-Bus thread";
return -1;
}
// Wait for the creation to complete.
event.Wait();
if (!pk_proxy) {
LOG(ERROR) << "Failed in creating the PackageKit proxy";
return -1;
}
event.Reset();
// These need to be created on the D-Bus thread.
std::unique_ptr<vm_tools::garcon::ScreenSaverDBusService> screensaver;
std::unique_ptr<vm_tools::garcon::FileChooserDBusService> file_chooser;
ret = dbus_thread.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&CreateDBusServices, &event, host_notifier.get(),
&screensaver, &file_chooser));
if (!ret) {
LOG(ERROR) << "Failed to post D-Bus server creation to D-Bus thread";
return -1;
}
// Wait for the creation to complete.
event.Wait();
if (!screensaver || !file_chooser) {
// Not returning -1 on failure as it is not essential for the VM to start.
LOG(ERROR) << "Failed in creating the D-Bus servers";
}
event.Reset();
// Launch the gRPC server on the gRPC thread.
std::shared_ptr<grpc::Server> server_copy;
int vsock_listen_port = 0;
ret = grpc_thread.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&RunGarconService, pk_proxy.get(), &event,
&server_copy, &vsock_listen_port,
garcon_service_tasks_thread.task_runner(),
host_notifier.get(), startup_notify_allowed));
if (!ret) {
LOG(ERROR) << "Failed to post server startup task to grpc thread";
return -1;
}
// Wait for the gRPC server to start.
event.Wait();
if (!server_copy) {
LOG(ERROR) << "gRPC server failed to start";
return -1;
}
event.Reset();
// Launch a thread that listens for incoming connections and runs sftp-server
// to handle them.
uint32_t sftp_port = 0;
ret = sftp_thread.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&RunSftpHandler, &sftp_port, &event));
if (!ret) {
LOG(ERROR) << "Failed to post server startup task to sftp thread";
return -1;
}
// Wait for the sftp server to start.
event.Wait();
if (sftp_port == 0) {
LOG(ERROR) << "sftp server failed to start";
return -1;
}
event.Reset();
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
PLOG(ERROR) << "Unable to explicitly ignore SIGCHILD";
return -1;
}
if (!host_notifier->InitServer(run_loop.QuitClosure(),
static_cast<uint32_t>(vsock_listen_port),
sftp_port, pk_proxy.get())) {
LOG(ERROR) << "Failed to set up host notifier";
return -1;
}
// Start the main run loop now for the HostNotifier.
run_loop.Run();
// We get here after a SIGTERM gets posted and the main run loop has exited.
// We then shutdown the gRPC server (which will terminate that thread) and
// then stop the D-Bus thread. We will be the only remaining thread at that
// point so everything can be safely destructed and we remove the need for
// any weak pointers.
server_copy->Shutdown();
dbus_thread.Stop();
garcon_service_tasks_thread.Stop();
sftp_thread.Stop();
return 0;
}