162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> 462306a36Sopenharmony_ci * 2005-2007 Takahiro Hirofuchi 562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Samsung Electronics 662306a36Sopenharmony_ci * Igor Kotrasinski <i.kotrasinsk@samsung.com> 762306a36Sopenharmony_ci * Krzysztof Opasiak <k.opasiak@samsung.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#ifdef HAVE_CONFIG_H 1162306a36Sopenharmony_ci#include "../config.h" 1262306a36Sopenharmony_ci#endif 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define _GNU_SOURCE 1562306a36Sopenharmony_ci#include <errno.h> 1662306a36Sopenharmony_ci#include <unistd.h> 1762306a36Sopenharmony_ci#include <netdb.h> 1862306a36Sopenharmony_ci#include <string.h> 1962306a36Sopenharmony_ci#include <stdlib.h> 2062306a36Sopenharmony_ci#include <sys/types.h> 2162306a36Sopenharmony_ci#include <sys/stat.h> 2262306a36Sopenharmony_ci#include <arpa/inet.h> 2362306a36Sopenharmony_ci#include <sys/socket.h> 2462306a36Sopenharmony_ci#include <netinet/in.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#ifdef HAVE_LIBWRAP 2762306a36Sopenharmony_ci#include <tcpd.h> 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <getopt.h> 3162306a36Sopenharmony_ci#include <signal.h> 3262306a36Sopenharmony_ci#include <poll.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "usbip_host_driver.h" 3562306a36Sopenharmony_ci#include "usbip_host_common.h" 3662306a36Sopenharmony_ci#include "usbip_device_driver.h" 3762306a36Sopenharmony_ci#include "usbip_common.h" 3862306a36Sopenharmony_ci#include "usbip_network.h" 3962306a36Sopenharmony_ci#include "list.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#undef PROGNAME 4262306a36Sopenharmony_ci#define PROGNAME "usbipd" 4362306a36Sopenharmony_ci#define MAXSOCKFD 20 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define MAIN_LOOP_TIMEOUT 10 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const char usbip_version_string[] = PACKAGE_STRING; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const char usbipd_help_string[] = 5262306a36Sopenharmony_ci "usage: usbipd [options]\n" 5362306a36Sopenharmony_ci "\n" 5462306a36Sopenharmony_ci " -4, --ipv4\n" 5562306a36Sopenharmony_ci " Bind to IPv4. Default is both.\n" 5662306a36Sopenharmony_ci "\n" 5762306a36Sopenharmony_ci " -6, --ipv6\n" 5862306a36Sopenharmony_ci " Bind to IPv6. Default is both.\n" 5962306a36Sopenharmony_ci "\n" 6062306a36Sopenharmony_ci " -e, --device\n" 6162306a36Sopenharmony_ci " Run in device mode.\n" 6262306a36Sopenharmony_ci " Rather than drive an attached device, create\n" 6362306a36Sopenharmony_ci " a virtual UDC to bind gadgets to.\n" 6462306a36Sopenharmony_ci "\n" 6562306a36Sopenharmony_ci " -D, --daemon\n" 6662306a36Sopenharmony_ci " Run as a daemon process.\n" 6762306a36Sopenharmony_ci "\n" 6862306a36Sopenharmony_ci " -d, --debug\n" 6962306a36Sopenharmony_ci " Print debugging information.\n" 7062306a36Sopenharmony_ci "\n" 7162306a36Sopenharmony_ci " -PFILE, --pid FILE\n" 7262306a36Sopenharmony_ci " Write process id to FILE.\n" 7362306a36Sopenharmony_ci " If no FILE specified, use " DEFAULT_PID_FILE "\n" 7462306a36Sopenharmony_ci "\n" 7562306a36Sopenharmony_ci " -tPORT, --tcp-port PORT\n" 7662306a36Sopenharmony_ci " Listen on TCP/IP port PORT.\n" 7762306a36Sopenharmony_ci "\n" 7862306a36Sopenharmony_ci " -h, --help\n" 7962306a36Sopenharmony_ci " Print this help.\n" 8062306a36Sopenharmony_ci "\n" 8162306a36Sopenharmony_ci " -v, --version\n" 8262306a36Sopenharmony_ci " Show version.\n"; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct usbip_host_driver *driver; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void usbipd_help(void) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci printf("%s\n", usbipd_help_string); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int recv_request_import(int sockfd) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct op_import_request req; 9462306a36Sopenharmony_ci struct usbip_exported_device *edev; 9562306a36Sopenharmony_ci struct usbip_usb_device pdu_udev; 9662306a36Sopenharmony_ci struct list_head *i; 9762306a36Sopenharmony_ci int found = 0; 9862306a36Sopenharmony_ci int status = ST_OK; 9962306a36Sopenharmony_ci int rc; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci rc = usbip_net_recv(sockfd, &req, sizeof(req)); 10462306a36Sopenharmony_ci if (rc < 0) { 10562306a36Sopenharmony_ci dbg("usbip_net_recv failed: import request"); 10662306a36Sopenharmony_ci return -1; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci PACK_OP_IMPORT_REQUEST(0, &req); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci list_for_each(i, &driver->edev_list) { 11162306a36Sopenharmony_ci edev = list_entry(i, struct usbip_exported_device, node); 11262306a36Sopenharmony_ci if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) { 11362306a36Sopenharmony_ci info("found requested device: %s", req.busid); 11462306a36Sopenharmony_ci found = 1; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (found) { 12062306a36Sopenharmony_ci /* should set TCP_NODELAY for usbip */ 12162306a36Sopenharmony_ci usbip_net_set_nodelay(sockfd); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* export device needs a TCP/IP socket descriptor */ 12462306a36Sopenharmony_ci status = usbip_export_device(edev, sockfd); 12562306a36Sopenharmony_ci if (status < 0) 12662306a36Sopenharmony_ci status = ST_NA; 12762306a36Sopenharmony_ci } else { 12862306a36Sopenharmony_ci info("requested device not found: %s", req.busid); 12962306a36Sopenharmony_ci status = ST_NODEV; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, status); 13362306a36Sopenharmony_ci if (rc < 0) { 13462306a36Sopenharmony_ci dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT); 13562306a36Sopenharmony_ci return -1; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (status) { 13962306a36Sopenharmony_ci dbg("import request busid %s: failed", req.busid); 14062306a36Sopenharmony_ci return -1; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); 14462306a36Sopenharmony_ci usbip_net_pack_usb_device(1, &pdu_udev); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev)); 14762306a36Sopenharmony_ci if (rc < 0) { 14862306a36Sopenharmony_ci dbg("usbip_net_send failed: devinfo"); 14962306a36Sopenharmony_ci return -1; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci dbg("import request busid %s: complete", req.busid); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int send_reply_devlist(int connfd) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct usbip_exported_device *edev; 16062306a36Sopenharmony_ci struct usbip_usb_device pdu_udev; 16162306a36Sopenharmony_ci struct usbip_usb_interface pdu_uinf; 16262306a36Sopenharmony_ci struct op_devlist_reply reply; 16362306a36Sopenharmony_ci struct list_head *j; 16462306a36Sopenharmony_ci int rc, i; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * Exclude devices that are already exported to a client from 16862306a36Sopenharmony_ci * the exportable device list to avoid: 16962306a36Sopenharmony_ci * - import requests for devices that are exported only to 17062306a36Sopenharmony_ci * fail the request. 17162306a36Sopenharmony_ci * - revealing devices that are imported by a client to 17262306a36Sopenharmony_ci * another client. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci reply.ndev = 0; 17662306a36Sopenharmony_ci /* number of exported devices */ 17762306a36Sopenharmony_ci list_for_each(j, &driver->edev_list) { 17862306a36Sopenharmony_ci edev = list_entry(j, struct usbip_exported_device, node); 17962306a36Sopenharmony_ci if (edev->status != SDEV_ST_USED) 18062306a36Sopenharmony_ci reply.ndev += 1; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci info("exportable devices: %d", reply.ndev); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK); 18562306a36Sopenharmony_ci if (rc < 0) { 18662306a36Sopenharmony_ci dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST); 18762306a36Sopenharmony_ci return -1; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci PACK_OP_DEVLIST_REPLY(1, &reply); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci rc = usbip_net_send(connfd, &reply, sizeof(reply)); 19262306a36Sopenharmony_ci if (rc < 0) { 19362306a36Sopenharmony_ci dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST); 19462306a36Sopenharmony_ci return -1; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci list_for_each(j, &driver->edev_list) { 19862306a36Sopenharmony_ci edev = list_entry(j, struct usbip_exported_device, node); 19962306a36Sopenharmony_ci if (edev->status == SDEV_ST_USED) 20062306a36Sopenharmony_ci continue; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci dump_usb_device(&edev->udev); 20362306a36Sopenharmony_ci memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); 20462306a36Sopenharmony_ci usbip_net_pack_usb_device(1, &pdu_udev); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev)); 20762306a36Sopenharmony_ci if (rc < 0) { 20862306a36Sopenharmony_ci dbg("usbip_net_send failed: pdu_udev"); 20962306a36Sopenharmony_ci return -1; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (i = 0; i < edev->udev.bNumInterfaces; i++) { 21362306a36Sopenharmony_ci dump_usb_interface(&edev->uinf[i]); 21462306a36Sopenharmony_ci memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf)); 21562306a36Sopenharmony_ci usbip_net_pack_usb_interface(1, &pdu_uinf); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci rc = usbip_net_send(connfd, &pdu_uinf, 21862306a36Sopenharmony_ci sizeof(pdu_uinf)); 21962306a36Sopenharmony_ci if (rc < 0) { 22062306a36Sopenharmony_ci err("usbip_net_send failed: pdu_uinf"); 22162306a36Sopenharmony_ci return -1; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int recv_request_devlist(int connfd) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct op_devlist_request req; 23262306a36Sopenharmony_ci int rc; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci rc = usbip_net_recv(connfd, &req, sizeof(req)); 23762306a36Sopenharmony_ci if (rc < 0) { 23862306a36Sopenharmony_ci dbg("usbip_net_recv failed: devlist request"); 23962306a36Sopenharmony_ci return -1; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci rc = send_reply_devlist(connfd); 24362306a36Sopenharmony_ci if (rc < 0) { 24462306a36Sopenharmony_ci dbg("send_reply_devlist failed"); 24562306a36Sopenharmony_ci return -1; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int recv_pdu(int connfd) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci uint16_t code = OP_UNSPEC; 25462306a36Sopenharmony_ci int ret; 25562306a36Sopenharmony_ci int status; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ret = usbip_net_recv_op_common(connfd, &code, &status); 25862306a36Sopenharmony_ci if (ret < 0) { 25962306a36Sopenharmony_ci dbg("could not receive opcode: %#0x", code); 26062306a36Sopenharmony_ci return -1; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = usbip_refresh_device_list(driver); 26462306a36Sopenharmony_ci if (ret < 0) { 26562306a36Sopenharmony_ci dbg("could not refresh device list: %d", ret); 26662306a36Sopenharmony_ci return -1; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci info("received request: %#0x(%d)", code, connfd); 27062306a36Sopenharmony_ci switch (code) { 27162306a36Sopenharmony_ci case OP_REQ_DEVLIST: 27262306a36Sopenharmony_ci ret = recv_request_devlist(connfd); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case OP_REQ_IMPORT: 27562306a36Sopenharmony_ci ret = recv_request_import(connfd); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case OP_REQ_DEVINFO: 27862306a36Sopenharmony_ci case OP_REQ_CRYPKEY: 27962306a36Sopenharmony_ci default: 28062306a36Sopenharmony_ci err("received an unknown opcode: %#0x", code); 28162306a36Sopenharmony_ci ret = -1; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (ret == 0) 28562306a36Sopenharmony_ci info("request %#0x(%d): complete", code, connfd); 28662306a36Sopenharmony_ci else 28762306a36Sopenharmony_ci info("request %#0x(%d): failed", code, connfd); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#ifdef HAVE_LIBWRAP 29362306a36Sopenharmony_cistatic int tcpd_auth(int connfd) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct request_info request; 29662306a36Sopenharmony_ci int rc; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0); 29962306a36Sopenharmony_ci fromhost(&request); 30062306a36Sopenharmony_ci rc = hosts_access(&request); 30162306a36Sopenharmony_ci if (rc == 0) 30262306a36Sopenharmony_ci return -1; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci#endif 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int do_accept(int listenfd) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci int connfd; 31162306a36Sopenharmony_ci struct sockaddr_storage ss; 31262306a36Sopenharmony_ci socklen_t len = sizeof(ss); 31362306a36Sopenharmony_ci char host[NI_MAXHOST], port[NI_MAXSERV]; 31462306a36Sopenharmony_ci int rc; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci memset(&ss, 0, sizeof(ss)); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci connfd = accept(listenfd, (struct sockaddr *)&ss, &len); 31962306a36Sopenharmony_ci if (connfd < 0) { 32062306a36Sopenharmony_ci err("failed to accept connection"); 32162306a36Sopenharmony_ci return -1; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host), 32562306a36Sopenharmony_ci port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); 32662306a36Sopenharmony_ci if (rc) 32762306a36Sopenharmony_ci err("getnameinfo: %s", gai_strerror(rc)); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci#ifdef HAVE_LIBWRAP 33062306a36Sopenharmony_ci rc = tcpd_auth(connfd); 33162306a36Sopenharmony_ci if (rc < 0) { 33262306a36Sopenharmony_ci info("denied access from %s", host); 33362306a36Sopenharmony_ci close(connfd); 33462306a36Sopenharmony_ci return -1; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci#endif 33762306a36Sopenharmony_ci info("connection from %s:%s", host, port); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return connfd; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciint process_request(int listenfd) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci pid_t childpid; 34562306a36Sopenharmony_ci int connfd; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci connfd = do_accept(listenfd); 34862306a36Sopenharmony_ci if (connfd < 0) 34962306a36Sopenharmony_ci return -1; 35062306a36Sopenharmony_ci childpid = fork(); 35162306a36Sopenharmony_ci if (childpid == 0) { 35262306a36Sopenharmony_ci close(listenfd); 35362306a36Sopenharmony_ci recv_pdu(connfd); 35462306a36Sopenharmony_ci exit(0); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci close(connfd); 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void addrinfo_to_text(struct addrinfo *ai, char buf[], 36162306a36Sopenharmony_ci const size_t buf_size) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci char hbuf[NI_MAXHOST]; 36462306a36Sopenharmony_ci char sbuf[NI_MAXSERV]; 36562306a36Sopenharmony_ci int rc; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci buf[0] = '\0'; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), 37062306a36Sopenharmony_ci sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 37162306a36Sopenharmony_ci if (rc) 37262306a36Sopenharmony_ci err("getnameinfo: %s", gai_strerror(rc)); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci snprintf(buf, buf_size, "%s:%s", hbuf, sbuf); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[], 37862306a36Sopenharmony_ci int maxsockfd) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct addrinfo *ai; 38162306a36Sopenharmony_ci int ret, nsockfd = 0; 38262306a36Sopenharmony_ci const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2; 38362306a36Sopenharmony_ci char ai_buf[ai_buf_size]; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) { 38662306a36Sopenharmony_ci int sock; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci addrinfo_to_text(ai, ai_buf, ai_buf_size); 38962306a36Sopenharmony_ci dbg("opening %s", ai_buf); 39062306a36Sopenharmony_ci sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 39162306a36Sopenharmony_ci if (sock < 0) { 39262306a36Sopenharmony_ci err("socket: %s: %d (%s)", 39362306a36Sopenharmony_ci ai_buf, errno, strerror(errno)); 39462306a36Sopenharmony_ci continue; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci usbip_net_set_reuseaddr(sock); 39862306a36Sopenharmony_ci usbip_net_set_nodelay(sock); 39962306a36Sopenharmony_ci /* We use seperate sockets for IPv4 and IPv6 40062306a36Sopenharmony_ci * (see do_standalone_mode()) */ 40162306a36Sopenharmony_ci usbip_net_set_v6only(sock); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ret = bind(sock, ai->ai_addr, ai->ai_addrlen); 40462306a36Sopenharmony_ci if (ret < 0) { 40562306a36Sopenharmony_ci err("bind: %s: %d (%s)", 40662306a36Sopenharmony_ci ai_buf, errno, strerror(errno)); 40762306a36Sopenharmony_ci close(sock); 40862306a36Sopenharmony_ci continue; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ret = listen(sock, SOMAXCONN); 41262306a36Sopenharmony_ci if (ret < 0) { 41362306a36Sopenharmony_ci err("listen: %s: %d (%s)", 41462306a36Sopenharmony_ci ai_buf, errno, strerror(errno)); 41562306a36Sopenharmony_ci close(sock); 41662306a36Sopenharmony_ci continue; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci info("listening on %s", ai_buf); 42062306a36Sopenharmony_ci sockfdlist[nsockfd++] = sock; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return nsockfd; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic struct addrinfo *do_getaddrinfo(char *host, int ai_family) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct addrinfo hints, *ai_head; 42962306a36Sopenharmony_ci int rc; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci memset(&hints, 0, sizeof(hints)); 43262306a36Sopenharmony_ci hints.ai_family = ai_family; 43362306a36Sopenharmony_ci hints.ai_socktype = SOCK_STREAM; 43462306a36Sopenharmony_ci hints.ai_flags = AI_PASSIVE; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head); 43762306a36Sopenharmony_ci if (rc) { 43862306a36Sopenharmony_ci err("failed to get a network address %s: %s", usbip_port_string, 43962306a36Sopenharmony_ci gai_strerror(rc)); 44062306a36Sopenharmony_ci return NULL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return ai_head; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void signal_handler(int i) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci dbg("received '%s' signal", strsignal(i)); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void set_signal(void) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct sigaction act; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci memset(&act, 0, sizeof(act)); 45662306a36Sopenharmony_ci act.sa_handler = signal_handler; 45762306a36Sopenharmony_ci sigemptyset(&act.sa_mask); 45862306a36Sopenharmony_ci sigaction(SIGTERM, &act, NULL); 45962306a36Sopenharmony_ci sigaction(SIGINT, &act, NULL); 46062306a36Sopenharmony_ci act.sa_handler = SIG_IGN; 46162306a36Sopenharmony_ci sigaction(SIGCHLD, &act, NULL); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic const char *pid_file; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic void write_pid_file(void) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci if (pid_file) { 46962306a36Sopenharmony_ci dbg("creating pid file %s", pid_file); 47062306a36Sopenharmony_ci FILE *fp; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci fp = fopen(pid_file, "w"); 47362306a36Sopenharmony_ci if (!fp) { 47462306a36Sopenharmony_ci err("pid_file: %s: %d (%s)", 47562306a36Sopenharmony_ci pid_file, errno, strerror(errno)); 47662306a36Sopenharmony_ci return; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci fprintf(fp, "%d\n", getpid()); 47962306a36Sopenharmony_ci fclose(fp); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void remove_pid_file(void) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci if (pid_file) { 48662306a36Sopenharmony_ci dbg("removing pid file %s", pid_file); 48762306a36Sopenharmony_ci unlink(pid_file); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int do_standalone_mode(int daemonize, int ipv4, int ipv6) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct addrinfo *ai_head; 49462306a36Sopenharmony_ci int sockfdlist[MAXSOCKFD]; 49562306a36Sopenharmony_ci int nsockfd, family; 49662306a36Sopenharmony_ci int i, terminate; 49762306a36Sopenharmony_ci struct pollfd *fds; 49862306a36Sopenharmony_ci struct timespec timeout; 49962306a36Sopenharmony_ci sigset_t sigmask; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (usbip_driver_open(driver)) 50262306a36Sopenharmony_ci return -1; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (daemonize) { 50562306a36Sopenharmony_ci if (daemon(0, 0) < 0) { 50662306a36Sopenharmony_ci err("daemonizing failed: %s", strerror(errno)); 50762306a36Sopenharmony_ci usbip_driver_close(driver); 50862306a36Sopenharmony_ci return -1; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci umask(0); 51162306a36Sopenharmony_ci usbip_use_syslog = 1; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci set_signal(); 51462306a36Sopenharmony_ci write_pid_file(); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci info("starting " PROGNAME " (%s)", usbip_version_string); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * To suppress warnings on systems with bindv6only disabled 52062306a36Sopenharmony_ci * (default), we use seperate sockets for IPv6 and IPv4 and set 52162306a36Sopenharmony_ci * IPV6_V6ONLY on the IPv6 sockets. 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci if (ipv4 && ipv6) 52462306a36Sopenharmony_ci family = AF_UNSPEC; 52562306a36Sopenharmony_ci else if (ipv4) 52662306a36Sopenharmony_ci family = AF_INET; 52762306a36Sopenharmony_ci else 52862306a36Sopenharmony_ci family = AF_INET6; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ai_head = do_getaddrinfo(NULL, family); 53162306a36Sopenharmony_ci if (!ai_head) { 53262306a36Sopenharmony_ci usbip_driver_close(driver); 53362306a36Sopenharmony_ci return -1; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci nsockfd = listen_all_addrinfo(ai_head, sockfdlist, 53662306a36Sopenharmony_ci sizeof(sockfdlist) / sizeof(*sockfdlist)); 53762306a36Sopenharmony_ci freeaddrinfo(ai_head); 53862306a36Sopenharmony_ci if (nsockfd <= 0) { 53962306a36Sopenharmony_ci err("failed to open a listening socket"); 54062306a36Sopenharmony_ci usbip_driver_close(driver); 54162306a36Sopenharmony_ci return -1; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es"); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci fds = calloc(nsockfd, sizeof(struct pollfd)); 54762306a36Sopenharmony_ci for (i = 0; i < nsockfd; i++) { 54862306a36Sopenharmony_ci fds[i].fd = sockfdlist[i]; 54962306a36Sopenharmony_ci fds[i].events = POLLIN; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci timeout.tv_sec = MAIN_LOOP_TIMEOUT; 55262306a36Sopenharmony_ci timeout.tv_nsec = 0; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci sigfillset(&sigmask); 55562306a36Sopenharmony_ci sigdelset(&sigmask, SIGTERM); 55662306a36Sopenharmony_ci sigdelset(&sigmask, SIGINT); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci terminate = 0; 55962306a36Sopenharmony_ci while (!terminate) { 56062306a36Sopenharmony_ci int r; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci r = ppoll(fds, nsockfd, &timeout, &sigmask); 56362306a36Sopenharmony_ci if (r < 0) { 56462306a36Sopenharmony_ci dbg("%s", strerror(errno)); 56562306a36Sopenharmony_ci terminate = 1; 56662306a36Sopenharmony_ci } else if (r) { 56762306a36Sopenharmony_ci for (i = 0; i < nsockfd; i++) { 56862306a36Sopenharmony_ci if (fds[i].revents & POLLIN) { 56962306a36Sopenharmony_ci dbg("read event on fd[%d]=%d", 57062306a36Sopenharmony_ci i, sockfdlist[i]); 57162306a36Sopenharmony_ci process_request(sockfdlist[i]); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci } else { 57562306a36Sopenharmony_ci dbg("heartbeat timeout on ppoll()"); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci info("shutting down " PROGNAME); 58062306a36Sopenharmony_ci free(fds); 58162306a36Sopenharmony_ci usbip_driver_close(driver); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ciint main(int argc, char *argv[]) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci static const struct option longopts[] = { 58962306a36Sopenharmony_ci { "ipv4", no_argument, NULL, '4' }, 59062306a36Sopenharmony_ci { "ipv6", no_argument, NULL, '6' }, 59162306a36Sopenharmony_ci { "daemon", no_argument, NULL, 'D' }, 59262306a36Sopenharmony_ci { "daemon", no_argument, NULL, 'D' }, 59362306a36Sopenharmony_ci { "debug", no_argument, NULL, 'd' }, 59462306a36Sopenharmony_ci { "device", no_argument, NULL, 'e' }, 59562306a36Sopenharmony_ci { "pid", optional_argument, NULL, 'P' }, 59662306a36Sopenharmony_ci { "tcp-port", required_argument, NULL, 't' }, 59762306a36Sopenharmony_ci { "help", no_argument, NULL, 'h' }, 59862306a36Sopenharmony_ci { "version", no_argument, NULL, 'v' }, 59962306a36Sopenharmony_ci { NULL, 0, NULL, 0 } 60062306a36Sopenharmony_ci }; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci enum { 60362306a36Sopenharmony_ci cmd_standalone_mode = 1, 60462306a36Sopenharmony_ci cmd_help, 60562306a36Sopenharmony_ci cmd_version 60662306a36Sopenharmony_ci } cmd; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci int daemonize = 0; 60962306a36Sopenharmony_ci int ipv4 = 0, ipv6 = 0; 61062306a36Sopenharmony_ci int opt, rc = -1; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci pid_file = NULL; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci usbip_use_stderr = 1; 61562306a36Sopenharmony_ci usbip_use_syslog = 0; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (geteuid() != 0) 61862306a36Sopenharmony_ci err("not running as root?"); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci cmd = cmd_standalone_mode; 62162306a36Sopenharmony_ci driver = &host_driver; 62262306a36Sopenharmony_ci for (;;) { 62362306a36Sopenharmony_ci opt = getopt_long(argc, argv, "46DdeP::t:hv", longopts, NULL); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (opt == -1) 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci switch (opt) { 62962306a36Sopenharmony_ci case '4': 63062306a36Sopenharmony_ci ipv4 = 1; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case '6': 63362306a36Sopenharmony_ci ipv6 = 1; 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci case 'D': 63662306a36Sopenharmony_ci daemonize = 1; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci case 'd': 63962306a36Sopenharmony_ci usbip_use_debug = 1; 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case 'h': 64262306a36Sopenharmony_ci cmd = cmd_help; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case 'P': 64562306a36Sopenharmony_ci pid_file = optarg ? optarg : DEFAULT_PID_FILE; 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci case 't': 64862306a36Sopenharmony_ci usbip_setup_port_number(optarg); 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci case 'v': 65162306a36Sopenharmony_ci cmd = cmd_version; 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case 'e': 65462306a36Sopenharmony_ci driver = &device_driver; 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci case '?': 65762306a36Sopenharmony_ci usbipd_help(); 65862306a36Sopenharmony_ci default: 65962306a36Sopenharmony_ci goto err_out; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (!ipv4 && !ipv6) 66462306a36Sopenharmony_ci ipv4 = ipv6 = 1; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci switch (cmd) { 66762306a36Sopenharmony_ci case cmd_standalone_mode: 66862306a36Sopenharmony_ci rc = do_standalone_mode(daemonize, ipv4, ipv6); 66962306a36Sopenharmony_ci remove_pid_file(); 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci case cmd_version: 67262306a36Sopenharmony_ci printf(PROGNAME " (%s)\n", usbip_version_string); 67362306a36Sopenharmony_ci rc = 0; 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci case cmd_help: 67662306a36Sopenharmony_ci usbipd_help(); 67762306a36Sopenharmony_ci rc = 0; 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci default: 68062306a36Sopenharmony_ci usbipd_help(); 68162306a36Sopenharmony_ci goto err_out; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cierr_out: 68562306a36Sopenharmony_ci return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE); 68662306a36Sopenharmony_ci} 687