blob: 147fcd0b2f86f04122608d31b07da6720334ec82 [file] [log] [blame]
/*
* Copyright (C) 2021 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 "libcompos_client.h"
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <binder/IInterface.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <binder_rpc_unstable.hpp>
#include <memory>
#include "aidl/android/system/composd/IIsolatedCompilationService.h"
#include "aidl/com/android/compos/FdAnnotation.h"
#include "aidl/com/android/compos/ICompOsService.h"
using aidl::android::system::composd::IIsolatedCompilationService;
using aidl::com::android::compos::FdAnnotation;
using aidl::com::android::compos::ICompOsService;
using android::base::Join;
using android::base::Pipe;
using android::base::unique_fd;
namespace {
constexpr unsigned int kCompsvcRpcPort = 6432;
constexpr const char* kComposdServiceName = "android.system.composd";
void ExecFdServer(const int* ro_fds, size_t ro_fds_num, const int* rw_fds, size_t rw_fds_num,
unique_fd ready_fd) {
// Holder of C Strings, with enough memory reserved to avoid reallocation. Otherwise,
// `holder.rbegin()->c_str()` may become invalid.
std::vector<std::string> holder;
holder.reserve(ro_fds_num + rw_fds_num + 1 /* for --ready-fd */);
std::vector<char const*> args = {"/apex/com.android.virt/bin/fd_server"};
for (int i = 0; i < ro_fds_num; ++i) {
args.emplace_back("--ro-fds");
holder.emplace_back(std::to_string(*(ro_fds + i)));
args.emplace_back(holder.rbegin()->c_str());
}
for (int i = 0; i < rw_fds_num; ++i) {
args.emplace_back("--rw-fds");
holder.emplace_back(std::to_string(*(rw_fds + i)));
args.emplace_back(holder.rbegin()->c_str());
}
args.emplace_back("--ready-fd");
holder.emplace_back(std::to_string(ready_fd.get()));
args.emplace_back(holder.rbegin()->c_str());
LOG(DEBUG) << "Starting fd_server, args: " << Join(args, ' ');
args.emplace_back(nullptr);
if (execv(args[0], const_cast<char* const*>(args.data())) < 0) {
PLOG(ERROR) << "execv failed";
}
}
class FileSharingSession final {
public:
static std::unique_ptr<FileSharingSession> Create(const int* ro_fds, size_t ro_fds_num,
const int* rw_fds, size_t rw_fds_num) {
// Create pipe for receiving a ready ping from fd_server.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write, /* flags= */ 0)) {
PLOG(ERROR) << "Cannot create pipe";
return nullptr;
}
pid_t pid = fork();
if (pid < 0) {
PLOG(ERROR) << "fork error";
return nullptr;
} else if (pid > 0) {
pipe_write.reset();
// When fd_server is ready it closes its end of the pipe. And if it exits, the pipe is
// also closed. Either way this read will return 0 bytes at that point, and there's no
// point waiting any longer.
char c;
read(pipe_read.get(), &c, sizeof(c));
std::unique_ptr<FileSharingSession> session(new FileSharingSession(pid));
return session;
} else if (pid == 0) {
pipe_read.reset();
ExecFdServer(ro_fds, ro_fds_num, rw_fds, rw_fds_num, std::move(pipe_write));
exit(EXIT_FAILURE);
}
return nullptr;
}
~FileSharingSession() {
if (kill(fd_server_pid_, SIGTERM) < 0) {
PLOG(ERROR) << "Cannot kill fd_server (pid " << std::to_string(fd_server_pid_)
<< ") with SIGTERM. Retry with SIGKILL.";
if (kill(fd_server_pid_, SIGKILL) < 0) {
PLOG(ERROR) << "Still cannot terminate with SIGKILL. Give up.";
// TODO: it may be the safest if we turn fd_server into a library to run in a
// thread.
}
}
}
private:
explicit FileSharingSession(pid_t pid) : fd_server_pid_(pid) {}
pid_t fd_server_pid_;
};
int MakeRequestToVM(int cid, const uint8_t* marshaled, size_t size, const int* ro_fds,
size_t ro_fds_num, const int* rw_fds, size_t rw_fds_num) {
ndk::SpAIBinder binder(RpcClient(cid, kCompsvcRpcPort));
std::shared_ptr<ICompOsService> service = ICompOsService::fromBinder(binder);
if (!service) {
LOG(ERROR) << "Cannot connect to the service";
return -1;
}
std::unique_ptr<FileSharingSession> session_raii =
FileSharingSession::Create(ro_fds, ro_fds_num, rw_fds, rw_fds_num);
if (!session_raii) {
LOG(ERROR) << "Cannot start to share FDs";
return -1;
}
// Since the input from the C API are raw pointers, we need to duplicate them into vectors in
// order to pass to the binder API.
std::vector<uint8_t> duplicated_buffer(marshaled, marshaled + size);
FdAnnotation fd_annotation = {
.input_fds = std::vector<int>(ro_fds, ro_fds + ro_fds_num),
.output_fds = std::vector<int>(rw_fds, rw_fds + rw_fds_num),
};
int8_t exit_code;
ndk::ScopedAStatus status = service->compile(duplicated_buffer, fd_annotation, &exit_code);
if (!status.isOk()) {
LOG(ERROR) << "Compilation failed (exit " << std::to_string(exit_code)
<< "): " << status.getDescription();
return -1;
}
return 0;
}
int MakeRequestToComposd(const uint8_t* marshaled, size_t size, const int* ro_fds,
size_t ro_fds_num, const int* rw_fds, size_t rw_fds_num) {
ndk::SpAIBinder binder(AServiceManager_getService(kComposdServiceName));
std::shared_ptr<IIsolatedCompilationService> service =
IIsolatedCompilationService::fromBinder(binder);
if (!service) {
LOG(ERROR) << "Cannot connect to the service";
return -1;
}
auto session_raii = std::unique_ptr<FileSharingSession>(
FileSharingSession::Create(ro_fds, ro_fds_num, rw_fds, rw_fds_num));
if (!session_raii) {
LOG(ERROR) << "Cannot start to share FDs";
return -1;
}
// Since the input from the C API are raw pointers, we need to duplicate them into vectors in
// order to pass to the binder API.
std::vector<uint8_t> duplicated_buffer(marshaled, marshaled + size);
FdAnnotation fd_annotation = {
.input_fds = std::vector<int>(ro_fds, ro_fds + ro_fds_num),
.output_fds = std::vector<int>(rw_fds, rw_fds + rw_fds_num),
};
int8_t exit_code;
ndk::ScopedAStatus status = service->compile(duplicated_buffer, fd_annotation, &exit_code);
if (!status.isOk()) {
LOG(ERROR) << "Compilation failed (exit " << std::to_string(exit_code)
<< "): " << status.getDescription();
return -1;
}
return 0;
}
} // namespace
__BEGIN_DECLS
int AComposClient_Request(int cid, const uint8_t* marshaled, size_t size, const int* ro_fds,
size_t ro_fds_num, const int* rw_fds, size_t rw_fds_num) {
if (cid == -1 /* VMADDR_CID_ANY */) {
return MakeRequestToComposd(marshaled, size, ro_fds, ro_fds_num, rw_fds, rw_fds_num);
} else {
return MakeRequestToVM(cid, marshaled, size, ro_fds, ro_fds_num, rw_fds, rw_fds_num);
}
}
__END_DECLS