blob: a23e3393aa50665662cba471eccd51c6d03d72c2 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "imageloader/helper_process_proxy.h"
#include <poll.h>
#include <signal.h>
#include <sys/socket.h>
#include <utility>
#include <vector>
#include <base/posix/eintr_wrapper.h>
#include <base/process/launch.h>
#include "imageloader/component.h"
#include "imageloader/imageloader_impl.h"
#include "imageloader/ipc.pb.h"
#include "imageloader/verity_mounter.h"
namespace imageloader {
namespace {
constexpr int kPollingTimeoutSeconds = 10;
} // namespace
void HelperProcessProxy::Start(int argc,
char* argv[],
const std::string& fd_arg) {
int control[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, control) != 0)
PLOG(FATAL) << "socketpair failed";
control_fd_.reset(control[0]);
const int subprocess_fd = control[1];
CHECK_GE(argc, 1);
std::vector<std::string> child_argv;
for (int i = 0; i < argc; i++)
child_argv.push_back(argv[i]);
child_argv.push_back(fd_arg + "=" + std::to_string(subprocess_fd));
base::FileHandleMappingVector fd_mapping;
fd_mapping.push_back({subprocess_fd, subprocess_fd});
base::LaunchOptions options;
options.fds_to_remap = std::move(fd_mapping);
base::Process p = base::LaunchProcess(child_argv, options);
CHECK(p.IsValid());
pid_ = p.Pid();
}
std::unique_ptr<CommandResponse> HelperProcessProxy::SendCommand(
const ImageCommand& image_command, struct msghdr* msg) {
std::vector<char> msg_buf(image_command.ByteSizeLong());
if (!image_command.SerializeToArray(msg_buf.data(), msg_buf.size()))
LOG(FATAL) << "error serializing protobuf";
struct iovec iov[1];
iov[0].iov_base = msg_buf.data();
iov[0].iov_len = image_command.ByteSizeLong();
msg->msg_iov = iov;
msg->msg_iovlen = sizeof(iov) / sizeof(iov[0]);
if (sendmsg(control_fd_.get(), msg, 0) < 0)
PLOG(FATAL) << "sendmsg failed";
return WaitForResponse();
}
bool HelperProcessProxy::SendMountCommand(int fd,
const std::string& path,
FileSystem fs_type,
const std::string& table) {
struct msghdr msg = {0};
char fds[CMSG_SPACE(sizeof(fd))];
memset(fds, '\0', sizeof(fds));
// 1. Construct message object.
ImageCommand image_command;
image_command.mutable_mount_command()->set_mount_path(path);
image_command.mutable_mount_command()->set_table(table);
// Convert the internal enum to the protobuf enum.
switch (fs_type) {
case FileSystem::kExt4:
image_command.mutable_mount_command()->set_fs_type(MountCommand::EXT4);
break;
case FileSystem::kSquashFS:
image_command.mutable_mount_command()->set_fs_type(MountCommand::SQUASH);
break;
default:
LOG(FATAL) << "Unknown file system type passed to helper process.";
}
// 2. Encode the fd into message.
msg.msg_control = fds;
msg.msg_controllen = sizeof(fds);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
// Move the file descriptor into the payload.
memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
msg.msg_controllen = cmsg->cmsg_len;
// 3. Send the command.
return SendCommand(image_command, &msg)->success();
}
bool HelperProcessProxy::SendUnmountAllCommand(
bool dry_run,
const std::string& rootpath,
std::vector<std::string>* paths) {
struct msghdr msg = {0};
// 1. Construct message object.
ImageCommand image_command;
image_command.mutable_unmount_all_command()->set_dry_run(dry_run);
image_command.mutable_unmount_all_command()->set_unmount_rootpath(rootpath);
// 2. Send the command.
std::unique_ptr<CommandResponse> response = SendCommand(image_command, &msg);
// 3. Process return value.
if (paths) {
for (int i = 0; i < response->paths_size(); i++) {
std::string path(response->paths(i));
paths->push_back(path);
}
}
return response->success();
}
bool HelperProcessProxy::SendUnmountCommand(const std::string& path) {
struct msghdr msg = {0};
// 1. Construct message object.
ImageCommand image_command;
image_command.mutable_unmount_command()->set_unmount_path(path);
// 2. Send the command.
return SendCommand(image_command, &msg)->success();
}
std::unique_ptr<CommandResponse> HelperProcessProxy::WaitForResponse() {
struct pollfd pfd;
pfd.fd = control_fd_.get();
pfd.events = POLLIN;
CHECK_LE(kDMSetupTimeoutSeconds, kPollingTimeoutSeconds);
int rc = poll(&pfd, 1, kPollingTimeoutSeconds * 1000 /* (ms) */);
PCHECK(rc >= 0 || errno == EINTR);
std::unique_ptr<CommandResponse> response =
std::make_unique<CommandResponse>();
if (pfd.revents & POLLIN) {
char buffer[4096];
memset(buffer, '\0', sizeof(buffer));
ssize_t bytes =
HANDLE_EINTR(read(control_fd_.get(), buffer, sizeof(buffer)));
PCHECK(bytes != -1);
if (!response->ParseFromArray(buffer, bytes)) {
LOG(FATAL) << "could not deserialize protobuf: " << buffer;
}
}
return response;
}
} // namespace imageloader