| // 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; | |
| } |