• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2020 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 #include <signal.h>
17 #include <sys/signalfd.h>
18 
19 #include <android-base/logging.h>
20 #include <android-base/strings.h>
21 #include <gflags/gflags.h>
22 
23 #include "common/libs/fs/shared_fd.h"
24 #include "common/libs/utils/tee_logging.h"
25 #include "host/libs/config/cuttlefish_config.h"
26 
27 DEFINE_string(process_name, "", "The process to credit log messages to");
28 DEFINE_int32(log_fd_in, -1, "The file descriptor to read logs from.");
29 
main(int argc,char ** argv)30 int main(int argc, char** argv) {
31   ::android::base::InitLogging(argv, android::base::StderrLogger);
32   google::ParseCommandLineFlags(&argc, &argv, /* remove_flags */ true);
33 
34   CHECK(FLAGS_log_fd_in >= 0) << "-log_fd_in is required";
35 
36   auto config = cuttlefish::CuttlefishConfig::Get();
37 
38   CHECK(config) << "Could not open cuttlefish config";
39 
40   auto instance = config->ForDefaultInstance();
41 
42   if (instance.run_as_daemon()) {
43     android::base::SetLogger(
44         cuttlefish::LogToFiles({instance.launcher_log_path()}));
45   } else {
46     android::base::SetLogger(
47         cuttlefish::LogToStderrAndFiles({instance.launcher_log_path()}));
48   }
49 
50   auto log_fd = cuttlefish::SharedFD::Dup(FLAGS_log_fd_in);
51   CHECK(log_fd->IsOpen()) << "Failed to dup log_fd_in: " <<  log_fd->StrError();
52   close(FLAGS_log_fd_in);
53 
54   if (FLAGS_process_name.size() > 0) {
55     android::base::SetDefaultTag(FLAGS_process_name);
56   }
57 
58   // mask SIGINT and handle it using signalfd
59   sigset_t mask;
60   sigemptyset(&mask);
61   sigaddset(&mask, SIGINT);
62   CHECK(sigprocmask(SIG_BLOCK, &mask, NULL) == 0)
63       << "sigprocmask failed: " << strerror(errno);
64   int sfd = signalfd(-1, &mask, 0);
65   CHECK(sfd >= 0) << "signalfd failed: " << strerror(errno);
66   auto int_fd = cuttlefish::SharedFD::Dup(sfd);
67   close(sfd);
68 
69   auto poll_fds = std::vector<cuttlefish::PollSharedFd>{
70       cuttlefish::PollSharedFd{
71           .fd = log_fd,
72           .events = POLL_IN,
73           .revents = 0,
74       },
75       cuttlefish::PollSharedFd{
76           .fd = int_fd,
77           .events = POLL_IN,
78           .revents = 0,
79       },
80   };
81 
82   LOG(DEBUG) << "Starting to read from process " << FLAGS_process_name;
83 
84   char buf[1 << 16];
85   ssize_t chars_read = 0;
86   for (;;) {
87     // We can assume all writers to `log_fd` have completed before a SIGINT is
88     // sent, but we need to make sure we've actually read all the data before
89     // exiting. So, keep reading from `log_fd` until both (1) we get SIGINT and
90     // (2) `log_fd` is empty (but not necessarily EOF).
91     //
92     // This could be simpler if all the writers would close their FDs when they
93     // are finished. Then, we could just read until EOF. However that would
94     // require more work elsewhere in cuttlefish.
95     CHECK(cuttlefish::SharedFD::Poll(poll_fds, /*timeout=*/-1) >= 0)
96         << "poll failed: " << strerror(errno);
97     if (poll_fds[0].revents) {
98       chars_read = log_fd->Read(buf, sizeof(buf));
99       if (chars_read < 0) {
100         LOG(DEBUG) << "Failed to read from process " << FLAGS_process_name
101                    << ": " << log_fd->StrError();
102         break;
103       }
104       if (chars_read == 0) {
105         break;
106       }
107       auto trimmed = android::base::Trim(std::string_view(buf, chars_read));
108       // Newlines inside `trimmed` are handled by the android logging code.
109       // These checks attempt to determine the log severity coming from crosvm.
110       // There is no guarantee of success all the time since log line boundaries
111       // could be out sync with the reads, but that's ok.
112       //
113       // TODO(b/270424669): These checks are wrong, the format is
114       // "[<timestamp> ERROR". Maybe just stop bothering and send
115       // everything to LOG(DEBUG).
116       if (android::base::StartsWith(trimmed, "[INFO")) {
117         LOG(DEBUG) << trimmed;
118       } else if (android::base::StartsWith(trimmed, "[ERROR")) {
119         LOG(ERROR) << trimmed;
120       } else if (android::base::StartsWith(trimmed, "[WARNING")) {
121         LOG(WARNING) << trimmed;
122       } else if (android::base::StartsWith(trimmed, "[VERBOSE")) {
123         LOG(VERBOSE) << trimmed;
124       } else {
125         LOG(DEBUG) << trimmed;
126       }
127 
128       // Go back to polling immediately to see if there is more data, don't
129       // handle any signals yet.
130       continue;
131     }
132     if (poll_fds[1].revents) {
133       struct signalfd_siginfo siginfo;
134       int s = int_fd->Read(&siginfo, sizeof(siginfo));
135       CHECK(s == sizeof(siginfo)) << "bad read size on signalfd, expected "
136                                   << sizeof(siginfo) << " got " << s;
137       CHECK(siginfo.ssi_signo == SIGINT)
138           << "unexpected signal: " << siginfo.ssi_signo;
139       break;
140     }
141   }
142 
143   LOG(DEBUG) << "Finished reading from process " << FLAGS_process_name;
144 }
145