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