• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android-base/logging.h>
23 #include <android-base/strings.h>
24 #include <netinet/in.h>
25 #include "common/libs/fs/shared_select.h"
26 #include "host/libs/config/cuttlefish_config.h"
27 
28 using cuttlefish::SharedFD;
29 
30 namespace {
31 static const std::map<std::string, std::string> kInformationalPatterns = {
32     {"U-Boot ", "GUEST_UBOOT_VERSION: "},
33     {"] Linux version ", "GUEST_KERNEL_VERSION: "},
34     {"GUEST_BUILD_FINGERPRINT: ", "GUEST_BUILD_FINGERPRINT: "},
35 };
36 
37 static const std::map<std::string, monitor::Event> kStageToEventMap = {
38     {cuttlefish::kBootStartedMessage, monitor::Event::BootStarted},
39     {cuttlefish::kBootCompletedMessage, monitor::Event::BootCompleted},
40     {cuttlefish::kBootFailedMessage, monitor::Event::BootFailed},
41     {cuttlefish::kMobileNetworkConnectedMessage,
42      monitor::Event::MobileNetworkConnected},
43     {cuttlefish::kWifiConnectedMessage, monitor::Event::WifiNetworkConnected},
44     {cuttlefish::kEthernetConnectedMessage, monitor::Event::EthernetNetworkConnected},
45     // TODO(b/131864854): Replace this with a string less likely to change
46     {"init: starting service 'adbd'...", monitor::Event::AdbdStarted},
47     {cuttlefish::kScreenChangedMessage, monitor::Event::ScreenChanged},
48 };
49 
ProcessSubscriptions(Json::Value message,std::vector<monitor::EventCallback> * subscribers)50 void ProcessSubscriptions(
51     Json::Value message,
52     std::vector<monitor::EventCallback>* subscribers) {
53   auto active_subscription_count = subscribers->size();
54   std::size_t idx = 0;
55   while (idx < active_subscription_count) {
56     // Call the callback
57     auto action = (*subscribers)[idx](message);
58     if (action == monitor::SubscriptionAction::ContinueSubscription) {
59       ++idx;
60     } else {
61       // Cancel the subscription by swaping it with the last active subscription
62       // and decreasing the active subscription count
63       --active_subscription_count;
64       std::swap((*subscribers)[idx], (*subscribers)[active_subscription_count]);
65     }
66   }
67   // Keep only the active subscriptions
68   subscribers->resize(active_subscription_count);
69 }
70 }  // namespace
71 
72 namespace monitor {
KernelLogServer(cuttlefish::SharedFD pipe_fd,const std::string & log_name,bool deprecated_boot_completed)73 KernelLogServer::KernelLogServer(cuttlefish::SharedFD pipe_fd,
74                                  const std::string& log_name,
75                                  bool deprecated_boot_completed)
76     : pipe_fd_(pipe_fd),
77       log_fd_(cuttlefish::SharedFD::Open(log_name.c_str(), O_CREAT | O_RDWR | O_APPEND, 0666)),
78       deprecated_boot_completed_(deprecated_boot_completed) {}
79 
BeforeSelect(cuttlefish::SharedFDSet * fd_read) const80 void KernelLogServer::BeforeSelect(cuttlefish::SharedFDSet* fd_read) const {
81   fd_read->Set(pipe_fd_);
82 }
83 
AfterSelect(const cuttlefish::SharedFDSet & fd_read)84 void KernelLogServer::AfterSelect(const cuttlefish::SharedFDSet& fd_read) {
85   if (fd_read.IsSet(pipe_fd_)) {
86     HandleIncomingMessage();
87   }
88 }
89 
SubscribeToEvents(monitor::EventCallback callback)90 void KernelLogServer::SubscribeToEvents(monitor::EventCallback callback) {
91   subscribers_.push_back(callback);
92 }
93 
HandleIncomingMessage()94 bool KernelLogServer::HandleIncomingMessage() {
95   const size_t buf_len = 256;
96   char buf[buf_len];
97   ssize_t ret = pipe_fd_->Read(buf, buf_len);
98   if (ret < 0) {
99     LOG(ERROR) << "Could not read kernel logs: " << pipe_fd_->StrError();
100     return false;
101   }
102   if (ret == 0) return false;
103   // Write the log to a file
104   if (log_fd_->Write(buf, ret) < 0) {
105     LOG(ERROR) << "Could not write kernel log to file: " << log_fd_->StrError();
106     return false;
107   }
108 
109   // Detect VIRTUAL_DEVICE_BOOT_*
110   for (ssize_t i=0; i<ret; i++) {
111     if ('\n' == buf[i]) {
112       for (auto& info_kv : kInformationalPatterns) {
113         auto& match = info_kv.first;
114         auto& prefix = info_kv.second;
115         auto pos = line_.find(match);
116         if (std::string::npos != pos) {
117           LOG(INFO) << prefix << line_.substr(pos + match.size());
118         }
119       }
120       for (auto& stage_kv : kStageToEventMap) {
121         auto& stage = stage_kv.first;
122         auto event = stage_kv.second;
123         auto pos = line_.find(stage);
124         if (std::string::npos != pos) {
125           // Log the stage
126           LOG(INFO) << stage;
127 
128           Json::Value message;
129           message["event"] = event;
130           Json::Value metadata;
131           // Expect space-separated key=value pairs in the log message.
132           const auto& fields = android::base::Split(
133               line_.substr(pos + stage.size()), " ");
134           for (std::string field : fields) {
135             field = android::base::Trim(field);
136             if (field.empty()) {
137               // Expected; android::base::Split() always returns at least
138               // one (possibly empty) string.
139               LOG(DEBUG) << "Empty field for line: " << line_;
140               continue;
141             }
142             const auto& keyvalue = android::base::Split(field, "=");
143             if (keyvalue.size() != 2) {
144               LOG(WARNING) << "Field is not in key=value format: " << field;
145               continue;
146             }
147             metadata[keyvalue[0]] = keyvalue[1];
148           }
149           message["metadata"] = metadata;
150           ProcessSubscriptions(message, &subscribers_);
151 
152           //TODO(b/69417553) Remove this when our clients have transitioned to the
153           // new boot completed
154           if (deprecated_boot_completed_) {
155             // Write to host kernel log
156             FILE* log = popen("/usr/bin/sudo /usr/bin/tee /dev/kmsg", "w");
157             fprintf(log, "%s\n", stage.c_str());
158             fclose(log);
159           }
160         }
161       }
162       line_.clear();
163     }
164     line_.append(1, buf[i]);
165   }
166 
167   return true;
168 }
169 
170 }  // namespace monitor
171