18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> 48c2ecf20Sopenharmony_ci * 2005-2007 Takahiro Hirofuchi 58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Samsung Electronics 68c2ecf20Sopenharmony_ci * Igor Kotrasinski <i.kotrasinsk@samsung.com> 78c2ecf20Sopenharmony_ci * Krzysztof Opasiak <k.opasiak@samsung.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#ifdef HAVE_CONFIG_H 118c2ecf20Sopenharmony_ci#include "../config.h" 128c2ecf20Sopenharmony_ci#endif 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define _GNU_SOURCE 158c2ecf20Sopenharmony_ci#include <errno.h> 168c2ecf20Sopenharmony_ci#include <unistd.h> 178c2ecf20Sopenharmony_ci#include <netdb.h> 188c2ecf20Sopenharmony_ci#include <string.h> 198c2ecf20Sopenharmony_ci#include <stdlib.h> 208c2ecf20Sopenharmony_ci#include <sys/types.h> 218c2ecf20Sopenharmony_ci#include <sys/stat.h> 228c2ecf20Sopenharmony_ci#include <arpa/inet.h> 238c2ecf20Sopenharmony_ci#include <sys/socket.h> 248c2ecf20Sopenharmony_ci#include <netinet/in.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#ifdef HAVE_LIBWRAP 278c2ecf20Sopenharmony_ci#include <tcpd.h> 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <getopt.h> 318c2ecf20Sopenharmony_ci#include <signal.h> 328c2ecf20Sopenharmony_ci#include <poll.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "usbip_host_driver.h" 358c2ecf20Sopenharmony_ci#include "usbip_host_common.h" 368c2ecf20Sopenharmony_ci#include "usbip_device_driver.h" 378c2ecf20Sopenharmony_ci#include "usbip_common.h" 388c2ecf20Sopenharmony_ci#include "usbip_network.h" 398c2ecf20Sopenharmony_ci#include "list.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#undef PROGNAME 428c2ecf20Sopenharmony_ci#define PROGNAME "usbipd" 438c2ecf20Sopenharmony_ci#define MAXSOCKFD 20 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MAIN_LOOP_TIMEOUT 10 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid" 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const char usbip_version_string[] = PACKAGE_STRING; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const char usbipd_help_string[] = 528c2ecf20Sopenharmony_ci "usage: usbipd [options]\n" 538c2ecf20Sopenharmony_ci "\n" 548c2ecf20Sopenharmony_ci " -4, --ipv4\n" 558c2ecf20Sopenharmony_ci " Bind to IPv4. Default is both.\n" 568c2ecf20Sopenharmony_ci "\n" 578c2ecf20Sopenharmony_ci " -6, --ipv6\n" 588c2ecf20Sopenharmony_ci " Bind to IPv6. Default is both.\n" 598c2ecf20Sopenharmony_ci "\n" 608c2ecf20Sopenharmony_ci " -e, --device\n" 618c2ecf20Sopenharmony_ci " Run in device mode.\n" 628c2ecf20Sopenharmony_ci " Rather than drive an attached device, create\n" 638c2ecf20Sopenharmony_ci " a virtual UDC to bind gadgets to.\n" 648c2ecf20Sopenharmony_ci "\n" 658c2ecf20Sopenharmony_ci " -D, --daemon\n" 668c2ecf20Sopenharmony_ci " Run as a daemon process.\n" 678c2ecf20Sopenharmony_ci "\n" 688c2ecf20Sopenharmony_ci " -d, --debug\n" 698c2ecf20Sopenharmony_ci " Print debugging information.\n" 708c2ecf20Sopenharmony_ci "\n" 718c2ecf20Sopenharmony_ci " -PFILE, --pid FILE\n" 728c2ecf20Sopenharmony_ci " Write process id to FILE.\n" 738c2ecf20Sopenharmony_ci " If no FILE specified, use " DEFAULT_PID_FILE "\n" 748c2ecf20Sopenharmony_ci "\n" 758c2ecf20Sopenharmony_ci " -tPORT, --tcp-port PORT\n" 768c2ecf20Sopenharmony_ci " Listen on TCP/IP port PORT.\n" 778c2ecf20Sopenharmony_ci "\n" 788c2ecf20Sopenharmony_ci " -h, --help\n" 798c2ecf20Sopenharmony_ci " Print this help.\n" 808c2ecf20Sopenharmony_ci "\n" 818c2ecf20Sopenharmony_ci " -v, --version\n" 828c2ecf20Sopenharmony_ci " Show version.\n"; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic struct usbip_host_driver *driver; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void usbipd_help(void) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci printf("%s\n", usbipd_help_string); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int recv_request_import(int sockfd) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct op_import_request req; 948c2ecf20Sopenharmony_ci struct usbip_exported_device *edev; 958c2ecf20Sopenharmony_ci struct usbip_usb_device pdu_udev; 968c2ecf20Sopenharmony_ci struct list_head *i; 978c2ecf20Sopenharmony_ci int found = 0; 988c2ecf20Sopenharmony_ci int status = ST_OK; 998c2ecf20Sopenharmony_ci int rc; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci rc = usbip_net_recv(sockfd, &req, sizeof(req)); 1048c2ecf20Sopenharmony_ci if (rc < 0) { 1058c2ecf20Sopenharmony_ci dbg("usbip_net_recv failed: import request"); 1068c2ecf20Sopenharmony_ci return -1; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci PACK_OP_IMPORT_REQUEST(0, &req); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci list_for_each(i, &driver->edev_list) { 1118c2ecf20Sopenharmony_ci edev = list_entry(i, struct usbip_exported_device, node); 1128c2ecf20Sopenharmony_ci if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) { 1138c2ecf20Sopenharmony_ci info("found requested device: %s", req.busid); 1148c2ecf20Sopenharmony_ci found = 1; 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (found) { 1208c2ecf20Sopenharmony_ci /* should set TCP_NODELAY for usbip */ 1218c2ecf20Sopenharmony_ci usbip_net_set_nodelay(sockfd); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* export device needs a TCP/IP socket descriptor */ 1248c2ecf20Sopenharmony_ci status = usbip_export_device(edev, sockfd); 1258c2ecf20Sopenharmony_ci if (status < 0) 1268c2ecf20Sopenharmony_ci status = ST_NA; 1278c2ecf20Sopenharmony_ci } else { 1288c2ecf20Sopenharmony_ci info("requested device not found: %s", req.busid); 1298c2ecf20Sopenharmony_ci status = ST_NODEV; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, status); 1338c2ecf20Sopenharmony_ci if (rc < 0) { 1348c2ecf20Sopenharmony_ci dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT); 1358c2ecf20Sopenharmony_ci return -1; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (status) { 1398c2ecf20Sopenharmony_ci dbg("import request busid %s: failed", req.busid); 1408c2ecf20Sopenharmony_ci return -1; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); 1448c2ecf20Sopenharmony_ci usbip_net_pack_usb_device(1, &pdu_udev); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev)); 1478c2ecf20Sopenharmony_ci if (rc < 0) { 1488c2ecf20Sopenharmony_ci dbg("usbip_net_send failed: devinfo"); 1498c2ecf20Sopenharmony_ci return -1; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci dbg("import request busid %s: complete", req.busid); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int send_reply_devlist(int connfd) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct usbip_exported_device *edev; 1608c2ecf20Sopenharmony_ci struct usbip_usb_device pdu_udev; 1618c2ecf20Sopenharmony_ci struct usbip_usb_interface pdu_uinf; 1628c2ecf20Sopenharmony_ci struct op_devlist_reply reply; 1638c2ecf20Sopenharmony_ci struct list_head *j; 1648c2ecf20Sopenharmony_ci int rc, i; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * Exclude devices that are already exported to a client from 1688c2ecf20Sopenharmony_ci * the exportable device list to avoid: 1698c2ecf20Sopenharmony_ci * - import requests for devices that are exported only to 1708c2ecf20Sopenharmony_ci * fail the request. 1718c2ecf20Sopenharmony_ci * - revealing devices that are imported by a client to 1728c2ecf20Sopenharmony_ci * another client. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci reply.ndev = 0; 1768c2ecf20Sopenharmony_ci /* number of exported devices */ 1778c2ecf20Sopenharmony_ci list_for_each(j, &driver->edev_list) { 1788c2ecf20Sopenharmony_ci edev = list_entry(j, struct usbip_exported_device, node); 1798c2ecf20Sopenharmony_ci if (edev->status != SDEV_ST_USED) 1808c2ecf20Sopenharmony_ci reply.ndev += 1; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci info("exportable devices: %d", reply.ndev); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK); 1858c2ecf20Sopenharmony_ci if (rc < 0) { 1868c2ecf20Sopenharmony_ci dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST); 1878c2ecf20Sopenharmony_ci return -1; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci PACK_OP_DEVLIST_REPLY(1, &reply); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci rc = usbip_net_send(connfd, &reply, sizeof(reply)); 1928c2ecf20Sopenharmony_ci if (rc < 0) { 1938c2ecf20Sopenharmony_ci dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST); 1948c2ecf20Sopenharmony_ci return -1; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci list_for_each(j, &driver->edev_list) { 1988c2ecf20Sopenharmony_ci edev = list_entry(j, struct usbip_exported_device, node); 1998c2ecf20Sopenharmony_ci if (edev->status == SDEV_ST_USED) 2008c2ecf20Sopenharmony_ci continue; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci dump_usb_device(&edev->udev); 2038c2ecf20Sopenharmony_ci memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); 2048c2ecf20Sopenharmony_ci usbip_net_pack_usb_device(1, &pdu_udev); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev)); 2078c2ecf20Sopenharmony_ci if (rc < 0) { 2088c2ecf20Sopenharmony_ci dbg("usbip_net_send failed: pdu_udev"); 2098c2ecf20Sopenharmony_ci return -1; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci for (i = 0; i < edev->udev.bNumInterfaces; i++) { 2138c2ecf20Sopenharmony_ci dump_usb_interface(&edev->uinf[i]); 2148c2ecf20Sopenharmony_ci memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf)); 2158c2ecf20Sopenharmony_ci usbip_net_pack_usb_interface(1, &pdu_uinf); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci rc = usbip_net_send(connfd, &pdu_uinf, 2188c2ecf20Sopenharmony_ci sizeof(pdu_uinf)); 2198c2ecf20Sopenharmony_ci if (rc < 0) { 2208c2ecf20Sopenharmony_ci err("usbip_net_send failed: pdu_uinf"); 2218c2ecf20Sopenharmony_ci return -1; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int recv_request_devlist(int connfd) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct op_devlist_request req; 2328c2ecf20Sopenharmony_ci int rc; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci rc = usbip_net_recv(connfd, &req, sizeof(req)); 2378c2ecf20Sopenharmony_ci if (rc < 0) { 2388c2ecf20Sopenharmony_ci dbg("usbip_net_recv failed: devlist request"); 2398c2ecf20Sopenharmony_ci return -1; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci rc = send_reply_devlist(connfd); 2438c2ecf20Sopenharmony_ci if (rc < 0) { 2448c2ecf20Sopenharmony_ci dbg("send_reply_devlist failed"); 2458c2ecf20Sopenharmony_ci return -1; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int recv_pdu(int connfd) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci uint16_t code = OP_UNSPEC; 2548c2ecf20Sopenharmony_ci int ret; 2558c2ecf20Sopenharmony_ci int status; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ret = usbip_net_recv_op_common(connfd, &code, &status); 2588c2ecf20Sopenharmony_ci if (ret < 0) { 2598c2ecf20Sopenharmony_ci dbg("could not receive opcode: %#0x", code); 2608c2ecf20Sopenharmony_ci return -1; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = usbip_refresh_device_list(driver); 2648c2ecf20Sopenharmony_ci if (ret < 0) { 2658c2ecf20Sopenharmony_ci dbg("could not refresh device list: %d", ret); 2668c2ecf20Sopenharmony_ci return -1; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci info("received request: %#0x(%d)", code, connfd); 2708c2ecf20Sopenharmony_ci switch (code) { 2718c2ecf20Sopenharmony_ci case OP_REQ_DEVLIST: 2728c2ecf20Sopenharmony_ci ret = recv_request_devlist(connfd); 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci case OP_REQ_IMPORT: 2758c2ecf20Sopenharmony_ci ret = recv_request_import(connfd); 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci case OP_REQ_DEVINFO: 2788c2ecf20Sopenharmony_ci case OP_REQ_CRYPKEY: 2798c2ecf20Sopenharmony_ci default: 2808c2ecf20Sopenharmony_ci err("received an unknown opcode: %#0x", code); 2818c2ecf20Sopenharmony_ci ret = -1; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (ret == 0) 2858c2ecf20Sopenharmony_ci info("request %#0x(%d): complete", code, connfd); 2868c2ecf20Sopenharmony_ci else 2878c2ecf20Sopenharmony_ci info("request %#0x(%d): failed", code, connfd); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return ret; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#ifdef HAVE_LIBWRAP 2938c2ecf20Sopenharmony_cistatic int tcpd_auth(int connfd) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct request_info request; 2968c2ecf20Sopenharmony_ci int rc; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0); 2998c2ecf20Sopenharmony_ci fromhost(&request); 3008c2ecf20Sopenharmony_ci rc = hosts_access(&request); 3018c2ecf20Sopenharmony_ci if (rc == 0) 3028c2ecf20Sopenharmony_ci return -1; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci#endif 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int do_accept(int listenfd) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci int connfd; 3118c2ecf20Sopenharmony_ci struct sockaddr_storage ss; 3128c2ecf20Sopenharmony_ci socklen_t len = sizeof(ss); 3138c2ecf20Sopenharmony_ci char host[NI_MAXHOST], port[NI_MAXSERV]; 3148c2ecf20Sopenharmony_ci int rc; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci memset(&ss, 0, sizeof(ss)); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci connfd = accept(listenfd, (struct sockaddr *)&ss, &len); 3198c2ecf20Sopenharmony_ci if (connfd < 0) { 3208c2ecf20Sopenharmony_ci err("failed to accept connection"); 3218c2ecf20Sopenharmony_ci return -1; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host), 3258c2ecf20Sopenharmony_ci port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); 3268c2ecf20Sopenharmony_ci if (rc) 3278c2ecf20Sopenharmony_ci err("getnameinfo: %s", gai_strerror(rc)); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci#ifdef HAVE_LIBWRAP 3308c2ecf20Sopenharmony_ci rc = tcpd_auth(connfd); 3318c2ecf20Sopenharmony_ci if (rc < 0) { 3328c2ecf20Sopenharmony_ci info("denied access from %s", host); 3338c2ecf20Sopenharmony_ci close(connfd); 3348c2ecf20Sopenharmony_ci return -1; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci#endif 3378c2ecf20Sopenharmony_ci info("connection from %s:%s", host, port); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return connfd; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciint process_request(int listenfd) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci pid_t childpid; 3458c2ecf20Sopenharmony_ci int connfd; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci connfd = do_accept(listenfd); 3488c2ecf20Sopenharmony_ci if (connfd < 0) 3498c2ecf20Sopenharmony_ci return -1; 3508c2ecf20Sopenharmony_ci childpid = fork(); 3518c2ecf20Sopenharmony_ci if (childpid == 0) { 3528c2ecf20Sopenharmony_ci close(listenfd); 3538c2ecf20Sopenharmony_ci recv_pdu(connfd); 3548c2ecf20Sopenharmony_ci exit(0); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci close(connfd); 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic void addrinfo_to_text(struct addrinfo *ai, char buf[], 3618c2ecf20Sopenharmony_ci const size_t buf_size) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci char hbuf[NI_MAXHOST]; 3648c2ecf20Sopenharmony_ci char sbuf[NI_MAXSERV]; 3658c2ecf20Sopenharmony_ci int rc; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci buf[0] = '\0'; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), 3708c2ecf20Sopenharmony_ci sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 3718c2ecf20Sopenharmony_ci if (rc) 3728c2ecf20Sopenharmony_ci err("getnameinfo: %s", gai_strerror(rc)); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci snprintf(buf, buf_size, "%s:%s", hbuf, sbuf); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[], 3788c2ecf20Sopenharmony_ci int maxsockfd) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct addrinfo *ai; 3818c2ecf20Sopenharmony_ci int ret, nsockfd = 0; 3828c2ecf20Sopenharmony_ci const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2; 3838c2ecf20Sopenharmony_ci char ai_buf[ai_buf_size]; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) { 3868c2ecf20Sopenharmony_ci int sock; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci addrinfo_to_text(ai, ai_buf, ai_buf_size); 3898c2ecf20Sopenharmony_ci dbg("opening %s", ai_buf); 3908c2ecf20Sopenharmony_ci sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 3918c2ecf20Sopenharmony_ci if (sock < 0) { 3928c2ecf20Sopenharmony_ci err("socket: %s: %d (%s)", 3938c2ecf20Sopenharmony_ci ai_buf, errno, strerror(errno)); 3948c2ecf20Sopenharmony_ci continue; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci usbip_net_set_reuseaddr(sock); 3988c2ecf20Sopenharmony_ci usbip_net_set_nodelay(sock); 3998c2ecf20Sopenharmony_ci /* We use seperate sockets for IPv4 and IPv6 4008c2ecf20Sopenharmony_ci * (see do_standalone_mode()) */ 4018c2ecf20Sopenharmony_ci usbip_net_set_v6only(sock); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ret = bind(sock, ai->ai_addr, ai->ai_addrlen); 4048c2ecf20Sopenharmony_ci if (ret < 0) { 4058c2ecf20Sopenharmony_ci err("bind: %s: %d (%s)", 4068c2ecf20Sopenharmony_ci ai_buf, errno, strerror(errno)); 4078c2ecf20Sopenharmony_ci close(sock); 4088c2ecf20Sopenharmony_ci continue; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ret = listen(sock, SOMAXCONN); 4128c2ecf20Sopenharmony_ci if (ret < 0) { 4138c2ecf20Sopenharmony_ci err("listen: %s: %d (%s)", 4148c2ecf20Sopenharmony_ci ai_buf, errno, strerror(errno)); 4158c2ecf20Sopenharmony_ci close(sock); 4168c2ecf20Sopenharmony_ci continue; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci info("listening on %s", ai_buf); 4208c2ecf20Sopenharmony_ci sockfdlist[nsockfd++] = sock; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return nsockfd; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic struct addrinfo *do_getaddrinfo(char *host, int ai_family) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct addrinfo hints, *ai_head; 4298c2ecf20Sopenharmony_ci int rc; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci memset(&hints, 0, sizeof(hints)); 4328c2ecf20Sopenharmony_ci hints.ai_family = ai_family; 4338c2ecf20Sopenharmony_ci hints.ai_socktype = SOCK_STREAM; 4348c2ecf20Sopenharmony_ci hints.ai_flags = AI_PASSIVE; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head); 4378c2ecf20Sopenharmony_ci if (rc) { 4388c2ecf20Sopenharmony_ci err("failed to get a network address %s: %s", usbip_port_string, 4398c2ecf20Sopenharmony_ci gai_strerror(rc)); 4408c2ecf20Sopenharmony_ci return NULL; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return ai_head; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void signal_handler(int i) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci dbg("received '%s' signal", strsignal(i)); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic void set_signal(void) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct sigaction act; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci memset(&act, 0, sizeof(act)); 4568c2ecf20Sopenharmony_ci act.sa_handler = signal_handler; 4578c2ecf20Sopenharmony_ci sigemptyset(&act.sa_mask); 4588c2ecf20Sopenharmony_ci sigaction(SIGTERM, &act, NULL); 4598c2ecf20Sopenharmony_ci sigaction(SIGINT, &act, NULL); 4608c2ecf20Sopenharmony_ci act.sa_handler = SIG_IGN; 4618c2ecf20Sopenharmony_ci sigaction(SIGCHLD, &act, NULL); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic const char *pid_file; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic void write_pid_file(void) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci if (pid_file) { 4698c2ecf20Sopenharmony_ci dbg("creating pid file %s", pid_file); 4708c2ecf20Sopenharmony_ci FILE *fp; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci fp = fopen(pid_file, "w"); 4738c2ecf20Sopenharmony_ci if (!fp) { 4748c2ecf20Sopenharmony_ci err("pid_file: %s: %d (%s)", 4758c2ecf20Sopenharmony_ci pid_file, errno, strerror(errno)); 4768c2ecf20Sopenharmony_ci return; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci fprintf(fp, "%d\n", getpid()); 4798c2ecf20Sopenharmony_ci fclose(fp); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic void remove_pid_file(void) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci if (pid_file) { 4868c2ecf20Sopenharmony_ci dbg("removing pid file %s", pid_file); 4878c2ecf20Sopenharmony_ci unlink(pid_file); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int do_standalone_mode(int daemonize, int ipv4, int ipv6) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct addrinfo *ai_head; 4948c2ecf20Sopenharmony_ci int sockfdlist[MAXSOCKFD]; 4958c2ecf20Sopenharmony_ci int nsockfd, family; 4968c2ecf20Sopenharmony_ci int i, terminate; 4978c2ecf20Sopenharmony_ci struct pollfd *fds; 4988c2ecf20Sopenharmony_ci struct timespec timeout; 4998c2ecf20Sopenharmony_ci sigset_t sigmask; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (usbip_driver_open(driver)) 5028c2ecf20Sopenharmony_ci return -1; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (daemonize) { 5058c2ecf20Sopenharmony_ci if (daemon(0, 0) < 0) { 5068c2ecf20Sopenharmony_ci err("daemonizing failed: %s", strerror(errno)); 5078c2ecf20Sopenharmony_ci usbip_driver_close(driver); 5088c2ecf20Sopenharmony_ci return -1; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci umask(0); 5118c2ecf20Sopenharmony_ci usbip_use_syslog = 1; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci set_signal(); 5148c2ecf20Sopenharmony_ci write_pid_file(); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci info("starting " PROGNAME " (%s)", usbip_version_string); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * To suppress warnings on systems with bindv6only disabled 5208c2ecf20Sopenharmony_ci * (default), we use seperate sockets for IPv6 and IPv4 and set 5218c2ecf20Sopenharmony_ci * IPV6_V6ONLY on the IPv6 sockets. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci if (ipv4 && ipv6) 5248c2ecf20Sopenharmony_ci family = AF_UNSPEC; 5258c2ecf20Sopenharmony_ci else if (ipv4) 5268c2ecf20Sopenharmony_ci family = AF_INET; 5278c2ecf20Sopenharmony_ci else 5288c2ecf20Sopenharmony_ci family = AF_INET6; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ai_head = do_getaddrinfo(NULL, family); 5318c2ecf20Sopenharmony_ci if (!ai_head) { 5328c2ecf20Sopenharmony_ci usbip_driver_close(driver); 5338c2ecf20Sopenharmony_ci return -1; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci nsockfd = listen_all_addrinfo(ai_head, sockfdlist, 5368c2ecf20Sopenharmony_ci sizeof(sockfdlist) / sizeof(*sockfdlist)); 5378c2ecf20Sopenharmony_ci freeaddrinfo(ai_head); 5388c2ecf20Sopenharmony_ci if (nsockfd <= 0) { 5398c2ecf20Sopenharmony_ci err("failed to open a listening socket"); 5408c2ecf20Sopenharmony_ci usbip_driver_close(driver); 5418c2ecf20Sopenharmony_ci return -1; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es"); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci fds = calloc(nsockfd, sizeof(struct pollfd)); 5478c2ecf20Sopenharmony_ci for (i = 0; i < nsockfd; i++) { 5488c2ecf20Sopenharmony_ci fds[i].fd = sockfdlist[i]; 5498c2ecf20Sopenharmony_ci fds[i].events = POLLIN; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci timeout.tv_sec = MAIN_LOOP_TIMEOUT; 5528c2ecf20Sopenharmony_ci timeout.tv_nsec = 0; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci sigfillset(&sigmask); 5558c2ecf20Sopenharmony_ci sigdelset(&sigmask, SIGTERM); 5568c2ecf20Sopenharmony_ci sigdelset(&sigmask, SIGINT); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci terminate = 0; 5598c2ecf20Sopenharmony_ci while (!terminate) { 5608c2ecf20Sopenharmony_ci int r; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci r = ppoll(fds, nsockfd, &timeout, &sigmask); 5638c2ecf20Sopenharmony_ci if (r < 0) { 5648c2ecf20Sopenharmony_ci dbg("%s", strerror(errno)); 5658c2ecf20Sopenharmony_ci terminate = 1; 5668c2ecf20Sopenharmony_ci } else if (r) { 5678c2ecf20Sopenharmony_ci for (i = 0; i < nsockfd; i++) { 5688c2ecf20Sopenharmony_ci if (fds[i].revents & POLLIN) { 5698c2ecf20Sopenharmony_ci dbg("read event on fd[%d]=%d", 5708c2ecf20Sopenharmony_ci i, sockfdlist[i]); 5718c2ecf20Sopenharmony_ci process_request(sockfdlist[i]); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci dbg("heartbeat timeout on ppoll()"); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci info("shutting down " PROGNAME); 5808c2ecf20Sopenharmony_ci free(fds); 5818c2ecf20Sopenharmony_ci usbip_driver_close(driver); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ciint main(int argc, char *argv[]) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci static const struct option longopts[] = { 5898c2ecf20Sopenharmony_ci { "ipv4", no_argument, NULL, '4' }, 5908c2ecf20Sopenharmony_ci { "ipv6", no_argument, NULL, '6' }, 5918c2ecf20Sopenharmony_ci { "daemon", no_argument, NULL, 'D' }, 5928c2ecf20Sopenharmony_ci { "daemon", no_argument, NULL, 'D' }, 5938c2ecf20Sopenharmony_ci { "debug", no_argument, NULL, 'd' }, 5948c2ecf20Sopenharmony_ci { "device", no_argument, NULL, 'e' }, 5958c2ecf20Sopenharmony_ci { "pid", optional_argument, NULL, 'P' }, 5968c2ecf20Sopenharmony_ci { "tcp-port", required_argument, NULL, 't' }, 5978c2ecf20Sopenharmony_ci { "help", no_argument, NULL, 'h' }, 5988c2ecf20Sopenharmony_ci { "version", no_argument, NULL, 'v' }, 5998c2ecf20Sopenharmony_ci { NULL, 0, NULL, 0 } 6008c2ecf20Sopenharmony_ci }; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci enum { 6038c2ecf20Sopenharmony_ci cmd_standalone_mode = 1, 6048c2ecf20Sopenharmony_ci cmd_help, 6058c2ecf20Sopenharmony_ci cmd_version 6068c2ecf20Sopenharmony_ci } cmd; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci int daemonize = 0; 6098c2ecf20Sopenharmony_ci int ipv4 = 0, ipv6 = 0; 6108c2ecf20Sopenharmony_ci int opt, rc = -1; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci pid_file = NULL; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci usbip_use_stderr = 1; 6158c2ecf20Sopenharmony_ci usbip_use_syslog = 0; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (geteuid() != 0) 6188c2ecf20Sopenharmony_ci err("not running as root?"); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci cmd = cmd_standalone_mode; 6218c2ecf20Sopenharmony_ci driver = &host_driver; 6228c2ecf20Sopenharmony_ci for (;;) { 6238c2ecf20Sopenharmony_ci opt = getopt_long(argc, argv, "46DdeP::t:hv", longopts, NULL); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (opt == -1) 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci switch (opt) { 6298c2ecf20Sopenharmony_ci case '4': 6308c2ecf20Sopenharmony_ci ipv4 = 1; 6318c2ecf20Sopenharmony_ci break; 6328c2ecf20Sopenharmony_ci case '6': 6338c2ecf20Sopenharmony_ci ipv6 = 1; 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci case 'D': 6368c2ecf20Sopenharmony_ci daemonize = 1; 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci case 'd': 6398c2ecf20Sopenharmony_ci usbip_use_debug = 1; 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci case 'h': 6428c2ecf20Sopenharmony_ci cmd = cmd_help; 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci case 'P': 6458c2ecf20Sopenharmony_ci pid_file = optarg ? optarg : DEFAULT_PID_FILE; 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci case 't': 6488c2ecf20Sopenharmony_ci usbip_setup_port_number(optarg); 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci case 'v': 6518c2ecf20Sopenharmony_ci cmd = cmd_version; 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci case 'e': 6548c2ecf20Sopenharmony_ci driver = &device_driver; 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci case '?': 6578c2ecf20Sopenharmony_ci usbipd_help(); 6588c2ecf20Sopenharmony_ci default: 6598c2ecf20Sopenharmony_ci goto err_out; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (!ipv4 && !ipv6) 6648c2ecf20Sopenharmony_ci ipv4 = ipv6 = 1; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci switch (cmd) { 6678c2ecf20Sopenharmony_ci case cmd_standalone_mode: 6688c2ecf20Sopenharmony_ci rc = do_standalone_mode(daemonize, ipv4, ipv6); 6698c2ecf20Sopenharmony_ci remove_pid_file(); 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci case cmd_version: 6728c2ecf20Sopenharmony_ci printf(PROGNAME " (%s)\n", usbip_version_string); 6738c2ecf20Sopenharmony_ci rc = 0; 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci case cmd_help: 6768c2ecf20Sopenharmony_ci usbipd_help(); 6778c2ecf20Sopenharmony_ci rc = 0; 6788c2ecf20Sopenharmony_ci break; 6798c2ecf20Sopenharmony_ci default: 6808c2ecf20Sopenharmony_ci usbipd_help(); 6818c2ecf20Sopenharmony_ci goto err_out; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cierr_out: 6858c2ecf20Sopenharmony_ci return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE); 6868c2ecf20Sopenharmony_ci} 687