blob: 48dadb53c9acf34d4e5ca3358a69234b4bd1fb91 [file] [log] [blame]
/*
* Copyright (C) 2022 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 <sys/capability.h>
#include <sys/resource.h>
#include <unistd.h>
#include <iostream>
#include <iterator>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "android-base/logging.h"
#include "android-base/result.h"
#include "android-base/strings.h"
#include "base/macros.h"
#include "base/scoped_cap.h"
#include "processgroup/processgroup.h"
#include "system/thread_defs.h"
namespace {
using ::android::base::ConsumePrefix;
using ::android::base::Join;
using ::android::base::Result;
using ::android::base::Split;
constexpr const char* kUsage =
R"(A wrapper binary that configures the process and executes a command.
Usage: art_exec [OPTIONS]... -- [COMMAND]...
Supported options:
--help: Print this text.
--set-task-profile=PROFILES: Apply a set of task profiles (see
https://source.android.com/devices/tech/perf/cgroups). Requires root access. PROFILES can be a
comma-separated list of task profile names.
--set-priority=PRIORITY: Apply the process priority. Currently, the only supported value of
PRIORITY is "background".
--drop-capabilities: Drop all root capabilities. Note that this has effect only if `art_exec` runs
with some root capabilities but not as the root user.
)";
constexpr int kErrorUsage = 100;
constexpr int kErrorOther = 101;
struct Options {
int command_pos = -1;
std::vector<std::string> task_profiles;
std::optional<int> priority = std::nullopt;
bool drop_capabilities = false;
};
[[noreturn]] void Usage(const std::string& error_msg) {
LOG(ERROR) << error_msg;
std::cerr << error_msg << "\n" << kUsage << "\n";
exit(kErrorUsage);
}
Options ParseOptions(int argc, char** argv) {
Options options;
for (int i = 1; i < argc; i++) {
std::string_view arg = argv[i];
if (arg == "--help") {
std::cerr << kUsage << "\n";
exit(0);
} else if (ConsumePrefix(&arg, "--set-task-profile=")) {
options.task_profiles = Split(std::string(arg), ",");
if (options.task_profiles.empty()) {
Usage("Empty task profile list");
}
} else if (ConsumePrefix(&arg, "--set-priority=")) {
if (arg == "background") {
options.priority = ANDROID_PRIORITY_BACKGROUND;
} else {
Usage("Unknown priority " + std::string(arg));
}
} else if (arg == "--drop-capabilities") {
options.drop_capabilities = true;
} else if (arg == "--") {
if (i + 1 >= argc) {
Usage("Missing command after '--'");
}
options.command_pos = i + 1;
return options;
} else {
Usage("Unknown option " + std::string(arg));
}
}
Usage("Missing '--'");
}
Result<void> DropInheritableCaps() {
art::ScopedCap cap(cap_get_proc());
if (cap.Get() == nullptr) {
return ErrnoErrorf("Failed to call cap_get_proc");
}
if (cap_clear_flag(cap.Get(), CAP_INHERITABLE) != 0) {
return ErrnoErrorf("Failed to call cap_clear_flag");
}
if (cap_set_proc(cap.Get()) != 0) {
return ErrnoErrorf("Failed to call cap_set_proc");
}
return {};
}
} // namespace
int main(int argc, char** argv) {
android::base::InitLogging(argv);
Options options = ParseOptions(argc, argv);
if (!options.task_profiles.empty()) {
if (!SetTaskProfiles(/*tid=*/0, options.task_profiles)) {
LOG(ERROR) << "Failed to set task profile";
return kErrorOther;
}
}
if (options.priority.has_value()) {
if (setpriority(PRIO_PROCESS, /*who=*/0, options.priority.value()) != 0) {
PLOG(ERROR) << "Failed to setpriority";
return kErrorOther;
}
}
if (options.drop_capabilities) {
if (auto result = DropInheritableCaps(); !result.ok()) {
LOG(ERROR) << "Failed to drop inheritable capabilities: " << result.error();
return kErrorOther;
}
}
execv(argv[options.command_pos], argv + options.command_pos);
std::vector<const char*> command_args(argv + options.command_pos, argv + argc);
PLOG(FATAL) << "Failed to execute (" << Join(command_args, ' ') << ")";
UNREACHABLE();
}