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