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