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 <netinet/in.h>
24 #include "common/libs/fs/shared_select.h"
25 #include "host/libs/config/cuttlefish_config.h"
26 
27 using cuttlefish::SharedFD;
28 
29 namespace {
30 static const std::map<std::string, std::string> kInformationalPatterns = {
31     {"] Linux version ", "GUEST_KERNEL_VERSION: "},
32     {"GUEST_BUILD_FINGERPRINT: ", "GUEST_BUILD_FINGERPRINT: "},
33 };
34 
35 static const std::map<std::string, monitor::BootEvent> kStageToEventMap = {
36     {cuttlefish::kBootStartedMessage, monitor::BootEvent::BootStarted},
37     {cuttlefish::kBootCompletedMessage, monitor::BootEvent::BootCompleted},
38     {cuttlefish::kBootFailedMessage, monitor::BootEvent::BootFailed},
39     {cuttlefish::kMobileNetworkConnectedMessage,
40      monitor::BootEvent::MobileNetworkConnected},
41     {cuttlefish::kWifiConnectedMessage, 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(cuttlefish::SharedFD pipe_fd,const std::string & log_name,bool deprecated_boot_completed)69 KernelLogServer::KernelLogServer(cuttlefish::SharedFD pipe_fd,
70                                  const std::string& log_name,
71                                  bool deprecated_boot_completed)
72     : pipe_fd_(pipe_fd),
73       log_fd_(cuttlefish::SharedFD::Open(log_name.c_str(), O_CREAT | O_RDWR, 0666)),
74       deprecated_boot_completed_(deprecated_boot_completed) {}
75 
BeforeSelect(cuttlefish::SharedFDSet * fd_read) const76 void KernelLogServer::BeforeSelect(cuttlefish::SharedFDSet* fd_read) const {
77   fd_read->Set(pipe_fd_);
78 }
79 
AfterSelect(const cuttlefish::SharedFDSet & fd_read)80 void KernelLogServer::AfterSelect(const cuttlefish::SharedFDSet& fd_read) {
81   if (fd_read.IsSet(pipe_fd_)) {
82     HandleIncomingMessage();
83   }
84 }
85 
SubscribeToBootEvents(monitor::BootEventCallback callback)86 void KernelLogServer::SubscribeToBootEvents(
87     monitor::BootEventCallback callback) {
88   subscribers_.push_back(callback);
89 }
90 
HandleIncomingMessage()91 bool KernelLogServer::HandleIncomingMessage() {
92   const size_t buf_len = 256;
93   char buf[buf_len];
94   ssize_t ret = pipe_fd_->Read(buf, buf_len);
95   if (ret < 0) {
96     LOG(ERROR) << "Could not read kernel logs: " << pipe_fd_->StrError();
97     return false;
98   }
99   if (ret == 0) return false;
100   // Write the log to a file
101   if (log_fd_->Write(buf, ret) < 0) {
102     LOG(ERROR) << "Could not write kernel log to file: " << log_fd_->StrError();
103     return false;
104   }
105 
106   // Detect VIRTUAL_DEVICE_BOOT_*
107   for (ssize_t i=0; i<ret; i++) {
108     if ('\n' == buf[i]) {
109       for (auto& info_kv : kInformationalPatterns) {
110         auto& match = info_kv.first;
111         auto& prefix = info_kv.second;
112         auto pos = line_.find(match);
113         if (std::string::npos != pos) {
114           LOG(INFO) << prefix << line_.substr(pos + match.size());
115         }
116       }
117       for (auto& stage_kv : kStageToEventMap) {
118         auto& stage = stage_kv.first;
119         auto event = stage_kv.second;
120         if (std::string::npos != line_.find(stage)) {
121           // Log the stage
122           LOG(INFO) << stage;
123           ProcessSubscriptions(event, &subscribers_);
124           //TODO(b/69417553) Remove this when our clients have transitioned to the
125           // new boot completed
126           if (deprecated_boot_completed_) {
127             // Write to host kernel log
128             FILE* log = popen("/usr/bin/sudo /usr/bin/tee /dev/kmsg", "w");
129             fprintf(log, "%s\n", stage.c_str());
130             fclose(log);
131           }
132         }
133       }
134       line_.clear();
135     }
136     line_.append(1, buf[i]);
137   }
138 
139   return true;
140 }
141 
142 }  // namespace monitor
143