blob: 2dd4318dc37fbb6a74371bfa0f976b088fa65cb2 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "vsock_logcat"
#include <string.h>
#include <fstream>
#include <sstream>
#include <string>
#include <cutils/properties.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/subprocess.h"
DEFINE_uint32(port, property_get_int32("ro.boot.vsock_logcat_port", 0),
"VSOCK port to send logcat output to");
DEFINE_uint32(cid, 2, "VSOCK CID to send logcat output to");
namespace {
class ServiceStatus {
public:
static const char* kServiceStatusProperty;
static const char* kStatusStarted;
static const char* kStatusFailed;
ServiceStatus() {
// This can fail if the property isn't set (the first time it runs), so
// ignore the result.
property_get(kServiceStatusProperty, status_, kStatusStarted);
}
bool Set(const char* status) {
auto ret = property_set(kServiceStatusProperty, status);
if (ret == 0) {
strcpy(status_, status);
return true;
}
return false;
}
const char* Get() { return status_; }
private:
char status_[PROP_VALUE_MAX];
};
const char* ServiceStatus::kServiceStatusProperty = "vsock_logcat_status";
const char* ServiceStatus::kStatusStarted = "started";
const char* ServiceStatus::kStatusFailed = "failed";
void LogFailed(const std::string& msg, ServiceStatus* status) {
// Only log if status is not failed, ensuring it logs once per fail.
if (strcmp(status->Get(), ServiceStatus::kStatusFailed) != 0) {
LOG(ERROR) << msg;
std::ofstream kmsg;
kmsg.open("/dev/kmsg");
kmsg << LOG_TAG << ": " << msg;
kmsg << "";
kmsg.close();
}
auto ret = status->Set(ServiceStatus::kStatusFailed);
if (!ret) {
LOG(ERROR) << "Unable to set value of property: "
<< ServiceStatus::kServiceStatusProperty;
}
}
} // namespace
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(FLAGS_port != 0) << "Port flag is required";
ServiceStatus status;
auto log_fd = cvd::SharedFD::VsockClient(FLAGS_cid, FLAGS_port, SOCK_STREAM);
if (!log_fd->IsOpen()) {
std::ostringstream msg;
msg << "Unable to connect to vsock:" << FLAGS_cid << ":" << FLAGS_port
<< ": " << log_fd->StrError();
LogFailed(msg.str(), &status);
return 1;
}
auto ret = status.Set(ServiceStatus::kStatusStarted);
if (!ret) {
LOG(ERROR) << "Unable to set value of property: "
<< ServiceStatus::kServiceStatusProperty;
}
cvd::Command logcat_cmd("/system/bin/logcat");
logcat_cmd.AddParameter("-b");
logcat_cmd.AddParameter("all");
logcat_cmd.AddParameter("-v");
logcat_cmd.AddParameter("threadtime");
logcat_cmd.AddParameter("*:V");
logcat_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut, log_fd);
while (true) {
int wstatus;
logcat_cmd.Start().Wait(&wstatus, 0);
std::ostringstream exit_msg_builder;
exit_msg_builder << std::endl
<< "Logcat process exited with wstatus " << wstatus
<< ", restarting ..." << std::endl
<< std::endl;
auto exit_msg = exit_msg_builder.str();
size_t written = log_fd->Write(exit_msg.c_str(), exit_msg.size());
if (written != exit_msg.size()) {
std::ostringstream ss;
ss << exit_msg << std::endl
<< "Unable to write complete message on socket: "
<< log_fd->StrError();
LogFailed(ss.str(), &status);
return 2;
}
}
}