blob: ed9b6ae4225a22ac8b5c290352cb0ea21ff563de [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "option.h"
#include <lib/boot-options/word-view.h>
#include <lib/stdcompat/string_view.h>
#include "util.h"
namespace {
constexpr std::string_view kOptPrefix = "userboot";
constexpr std::string_view kRootOpt = "userboot.root";
constexpr std::string_view kNextOpt = "userboot.next";
constexpr std::string_view kShutdownOpt = "userboot.shutdown";
constexpr std::string_view kRebootOpt = "userboot.reboot";
// TODO(joshuaseaton): This should really be defined as a default value of
// `Options::next` and expressed as a std::string_view; however, that can
// sometimes generate a writable data section. While such sections are
// prohibited, we apply the default within ParseCmdline() below and keep this
// value as a char array.
constexpr const char kNextDefault[] = "bin/component_manager+--boot";
struct KeyAndValue {
std::string_view key, value;
};
KeyAndValue SplitOpt(std::string_view opt) {
std::string_view key = opt.substr(0, opt.find_first_of('='));
opt.remove_prefix(std::min(opt.size(), key.size() + 1));
return {key, opt};
}
} // namespace
void ParseCmdline(const zx::debuglog& log, std::string_view cmdline, Options& opts) {
if (opts.next.empty()) {
opts.next = kNextDefault;
}
for (std::string_view opt : WordView(cmdline)) {
if (!cpp20::starts_with(opt, kOptPrefix)) {
continue;
}
auto [key, value] = SplitOpt(opt);
if (key == kNextOpt) {
opts.next = value;
} else if (key == kRootOpt) {
// Normalize away a trailing '/', if present.
if (!value.empty() && value.back() == '/') {
value.remove_suffix(1);
}
opts.root = value;
} else if (key == kShutdownOpt) {
opts.epilogue = Epilogue::kPowerOffAfterChildExit;
} else if (key == kRebootOpt) {
opts.epilogue = Epilogue::kRebootAfterChildExit;
} else {
printl(log, "WARNING: unknown option %.*s ignored\n", static_cast<int>(key.size()),
key.data());
continue;
}
printl(log, "OPTION %.*s%s%.*s\n", static_cast<int>(key.size()), key.data(),
value.empty() ? "" : "=", static_cast<int>(value.size()), value.data());
}
if (!opts.root.empty() && opts.root.front() == '/') {
fail(log, "`userboot.root` (\"%.*s\" must not begin with a \'/\'",
static_cast<int>(opts.root.size()), opts.root.data());
}
}