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