summaryrefslogtreecommitdiff
path: root/utils/hwstub/lib/hwstub_uri.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwstub/lib/hwstub_uri.cpp')
-rw-r--r--utils/hwstub/lib/hwstub_uri.cpp332
1 files changed, 332 insertions, 0 deletions
diff --git a/utils/hwstub/lib/hwstub_uri.cpp b/utils/hwstub/lib/hwstub_uri.cpp
new file mode 100644
index 0000000000..e2f252f3dc
--- /dev/null
+++ b/utils/hwstub/lib/hwstub_uri.cpp
@@ -0,0 +1,332 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2015 by Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "hwstub_uri.hpp"
22#include "hwstub_usb.hpp"
23#include "hwstub_virtual.hpp"
24#include "hwstub_net.hpp"
25#include <cctype>
26
27namespace hwstub {
28namespace uri {
29
30void print_usage(FILE *f, bool client, bool server)
31{
32 if(client && server)
33 fprintf(f, "A context/server");
34 else if(client)
35 fprintf(f, "A context");
36 else
37 fprintf(f, "A server");
38 fprintf(f, " URI has the following format:\n");
39 fprintf(f, " scheme:[//domain[:port]][/]\n");
40 fprintf(f, "The scheme is mandatory and specifies the type of context to create.\n");
41 fprintf(f, "The following scheme are recognized:\n");
42 if(client)
43 fprintf(f, " usb USB context (using libusb)\n");
44 fprintf(f, " tcp TCP context\n");
45 fprintf(f, " unix Local unix domain context\n");
46 if(client)
47 fprintf(f, " virt A virtual context (testing and debugging mostly)\n");
48 fprintf(f, " default Default choice made by the library\n");
49 if(client)
50 {
51 fprintf(f, "When creating a USB context, the domain and port must be empty:\n");
52 fprintf(f, " usb:\n");
53 }
54 fprintf(f, "When creating a TCP context, the domain and port are the usual TCP parameters:\n");
55 fprintf(f, " tcp://localhost\n");
56 fprintf(f, " tcp://localhost:6666\n");
57 fprintf(f, "The port is optional, in which the default port %s is used.\n", hwstub::net::context::default_tcp_port().c_str());
58 fprintf(f, "When creating a unix domain context, the port must be empty and there are two type of unix domains.\n");
59 fprintf(f, "Normal domains are specified by filesystem paths. Abstract domains (Linux-only) can be arbitrary strings,\n");
60 fprintf(f, "in which case the domain must start with a '#':\n");
61 fprintf(f, " unix:///path/to/socket\n");
62 fprintf(f, " unix://#hwstub\n");
63 if(client)
64 {
65 fprintf(f, "When creating a virtual context, the domain must contain a specification of the devices.\n");
66 fprintf(f, "The device list is of the form type(param);type(param);... where the only supported type\n");
67 fprintf(f, "at the moment is 'dummy' with a single parameter which is the device name:\n");
68 fprintf(f, " virt://dummy(Device A);dummy(Device B);dummy(Super device C)\n");
69 }
70 if(server && client)
71 fprintf(f, "Note that usb and virt schemes are not supported for server URIs.\n");
72}
73
74uri::uri(const std::string& uri)
75 :m_uri(uri), m_valid(false)
76{
77 parse();
78}
79
80bool uri::validate_scheme()
81{
82 /* scheme must be nonempty */
83 if(m_scheme.empty())
84 {
85 m_error = "empty scheme";
86 return false;
87 }
88 /* and contain alphanumeric characters or '+', '-' or '.' */
89 for(auto c : m_scheme)
90 if(!isalnum(c) && c != '+' && c != '-' && c != '.')
91 {
92 m_error = std::string("invalid character '") + c + "' in scheme";
93 return false;
94 }
95 return true;
96}
97
98bool uri::validate_domain()
99{
100 return true;
101}
102
103bool uri::validate_port()
104{
105 return true;
106}
107
108void uri::parse()
109{
110 std::string str = m_uri;
111 /* try to find the first ':' */
112 size_t scheme_colon = str.find(':');
113 if(scheme_colon == std::string::npos)
114 {
115 m_error = "URI contains no scheme";
116 return;
117 }
118 /* found scheme */
119 m_scheme = str.substr(0, scheme_colon);
120 /* validate scheme */
121 if(!validate_scheme())
122 return;
123 /* remove scheme: */
124 str = str.substr(scheme_colon + 1);
125 /* check if it begins with // */
126 if(str.size() >= 2 && str[0] == '/' && str[1] == '/')
127 {
128 /* remove it */
129 str = str.substr(2);
130 /* find next '/' */
131 std::string path;
132 size_t next_slash = str.find('/');
133 /* if none, we can assume the path is empty, otherwise split path */
134 if(next_slash != std::string::npos)
135 {
136 path = str.substr(next_slash);
137 str = str.substr(0, next_slash);
138 }
139 /* find last ':' if any, indicating a port */
140 size_t port_colon = str.rfind(':');
141 if(port_colon != std::string::npos)
142 {
143 m_domain = str.substr(0, port_colon);
144 m_port = str.substr(port_colon + 1);
145 }
146 else
147 m_domain = str;
148 if(!validate_domain() || !validate_port())
149 return;
150 /* pursue with path */
151 str = path;
152 }
153 /* next is path */
154 m_path = str;
155 m_valid = true;
156}
157
158bool uri::valid() const
159{
160 return m_valid;
161}
162
163std::string uri::error() const
164{
165 return m_error;
166}
167
168std::string uri::full_uri() const
169{
170 return m_uri;
171}
172
173std::string uri::scheme() const
174{
175 return m_scheme;
176}
177
178std::string uri::domain() const
179{
180 return m_domain;
181}
182
183std::string uri::port() const
184{
185 return m_port;
186}
187
188std::string uri::path() const
189{
190 return m_path;
191}
192
193/** Context creator */
194
195std::shared_ptr<context> create_default_context(std::string *error)
196{
197 /* first try to create a unix context with abstract name 'hwstub' */
198 std::shared_ptr<context> ctx = hwstub::net::context::create_unix_abstract(
199 hwstub::net::context::default_unix_path(), error);
200 if(ctx)
201 return ctx;
202 /* otherwise try default tcp */
203 ctx = hwstub::net::context::create_tcp(hwstub::net::context::default_tcp_domain(),
204 hwstub::net::context::default_tcp_port(), error);
205 if(ctx)
206 return ctx;
207 /* otherwise default to usb */
208 return hwstub::usb::context::create(nullptr, false, error);
209}
210
211std::shared_ptr<context> create_context(const uri& uri, std::string *error)
212{
213 /* check URI is valid */
214 if(!uri.valid())
215 {
216 if(error)
217 *error = "invalid URI: " + uri.error();
218 return std::shared_ptr<context>();
219 }
220 /* handle different types of contexts */
221 if(uri.scheme() == "usb")
222 {
223 /* domain and port must be empty */
224 if(!uri.domain().empty() || !uri.port().empty())
225 {
226 if(error)
227 *error = "USB URI cannot contain a domain or a port";
228 return std::shared_ptr<context>();
229 }
230 /* in doubt, create a new libusb context and let the context destroy it */
231 libusb_context *ctx;
232 libusb_init(&ctx);
233 return hwstub::usb::context::create(ctx, true);
234 }
235 else if(uri.scheme() == "virt")
236 {
237 /* port must be empty */
238 if(!uri.port().empty())
239 {
240 if(error)
241 *error = "virt URI cannot contain a port";
242 return std::shared_ptr<context>();
243 }
244 return hwstub::virt::context::create_spec(uri.domain(), error);
245 }
246 else if(uri.scheme() == "tcp")
247 {
248 return hwstub::net::context::create_tcp(uri.domain(), uri.port(), error);
249 }
250 else if(uri.scheme() == "unix")
251 {
252 /* port must be empty */
253 if(!uri.port().empty())
254 {
255 if(error)
256 *error = "unix URI cannot contain a port";
257 return std::shared_ptr<context>();
258 }
259 if(!uri.domain().empty() && uri.domain()[0] == '#')
260 return hwstub::net::context::create_unix_abstract(uri.domain().substr(1), error);
261 else
262 return hwstub::net::context::create_unix(uri.domain(), error);
263 }
264 else if(uri.scheme() == "default")
265 {
266 /* domain and port must be empty */
267 if(!uri.domain().empty() || !uri.port().empty())
268 {
269 if(error)
270 *error = "Default URI cannot contain a domain or a port";
271 return std::shared_ptr<context>();
272 }
273 return create_default_context(error);
274 }
275 else
276 {
277 if(error)
278 *error = "unknown scheme '" + uri.scheme() + "'";
279 return std::shared_ptr<context>();
280 }
281}
282
283std::shared_ptr<net::server> create_server(std::shared_ptr<context> ctx,
284 const uri& uri, std::string *error)
285{
286 /* check URI is valid */
287 if(!uri.valid())
288 {
289 if(error)
290 *error = "invalid URI: " + uri.error();
291 return std::shared_ptr<net::server>();
292 }
293 /* handle different types of contexts */
294 if(uri.scheme() == "unix")
295 {
296 /* port must be empty */
297 if(!uri.port().empty())
298 {
299 if(error)
300 *error = "unix URI cannot contain a port";
301 return std::shared_ptr<net::server>();
302 }
303 if(!uri.domain().empty() && uri.domain()[0] == '#')
304 return hwstub::net::server::create_unix_abstract(ctx, uri.domain().substr(1), error);
305 else
306 return hwstub::net::server::create_unix(ctx, uri.domain(), error);
307 }
308 else if(uri.scheme() == "tcp")
309 {
310 return hwstub::net::server::create_tcp(ctx, uri.domain(), uri.port(), error);
311 }
312 else
313 {
314 if(error)
315 *error = "unknown scheme '" + uri.scheme() + "'";
316 return std::shared_ptr<net::server>();
317 }
318}
319
320uri default_uri()
321{
322 return uri("default:");
323}
324
325uri default_server_uri()
326{
327 return uri("unix://#" + hwstub::net::context::default_unix_path());
328}
329
330} // namespace uri
331} // namespace hwstub
332