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)78absl::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