blob: 43c6a995e514033fcdab700b6d3e2007886a1336 [file] [log] [blame] [edit]
// 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;
}