blob: 1c854ee602ad7dd49d36bda4751dadff2bd130c6 [file] [log] [blame] [edit]
// Copyright 2012 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>
#include <base/command_line.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <brillo/daemons/dbus_daemon.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include <chromeos/dbus/fbpreprocessor/dbus-constants.h>
#include <chromeos/dbus/service_constants.h>
#include <chromeos/libminijail.h>
#include <chromeos/scoped_minijail.h>
#include <memory>
#include "debugd/src/debugd_dbus_adaptor.h"
namespace {
// Minijail APIs tend to return the errno value directly, so pull that out and
// then return success based on it.
static inline bool mj_call(int ret) {
errno = -ret;
return errno == 0;
}
// @brief Enter a VFS namespace.
//
// We don't want anyone other than our descendants to see our tmpfs.
void enter_vfs_namespace() {
ScopedMinijail j(minijail_new());
// Create a minimalistic mount namespace with just the bare minimum required.
minijail_namespace_vfs(j.get());
PCHECK(mj_call(minijail_enter_pivot_root(j.get(), "/mnt/empty")));
PCHECK(mj_call(minijail_bind(j.get(), "/", "/", 0)));
PCHECK(mj_call(minijail_mount_with_data(j.get(), "none", "/proc", "proc",
MS_NOSUID | MS_NOEXEC | MS_NODEV,
nullptr)));
PCHECK(mj_call(minijail_bind(j.get(), "/var", "/var", 1)));
minijail_remount_mode(j.get(), MS_SLAVE);
PCHECK(mj_call(minijail_mount_with_data(j.get(), "tmpfs", "/run", "tmpfs",
MS_NOSUID | MS_NOEXEC | MS_NODEV,
nullptr)));
PCHECK(mj_call(minijail_mount(j.get(), "/run/daemon-store/debugd",
"/run/daemon-store/debugd", "none",
MS_BIND | MS_REC)));
// Mount /run/daemon-store/fbpreprocessord for debugd to be able to access
// and process binary logs to attach with feedback reports. fbpreprocessord
// isn't installed on all boards, only attempt to mount the daemon-store if it
// actually exists.
if (base::PathExists(base::FilePath(fbpreprocessor::kDaemonStorageRoot))) {
PCHECK(mj_call(minijail_mount(j.get(), fbpreprocessor::kDaemonStorageRoot,
fbpreprocessor::kDaemonStorageRoot, "none",
MS_BIND | MS_REC)));
}
// Mount /run/debugd for a shared place for runtime data.
PCHECK(mj_call(minijail_bind(j.get(), "/run/debugd", "/run/debugd", 1)));
// Mount /run/dbus to be able to communicate with D-Bus.
PCHECK(mj_call(minijail_bind(j.get(), "/run/dbus", "/run/dbus", 0)));
// Mount /tmp, /run/cups, and /run/ippusb to be able to communicate with CUPS.
// /tmp must be at least 3 * kernel partition size plus a little extra. This
// is required by make_dev_ssd.sh, which is called from debugd through
// dev_features_rootfs_verification.
//
// The script reads out the old kernel partition as a blob, repacks it (which
// often leads to a smaller blob), then copies the old blob to a new blob and
// overwrites the repacked kernel onto the new blob.
minijail_mount_tmp_size(j.get(), 100 * 1024 * 1024);
PCHECK(mj_call(minijail_bind(j.get(), "/run/cups", "/run/cups", 0)));
// Mount /run/ippusb to be able to communicate with CUPS.
PCHECK(mj_call(minijail_bind(j.get(), "/run/ippusb", "/run/ippusb", 0)));
// Mount writable debug directories for cups, ippusb, and lorgnette for use
// with printscan_tool.
PCHECK(
mj_call(minijail_bind(j.get(), "/run/cups/debug", "/run/cups/debug", 1)));
PCHECK(mj_call(
minijail_bind(j.get(), "/run/ippusb/debug", "/run/ippusb/debug", 1)));
PCHECK(mj_call(minijail_bind(j.get(), "/run/lorgnette/debug",
"/run/lorgnette/debug", 1)));
// In case we start before avahi-daemon, make sure the path exists.
mkdir("/var/run/avahi-daemon", 0755);
// Mount /run/avahi-daemon in order to perform mdns name resolution.
PCHECK(mj_call(
minijail_bind(j.get(), "/run/avahi-daemon", "/run/avahi-daemon", 0)));
// Since shill provides network resolution settings, bind mount it.
// In case we start before shill, make sure the path exists.
// TODO(259354228): Remove once resolv.conf migration to dns-proxy is done.
mkdir("/run/shill", 0755);
PCHECK(mj_call(minijail_bind(j.get(), "/run/shill", "/run/shill", 0)));
// Since dns-proxy provides network resolution settings, bind mount it.
// Path is expected to always exists before as it is created through
// tmpfiles.d.
PCHECK(
mj_call(minijail_bind(j.get(), "/run/dns-proxy", "/run/dns-proxy", 0)));
// We need to write to VPD (and its cache files in /run/vpd) to modify
// RLZ-related keys.
PCHECK(mj_call(minijail_bind(j.get(), "/run/vpd", "/run/vpd", 1)));
// Bind mount /run/lockbox and /var/lib/devicesettings to be able to read
// policy files and check device policies.
// In case we start before, make sure the path exists.
mkdir("/run/lockbox", 0755);
PCHECK(mj_call(minijail_bind(j.get(), "/run/lockbox", "/run/lockbox", 0)));
// In case we start before, make sure the path exists.
mkdir("/var/lib/devicesettings", 0755);
PCHECK(mj_call(minijail_bind(j.get(), "/var/lib/devicesettings",
"/var/lib/devicesettings", 0)));
// Mount /dev to be able to inspect devices.
PCHECK(mj_call(minijail_mount_with_data(j.get(), "/dev", "/dev", "bind",
MS_BIND | MS_REC, nullptr)));
// Mount /sys to access some logs.
PCHECK(mj_call(minijail_mount_with_data(j.get(), "/sys", "/sys", "bind",
MS_BIND | MS_REC, nullptr)));
// Mount /run/chromeos-config/v1 to access chromeos-config.
// /run/chromeos-config/v1 is not consistently available on devices in the
// field, see b/256739303. Make this failure non-fatal.
if (!mj_call(minijail_bind(j.get(), "/run/chromeos-config/v1",
"/run/chromeos-config/v1", 0))) {
PLOG(ERROR) << "Could not bind mount /run/chromeos-config/v1";
}
// Mount /run/lock so that lock file for crossystem is shared.
PCHECK(mj_call(minijail_bind(j.get(), "/run/lock", "/run/lock", 1)));
// Mount /run/touch-updater to access touch firmware version information.
// /run/touch-updater is not available in VM's since they don't have internal
// touch devices. Make the failure non-fatal.
if (!mj_call(minijail_bind(j.get(), "/run/touch-updater",
"/run/touch-updater", 0))) {
PLOG(ERROR) << "Could not bind mount /run/touch-updater";
}
minijail_enter(j.get());
}
class Daemon : public brillo::DBusServiceDaemon {
public:
explicit Daemon(const bool perf_logging)
: DBusServiceDaemon(debugd::kDebugdServiceName),
perf_logging_(perf_logging) {}
Daemon(const Daemon&) = delete;
Daemon& operator=(const Daemon&) = delete;
protected:
void RegisterDBusObjectsAsync(
brillo::dbus_utils::AsyncEventSequencer* sequencer) override {
adaptor_ = std::make_unique<debugd::DebugdDBusAdaptor>(bus_, perf_logging_);
adaptor_->RegisterAsync(
sequencer->GetHandler("RegisterAsync() failed.", true));
}
private:
std::unique_ptr<debugd::DebugdDBusAdaptor> adaptor_;
bool perf_logging_;
};
} // namespace
int main(int argc, char* argv[]) {
DEFINE_bool(perf_logging, false,
"Record and locally log the performance of all LogTool sub-tasks "
"within the feedback log collection function.")
brillo::FlagHelper::Init(argc, argv, "CrOS debug daemon");
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderrIfTty);
enter_vfs_namespace();
Daemon(FLAGS_perf_logging).Run();
return 0;
}