• 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 <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