blob: e015c29e45e95ea5864136d3372eabdca4203c18 [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 "minios/utils.h"
#include <cstdio>
#include <tuple>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_split.h>
#include "minios/minios.h"
#include "minios/process_manager.h"
namespace minios {
std::tuple<bool, std::string> ReadFileContentWithinRange(
const base::FilePath& file_path,
int64_t start_offset,
int64_t end_offset,
int max_columns) {
base::File f(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!f.IsValid()) {
PLOG(ERROR) << "Failed to open file " << file_path.value();
return {false, {}};
}
if (f.Seek(base::File::Whence::FROM_BEGIN, start_offset) != start_offset) {
PLOG(ERROR) << "Failed to seek file " << file_path.value() << " at offset "
<< start_offset;
return {false, {}};
}
int64_t bytes_to_read = end_offset - start_offset;
std::string content;
content.reserve(bytes_to_read);
int current_col = 0;
while (bytes_to_read-- > 0) {
char c;
switch (f.ReadAtCurrentPos(&c, 1)) {
case -1:
PLOG(ERROR) << "Failed to read file " << file_path.value();
return {false, {}};
case 0:
// Equivalent of EOF.
return {true, content};
default:
break;
}
if (c == '\n') {
if (content.empty() || content.back() != '\n')
content.push_back(c);
current_col = 0;
continue;
}
if (current_col < max_columns) {
content.push_back(c);
if (++current_col >= max_columns) {
content.push_back('\n');
current_col = 0;
}
}
}
return {true, content};
}
std::tuple<bool, std::string, int64_t> ReadFileContent(
const base::FilePath& file_path,
int64_t offset,
int num_lines,
int num_cols) {
base::File f(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!f.IsValid())
return {false, {}, 0};
if (f.Seek(base::File::Whence::FROM_BEGIN, offset) == -1)
return {false, {}, 0};
char c;
std::string content;
content.reserve(num_lines * num_cols);
int64_t bytes_read = 0;
int current_line = 0, current_col = 0, read_buffer_lines = 0;
while (f.ReadAtCurrentPos(&c, 1) > 0 && read_buffer_lines < num_lines) {
++bytes_read;
if (c == '\n') {
// Skip double newlining.
if (content.back() != '\n') {
content.push_back(c);
++read_buffer_lines;
}
current_col = 0;
++current_line;
continue;
}
if (current_col < num_cols) {
content.push_back(c);
if (++current_col >= num_cols) {
content.push_back('\n');
current_col = 0;
++read_buffer_lines;
}
}
}
return {true, content, bytes_read};
}
bool GetCrosRegionData(ProcessManagerInterface* process_manager,
std::string key,
std::string* value) {
int exit_code = 0;
std::string error, xkb_keyboard;
// Get the first item in the keyboard list for a given region.
if (!process_manager->RunCommandWithOutput(
{"/usr/bin/cros_region_data", "-s", key}, &exit_code, value,
&error) ||
exit_code) {
LOG(ERROR) << "Could not get " << key << " region data. Exit code "
<< exit_code << " with error " << error;
*value = "";
return false;
}
return true;
}
bool TriggerShutdown() {
ProcessManager process_manager;
if (!process_manager.RunCommand({"/sbin/poweroff", "-f"},
ProcessManager::IORedirection{
.input = minios::kDebugConsole,
.output = minios::kDebugConsole,
})) {
LOG(ERROR) << "Could not trigger shutdown";
return false;
}
LOG(INFO) << "Shutdown requested.";
return true;
}
std::string GetKeyboardLayout(ProcessManagerInterface* process_manager) {
std::string keyboard_layout;
if (!GetCrosRegionData(process_manager, "keyboards", &keyboard_layout)) {
LOG(WARNING) << "Could not get region data. Defaulting to 'us'.";
return "us";
}
// Get the country code from the full keyboard string (i.e xkb:us::eng).
const auto& keyboard_parts = base::SplitString(
keyboard_layout, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (keyboard_parts.size() < 2 || keyboard_parts[1].size() < 2) {
LOG(WARNING) << "Could not get country code from " << keyboard_layout
<< " Defaulting to 'us'.";
return "us";
}
return keyboard_parts[1];
}
} // namespace minios