1 /*
2  * Copyright (C) 2024 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 "host/commands/process_sandboxer/logs.h"
17 
18 #include <fcntl.h>
19 #include <unistd.h>
20 
21 #include <memory>
22 #include <sstream>
23 #include <string>
24 
25 #include <absl/log/log.h>
26 #include <absl/log/log_sink.h>
27 #include <absl/log/log_sink_registry.h>
28 #include <absl/status/statusor.h>
29 
30 namespace cuttlefish {
31 namespace process_sandboxer {
32 namespace {
33 
34 // Implementation based on absl::log_internal::StderrLogSink
35 class FileLogSink final : absl::LogSink {
36  public:
FromPath(const std::string & path)37   static absl::StatusOr<std::unique_ptr<FileLogSink>> FromPath(
38       const std::string& path) {
39     std::unique_ptr<FileLogSink> sink(new FileLogSink());
40     sink->fd_ = open(path.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666);
41     if (sink->fd_ < 0) {
42       return absl::ErrnoToStatus(errno, "open failed");
43     }
44     absl::AddLogSink(sink.get());
45     return sink;
46   }
47   FileLogSink(FileLogSink&) = delete;
~FileLogSink()48   ~FileLogSink() {
49     absl::RemoveLogSink(this);
50     if (fd_ >= 0 && close(fd_) < 0) {
51       PLOG(ERROR) << "Failed to close fd '" << fd_ << "'";
52     }
53   }
54 
Send(const absl::LogEntry & entry)55   void Send(const absl::LogEntry& entry) override {
56     std::stringstream message_stream;
57     if (!entry.stacktrace().empty()) {
58       message_stream << entry.stacktrace();
59     }
60     message_stream << entry.text_message_with_prefix_and_newline();
61     auto message = message_stream.str();
62     auto written = write(fd_, message.c_str(), message.size());
63     if (written < 0) {
64       // LOG calls inside here would recurse infinitely because of AddLogSink
65       std::cerr << "FileLogSink: write(" << fd_
66                 << ") failed: " << strerror(errno) << '\n';
67     }
68   }
69 
70  private:
71   FileLogSink() = default;
72 
73   int fd_ = -1;
74 };
75 
76 }  // namespace
77 
LogToFiles(const std::vector<std::string> & paths)78 absl::Status LogToFiles(const std::vector<std::string>& paths) {
79   for (const auto& path : paths) {
80     auto sink_status = FileLogSink::FromPath(path);
81     if (!sink_status.ok()) {
82       return sink_status.status();
83     }
84     sink_status->release();  // Deliberate leak so LOG always writes here
85   }
86   return absl::OkStatus();
87 }
88 
89 }  // namespace process_sandboxer
90 }  // namespace cuttlefish
91