#include #include #include #include #include #include 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(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 \n" << " warppipe_cli list-links\n" << " warppipe_cli list-rules\n" << " warppipe_cli create-sink [--rate N] [--channels N]\n" << " warppipe_cli create-source [--rate N] [--channels N]\n" << " warppipe_cli link [--passive] [--linger]\n" << " warppipe_cli unlink \n" << " warppipe_cli add-rule --app --target [--process ] [--role ]\n" << " warppipe_cli remove-rule \n" << " warppipe_cli save-config \n" << " warppipe_cli load-config \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 \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(); }