xref: /aosp_15_r20/external/openscreen/osp/demo/osp_demo.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2018 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 #include <poll.h>
7 #include <signal.h>
8 #include <unistd.h>
9 
10 #include <algorithm>
11 #include <iostream>
12 #include <memory>
13 #include <string>
14 #include <vector>
15 
16 #include "absl/strings/string_view.h"
17 #include "osp/msgs/osp_messages.h"
18 #include "osp/public/mdns_service_listener_factory.h"
19 #include "osp/public/message_demuxer.h"
20 #include "osp/public/network_service_manager.h"
21 #include "osp/public/presentation/presentation_controller.h"
22 #include "osp/public/presentation/presentation_receiver.h"
23 #include "osp/public/protocol_connection_client.h"
24 #include "osp/public/protocol_connection_client_factory.h"
25 #include "osp/public/protocol_connection_server.h"
26 #include "osp/public/protocol_connection_server_factory.h"
27 #include "osp/public/service_listener.h"
28 #include "osp/public/service_publisher.h"
29 #include "osp/public/service_publisher_factory.h"
30 #include "platform/api/network_interface.h"
31 #include "platform/api/time.h"
32 #include "platform/impl/logging.h"
33 #include "platform/impl/platform_client_posix.h"
34 #include "platform/impl/task_runner.h"
35 #include "platform/impl/text_trace_logging_platform.h"
36 #include "platform/impl/udp_socket_reader_posix.h"
37 #include "third_party/tinycbor/src/src/cbor.h"
38 #include "util/trace_logging.h"
39 
40 namespace {
41 
42 const char* kReceiverLogFilename = "_recv_fifo";
43 const char* kControllerLogFilename = "_cntl_fifo";
44 
45 bool g_done = false;
46 bool g_dump_services = false;
47 
sigusr1_dump_services(int)48 void sigusr1_dump_services(int) {
49   g_dump_services = true;
50 }
51 
sigint_stop(int)52 void sigint_stop(int) {
53   OSP_LOG_INFO << "caught SIGINT, exiting...";
54   g_done = true;
55 }
56 
SignalThings()57 void SignalThings() {
58   struct sigaction usr1_sa;
59   struct sigaction int_sa;
60   struct sigaction unused;
61 
62   usr1_sa.sa_handler = &sigusr1_dump_services;
63   sigemptyset(&usr1_sa.sa_mask);
64   usr1_sa.sa_flags = 0;
65 
66   int_sa.sa_handler = &sigint_stop;
67   sigemptyset(&int_sa.sa_mask);
68   int_sa.sa_flags = 0;
69 
70   sigaction(SIGUSR1, &usr1_sa, &unused);
71   sigaction(SIGINT, &int_sa, &unused);
72 
73   OSP_LOG_INFO << "signal handlers setup" << std::endl << "pid: " << getpid();
74 }
75 
76 }  // namespace
77 
78 namespace openscreen {
79 namespace osp {
80 
81 class DemoListenerObserver final : public ServiceListener::Observer {
82  public:
83   ~DemoListenerObserver() override = default;
OnStarted()84   void OnStarted() override { OSP_LOG_INFO << "listener started!"; }
OnStopped()85   void OnStopped() override { OSP_LOG_INFO << "listener stopped!"; }
OnSuspended()86   void OnSuspended() override { OSP_LOG_INFO << "listener suspended!"; }
OnSearching()87   void OnSearching() override { OSP_LOG_INFO << "listener searching!"; }
88 
OnReceiverAdded(const ServiceInfo & info)89   void OnReceiverAdded(const ServiceInfo& info) override {
90     OSP_LOG_INFO << "found! " << info.friendly_name;
91   }
OnReceiverChanged(const ServiceInfo & info)92   void OnReceiverChanged(const ServiceInfo& info) override {
93     OSP_LOG_INFO << "changed! " << info.friendly_name;
94   }
OnReceiverRemoved(const ServiceInfo & info)95   void OnReceiverRemoved(const ServiceInfo& info) override {
96     OSP_LOG_INFO << "removed! " << info.friendly_name;
97   }
OnAllReceiversRemoved()98   void OnAllReceiversRemoved() override { OSP_LOG_INFO << "all removed!"; }
OnError(ServiceListenerError)99   void OnError(ServiceListenerError) override {}
OnMetrics(ServiceListener::Metrics)100   void OnMetrics(ServiceListener::Metrics) override {}
101 };
102 
SanitizeServiceId(absl::string_view service_id)103 std::string SanitizeServiceId(absl::string_view service_id) {
104   std::string safe_service_id(service_id);
105   for (auto& c : safe_service_id) {
106     if (c < ' ' || c > '~') {
107       c = '.';
108     }
109   }
110   return safe_service_id;
111 }
112 
113 class DemoReceiverObserver final : public ReceiverObserver {
114  public:
115   ~DemoReceiverObserver() override = default;
116 
OnRequestFailed(const std::string & presentation_url,const std::string & service_id)117   void OnRequestFailed(const std::string& presentation_url,
118                        const std::string& service_id) override {
119     std::string safe_service_id = SanitizeServiceId(service_id);
120     OSP_LOG_WARN << "request failed: (" << presentation_url << ", "
121                  << safe_service_id << ")";
122   }
OnReceiverAvailable(const std::string & presentation_url,const std::string & service_id)123   void OnReceiverAvailable(const std::string& presentation_url,
124                            const std::string& service_id) override {
125     std::string safe_service_id = SanitizeServiceId(service_id);
126     safe_service_ids_.emplace(safe_service_id, service_id);
127     OSP_LOG_INFO << "available! " << safe_service_id;
128   }
OnReceiverUnavailable(const std::string & presentation_url,const std::string & service_id)129   void OnReceiverUnavailable(const std::string& presentation_url,
130                              const std::string& service_id) override {
131     std::string safe_service_id = SanitizeServiceId(service_id);
132     safe_service_ids_.erase(safe_service_id);
133     OSP_LOG_INFO << "unavailable! " << safe_service_id;
134   }
135 
GetServiceId(const std::string & safe_service_id)136   const std::string& GetServiceId(const std::string& safe_service_id) {
137     OSP_DCHECK(safe_service_ids_.find(safe_service_id) !=
138                safe_service_ids_.end())
139         << safe_service_id << " not found in map";
140     return safe_service_ids_[safe_service_id];
141   }
142 
143  private:
144   std::map<std::string, std::string> safe_service_ids_;
145 };
146 
147 class DemoPublisherObserver final : public ServicePublisher::Observer {
148  public:
149   ~DemoPublisherObserver() override = default;
150 
OnStarted()151   void OnStarted() override { OSP_LOG_INFO << "publisher started!"; }
OnStopped()152   void OnStopped() override { OSP_LOG_INFO << "publisher stopped!"; }
OnSuspended()153   void OnSuspended() override { OSP_LOG_INFO << "publisher suspended!"; }
154 
OnError(Error error)155   void OnError(Error error) override {
156     OSP_LOG_ERROR << "publisher error: " << error;
157   }
OnMetrics(ServicePublisher::Metrics)158   void OnMetrics(ServicePublisher::Metrics) override {}
159 };
160 
161 class DemoConnectionClientObserver final
162     : public ProtocolConnectionServiceObserver {
163  public:
164   ~DemoConnectionClientObserver() override = default;
OnRunning()165   void OnRunning() override {}
OnStopped()166   void OnStopped() override {}
167 
OnMetrics(const NetworkMetrics & metrics)168   void OnMetrics(const NetworkMetrics& metrics) override {}
OnError(const Error & error)169   void OnError(const Error& error) override {}
170 };
171 
172 class DemoConnectionServerObserver final
173     : public ProtocolConnectionServer::Observer {
174  public:
175   class ConnectionObserver final : public ProtocolConnection::Observer {
176    public:
ConnectionObserver(DemoConnectionServerObserver * parent)177     explicit ConnectionObserver(DemoConnectionServerObserver* parent)
178         : parent_(parent) {}
179     ~ConnectionObserver() override = default;
180 
OnConnectionClosed(const ProtocolConnection & connection)181     void OnConnectionClosed(const ProtocolConnection& connection) override {
182       auto& connections = parent_->connections_;
183       connections.erase(
184           std::remove_if(
185               connections.begin(), connections.end(),
186               [this](const std::pair<std::unique_ptr<ConnectionObserver>,
187                                      std::unique_ptr<ProtocolConnection>>& p) {
188                 return p.first.get() == this;
189               }),
190           connections.end());
191     }
192 
193    private:
194     DemoConnectionServerObserver* const parent_;
195   };
196 
197   ~DemoConnectionServerObserver() override = default;
198 
OnRunning()199   void OnRunning() override {}
OnStopped()200   void OnStopped() override {}
OnSuspended()201   void OnSuspended() override {}
202 
OnMetrics(const NetworkMetrics & metrics)203   void OnMetrics(const NetworkMetrics& metrics) override {}
OnError(const Error & error)204   void OnError(const Error& error) override {}
205 
OnIncomingConnection(std::unique_ptr<ProtocolConnection> connection)206   void OnIncomingConnection(
207       std::unique_ptr<ProtocolConnection> connection) override {
208     auto observer = std::make_unique<ConnectionObserver>(this);
209     connection->SetObserver(observer.get());
210     connections_.emplace_back(std::move(observer), std::move(connection));
211     connections_.back().second->CloseWriteEnd();
212   }
213 
214  private:
215   std::vector<std::pair<std::unique_ptr<ConnectionObserver>,
216                         std::unique_ptr<ProtocolConnection>>>
217       connections_;
218 };
219 
220 class DemoRequestDelegate final : public RequestDelegate {
221  public:
222   DemoRequestDelegate() = default;
223   ~DemoRequestDelegate() override = default;
224 
OnConnection(std::unique_ptr<Connection> connection)225   void OnConnection(std::unique_ptr<Connection> connection) override {
226     OSP_LOG_INFO << "request successful";
227     this->connection = std::move(connection);
228   }
229 
OnError(const Error & error)230   void OnError(const Error& error) override {
231     OSP_LOG_INFO << "on request error";
232   }
233 
234   std::unique_ptr<Connection> connection;
235 };
236 
237 class DemoConnectionDelegate final : public Connection::Delegate {
238  public:
239   DemoConnectionDelegate() = default;
240   ~DemoConnectionDelegate() override = default;
241 
OnConnected()242   void OnConnected() override {
243     OSP_LOG_INFO << "presentation connection connected";
244   }
OnClosedByRemote()245   void OnClosedByRemote() override {
246     OSP_LOG_INFO << "presentation connection closed by remote";
247   }
OnDiscarded()248   void OnDiscarded() override {}
OnError(const absl::string_view message)249   void OnError(const absl::string_view message) override {}
OnTerminated()250   void OnTerminated() override { OSP_LOG_INFO << "presentation terminated"; }
251 
OnStringMessage(absl::string_view message)252   void OnStringMessage(absl::string_view message) override {
253     OSP_LOG_INFO << "got message: " << message;
254   }
OnBinaryMessage(const std::vector<uint8_t> & data)255   void OnBinaryMessage(const std::vector<uint8_t>& data) override {}
256 };
257 
258 class DemoReceiverConnectionDelegate final : public Connection::Delegate {
259  public:
260   DemoReceiverConnectionDelegate() = default;
261   ~DemoReceiverConnectionDelegate() override = default;
262 
OnConnected()263   void OnConnected() override {
264     OSP_LOG_INFO << "presentation connection connected";
265   }
OnClosedByRemote()266   void OnClosedByRemote() override {
267     OSP_LOG_INFO << "presentation connection closed by remote";
268   }
OnDiscarded()269   void OnDiscarded() override {}
OnError(const absl::string_view message)270   void OnError(const absl::string_view message) override {}
OnTerminated()271   void OnTerminated() override { OSP_LOG_INFO << "presentation terminated"; }
272 
OnStringMessage(const absl::string_view message)273   void OnStringMessage(const absl::string_view message) override {
274     OSP_LOG_INFO << "got message: " << message;
275     connection->SendString("--echo-- " + std::string(message));
276   }
OnBinaryMessage(const std::vector<uint8_t> & data)277   void OnBinaryMessage(const std::vector<uint8_t>& data) override {}
278 
279   Connection* connection;
280 };
281 
282 class DemoReceiverDelegate final : public ReceiverDelegate {
283  public:
284   ~DemoReceiverDelegate() override = default;
285 
OnUrlAvailabilityRequest(uint64_t client_id,uint64_t request_duration,std::vector<std::string> urls)286   std::vector<msgs::UrlAvailability> OnUrlAvailabilityRequest(
287       uint64_t client_id,
288       uint64_t request_duration,
289       std::vector<std::string> urls) override {
290     std::vector<msgs::UrlAvailability> result;
291     result.reserve(urls.size());
292     for (const auto& url : urls) {
293       OSP_LOG_INFO << "got availability request for: " << url;
294       result.push_back(msgs::UrlAvailability::kAvailable);
295     }
296     return result;
297   }
298 
StartPresentation(const Connection::PresentationInfo & info,uint64_t source_id,const std::vector<msgs::HttpHeader> & http_headers)299   bool StartPresentation(
300       const Connection::PresentationInfo& info,
301       uint64_t source_id,
302       const std::vector<msgs::HttpHeader>& http_headers) override {
303     presentation_id = info.id;
304     connection = std::make_unique<Connection>(info, &cd, Receiver::Get());
305     cd.connection = connection.get();
306     Receiver::Get()->OnPresentationStarted(info.id, connection.get(),
307                                            ResponseResult::kSuccess);
308     return true;
309   }
310 
ConnectToPresentation(uint64_t request_id,const std::string & id,uint64_t source_id)311   bool ConnectToPresentation(uint64_t request_id,
312                              const std::string& id,
313                              uint64_t source_id) override {
314     connection = std::make_unique<Connection>(
315         Connection::PresentationInfo{id, connection->presentation_info().url},
316         &cd, Receiver::Get());
317     cd.connection = connection.get();
318     Receiver::Get()->OnConnectionCreated(request_id, connection.get(),
319                                          ResponseResult::kSuccess);
320     return true;
321   }
322 
TerminatePresentation(const std::string & id,TerminationReason reason)323   void TerminatePresentation(const std::string& id,
324                              TerminationReason reason) override {
325     Receiver::Get()->OnPresentationTerminated(id, reason);
326   }
327 
328   std::string presentation_id;
329   std::unique_ptr<Connection> connection;
330   DemoReceiverConnectionDelegate cd;
331 };
332 
333 struct CommandLineSplit {
334   std::string command;
335   std::string argument_tail;
336 };
337 
SeparateCommandFromArguments(const std::string & line)338 CommandLineSplit SeparateCommandFromArguments(const std::string& line) {
339   size_t split_index = line.find_first_of(' ');
340   // NOTE: |split_index| can be std::string::npos because not all commands
341   // accept arguments.
342   std::string command = line.substr(0, split_index);
343   std::string argument_tail =
344       split_index < line.size() ? line.substr(split_index + 1) : std::string();
345   return {std::move(command), std::move(argument_tail)};
346 }
347 
348 struct CommandWaitResult {
349   bool done;
350   CommandLineSplit command_line;
351 };
352 
WaitForCommand(pollfd * pollfd)353 CommandWaitResult WaitForCommand(pollfd* pollfd) {
354   while (poll(pollfd, 1, 10) >= 0) {
355     if (g_done) {
356       return {true};
357     }
358 
359     if (pollfd->revents == 0) {
360       continue;
361     } else if (pollfd->revents & (POLLERR | POLLHUP)) {
362       return {true};
363     }
364 
365     std::string line;
366     if (!std::getline(std::cin, line)) {
367       return {true};
368     }
369 
370     CommandWaitResult result;
371     result.done = false;
372     result.command_line = SeparateCommandFromArguments(line);
373     return result;
374   }
375   return {true};
376 }
377 
RunControllerPollLoop(Controller * controller)378 void RunControllerPollLoop(Controller* controller) {
379   DemoReceiverObserver receiver_observer;
380   DemoRequestDelegate request_delegate;
381   DemoConnectionDelegate connection_delegate;
382   Controller::ReceiverWatch watch;
383   Controller::ConnectRequest connect_request;
384 
385   pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
386   while (true) {
387     OSP_CHECK_EQ(write(STDOUT_FILENO, "$ ", 2), 2);
388 
389     CommandWaitResult command_result = WaitForCommand(&stdin_pollfd);
390     if (command_result.done) {
391       break;
392     }
393 
394     if (command_result.command_line.command == "avail") {
395       watch = controller->RegisterReceiverWatch(
396           {std::string(command_result.command_line.argument_tail)},
397           &receiver_observer);
398     } else if (command_result.command_line.command == "start") {
399       const absl::string_view& argument_tail =
400           command_result.command_line.argument_tail;
401       size_t next_split = argument_tail.find_first_of(' ');
402       const std::string& service_id = receiver_observer.GetServiceId(
403           std::string(argument_tail.substr(next_split + 1)));
404       const std::string url =
405           static_cast<std::string>(argument_tail.substr(0, next_split));
406       connect_request = controller->StartPresentation(
407           url, service_id, &request_delegate, &connection_delegate);
408     } else if (command_result.command_line.command == "msg") {
409       request_delegate.connection->SendString(
410           command_result.command_line.argument_tail);
411     } else if (command_result.command_line.command == "close") {
412       request_delegate.connection->Close(Connection::CloseReason::kClosed);
413     } else if (command_result.command_line.command == "reconnect") {
414       connect_request = controller->ReconnectConnection(
415           std::move(request_delegate.connection), &request_delegate);
416     } else if (command_result.command_line.command == "term") {
417       request_delegate.connection->Terminate(
418           TerminationReason::kControllerTerminateCalled);
419     }
420   }
421 
422   watch = Controller::ReceiverWatch();
423 }
424 
ListenerDemo()425 void ListenerDemo() {
426   SignalThings();
427 
428   DemoListenerObserver listener_observer;
429   MdnsServiceListenerConfig listener_config;
430   auto mdns_listener = MdnsServiceListenerFactory::Create(
431       listener_config, &listener_observer,
432       PlatformClientPosix::GetInstance()->GetTaskRunner());
433 
434   MessageDemuxer demuxer(Clock::now, MessageDemuxer::kDefaultBufferLimit);
435   DemoConnectionClientObserver client_observer;
436   auto connection_client = ProtocolConnectionClientFactory::Create(
437       &demuxer, &client_observer,
438       PlatformClientPosix::GetInstance()->GetTaskRunner());
439 
440   auto* network_service = NetworkServiceManager::Create(
441       std::move(mdns_listener), nullptr, std::move(connection_client), nullptr);
442   auto controller = std::make_unique<Controller>(Clock::now);
443 
444   network_service->GetMdnsServiceListener()->Start();
445   network_service->GetProtocolConnectionClient()->Start();
446 
447   RunControllerPollLoop(controller.get());
448 
449   network_service->GetMdnsServiceListener()->Stop();
450   network_service->GetProtocolConnectionClient()->Stop();
451 
452   controller.reset();
453 
454   NetworkServiceManager::Dispose();
455 }
456 
HandleReceiverCommand(absl::string_view command,absl::string_view argument_tail,DemoReceiverDelegate & delegate,NetworkServiceManager * manager)457 void HandleReceiverCommand(absl::string_view command,
458                            absl::string_view argument_tail,
459                            DemoReceiverDelegate& delegate,
460                            NetworkServiceManager* manager) {
461   if (command == "avail") {
462     ServicePublisher* publisher = manager->GetServicePublisher();
463 
464     OSP_LOG_INFO << "publisher->state() == "
465                  << static_cast<int>(publisher->state());
466 
467     if (publisher->state() == ServicePublisher::State::kSuspended) {
468       publisher->Resume();
469     } else {
470       publisher->Suspend();
471     }
472   } else if (command == "close") {
473     delegate.connection->Close(Connection::CloseReason::kClosed);
474   } else if (command == "msg") {
475     delegate.connection->SendString(argument_tail);
476   } else if (command == "term") {
477     Receiver::Get()->OnPresentationTerminated(
478         delegate.presentation_id, TerminationReason::kReceiverUserTerminated);
479   } else {
480     OSP_LOG_FATAL << "Received unknown receiver command: " << command;
481   }
482 }
483 
RunReceiverPollLoop(pollfd & file_descriptor,NetworkServiceManager * manager,DemoReceiverDelegate & delegate)484 void RunReceiverPollLoop(pollfd& file_descriptor,
485                          NetworkServiceManager* manager,
486                          DemoReceiverDelegate& delegate) {
487   pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
488   while (true) {
489     OSP_CHECK_EQ(write(STDOUT_FILENO, "$ ", 2), 2);
490 
491     CommandWaitResult command_result = WaitForCommand(&stdin_pollfd);
492     if (command_result.done) {
493       break;
494     }
495 
496     HandleReceiverCommand(command_result.command_line.command,
497                           command_result.command_line.argument_tail, delegate,
498                           manager);
499   }
500 }
501 
CleanupPublisherDemo(NetworkServiceManager * manager)502 void CleanupPublisherDemo(NetworkServiceManager* manager) {
503   Receiver::Get()->SetReceiverDelegate(nullptr);
504   Receiver::Get()->Deinit();
505   manager->GetServicePublisher()->Stop();
506   manager->GetProtocolConnectionServer()->Stop();
507 
508   NetworkServiceManager::Dispose();
509 }
510 
PublisherDemo(absl::string_view friendly_name)511 void PublisherDemo(absl::string_view friendly_name) {
512   SignalThings();
513 
514   constexpr uint16_t server_port = 6667;
515 
516   // TODO(btolsch): aggregate initialization probably better?
517   ServicePublisher::Config publisher_config;
518   publisher_config.friendly_name = std::string(friendly_name);
519   publisher_config.hostname = "turtle-deadbeef";
520   publisher_config.service_instance_name = "deadbeef";
521   publisher_config.connection_server_port = server_port;
522 
523   ServerConfig server_config;
524   for (const InterfaceInfo& interface : GetNetworkInterfaces()) {
525     OSP_VLOG << "Found interface: " << interface;
526     if (!interface.addresses.empty()) {
527       server_config.connection_endpoints.push_back(
528           IPEndpoint{interface.addresses[0].address, server_port});
529       publisher_config.network_interfaces.push_back(interface);
530     }
531   }
532   OSP_LOG_IF(WARN, server_config.connection_endpoints.empty())
533       << "No network interfaces had usable addresses for mDNS publishing.";
534 
535   DemoPublisherObserver publisher_observer;
536   auto service_publisher = ServicePublisherFactory::Create(
537       publisher_config, &publisher_observer,
538       PlatformClientPosix::GetInstance()->GetTaskRunner());
539 
540   MessageDemuxer demuxer(Clock::now, MessageDemuxer::kDefaultBufferLimit);
541   DemoConnectionServerObserver server_observer;
542   auto connection_server = ProtocolConnectionServerFactory::Create(
543       server_config, &demuxer, &server_observer,
544       PlatformClientPosix::GetInstance()->GetTaskRunner());
545 
546   auto* network_service =
547       NetworkServiceManager::Create(nullptr, std::move(service_publisher),
548                                     nullptr, std::move(connection_server));
549 
550   DemoReceiverDelegate receiver_delegate;
551   Receiver::Get()->Init();
552   Receiver::Get()->SetReceiverDelegate(&receiver_delegate);
553   network_service->GetServicePublisher()->Start();
554   network_service->GetProtocolConnectionServer()->Start();
555 
556   pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
557 
558   RunReceiverPollLoop(stdin_pollfd, network_service, receiver_delegate);
559 
560   receiver_delegate.connection.reset();
561   CleanupPublisherDemo(network_service);
562 }
563 
564 }  // namespace osp
565 }  // namespace openscreen
566 
567 struct InputArgs {
568   absl::string_view friendly_server_name;
569   bool is_verbose;
570   bool is_help;
571   bool tracing_enabled;
572 };
573 
LogUsage(const char * argv0)574 void LogUsage(const char* argv0) {
575   std::cerr << R"(
576 usage: )" << argv0
577             << R"( <options> <friendly_name>
578 
579     friendly_name
580         Server name, runs the publisher demo. Omission runs the listener demo.
581 
582     -t, --tracing: Enable performance trace logging.
583 
584     -v, --verbose: Enable verbose logging.
585 
586     -h, --help: Show this help message.
587   )";
588 }
589 
GetInputArgs(int argc,char ** argv)590 InputArgs GetInputArgs(int argc, char** argv) {
591   // A note about modifying command line arguments: consider uniformity
592   // between all Open Screen executables. If it is a platform feature
593   // being exposed, consider if it applies to the standalone receiver,
594   // standalone sender, osp demo, and test_main argument options.
595   const struct option kArgumentOptions[] = {
596       {"tracing", no_argument, nullptr, 't'},
597       {"verbose", no_argument, nullptr, 'v'},
598       {"help", no_argument, nullptr, 'h'},
599       {nullptr, 0, nullptr, 0}};
600 
601   InputArgs args = {};
602   int ch = -1;
603   while ((ch = getopt_long(argc, argv, "tvh", kArgumentOptions, nullptr)) !=
604          -1) {
605     switch (ch) {
606       case 't':
607         args.tracing_enabled = true;
608         break;
609 
610       case 'v':
611         args.is_verbose = true;
612         break;
613 
614       case 'h':
615         args.is_help = true;
616         break;
617     }
618   }
619 
620   if (optind < argc) {
621     args.friendly_server_name = argv[optind];
622   }
623 
624   return args;
625 }
626 
main(int argc,char ** argv)627 int main(int argc, char** argv) {
628   using openscreen::Clock;
629   using openscreen::LogLevel;
630   using openscreen::PlatformClientPosix;
631 
632   InputArgs args = GetInputArgs(argc, argv);
633   if (args.is_help) {
634     LogUsage(argv[0]);
635     return 1;
636   }
637 
638   std::unique_ptr<openscreen::TextTraceLoggingPlatform> trace_logging_platform;
639   if (args.tracing_enabled) {
640     trace_logging_platform =
641         std::make_unique<openscreen::TextTraceLoggingPlatform>();
642   }
643 
644   const LogLevel level = args.is_verbose ? LogLevel::kVerbose : LogLevel::kInfo;
645   openscreen::SetLogLevel(level);
646 
647   const bool is_receiver_demo = !args.friendly_server_name.empty();
648   const char* log_filename =
649       is_receiver_demo ? kReceiverLogFilename : kControllerLogFilename;
650   // TODO(jophba): Mac on Mojave hangs on this command forever.
651   openscreen::SetLogFifoOrDie(log_filename);
652 
653   PlatformClientPosix::Create(std::chrono::milliseconds(50));
654 
655   if (is_receiver_demo) {
656     OSP_LOG_INFO << "Running publisher demo...";
657     openscreen::osp::PublisherDemo(args.friendly_server_name);
658   } else {
659     OSP_LOG_INFO << "Running listener demo...";
660     openscreen::osp::ListenerDemo();
661   }
662 
663   PlatformClientPosix::ShutDown();
664 
665   return 0;
666 }
667