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