diff options
Diffstat (limited to 'src/inode2filename/out_of_process_inode_resolver.cc')
-rw-r--r-- | src/inode2filename/out_of_process_inode_resolver.cc | 402 |
1 files changed, 0 insertions, 402 deletions
diff --git a/src/inode2filename/out_of_process_inode_resolver.cc b/src/inode2filename/out_of_process_inode_resolver.cc deleted file mode 100644 index f409fd2..0000000 --- a/src/inode2filename/out_of_process_inode_resolver.cc +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "common/cmd_utils.h" -#include "inode2filename/inode_resolver.h" -#include "inode2filename/out_of_process_inode_resolver.h" - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/unique_fd.h> - -#include <sstream> -#include <stdio.h> -#include <unistd.h> - -namespace rx = rxcpp; - -namespace iorap::inode2filename { - -using ::android::base::unique_fd; -using ::android::base::borrowed_fd; - -static const char* GetCommandFileName() { - // Avoid ENOENT by execve by specifying the absolute path of inode2filename. -#ifdef __ANDROID__ - return "/system/bin/iorap.inode2filename"; -#else - static const char* file_name = nullptr; - - if (file_name == nullptr) { - char* out_dir = getenv("ANDROID_HOST_OUT"); - static std::string file_name_str = out_dir; - if (out_dir != nullptr) { - file_name_str += "/bin/"; - } else { - // Assume it's in the same directory as the binary we are in. - std::string self_path; - CHECK(::android::base::Readlink("/proc/self/exe", /*out*/&self_path)); - - std::string self_dir = ::android::base::Dirname(self_path); - file_name_str = self_dir + "/"; - } - file_name_str += "iorap.inode2filename"; - - file_name = file_name_str.c_str(); - } - - return file_name; -#endif -} - -std::error_code ErrorCodeFromErrno() { - int err = errno; - return std::error_code(err, std::system_category()); -} - -std::ios_base::failure IosBaseFailureWithErrno(const char* message) { - std::error_code ec = ErrorCodeFromErrno(); - return std::ios_base::failure(message, ec); -} - -static constexpr bool kDebugFgets = false; - -int32_t ReadLineLength(FILE* stream, bool* eof) { - char buf[sizeof(int32_t)]; - size_t count = fread(buf, 1, sizeof(int32_t), stream); - if (feof(stream)) { - // If reaching the end of the stream when trying to read the first int, just - // return. This is legitimate, because after reading the last line, the next - // iteration will reach this. - *eof = true; - return 0; - } - int32_t length; - memcpy(&length, buf, sizeof(int32_t)); - return length; -} - -// The steam is like [size1][file1][size2][file2]...[sizeN][fileN]. -std::string ReadOneLine(FILE* stream, bool* eof) { - DCHECK(stream != nullptr); - DCHECK(eof != nullptr); - - int32_t length = ReadLineLength(stream, eof); - if (length <= 0) { - PLOG(ERROR) << "unexpected 0 length line."; - *eof = true; - return ""; - } - - std::string str(length, '\0'); - size_t count = fread(&str[0], sizeof(char), length, stream); - if (feof(stream) || ferror(stream) || count != (uint32_t)length) { - // error! :( - PLOG(ERROR) << "unexpected end of the line during fread"; - *eof = true; - return ""; - } - return str; -} - -static inline void LeftTrim(/*inout*/std::string& s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { - return !std::isspace(ch); - })); -} - -// Parses an --output-format=ipc kind of line into an InodeResult. -// Returns nullopt if parsing failed. -std::optional<InodeResult> ParseFromLine(const std::string& line) { - // inode <- INT:INT:INT - // line_ok <- 'K ' inode ' ' STRING - // line_err <- 'E ' inode ' ' INT - // - // result <- line_ok | line_err - - std::stringstream ss{line}; - - bool result_ok = false; - - std::string ok_or_error; - ss >> ok_or_error; - - if (ss.fail()) { - return std::nullopt; - } - - if (ok_or_error == "K") { - result_ok = true; - } else if (ok_or_error == "E") { - result_ok = false; - } else { - return std::nullopt; - } - - std::string inode_s; - ss >> inode_s; - - if (ss.fail()) { - return std::nullopt; - } - - Inode inode; - - std::string inode_parse_error_msg; - if (!Inode::Parse(inode_s, /*out*/&inode, /*out*/&inode_parse_error_msg)) { - return std::nullopt; - } - - if (result_ok == false) { - int error_code; - ss >> error_code; - - if (ss.fail()) { - return std::nullopt; - } - - return InodeResult::makeFailure(inode, error_code); - } else if (result_ok == true) { - std::string rest_of_line; - ss >> rest_of_line; - - // parse " string with potential spaces" - // into "string with potential spaces" - LeftTrim(/*inout*/rest_of_line); - - if (ss.fail()) { - return std::nullopt; - } - - const std::string& file_name = rest_of_line; - - return InodeResult::makeSuccess(inode, file_name); - } - - return std::nullopt; -} - -struct OutOfProcessInodeResolver::Impl { - Impl() { - } - - private: - // Create the argv we will pass to the forked inode2filename, corresponds to #EmitAll. - std::vector<std::string> CreateArgvAll(const InodeResolverDependencies& deps) const { - std::vector<std::string> argv; - argv.push_back("--all"); - - return CreateArgv(deps, std::move(argv)); - } - - // Create the argv we will pass to the forked inode2filename, corresponds to - // #FindFilenamesFromInodes. - std::vector<std::string> CreateArgvFind(const InodeResolverDependencies& deps, - const std::vector<Inode>& inodes) const { - std::vector<std::string> argv; - iorap::common::AppendArgsRepeatedly(argv, inodes); - - return CreateArgv(deps, std::move(argv)); - } - - std::vector<std::string> CreateArgv(const InodeResolverDependencies& deps, - std::vector<std::string> append_argv) const { - InodeResolverDependencies deps_oop = deps; - deps_oop.process_mode = ProcessMode::kInProcessDirect; - - std::vector<std::string> argv = ToArgs(deps_oop); - - argv.push_back("--output-format=ipc"); - - if (iorap::common::GetBoolEnvOrProperty("iorap.inode2filename.log.verbose", false)) { - argv.push_back("--verbose"); - } - - iorap::common::AppendArgsRepeatedly(argv, std::move(append_argv)); - - return argv; - } - - public: - // fork+exec into inode2filename with 'inodes' as the search list. - // Each result is parsed into a dest#on_next(result). - // If a fatal error occurs, dest#on_error is called once and no other callbacks are called. - void EmitFromCommandFind(rxcpp::subscriber<InodeResult>& dest, - const InodeResolverDependencies& deps, - const std::vector<Inode>& inodes) { - // Trivial case: complete immediately. - // Executing inode2filename with empty search list will just print the --help menu. - if (inodes.empty()) { - dest.on_completed(); - } - - std::vector<std::string> argv = CreateArgvFind(deps, inodes); - EmitFromCommandWithArgv(/*inout*/dest, std::move(argv), inodes.size()); - } - - // fork+exec into inode2filename with --all (listing *all* inodes). - // Each result is parsed into a dest#on_next(result). - // If a fatal error occurs, dest#on_error is called once and no other callbacks are called. - void EmitFromCommandAll(rxcpp::subscriber<InodeResult>& dest, - const InodeResolverDependencies& deps) { - std::vector<std::string> argv = CreateArgvAll(deps); - EmitFromCommandWithArgv(/*inout*/dest, std::move(argv), /*result_count*/std::nullopt); - } - - private: - void EmitFromCommandWithArgv(rxcpp::subscriber<InodeResult>& dest, - std::vector<std::string> argv_vec, - std::optional<size_t> result_count) { - unique_fd pipe_reader, pipe_writer; - if (!::android::base::Pipe(/*out*/&pipe_reader, /*out*/&pipe_writer)) { - dest.on_error( - rxcpp::util::make_error_ptr( - IosBaseFailureWithErrno("Failed to create out-going pipe for inode2filename"))); - return; - } - - pid_t child = fork(); - if (child == -1) { - dest.on_error( - rxcpp::util::make_error_ptr( - IosBaseFailureWithErrno("Failed to fork process for inode2filename"))); - return; - } else if (child > 0) { // we are the caller of this function - LOG(DEBUG) << "forked into a process for inode2filename , pid = " << child; - } else { - // we are the child that was forked - - const char* kCommandFileName = GetCommandFileName(); - - std::stringstream argv; // for debugging. - for (std::string arg : argv_vec) { - argv << arg << ' '; - } - LOG(DEBUG) << "fork+exec: " << kCommandFileName << " " << argv.str(); - - // Redirect only stdout. stdin is unused, stderr is same as parent. - if (dup2(pipe_writer.get(), STDOUT_FILENO) == -1) { - // Trying to call #on_error does not make sense here because we are in a forked process, - // the only thing we can do is crash definitively. - PLOG(FATAL) << "Failed to dup2 for inode2filename"; - } - - std::unique_ptr<const char *[]> argv_ptr = - common::VecToArgv(kCommandFileName, argv_vec); - - if (execve(kCommandFileName, - (char **)argv_ptr.get(), - /*envp*/nullptr) == -1) { - // Trying to call #on_error does not make sense here because we are in a forked process, - // the only thing we can do is crash definitively. - PLOG(FATAL) << "Failed to execve process for inode2filename"; - } - // This should never return. - } - - // Immediately close the writer end of the pipe because we never use it. - pipe_writer.reset(); - - // Convert pipe(reader) file descriptor into FILE*. - std::unique_ptr<FILE, int(*)(FILE*)> file_reader{ - ::android::base::Fdopen(std::move(pipe_reader), /*mode*/"r"), fclose}; - if (!file_reader) { - dest.on_error( - rxcpp::util::make_error_ptr( - IosBaseFailureWithErrno("Failed to fdopen for inode2filename"))); - return; - } - - size_t actual_result_count = 0; - - bool file_eof = false; - while (!file_eof) { - std::string inode2filename_line = ReadOneLine(file_reader.get(), /*out*/&file_eof); - - if (inode2filename_line.empty()) { - if (!file_eof) { - // Ignore empty lines. - LOG(WARNING) << "inode2filename: got empty line"; - } - continue; - } - - LOG(DEBUG) << "inode2filename output-line: " << inode2filename_line; - - std::optional<InodeResult> res = ParseFromLine(inode2filename_line); - if (!res) { - std::string error_msg = "Invalid output: "; - error_msg += inode2filename_line; - dest.on_error( - rxcpp::util::make_error_ptr(std::ios_base::failure(error_msg))); - return; - } - dest.on_next(*res); - - ++actual_result_count; - } - - LOG(DEBUG) << "inode2filename output-eof"; - - // Ensure that the # of inputs into the rx stream match the # of outputs. - // This is validating the post-condition of FindFilenamesFromInodes. - if (result_count && actual_result_count != *result_count) { - std::stringstream ss; - ss << "Invalid number of results, expected: " << *result_count; - ss << ", actual: " << actual_result_count; - - dest.on_error( - rxcpp::util::make_error_ptr(std::ios_base::failure(ss.str()))); - return; - } - - CHECK(child > 0); // we are in the parent process, parse the IPC output of inode2filename - dest.on_completed(); - } -}; - -rxcpp::observable<InodeResult> - OutOfProcessInodeResolver::FindFilenamesFromInodes(std::vector<Inode> inodes) const { - return rxcpp::observable<>::create<InodeResult>( - [self=std::static_pointer_cast<const OutOfProcessInodeResolver>(shared_from_this()), - inodes=std::move(inodes)]( - rxcpp::subscriber<InodeResult> s) { - self->impl_->EmitFromCommandFind(s, self->GetDependencies(), inodes); - }); -} - -rxcpp::observable<InodeResult> - OutOfProcessInodeResolver::EmitAll() const { - auto self = std::static_pointer_cast<const OutOfProcessInodeResolver>(shared_from_this()); - CHECK(self != nullptr); - CHECK(self->impl_ != nullptr); - - return rxcpp::observable<>::create<InodeResult>( - [self](rxcpp::subscriber<InodeResult> s) { - CHECK(self != nullptr); - CHECK(self->impl_ != nullptr); - self->impl_->EmitFromCommandAll(s, self->GetDependencies()); - }); -} - -OutOfProcessInodeResolver::OutOfProcessInodeResolver(InodeResolverDependencies dependencies) - : InodeResolver{std::move(dependencies)}, impl_{new Impl{}} { -} - -OutOfProcessInodeResolver::~OutOfProcessInodeResolver() { - // std::unique_ptr requires complete types, but we hide the definition in the header. - delete impl_; -} - -} // namespace iorap::inode2filename |