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/commands/kernel_log_monitor/kernel_log_server.h"
18
19 #include <map>
20 #include <utility>
21
22 #include <glog/logging.h>
23 #include <netinet/in.h>
24 #include "common/libs/fs/shared_select.h"
25
26 using cvd::SharedFD;
27
28 namespace {
29 static const std::map<std::string, std::string> kInformationalPatterns = {
30 {"] Linux version ", "GUEST_KERNEL_VERSION: "},
31 {"GUEST_BUILD_FINGERPRINT: ", "GUEST_BUILD_FINGERPRINT: "},
32 };
33
34 static const std::map<std::string, monitor::BootEvent> kStageToEventMap = {
35 {"VIRTUAL_DEVICE_BOOT_STARTED", monitor::BootEvent::BootStarted},
36 {"VIRTUAL_DEVICE_BOOT_COMPLETED", monitor::BootEvent::BootCompleted},
37 {"VIRTUAL_DEVICE_BOOT_FAILED", monitor::BootEvent::BootFailed},
38 {"VIRTUAL_DEVICE_NETWORK_MOBILE_CONNECTED",
39 monitor::BootEvent::MobileNetworkConnected},
40 {"VIRTUAL_DEVICE_NETWORK_WIFI_CONNECTED",
41 monitor::BootEvent::WifiNetworkConnected},
42 // TODO(b/131864854): Replace this with a string less likely to change
43 {"init: starting service 'adbd'", monitor::BootEvent::AdbdStarted},
44 };
45
ProcessSubscriptions(monitor::BootEvent evt,std::vector<monitor::BootEventCallback> * subscribers)46 void ProcessSubscriptions(
47 monitor::BootEvent evt,
48 std::vector<monitor::BootEventCallback>* subscribers) {
49 auto active_subscription_count = subscribers->size();
50 std::size_t idx = 0;
51 while (idx < active_subscription_count) {
52 // Call the callback
53 auto action = (*subscribers)[idx](evt);
54 if (action == monitor::SubscriptionAction::ContinueSubscription) {
55 ++idx;
56 } else {
57 // Cancel the subscription by swaping it with the last active subscription
58 // and decreasing the active subscription count
59 --active_subscription_count;
60 std::swap((*subscribers)[idx], (*subscribers)[active_subscription_count]);
61 }
62 }
63 // Keep only the active subscriptions
64 subscribers->resize(active_subscription_count);
65 }
66 } // namespace
67
68 namespace monitor {
KernelLogServer(cvd::SharedFD server_socket,const std::string & log_name,bool deprecated_boot_completed)69 KernelLogServer::KernelLogServer(cvd::SharedFD server_socket,
70 const std::string& log_name,
71 bool deprecated_boot_completed)
72 : server_fd_(server_socket),
73 log_fd_(cvd::SharedFD::Open(log_name.c_str(), O_CREAT | O_RDWR, 0666)),
74 deprecated_boot_completed_(deprecated_boot_completed) {}
75
BeforeSelect(cvd::SharedFDSet * fd_read) const76 void KernelLogServer::BeforeSelect(cvd::SharedFDSet* fd_read) const {
77 if (!client_fd_->IsOpen()) fd_read->Set(server_fd_);
78 if (client_fd_->IsOpen()) fd_read->Set(client_fd_);
79 }
80
AfterSelect(const cvd::SharedFDSet & fd_read)81 void KernelLogServer::AfterSelect(const cvd::SharedFDSet& fd_read) {
82 if (fd_read.IsSet(server_fd_)) HandleIncomingConnection();
83
84 if (client_fd_->IsOpen() && fd_read.IsSet(client_fd_)) {
85 if (!HandleIncomingMessage()) {
86 client_fd_->Close();
87 }
88 }
89 }
90
SubscribeToBootEvents(monitor::BootEventCallback callback)91 void KernelLogServer::SubscribeToBootEvents(
92 monitor::BootEventCallback callback) {
93 subscribers_.push_back(callback);
94 }
95
96 // Accept new kernel log connection.
HandleIncomingConnection()97 void KernelLogServer::HandleIncomingConnection() {
98 if (client_fd_->IsOpen()) {
99 LOG(ERROR) << "Client already connected. No longer accepting connection.";
100 return;
101 }
102
103 client_fd_ = SharedFD::Accept(*server_fd_, nullptr, nullptr);
104 if (!client_fd_->IsOpen()) {
105 LOG(ERROR) << "Client connection failed: " << client_fd_->StrError();
106 return;
107 }
108 if (client_fd_->Fcntl(F_SETFL, O_NONBLOCK) == -1) {
109 LOG(ERROR) << "Client connection refused O_NONBLOCK: " << client_fd_->StrError();
110 }
111 }
112
HandleIncomingMessage()113 bool KernelLogServer::HandleIncomingMessage() {
114 const size_t buf_len = 256;
115 char buf[buf_len];
116 ssize_t ret = client_fd_->Read(buf, buf_len);
117 if (ret < 0) {
118 LOG(ERROR) << "Could not read from QEmu serial port: " << client_fd_->StrError();
119 return false;
120 }
121 if (ret == 0) return false;
122 // Write the log to a file
123 if (log_fd_->Write(buf, ret) < 0) {
124 LOG(ERROR) << "Could not write kernel log to file: " << log_fd_->StrError();
125 return false;
126 }
127
128 // Detect VIRTUAL_DEVICE_BOOT_*
129 for (ssize_t i=0; i<ret; i++) {
130 if ('\n' == buf[i]) {
131 for (auto& info_kv : kInformationalPatterns) {
132 auto& match = info_kv.first;
133 auto& prefix = info_kv.second;
134 auto pos = line_.find(match);
135 if (std::string::npos != pos) {
136 LOG(INFO) << prefix << line_.substr(pos + match.size());
137 }
138 }
139 for (auto& stage_kv : kStageToEventMap) {
140 auto& stage = stage_kv.first;
141 auto event = stage_kv.second;
142 if (std::string::npos != line_.find(stage)) {
143 // Log the stage
144 LOG(INFO) << stage;
145 ProcessSubscriptions(event, &subscribers_);
146 //TODO(b/69417553) Remove this when our clients have transitioned to the
147 // new boot completed
148 if (deprecated_boot_completed_) {
149 // Write to host kernel log
150 FILE* log = popen("/usr/bin/sudo /usr/bin/tee /dev/kmsg", "w");
151 fprintf(log, "%s\n", stage.c_str());
152 fclose(log);
153 }
154 }
155 }
156 line_.clear();
157 }
158 line_.append(1, buf[i]);
159 }
160
161 return true;
162 }
163
164 } // namespace monitor
165