Commit | Line | Data |
---|---|---|
0945b4fe | 1 | /* |
e2ec6b4e | 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> |
3 | * 2005-2007 Takahiro Hirofuchi | |
0945b4fe | 4 | * |
e2ec6b4e | 5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
0945b4fe TH |
17 | */ |
18 | ||
19 | #ifdef HAVE_CONFIG_H | |
20 | #include "../config.h" | |
21 | #endif | |
22 | ||
328f7f8a | 23 | #define _GNU_SOURCE |
d2c15e25 | 24 | #include <errno.h> |
0945b4fe TH |
25 | #include <unistd.h> |
26 | #include <netdb.h> | |
950a4cd8 | 27 | #include <string.h> |
0945b4fe TH |
28 | #include <stdlib.h> |
29 | #include <sys/types.h> | |
30 | #include <sys/stat.h> | |
31 | #include <arpa/inet.h> | |
32 | #include <sys/socket.h> | |
33 | #include <netinet/in.h> | |
34 | ||
35 | #ifdef HAVE_LIBWRAP | |
36 | #include <tcpd.h> | |
37 | #endif | |
38 | ||
0945b4fe TH |
39 | #include <getopt.h> |
40 | #include <signal.h> | |
328f7f8a | 41 | #include <poll.h> |
0945b4fe | 42 | |
7104b5df | 43 | #include "usbip_host_driver.h" |
d2c15e25 | 44 | #include "usbip_common.h" |
0945b4fe TH |
45 | #include "usbip_network.h" |
46 | ||
e2ec6b4e | 47 | #undef PROGNAME |
48 | #define PROGNAME "usbipd" | |
49 | #define MAXSOCKFD 20 | |
50 | ||
328f7f8a | 51 | #define MAIN_LOOP_TIMEOUT 10 |
0945b4fe | 52 | |
e2ec6b4e | 53 | static const char usbip_version_string[] = PACKAGE_STRING; |
54 | ||
55 | static const char usbipd_help_string[] = | |
56 | "usage: usbipd [options] \n" | |
57 | " -D, --daemon \n" | |
58 | " Run as a daemon process. \n" | |
59 | " \n" | |
60 | " -d, --debug \n" | |
61 | " Print debugging information. \n" | |
62 | " \n" | |
5037307d | 63 | " -h, --help \n" |
e2ec6b4e | 64 | " Print this help. \n" |
65 | " \n" | |
66 | " -v, --version \n" | |
67 | " Show version. \n"; | |
68 | ||
69 | static void usbipd_help(void) | |
0945b4fe | 70 | { |
e2ec6b4e | 71 | printf("%s\n", usbipd_help_string); |
72 | } | |
73 | ||
74 | static int recv_request_import(int sockfd) | |
75 | { | |
76 | struct op_import_request req; | |
77 | struct op_common reply; | |
0945b4fe | 78 | struct usbip_exported_device *edev; |
e2ec6b4e | 79 | struct usbip_usb_device pdu_udev; |
80 | int found = 0; | |
81 | int error = 0; | |
82 | int rc; | |
0945b4fe | 83 | |
e2ec6b4e | 84 | memset(&req, 0, sizeof(req)); |
85 | memset(&reply, 0, sizeof(reply)); | |
0945b4fe | 86 | |
3c6e9e8f | 87 | rc = usbip_net_recv(sockfd, &req, sizeof(req)); |
e2ec6b4e | 88 | if (rc < 0) { |
3c6e9e8f | 89 | dbg("usbip_net_recv failed: import request"); |
e2ec6b4e | 90 | return -1; |
91 | } | |
92 | PACK_OP_IMPORT_REQUEST(0, &req); | |
0945b4fe | 93 | |
e2ec6b4e | 94 | dlist_for_each_data(host_driver->edev_list, edev, |
95 | struct usbip_exported_device) { | |
96 | if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) { | |
97 | info("found requested device: %s", req.busid); | |
98 | found = 1; | |
99 | break; | |
100 | } | |
0945b4fe TH |
101 | } |
102 | ||
e2ec6b4e | 103 | if (found) { |
104 | /* should set TCP_NODELAY for usbip */ | |
3c6e9e8f | 105 | usbip_net_set_nodelay(sockfd); |
0945b4fe | 106 | |
e2ec6b4e | 107 | /* export device needs a TCP/IP socket descriptor */ |
108 | rc = usbip_host_export_device(edev, sockfd); | |
109 | if (rc < 0) | |
110 | error = 1; | |
111 | } else { | |
112 | info("requested device not found: %s", req.busid); | |
113 | error = 1; | |
0945b4fe TH |
114 | } |
115 | ||
3c6e9e8f | 116 | rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, |
117 | (!error ? ST_OK : ST_NA)); | |
e2ec6b4e | 118 | if (rc < 0) { |
3c6e9e8f | 119 | dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT); |
e2ec6b4e | 120 | return -1; |
121 | } | |
122 | ||
123 | if (error) { | |
124 | dbg("import request busid %s: failed", req.busid); | |
125 | return -1; | |
126 | } | |
127 | ||
128 | memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); | |
3c6e9e8f | 129 | usbip_net_pack_usb_device(1, &pdu_udev); |
e2ec6b4e | 130 | |
3c6e9e8f | 131 | rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev)); |
e2ec6b4e | 132 | if (rc < 0) { |
3c6e9e8f | 133 | dbg("usbip_net_send failed: devinfo"); |
e2ec6b4e | 134 | return -1; |
0945b4fe TH |
135 | } |
136 | ||
e2ec6b4e | 137 | dbg("import request busid %s: complete", req.busid); |
138 | ||
139 | return 0; | |
140 | } | |
0945b4fe | 141 | |
e2ec6b4e | 142 | static int send_reply_devlist(int connfd) |
143 | { | |
144 | struct usbip_exported_device *edev; | |
145 | struct usbip_usb_device pdu_udev; | |
146 | struct usbip_usb_interface pdu_uinf; | |
147 | struct op_devlist_reply reply; | |
148 | int i; | |
149 | int rc; | |
150 | ||
151 | reply.ndev = 0; | |
152 | /* number of exported devices */ | |
153 | dlist_for_each_data(host_driver->edev_list, edev, | |
154 | struct usbip_exported_device) { | |
155 | reply.ndev += 1; | |
156 | } | |
157 | info("exportable devices: %d", reply.ndev); | |
158 | ||
3c6e9e8f | 159 | rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK); |
e2ec6b4e | 160 | if (rc < 0) { |
3c6e9e8f | 161 | dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST); |
e2ec6b4e | 162 | return -1; |
163 | } | |
164 | PACK_OP_DEVLIST_REPLY(1, &reply); | |
165 | ||
3c6e9e8f | 166 | rc = usbip_net_send(connfd, &reply, sizeof(reply)); |
e2ec6b4e | 167 | if (rc < 0) { |
3c6e9e8f | 168 | dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST); |
e2ec6b4e | 169 | return -1; |
170 | } | |
171 | ||
172 | dlist_for_each_data(host_driver->edev_list, edev, | |
173 | struct usbip_exported_device) { | |
0945b4fe TH |
174 | dump_usb_device(&edev->udev); |
175 | memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); | |
3c6e9e8f | 176 | usbip_net_pack_usb_device(1, &pdu_udev); |
0945b4fe | 177 | |
3c6e9e8f | 178 | rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev)); |
e2ec6b4e | 179 | if (rc < 0) { |
3c6e9e8f | 180 | dbg("usbip_net_send failed: pdu_udev"); |
e2ec6b4e | 181 | return -1; |
0945b4fe TH |
182 | } |
183 | ||
e2ec6b4e | 184 | for (i = 0; i < edev->udev.bNumInterfaces; i++) { |
0945b4fe TH |
185 | dump_usb_interface(&edev->uinf[i]); |
186 | memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf)); | |
3c6e9e8f | 187 | usbip_net_pack_usb_interface(1, &pdu_uinf); |
0945b4fe | 188 | |
3c6e9e8f | 189 | rc = usbip_net_send(connfd, &pdu_uinf, |
190 | sizeof(pdu_uinf)); | |
e2ec6b4e | 191 | if (rc < 0) { |
3c6e9e8f | 192 | dbg("usbip_net_send failed: pdu_uinf"); |
e2ec6b4e | 193 | return -1; |
0945b4fe TH |
194 | } |
195 | } | |
196 | } | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
e2ec6b4e | 201 | static int recv_request_devlist(int connfd) |
0945b4fe | 202 | { |
0945b4fe | 203 | struct op_devlist_request req; |
e2ec6b4e | 204 | int rc; |
0945b4fe | 205 | |
950a4cd8 | 206 | memset(&req, 0, sizeof(req)); |
0945b4fe | 207 | |
3c6e9e8f | 208 | rc = usbip_net_recv(connfd, &req, sizeof(req)); |
e2ec6b4e | 209 | if (rc < 0) { |
3c6e9e8f | 210 | dbg("usbip_net_recv failed: devlist request"); |
0945b4fe TH |
211 | return -1; |
212 | } | |
213 | ||
e2ec6b4e | 214 | rc = send_reply_devlist(connfd); |
215 | if (rc < 0) { | |
216 | dbg("send_reply_devlist failed"); | |
0945b4fe TH |
217 | return -1; |
218 | } | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
e2ec6b4e | 223 | static int recv_pdu(int connfd) |
0945b4fe | 224 | { |
e2ec6b4e | 225 | uint16_t code = OP_UNSPEC; |
0945b4fe | 226 | int ret; |
0945b4fe | 227 | |
3c6e9e8f | 228 | ret = usbip_net_recv_op_common(connfd, &code); |
0945b4fe | 229 | if (ret < 0) { |
e2ec6b4e | 230 | dbg("could not receive opcode: %#0x", code); |
0945b4fe TH |
231 | return -1; |
232 | } | |
233 | ||
e2ec6b4e | 234 | ret = usbip_host_refresh_device_list(); |
235 | if (ret < 0) { | |
236 | dbg("could not refresh device list: %d", ret); | |
237 | return -1; | |
0945b4fe TH |
238 | } |
239 | ||
e2ec6b4e | 240 | info("received request: %#0x(%d)", code, connfd); |
241 | switch (code) { | |
242 | case OP_REQ_DEVLIST: | |
243 | ret = recv_request_devlist(connfd); | |
244 | break; | |
245 | case OP_REQ_IMPORT: | |
246 | ret = recv_request_import(connfd); | |
247 | break; | |
248 | case OP_REQ_DEVINFO: | |
249 | case OP_REQ_CRYPKEY: | |
250 | default: | |
251 | err("received an unknown opcode: %#0x", code); | |
252 | ret = -1; | |
0945b4fe TH |
253 | } |
254 | ||
e2ec6b4e | 255 | if (ret == 0) |
256 | info("request %#0x(%d): complete", code, connfd); | |
257 | else | |
258 | info("request %#0x(%d): failed", code, connfd); | |
0945b4fe | 259 | |
e2ec6b4e | 260 | return ret; |
261 | } | |
0945b4fe | 262 | |
e2ec6b4e | 263 | #ifdef HAVE_LIBWRAP |
264 | static int tcpd_auth(int connfd) | |
265 | { | |
266 | struct request_info request; | |
267 | int rc; | |
0945b4fe | 268 | |
e2ec6b4e | 269 | request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0); |
270 | fromhost(&request); | |
271 | rc = hosts_access(&request); | |
272 | if (rc == 0) | |
273 | return -1; | |
0945b4fe TH |
274 | |
275 | return 0; | |
276 | } | |
e2ec6b4e | 277 | #endif |
0945b4fe | 278 | |
e2ec6b4e | 279 | static int do_accept(int listenfd) |
0945b4fe | 280 | { |
e2ec6b4e | 281 | int connfd; |
282 | struct sockaddr_storage ss; | |
283 | socklen_t len = sizeof(ss); | |
284 | char host[NI_MAXHOST], port[NI_MAXSERV]; | |
285 | int rc; | |
0945b4fe | 286 | |
e2ec6b4e | 287 | memset(&ss, 0, sizeof(ss)); |
0945b4fe | 288 | |
e2ec6b4e | 289 | connfd = accept(listenfd, (struct sockaddr *) &ss, &len); |
290 | if (connfd < 0) { | |
291 | err("failed to accept connection"); | |
292 | return -1; | |
0945b4fe TH |
293 | } |
294 | ||
e2ec6b4e | 295 | rc = getnameinfo((struct sockaddr *) &ss, len, host, sizeof(host), |
296 | port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); | |
297 | if (rc) | |
298 | err("getnameinfo: %s", gai_strerror(rc)); | |
0945b4fe | 299 | |
e2ec6b4e | 300 | #ifdef HAVE_LIBWRAP |
301 | rc = tcpd_auth(connfd); | |
302 | if (rc < 0) { | |
303 | info("denied access from %s", host); | |
304 | close(connfd); | |
0945b4fe | 305 | return -1; |
e2ec6b4e | 306 | } |
307 | #endif | |
308 | info("connection from %s:%s", host, port); | |
0945b4fe | 309 | |
e2ec6b4e | 310 | return connfd; |
311 | } | |
0945b4fe | 312 | |
328f7f8a | 313 | int process_request(int listenfd) |
e2ec6b4e | 314 | { |
328f7f8a | 315 | pid_t childpid; |
e2ec6b4e | 316 | int connfd; |
0945b4fe | 317 | |
328f7f8a IH |
318 | connfd = do_accept(listenfd); |
319 | if (connfd < 0) | |
320 | return -1; | |
321 | childpid = fork(); | |
322 | if (childpid == 0) { | |
323 | close(listenfd); | |
e2ec6b4e | 324 | recv_pdu(connfd); |
328f7f8a | 325 | exit(0); |
e2ec6b4e | 326 | } |
328f7f8a IH |
327 | close(connfd); |
328 | return 0; | |
e2ec6b4e | 329 | } |
0945b4fe TH |
330 | |
331 | static void log_addrinfo(struct addrinfo *ai) | |
332 | { | |
0945b4fe TH |
333 | char hbuf[NI_MAXHOST]; |
334 | char sbuf[NI_MAXSERV]; | |
e2ec6b4e | 335 | int rc; |
0945b4fe | 336 | |
e2ec6b4e | 337 | rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), |
338 | sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); | |
339 | if (rc) | |
340 | err("getnameinfo: %s", gai_strerror(rc)); | |
0945b4fe | 341 | |
e2ec6b4e | 342 | info("listening on %s:%s", hbuf, sbuf); |
0945b4fe TH |
343 | } |
344 | ||
e2ec6b4e | 345 | static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[]) |
0945b4fe TH |
346 | { |
347 | struct addrinfo *ai; | |
e2ec6b4e | 348 | int ret, nsockfd = 0; |
0945b4fe | 349 | |
e2ec6b4e | 350 | for (ai = ai_head; ai && nsockfd < MAXSOCKFD; ai = ai->ai_next) { |
351 | sockfdlist[nsockfd] = socket(ai->ai_family, ai->ai_socktype, | |
352 | ai->ai_protocol); | |
353 | if (sockfdlist[nsockfd] < 0) | |
0945b4fe TH |
354 | continue; |
355 | ||
3c6e9e8f | 356 | usbip_net_set_reuseaddr(sockfdlist[nsockfd]); |
357 | usbip_net_set_nodelay(sockfdlist[nsockfd]); | |
0945b4fe | 358 | |
e2ec6b4e | 359 | if (sockfdlist[nsockfd] >= FD_SETSIZE) { |
360 | close(sockfdlist[nsockfd]); | |
361 | sockfdlist[nsockfd] = -1; | |
0945b4fe TH |
362 | continue; |
363 | } | |
364 | ||
e2ec6b4e | 365 | ret = bind(sockfdlist[nsockfd], ai->ai_addr, ai->ai_addrlen); |
0945b4fe | 366 | if (ret < 0) { |
e2ec6b4e | 367 | close(sockfdlist[nsockfd]); |
368 | sockfdlist[nsockfd] = -1; | |
0945b4fe TH |
369 | continue; |
370 | } | |
371 | ||
e2ec6b4e | 372 | ret = listen(sockfdlist[nsockfd], SOMAXCONN); |
0945b4fe | 373 | if (ret < 0) { |
e2ec6b4e | 374 | close(sockfdlist[nsockfd]); |
375 | sockfdlist[nsockfd] = -1; | |
0945b4fe TH |
376 | continue; |
377 | } | |
378 | ||
379 | log_addrinfo(ai); | |
e2ec6b4e | 380 | nsockfd++; |
0945b4fe TH |
381 | } |
382 | ||
e2ec6b4e | 383 | if (nsockfd == 0) |
0945b4fe | 384 | return -1; |
0945b4fe | 385 | |
e2ec6b4e | 386 | dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es"); |
0945b4fe | 387 | |
e2ec6b4e | 388 | return nsockfd; |
0945b4fe | 389 | } |
0945b4fe | 390 | |
e2ec6b4e | 391 | static struct addrinfo *do_getaddrinfo(char *host, int ai_family) |
0945b4fe | 392 | { |
e2ec6b4e | 393 | struct addrinfo hints, *ai_head; |
394 | int rc; | |
0945b4fe | 395 | |
e2ec6b4e | 396 | memset(&hints, 0, sizeof(hints)); |
397 | hints.ai_family = ai_family; | |
398 | hints.ai_socktype = SOCK_STREAM; | |
399 | hints.ai_flags = AI_PASSIVE; | |
0945b4fe | 400 | |
e2ec6b4e | 401 | rc = getaddrinfo(host, USBIP_PORT_STRING, &hints, &ai_head); |
402 | if (rc) { | |
403 | err("failed to get a network address %s: %s", USBIP_PORT_STRING, | |
404 | gai_strerror(rc)); | |
405 | return NULL; | |
0945b4fe | 406 | } |
0945b4fe | 407 | |
e2ec6b4e | 408 | return ai_head; |
0945b4fe TH |
409 | } |
410 | ||
0945b4fe TH |
411 | static void signal_handler(int i) |
412 | { | |
328f7f8a | 413 | dbg("received '%s' signal", strsignal(i)); |
0945b4fe TH |
414 | } |
415 | ||
416 | static void set_signal(void) | |
417 | { | |
418 | struct sigaction act; | |
419 | ||
950a4cd8 | 420 | memset(&act, 0, sizeof(act)); |
0945b4fe TH |
421 | act.sa_handler = signal_handler; |
422 | sigemptyset(&act.sa_mask); | |
423 | sigaction(SIGTERM, &act, NULL); | |
424 | sigaction(SIGINT, &act, NULL); | |
328f7f8a IH |
425 | act.sa_handler = SIG_IGN; |
426 | sigaction(SIGCLD, &act, NULL); | |
0945b4fe TH |
427 | } |
428 | ||
328f7f8a | 429 | static int do_standalone_mode(int daemonize) |
0945b4fe | 430 | { |
0945b4fe | 431 | struct addrinfo *ai_head; |
e2ec6b4e | 432 | int sockfdlist[MAXSOCKFD]; |
433 | int nsockfd; | |
328f7f8a IH |
434 | int i, terminate; |
435 | struct pollfd *fds; | |
436 | struct timespec timeout; | |
437 | sigset_t sigmask; | |
0945b4fe | 438 | |
e2ec6b4e | 439 | if (usbip_names_init(USBIDS_FILE)) |
440 | err("failed to open %s", USBIDS_FILE); | |
0945b4fe | 441 | |
e2ec6b4e | 442 | if (usbip_host_driver_open()) { |
443 | err("please load " USBIP_CORE_MOD_NAME ".ko and " | |
444 | USBIP_HOST_DRV_NAME ".ko!"); | |
445 | return -1; | |
446 | } | |
0945b4fe TH |
447 | |
448 | if (daemonize) { | |
5037307d | 449 | if (daemon(0, 0) < 0) { |
e2ec6b4e | 450 | err("daemonizing failed: %s", strerror(errno)); |
451 | return -1; | |
452 | } | |
328f7f8a | 453 | umask(0); |
0945b4fe TH |
454 | usbip_use_syslog = 1; |
455 | } | |
0945b4fe TH |
456 | set_signal(); |
457 | ||
e2ec6b4e | 458 | ai_head = do_getaddrinfo(NULL, PF_UNSPEC); |
0945b4fe | 459 | if (!ai_head) |
e2ec6b4e | 460 | return -1; |
0945b4fe | 461 | |
e2ec6b4e | 462 | info("starting " PROGNAME " (%s)", usbip_version_string); |
0945b4fe | 463 | |
e2ec6b4e | 464 | nsockfd = listen_all_addrinfo(ai_head, sockfdlist); |
465 | if (nsockfd <= 0) { | |
466 | err("failed to open a listening socket"); | |
467 | return -1; | |
468 | } | |
328f7f8a | 469 | fds = calloc(nsockfd, sizeof(struct pollfd)); |
e2ec6b4e | 470 | for (i = 0; i < nsockfd; i++) { |
328f7f8a IH |
471 | fds[i].fd = sockfdlist[i]; |
472 | fds[i].events = POLLIN; | |
473 | } | |
474 | timeout.tv_sec = MAIN_LOOP_TIMEOUT; | |
475 | timeout.tv_nsec = 0; | |
476 | ||
477 | sigfillset(&sigmask); | |
478 | sigdelset(&sigmask, SIGTERM); | |
479 | sigdelset(&sigmask, SIGINT); | |
480 | ||
481 | terminate = 0; | |
482 | while (!terminate) { | |
483 | int r; | |
484 | ||
485 | r = ppoll(fds, nsockfd, &timeout, &sigmask); | |
486 | if (r < 0) { | |
487 | dbg("%s", strerror(errno)); | |
488 | terminate = 1; | |
489 | } else if (r) { | |
490 | for (i = 0; i < nsockfd; i++) { | |
491 | if (fds[i].revents & POLLIN) { | |
492 | dbg("read event on fd[%d]=%d", | |
493 | i, sockfdlist[i]); | |
494 | process_request(sockfdlist[i]); | |
495 | } | |
496 | } | |
497 | } else | |
498 | dbg("heartbeat timeout on ppoll()"); | |
0945b4fe | 499 | } |
0945b4fe | 500 | |
e2ec6b4e | 501 | info("shutting down " PROGNAME); |
328f7f8a | 502 | free(fds); |
0945b4fe | 503 | freeaddrinfo(ai_head); |
7104b5df | 504 | usbip_host_driver_close(); |
e2ec6b4e | 505 | usbip_names_free(); |
0945b4fe | 506 | |
e2ec6b4e | 507 | return 0; |
0945b4fe TH |
508 | } |
509 | ||
0945b4fe TH |
510 | int main(int argc, char *argv[]) |
511 | { | |
e2ec6b4e | 512 | static const struct option longopts[] = { |
513 | { "daemon", no_argument, NULL, 'D' }, | |
514 | { "debug", no_argument, NULL, 'd' }, | |
515 | { "help", no_argument, NULL, 'h' }, | |
516 | { "version", no_argument, NULL, 'v' }, | |
517 | { NULL, 0, NULL, 0 } | |
518 | }; | |
0945b4fe TH |
519 | |
520 | enum { | |
521 | cmd_standalone_mode = 1, | |
522 | cmd_help, | |
523 | cmd_version | |
e2ec6b4e | 524 | } cmd; |
0945b4fe | 525 | |
328f7f8a | 526 | int daemonize = 0; |
e2ec6b4e | 527 | int opt, rc = -1; |
0945b4fe TH |
528 | |
529 | usbip_use_stderr = 1; | |
530 | usbip_use_syslog = 0; | |
531 | ||
532 | if (geteuid() != 0) | |
e2ec6b4e | 533 | err("not running as root?"); |
0945b4fe | 534 | |
e2ec6b4e | 535 | cmd = cmd_standalone_mode; |
0945b4fe | 536 | for (;;) { |
e2ec6b4e | 537 | opt = getopt_long(argc, argv, "Ddhv", longopts, NULL); |
0945b4fe | 538 | |
e2ec6b4e | 539 | if (opt == -1) |
0945b4fe TH |
540 | break; |
541 | ||
e2ec6b4e | 542 | switch (opt) { |
543 | case 'D': | |
328f7f8a | 544 | daemonize = 1; |
e2ec6b4e | 545 | break; |
546 | case 'd': | |
547 | usbip_use_debug = 1; | |
0945b4fe | 548 | break; |
e2ec6b4e | 549 | case 'h': |
550 | cmd = cmd_help; | |
0945b4fe | 551 | break; |
e2ec6b4e | 552 | case 'v': |
553 | cmd = cmd_version; | |
0945b4fe | 554 | break; |
e2ec6b4e | 555 | case '?': |
556 | usbipd_help(); | |
0945b4fe | 557 | default: |
e2ec6b4e | 558 | goto err_out; |
559 | } | |
0945b4fe TH |
560 | } |
561 | ||
e2ec6b4e | 562 | switch (cmd) { |
563 | case cmd_standalone_mode: | |
564 | rc = do_standalone_mode(daemonize); | |
565 | break; | |
566 | case cmd_version: | |
567 | printf(PROGNAME " (%s)\n", usbip_version_string); | |
568 | rc = 0; | |
569 | break; | |
570 | case cmd_help: | |
571 | usbipd_help(); | |
572 | rc = 0; | |
573 | break; | |
574 | default: | |
575 | usbipd_help(); | |
576 | goto err_out; | |
577 | } | |
578 | ||
579 | err_out: | |
580 | return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE); | |
0945b4fe | 581 | } |