1 // Copyright (c) 2015-2018 Josh Blum
2 // SPDX-License-Identifier: BSL-1.0
3
4 #include "SoapyServer.hpp"
5 #include "SoapyRemoteDefs.hpp"
6 #include "SoapyURLUtils.hpp"
7 #include "SoapyInfoUtils.hpp"
8 #include "SoapyRPCSocket.hpp"
9 #include "SoapySSDPEndpoint.hpp"
10 #include "SoapyMDNSEndpoint.hpp"
11 #include <cstdlib>
12 #include <cstddef>
13 #include <iostream>
14 #include <getopt.h>
15 #include <csignal>
16
17 /***********************************************************************
18 * Print help message
19 **********************************************************************/
printHelp(void)20 static int printHelp(void)
21 {
22 std::cout << "Usage SoapySDRServer [options]" << std::endl;
23 std::cout << " Options summary:" << std::endl;
24 std::cout << " --help \t\t\t\t Print this help message" << std::endl;
25 std::cout << " --bind \t\t\t\t Bind and serve forever" << std::endl;
26 std::cout << std::endl;
27 return EXIT_SUCCESS;
28 }
29
30 /***********************************************************************
31 * Signal handler for Ctrl + C
32 **********************************************************************/
33 static sig_atomic_t serverDone = false;
sigIntHandler(const int)34 void sigIntHandler(const int)
35 {
36 std::cout << "Caught Ctrl+C, shutting down the server..." << std::endl;
37 serverDone = true;
38 }
39
40 /***********************************************************************
41 * Launch the server
42 **********************************************************************/
runServer(void)43 static int runServer(void)
44 {
45 SoapySocketSession sess;
46 const bool isIPv6Supported = not SoapyRPCSocket(SoapyURL("tcp", "::", "0").toString()).null();
47 const auto defaultBindNode = isIPv6Supported?"::":"0.0.0.0";
48 const int ipVerServices = isIPv6Supported?SOAPY_REMOTE_IPVER_UNSPEC:SOAPY_REMOTE_IPVER_INET;
49
50 //extract url from user input or generate automatically
51 const bool optargHasURL = (optarg != NULL and not std::string(optarg).empty());
52 auto url = (optargHasURL)? SoapyURL(optarg) : SoapyURL("tcp", defaultBindNode, "");
53
54 //default url parameters when not specified
55 if (url.getScheme().empty()) url.setScheme("tcp");
56 if (url.getService().empty()) url.setService(SOAPY_REMOTE_DEFAULT_SERVICE);
57
58 //this UUID identifies the server process
59 const auto serverUUID = SoapyInfo::generateUUID1();
60 std::cout << "Server version: " << SoapyInfo::getServerVersion() << std::endl;
61 std::cout << "Server UUID: " << serverUUID << std::endl;
62
63 std::cout << "Launching the server... " << url.toString() << std::endl;
64 SoapyRPCSocket s;
65 if (s.bind(url.toString()) != 0)
66 {
67 std::cerr << "Server socket bind FAIL: " << s.lastErrorMsg() << std::endl;
68 return EXIT_FAILURE;
69 }
70 std::cout << "Server bound to " << s.getsockname() << std::endl;
71 s.listen(SOAPY_REMOTE_LISTEN_BACKLOG);
72 auto serverListener = new SoapyServerListener(s, serverUUID);
73
74 std::cout << "Launching discovery server... " << std::endl;
75 auto ssdpEndpoint = new SoapySSDPEndpoint();
76 ssdpEndpoint->registerService(serverUUID, url.getService(), ipVerServices);
77
78 std::cout << "Connecting to DNS-SD daemon... " << std::endl;
79 auto dnssdPublish = new SoapyMDNSEndpoint();
80 dnssdPublish->printInfo();
81 dnssdPublish->registerService(serverUUID, url.getService(), ipVerServices);
82
83 std::cout << "Press Ctrl+C to stop the server" << std::endl;
84 signal(SIGINT, sigIntHandler);
85 bool exitFailure = false;
86 while (not serverDone and not exitFailure)
87 {
88 serverListener->handleOnce();
89 if (not s.status())
90 {
91 std::cerr << "Server socket failure: " << s.lastErrorMsg() << std::endl;
92 exitFailure = true;
93 }
94 if (not dnssdPublish->status())
95 {
96 std::cerr << "DNS-SD daemon disconnected..." << std::endl;
97 exitFailure = true;
98 }
99 }
100 if (exitFailure) std::cerr << "Exiting prematurely..." << std::endl;
101
102 delete ssdpEndpoint;
103 delete dnssdPublish;
104
105 std::cout << "Shutdown client handler threads" << std::endl;
106 delete serverListener;
107 s.close();
108
109 std::cout << "Cleanup complete, exiting" << std::endl;
110 return exitFailure?EXIT_FAILURE:EXIT_SUCCESS;
111 }
112
113 /***********************************************************************
114 * Parse and dispatch options
115 **********************************************************************/
main(int argc,char * argv[])116 int main(int argc, char *argv[])
117 {
118 std::cout << "######################################################" << std::endl;
119 std::cout << "## Soapy Server -- Use any Soapy SDR remotely" << std::endl;
120 std::cout << "######################################################" << std::endl;
121 std::cout << std::endl;
122
123 /*******************************************************************
124 * parse command line options
125 ******************************************************************/
126 static struct option long_options[] = {
127 {"help", no_argument, 0, 'h'},
128 {"bind", optional_argument, 0, 'b'},
129 {0, 0, 0, 0}
130 };
131 int long_index = 0;
132 int option = 0;
133 while ((option = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1)
134 {
135 switch (option)
136 {
137 case 'h': return printHelp();
138 case 'b': return runServer();
139 }
140 }
141
142 //unknown or unspecified options, do help...
143 return printHelp();
144 }
145