blob: 8b30d87c6a2419b5c37eeca34e6b0b70fdf999f8 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 <fuchsia/virtualization/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fit/function.h>
#include <lib/sys/cpp/component_context.h>
#include <iostream>
#include <optional>
#include "lib/sys/cpp/component_context.h"
#include "src/lib/fxl/strings/string_number_conversions.h"
#include "src/virtualization/bin/guest/balloon.h"
#include "src/virtualization/bin/guest/launch.h"
#include "src/virtualization/bin/guest/list.h"
#include "src/virtualization/bin/guest/serial.h"
#include "src/virtualization/bin/guest/socat.h"
#include "src/virtualization/bin/guest/vshc.h"
#include "src/virtualization/lib/guest_config/guest_config.h"
static void usage() {
std::cerr << "Usage: guest <command> <package> <command-args>...\n"
<< "\n"
<< "Commands:\n"
<< " balloon <env_id> <cid> <num-pages>\n"
<< " balloon-stats <env_id> <cid>\n"
<< " launch <package> <vmm-args>...\n"
<< " list\n"
<< " serial <env_id> <cid>\n"
<< " socat <env_id> <cid> <port>\n"
<< " socat-listen <env_id> <host-port>\n"
<< " vsh [<env_id> [<cid> [<port>]]]\n";
}
template <class T>
static bool parse_number(const char* arg, const char* name, T* value,
fxl::Base base = fxl::Base::k10) {
std::string_view arg_view(arg);
if (!fxl::StringToNumberWithError(arg_view, value, base)) {
std::cerr << "Invalid " << name << ": " << arg_view << "\n";
return false;
}
return true;
}
static bool read_guest_cfg(int argc, const char** argv, fuchsia::virtualization::GuestConfig* cfg) {
zx_status_t status = guest_config::ParseArguments(argc - 1, argv + 1, cfg);
if (status != ZX_OK) {
guest_config::PrintCommandLineUsage(argv[0]);
std::cerr << "Invalid arguments\n";
return false;
}
return true;
}
static bool parse_args(int argc, const char** argv, async::Loop* loop,
sys::ComponentContext* context, fit::closure* func) {
if (argc < 1) {
return false;
}
std::string_view cmd_view(argv[0]);
if (cmd_view == "balloon" && argc == 4) {
uint32_t env_id, cid, num_pages;
if (!parse_number(argv[1], "environment ID", &env_id)) {
return false;
} else if (!parse_number(argv[2], "context ID", &cid)) {
return false;
} else if (!parse_number(argv[3], "number of pages", &num_pages)) {
return false;
}
*func = [env_id, cid, num_pages, context]() {
handle_balloon(env_id, cid, num_pages, context);
};
} else if (cmd_view == "balloon-stats" && argc == 3) {
uint32_t env_id, cid;
if (!parse_number(argv[1], "environment ID", &env_id)) {
return false;
} else if (!parse_number(argv[2], "context ID", &cid)) {
return false;
}
*func = [env_id, cid, context]() { handle_balloon_stats(env_id, cid, context); };
} else if (cmd_view == "launch" && argc >= 2) {
*func = [argc, argv, loop, context]() mutable {
fuchsia::virtualization::GuestConfig cfg;
if (read_guest_cfg(argc, argv, &cfg)) {
handle_launch(argc - 1, argv + 1, loop, std::move(cfg), context);
}
};
} else if (cmd_view == "list") {
*func = [context]() { handle_list(context); };
} else if (cmd_view == "serial" && argc == 3) {
uint32_t env_id, cid;
if (!parse_number(argv[1], "environment ID", &env_id)) {
return false;
} else if (!parse_number(argv[2], "context ID", &cid)) {
return false;
}
*func = [env_id, cid, loop, context]() { handle_serial(env_id, cid, loop, context); };
} else if (cmd_view == "socat" && argc == 4) {
uint32_t env_id, cid, port;
if (!parse_number(argv[1], "environment ID", &env_id)) {
return false;
} else if (!parse_number(argv[2], "context ID", &cid)) {
return false;
} else if (!parse_number(argv[3], "port", &port)) {
return false;
}
*func = [env_id, cid, port, loop, context]() {
handle_socat_connect(env_id, cid, port, loop, context);
};
} else if (cmd_view == "socat-listen" && argc == 3) {
uint32_t env_id, host_port;
if (!parse_number(argv[1], "environment ID", &env_id)) {
return false;
} else if (!parse_number(argv[2], "host port", &host_port)) {
return false;
}
*func = [env_id, host_port, loop, context]() {
handle_socat_listen(env_id, host_port, loop, context);
};
} else if (cmd_view == "vsh" && (argc >= 1 && argc <= 4)) {
std::optional<uint32_t> env_id, cid, port;
bool success = true;
switch (argc) {
case 4:
port = -1;
success &= parse_number(argv[3], "port", &*port);
case 3:
cid = -1;
success &= parse_number(argv[2], "context ID", &*cid);
case 2:
env_id = -1;
success &= parse_number(argv[1], "environment ID", &*env_id);
case 1:
break;
default:
return false;
}
if (!success) {
return false;
}
*func = [env_id, cid, port, loop, context]() { handle_vsh(env_id, cid, port, loop, context); };
} else {
return false;
}
return true;
}
int main(int argc, const char** argv) {
fit::closure func;
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
// This program might be called via an alias representing the vsh subcommand, e.g.
// `guest vsh 0 3` vs `vsh 0 3`
// Only if called using the long form, we must adjust argv for input to |parse_args|
// so that argv[0] represents the subcommand name.
if (std::string_view(argv[0]) == "guest") {
argc--;
argv++;
}
if (!parse_args(argc, argv, &loop, context.get(), &func)) {
usage();
return ZX_ERR_INVALID_ARGS;
}
func();
return 0;
}