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