Home | History | Annotate | Download | only in vsock_logcat
      1 /*
      2  * Copyright (C) 2019 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "vsock_logcat"
     18 
     19 #include <string.h>
     20 
     21 #include <errno.h>
     22 #include <sys/stat.h>
     23 #include <sys/types.h>
     24 
     25 #include <fstream>
     26 #include <sstream>
     27 #include <string>
     28 
     29 #include <cutils/properties.h>
     30 #include <gflags/gflags.h>
     31 #include <glog/logging.h>
     32 
     33 #include "common/libs/fs/shared_fd.h"
     34 #include "common/libs/utils/files.h"
     35 #include "common/libs/utils/subprocess.h"
     36 
     37 DEFINE_uint32(port, property_get_int32("ro.boot.vsock_logcat_port", 0),
     38               "VSOCK port to send logcat output to");
     39 DEFINE_uint32(cid, 2, "VSOCK CID to send logcat output to");
     40 DEFINE_string(pipe_name, "/dev/cf_logcat_pipe",
     41               "The path for the named pipe logcat will write to");
     42 
     43 namespace {
     44 
     45 constexpr char kLogcatExitMsg[] = "\nDetected exit of logcat process\n\n";
     46 
     47 class ServiceStatus {
     48  public:
     49   static const char* kServiceStatusProperty;
     50   static const char* kStatusStarted;
     51   static const char* kStatusFailed;
     52 
     53   ServiceStatus() {
     54     // This can fail if the property isn't set (the first time it runs), so
     55     // ignore the result.
     56     property_get(kServiceStatusProperty, status_, kStatusStarted);
     57   }
     58 
     59   bool Set(const char* status) {
     60     auto ret = property_set(kServiceStatusProperty, status);
     61 
     62     if (ret == 0) {
     63       strcpy(status_, status);
     64       return true;
     65     }
     66     return false;
     67   }
     68 
     69   const char* Get() { return status_; }
     70 
     71  private:
     72   char status_[PROP_VALUE_MAX];
     73 };
     74 
     75 const char* ServiceStatus::kServiceStatusProperty = "vendor.vsock_logcat_status";
     76 const char* ServiceStatus::kStatusStarted = "started";
     77 const char* ServiceStatus::kStatusFailed = "failed";
     78 
     79 void LogFailed(const std::string& msg, ServiceStatus* status) {
     80   // Only log if status is not failed, ensuring it logs once per fail.
     81   if (strcmp(status->Get(), ServiceStatus::kStatusFailed) != 0) {
     82     LOG(ERROR) << msg;
     83     std::ofstream kmsg;
     84     kmsg.open("/dev/kmsg");
     85     kmsg << LOG_TAG << ": " << msg;
     86     kmsg << "";
     87     kmsg.close();
     88   }
     89   auto ret = status->Set(ServiceStatus::kStatusFailed);
     90   if (!ret) {
     91     LOG(ERROR) << "Unable to set value of property: "
     92                << ServiceStatus::kServiceStatusProperty;
     93   }
     94 }
     95 }  // namespace
     96 
     97 int main(int argc, char** argv) {
     98   gflags::ParseCommandLineFlags(&argc, &argv, true);
     99 
    100   CHECK(FLAGS_port != 0) << "Port flag is required";
    101 
    102   ServiceStatus status;
    103 
    104   auto log_fd = cvd::SharedFD::VsockClient(FLAGS_cid, FLAGS_port, SOCK_STREAM);
    105   if (!log_fd->IsOpen()) {
    106     std::ostringstream msg;
    107     msg << "Unable to connect to vsock:" << FLAGS_cid << ":" << FLAGS_port
    108         << ": " << log_fd->StrError();
    109     LogFailed(msg.str(), &status);
    110     return 1;
    111   }
    112   auto ret = status.Set(ServiceStatus::kStatusStarted);
    113   if (!ret) {
    114     LOG(ERROR) << "Unable to set value of property: "
    115                << ServiceStatus::kServiceStatusProperty;
    116   }
    117 
    118   if (cvd::FileExists(FLAGS_pipe_name)) {
    119     LOG(WARNING) << "The file " << FLAGS_pipe_name << " already exists. Deleting...";
    120     cvd::RemoveFile(FLAGS_pipe_name);
    121   }
    122   auto pipe = mkfifo(FLAGS_pipe_name.c_str(), 0600);
    123   if (pipe != 0) {
    124     LOG(FATAL) << "unable to create pipe: " << strerror(errno);
    125   }
    126   property_set("vendor.ser.cf-logcat", FLAGS_pipe_name.c_str());
    127   while (1) {
    128     auto conn = cvd::SharedFD::Open(FLAGS_pipe_name.c_str(), O_RDONLY);
    129     while (conn->IsOpen()) {
    130       char buff[4096];
    131       auto read = conn->Read(buff, sizeof(buff));
    132       if (read) {
    133         log_fd->Write(buff, read);
    134       } else {
    135         conn->Close();
    136       }
    137     }
    138     log_fd->Write(kLogcatExitMsg, sizeof(kLogcatExitMsg) - 1);
    139   }
    140 }
    141