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