blob: 83eb6c4de465be8ec73bde5683519991b2e76ae7 [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 <sysexits.h>
#include <unistd.h>
#include <base/check.h>
#include <base/check_op.h>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/no_destructor.h>
#include <base/strings/stringprintf.h>
#include <brillo/daemons/dbus_daemon.h>
#include <brillo/syslog_logging.h>
#include <chromeos/dbus/service_constants.h>
#include "fusebox/file_system.h"
#include "fusebox/file_system_fake.h"
#include "fusebox/fuse_frontend.h"
#include "fusebox/util.h"
using base::CommandLine;
namespace {
void SetupLogging() {
brillo::InitLog(brillo::kLogToStderr);
}
static bool g_use_fake_file_system;
fusebox::FileSystem* CreateFakeFileSystem() {
static base::NoDestructor<fusebox::FileSystemFake> fake_file_system;
return fake_file_system.get();
}
} // namespace
namespace fusebox {
class FuseBoxClient : public FileSystem {
public:
FuseBoxClient(scoped_refptr<dbus::Bus> bus, FuseMount* fuse)
: fuse_(fuse), bus_(bus) {}
FuseBoxClient(const FuseBoxClient&) = delete;
FuseBoxClient& operator=(const FuseBoxClient&) = delete;
virtual ~FuseBoxClient() = default;
void RegisterDBusObjectsAsync(
const brillo::dbus_utils::AsyncEventSequencer::CompletionAction& cb) {
// TODO(noel): register FuseBoxClient DBUS objects.
}
int StartFuseSession(base::OnceClosure quit_callback) {
quit_callback_ = std::move(quit_callback);
fuse_frontend_.reset(new FuseFrontend(fuse_));
auto* fs = g_use_fake_file_system ? CreateFakeFileSystem() : this;
if (!fuse_frontend_->CreateFuseSession(fs, FileSystem::FuseOps()))
return EX_SOFTWARE;
auto stop = base::BindOnce(&FuseBoxClient::Stop, base::Unretained(this));
fuse_frontend_->StartFuseSession(std::move(stop));
return EX_OK;
}
void Stop() {
fuse_frontend_.reset();
if (quit_callback_) {
std::move(quit_callback_).Run();
}
}
private:
// Fuse mount: not owned.
FuseMount* fuse_ = nullptr;
// D-Bus.
scoped_refptr<dbus::Bus> bus_;
// Fuse user-space frontend.
std::unique_ptr<FuseFrontend> fuse_frontend_;
// Quit callback.
base::OnceClosure quit_callback_;
};
class FuseBoxDaemon : public brillo::DBusServiceDaemon {
public:
explicit FuseBoxDaemon(FuseMount* fuse)
: DBusServiceDaemon(kFuseBoxClientName), fuse_(fuse) {}
FuseBoxDaemon(const FuseBoxDaemon&) = delete;
FuseBoxDaemon& operator=(const FuseBoxDaemon&) = delete;
~FuseBoxDaemon() = default;
protected:
// brillo::DBusServiceDaemon overrides.
void RegisterDBusObjectsAsync(
brillo::dbus_utils::AsyncEventSequencer* sequencer) override {
bus_->AssertOnDBusThread();
client_.reset(new FuseBoxClient(bus_, fuse_));
client_->RegisterDBusObjectsAsync(
sequencer->GetHandler("D-Bus register async failed", true));
}
int OnEventLoopStarted() override {
bus_->AssertOnDBusThread();
int ret = brillo::DBusServiceDaemon::OnEventLoopStarted();
if (ret != EX_OK)
return ret;
auto quit = base::BindOnce(&Daemon::Quit, base::Unretained(this));
return client_->StartFuseSession(std::move(quit));
}
void OnShutdown(int* exit_code) override {
bus_->AssertOnDBusThread();
DBusServiceDaemon::OnShutdown(exit_code);
client_.reset();
}
private:
// Fuse mount: not owned.
FuseMount* fuse_ = nullptr;
// Fuse user-space client.
std::unique_ptr<FuseBoxClient> client_;
};
int Run(char** mountpoint, fuse_chan* chan) {
LOG(INFO) << base::StringPrintf("fusebox %s [%d]", *mountpoint, getpid());
FuseMount fuse = FuseMount(mountpoint, chan);
auto daemon = FuseBoxDaemon(&fuse);
return daemon.Run();
}
} // namespace fusebox
int main(int argc, char** argv) {
CommandLine::Init(argc, argv);
SetupLogging();
if (CommandLine::ForCurrentProcess()->HasSwitch("fake"))
g_use_fake_file_system = true;
fuse_args args = FUSE_ARGS_INIT(argc, argv);
char* mountpoint = nullptr;
if (fuse_parse_cmdline(&args, &mountpoint, nullptr, nullptr) == -1) {
PLOG(ERROR) << "fuse_parse_cmdline() failed";
return EX_USAGE;
}
if (!mountpoint) {
LOG(ERROR) << "fuse_parse_cmdline() mountpoint expected";
return ENODEV;
}
fuse_chan* chan = fuse_mount(mountpoint, &args);
if (!chan) {
PLOG(ERROR) << "fuse_mount() [" << mountpoint << "] failed";
return ENODEV;
}
int exit_code = fusebox::Run(&mountpoint, chan);
if (!mountpoint) { // Kernel removed the FUSE mountpoint: umount(8).
exit_code = ENODEV;
} else {
fuse_unmount(mountpoint, nullptr);
}
errno = exit_code;
LOG_IF(ERROR, exit_code) << "fusebox exiting";
fuse_opt_free_args(&args);
return exit_code;
}