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/mdns_service_publisher_factory.h"
20 #include "osp/public/message_demuxer.h"
21 #include "osp/public/network_service_manager.h"
22 #include "osp/public/presentation/presentation_controller.h"
23 #include "osp/public/presentation/presentation_receiver.h"
24 #include "osp/public/protocol_connection_client.h"
25 #include "osp/public/protocol_connection_client_factory.h"
26 #include "osp/public/protocol_connection_server.h"
27 #include "osp/public/protocol_connection_server_factory.h"
28 #include "osp/public/service_listener.h"
29 #include "osp/public/service_publisher.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(ServicePublisherError)155   void OnError(ServicePublisherError) override {}
OnMetrics(ServicePublisher::Metrics)156   void OnMetrics(ServicePublisher::Metrics) override {}
157 };
158 
159 class DemoConnectionClientObserver final
160     : public ProtocolConnectionServiceObserver {
161  public:
162   ~DemoConnectionClientObserver() override = default;
OnRunning()163   void OnRunning() override {}
OnStopped()164   void OnStopped() override {}
165 
OnMetrics(const NetworkMetrics & metrics)166   void OnMetrics(const NetworkMetrics& metrics) override {}
OnError(const Error & error)167   void OnError(const Error& error) override {}
168 };
169 
170 class DemoConnectionServerObserver final
171     : public ProtocolConnectionServer::Observer {
172  public:
173   class ConnectionObserver final : public ProtocolConnection::Observer {
174    public:
ConnectionObserver(DemoConnectionServerObserver * parent)175     explicit ConnectionObserver(DemoConnectionServerObserver* parent)
176         : parent_(parent) {}
177     ~ConnectionObserver() override = default;
178 
OnConnectionClosed(const ProtocolConnection & connection)179     void OnConnectionClosed(const ProtocolConnection& connection) override {
180       auto& connections = parent_->connections_;
181       connections.erase(
182           std::remove_if(
183               connections.begin(), connections.end(),
184               [this](const std::pair<std::unique_ptr<ConnectionObserver>,
185                                      std::unique_ptr<ProtocolConnection>>& p) {
186                 return p.first.get() == this;
187               }),
188           connections.end());
189     }
190 
191    private:
192     DemoConnectionServerObserver* const parent_;
193   };
194 
195   ~DemoConnectionServerObserver() override = default;
196 
OnRunning()197   void OnRunning() override {}
OnStopped()198   void OnStopped() override {}
OnSuspended()199   void OnSuspended() override {}
200 
OnMetrics(const NetworkMetrics & metrics)201   void OnMetrics(const NetworkMetrics& metrics) override {}
OnError(const Error & error)202   void OnError(const Error& error) override {}
203 
OnIncomingConnection(std::unique_ptr<ProtocolConnection> connection)204   void OnIncomingConnection(
205       std::unique_ptr<ProtocolConnection> connection) override {
206     auto observer = std::make_unique<ConnectionObserver>(this);
207     connection->SetObserver(observer.get());
208     connections_.emplace_back(std::move(observer), std::move(connection));
209     connections_.back().second->CloseWriteEnd();
210   }
211 
212  private:
213   std::vector<std::pair<std::unique_ptr<ConnectionObserver>,
214                         std::unique_ptr<ProtocolConnection>>>
215       connections_;
216 };
217 
218 class DemoRequestDelegate final : public RequestDelegate {
219  public:
220   DemoRequestDelegate() = default;
221   ~DemoRequestDelegate() override = default;
222 
OnConnection(std::unique_ptr<Connection> connection)223   void OnConnection(std::unique_ptr<Connection> connection) override {
224     OSP_LOG_INFO << "request successful";
225     this->connection = std::move(connection);
226   }
227 
OnError(const Error & error)228   void OnError(const Error& error) override {
229     OSP_LOG_INFO << "on request error";
230   }
231 
232   std::unique_ptr<Connection> connection;
233 };
234 
235 class DemoConnectionDelegate final : public Connection::Delegate {
236  public:
237   DemoConnectionDelegate() = default;
238   ~DemoConnectionDelegate() override = default;
239 
OnConnected()240   void OnConnected() override {
241     OSP_LOG_INFO << "presentation connection connected";
242   }
OnClosedByRemote()243   void OnClosedByRemote() override {
244     OSP_LOG_INFO << "presentation connection closed by remote";
245   }
OnDiscarded()246   void OnDiscarded() override {}
OnError(const absl::string_view message)247   void OnError(const absl::string_view message) override {}
OnTerminated()248   void OnTerminated() override { OSP_LOG_INFO << "presentation terminated"; }
249 
OnStringMessage(absl::string_view message)250   void OnStringMessage(absl::string_view message) override {
251     OSP_LOG_INFO << "got message: " << message;
252   }
OnBinaryMessage(const std::vector<uint8_t> & data)253   void OnBinaryMessage(const std::vector<uint8_t>& data) override {}
254 };
255 
256 class DemoReceiverConnectionDelegate final : public Connection::Delegate {
257  public:
258   DemoReceiverConnectionDelegate() = default;
259   ~DemoReceiverConnectionDelegate() override = default;
260 
OnConnected()261   void OnConnected() override {
262     OSP_LOG_INFO << "presentation connection connected";
263   }
OnClosedByRemote()264   void OnClosedByRemote() override {
265     OSP_LOG_INFO << "presentation connection closed by remote";
266   }
OnDiscarded()267   void OnDiscarded() override {}
OnError(const absl::string_view message)268   void OnError(const absl::string_view message) override {}
OnTerminated()269   void OnTerminated() override { OSP_LOG_INFO << "presentation terminated"; }
270 
OnStringMessage(const absl::string_view message)271   void OnStringMessage(const absl::string_view message) override {
272     OSP_LOG_INFO << "got message: " << message;
273     connection->SendString("--echo-- " + std::string(message));
274   }
OnBinaryMessage(const std::vector<uint8_t> & data)275   void OnBinaryMessage(const std::vector<uint8_t>& data) override {}
276 
277   Connection* connection;
278 };
279 
280 class DemoReceiverDelegate final : public ReceiverDelegate {
281  public:
282   ~DemoReceiverDelegate() override = default;
283 
OnUrlAvailabilityRequest(uint64_t client_id,uint64_t request_duration,std::vector<std::string> urls)284   std::vector<msgs::UrlAvailability> OnUrlAvailabilityRequest(
285       uint64_t client_id,
286       uint64_t request_duration,
287       std::vector<std::string> urls) override {
288     std::vector<msgs::UrlAvailability> result;
289     result.reserve(urls.size());
290     for (const auto& url : urls) {
291       OSP_LOG_INFO << "got availability request for: " << url;
292       result.push_back(msgs::UrlAvailability::kAvailable);
293     }
294     return result;
295   }
296 
StartPresentation(const Connection::PresentationInfo & info,uint64_t source_id,const std::vector<msgs::HttpHeader> & http_headers)297   bool StartPresentation(
298       const Connection::PresentationInfo& info,
299       uint64_t source_id,
300       const std::vector<msgs::HttpHeader>& http_headers) override {
301     presentation_id = info.id;
302     connection = std::make_unique<Connection>(info, &cd, Receiver::Get());
303     cd.connection = connection.get();
304     Receiver::Get()->OnPresentationStarted(info.id, connection.get(),
305                                            ResponseResult::kSuccess);
306     return true;
307   }
308 
ConnectToPresentation(uint64_t request_id,const std::string & id,uint64_t source_id)309   bool ConnectToPresentation(uint64_t request_id,
310                              const std::string& id,
311                              uint64_t source_id) override {
312     connection = std::make_unique<Connection>(
313         Connection::PresentationInfo{id, connection->presentation_info().url},
314         &cd, Receiver::Get());
315     cd.connection = connection.get();
316     Receiver::Get()->OnConnectionCreated(request_id, connection.get(),
317                                          ResponseResult::kSuccess);
318     return true;
319   }
320 
TerminatePresentation(const std::string & id,TerminationReason reason)321   void TerminatePresentation(const std::string& id,
322                              TerminationReason reason) override {
323     Receiver::Get()->OnPresentationTerminated(id, reason);
324   }
325 
326   std::string presentation_id;
327   std::unique_ptr<Connection> connection;
328   DemoReceiverConnectionDelegate cd;
329 };
330 
331 struct CommandLineSplit {
332   std::string command;
333   std::string argument_tail;
334 };
335 
SeparateCommandFromArguments(const std::string & line)336 CommandLineSplit SeparateCommandFromArguments(const std::string& line) {
337   size_t split_index = line.find_first_of(' ');
338   // NOTE: |split_index| can be std::string::npos because not all commands
339   // accept arguments.
340   std::string command = line.substr(0, split_index);
341   std::string argument_tail =
342       split_index < line.size() ? line.substr(split_index + 1) : std::string();
343   return {std::move(command), std::move(argument_tail)};
344 }
345 
346 struct CommandWaitResult {
347   bool done;
348   CommandLineSplit command_line;
349 };
350 
WaitForCommand(pollfd * pollfd)351 CommandWaitResult WaitForCommand(pollfd* pollfd) {
352   while (poll(pollfd, 1, 10) >= 0) {
353     if (g_done) {
354       return {true};
355     }
356 
357     if (pollfd->revents == 0) {
358       continue;
359     } else if (pollfd->revents & (POLLERR | POLLHUP)) {
360       return {true};
361     }
362 
363     std::string line;
364     if (!std::getline(std::cin, line)) {
365       return {true};
366     }
367 
368     CommandWaitResult result;
369     result.done = false;
370     result.command_line = SeparateCommandFromArguments(line);
371     return result;
372   }
373   return {true};
374 }
375 
RunControllerPollLoop(Controller * controller)376 void RunControllerPollLoop(Controller* controller) {
377   DemoReceiverObserver receiver_observer;
378   DemoRequestDelegate request_delegate;
379   DemoConnectionDelegate connection_delegate;
380   Controller::ReceiverWatch watch;
381   Controller::ConnectRequest connect_request;
382 
383   pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
384   while (true) {
385     OSP_CHECK_EQ(write(STDOUT_FILENO, "$ ", 2), 2);
386 
387     CommandWaitResult command_result = WaitForCommand(&stdin_pollfd);
388     if (command_result.done) {
389       break;
390     }
391 
392     if (command_result.command_line.command == "avail") {
393       watch = controller->RegisterReceiverWatch(
394           {std::string(command_result.command_line.argument_tail)},
395           &receiver_observer);
396     } else if (command_result.command_line.command == "start") {
397       const absl::string_view& argument_tail =
398           command_result.command_line.argument_tail;
399       size_t next_split = argument_tail.find_first_of(' ');
400       const std::string& service_id = receiver_observer.GetServiceId(
401           std::string(argument_tail.substr(next_split + 1)));
402       const std::string url =
403           static_cast<std::string>(argument_tail.substr(0, next_split));
404       connect_request = controller->StartPresentation(
405           url, service_id, &request_delegate, &connection_delegate);
406     } else if (command_result.command_line.command == "msg") {
407       request_delegate.connection->SendString(
408           command_result.command_line.argument_tail);
409     } else if (command_result.command_line.command == "close") {
410       request_delegate.connection->Close(Connection::CloseReason::kClosed);
411     } else if (command_result.command_line.command == "reconnect") {
412       connect_request = controller->ReconnectConnection(
413           std::move(request_delegate.connection), &request_delegate);
414     } else if (command_result.command_line.command == "term") {
415       request_delegate.connection->Terminate(
416           TerminationReason::kControllerTerminateCalled);
417     }
418   }
419 
420   watch = Controller::ReceiverWatch();
421 }
422 
ListenerDemo()423 void ListenerDemo() {
424   SignalThings();
425 
426   DemoListenerObserver listener_observer;
427   MdnsServiceListenerConfig listener_config;
428   auto mdns_listener = MdnsServiceListenerFactory::Create(
429       listener_config, &listener_observer,
430       PlatformClientPosix::GetInstance()->GetTaskRunner());
431 
432   MessageDemuxer demuxer(Clock::now, MessageDemuxer::kDefaultBufferLimit);
433   DemoConnectionClientObserver client_observer;
434   auto connection_client = ProtocolConnectionClientFactory::Create(
435       &demuxer, &client_observer,
436       PlatformClientPosix::GetInstance()->GetTaskRunner());
437 
438   auto* network_service = NetworkServiceManager::Create(
439       std::move(mdns_listener), nullptr, std::move(connection_client), nullptr);
440   auto controller = std::make_unique<Controller>(Clock::now);
441 
442   network_service->GetMdnsServiceListener()->Start();
443   network_service->GetProtocolConnectionClient()->Start();
444 
445   RunControllerPollLoop(controller.get());
446 
447   network_service->GetMdnsServiceListener()->Stop();
448   network_service->GetProtocolConnectionClient()->Stop();
449 
450   controller.reset();
451 
452   NetworkServiceManager::Dispose();
453 }
454 
HandleReceiverCommand(absl::string_view command,absl::string_view argument_tail,DemoReceiverDelegate & delegate,NetworkServiceManager * manager)455 void HandleReceiverCommand(absl::string_view command,
456                            absl::string_view argument_tail,
457                            DemoReceiverDelegate& delegate,
458                            NetworkServiceManager* manager) {
459   if (command == "avail") {
460     ServicePublisher* publisher = manager->GetMdnsServicePublisher();
461 
462     if (publisher->state() == ServicePublisher::State::kSuspended) {
463       publisher->Resume();
464     } else {
465       publisher->Suspend();
466     }
467   } else if (command == "close") {
468     delegate.connection->Close(Connection::CloseReason::kClosed);
469   } else if (command == "msg") {
470     delegate.connection->SendString(argument_tail);
471   } else if (command == "term") {
472     Receiver::Get()->OnPresentationTerminated(
473         delegate.presentation_id, TerminationReason::kReceiverUserTerminated);
474   } else {
475     OSP_LOG_FATAL << "Received unknown receiver command: " << command;
476   }
477 }
478 
RunReceiverPollLoop(pollfd & file_descriptor,NetworkServiceManager * manager,DemoReceiverDelegate & delegate)479 void RunReceiverPollLoop(pollfd& file_descriptor,
480                          NetworkServiceManager* manager,
481                          DemoReceiverDelegate& delegate) {
482   pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
483   while (true) {
484     OSP_CHECK_EQ(write(STDOUT_FILENO, "$ ", 2), 2);
485 
486     CommandWaitResult command_result = WaitForCommand(&stdin_pollfd);
487     if (command_result.done) {
488       break;
489     }
490 
491     HandleReceiverCommand(command_result.command_line.command,
492                           command_result.command_line.argument_tail, delegate,
493                           manager);
494   }
495 }
496 
CleanupPublisherDemo(NetworkServiceManager * manager)497 void CleanupPublisherDemo(NetworkServiceManager* manager) {
498   Receiver::Get()->SetReceiverDelegate(nullptr);
499   Receiver::Get()->Deinit();
500   manager->GetMdnsServicePublisher()->Stop();
501   manager->GetProtocolConnectionServer()->Stop();
502 
503   NetworkServiceManager::Dispose();
504 }
505 
PublisherDemo(absl::string_view friendly_name)506 void PublisherDemo(absl::string_view friendly_name) {
507   SignalThings();
508 
509   constexpr uint16_t server_port = 6667;
510 
511   DemoPublisherObserver publisher_observer;
512   // TODO(btolsch): aggregate initialization probably better?
513   ServicePublisher::Config publisher_config;
514   publisher_config.friendly_name = std::string(friendly_name);
515   publisher_config.hostname = "turtle-deadbeef";
516   publisher_config.service_instance_name = "deadbeef";
517   publisher_config.connection_server_port = server_port;
518 
519   auto mdns_publisher = MdnsServicePublisherFactory::Create(
520       publisher_config, &publisher_observer,
521       PlatformClientPosix::GetInstance()->GetTaskRunner());
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     }
530   }
531   OSP_LOG_IF(WARN, server_config.connection_endpoints.empty())
532       << "No network interfaces had usable addresses for mDNS publishing.";
533 
534   MessageDemuxer demuxer(Clock::now, MessageDemuxer::kDefaultBufferLimit);
535   DemoConnectionServerObserver server_observer;
536   auto connection_server = ProtocolConnectionServerFactory::Create(
537       server_config, &demuxer, &server_observer,
538       PlatformClientPosix::GetInstance()->GetTaskRunner());
539 
540   auto* network_service =
541       NetworkServiceManager::Create(nullptr, std::move(mdns_publisher), nullptr,
542                                     std::move(connection_server));
543 
544   DemoReceiverDelegate receiver_delegate;
545   Receiver::Get()->Init();
546   Receiver::Get()->SetReceiverDelegate(&receiver_delegate);
547   network_service->GetMdnsServicePublisher()->Start();
548   network_service->GetProtocolConnectionServer()->Start();
549 
550   pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
551 
552   RunReceiverPollLoop(stdin_pollfd, network_service, receiver_delegate);
553 
554   receiver_delegate.connection.reset();
555   CleanupPublisherDemo(network_service);
556 }
557 
558 }  // namespace osp
559 }  // namespace openscreen
560 
561 struct InputArgs {
562   absl::string_view friendly_server_name;
563   bool is_verbose;
564   bool is_help;
565   bool tracing_enabled;
566 };
567 
LogUsage(const char * argv0)568 void LogUsage(const char* argv0) {
569   std::cerr << R"(
570 usage: )" << argv0
571             << R"( <options> <friendly_name>
572 
573     friendly_name
574         Server name, runs the publisher demo. Omission runs the listener demo.
575 
576     -t, --tracing: Enable performance trace logging.
577 
578     -v, --verbose: Enable verbose logging.
579 
580     -h, --help: Show this help message.
581   )";
582 }
583 
GetInputArgs(int argc,char ** argv)584 InputArgs GetInputArgs(int argc, char** argv) {
585   // A note about modifying command line arguments: consider uniformity
586   // between all Open Screen executables. If it is a platform feature
587   // being exposed, consider if it applies to the standalone receiver,
588   // standalone sender, osp demo, and test_main argument options.
589   const struct option kArgumentOptions[] = {
590       {"tracing", no_argument, nullptr, 't'},
591       {"verbose", no_argument, nullptr, 'v'},
592       {"help", no_argument, nullptr, 'h'},
593       {nullptr, 0, nullptr, 0}};
594 
595   InputArgs args = {};
596   int ch = -1;
597   while ((ch = getopt_long(argc, argv, "tvh", kArgumentOptions, nullptr)) !=
598          -1) {
599     switch (ch) {
600       case 't':
601         args.tracing_enabled = true;
602         break;
603 
604       case 'v':
605         args.is_verbose = true;
606         break;
607 
608       case 'h':
609         args.is_help = true;
610         break;
611     }
612   }
613 
614   if (optind < argc) {
615     args.friendly_server_name = argv[optind];
616   }
617 
618   return args;
619 }
620 
main(int argc,char ** argv)621 int main(int argc, char** argv) {
622   using openscreen::Clock;
623   using openscreen::LogLevel;
624   using openscreen::PlatformClientPosix;
625 
626   InputArgs args = GetInputArgs(argc, argv);
627   if (args.is_help) {
628     LogUsage(argv[0]);
629     return 1;
630   }
631 
632   std::unique_ptr<openscreen::TextTraceLoggingPlatform> trace_logging_platform;
633   if (args.tracing_enabled) {
634     trace_logging_platform =
635         std::make_unique<openscreen::TextTraceLoggingPlatform>();
636   }
637 
638   const LogLevel level = args.is_verbose ? LogLevel::kVerbose : LogLevel::kInfo;
639   openscreen::SetLogLevel(level);
640 
641   const bool is_receiver_demo = !args.friendly_server_name.empty();
642   const char* log_filename =
643       is_receiver_demo ? kReceiverLogFilename : kControllerLogFilename;
644   // TODO(jophba): Mac on Mojave hangs on this command forever.
645   openscreen::SetLogFifoOrDie(log_filename);
646 
647   PlatformClientPosix::Create(std::chrono::milliseconds(50),
648                               std::chrono::milliseconds(50));
649 
650   if (is_receiver_demo) {
651     OSP_LOG_INFO << "Running publisher demo...";
652     openscreen::osp::PublisherDemo(args.friendly_server_name);
653   } else {
654     OSP_LOG_INFO << "Running listener demo...";
655     openscreen::osp::ListenerDemo();
656   }
657 
658   PlatformClientPosix::ShutDown();
659 
660   return 0;
661 }
662