blob: 345b62eea21369bdd2385385c49c7faf163bd631 [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 <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <map>
#include <memory>
#include <string>
#include <base/logging.h>
#include <brillo/flag_helper.h>
#include <brillo/process.h>
#include <brillo/syslog_logging.h>
#include "vm_launcher/constants.h"
#include "vm_launcher/mac_address.h"
#include "vm_launcher/subnet.h"
namespace {
std::string BuildKernelCommandLine(
const std::map<std::string, std::string>& args) {
std::string command_line;
for (const auto& key_value : args) {
if (!command_line.empty()) {
command_line += " ";
}
command_line += key_value.first;
command_line += "=";
command_line += key_value.second;
}
return command_line;
}
} // namespace
int main(int argc, char** argv) {
DEFINE_bool(kvmtool,
false,
"Use the kvmtool hypervisor instead of the default crosvm");
DEFINE_bool(runc, false, "Use the runc container runtime instead of run_oci");
DEFINE_string(container, "", "Path of the container to start");
brillo::FlagHelper::Init(argc, argv, "Launches a container in a VM");
brillo::InitLog(brillo::kLogToSyslog);
if (FLAGS_container.empty()) {
LOG(ERROR) << "No container to start";
return 1;
}
struct stat container_stat;
int rc = stat(FLAGS_container.c_str(), &container_stat);
if (rc) {
PLOG(ERROR) << "Failed to stat container path";
return 1;
}
// TODO(smbarber): Make an init script do this.
rc = mkdir(vm_launcher::kVmRuntimeDirectory, 0700);
if (rc && errno != EEXIST) {
PLOG(ERROR) << "Failed to create vm runtime directory";
return 1;
}
// TODO(smbarber): Work with crosvm one day.
if (!FLAGS_kvmtool) {
LOG(ERROR) << "Only kvmtool is supported as a VM hypervisor";
return 1;
}
brillo::ProcessImpl vm_process;
vm_process.AddArg(vm_launcher::kLkvmBin);
vm_process.AddArg("run");
vm_process.AddStringOption("-k", vm_launcher::kVmKernelPath);
vm_process.AddStringOption("-d",
std::string(vm_launcher::kVmRootfsPath) + ",ro");
if (S_ISDIR(container_stat.st_mode & S_IFMT)) {
vm_process.AddStringOption("--9p", FLAGS_container + ",container_rootfs");
} else {
vm_process.AddStringOption("-d", FLAGS_container + ",ro");
}
auto mac_addr = vm_launcher::MacAddress::Create();
if (!mac_addr) {
LOG(ERROR) << "Could not allocate MAC address";
return 1;
}
LOG(INFO) << "Allocated MAC address " << mac_addr->ToString();
auto subnet = vm_launcher::Subnet::Create();
if (!subnet) {
LOG(ERROR) << "Could not allocate subnet";
return 1;
}
LOG(INFO) << "Allocated subnet with"
<< " gateway: " << subnet->GetGatewayAddress()
<< " ip: " << subnet->GetIpAddress()
<< " netmask: " << subnet->GetNetmask();
// Handle networking-specific args.
vm_process.AddStringOption(
"-n",
base::StringPrintf("mode=tap,guest_mac=%s,host_ip=%s,guest_ip=%s",
mac_addr->ToString().c_str(),
subnet->GetGatewayAddress().c_str(),
subnet->GetIpAddress().c_str()));
// Create kernel command line args.
std::map<std::string, std::string> args = {
{"container_runtime", FLAGS_runc ? "runc" : "kvmtool"},
{"ip_addr", subnet->GetIpAddress()},
{"netmask", subnet->GetNetmask()},
{"gateway", subnet->GetGatewayAddress()}};
vm_process.AddStringOption("-p", BuildKernelCommandLine(args));
vm_process.AddArg("--rng");
// kvmtool likes sticking sockets in HOME. Force it to use /run/vm instead.
rc = setenv("HOME", vm_launcher::kVmRuntimeDirectory, true);
if (rc < 0) {
PLOG(ERROR) << "Could not set HOME before launching kvmtool";
return 1;
}
rc = vm_process.Run();
LOG(INFO) << "VM exit with status code " << rc;
return 0;
}