1 /* 2 * Copyright (C) 2017 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 #include "host/libs/monitor/kernel_log_server.h" 18 19 #include <glog/logging.h> 20 #include <netinet/in.h> 21 #include "common/libs/fs/shared_select.h" 22 23 using cvd::SharedFD; 24 25 namespace { 26 static const std::string kVirtualDeviceStages[] = { 27 "VIRTUAL_DEVICE_BOOT_STARTED", 28 "VIRTUAL_DEVICE_BOOT_COMPLETED", 29 "VIRTUAL_DEVICE_BOOT_FAILED", 30 }; 31 } // namespace 32 33 namespace monitor { 34 KernelLogServer::KernelLogServer(const std::string& socket_name, 35 const std::string& log_name, 36 bool deprecated_boot_completed) 37 : name_(socket_name), 38 log_fd_(cvd::SharedFD::Open( 39 log_name.c_str(), O_CREAT | O_RDWR, 0666)), 40 deprecated_boot_completed_(deprecated_boot_completed) {} 41 42 bool KernelLogServer::Init() { return CreateServerSocket(); } 43 44 // Open new listening server socket. 45 // Returns false, if listening socket could not be created. 46 bool KernelLogServer::CreateServerSocket() { 47 LOG(INFO) << "Starting server socket: " << name_; 48 49 server_fd_ = 50 cvd::SharedFD::SocketLocalServer(name_.c_str(), false, SOCK_STREAM, 0666); 51 if (!server_fd_->IsOpen()) { 52 LOG(ERROR) << "Could not create socket: " << server_fd_->StrError(); 53 return false; 54 } 55 return true; 56 } 57 58 void KernelLogServer::BeforeSelect(cvd::SharedFDSet* fd_read) const { 59 if (!client_fd_->IsOpen()) fd_read->Set(server_fd_); 60 if (client_fd_->IsOpen()) fd_read->Set(client_fd_); 61 } 62 63 void KernelLogServer::AfterSelect(const cvd::SharedFDSet& fd_read) { 64 if (fd_read.IsSet(server_fd_)) HandleIncomingConnection(); 65 66 if (client_fd_->IsOpen() && fd_read.IsSet(client_fd_)) { 67 if (!HandleIncomingMessage()) { 68 client_fd_->Close(); 69 } 70 } 71 } 72 73 // Accept new kernel log connection. 74 void KernelLogServer::HandleIncomingConnection() { 75 if (client_fd_->IsOpen()) { 76 LOG(ERROR) << "Client already connected. No longer accepting connection."; 77 return; 78 } 79 80 client_fd_ = SharedFD::Accept(*server_fd_, nullptr, nullptr); 81 if (!client_fd_->IsOpen()) { 82 LOG(ERROR) << "Client connection failed: " << client_fd_->StrError(); 83 return; 84 } 85 if (client_fd_->Fcntl(F_SETFL, O_NONBLOCK) == -1) { 86 LOG(ERROR) << "Client connection refused O_NONBLOCK: " << client_fd_->StrError(); 87 } 88 } 89 90 bool KernelLogServer::HandleIncomingMessage() { 91 const size_t buf_len = 256; 92 char buf[buf_len]; 93 ssize_t ret = client_fd_->Read(buf, buf_len); 94 if (ret < 0) { 95 LOG(ERROR) << "Could not read from QEmu serial port: " << client_fd_->StrError(); 96 return false; 97 } 98 if (ret == 0) return false; 99 // Write the log to a file 100 if (log_fd_->Write(buf, ret) < 0) { 101 LOG(ERROR) << "Could not write kernel log to file: " << log_fd_->StrError(); 102 return false; 103 } 104 105 // Detect VIRTUAL_DEVICE_BOOT_* 106 for (ssize_t i=0; i<ret; i++) { 107 if ('\n' == buf[i]) { 108 for (auto stage : kVirtualDeviceStages) { 109 if (std::string::npos != line_.find(stage)) { 110 LOG(INFO) << stage; 111 //TODO(b/69417553) Remove this when our clients have transitioned to the 112 // new boot completed 113 if (deprecated_boot_completed_) { 114 // Write to host kernel log 115 FILE* log = popen("/usr/bin/sudo /usr/bin/tee /dev/kmsg", "w"); 116 fprintf(log, "%s\n", stage.c_str()); 117 fclose(log); 118 } 119 } 120 } 121 line_.clear(); 122 } 123 line_.append(1, buf[i]); 124 } 125 126 return true; 127 } 128 129 } // namespace monitor 130