304 lines
9.1 KiB
C++
304 lines
9.1 KiB
C++
#include <csignal>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
#include <warppipe/warppipe.hpp>
|
|
|
|
namespace {
|
|
|
|
volatile sig_atomic_t g_running = 1;
|
|
|
|
void SignalHandler(int) {
|
|
g_running = 0;
|
|
}
|
|
|
|
uint32_t ParseId(const char* value) {
|
|
if (!value) {
|
|
return 0;
|
|
}
|
|
char* end = nullptr;
|
|
unsigned long parsed = std::strtoul(value, &end, 10);
|
|
if (!end || end == value) {
|
|
return 0;
|
|
}
|
|
return static_cast<uint32_t>(parsed);
|
|
}
|
|
|
|
std::string ArgValue(int argc, char* argv[], int i, const char* flag) {
|
|
if (i + 1 < argc) {
|
|
return argv[i + 1];
|
|
}
|
|
std::cerr << "warppipe: missing value for " << flag << "\n";
|
|
return "";
|
|
}
|
|
|
|
int Usage() {
|
|
std::cerr << "Usage:\n"
|
|
<< " warppipe_cli list-nodes\n"
|
|
<< " warppipe_cli list-ports <node-id>\n"
|
|
<< " warppipe_cli list-links\n"
|
|
<< " warppipe_cli list-rules\n"
|
|
<< " warppipe_cli create-sink <name> [--rate N] [--channels N]\n"
|
|
<< " warppipe_cli create-source <name> [--rate N] [--channels N]\n"
|
|
<< " warppipe_cli link <out-node> <out-port> <in-node> <in-port> [--passive] [--linger]\n"
|
|
<< " warppipe_cli unlink <link-id>\n"
|
|
<< " warppipe_cli add-rule --app <name> --target <node> [--process <bin>] [--role <role>]\n"
|
|
<< " warppipe_cli remove-rule <rule-id>\n"
|
|
<< " warppipe_cli save-config <path>\n"
|
|
<< " warppipe_cli load-config <path>\n"
|
|
<< " warppipe_cli defaults\n";
|
|
return 2;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char* argv[]) {
|
|
if (argc < 2) {
|
|
return Usage();
|
|
}
|
|
|
|
std::string command = argv[1];
|
|
warppipe::ConnectionOptions options;
|
|
options.application_name = "warppipe-cli";
|
|
|
|
auto client_result = warppipe::Client::Create(options);
|
|
if (!client_result.ok()) {
|
|
std::cerr << "warppipe: failed to connect: "
|
|
<< client_result.status.message << "\n";
|
|
return 1;
|
|
}
|
|
|
|
if (command == "list-nodes") {
|
|
auto nodes = client_result.value->ListNodes();
|
|
if (!nodes.ok()) {
|
|
std::cerr << "warppipe: list-nodes failed: " << nodes.status.message << "\n";
|
|
return 1;
|
|
}
|
|
for (const auto& node : nodes.value) {
|
|
std::cout << node.id.value << "\t" << node.name << "\t" << node.media_class << "\n";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (command == "list-ports") {
|
|
if (argc < 3) {
|
|
return Usage();
|
|
}
|
|
uint32_t node_id = ParseId(argv[2]);
|
|
if (node_id == 0) {
|
|
std::cerr << "warppipe: invalid node id\n";
|
|
return 1;
|
|
}
|
|
auto ports = client_result.value->ListPorts(warppipe::NodeId{node_id});
|
|
if (!ports.ok()) {
|
|
std::cerr << "warppipe: list-ports failed: " << ports.status.message << "\n";
|
|
return 1;
|
|
}
|
|
for (const auto& port : ports.value) {
|
|
std::cout << port.id.value << "\t" << port.name << "\t"
|
|
<< (port.is_input ? "in" : "out") << "\n";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (command == "list-links") {
|
|
auto links = client_result.value->ListLinks();
|
|
if (!links.ok()) {
|
|
std::cerr << "warppipe: list-links failed: " << links.status.message << "\n";
|
|
return 1;
|
|
}
|
|
for (const auto& link : links.value) {
|
|
std::cout << link.id.value << "\t" << link.output_port.value << "\t"
|
|
<< link.input_port.value << "\n";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (command == "list-rules") {
|
|
auto rules = client_result.value->ListRouteRules();
|
|
if (!rules.ok()) {
|
|
std::cerr << "warppipe: list-rules failed: " << rules.status.message << "\n";
|
|
return 1;
|
|
}
|
|
for (const auto& rule : rules.value) {
|
|
std::cout << rule.id.value << "\t"
|
|
<< "app=" << rule.match.application_name << "\t"
|
|
<< "proc=" << rule.match.process_binary << "\t"
|
|
<< "role=" << rule.match.media_role << "\t"
|
|
<< "-> " << rule.target_node << "\n";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (command == "create-sink" || command == "create-source") {
|
|
if (argc < 3) {
|
|
return Usage();
|
|
}
|
|
std::string name = argv[2];
|
|
warppipe::VirtualNodeOptions node_options;
|
|
for (int i = 3; i < argc; ++i) {
|
|
std::string arg = argv[i];
|
|
if (arg == "--rate" && i + 1 < argc) {
|
|
node_options.format.rate = ParseId(argv[++i]);
|
|
} else if (arg == "--channels" && i + 1 < argc) {
|
|
node_options.format.channels = ParseId(argv[++i]);
|
|
}
|
|
}
|
|
|
|
if (command == "create-sink") {
|
|
auto result = client_result.value->CreateVirtualSink(name, node_options);
|
|
if (!result.ok()) {
|
|
std::cerr << "warppipe: create-sink failed: " << result.status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "created sink " << result.value.name
|
|
<< " (node " << result.value.node.value << ")\n";
|
|
} else {
|
|
auto result = client_result.value->CreateVirtualSource(name, node_options);
|
|
if (!result.ok()) {
|
|
std::cerr << "warppipe: create-source failed: " << result.status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "created source " << result.value.name
|
|
<< " (node " << result.value.node.value << ")\n";
|
|
}
|
|
|
|
std::cout << "press Ctrl+C to stop\n";
|
|
std::signal(SIGINT, SignalHandler);
|
|
std::signal(SIGTERM, SignalHandler);
|
|
while (g_running) {
|
|
usleep(100000);
|
|
}
|
|
std::cout << "\nshutting down\n";
|
|
return 0;
|
|
}
|
|
|
|
if (command == "link") {
|
|
if (argc < 6) {
|
|
std::cerr << "warppipe: link requires <out-node> <out-port> <in-node> <in-port>\n";
|
|
return Usage();
|
|
}
|
|
std::string out_node = argv[2];
|
|
std::string out_port = argv[3];
|
|
std::string in_node = argv[4];
|
|
std::string in_port = argv[5];
|
|
warppipe::LinkOptions link_opts;
|
|
for (int i = 6; i < argc; ++i) {
|
|
std::string arg = argv[i];
|
|
if (arg == "--passive") {
|
|
link_opts.passive = true;
|
|
} else if (arg == "--linger") {
|
|
link_opts.linger = true;
|
|
}
|
|
}
|
|
auto result = client_result.value->CreateLinkByName(out_node, out_port, in_node, in_port, link_opts);
|
|
if (!result.ok()) {
|
|
std::cerr << "warppipe: link failed: " << result.status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "created link " << result.value.id.value << "\n";
|
|
return 0;
|
|
}
|
|
|
|
if (command == "unlink") {
|
|
if (argc < 3) {
|
|
return Usage();
|
|
}
|
|
uint32_t link_id = ParseId(argv[2]);
|
|
if (link_id == 0) {
|
|
std::cerr << "warppipe: invalid link id\n";
|
|
return 1;
|
|
}
|
|
auto status = client_result.value->RemoveLink(warppipe::LinkId{link_id});
|
|
if (!status.ok()) {
|
|
std::cerr << "warppipe: unlink failed: " << status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "removed link " << link_id << "\n";
|
|
return 0;
|
|
}
|
|
|
|
if (command == "add-rule") {
|
|
warppipe::RouteRule rule;
|
|
for (int i = 2; i < argc; ++i) {
|
|
std::string arg = argv[i];
|
|
if (arg == "--app" && i + 1 < argc) {
|
|
rule.match.application_name = argv[++i];
|
|
} else if (arg == "--process" && i + 1 < argc) {
|
|
rule.match.process_binary = argv[++i];
|
|
} else if (arg == "--role" && i + 1 < argc) {
|
|
rule.match.media_role = argv[++i];
|
|
} else if (arg == "--target" && i + 1 < argc) {
|
|
rule.target_node = argv[++i];
|
|
}
|
|
}
|
|
auto result = client_result.value->AddRouteRule(rule);
|
|
if (!result.ok()) {
|
|
std::cerr << "warppipe: add-rule failed: " << result.status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "added rule " << result.value.value << "\n";
|
|
return 0;
|
|
}
|
|
|
|
if (command == "remove-rule") {
|
|
if (argc < 3) {
|
|
return Usage();
|
|
}
|
|
uint32_t rule_id = ParseId(argv[2]);
|
|
if (rule_id == 0) {
|
|
std::cerr << "warppipe: invalid rule id\n";
|
|
return 1;
|
|
}
|
|
auto status = client_result.value->RemoveRouteRule(warppipe::RuleId{rule_id});
|
|
if (!status.ok()) {
|
|
std::cerr << "warppipe: remove-rule failed: " << status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "removed rule " << rule_id << "\n";
|
|
return 0;
|
|
}
|
|
|
|
if (command == "save-config") {
|
|
if (argc < 3) {
|
|
return Usage();
|
|
}
|
|
auto status = client_result.value->SaveConfig(argv[2]);
|
|
if (!status.ok()) {
|
|
std::cerr << "warppipe: save-config failed: " << status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "saved config to " << argv[2] << "\n";
|
|
return 0;
|
|
}
|
|
|
|
if (command == "load-config") {
|
|
if (argc < 3) {
|
|
return Usage();
|
|
}
|
|
auto status = client_result.value->LoadConfig(argv[2]);
|
|
if (!status.ok()) {
|
|
std::cerr << "warppipe: load-config failed: " << status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "loaded config from " << argv[2] << "\n";
|
|
return 0;
|
|
}
|
|
|
|
if (command == "defaults") {
|
|
auto defaults = client_result.value->GetDefaults();
|
|
if (!defaults.ok()) {
|
|
std::cerr << "warppipe: defaults failed: " << defaults.status.message << "\n";
|
|
return 1;
|
|
}
|
|
std::cout << "default_sink\t" << defaults.value.default_sink_name << "\n"
|
|
<< "default_source\t" << defaults.value.default_source_name << "\n"
|
|
<< "configured_sink\t" << defaults.value.configured_sink_name << "\n"
|
|
<< "configured_source\t" << defaults.value.configured_source_name << "\n";
|
|
return 0;
|
|
}
|
|
|
|
return Usage();
|
|
}
|