| // Copyright (C) 2024 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. |
| |
| // Helper wrapper for hermetic tools to wrap arguments. |
| // |
| // This roughly equivalent to: |
| // 1. mypath = $(readlink /proc/self/exe) |
| // 2. tool_name = basename(mypath) |
| // 3. internal_dir = dirname(mypath)/kleaf_internal_do_not_use |
| // 3. call <internal_dir>/<tool_name> $@ \\ |
| // $(cat <internal_dir>/<tool_name>_args.txt) |
| // |
| // This is a C++ binary instead of a shell / Python script so that |
| // /proc/self/exe is a proper anchor to find internal_dir. If this were a |
| // script, /proc/self/exe would be the path to the interpreter. |
| // This also avoids using any hermetic tools in order to determine the path to |
| // them. |
| |
| #include <linux/limits.h> |
| #include <string.h> |
| #include <sysexits.h> |
| #include <unistd.h> |
| |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| #include <string> |
| #include <vector> |
| |
| namespace { |
| |
| // $(realpath /proc/self/exe) |
| std::filesystem::path get_my_path() { |
| std::error_code ec; |
| auto my_path = std::filesystem::read_symlink("/proc/self/exe", ec); |
| if (ec.value() != 0) { |
| std::cerr << "ERROR: read_symlink /proc/self/exe: " << ec.message() |
| << std::endl; |
| exit(EX_SOFTWARE); |
| } |
| return my_path; |
| } |
| |
| // Loads <tool_name>_args.txt from hermetic_tools.extra_args |
| std::vector<std::string> load_arg_file(const std::filesystem::path& path) { |
| std::ifstream ifs(path); |
| if (!ifs) { |
| int saved_errno = errno; |
| std::cerr << "Unable to open " << path << ": " << strerror(saved_errno) |
| << std::endl; |
| exit(EX_SOFTWARE); |
| } |
| std::vector<std::string> args; |
| for (std::string arg; std::getline(ifs, arg);) { |
| args.push_back(arg); |
| } |
| return args; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char* argv[]) { |
| auto my_path = get_my_path(); |
| auto internal_dir = my_path.parent_path() / "kleaf_internal_do_not_use"; |
| |
| std::string tool_name(my_path.filename()); |
| |
| // The actual executable we are going to call. Cast to string to use |
| // in new_argv. |
| std::string real_executable = internal_dir / tool_name; |
| |
| std::vector<char*> new_argv; |
| new_argv.push_back(real_executable.data()); |
| |
| if (argc < 1) { |
| std::cerr << "ERROR: argc == " << argc << " < 1" << std::endl; |
| return EX_SOFTWARE; |
| } |
| for (int i = 1; i < argc; i++) { |
| new_argv.push_back(argv[i]); |
| } |
| |
| auto extra_args_file = internal_dir / (tool_name + "_args.txt"); |
| auto preset_args = load_arg_file(extra_args_file); |
| for (auto& preset_arg : preset_args) { |
| new_argv.push_back(preset_arg.data()); |
| } |
| new_argv.push_back(nullptr); |
| |
| if (-1 != execv(real_executable.c_str(), new_argv.data())) { |
| int saved_errno = errno; |
| std::cerr << "ERROR: execv: " << real_executable << ": " |
| << strerror(saved_errno) << std::endl; |
| return EX_SOFTWARE; |
| } |
| std::cerr << "ERROR: execv returns!" << std::endl; |
| return EX_SOFTWARE; |
| } |