Commit | Line | Data |
---|---|---|
0945b4fe | 1 | /* |
e2ec6b4e | 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> |
3 | * 2005-2007 Takahiro Hirofuchi | |
3391ba0e KO |
4 | * Copyright (C) 2015-2016 Samsung Electronics |
5 | * Igor Kotrasinski <i.kotrasinsk@samsung.com> | |
6 | * Krzysztof Opasiak <k.opasiak@samsung.com> | |
0945b4fe | 7 | * |
e2ec6b4e | 8 | * This program is free software: you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation, either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
0945b4fe TH |
20 | */ |
21 | ||
22 | #ifdef HAVE_CONFIG_H | |
23 | #include "../config.h" | |
24 | #endif | |
25 | ||
328f7f8a | 26 | #define _GNU_SOURCE |
d2c15e25 | 27 | #include <errno.h> |
0945b4fe TH |
28 | #include <unistd.h> |
29 | #include <netdb.h> | |
950a4cd8 | 30 | #include <string.h> |
0945b4fe TH |
31 | #include <stdlib.h> |
32 | #include <sys/types.h> | |
33 | #include <sys/stat.h> | |
34 | #include <arpa/inet.h> | |
35 | #include <sys/socket.h> | |
36 | #include <netinet/in.h> | |
37 | ||
38 | #ifdef HAVE_LIBWRAP | |
39 | #include <tcpd.h> | |
40 | #endif | |
41 | ||
0945b4fe TH |
42 | #include <getopt.h> |
43 | #include <signal.h> | |
328f7f8a | 44 | #include <poll.h> |
0945b4fe | 45 | |
7104b5df | 46 | #include "usbip_host_driver.h" |
3391ba0e | 47 | #include "usbip_host_common.h" |
e0546fd8 | 48 | #include "usbip_device_driver.h" |
d2c15e25 | 49 | #include "usbip_common.h" |
0945b4fe | 50 | #include "usbip_network.h" |
021aed84 | 51 | #include "list.h" |
0945b4fe | 52 | |
e2ec6b4e | 53 | #undef PROGNAME |
54 | #define PROGNAME "usbipd" | |
55 | #define MAXSOCKFD 20 | |
56 | ||
328f7f8a | 57 | #define MAIN_LOOP_TIMEOUT 10 |
0945b4fe | 58 | |
9ead219b AF |
59 | #define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid" |
60 | ||
e2ec6b4e | 61 | static const char usbip_version_string[] = PACKAGE_STRING; |
62 | ||
63 | static const char usbipd_help_string[] = | |
04948a34 | 64 | "usage: usbipd [options]\n" |
f49ad35c DP |
65 | "\n" |
66 | " -4, --ipv4\n" | |
67 | " Bind to IPv4. Default is both.\n" | |
68 | "\n" | |
69 | " -6, --ipv6\n" | |
70 | " Bind to IPv6. Default is both.\n" | |
71 | "\n" | |
e0546fd8 IK |
72 | " -e, --device\n" |
73 | " Run in device mode.\n" | |
74 | " Rather than drive an attached device, create\n" | |
75 | " a virtual UDC to bind gadgets to.\n" | |
76 | "\n" | |
04948a34 AF |
77 | " -D, --daemon\n" |
78 | " Run as a daemon process.\n" | |
79 | "\n" | |
80 | " -d, --debug\n" | |
81 | " Print debugging information.\n" | |
82 | "\n" | |
9ead219b AF |
83 | " -PFILE, --pid FILE\n" |
84 | " Write process id to FILE.\n" | |
85 | " If no FILE specified, use " DEFAULT_PID_FILE "\n" | |
86 | "\n" | |
7182f8f8 AF |
87 | " -tPORT, --tcp-port PORT\n" |
88 | " Listen on TCP/IP port PORT.\n" | |
89 | "\n" | |
04948a34 AF |
90 | " -h, --help\n" |
91 | " Print this help.\n" | |
92 | "\n" | |
93 | " -v, --version\n" | |
94 | " Show version.\n"; | |
e2ec6b4e | 95 | |
3391ba0e KO |
96 | static struct usbip_host_driver *driver; |
97 | ||
e2ec6b4e | 98 | static void usbipd_help(void) |
0945b4fe | 99 | { |
e2ec6b4e | 100 | printf("%s\n", usbipd_help_string); |
101 | } | |
102 | ||
103 | static int recv_request_import(int sockfd) | |
104 | { | |
105 | struct op_import_request req; | |
0945b4fe | 106 | struct usbip_exported_device *edev; |
e2ec6b4e | 107 | struct usbip_usb_device pdu_udev; |
021aed84 | 108 | struct list_head *i; |
e2ec6b4e | 109 | int found = 0; |
ad81b15d | 110 | int status = ST_OK; |
e2ec6b4e | 111 | int rc; |
0945b4fe | 112 | |
e2ec6b4e | 113 | memset(&req, 0, sizeof(req)); |
0945b4fe | 114 | |
3c6e9e8f | 115 | rc = usbip_net_recv(sockfd, &req, sizeof(req)); |
e2ec6b4e | 116 | if (rc < 0) { |
3c6e9e8f | 117 | dbg("usbip_net_recv failed: import request"); |
e2ec6b4e | 118 | return -1; |
119 | } | |
120 | PACK_OP_IMPORT_REQUEST(0, &req); | |
0945b4fe | 121 | |
3391ba0e | 122 | list_for_each(i, &driver->edev_list) { |
021aed84 | 123 | edev = list_entry(i, struct usbip_exported_device, node); |
e2ec6b4e | 124 | if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) { |
125 | info("found requested device: %s", req.busid); | |
126 | found = 1; | |
127 | break; | |
128 | } | |
0945b4fe TH |
129 | } |
130 | ||
e2ec6b4e | 131 | if (found) { |
132 | /* should set TCP_NODELAY for usbip */ | |
3c6e9e8f | 133 | usbip_net_set_nodelay(sockfd); |
0945b4fe | 134 | |
e2ec6b4e | 135 | /* export device needs a TCP/IP socket descriptor */ |
ad81b15d SK |
136 | status = usbip_export_device(edev, sockfd); |
137 | if (status < 0) | |
138 | status = ST_NA; | |
e2ec6b4e | 139 | } else { |
140 | info("requested device not found: %s", req.busid); | |
ad81b15d | 141 | status = ST_NODEV; |
0945b4fe TH |
142 | } |
143 | ||
ad81b15d | 144 | rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, status); |
e2ec6b4e | 145 | if (rc < 0) { |
3c6e9e8f | 146 | dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT); |
e2ec6b4e | 147 | return -1; |
148 | } | |
149 | ||
ad81b15d | 150 | if (status) { |
e2ec6b4e | 151 | dbg("import request busid %s: failed", req.busid); |
152 | return -1; | |
153 | } | |
154 | ||
155 | memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); | |
3c6e9e8f | 156 | usbip_net_pack_usb_device(1, &pdu_udev); |
e2ec6b4e | 157 | |
3c6e9e8f | 158 | rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev)); |
e2ec6b4e | 159 | if (rc < 0) { |
3c6e9e8f | 160 | dbg("usbip_net_send failed: devinfo"); |
e2ec6b4e | 161 | return -1; |
0945b4fe TH |
162 | } |
163 | ||
e2ec6b4e | 164 | dbg("import request busid %s: complete", req.busid); |
165 | ||
166 | return 0; | |
167 | } | |
0945b4fe | 168 | |
e2ec6b4e | 169 | static int send_reply_devlist(int connfd) |
170 | { | |
171 | struct usbip_exported_device *edev; | |
172 | struct usbip_usb_device pdu_udev; | |
29a30ad7 | 173 | struct usbip_usb_interface pdu_uinf; |
e2ec6b4e | 174 | struct op_devlist_reply reply; |
021aed84 | 175 | struct list_head *j; |
29a30ad7 | 176 | int rc, i; |
e2ec6b4e | 177 | |
178 | reply.ndev = 0; | |
179 | /* number of exported devices */ | |
3391ba0e | 180 | list_for_each(j, &driver->edev_list) { |
e2ec6b4e | 181 | reply.ndev += 1; |
182 | } | |
183 | info("exportable devices: %d", reply.ndev); | |
184 | ||
3c6e9e8f | 185 | rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK); |
e2ec6b4e | 186 | if (rc < 0) { |
3c6e9e8f | 187 | dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST); |
e2ec6b4e | 188 | return -1; |
189 | } | |
190 | PACK_OP_DEVLIST_REPLY(1, &reply); | |
191 | ||
3c6e9e8f | 192 | rc = usbip_net_send(connfd, &reply, sizeof(reply)); |
e2ec6b4e | 193 | if (rc < 0) { |
3c6e9e8f | 194 | dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST); |
e2ec6b4e | 195 | return -1; |
196 | } | |
197 | ||
3391ba0e | 198 | list_for_each(j, &driver->edev_list) { |
021aed84 | 199 | edev = list_entry(j, struct usbip_exported_device, node); |
0945b4fe TH |
200 | dump_usb_device(&edev->udev); |
201 | memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); | |
3c6e9e8f | 202 | usbip_net_pack_usb_device(1, &pdu_udev); |
0945b4fe | 203 | |
3c6e9e8f | 204 | rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev)); |
e2ec6b4e | 205 | if (rc < 0) { |
3c6e9e8f | 206 | dbg("usbip_net_send failed: pdu_udev"); |
e2ec6b4e | 207 | return -1; |
0945b4fe | 208 | } |
29a30ad7 VM |
209 | |
210 | for (i = 0; i < edev->udev.bNumInterfaces; i++) { | |
211 | dump_usb_interface(&edev->uinf[i]); | |
212 | memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf)); | |
213 | usbip_net_pack_usb_interface(1, &pdu_uinf); | |
214 | ||
215 | rc = usbip_net_send(connfd, &pdu_uinf, | |
216 | sizeof(pdu_uinf)); | |
217 | if (rc < 0) { | |
218 | err("usbip_net_send failed: pdu_uinf"); | |
219 | return -1; | |
220 | } | |
221 | } | |
0945b4fe TH |
222 | } |
223 | ||
224 | return 0; | |
225 | } | |
226 | ||
e2ec6b4e | 227 | static int recv_request_devlist(int connfd) |
0945b4fe | 228 | { |
0945b4fe | 229 | struct op_devlist_request req; |
e2ec6b4e | 230 | int rc; |
0945b4fe | 231 | |
950a4cd8 | 232 | memset(&req, 0, sizeof(req)); |
0945b4fe | 233 | |
3c6e9e8f | 234 | rc = usbip_net_recv(connfd, &req, sizeof(req)); |
e2ec6b4e | 235 | if (rc < 0) { |
3c6e9e8f | 236 | dbg("usbip_net_recv failed: devlist request"); |
0945b4fe TH |
237 | return -1; |
238 | } | |
239 | ||
e2ec6b4e | 240 | rc = send_reply_devlist(connfd); |
241 | if (rc < 0) { | |
242 | dbg("send_reply_devlist failed"); | |
0945b4fe TH |
243 | return -1; |
244 | } | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
e2ec6b4e | 249 | static int recv_pdu(int connfd) |
0945b4fe | 250 | { |
e2ec6b4e | 251 | uint16_t code = OP_UNSPEC; |
0945b4fe | 252 | int ret; |
ad81b15d | 253 | int status; |
0945b4fe | 254 | |
ad81b15d | 255 | ret = usbip_net_recv_op_common(connfd, &code, &status); |
0945b4fe | 256 | if (ret < 0) { |
e2ec6b4e | 257 | dbg("could not receive opcode: %#0x", code); |
0945b4fe TH |
258 | return -1; |
259 | } | |
260 | ||
3391ba0e | 261 | ret = usbip_refresh_device_list(driver); |
e2ec6b4e | 262 | if (ret < 0) { |
263 | dbg("could not refresh device list: %d", ret); | |
264 | return -1; | |
0945b4fe TH |
265 | } |
266 | ||
e2ec6b4e | 267 | info("received request: %#0x(%d)", code, connfd); |
268 | switch (code) { | |
269 | case OP_REQ_DEVLIST: | |
270 | ret = recv_request_devlist(connfd); | |
271 | break; | |
272 | case OP_REQ_IMPORT: | |
273 | ret = recv_request_import(connfd); | |
274 | break; | |
275 | case OP_REQ_DEVINFO: | |
276 | case OP_REQ_CRYPKEY: | |
277 | default: | |
278 | err("received an unknown opcode: %#0x", code); | |
279 | ret = -1; | |
0945b4fe TH |
280 | } |
281 | ||
e2ec6b4e | 282 | if (ret == 0) |
283 | info("request %#0x(%d): complete", code, connfd); | |
284 | else | |
285 | info("request %#0x(%d): failed", code, connfd); | |
0945b4fe | 286 | |
e2ec6b4e | 287 | return ret; |
288 | } | |
0945b4fe | 289 | |
e2ec6b4e | 290 | #ifdef HAVE_LIBWRAP |
291 | static int tcpd_auth(int connfd) | |
292 | { | |
293 | struct request_info request; | |
294 | int rc; | |
0945b4fe | 295 | |
e2ec6b4e | 296 | request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0); |
297 | fromhost(&request); | |
298 | rc = hosts_access(&request); | |
299 | if (rc == 0) | |
300 | return -1; | |
0945b4fe TH |
301 | |
302 | return 0; | |
303 | } | |
e2ec6b4e | 304 | #endif |
0945b4fe | 305 | |
e2ec6b4e | 306 | static int do_accept(int listenfd) |
0945b4fe | 307 | { |
e2ec6b4e | 308 | int connfd; |
309 | struct sockaddr_storage ss; | |
310 | socklen_t len = sizeof(ss); | |
311 | char host[NI_MAXHOST], port[NI_MAXSERV]; | |
312 | int rc; | |
0945b4fe | 313 | |
e2ec6b4e | 314 | memset(&ss, 0, sizeof(ss)); |
0945b4fe | 315 | |
04948a34 | 316 | connfd = accept(listenfd, (struct sockaddr *)&ss, &len); |
e2ec6b4e | 317 | if (connfd < 0) { |
318 | err("failed to accept connection"); | |
319 | return -1; | |
0945b4fe TH |
320 | } |
321 | ||
04948a34 | 322 | rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host), |
e2ec6b4e | 323 | port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); |
324 | if (rc) | |
325 | err("getnameinfo: %s", gai_strerror(rc)); | |
0945b4fe | 326 | |
e2ec6b4e | 327 | #ifdef HAVE_LIBWRAP |
328 | rc = tcpd_auth(connfd); | |
329 | if (rc < 0) { | |
330 | info("denied access from %s", host); | |
331 | close(connfd); | |
0945b4fe | 332 | return -1; |
e2ec6b4e | 333 | } |
334 | #endif | |
335 | info("connection from %s:%s", host, port); | |
0945b4fe | 336 | |
e2ec6b4e | 337 | return connfd; |
338 | } | |
0945b4fe | 339 | |
328f7f8a | 340 | int process_request(int listenfd) |
e2ec6b4e | 341 | { |
328f7f8a | 342 | pid_t childpid; |
e2ec6b4e | 343 | int connfd; |
0945b4fe | 344 | |
328f7f8a IH |
345 | connfd = do_accept(listenfd); |
346 | if (connfd < 0) | |
347 | return -1; | |
348 | childpid = fork(); | |
349 | if (childpid == 0) { | |
350 | close(listenfd); | |
e2ec6b4e | 351 | recv_pdu(connfd); |
328f7f8a | 352 | exit(0); |
e2ec6b4e | 353 | } |
328f7f8a IH |
354 | close(connfd); |
355 | return 0; | |
e2ec6b4e | 356 | } |
0945b4fe | 357 | |
a159d620 AF |
358 | static void addrinfo_to_text(struct addrinfo *ai, char buf[], |
359 | const size_t buf_size) | |
0945b4fe | 360 | { |
0945b4fe TH |
361 | char hbuf[NI_MAXHOST]; |
362 | char sbuf[NI_MAXSERV]; | |
e2ec6b4e | 363 | int rc; |
0945b4fe | 364 | |
a159d620 AF |
365 | buf[0] = '\0'; |
366 | ||
e2ec6b4e | 367 | rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), |
368 | sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); | |
369 | if (rc) | |
370 | err("getnameinfo: %s", gai_strerror(rc)); | |
0945b4fe | 371 | |
a159d620 | 372 | snprintf(buf, buf_size, "%s:%s", hbuf, sbuf); |
0945b4fe TH |
373 | } |
374 | ||
f49ad35c DP |
375 | static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[], |
376 | int maxsockfd) | |
0945b4fe TH |
377 | { |
378 | struct addrinfo *ai; | |
e2ec6b4e | 379 | int ret, nsockfd = 0; |
a159d620 AF |
380 | const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2; |
381 | char ai_buf[ai_buf_size]; | |
0945b4fe | 382 | |
f49ad35c | 383 | for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) { |
a159d620 | 384 | int sock; |
3eed8c03 | 385 | |
a159d620 AF |
386 | addrinfo_to_text(ai, ai_buf, ai_buf_size); |
387 | dbg("opening %s", ai_buf); | |
388 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | |
389 | if (sock < 0) { | |
390 | err("socket: %s: %d (%s)", | |
391 | ai_buf, errno, strerror(errno)); | |
0945b4fe | 392 | continue; |
a159d620 | 393 | } |
0945b4fe | 394 | |
2568dd1b AF |
395 | usbip_net_set_reuseaddr(sock); |
396 | usbip_net_set_nodelay(sock); | |
f49ad35c DP |
397 | /* We use seperate sockets for IPv4 and IPv6 |
398 | * (see do_standalone_mode()) */ | |
399 | usbip_net_set_v6only(sock); | |
0945b4fe | 400 | |
2568dd1b | 401 | ret = bind(sock, ai->ai_addr, ai->ai_addrlen); |
0945b4fe | 402 | if (ret < 0) { |
a159d620 AF |
403 | err("bind: %s: %d (%s)", |
404 | ai_buf, errno, strerror(errno)); | |
2568dd1b | 405 | close(sock); |
0945b4fe TH |
406 | continue; |
407 | } | |
408 | ||
2568dd1b | 409 | ret = listen(sock, SOMAXCONN); |
0945b4fe | 410 | if (ret < 0) { |
a159d620 AF |
411 | err("listen: %s: %d (%s)", |
412 | ai_buf, errno, strerror(errno)); | |
2568dd1b | 413 | close(sock); |
0945b4fe TH |
414 | continue; |
415 | } | |
416 | ||
a159d620 | 417 | info("listening on %s", ai_buf); |
2568dd1b | 418 | sockfdlist[nsockfd++] = sock; |
0945b4fe TH |
419 | } |
420 | ||
e2ec6b4e | 421 | return nsockfd; |
0945b4fe | 422 | } |
0945b4fe | 423 | |
e2ec6b4e | 424 | static struct addrinfo *do_getaddrinfo(char *host, int ai_family) |
0945b4fe | 425 | { |
e2ec6b4e | 426 | struct addrinfo hints, *ai_head; |
427 | int rc; | |
0945b4fe | 428 | |
e2ec6b4e | 429 | memset(&hints, 0, sizeof(hints)); |
430 | hints.ai_family = ai_family; | |
431 | hints.ai_socktype = SOCK_STREAM; | |
432 | hints.ai_flags = AI_PASSIVE; | |
0945b4fe | 433 | |
7182f8f8 | 434 | rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head); |
e2ec6b4e | 435 | if (rc) { |
7182f8f8 | 436 | err("failed to get a network address %s: %s", usbip_port_string, |
e2ec6b4e | 437 | gai_strerror(rc)); |
438 | return NULL; | |
0945b4fe | 439 | } |
0945b4fe | 440 | |
e2ec6b4e | 441 | return ai_head; |
0945b4fe TH |
442 | } |
443 | ||
0945b4fe TH |
444 | static void signal_handler(int i) |
445 | { | |
328f7f8a | 446 | dbg("received '%s' signal", strsignal(i)); |
0945b4fe TH |
447 | } |
448 | ||
449 | static void set_signal(void) | |
450 | { | |
451 | struct sigaction act; | |
452 | ||
950a4cd8 | 453 | memset(&act, 0, sizeof(act)); |
0945b4fe TH |
454 | act.sa_handler = signal_handler; |
455 | sigemptyset(&act.sa_mask); | |
456 | sigaction(SIGTERM, &act, NULL); | |
457 | sigaction(SIGINT, &act, NULL); | |
328f7f8a | 458 | act.sa_handler = SIG_IGN; |
77be4c87 | 459 | sigaction(SIGCHLD, &act, NULL); |
0945b4fe TH |
460 | } |
461 | ||
9ead219b AF |
462 | static const char *pid_file; |
463 | ||
19495513 | 464 | static void write_pid_file(void) |
9ead219b AF |
465 | { |
466 | if (pid_file) { | |
467 | dbg("creating pid file %s", pid_file); | |
3eed8c03 PL |
468 | FILE *fp; |
469 | ||
470 | fp = fopen(pid_file, "w"); | |
9ead219b AF |
471 | if (!fp) { |
472 | err("pid_file: %s: %d (%s)", | |
473 | pid_file, errno, strerror(errno)); | |
474 | return; | |
475 | } | |
476 | fprintf(fp, "%d\n", getpid()); | |
477 | fclose(fp); | |
478 | } | |
479 | } | |
480 | ||
19495513 | 481 | static void remove_pid_file(void) |
9ead219b AF |
482 | { |
483 | if (pid_file) { | |
484 | dbg("removing pid file %s", pid_file); | |
485 | unlink(pid_file); | |
486 | } | |
487 | } | |
488 | ||
f49ad35c | 489 | static int do_standalone_mode(int daemonize, int ipv4, int ipv6) |
0945b4fe | 490 | { |
0945b4fe | 491 | struct addrinfo *ai_head; |
e2ec6b4e | 492 | int sockfdlist[MAXSOCKFD]; |
f49ad35c | 493 | int nsockfd, family; |
328f7f8a IH |
494 | int i, terminate; |
495 | struct pollfd *fds; | |
496 | struct timespec timeout; | |
497 | sigset_t sigmask; | |
0945b4fe | 498 | |
3391ba0e | 499 | if (usbip_driver_open(driver)) |
e2ec6b4e | 500 | return -1; |
0945b4fe TH |
501 | |
502 | if (daemonize) { | |
5037307d | 503 | if (daemon(0, 0) < 0) { |
e2ec6b4e | 504 | err("daemonizing failed: %s", strerror(errno)); |
3391ba0e | 505 | usbip_driver_close(driver); |
e2ec6b4e | 506 | return -1; |
507 | } | |
328f7f8a | 508 | umask(0); |
0945b4fe TH |
509 | usbip_use_syslog = 1; |
510 | } | |
0945b4fe | 511 | set_signal(); |
9ead219b | 512 | write_pid_file(); |
0945b4fe | 513 | |
f49ad35c DP |
514 | info("starting " PROGNAME " (%s)", usbip_version_string); |
515 | ||
516 | /* | |
517 | * To suppress warnings on systems with bindv6only disabled | |
518 | * (default), we use seperate sockets for IPv6 and IPv4 and set | |
519 | * IPV6_V6ONLY on the IPv6 sockets. | |
520 | */ | |
521 | if (ipv4 && ipv6) | |
522 | family = AF_UNSPEC; | |
523 | else if (ipv4) | |
524 | family = AF_INET; | |
525 | else | |
526 | family = AF_INET6; | |
527 | ||
528 | ai_head = do_getaddrinfo(NULL, family); | |
e6979499 | 529 | if (!ai_head) { |
3391ba0e | 530 | usbip_driver_close(driver); |
e2ec6b4e | 531 | return -1; |
e6979499 | 532 | } |
f49ad35c DP |
533 | nsockfd = listen_all_addrinfo(ai_head, sockfdlist, |
534 | sizeof(sockfdlist) / sizeof(*sockfdlist)); | |
535 | freeaddrinfo(ai_head); | |
e2ec6b4e | 536 | if (nsockfd <= 0) { |
537 | err("failed to open a listening socket"); | |
3391ba0e | 538 | usbip_driver_close(driver); |
e2ec6b4e | 539 | return -1; |
540 | } | |
f49ad35c DP |
541 | |
542 | dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es"); | |
543 | ||
328f7f8a | 544 | fds = calloc(nsockfd, sizeof(struct pollfd)); |
e2ec6b4e | 545 | for (i = 0; i < nsockfd; i++) { |
328f7f8a IH |
546 | fds[i].fd = sockfdlist[i]; |
547 | fds[i].events = POLLIN; | |
548 | } | |
549 | timeout.tv_sec = MAIN_LOOP_TIMEOUT; | |
550 | timeout.tv_nsec = 0; | |
551 | ||
552 | sigfillset(&sigmask); | |
553 | sigdelset(&sigmask, SIGTERM); | |
554 | sigdelset(&sigmask, SIGINT); | |
555 | ||
556 | terminate = 0; | |
557 | while (!terminate) { | |
558 | int r; | |
559 | ||
560 | r = ppoll(fds, nsockfd, &timeout, &sigmask); | |
561 | if (r < 0) { | |
562 | dbg("%s", strerror(errno)); | |
563 | terminate = 1; | |
564 | } else if (r) { | |
565 | for (i = 0; i < nsockfd; i++) { | |
566 | if (fds[i].revents & POLLIN) { | |
567 | dbg("read event on fd[%d]=%d", | |
568 | i, sockfdlist[i]); | |
569 | process_request(sockfdlist[i]); | |
570 | } | |
571 | } | |
04948a34 | 572 | } else { |
328f7f8a | 573 | dbg("heartbeat timeout on ppoll()"); |
04948a34 | 574 | } |
0945b4fe | 575 | } |
0945b4fe | 576 | |
e2ec6b4e | 577 | info("shutting down " PROGNAME); |
328f7f8a | 578 | free(fds); |
3391ba0e | 579 | usbip_driver_close(driver); |
0945b4fe | 580 | |
e2ec6b4e | 581 | return 0; |
0945b4fe TH |
582 | } |
583 | ||
0945b4fe TH |
584 | int main(int argc, char *argv[]) |
585 | { | |
e2ec6b4e | 586 | static const struct option longopts[] = { |
f49ad35c DP |
587 | { "ipv4", no_argument, NULL, '4' }, |
588 | { "ipv6", no_argument, NULL, '6' }, | |
589 | { "daemon", no_argument, NULL, 'D' }, | |
92e11aef AF |
590 | { "daemon", no_argument, NULL, 'D' }, |
591 | { "debug", no_argument, NULL, 'd' }, | |
e0546fd8 | 592 | { "device", no_argument, NULL, 'e' }, |
92e11aef | 593 | { "pid", optional_argument, NULL, 'P' }, |
7182f8f8 | 594 | { "tcp-port", required_argument, NULL, 't' }, |
92e11aef AF |
595 | { "help", no_argument, NULL, 'h' }, |
596 | { "version", no_argument, NULL, 'v' }, | |
597 | { NULL, 0, NULL, 0 } | |
e2ec6b4e | 598 | }; |
0945b4fe TH |
599 | |
600 | enum { | |
601 | cmd_standalone_mode = 1, | |
602 | cmd_help, | |
603 | cmd_version | |
e2ec6b4e | 604 | } cmd; |
0945b4fe | 605 | |
328f7f8a | 606 | int daemonize = 0; |
f49ad35c | 607 | int ipv4 = 0, ipv6 = 0; |
e2ec6b4e | 608 | int opt, rc = -1; |
3eed8c03 | 609 | |
9ead219b | 610 | pid_file = NULL; |
0945b4fe TH |
611 | |
612 | usbip_use_stderr = 1; | |
613 | usbip_use_syslog = 0; | |
614 | ||
615 | if (geteuid() != 0) | |
e2ec6b4e | 616 | err("not running as root?"); |
0945b4fe | 617 | |
e2ec6b4e | 618 | cmd = cmd_standalone_mode; |
3391ba0e | 619 | driver = &host_driver; |
0945b4fe | 620 | for (;;) { |
e0546fd8 | 621 | opt = getopt_long(argc, argv, "46DdeP::t:hv", longopts, NULL); |
0945b4fe | 622 | |
e2ec6b4e | 623 | if (opt == -1) |
0945b4fe TH |
624 | break; |
625 | ||
e2ec6b4e | 626 | switch (opt) { |
f49ad35c DP |
627 | case '4': |
628 | ipv4 = 1; | |
629 | break; | |
630 | case '6': | |
631 | ipv6 = 1; | |
632 | break; | |
e2ec6b4e | 633 | case 'D': |
328f7f8a | 634 | daemonize = 1; |
e2ec6b4e | 635 | break; |
636 | case 'd': | |
637 | usbip_use_debug = 1; | |
0945b4fe | 638 | break; |
e2ec6b4e | 639 | case 'h': |
640 | cmd = cmd_help; | |
0945b4fe | 641 | break; |
9ead219b AF |
642 | case 'P': |
643 | pid_file = optarg ? optarg : DEFAULT_PID_FILE; | |
644 | break; | |
7182f8f8 AF |
645 | case 't': |
646 | usbip_setup_port_number(optarg); | |
647 | break; | |
e2ec6b4e | 648 | case 'v': |
649 | cmd = cmd_version; | |
0945b4fe | 650 | break; |
e0546fd8 IK |
651 | case 'e': |
652 | driver = &device_driver; | |
653 | break; | |
e2ec6b4e | 654 | case '?': |
655 | usbipd_help(); | |
0945b4fe | 656 | default: |
e2ec6b4e | 657 | goto err_out; |
658 | } | |
0945b4fe TH |
659 | } |
660 | ||
f49ad35c DP |
661 | if (!ipv4 && !ipv6) |
662 | ipv4 = ipv6 = 1; | |
663 | ||
e2ec6b4e | 664 | switch (cmd) { |
665 | case cmd_standalone_mode: | |
f49ad35c | 666 | rc = do_standalone_mode(daemonize, ipv4, ipv6); |
9ead219b | 667 | remove_pid_file(); |
e2ec6b4e | 668 | break; |
669 | case cmd_version: | |
670 | printf(PROGNAME " (%s)\n", usbip_version_string); | |
671 | rc = 0; | |
672 | break; | |
673 | case cmd_help: | |
674 | usbipd_help(); | |
675 | rc = 0; | |
676 | break; | |
677 | default: | |
678 | usbipd_help(); | |
679 | goto err_out; | |
680 | } | |
681 | ||
682 | err_out: | |
683 | return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE); | |
0945b4fe | 684 | } |