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