1/*
2 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
3 *               2005-2007 Takahiro Hirofuchi
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <sys/types.h>
20#include <libudev.h>
21
22#include <errno.h>
23#include <stdbool.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <getopt.h>
30#include <netdb.h>
31#include <unistd.h>
32
33#include "usbip_common.h"
34#include "usbip_network.h"
35#include "usbip.h"
36
37static const char usbip_list_usage_string[] =
38	"usbip list [-p|--parsable] <args>\n"
39	"    -p, --parsable         Parsable list format\n"
40	"    -r, --remote=<host>    List the exportable USB devices on <host>\n"
41	"    -l, --local            List the local USB devices\n";
42
43void usbip_list_usage(void)
44{
45	printf("usage: %s", usbip_list_usage_string);
46}
47
48static int get_exported_devices(char *host, int sockfd)
49{
50	char product_name[100];
51	char class_name[100];
52	struct op_devlist_reply reply;
53	uint16_t code = OP_REP_DEVLIST;
54	struct usbip_usb_device udev;
55	struct usbip_usb_interface uintf;
56	unsigned int i;
57	int rc, j;
58
59	rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
60	if (rc < 0) {
61		dbg("usbip_net_send_op_common failed");
62		return -1;
63	}
64
65	rc = usbip_net_recv_op_common(sockfd, &code);
66	if (rc < 0) {
67		dbg("usbip_net_recv_op_common failed");
68		return -1;
69	}
70
71	memset(&reply, 0, sizeof(reply));
72	rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
73	if (rc < 0) {
74		dbg("usbip_net_recv_op_devlist failed");
75		return -1;
76	}
77	PACK_OP_DEVLIST_REPLY(0, &reply);
78	dbg("exportable devices: %d\n", reply.ndev);
79
80	if (reply.ndev == 0) {
81		info("no exportable devices found on %s", host);
82		return 0;
83	}
84
85	printf("Exportable USB devices\n");
86	printf("======================\n");
87	printf(" - %s\n", host);
88
89	for (i = 0; i < reply.ndev; i++) {
90		memset(&udev, 0, sizeof(udev));
91		rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
92		if (rc < 0) {
93			dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
94			return -1;
95		}
96		usbip_net_pack_usb_device(0, &udev);
97
98		usbip_names_get_product(product_name, sizeof(product_name),
99					udev.idVendor, udev.idProduct);
100		usbip_names_get_class(class_name, sizeof(class_name),
101				      udev.bDeviceClass, udev.bDeviceSubClass,
102				      udev.bDeviceProtocol);
103		printf("%11s: %s\n", udev.busid, product_name);
104		printf("%11s: %s\n", "", udev.path);
105		printf("%11s: %s\n", "", class_name);
106
107		for (j = 0; j < udev.bNumInterfaces; j++) {
108			rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
109			if (rc < 0) {
110				err("usbip_net_recv failed: usbip_usb_intf[%d]",
111						j);
112
113				return -1;
114			}
115			usbip_net_pack_usb_interface(0, &uintf);
116
117			usbip_names_get_class(class_name, sizeof(class_name),
118					uintf.bInterfaceClass,
119					uintf.bInterfaceSubClass,
120					uintf.bInterfaceProtocol);
121			printf("%11s: %2d - %s\n", "", j, class_name);
122		}
123
124		printf("\n");
125	}
126
127	return 0;
128}
129
130static int list_exported_devices(char *host)
131{
132	int rc;
133	int sockfd;
134
135	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
136	if (sockfd < 0) {
137		err("could not connect to %s:%s: %s", host,
138		    usbip_port_string, gai_strerror(sockfd));
139		return -1;
140	}
141	dbg("connected to %s:%s", host, usbip_port_string);
142
143	rc = get_exported_devices(host, sockfd);
144	if (rc < 0) {
145		err("failed to get device list from %s", host);
146		return -1;
147	}
148
149	close(sockfd);
150
151	return 0;
152}
153
154static void print_device(const char *busid, const char *vendor,
155			 const char *product, bool parsable)
156{
157	if (parsable)
158		printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product);
159	else
160		printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product);
161}
162
163static void print_product_name(char *product_name, bool parsable)
164{
165	if (!parsable)
166		printf("   %s\n", product_name);
167}
168
169static int list_devices(bool parsable)
170{
171	struct udev *udev;
172	struct udev_enumerate *enumerate;
173	struct udev_list_entry *devices, *dev_list_entry;
174	struct udev_device *dev;
175	const char *path;
176	const char *idVendor;
177	const char *idProduct;
178	const char *bConfValue;
179	const char *bNumIntfs;
180	const char *busid;
181	char product_name[128];
182	int ret = -1;
183
184	/* Create libudev context. */
185	udev = udev_new();
186
187	/* Create libudev device enumeration. */
188	enumerate = udev_enumerate_new(udev);
189
190	/* Take only USB devices that are not hubs and do not have
191	 * the bInterfaceNumber attribute, i.e. are not interfaces.
192	 */
193	udev_enumerate_add_match_subsystem(enumerate, "usb");
194	udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass", "09");
195	udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber", NULL);
196	udev_enumerate_scan_devices(enumerate);
197
198	devices = udev_enumerate_get_list_entry(enumerate);
199
200	/* Show information about each device. */
201	udev_list_entry_foreach(dev_list_entry, devices) {
202		path = udev_list_entry_get_name(dev_list_entry);
203		dev = udev_device_new_from_syspath(udev, path);
204
205		/* Get device information. */
206		idVendor = udev_device_get_sysattr_value(dev, "idVendor");
207		idProduct = udev_device_get_sysattr_value(dev, "idProduct");
208		bConfValue = udev_device_get_sysattr_value(dev, "bConfigurationValue");
209		bNumIntfs = udev_device_get_sysattr_value(dev, "bNumInterfaces");
210		busid = udev_device_get_sysname(dev);
211		if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) {
212			err("problem getting device attributes: %s",
213			    strerror(errno));
214			goto err_out;
215		}
216
217		/* Get product name. */
218		usbip_names_get_product(product_name, sizeof(product_name),
219					strtol(idVendor, NULL, 16),
220					strtol(idProduct, NULL, 16));
221
222		/* Print information. */
223		print_device(busid, idVendor, idProduct, parsable);
224		print_product_name(product_name, parsable);
225
226		printf("\n");
227
228		udev_device_unref(dev);
229	}
230
231	ret = 0;
232
233err_out:
234	udev_enumerate_unref(enumerate);
235	udev_unref(udev);
236
237	return ret;
238}
239
240int usbip_list(int argc, char *argv[])
241{
242	static const struct option opts[] = {
243		{ "parsable", no_argument,       NULL, 'p' },
244		{ "remote",   required_argument, NULL, 'r' },
245		{ "local",    no_argument,       NULL, 'l' },
246		{ NULL,       0,                 NULL,  0  }
247	};
248
249	bool parsable = false;
250	int opt;
251	int ret = -1;
252
253	if (usbip_names_init(USBIDS_FILE))
254		err("failed to open %s", USBIDS_FILE);
255
256	for (;;) {
257		opt = getopt_long(argc, argv, "pr:l", opts, NULL);
258
259		if (opt == -1)
260			break;
261
262		switch (opt) {
263		case 'p':
264			parsable = true;
265			break;
266		case 'r':
267			ret = list_exported_devices(optarg);
268			goto out;
269		case 'l':
270			ret = list_devices(parsable);
271			goto out;
272		default:
273			goto err_out;
274		}
275	}
276
277err_out:
278	usbip_list_usage();
279out:
280	usbip_names_free();
281
282	return ret;
283}
284