xref: /aosp_15_r20/external/openscreen/cast/standalone_receiver/main.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1*3f982cf4SFabien Sanglard // Copyright 2019 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard 
5*3f982cf4SFabien Sanglard #include <getopt.h>
6*3f982cf4SFabien Sanglard 
7*3f982cf4SFabien Sanglard #include <algorithm>
8*3f982cf4SFabien Sanglard #include <iostream>
9*3f982cf4SFabien Sanglard #include <memory>
10*3f982cf4SFabien Sanglard #include <string>
11*3f982cf4SFabien Sanglard #include <utility>
12*3f982cf4SFabien Sanglard #include <vector>
13*3f982cf4SFabien Sanglard 
14*3f982cf4SFabien Sanglard #include "absl/strings/str_cat.h"
15*3f982cf4SFabien Sanglard #include "cast/receiver/channel/static_credentials.h"
16*3f982cf4SFabien Sanglard #include "cast/standalone_receiver/cast_service.h"
17*3f982cf4SFabien Sanglard #include "platform/api/time.h"
18*3f982cf4SFabien Sanglard #include "platform/base/error.h"
19*3f982cf4SFabien Sanglard #include "platform/base/ip_address.h"
20*3f982cf4SFabien Sanglard #include "platform/impl/logging.h"
21*3f982cf4SFabien Sanglard #include "platform/impl/network_interface.h"
22*3f982cf4SFabien Sanglard #include "platform/impl/platform_client_posix.h"
23*3f982cf4SFabien Sanglard #include "platform/impl/task_runner.h"
24*3f982cf4SFabien Sanglard #include "platform/impl/text_trace_logging_platform.h"
25*3f982cf4SFabien Sanglard #include "util/chrono_helpers.h"
26*3f982cf4SFabien Sanglard #include "util/stringprintf.h"
27*3f982cf4SFabien Sanglard #include "util/trace_logging.h"
28*3f982cf4SFabien Sanglard 
29*3f982cf4SFabien Sanglard namespace openscreen {
30*3f982cf4SFabien Sanglard namespace cast {
31*3f982cf4SFabien Sanglard namespace {
32*3f982cf4SFabien Sanglard 
LogUsage(const char * argv0)33*3f982cf4SFabien Sanglard void LogUsage(const char* argv0) {
34*3f982cf4SFabien Sanglard   constexpr char kTemplate[] = R"(
35*3f982cf4SFabien Sanglard usage: %s <options> <interface>
36*3f982cf4SFabien Sanglard 
37*3f982cf4SFabien Sanglard     interface
38*3f982cf4SFabien Sanglard         Specifies the network interface to bind to. The interface is
39*3f982cf4SFabien Sanglard         looked up from the system interface registry.
40*3f982cf4SFabien Sanglard         Mandatory, as it must be known for publishing discovery.
41*3f982cf4SFabien Sanglard 
42*3f982cf4SFabien Sanglard options:
43*3f982cf4SFabien Sanglard     -p, --private-key=path-to-key: Path to OpenSSL-generated private key to be
44*3f982cf4SFabien Sanglard                     used for TLS authentication. If a private key is not
45*3f982cf4SFabien Sanglard                     provided, a randomly generated one will be used for this
46*3f982cf4SFabien Sanglard                     session.
47*3f982cf4SFabien Sanglard 
48*3f982cf4SFabien Sanglard     -d, --developer-certificate=path-to-cert: Path to PEM file containing a
49*3f982cf4SFabien Sanglard                            developer generated server root TLS certificate.
50*3f982cf4SFabien Sanglard                            If a root server certificate is not provided, one
51*3f982cf4SFabien Sanglard                            will be generated using a randomly generated
52*3f982cf4SFabien Sanglard                            private key. Note that if a certificate path is
53*3f982cf4SFabien Sanglard                            passed, the private key path is a mandatory field.
54*3f982cf4SFabien Sanglard 
55*3f982cf4SFabien Sanglard     -g, --generate-credentials: Instructs the binary to generate a private key
56*3f982cf4SFabien Sanglard                                 and self-signed root certificate with the CA
57*3f982cf4SFabien Sanglard                                 bit set to true, and then exit. The resulting
58*3f982cf4SFabien Sanglard                                 private key and certificate can then be used as
59*3f982cf4SFabien Sanglard                                 values for the -p and -s flags.
60*3f982cf4SFabien Sanglard 
61*3f982cf4SFabien Sanglard     -f, --friendly-name: Friendly name to be used for receiver discovery.
62*3f982cf4SFabien Sanglard 
63*3f982cf4SFabien Sanglard     -m, --model-name: Model name to be used for receiver discovery.
64*3f982cf4SFabien Sanglard 
65*3f982cf4SFabien Sanglard     -t, --tracing: Enable performance tracing logging.
66*3f982cf4SFabien Sanglard 
67*3f982cf4SFabien Sanglard     -v, --verbose: Enable verbose logging.
68*3f982cf4SFabien Sanglard 
69*3f982cf4SFabien Sanglard     -h, --help: Show this help message.
70*3f982cf4SFabien Sanglard 
71*3f982cf4SFabien Sanglard     -x, --disable-discovery: Disable discovery, useful for platforms like Mac OS
72*3f982cf4SFabien Sanglard                              where our implementation is incompatible with
73*3f982cf4SFabien Sanglard                              the native Bonjour service.
74*3f982cf4SFabien Sanglard 
75*3f982cf4SFabien Sanglard )";
76*3f982cf4SFabien Sanglard 
77*3f982cf4SFabien Sanglard   std::cerr << StringPrintf(kTemplate, argv0);
78*3f982cf4SFabien Sanglard }
79*3f982cf4SFabien Sanglard 
GetInterfaceInfoFromName(const char * name)80*3f982cf4SFabien Sanglard InterfaceInfo GetInterfaceInfoFromName(const char* name) {
81*3f982cf4SFabien Sanglard   OSP_CHECK(name != nullptr) << "Missing mandatory argument: interface.";
82*3f982cf4SFabien Sanglard   InterfaceInfo interface_info;
83*3f982cf4SFabien Sanglard   std::vector<InterfaceInfo> network_interfaces = GetNetworkInterfaces();
84*3f982cf4SFabien Sanglard   for (auto& interface : network_interfaces) {
85*3f982cf4SFabien Sanglard     if (interface.name == name) {
86*3f982cf4SFabien Sanglard       interface_info = std::move(interface);
87*3f982cf4SFabien Sanglard       break;
88*3f982cf4SFabien Sanglard     }
89*3f982cf4SFabien Sanglard   }
90*3f982cf4SFabien Sanglard 
91*3f982cf4SFabien Sanglard   if (interface_info.name.empty()) {
92*3f982cf4SFabien Sanglard     auto error_or_info = GetLoopbackInterfaceForTesting();
93*3f982cf4SFabien Sanglard     if (error_or_info.has_value()) {
94*3f982cf4SFabien Sanglard       if (error_or_info.value().name == name) {
95*3f982cf4SFabien Sanglard         interface_info = std::move(error_or_info.value());
96*3f982cf4SFabien Sanglard       }
97*3f982cf4SFabien Sanglard     }
98*3f982cf4SFabien Sanglard   }
99*3f982cf4SFabien Sanglard   OSP_CHECK(!interface_info.name.empty()) << "Invalid interface specified.";
100*3f982cf4SFabien Sanglard   return interface_info;
101*3f982cf4SFabien Sanglard }
102*3f982cf4SFabien Sanglard 
RunCastService(TaskRunnerImpl * runner,CastService::Configuration config)103*3f982cf4SFabien Sanglard void RunCastService(TaskRunnerImpl* runner, CastService::Configuration config) {
104*3f982cf4SFabien Sanglard   std::unique_ptr<CastService> service;
105*3f982cf4SFabien Sanglard   runner->PostTask(
106*3f982cf4SFabien Sanglard       [&] { service = std::make_unique<CastService>(std::move(config)); });
107*3f982cf4SFabien Sanglard 
108*3f982cf4SFabien Sanglard   OSP_LOG_INFO << "CastService is running. CTRL-C (SIGINT), or send a "
109*3f982cf4SFabien Sanglard                   "SIGTERM to exit.";
110*3f982cf4SFabien Sanglard   runner->RunUntilSignaled();
111*3f982cf4SFabien Sanglard 
112*3f982cf4SFabien Sanglard   // Spin the TaskRunner to execute destruction/shutdown tasks.
113*3f982cf4SFabien Sanglard   OSP_LOG_INFO << "Shutting down...";
114*3f982cf4SFabien Sanglard   runner->PostTask([&] {
115*3f982cf4SFabien Sanglard     service.reset();
116*3f982cf4SFabien Sanglard     runner->RequestStopSoon();
117*3f982cf4SFabien Sanglard   });
118*3f982cf4SFabien Sanglard   runner->RunUntilStopped();
119*3f982cf4SFabien Sanglard   OSP_LOG_INFO << "Bye!";
120*3f982cf4SFabien Sanglard }
121*3f982cf4SFabien Sanglard 
RunStandaloneReceiver(int argc,char * argv[])122*3f982cf4SFabien Sanglard int RunStandaloneReceiver(int argc, char* argv[]) {
123*3f982cf4SFabien Sanglard #if !defined(CAST_ALLOW_DEVELOPER_CERTIFICATE)
124*3f982cf4SFabien Sanglard   OSP_LOG_FATAL
125*3f982cf4SFabien Sanglard       << "It compiled! However cast_receiver currently only supports using a "
126*3f982cf4SFabien Sanglard          "passed-in certificate and private key, and must be built with "
127*3f982cf4SFabien Sanglard          "cast_allow_developer_certificate=true set in the GN args to "
128*3f982cf4SFabien Sanglard          "actually do anything interesting.";
129*3f982cf4SFabien Sanglard   return 1;
130*3f982cf4SFabien Sanglard #endif
131*3f982cf4SFabien Sanglard 
132*3f982cf4SFabien Sanglard #if !defined(CAST_STANDALONE_RECEIVER_HAVE_EXTERNAL_LIBS)
133*3f982cf4SFabien Sanglard   OSP_LOG_INFO
134*3f982cf4SFabien Sanglard       << "Note: compiled without external libs. The dummy player will "
135*3f982cf4SFabien Sanglard          "be linked and no video decoding will occur. If this is not desired, "
136*3f982cf4SFabien Sanglard          "install the required external libraries. For more information, see: "
137*3f982cf4SFabien Sanglard          "[external_libraries.md](../../build/config/external_libraries.md).";
138*3f982cf4SFabien Sanglard #endif
139*3f982cf4SFabien Sanglard 
140*3f982cf4SFabien Sanglard   // A note about modifying command line arguments: consider uniformity
141*3f982cf4SFabien Sanglard   // between all Open Screen executables. If it is a platform feature
142*3f982cf4SFabien Sanglard   // being exposed, consider if it applies to the standalone receiver,
143*3f982cf4SFabien Sanglard   // standalone sender, osp demo, and test_main argument options.
144*3f982cf4SFabien Sanglard   const struct option kArgumentOptions[] = {
145*3f982cf4SFabien Sanglard       {"private-key", required_argument, nullptr, 'p'},
146*3f982cf4SFabien Sanglard       {"developer-certificate", required_argument, nullptr, 'd'},
147*3f982cf4SFabien Sanglard       {"generate-credentials", no_argument, nullptr, 'g'},
148*3f982cf4SFabien Sanglard       {"friendly-name", required_argument, nullptr, 'f'},
149*3f982cf4SFabien Sanglard       {"model-name", required_argument, nullptr, 'm'},
150*3f982cf4SFabien Sanglard       {"tracing", no_argument, nullptr, 't'},
151*3f982cf4SFabien Sanglard       {"verbose", no_argument, nullptr, 'v'},
152*3f982cf4SFabien Sanglard       {"help", no_argument, nullptr, 'h'},
153*3f982cf4SFabien Sanglard 
154*3f982cf4SFabien Sanglard       // Discovery is enabled by default, however there are cases where it
155*3f982cf4SFabien Sanglard       // needs to be disabled, such as on Mac OS X.
156*3f982cf4SFabien Sanglard       {"disable-discovery", no_argument, nullptr, 'x'},
157*3f982cf4SFabien Sanglard       {nullptr, 0, nullptr, 0}};
158*3f982cf4SFabien Sanglard 
159*3f982cf4SFabien Sanglard   bool is_verbose = false;
160*3f982cf4SFabien Sanglard   bool enable_discovery = true;
161*3f982cf4SFabien Sanglard   std::string private_key_path;
162*3f982cf4SFabien Sanglard   std::string developer_certificate_path;
163*3f982cf4SFabien Sanglard   std::string friendly_name = "Cast Standalone Receiver";
164*3f982cf4SFabien Sanglard   std::string model_name = "cast_standalone_receiver";
165*3f982cf4SFabien Sanglard   bool should_generate_credentials = false;
166*3f982cf4SFabien Sanglard   std::unique_ptr<TextTraceLoggingPlatform> trace_logger;
167*3f982cf4SFabien Sanglard   int ch = -1;
168*3f982cf4SFabien Sanglard   while ((ch = getopt_long(argc, argv, "p:d:f:m:grtvhx", kArgumentOptions,
169*3f982cf4SFabien Sanglard                            nullptr)) != -1) {
170*3f982cf4SFabien Sanglard     switch (ch) {
171*3f982cf4SFabien Sanglard       case 'p':
172*3f982cf4SFabien Sanglard         private_key_path = optarg;
173*3f982cf4SFabien Sanglard         break;
174*3f982cf4SFabien Sanglard       case 'd':
175*3f982cf4SFabien Sanglard         developer_certificate_path = optarg;
176*3f982cf4SFabien Sanglard         break;
177*3f982cf4SFabien Sanglard       case 'f':
178*3f982cf4SFabien Sanglard         friendly_name = optarg;
179*3f982cf4SFabien Sanglard         break;
180*3f982cf4SFabien Sanglard       case 'm':
181*3f982cf4SFabien Sanglard         model_name = optarg;
182*3f982cf4SFabien Sanglard         break;
183*3f982cf4SFabien Sanglard       case 'g':
184*3f982cf4SFabien Sanglard         should_generate_credentials = true;
185*3f982cf4SFabien Sanglard         break;
186*3f982cf4SFabien Sanglard       case 't':
187*3f982cf4SFabien Sanglard         trace_logger = std::make_unique<TextTraceLoggingPlatform>();
188*3f982cf4SFabien Sanglard         break;
189*3f982cf4SFabien Sanglard       case 'v':
190*3f982cf4SFabien Sanglard         is_verbose = true;
191*3f982cf4SFabien Sanglard         break;
192*3f982cf4SFabien Sanglard       case 'x':
193*3f982cf4SFabien Sanglard         enable_discovery = false;
194*3f982cf4SFabien Sanglard         break;
195*3f982cf4SFabien Sanglard       case 'h':
196*3f982cf4SFabien Sanglard         LogUsage(argv[0]);
197*3f982cf4SFabien Sanglard         return 1;
198*3f982cf4SFabien Sanglard     }
199*3f982cf4SFabien Sanglard   }
200*3f982cf4SFabien Sanglard 
201*3f982cf4SFabien Sanglard   SetLogLevel(is_verbose ? LogLevel::kVerbose : LogLevel::kInfo);
202*3f982cf4SFabien Sanglard 
203*3f982cf4SFabien Sanglard   // Either -g is required, or both -p and -d.
204*3f982cf4SFabien Sanglard   if (should_generate_credentials) {
205*3f982cf4SFabien Sanglard     GenerateDeveloperCredentialsToFile();
206*3f982cf4SFabien Sanglard     return 0;
207*3f982cf4SFabien Sanglard   }
208*3f982cf4SFabien Sanglard   if (private_key_path.empty() || developer_certificate_path.empty()) {
209*3f982cf4SFabien Sanglard     OSP_LOG_FATAL << "You must either invoke with -g to generate credentials, "
210*3f982cf4SFabien Sanglard                      "or provide both a private key path and root certificate "
211*3f982cf4SFabien Sanglard                      "using -p and -d";
212*3f982cf4SFabien Sanglard     return 1;
213*3f982cf4SFabien Sanglard   }
214*3f982cf4SFabien Sanglard 
215*3f982cf4SFabien Sanglard   const char* interface_name = argv[optind];
216*3f982cf4SFabien Sanglard   OSP_CHECK(interface_name && strlen(interface_name) > 0)
217*3f982cf4SFabien Sanglard       << "No interface name provided.";
218*3f982cf4SFabien Sanglard 
219*3f982cf4SFabien Sanglard   std::string receiver_id =
220*3f982cf4SFabien Sanglard       absl::StrCat("Standalone Receiver on ", interface_name);
221*3f982cf4SFabien Sanglard   ErrorOr<GeneratedCredentials> creds = GenerateCredentials(
222*3f982cf4SFabien Sanglard       receiver_id, private_key_path, developer_certificate_path);
223*3f982cf4SFabien Sanglard   OSP_CHECK(creds.is_value()) << creds.error();
224*3f982cf4SFabien Sanglard 
225*3f982cf4SFabien Sanglard   const InterfaceInfo interface = GetInterfaceInfoFromName(interface_name);
226*3f982cf4SFabien Sanglard   OSP_CHECK(interface.GetIpAddressV4() || interface.GetIpAddressV6());
227*3f982cf4SFabien Sanglard 
228*3f982cf4SFabien Sanglard   auto* const task_runner = new TaskRunnerImpl(&Clock::now);
229*3f982cf4SFabien Sanglard   PlatformClientPosix::Create(milliseconds(50),
230*3f982cf4SFabien Sanglard                               std::unique_ptr<TaskRunnerImpl>(task_runner));
231*3f982cf4SFabien Sanglard   RunCastService(task_runner,
232*3f982cf4SFabien Sanglard                  CastService::Configuration{
233*3f982cf4SFabien Sanglard                      task_runner, interface, std::move(creds.value()),
234*3f982cf4SFabien Sanglard                      friendly_name, model_name, enable_discovery});
235*3f982cf4SFabien Sanglard   PlatformClientPosix::ShutDown();
236*3f982cf4SFabien Sanglard 
237*3f982cf4SFabien Sanglard   return 0;
238*3f982cf4SFabien Sanglard }
239*3f982cf4SFabien Sanglard 
240*3f982cf4SFabien Sanglard }  // namespace
241*3f982cf4SFabien Sanglard }  // namespace cast
242*3f982cf4SFabien Sanglard }  // namespace openscreen
243*3f982cf4SFabien Sanglard 
main(int argc,char * argv[])244*3f982cf4SFabien Sanglard int main(int argc, char* argv[]) {
245*3f982cf4SFabien Sanglard   return openscreen::cast::RunStandaloneReceiver(argc, argv);
246*3f982cf4SFabien Sanglard }
247