blob: 0e4c63cc927ffefe94a74ade607d4342b6d45b9c [file] [log] [blame]
// Copyright 2015 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 <string>
#include <fcntl.h>
#include <poll.h>
#include <sysexits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <brillo/daemons/daemon.h>
#include <base/files/file_util.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/message_loop/message_loop.h>
#include <base/strings/string_util.h>
#include <brillo/flag_helper.h>
namespace {
const char kDefaultDeviceLogFile[] = "/sys/kernel/debug/cros_ec/console_log";
const char kDefaultLogDirectory[] = "/var/log/";
const char kCurrentLogExt[] = ".log";
const char kPreviousLogExt[] = ".previous";
const int kMaxCurrentLogSize = 10 * 1024 * 1024;
class TimberSlide : public brillo::Daemon,
public base::MessageLoopForIO::Watcher {
public:
TimberSlide(const std::string& ec_type,
base::File device_file,
const base::FilePath& log_dir)
: device_file_(std::move(device_file)),
fd_watcher_(FROM_HERE),
total_size_(0) {
current_log_ = log_dir.Append(ec_type + kCurrentLogExt);
previous_log_ = log_dir.Append(ec_type + kPreviousLogExt);
}
private:
int OnInit() override {
LOG(INFO) << "Starting timberslide daemon";
int ret = brillo::Daemon::OnInit();
if (ret != EX_OK)
return ret;
RotateLogs(previous_log_, current_log_);
CHECK(base::MessageLoopForIO::current()->WatchFileDescriptor(
device_file_.GetPlatformFile(), true,
base::MessageLoopForIO::WATCH_READ, &fd_watcher_, this));
return EX_OK;
}
void OnFileCanWriteWithoutBlocking(int fd) override {
LOG(FATAL) << "Unexpected call to write event handler";
}
void OnFileCanReadWithoutBlocking(int fd) override {
char buffer[4096];
int ret;
CHECK_EQ(fd, device_file_.GetPlatformFile());
ret = TEMP_FAILURE_RETRY(
device_file_.ReadAtCurrentPosNoBestEffort(buffer, sizeof(buffer)));
if (ret == 0)
return;
if (ret < 0) {
PLOG(FATAL) << "Read error";
return;
}
if (!base::AppendToFile(current_log_, buffer, ret)) {
PLOG(FATAL) << "Could not append log file";
return;
}
total_size_ += ret;
if (total_size_ >= kMaxCurrentLogSize) {
RotateLogs(previous_log_, current_log_);
total_size_ = 0;
}
}
void RotateLogs(const base::FilePath& previous_log,
const base::FilePath& current_log) {
CHECK(base::DeleteFile(previous_log, /* recursive = */ false));
if (base::PathExists(current_log))
CHECK(base::Move(current_log, previous_log));
base::WriteFile(current_log, "", 0);
}
base::File device_file_;
base::FilePath current_log_;
base::FilePath previous_log_;
base::MessageLoopForIO::FileDescriptorWatcher fd_watcher_;
int total_size_;
};
} // namespace
int main(int argc, char* argv[]) {
DEFINE_string(device_log, kDefaultDeviceLogFile,
"File where the recent EC logs are posted to.");
DEFINE_string(log_directory, kDefaultLogDirectory,
"Directory where the output logs should be.");
brillo::FlagHelper::Init(argc, argv,
"timberslide concatenates EC logs for use in debugging.");
base::FilePath path(FLAGS_device_log);
base::File device_file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!device_file.IsValid()) {
LOG(ERROR) << "Error opening " << FLAGS_device_log << ": "
<< base::File::ErrorToString(device_file.error_details());
return EX_UNAVAILABLE;
}
std::string ec_type = path.DirName().BaseName().value();
TimberSlide ts(ec_type, std::move(device_file),
(base::FilePath(FLAGS_log_directory)));
return ts.Run();
}